sessions-ext/src/crypto.ts

86 lines
2.2 KiB
TypeScript

const ITERATIONS = 100000;
const SALT_LENGTH = 16;
const IV_LENGTH = 12;
const enc = new TextEncoder();
const dec = new TextDecoder();
export const encrypt = async (data: string, password: string) => {
const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
const passwordKey = await getPasswordKey(password);
const aesKey = await deriveKey(passwordKey, salt, ['encrypt']);
const encryptedContent = await crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: iv,
},
aesKey,
enc.encode(data)
);
const encryptedContentArr = new Uint8Array(encryptedContent);
const buf = new Uint8Array(
salt.byteLength + iv.byteLength + encryptedContentArr.byteLength
);
buf.set(salt, 0);
buf.set(iv, salt.byteLength);
buf.set(encryptedContentArr, salt.byteLength + iv.byteLength);
return bufToBase64(buf);
};
export const decrypt = async (encryptedData: string, password: string) => {
const encryptedDataBuff = base64ToBuf(encryptedData);
const salt = encryptedDataBuff.slice(0, SALT_LENGTH);
const iv = encryptedDataBuff.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
const data = encryptedDataBuff.slice(SALT_LENGTH + IV_LENGTH);
const passwordKey = await getPasswordKey(password);
const aesKey = await deriveKey(passwordKey, salt, ['decrypt']);
const decryptedContent = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: iv,
},
aesKey,
data
);
return dec.decode(decryptedContent);
};
const getPasswordKey = (password: string) => {
return crypto.subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, [
'deriveKey',
]);
};
const deriveKey = (
passwordKey: CryptoKey,
salt: Uint8Array,
keyUsages: KeyUsage[]
) => {
return crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt: salt,
iterations: ITERATIONS,
hash: 'SHA-256',
},
passwordKey,
{ name: 'AES-GCM', length: 256 },
false,
keyUsages
);
};
const bufToBase64 = (buf: Uint8Array) => {
let s = '';
buf.forEach((c) => {
s += String.fromCharCode(c);
});
return btoa(s);
};
const base64ToBuf = (b64: string) => {
return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
};