1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

[PM-17984] Remove AES128CBC-HMAC encryption (#13304)

* Remove AES128CBC-HMAC encryption

* Increase test coverage
This commit is contained in:
Bernd Schoolmann
2025-03-11 14:20:02 +01:00
committed by GitHub
parent 5cd47ac907
commit 9683779dbf
10 changed files with 50 additions and 105 deletions

View File

@@ -36,7 +36,6 @@ export abstract class EncryptService {
): Promise<Uint8Array | null>;
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array>;
abstract resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey;
/**
* @deprecated Replaced by BulkEncryptService, remove once the feature is tested and the featureflag PM-4154-multi-worker-encryption-service is removed
* @param items The items to decrypt

View File

@@ -78,8 +78,6 @@ export class EncryptServiceImplementation implements EncryptService {
throw new Error("No key provided for decryption.");
}
key = this.resolveLegacyKey(key, encString);
// DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality.
if (key.macKey != null && encString?.mac == null) {
this.logService.error(
@@ -145,8 +143,6 @@ export class EncryptServiceImplementation implements EncryptService {
throw new Error("Nothing provided for decryption.");
}
key = this.resolveLegacyKey(key, encThing);
// DO NOT REMOVE OR MOVE. This prevents downgrade to mac-less CBC, which would compromise integrity and confidentiality.
if (key.macKey != null && encThing.macBytes == null) {
this.logService.error(
@@ -298,19 +294,4 @@ export class EncryptServiceImplementation implements EncryptService {
this.logService.error(msg);
}
}
/**
* Transform into new key for the old encrypt-then-mac scheme if required, otherwise return the current key unchanged
* @param encThing The encrypted object (e.g. encString or encArrayBuffer) that you want to decrypt
*/
resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey {
if (
encThing.encryptionType === EncryptionType.AesCbc128_HmacSha256_B64 &&
key.encType === EncryptionType.AesCbc256_B64
) {
return new SymmetricCryptoKey(key.key, EncryptionType.AesCbc128_HmacSha256_B64);
}
return key;
}
}

View File

@@ -325,6 +325,25 @@ describe("EncryptService", () => {
});
});
describe("decryptToUtf8", () => {
it("throws if no key is provided", () => {
return expect(encryptService.decryptToUtf8(null, null)).rejects.toThrow(
"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,
);
const encString = new EncString(EncryptionType.AesCbc256_B64, "data");
const actual = await encryptService.decryptToUtf8(encString, key);
expect(actual).toBeNull();
expect(logService.error).toHaveBeenCalled();
});
});
describe("rsa", () => {
const data = makeStaticByteArray(10, 100);
const encryptedData = makeStaticByteArray(10, 150);
@@ -370,17 +389,16 @@ describe("EncryptService", () => {
return expect(encryptService.rsaDecrypt(encString, null)).rejects.toThrow("No private key");
});
it.each([
EncryptionType.AesCbc256_B64,
EncryptionType.AesCbc128_HmacSha256_B64,
EncryptionType.AesCbc256_HmacSha256_B64,
])("throws if encryption type is %s", async (encType) => {
encString.encryptionType = encType;
it.each([EncryptionType.AesCbc256_B64, EncryptionType.AesCbc256_HmacSha256_B64])(
"throws if encryption type is %s",
async (encType) => {
encString.encryptionType = encType;
await expect(encryptService.rsaDecrypt(encString, privateKey)).rejects.toThrow(
"Invalid encryption type",
);
});
await expect(encryptService.rsaDecrypt(encString, privateKey)).rejects.toThrow(
"Invalid encryption type",
);
},
);
it("decrypts data with provided key", async () => {
cryptoFunctionService.rsaDecrypt.mockResolvedValue(data);
@@ -398,30 +416,6 @@ describe("EncryptService", () => {
});
});
describe("resolveLegacyKey", () => {
it("creates a legacy key if required", async () => {
const key = new SymmetricCryptoKey(makeStaticByteArray(32), EncryptionType.AesCbc256_B64);
const encString = mock<EncString>();
encString.encryptionType = EncryptionType.AesCbc128_HmacSha256_B64;
const actual = encryptService.resolveLegacyKey(key, encString);
const expected = new SymmetricCryptoKey(key.key, EncryptionType.AesCbc128_HmacSha256_B64);
expect(actual).toEqual(expected);
});
it("does not create a legacy key if not required", async () => {
const encType = EncryptionType.AesCbc256_HmacSha256_B64;
const key = new SymmetricCryptoKey(makeStaticByteArray(64), encType);
const encString = mock<EncString>();
encString.encryptionType = encType;
const actual = encryptService.resolveLegacyKey(key, encString);
expect(actual).toEqual(key);
});
});
describe("hash", () => {
it("hashes a string and returns b64", async () => {
cryptoFunctionService.hash.mockResolvedValue(Uint8Array.from([1, 2, 3]));