mirror of
https://framagit.org/hubzilla/core.git
synced 2026-06-21 00:52:33 -04:00
129 lines
3.4 KiB
JavaScript
129 lines
3.4 KiB
JavaScript
async function sodium_encrypt(element) {
|
|
if (typeof tinyMCE !== typeof undefined) {
|
|
tinyMCE.triggerSave(false,true);
|
|
}
|
|
|
|
const message = $(element).val();
|
|
|
|
if (!message) {
|
|
return false;
|
|
}
|
|
|
|
const password = prompt(aStr['passphrase']);
|
|
|
|
if (!password) {
|
|
return false;
|
|
}
|
|
|
|
const hint = bin2hex(prompt(aStr['passhint']));
|
|
|
|
/* This (Argon2) is more secure against bruteforce attacks than PBKDF2 but is overkill for javascript according to author
|
|
* https://github.com/jedisct1/libsodium.js/issues/250#issuecomment-685971738
|
|
*
|
|
* It does not work in chromium mobile at all (freezing)
|
|
*
|
|
* This functions requires the browser-sumo/sodium.js
|
|
|
|
const salt = await sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
|
|
const key = await sodium.crypto_pwhash(
|
|
sodium.crypto_secretbox_KEYBYTES,
|
|
password,
|
|
salt,
|
|
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
sodium.crypto_pwhash_ALG_DEFAULT
|
|
);
|
|
*/
|
|
|
|
const salt = crypto.getRandomValues(new Uint8Array(32)); // Generate a random salt (32 bytes)
|
|
const key = await derivePBKDF2Hash(password, salt);
|
|
|
|
const nonce = await sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
|
|
|
|
// Message can be a string, buffer, array, etc.
|
|
const ciphertext = await sodium.crypto_secretbox_easy(message, nonce, key);
|
|
delete message, password, key;
|
|
|
|
const payload = {
|
|
hint: hint,
|
|
alg: 'XSalsa20',
|
|
salt: await sodium.to_hex(salt),
|
|
nonce: await sodium.to_hex(nonce),
|
|
ciphertext: await sodium.to_hex(ciphertext)
|
|
};
|
|
|
|
const val = "[crypt]" + window.btoa(JSON.stringify(payload)) + '[/crypt]';
|
|
|
|
$(element).val(val);
|
|
}
|
|
|
|
async function sodium_decrypt(payload, element) {
|
|
const arr = JSON.parse(window.atob(payload));
|
|
|
|
if (arr.alg !== 'XSalsa20') {
|
|
alert('Unsupported algorithm');
|
|
return false;
|
|
}
|
|
|
|
const password = prompt((arr.hint.length) ? hex2bin(arr.hint) : aStr['passphrase']);
|
|
|
|
if (!password) {
|
|
return false;
|
|
}
|
|
|
|
const salt = await sodium.from_hex(arr.salt);
|
|
const nonce = await sodium.from_hex(arr.nonce);
|
|
const ciphertext = await sodium.from_hex(arr.ciphertext);
|
|
|
|
/* See sodium_encrypt()
|
|
const key = await sodium.crypto_pwhash(
|
|
sodium.crypto_secretbox_KEYBYTES,
|
|
password,
|
|
salt,
|
|
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
sodium.crypto_pwhash_ALG_DEFAULT
|
|
);
|
|
*/
|
|
|
|
const key = await derivePBKDF2Hash(password, salt);
|
|
|
|
const decrypted = await sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);
|
|
delete password, key;
|
|
|
|
if ($(element).css('display') === 'none' && typeof tinyMCE !== typeof undefined) {
|
|
tinyMCE.activeEditor.setContent(sodium.to_string(decrypted));
|
|
}
|
|
else {
|
|
$(element).html(sodium.to_string(decrypted));
|
|
}
|
|
}
|
|
|
|
|
|
async function derivePBKDF2Hash(password, salt) {
|
|
const encoder = new TextEncoder();
|
|
const passwordBuffer = encoder.encode(password);
|
|
const saltBuffer = encoder.encode(salt);
|
|
|
|
const key = await crypto.subtle.importKey(
|
|
'raw', // key format
|
|
passwordBuffer, // password as raw data
|
|
{ name: 'PBKDF2' }, // algorithm
|
|
false, // whether the key can be exported
|
|
['deriveBits'] // key usages
|
|
);
|
|
|
|
const derivedBits = await crypto.subtle.deriveBits(
|
|
{
|
|
name: 'PBKDF2',
|
|
salt: saltBuffer,
|
|
iterations: 100000, // The higher the more secure should be at least 100000
|
|
hash: 'SHA-256', // could also be SHA-512
|
|
},
|
|
key,
|
|
256 // derived key length in bits (32 bytes)
|
|
);
|
|
|
|
return new Uint8Array(derivedBits);
|
|
}
|