mirror of
https://github.com/bitwarden/browser
synced 2026-02-21 20:04:02 +00:00
Merge branch 'km/refactor-symmetric-keys-2' into km/refactor-symmetric-keys-3
This commit is contained in:
@@ -142,7 +142,7 @@ export abstract class ApiService {
|
||||
body: any,
|
||||
authed: boolean,
|
||||
hasResponse: boolean,
|
||||
apiUrl?: string,
|
||||
apiUrl?: string | null,
|
||||
alterHeaders?: (headers: Headers) => void,
|
||||
) => Promise<any>;
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ export enum FeatureFlag {
|
||||
PrivateKeyRegeneration = "pm-12241-private-key-regeneration",
|
||||
ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs",
|
||||
AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner",
|
||||
NewDeviceVerification = "new-device-verification",
|
||||
PM15179_AddExistingOrgsFromProviderPortal = "pm-15179-add-existing-orgs-from-provider-portal",
|
||||
RecoveryCodeLogin = "pm-17128-recovery-code-login",
|
||||
PM12276_BreadcrumbEventLogs = "pm-12276-breadcrumbing-for-business-features",
|
||||
@@ -104,7 +103,6 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.PrivateKeyRegeneration]: FALSE,
|
||||
[FeatureFlag.ResellerManagedOrgAlert]: FALSE,
|
||||
[FeatureFlag.AccountDeprovisioningBanner]: FALSE,
|
||||
[FeatureFlag.NewDeviceVerification]: FALSE,
|
||||
[FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal]: FALSE,
|
||||
[FeatureFlag.RecoveryCodeLogin]: FALSE,
|
||||
[FeatureFlag.PM12276_BreadcrumbEventLogs]: FALSE,
|
||||
|
||||
@@ -15,6 +15,7 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { EncryptedObject } from "@bitwarden/common/platform/models/domain/encrypted-object";
|
||||
import {
|
||||
Aes256CbcHmacKey,
|
||||
Aes256CbcKey,
|
||||
SymmetricCryptoKey,
|
||||
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
@@ -72,8 +73,13 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
encBytes.set(new Uint8Array(encValue.mac), 1 + encValue.iv.byteLength);
|
||||
encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength + macLen);
|
||||
return new EncArrayBuffer(encBytes);
|
||||
} else {
|
||||
throw new Error(`Encrypt is not supported for keys of type ${innerKey.type}`);
|
||||
} else if (innerKey.type === EncryptionType.AesCbc256_B64) {
|
||||
const encValue = await this.aesEncryptLegacy(plainValue, innerKey);
|
||||
const encBytes = new Uint8Array(1 + encValue.iv.byteLength + encValue.data.byteLength);
|
||||
encBytes.set([innerKey.type]);
|
||||
encBytes.set(new Uint8Array(encValue.iv), 1);
|
||||
encBytes.set(new Uint8Array(encValue.data), 1 + encValue.iv.byteLength);
|
||||
return new EncArrayBuffer(encBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,6 +304,16 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Removed once AesCbc256_B64 support is removed
|
||||
*/
|
||||
private async aesEncryptLegacy(data: Uint8Array, key: Aes256CbcKey): Promise<EncryptedObject> {
|
||||
const obj = new EncryptedObject();
|
||||
obj.iv = await this.cryptoFunctionService.randomBytes(16);
|
||||
obj.data = await this.cryptoFunctionService.aesEncrypt(data, obj.iv, key.encryptionKey);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private logDecryptError(
|
||||
msg: string,
|
||||
keyEncType: EncryptionType,
|
||||
|
||||
@@ -40,13 +40,7 @@ describe("EncryptService", () => {
|
||||
const actual = await encryptService.encrypt(null, key);
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
it("throws for AesCbc256_B64", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(32));
|
||||
await expect(encryptService.encrypt("data", key)).rejects.toThrow(
|
||||
"Encrypt is not supported for keys of type 0",
|
||||
);
|
||||
});
|
||||
it("creates an EncString for AesCbc256_HmacSha256_B64", async () => {
|
||||
it("creates an EncString for Aes256Cbc_HmacSha256_B64", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(64));
|
||||
const plainValue = "data";
|
||||
cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(32));
|
||||
@@ -70,15 +64,23 @@ describe("EncryptService", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("throws for an AesCbc256_B64 key", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(32, 100));
|
||||
it("encrypts data with provided Aes256Cbc key and returns correct encbuffer", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(32, 0));
|
||||
const iv = makeStaticByteArray(16, 80);
|
||||
const cipherText = makeStaticByteArray(20, 150);
|
||||
cryptoFunctionService.randomBytes.mockResolvedValue(iv as CsprngArray);
|
||||
cryptoFunctionService.aesEncrypt.mockResolvedValue(cipherText);
|
||||
|
||||
await expect(encryptService.encryptToBytes(plainValue, key)).rejects.toThrow(
|
||||
"Encrypt is not supported for keys of type 0",
|
||||
);
|
||||
const actual = await encryptService.encryptToBytes(plainValue, key);
|
||||
const expectedBytes = new Uint8Array(1 + iv.byteLength + cipherText.byteLength);
|
||||
expectedBytes.set([EncryptionType.AesCbc256_B64]);
|
||||
expectedBytes.set(iv, 1);
|
||||
expectedBytes.set(cipherText, 1 + iv.byteLength);
|
||||
|
||||
expect(actual.buffer).toEqualBuffer(expectedBytes);
|
||||
});
|
||||
|
||||
it("encrypts data with provided key and returns correct encbuffer", async () => {
|
||||
it("encrypts data with provided Aes256Cbc_HmacSha256 key and returns correct encbuffer", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(64, 0));
|
||||
const iv = makeStaticByteArray(16, 80);
|
||||
const mac = makeStaticByteArray(32, 100);
|
||||
@@ -122,7 +124,7 @@ describe("EncryptService", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("decrypts data with provided key for AesCbc256Hmac", async () => {
|
||||
it("decrypts data with provided key for Aes256CbcHmac", async () => {
|
||||
const decryptedBytes = makeStaticByteArray(10, 200);
|
||||
|
||||
cryptoFunctionService.hmac.mockResolvedValue(makeStaticByteArray(1));
|
||||
@@ -141,7 +143,7 @@ describe("EncryptService", () => {
|
||||
expect(actual).toEqualBuffer(decryptedBytes);
|
||||
});
|
||||
|
||||
it("decrypts data with provided key for AesCbc256", async () => {
|
||||
it("decrypts data with provided key for Aes256Cbc", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(32, 0));
|
||||
const encBuffer = new EncArrayBuffer(makeStaticByteArray(60, EncryptionType.AesCbc256_B64));
|
||||
const decryptedBytes = makeStaticByteArray(10, 200);
|
||||
@@ -192,7 +194,16 @@ describe("EncryptService", () => {
|
||||
expect(actual).toBeNull();
|
||||
});
|
||||
|
||||
it("returns null if key is AesCbc256 but encbuffer is AesCbc256_HMAC", async () => {
|
||||
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 Aes256Cbc but encbuffer is Aes256Cbc_HmacSha256", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(32, 0));
|
||||
cryptoFunctionService.compare.mockResolvedValue(true);
|
||||
|
||||
@@ -202,7 +213,7 @@ describe("EncryptService", () => {
|
||||
expect(cryptoFunctionService.aesDecrypt).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns null if key is AesCbc256_HMAC but encbuffer is AesCbc256", async () => {
|
||||
it("returns null if key is Aes256Cbc_HmacSha256 but encbuffer is Aes256Cbc", async () => {
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(64, 0));
|
||||
cryptoFunctionService.compare.mockResolvedValue(true);
|
||||
const buffer = new EncArrayBuffer(makeStaticByteArray(200, EncryptionType.AesCbc256_B64));
|
||||
|
||||
@@ -102,4 +102,21 @@ describe("SymmetricCryptoKey", () => {
|
||||
|
||||
expect(actual).toEqual(keyB64);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -199,3 +199,4 @@ export const NEW_DEVICE_VERIFICATION_NOTICE = new StateDefinition(
|
||||
);
|
||||
export const VAULT_APPEARANCE = new StateDefinition("vaultAppearance", "disk");
|
||||
export const SECURITY_TASKS_DISK = new StateDefinition("securityTasks", "disk");
|
||||
export const AT_RISK_PASSWORDS_PAGE_DISK = new StateDefinition("atRiskPasswordsPage", "disk");
|
||||
|
||||
@@ -1863,7 +1863,7 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
body: any,
|
||||
authed: boolean,
|
||||
hasResponse: boolean,
|
||||
apiUrl?: string,
|
||||
apiUrl?: string | null,
|
||||
alterHeaders?: (headers: Headers) => void,
|
||||
): Promise<any> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
|
||||
Reference in New Issue
Block a user