diff --git a/libs/common/src/platform/abstractions/encrypt.service.ts b/libs/common/src/platform/abstractions/encrypt.service.ts index 5b28b98803b..dd6457d14b7 100644 --- a/libs/common/src/platform/abstractions/encrypt.service.ts +++ b/libs/common/src/platform/abstractions/encrypt.service.ts @@ -14,6 +14,17 @@ export abstract class EncryptService { decryptContext?: string, ): Promise; abstract decryptToBytes(encThing: Encrypted, key: SymmetricCryptoKey): Promise; + /** + * Decrypts aes gcm encrypted data given a key. + * + * The data is expected to be a concatenation of cipherText, tag, and iv in that order. + * Tag is required to be 16 bytes. + * iv is required to be 12 bytes + * + * @param data The encrypted data + tag + iv + * @param key The key to decrypt the data + */ + abstract aesGcmDecryptToBytes(data: Uint8Array, key: Uint8Array): Promise; abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise; abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise; abstract resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey; diff --git a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts b/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts index 137d67ca0f0..fb481d45386 100644 --- a/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts +++ b/libs/common/src/platform/services/cryptography/encrypt.service.implementation.ts @@ -126,6 +126,23 @@ export class EncryptServiceImplementation implements EncryptService { return await this.cryptoFunctionService.aesDecryptFast(fastParams, "cbc"); } + async aesGcmDecryptToBytes(data: Uint8Array, key: Uint8Array): Promise { + if (key == null) { + throw new Error("No encryption key provided."); + } + + if (data == null) { + throw new Error("Nothing provided for decryption."); + } + + // split data into cipherText + tag and iv + const dataAndTag = data.slice(0, -12); // 12 bytes is the iv length + const iv = data.slice(-12); + + // aesDecrypt expects cipher + tag, but iv split + return await this.cryptoFunctionService.aesDecrypt(dataAndTag, iv, key, "gcm"); + } + async decryptToBytes(encThing: Encrypted, key: SymmetricCryptoKey): Promise { if (key == null) { throw new Error("No encryption key provided."); diff --git a/libs/common/src/platform/services/encrypt.service.spec.ts b/libs/common/src/platform/services/encrypt.service.spec.ts index 609b5100a10..bd97b24594a 100644 --- a/libs/common/src/platform/services/encrypt.service.spec.ts +++ b/libs/common/src/platform/services/encrypt.service.spec.ts @@ -82,6 +82,41 @@ describe("EncryptService", () => { }); }); + describe("aesGcmDecryptToBytes", () => { + const data = makeStaticByteArray(10, 100); + const key = makeStaticByteArray(32, 200); + const encryptedData = new Uint8Array(1); + + beforeEach(() => { + cryptoFunctionService.aesDecrypt.mockResolvedValue(data); + }); + + it("throws if no data is provided", () => { + return expect(encryptService.aesGcmDecryptToBytes(null, key)).rejects.toThrow( + "Nothing provided for decryption", + ); + }); + + it("throws if no key is provided", () => { + return expect(encryptService.aesGcmDecryptToBytes(encryptedData, null)).rejects.toThrow( + "No encryption key", + ); + }); + + it("strips off the tag and decrypts data with provided key and iv", async () => { + const actual = await encryptService.aesGcmDecryptToBytes(encryptedData, key); + + expect(cryptoFunctionService.aesDecrypt).toHaveBeenCalledWith( + expect.toEqualBuffer(encryptedData.slice(0, -12)), + expect.toEqualBuffer(encryptedData.slice(-12)), + expect.toEqualBuffer(key), + "gcm", + ); + + expect(actual).toEqualBuffer(data); + }); + }); + describe("decryptToBytes", () => { const encType = EncryptionType.AesCbc256_HmacSha256_B64; const key = new SymmetricCryptoKey(makeStaticByteArray(64, 100), encType);