2022-10-17 07:35:03 +00:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
2022-10-17 10:31:02 +00:00
< title > Decrypt text< / title >
2022-10-17 07:35:03 +00:00
< meta name = "theme-color" content = "#006db3" >
< meta name = "viewport" content = "width=device-width,minimum-scale=1,initial-scale=1" >
2022-10-17 10:31:02 +00:00
< meta name = "description" content = "Decrypt text" >
2022-10-17 07:35:03 +00:00
< meta name = "author" content = "M66B" >
< meta name = "robots" content = "noindex" >
<!-- https://developer.mozilla.org/en - US/docs/Web/HTTP/CSP -->
2022-10-17 16:40:41 +00:00
< meta http-equiv = "Content-Security-Policy" content = "default-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; script-src 'unsafe-inline' *.faircode.eu;" >
2022-10-17 07:35:03 +00:00
< style >
body { padding-left: 10px; padding-right: 10px; font-family: Arial, Helvetica, sans-serif; }
< / style >
< style >
.noscript { display: none; }
< / style >
< noscript >
< style >
.noscript { display: block; }
< / style >
< / noscript >
2022-10-17 16:40:41 +00:00
<!-- https://github.com/cure53/DOMPurify 2.4.0 -->
< script src = "https://email.faircode.eu/decrypt/purify.min.js" > < / script >
2022-10-17 07:35:03 +00:00
< script >
window.addEventListener('load', load);
function load() {
2022-10-17 13:33:51 +00:00
let form = document.getElementById('form')
let password = document.getElementById('password');
let message = document.getElementById('message');
let error = document.getElementById('error');
let details = document.getElementById('details');
form.addEventListener('submit', submit);
2022-10-17 07:35:03 +00:00
if (window.location.hash)
if (crypto.subtle & &
typeof Uint8Array === 'function' & &
typeof TextEncoder === 'function') {
2022-10-17 13:33:51 +00:00
form.style.display = 'block';
password.focus();
2022-10-17 07:35:03 +00:00
}
else {
2022-10-17 13:33:51 +00:00
error.textContent = 'Your browser is unsuitable for decrypting text';
error.style.display = 'block';
details.innerHTML =
2022-10-17 07:35:03 +00:00
'crypto.subtle: ' + (crypto.subtle ? 'Yes' : 'No') + '< br > ' +
'Uint8Array: ' + (Uint8Array ? 'Yes' : 'No') + '< br > ' +
'TextEncoder: ' + (TextEncoder ? 'Yes' : 'No') + '< br > ';
2022-10-17 13:33:51 +00:00
details.style.display = 'block';
2022-10-17 07:35:03 +00:00
}
else {
2022-10-17 13:33:51 +00:00
message.innerHTML = 'Nothing to see here';
message.style.display = 'block';
2022-10-17 07:35:03 +00:00
}
document.getElementById('year').textContent = new Date().getFullYear();
}
2022-10-17 13:33:51 +00:00
function submit(event) {
2022-10-17 07:35:03 +00:00
event.preventDefault();
decrypt();
}
async function decrypt() {
2022-10-17 13:33:51 +00:00
let fields = document.getElementById('fields');
let form = document.getElementById('form')
let password = document.getElementById('password');
let message = document.getElementById('message');
let error = document.getElementById('error');
let details = document.getElementById('details');
let copyright = document.getElementById('copyright');
2022-10-17 07:35:03 +00:00
try {
2022-10-17 13:33:51 +00:00
fields.disabled = true;
message.style.display = 'none';
error.style.display = 'none';
details.style.display = 'none';
if (!password.value)
2022-10-17 07:35:03 +00:00
throw new Error('Password required');
2022-10-17 13:33:51 +00:00
2022-10-17 16:40:41 +00:00
let dirty = await _decrypt(password.value);
let clean = DOMPurify.sanitize(dirty, { USE_PROFILES: { html: true } });
2022-10-17 13:33:51 +00:00
form.style.display = 'none';
2022-10-17 16:40:41 +00:00
message.innerHTML = clean;
2022-10-17 13:33:51 +00:00
message.style.display = 'block';
copyright.style.display = 'none';
2022-10-17 07:35:03 +00:00
} catch (e) {
console.log("%O", e);
2022-10-17 13:33:51 +00:00
fields.disabled = false;
password.value = '';
password.focus();
error.textContent = 'Could not decrypt the message. Is the password correct?';
error.style.display = 'block';
details.textContent = e.toString();
details.style.display = 'block';
2022-10-17 07:35:03 +00:00
}
}
async function _decrypt(password) {
let msg = atob(window.location.hash.substr(1).replaceAll('-', '+').replaceAll('_', '/'));
const buf = new Uint8Array(msg.length);
for (let i = 0; i < msg.length ; i + + )
buf[i] = msg.charCodeAt(i);
const version = buf[0];
const salt = buf.slice(1, 1 + 16);
const iv = buf.slice(1 + 16, 1 + 16 + 12);
const e = buf.slice(1 + 16 + 12, buf.length);
// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle
// https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
const passwordBuffer = new TextEncoder('UTF-8').encode(password);
const importedKey = await crypto.subtle.importKey('raw', passwordBuffer, 'PBKDF2', false, ['deriveBits']);
const derivation = await crypto.subtle.deriveBits({name: 'PBKDF2', hash: 'SHA-512', salt: salt, iterations: 120000}, importedKey, 256);
const importedEncryptionKey = await crypto.subtle.importKey('raw', derivation, {name: 'AES-GCM'}, false, ['decrypt']);
2022-10-17 13:33:51 +00:00
const decrypted = await crypto.subtle.decrypt({name: 'AES-GCM', iv: iv, tagLength: 128}, importedEncryptionKey, e);
2022-10-17 07:35:03 +00:00
return new TextDecoder('UTF-8').decode(decrypted);
}
< / script >
< / head >
< body >
< p class = "noscript" style = "color: red; font-weight: bold;" > Please enable JavaScript< / p >
2022-10-17 13:33:51 +00:00
< form id = "form" action = "#" method = "GET" style = "display: none;" >
2022-10-17 07:35:03 +00:00
< p >
2022-10-17 10:31:02 +00:00
Someone sent you password protected text with < a href = "https://email.faircode.eu/" target = "_blank" > FairEmail< / a > .
2022-10-17 07:35:03 +00:00
< / p >
< fieldset id = "fields" style = "border:0 none; margin: 0; padding: 0;" >
< p >
2022-10-17 10:31:02 +00:00
< label for = "password" > Enter password:< / label > < br >
2022-10-17 07:35:03 +00:00
< input id = "password" name = "password" type = "password" required > < br >
< span style = "font-size: smaller;" > Passwords are case-sensitive < / span >
< / p >
< p >
< input id = "submit" type = "submit" value = "Decrypt" >
< / p >
< / fieldset >
< p style = "font-size: smaller;" >
2022-10-17 12:45:12 +00:00
Password protected text is sent as a < a href = "https://en.wikipedia.org/wiki/URI_fragment" target = "_blank" > URI fragment< / a > and not stored on third party servers, and decrypted in the browser with JavaScript.< br >
2022-10-17 10:31:02 +00:00
Password protected text is encrypted with AES/GCM with a 256 bits key derived with PBKDF2/SHA-512 with 120,000 iterations.< br >
Please see < a href = "https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq184" target = "_blank" > here< / a > for more information.
2022-10-17 07:35:03 +00:00
< / p >
< / form >
< p id = "message" style = "display: none;" > < / p >
< p id = "error" style = "color: red; font-weight: bold; display: none;" > < / p >
< p id = "details" style = "font-size: x-small; display: none;" > < / p >
< p id = "copyright" style = "padding-top: 20px;" >
Copyright © 2018– < span id = "year" > 2022< / span > by Marcel Bokhorst (M66B)
< br >
< br >
< span style = "font-size: smaller;" > This page is < a href = "https://github.com/M66B/FairEmail/blob/master/decrypt/index.html" target = "_blank" > open source< / a > .< / span >
< / p >
< / body >
< / html >