Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / crypto / lib / subtle / aesGcm.ts
blob2a9e35e939ef49dd45185a1d0ba3d7e3aa5b99fc
1 import mergeUint8Arrays from '@proton/utils/mergeUint8Arrays';
3 export const KEY_LENGTH_BYTES = 32;
4 const IV_LENGTH_BYTES = 12;
5 export const ENCRYPTION_ALGORITHM = 'AES-GCM';
6 export type AesCryptoKey = CryptoKey;
8 type AesGcmKeyUsage = 'decrypt' | 'encrypt';
9 interface AesGcmKeyOptions {
10     /** allowed key operations */
11     keyUsage?: AesGcmKeyUsage[];
12     /** whether the key can be exported using `subtle.crypto.exportKey` */
13     extractable?: boolean;
16 /**
17  * Import an AES-GCM key in order to use it with `encryptData` and `decryptData`.
18  */
19 export const importKey = async (
20     key: Uint8Array,
21     keyUsage: AesGcmKeyUsage[] = ['decrypt', 'encrypt']
22 ): Promise<AesCryptoKey> => {
23     return crypto.subtle.importKey('raw', key, ENCRYPTION_ALGORITHM, false, keyUsage);
26 /**
27  * Generate key (bytes) for AES-GCM.
28  * The key needs to be imported using `importKey` to used for `encryptData` and `decryptData`.
29  */
30 export const generateKey = (): Uint8Array => crypto.getRandomValues(new Uint8Array(KEY_LENGTH_BYTES));
32 /**
33  * Use HKDF to derive AES-GCM key material from some high-entropy secret.
34  * NB: this is NOT designed to derive keys from relatively low-entropy inputs such as passwords.
35  * @param highEntropySecret - input key material for HKDF
36  * @param salt - HKDF salt
37  * @param info - context or application specific information to bind to the derived key
38  */
39 export const deriveKey = async (
40     highEntropySecret: Uint8Array,
41     salt: Uint8Array,
42     info: Uint8Array,
43     { keyUsage = ['decrypt', 'encrypt'], extractable = false }: AesGcmKeyOptions = {}
44 ) => {
45     // This is meant more as a sanity check than a security safe-guard, since entropy might still be
46     // too low even for longer inputs
47     if (highEntropySecret.length < 16) {
48         throw new Error('Unexpected HKDF input size: secret input is too short');
49     }
51     const inputKeyMaterial = await crypto.subtle.importKey('raw', highEntropySecret, 'HKDF', false, ['deriveKey']);
53     return crypto.subtle.deriveKey(
54         { name: 'HKDF', salt, info, hash: 'SHA-256' },
55         inputKeyMaterial,
56         { name: ENCRYPTION_ALGORITHM, length: KEY_LENGTH_BYTES * 8 },
57         extractable,
58         keyUsage
59     );
62 /**
63  * Encrypt data using AES-GCM
64  * @param key - WebCrypto key for encryption
65  * @param data - data to encrypt
66  * @param additionalData - additional data to authenticate
67  */
68 export const encryptData = async (key: AesCryptoKey, data: Uint8Array, additionalData?: Uint8Array) => {
69     const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH_BYTES));
70     const ciphertext = await crypto.subtle.encrypt(
71         { name: ENCRYPTION_ALGORITHM, iv, ...(additionalData !== undefined ? { additionalData } : undefined) },
72         key,
73         data
74     );
76     return mergeUint8Arrays([iv, new Uint8Array(ciphertext)]);
79 /**
80  * Decrypt data using AES-GCM
81  * @param key - WebCrypto key for decryption
82  * @param data - ciphertext to decrypt
83  * @param additionalData - additional authenticated data
84  * @param with16ByteIV - whether a non-standard IV size of 16 bytes was used on encryption
85  */
86 export const decryptData = async (
87     key: AesCryptoKey,
88     data: Uint8Array,
89     additionalData?: Uint8Array,
90     with16ByteIV = false
91 ) => {
92     const ivLength = with16ByteIV ? 16 : IV_LENGTH_BYTES;
93     const iv = data.slice(0, ivLength);
94     const ciphertext = data.slice(ivLength, data.length);
95     const result = await crypto.subtle.decrypt(
96         { name: ENCRYPTION_ALGORITHM, iv, ...(additionalData !== undefined ? { additionalData } : undefined) },
97         key,
98         ciphertext
99     );
101     return new Uint8Array(result);
105  * DEPRECATED: use `encryptData` instead.
106  * This function encrypts using a non-standard IV of 16 bytes.
107  * @param key - WebCrypto key for encryption
108  * @param data - data to encrypt
109  * @param additionalData - additional data to authenticate
110  * @deprecated use `encryptData` instead; this helper is kept around for legacy use-cases only.
111  */
112 export const encryptDataWith16ByteIV = async (key: AesCryptoKey, data: Uint8Array, additionalData?: Uint8Array) => {
113     const ivLength = 16;
114     // A random 16-byte IV is non-standard, but it does not negatively affect the max number of encryptable messages using the same key,
115     // nor does it increase the risk of nonce collision; see summary at https://crypto.stackexchange.com/a/80390.
116     const iv = crypto.getRandomValues(new Uint8Array(ivLength));
117     const ciphertext = await crypto.subtle.encrypt(
118         { name: ENCRYPTION_ALGORITHM, iv, ...(additionalData !== undefined ? { additionalData } : undefined) },
119         key,
120         data
121     );
123     return mergeUint8Arrays([iv, new Uint8Array(ciphertext)]);