1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-15 16:05:03 +00:00

Add necessary gcm decryption to encryptService

This is not added as an EncString and SymmetricCryptoKey because this is part of the transport layer, not vault encryption.

TODO: This still probably needs better typing.
This commit is contained in:
Matt Gibson
2024-11-14 16:55:13 -08:00
parent b036c0ce16
commit a59512daa9
3 changed files with 63 additions and 0 deletions

View File

@@ -14,6 +14,17 @@ export abstract class EncryptService {
decryptContext?: string,
): Promise<string>;
abstract decryptToBytes(encThing: Encrypted, key: SymmetricCryptoKey): Promise<Uint8Array>;
/**
* 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<Uint8Array>;
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array>;
abstract resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey;

View File

@@ -126,6 +126,23 @@ export class EncryptServiceImplementation implements EncryptService {
return await this.cryptoFunctionService.aesDecryptFast(fastParams, "cbc");
}
async aesGcmDecryptToBytes(data: Uint8Array, key: Uint8Array): Promise<Uint8Array> {
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<Uint8Array> {
if (key == null) {
throw new Error("No encryption key provided.");

View File

@@ -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);