You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/util/crypto/profileEncrypter.ts

67 lines
2.4 KiB
TypeScript

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<ArrayBuffer> {
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<ArrayBuffer> {
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;
})
);
}