passed/static/crypto.js
2024-10-30 13:24:59 +01:00

108 lines
2.5 KiB
JavaScript

const ALGORITHM_NAME = "AES-GCM";
const ALGORITHM_LENGTH = 256;
const IV_LENGTH = 16;
const TEXT_ENCODER = new TextEncoder();
const TEXT_DECODER = new TextDecoder();
/** Coverts an ArrayBuffer into a Base64 encoded string
* @param {ArrayBuffer} buffer
* @returns {string}
*/
function bufferToBase64(buffer) {
let binary = "";
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
/** Converts a Base64 encoded string into an ArrayBuffer
* @param {string} base64
* @returns {ArrayBuffer}
*/
function base64ToBuffer(base64) {
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
/** Generates an key and encrypts "password" using it
* @param {Promise<string>} password
*/
async function encryptPassword(password) {
const iv = new Uint8Array(IV_LENGTH);
window.crypto.getRandomValues(iv);
const key = await window.crypto.subtle.generateKey(
{
name: ALGORITHM_NAME,
length: ALGORITHM_LENGTH,
},
true,
["encrypt", "decrypt"],
);
const encryptedPassword = await window.crypto.subtle.encrypt(
{
name: ALGORITHM_NAME,
iv: iv,
},
key,
TEXT_ENCODER.encode(password),
);
const exportedKey = await window.crypto.subtle.exportKey("raw", key);
const encodedPassword = bufferToBase64(encryptedPassword);
const encodedKey = bufferToBase64(exportedKey);
const encodedIv = bufferToBase64(iv.buffer);
return {
password: encodedPassword,
key: encodedKey,
iv: encodedIv,
};
}
/** Decodes to key and iv and decryptes the password
* @param {string} password
* @param {string} key
* @param {string} iv
* @returns {Promise<string>}
*/
async function decryptPassword(password, key, iv) {
const decodedPassword = base64ToBuffer(password);
const decodedKey = base64ToBuffer(key);
const decodedIv = base64ToBuffer(iv);
const cryptoKey = await window.crypto.subtle.importKey(
"raw",
decodedKey,
{
name: ALGORITHM_NAME,
length: ALGORITHM_LENGTH,
},
true,
["encrypt", "decrypt"],
);
const decryptedPassword = await window.crypto.subtle.decrypt(
{
name: ALGORITHM_NAME,
iv: decodedIv,
},
cryptoKey,
decodedPassword,
);
return TEXT_DECODER.decode(decryptedPassword);
}