import { getIV } from "encryption/low-level/util"; export interface EncryptedData { iv: Uint8Array; data: ArrayBuffer; } export interface AESEncryptorI { encryptBinary(data: Uint8Array | ArrayBuffer): Promise; decryptBinary( iv: Uint8Array | ArrayBuffer, data: Uint8Array | ArrayBuffer ): Promise; getAlgorithm(): AesKeyAlgorithm; } /** * Creates an instance of an AES. * * This is meant to be fairly low level, and is not intended to be used directly. * You're probably instead interested in the DataEncryptor. * * @constructor {CryptoKey} The key used to encrypt and decrypt data. */ export function AESEncryptor(key: CryptoKey): AESEncryptorI { const algorithm = AESEncryptor.getDefaultAlgorithm(); // For AES-GCM we will use 12 bytes // https://crypto.stackexchange.com/questions/41601/aes-gcm-recommended-iv-size-why-12-bytes/41610 const IVLength = 12; const getAlgorithm = (iv: Uint8Array | ArrayBuffer): AesKeyAlgorithm => Object.assign({}, algorithm, { iv: iv }); return { async encryptBinary( data: Uint8Array | ArrayBuffer ): Promise { const iv = getIV(IVLength); const cipherText = await crypto.subtle.encrypt( getAlgorithm(iv), key, data ); return { iv: iv, data: cipherText }; }, async decryptBinary( iv: Uint8Array | ArrayBuffer, data: Uint8Array | ArrayBuffer ): Promise { const rawResult = await crypto.subtle.decrypt( getAlgorithm(iv), key, data ); return rawResult; }, getAlgorithm(): AesKeyAlgorithm { return algorithm; } }; } AESEncryptor.getDefaultAlgorithm = function(): AesKeyAlgorithm { return Object.freeze({ name: "AES-GCM", length: 256 }); }; AESEncryptor.generateKey = async function(): Promise { return (await crypto.subtle.generateKey( AESEncryptor.getDefaultAlgorithm(), // We need to be able to encrypt this key, so it needs to be extractable true, ["encrypt", "decrypt"] )) as CryptoKey; };