symKey = await this.importSymKey(symKeyStr, operation); let cryptMethod = ( operation === OPERATIONS.ENCRYPT ? crypto.subtle.encrypt : crypto.subtle.decrypt ).bind(crypto.subtle); let algo = { name: CRYPT_ALGO, iv }; let keyBytes = await cryptMethod.call(crypto.subtle, algo, symKey, data); return new Uint8Array(keyBytes); }, async generateRandomKey() { this.log("generateRandomKey() called"); let algo = { name: CRYPT_ALGO, length: CRYPT_ALGO_LENGTH, }; let key = await crypto.subtle.generateKey(algo, true, CRYPT_ALGO_USAGES); let keyBytes = await crypto.subtle.exportKey("raw", key); return this.encodeBase64(new Uint8Array(keyBytes)); }, generateRandomIV() { return this.generateRandomBytes(AES_CBC_IV_SIZE); }, generateRandomBytes(byteCount) { this.log("generateRandomBytes() called"); let randBytes = new Uint8Array(byteCount); crypto.getRandomValues(randBytes); return this.encodeBase64(randBytes); }, // // SymKey CryptoKey memoization. // // Memoize the import of symmetric keys. We do this by using the base64 // string itself as a key. _encryptionSymKeyMemo: {}, _decryptionSymKeyMemo: {}, async importSymKey(encodedKeyString, operation) { let memo; // We use two separate memos for thoroughness: operation is an input to // key import. switch (operation) { case OPERATIONS.ENCRYPT: memo = this._encryptionSymKeyMemo; break; case OPERATIONS.DECRYPT: memo = this._decryptionSymKeyMemo; break; default: throw new Error("Unsupported operation in importSymKey."); } if (encodedKeyString in memo) { return memo[encodedKeyString]; } let symmetricKeyBuffer = this.makeUint8Array(encodedKeyString, true); let algo = { name: CRYPT_ALGO }; let usages = [operation === OPERATIONS.ENCRYPT ? "encrypt" : "decrypt"]; let symKey = await crypto.subtle.importKey( "raw", symmetricKeyBuffer, algo, false, usages ); memo[encodedKeyString] = symKey; return symKey; }, // // Utility functions // /** * Returns an Uint8Array filled with a JS string, * which means we only keep utf-16 characters from 0x00 to 0xFF. */ byteCompressInts(str) { let arrayBuffer = new Uint8Array(str.length); for (let i = 0; i < str.length; i++) { arrayBuffer[i] = str.charCodeAt(i) & 0xff; } return arrayBuffer; }, expandData(data) { let expanded = ""; for (let i = 0; i < data.length; i++) { expanded += String.fromCharCode(data[i]); } return expanded; }, encodeBase64(data) { return btoa(this.expandData(data)); }, makeUint8Array(input, isEncoded) { if (isEncoded) { input = atob(input); } return this.byteCompressInts(input); }, }; PK