sessions-ext/src/crypto.ts

86 lines
2.2 KiB
TypeScript

// https://github.com/bradyjoslin/webcrypto-example/blob/master/script.js
const ITERATIONS = 100000;
const enc = new TextEncoder();
const dec = new TextDecoder();
export const encrypt = async (data: string, password: string) => {
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
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);
let 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, 16);
const iv = encryptedDataBuff.slice(16, 16 + 12);
const data = encryptedDataBuff.slice(16 + 12);
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));
};