diff --git a/libs/common/src/key-management/crypto/services/encrypt.service.spec.ts b/libs/common/src/key-management/crypto/services/encrypt.service.spec.ts index faa703cbecc..0f4306e159d 100644 --- a/libs/common/src/key-management/crypto/services/encrypt.service.spec.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.service.spec.ts @@ -37,12 +37,23 @@ describe("EncryptService", () => { const actual = await encryptService.encrypt(null, key); expect(actual).toBeNull(); }); + it("creates an EncString for AesCbc256_HmacSha256_B64", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(64)); + const plainValue = "data"; + cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(32)); + cryptoFunctionService.aesEncrypt.mockResolvedValue(makeStaticByteArray(32)); + cryptoFunctionService.randomBytes.mockResolvedValue(makeStaticByteArray(16) as CsprngArray); + const result = await encryptService.encrypt(plainValue, key); + expect(cryptoFunctionService.aesEncrypt).toHaveBeenCalled(); + expect(cryptoFunctionService.hmac).toHaveBeenCalled(); + expect(Utils.fromB64ToArray(result.data).length).toEqual(32); + expect(Utils.fromB64ToArray(result.iv).length).toEqual(16); + expect(Utils.fromB64ToArray(result.mac).length).toEqual(32); + }); }); describe("encryptToBytes", () => { const plainValue = makeStaticByteArray(16, 1); - const iv = makeStaticByteArray(16, 30); - const encryptedData = makeStaticByteArray(20, 50); it("throws if no key is provided", () => { return expect(encryptService.encryptToBytes(plainValue, null)).rejects.toThrow( @@ -50,35 +61,31 @@ describe("EncryptService", () => { ); }); - describe("encrypts data", () => { - beforeEach(() => { - cryptoFunctionService.randomBytes.calledWith(16).mockResolvedValueOnce(iv as CsprngArray); - cryptoFunctionService.aesEncrypt.mockResolvedValue(encryptedData); - }); + it("encrypts data with provided key and returns correct encbuffer", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(64, 0)); + const iv = makeStaticByteArray(16, 80); + const mac = makeStaticByteArray(32, 100); + const cipherText = makeStaticByteArray(20, 150); + cryptoFunctionService.randomBytes.mockResolvedValue(iv as CsprngArray); + cryptoFunctionService.aesEncrypt.mockResolvedValue(cipherText); + cryptoFunctionService.hmac.mockResolvedValue(mac); - it("using a key which doesn't support mac", async () => { - const key = mock(); - const encType = EncryptionType.AesCbc256_B64; - key.encType = encType; + const actual = await encryptService.encryptToBytes(plainValue, key); + const expectedBytes = new Uint8Array( + 1 + iv.byteLength + mac.byteLength + cipherText.byteLength, + ); + expectedBytes.set([EncryptionType.AesCbc256_HmacSha256_B64]); + expectedBytes.set(iv, 1); + expectedBytes.set(mac, 1 + iv.byteLength); + expectedBytes.set(cipherText, 1 + iv.byteLength + mac.byteLength); - key.macKey = null; - - const actual = await encryptService.encryptToBytes(plainValue, key); - - expect(cryptoFunctionService.hmac).not.toBeCalled(); - - expect(actual.encryptionType).toEqual(encType); - expect(actual.ivBytes).toEqualBuffer(iv); - expect(actual.macBytes).toBeNull(); - expect(actual.dataBytes).toEqualBuffer(encryptedData); - expect(actual.buffer.byteLength).toEqual(1 + iv.byteLength + encryptedData.byteLength); - }); + expect(actual.buffer).toEqualBuffer(expectedBytes); }); }); describe("decryptToBytes", () => { const encType = EncryptionType.AesCbc256_HmacSha256_B64; - const key = new SymmetricCryptoKey(makeStaticByteArray(64, 100), encType); + const key = new SymmetricCryptoKey(makeStaticByteArray(64, 100)); const computedMac = new Uint8Array(1); const encBuffer = new EncArrayBuffer(makeStaticByteArray(60, encType)); @@ -98,7 +105,28 @@ describe("EncryptService", () => { ); }); - it("decrypts data with provided key", async () => { + it("decrypts data with provided key for AesCbc256Hmac", async () => { + const decryptedBytes = makeStaticByteArray(10, 200); + + cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(1)); + cryptoFunctionService.compare.mockResolvedValue(true); + cryptoFunctionService.aesDecrypt.mockResolvedValueOnce(decryptedBytes); + + const actual = await encryptService.decryptToBytes(encBuffer, key); + + expect(cryptoFunctionService.aesDecrypt).toBeCalledWith( + expect.toEqualBuffer(encBuffer.dataBytes), + expect.toEqualBuffer(encBuffer.ivBytes), + expect.toEqualBuffer(key.encKey), + "cbc", + ); + + expect(actual).toEqualBuffer(decryptedBytes); + }); + + it("decrypts data with provided key for AesCbc256", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(32, 0)); + const encBuffer = new EncArrayBuffer(makeStaticByteArray(60, EncryptionType.AesCbc256_B64)); const decryptedBytes = makeStaticByteArray(10, 200); cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(1)); @@ -147,8 +175,17 @@ describe("EncryptService", () => { expect(actual).toBeNull(); }); - it("returns null if encTypes don't match", async () => { - key.encType = EncryptionType.AesCbc256_B64; + it("returns null if mac could not be calculated", async () => { + cryptoFunctionService.hmac.mockResolvedValue(null); + + const actual = await encryptService.decryptToBytes(encBuffer, key); + expect(cryptoFunctionService.hmac).toHaveBeenCalled(); + expect(cryptoFunctionService.aesDecrypt).not.toHaveBeenCalled(); + expect(actual).toBeNull(); + }); + + it("returns null if key is AesCbc256 but encbuffer is AesCbc256_HMAC", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(32, 0)); cryptoFunctionService.compare.mockResolvedValue(true); const actual = await encryptService.decryptToBytes(encBuffer, key); @@ -156,6 +193,16 @@ describe("EncryptService", () => { expect(actual).toBeNull(); expect(cryptoFunctionService.aesDecrypt).not.toHaveBeenCalled(); }); + + it("returns null if key is AesCbc256_HMAC but encbuffer is AesCbc256", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(64, 0)); + cryptoFunctionService.compare.mockResolvedValue(true); + const buffer = new EncArrayBuffer(makeStaticByteArray(200, EncryptionType.AesCbc256_B64)); + const actual = await encryptService.decryptToBytes(buffer, key); + + expect(actual).toBeNull(); + expect(cryptoFunctionService.aesDecrypt).not.toHaveBeenCalled(); + }); }); describe("decryptToUtf8", () => { @@ -164,17 +211,70 @@ describe("EncryptService", () => { "No key provided for decryption.", ); }); - it("returns null if key is mac key but encstring has no mac", async () => { - const key = new SymmetricCryptoKey( - makeStaticByteArray(64, 0), - EncryptionType.AesCbc256_HmacSha256_B64, - ); + + it("decrypts data with provided key for AesCbc256_HmacSha256", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(64, 0)); + const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac"); + cryptoFunctionService.aesDecryptFastParameters.mockReturnValue({ + macData: makeStaticByteArray(32, 0), + macKey: makeStaticByteArray(32, 0), + mac: makeStaticByteArray(32, 0), + } as any); + cryptoFunctionService.hmacFast.mockResolvedValue(makeStaticByteArray(32, 0)); + cryptoFunctionService.compareFast.mockResolvedValue(true); + cryptoFunctionService.aesDecryptFast.mockResolvedValue("data"); + + const actual = await encryptService.decryptToUtf8(encString, key); + expect(actual).toEqual("data"); + expect(cryptoFunctionService.compareFast).toHaveBeenCalled(); + }); + + it("decrypts data with provided key for AesCbc256", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(32, 0)); + const encString = new EncString(EncryptionType.AesCbc256_B64, "data"); + cryptoFunctionService.aesDecryptFastParameters.mockReturnValue({} as any); + cryptoFunctionService.hmacFast.mockResolvedValue(makeStaticByteArray(32, 0)); + cryptoFunctionService.compareFast.mockResolvedValue(true); + cryptoFunctionService.aesDecryptFast.mockResolvedValue("data"); + + const actual = await encryptService.decryptToUtf8(encString, key); + expect(actual).toEqual("data"); + expect(cryptoFunctionService.compareFast).not.toHaveBeenCalled(); + }); + + it("returns null if key is Aes256-Cbc-Hmac but EncString is Aes256-Cbc", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(64, 0)); const encString = new EncString(EncryptionType.AesCbc256_B64, "data"); const actual = await encryptService.decryptToUtf8(encString, key); expect(actual).toBeNull(); expect(logService.error).toHaveBeenCalled(); }); + + it("returns null if key is AesCbc256 but encstring is AesCbc256_HMAC", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(32, 0)); + const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac"); + + const actual = await encryptService.decryptToUtf8(encString, key); + expect(actual).toBeNull(); + expect(logService.error).toHaveBeenCalled(); + }); + + it("returns null if macs don't match", async () => { + const key = new SymmetricCryptoKey(makeStaticByteArray(64, 0)); + const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac"); + cryptoFunctionService.aesDecryptFastParameters.mockReturnValue({ + macData: makeStaticByteArray(32, 0), + macKey: makeStaticByteArray(32, 0), + mac: makeStaticByteArray(32, 0), + } as any); + cryptoFunctionService.hmacFast.mockResolvedValue(makeStaticByteArray(32, 0)); + cryptoFunctionService.compareFast.mockResolvedValue(false); + cryptoFunctionService.aesDecryptFast.mockResolvedValue("data"); + + const actual = await encryptService.decryptToUtf8(encString, key); + expect(actual).toBeNull(); + }); }); describe("rsa", () => { @@ -248,4 +348,31 @@ describe("EncryptService", () => { }); }); }); + + describe("hash", () => { + it("hashes a string and returns b64", async () => { + cryptoFunctionService.hash.mockResolvedValue(Uint8Array.from([1, 2, 3])); + expect(await encryptService.hash("test", "sha256")).toEqual("AQID"); + expect(cryptoFunctionService.hash).toHaveBeenCalledWith("test", "sha256"); + }); + }); + + describe("decryptItems", () => { + it("returns empty array if no items are provided", async () => { + const key = mock(); + const actual = await encryptService.decryptItems(null, key); + expect(actual).toEqual([]); + }); + + it("returns items decrypted with provided key", async () => { + const key = mock(); + const decryptable = { + decrypt: jest.fn().mockResolvedValue("decrypted"), + }; + const items = [decryptable]; + const actual = await encryptService.decryptItems(items as any, key); + expect(actual).toEqual(["decrypted"]); + expect(decryptable.decrypt).toHaveBeenCalledWith(key); + }); + }); }); diff --git a/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts b/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts index 0186e2ebe24..58c902ebab6 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.spec.ts @@ -1,4 +1,5 @@ import { makeStaticByteArray } from "../../../../spec"; +import { EncryptionType } from "../../enums"; import { SymmetricCryptoKey } from "./symmetric-crypto-key"; @@ -19,7 +20,7 @@ describe("SymmetricCryptoKey", () => { expect(cryptoKey).toEqual({ encKey: key, encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", - encType: 0, + encType: EncryptionType.AesCbc256_B64, key: key, keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", macKey: null, @@ -33,7 +34,7 @@ describe("SymmetricCryptoKey", () => { expect(cryptoKey).toEqual({ encKey: key.slice(0, 32), encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", - encType: 2, + encType: EncryptionType.AesCbc256_HmacSha256_B64, key: key, keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==", @@ -67,4 +68,21 @@ describe("SymmetricCryptoKey", () => { expect(actual).toEqual(expected); expect(actual).toBeInstanceOf(SymmetricCryptoKey); }); + + describe("fromString", () => { + it("null string returns null", () => { + const actual = SymmetricCryptoKey.fromString(null); + + expect(actual).toBeNull(); + }); + + it("base64 string creates object", () => { + const key = makeStaticByteArray(64); + const expected = new SymmetricCryptoKey(key); + const actual = SymmetricCryptoKey.fromString(expected.keyB64); + + expect(actual).toEqual(expected); + expect(actual).toBeInstanceOf(SymmetricCryptoKey); + }); + }); });