import { getSodiumRenderer } from '../../session/crypto'; const PROFILE_IV_LENGTH = 12; // bytes const PROFILE_KEY_LENGTH = 32; // bytes const PROFILE_TAG_LENGTH = 128; // bits export async function decryptProfile(data: ArrayBuffer, key: ArrayBuffer): Promise { if (data.byteLength < 12 + 16 + 1) { throw new Error(`Got too short input: ${data.byteLength}`); } const iv = data.slice(0, PROFILE_IV_LENGTH); const ciphertext = data.slice(PROFILE_IV_LENGTH, data.byteLength); if (key.byteLength !== PROFILE_KEY_LENGTH) { throw new Error('Got invalid length profile key'); } if (iv.byteLength !== PROFILE_IV_LENGTH) { throw new Error('Got invalid length profile iv'); } const error = new Error(); // save stack return crypto.subtle .importKey('raw', key, { name: 'AES-GCM' }, false, ['decrypt']) .then(keyForEncryption => crypto.subtle .decrypt( { name: 'AES-GCM', iv, tagLength: PROFILE_TAG_LENGTH }, keyForEncryption, ciphertext ) .catch(e => { if (e.name === 'OperationError') { // bad mac, basically. error.message = 'Failed to decrypt profile data. Most likely the profile key has changed.'; error.name = 'ProfileDecryptError'; throw error; } }) ); } async function getRandomBytesFromLength(n: number) { return (await getSodiumRenderer()).randombytes_buf(n); } export async function encryptProfile(data: ArrayBuffer, key: ArrayBuffer): Promise { const iv = await getRandomBytesFromLength(PROFILE_IV_LENGTH); if (key.byteLength !== PROFILE_KEY_LENGTH) { throw new Error('Got invalid length profile key'); } if (iv.byteLength !== PROFILE_IV_LENGTH) { throw new Error('Got invalid length profile iv'); } return crypto.subtle .importKey('raw', key, { name: 'AES-GCM' }, false, ['encrypt']) .then(keyForEncryption => crypto.subtle .encrypt({ name: 'AES-GCM', iv, tagLength: PROFILE_TAG_LENGTH }, keyForEncryption, data) .then(ciphertext => { // tslint:disable-next-line: restrict-plus-operands const ivAndCiphertext = new Uint8Array(PROFILE_IV_LENGTH + ciphertext.byteLength); ivAndCiphertext.set(new Uint8Array(iv)); ivAndCiphertext.set(new Uint8Array(ciphertext), PROFILE_IV_LENGTH); return ivAndCiphertext.buffer; }) ); }