From 05106055e6450890368328e3f698cd62c2f42770 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 27 Feb 2025 12:50:38 +0100 Subject: [PATCH] Remove SymmetricCryptoKey buffer representation --- ...rganization-user-reset-password.service.ts | 2 +- .../rotateable-key-set.service.spec.ts | 3 +- .../services/rotateable-key-set.service.ts | 10 ++++- .../auth/components/set-password.component.ts | 5 ++- .../default-set-password-jit.service.spec.ts | 2 +- .../default-set-password-jit.service.ts | 2 +- .../auth-request/auth-request.service.spec.ts | 4 +- .../auth-request/auth-request.service.ts | 2 +- .../pin/pin.service.implementation.ts | 9 +++-- .../common/services/pin/pin.service.spec.ts | 8 ++-- .../device-trust.service.implementation.ts | 4 +- .../services/device-trust.service.spec.ts | 6 +-- .../master-password.service.ts | 7 +++- ...-enrollment.service.implementation.spec.ts | 12 +++++- ...reset-enrollment.service.implementation.ts | 2 +- .../webauthn-login-prf-key.service.spec.ts | 4 +- .../abstractions/key-generation.service.ts | 4 +- .../domain/symmetric-crypto-key.spec.ts | 2 - .../models/domain/symmetric-crypto-key.ts | 3 -- .../services/key-generation.service.spec.ts | 9 +++-- .../services/key-generation.service.ts | 18 +++++++-- .../src/vault/services/cipher.service.ts | 4 +- libs/key-management/src/key.service.spec.ts | 4 +- libs/key-management/src/key.service.ts | 39 ++++++++++++------- 24 files changed, 104 insertions(+), 61 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index 8e583d3106c..61fdff636c6 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -60,7 +60,7 @@ export class OrganizationUserResetPasswordService if (userKey == null) { throw new Error("No user key found"); } - const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey); + const encryptedKey = await this.encryptService.rsaEncrypt(userKey.toEncoded(), publicKey); return encryptedKey.encryptedString; } diff --git a/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts b/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts index 1241ea88fe9..c1caf156195 100644 --- a/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts +++ b/apps/web/src/app/auth/core/services/rotateable-key-set.service.spec.ts @@ -4,6 +4,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { UserKey } from "@bitwarden/common/types/key"; import { KeyService } from "@bitwarden/key-management"; import { RotateableKeySetService } from "./rotateable-key-set.service"; @@ -34,7 +35,7 @@ describe("RotateableKeySetService", () => { const encryptedPublicKey = Symbol(); const encryptedPrivateKey = Symbol(); keyService.makeKeyPair.mockResolvedValue(["publicKey", encryptedPrivateKey as any]); - keyService.getUserKey.mockResolvedValue({ key: userKey.key } as any); + keyService.getUserKey.mockResolvedValue(userKey as UserKey); encryptService.rsaEncrypt.mockResolvedValue(encryptedUserKey as any); encryptService.encrypt.mockResolvedValue(encryptedPublicKey as any); diff --git a/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts b/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts index 64a8bdfbfb4..fc666fb65ad 100644 --- a/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts +++ b/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts @@ -25,7 +25,10 @@ export class RotateableKeySetService { const userKey = await this.keyService.getUserKey(); const rawPublicKey = Utils.fromB64ToArray(publicKey); - const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.key, rawPublicKey); + const encryptedUserKey = await this.encryptService.rsaEncrypt( + userKey.toEncoded(), + rawPublicKey, + ); const encryptedPublicKey = await this.encryptService.encrypt(rawPublicKey, userKey); return new RotateableKeySet(encryptedUserKey, encryptedPublicKey, encryptedPrivateKey); } @@ -60,7 +63,10 @@ export class RotateableKeySetService { throw new Error("failed to rotate key set: could not decrypt public key"); } const newEncryptedPublicKey = await this.encryptService.encrypt(publicKey, newUserKey); - const newEncryptedUserKey = await this.encryptService.rsaEncrypt(newUserKey.key, publicKey); + const newEncryptedUserKey = await this.encryptService.rsaEncrypt( + newUserKey.toEncoded(), + publicKey, + ); const newRotateableKeySet = new RotateableKeySet( newEncryptedUserKey, diff --git a/libs/angular/src/auth/components/set-password.component.ts b/libs/angular/src/auth/components/set-password.component.ts index de079a7ebca..817d54eef51 100644 --- a/libs/angular/src/auth/components/set-password.component.ts +++ b/libs/angular/src/auth/components/set-password.component.ts @@ -209,7 +209,10 @@ export class SetPasswordComponent extends BaseChangePasswordComponent implements // RSA Encrypt user key with organization public key const userKey = await this.keyService.getUserKey(); - const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey); + const encryptedUserKey = await this.encryptService.rsaEncrypt( + userKey.toEncoded(), + publicKey, + ); const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest(); resetRequest.masterPasswordHash = masterPasswordHash; diff --git a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts index 726110663fc..56849c4d638 100644 --- a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts +++ b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.spec.ts @@ -212,7 +212,7 @@ describe("DefaultSetPasswordJitService", () => { // Assert expect(apiService.setPassword).toHaveBeenCalledWith(setPasswordRequest); expect(organizationApiService.getKeys).toHaveBeenCalledWith(orgId); - expect(encryptService.rsaEncrypt).toHaveBeenCalledWith(userKey.key, orgPublicKey); + expect(encryptService.rsaEncrypt).toHaveBeenCalledWith(userKey.toEncoded(), orgPublicKey); expect( organizationUserApiService.putOrganizationUserResetPasswordEnrollment, ).toHaveBeenCalled(); diff --git a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts index 6c9ce8f9267..d6236fedea6 100644 --- a/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts +++ b/libs/auth/src/angular/set-password-jit/default-set-password-jit.service.ts @@ -159,7 +159,7 @@ export class DefaultSetPasswordJitService implements SetPasswordJitService { throw new Error("userKey not found. Could not handle reset password auto enroll."); } - const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.key, publicKey); + const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.toEncoded(), publicKey); const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest(); resetRequest.masterPasswordHash = masterKeyHash; diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts index 76b965511a8..fe086716f5d 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.spec.ts @@ -117,7 +117,9 @@ describe("AuthRequestService", () => { }); it("should use the user key if the master key and hash do not exist", async () => { - keyService.getUserKey.mockResolvedValueOnce({ key: new Uint8Array(64) } as UserKey); + keyService.getUserKey.mockResolvedValueOnce( + new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, + ); await sut.approveOrDenyAuthRequest( true, diff --git a/libs/auth/src/common/services/auth-request/auth-request.service.ts b/libs/auth/src/common/services/auth-request/auth-request.service.ts index 41b3d4805f4..e571482ce3d 100644 --- a/libs/auth/src/common/services/auth-request/auth-request.service.ts +++ b/libs/auth/src/common/services/auth-request/auth-request.service.ts @@ -117,7 +117,7 @@ export class AuthRequestService implements AuthRequestServiceAbstraction { keyToEncrypt = (masterKey.inner() as Aes256CbcKey).encryptionKey; } else { const userKey = await this.keyService.getUserKey(); - keyToEncrypt = userKey.key; + keyToEncrypt = userKey.toEncoded(); } const encryptedKey = await this.encryptService.rsaEncrypt(keyToEncrypt, pubKey); diff --git a/libs/auth/src/common/services/pin/pin.service.implementation.ts b/libs/auth/src/common/services/pin/pin.service.implementation.ts index c903a45b3a2..0c0737b4c4c 100644 --- a/libs/auth/src/common/services/pin/pin.service.implementation.ts +++ b/libs/auth/src/common/services/pin/pin.service.implementation.ts @@ -10,7 +10,10 @@ import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/ke import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { EncString, EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { + Aes256CbcKey, + SymmetricCryptoKey, +} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { PIN_DISK, PIN_MEMORY, @@ -193,7 +196,7 @@ export class PinService implements PinServiceAbstraction { const pinKey = await this.makePinKey(pin, email, kdfConfig); - return await this.encryptService.encrypt(userKey.key, pinKey); + return await this.encryptService.encrypt(userKey.toEncoded(), pinKey); } async storePinKeyEncryptedUserKey( @@ -258,7 +261,7 @@ export class PinService implements PinServiceAbstraction { async makePinKey(pin: string, salt: string, kdfConfig: KdfConfig): Promise { const pinKey = await this.keyGenerationService.deriveKeyFromPassword(pin, salt, kdfConfig); - return (await this.keyGenerationService.stretchKey(pinKey)) as PinKey; + return (await this.keyGenerationService.stretchKey(pinKey.inner() as Aes256CbcKey)) as PinKey; } async getPinLockType(userId: UserId): Promise { diff --git a/libs/auth/src/common/services/pin/pin.service.spec.ts b/libs/auth/src/common/services/pin/pin.service.spec.ts index 1d6443535bc..f86c42b204c 100644 --- a/libs/auth/src/common/services/pin/pin.service.spec.ts +++ b/libs/auth/src/common/services/pin/pin.service.spec.ts @@ -189,7 +189,7 @@ describe("PinService", () => { await sut.createPinKeyEncryptedUserKey(mockPin, mockUserKey, mockUserId); // Assert - expect(encryptService.encrypt).toHaveBeenCalledWith(mockUserKey.key, mockPinKey); + expect(encryptService.encrypt).toHaveBeenCalledWith(mockUserKey.toEncoded(), mockPinKey); }); }); @@ -327,7 +327,7 @@ describe("PinService", () => { mockUserEmail, DEFAULT_KDF_CONFIG, ); - expect(keyGenerationService.stretchKey).toHaveBeenCalledWith(mockPinKey); + expect(keyGenerationService.stretchKey).toHaveBeenCalledWith(mockPinKey.inner()); }); }); @@ -499,7 +499,7 @@ describe("PinService", () => { async function mockDecryptAndMigrateOldPinKeyEncryptedMasterKeyFn() { sut.makePinKey = jest.fn().mockResolvedValue(mockPinKey); - encryptService.decryptToBytes.mockResolvedValue(mockMasterKey.key); + encryptService.decryptToBytes.mockResolvedValue(mockMasterKey.toEncoded()); stateService.getEncryptedCryptoSymmetricKey.mockResolvedValue(mockUserKey.keyB64); masterPasswordService.mock.decryptUserKeyWithMasterKey.mockResolvedValue(mockUserKey); @@ -521,7 +521,7 @@ describe("PinService", () => { .fn() .mockResolvedValue(pinKeyEncryptedUserKeyPersistant); sut.makePinKey = jest.fn().mockResolvedValue(mockPinKey); - encryptService.decryptToBytes.mockResolvedValue(mockUserKey.key); + encryptService.decryptToBytes.mockResolvedValue(mockUserKey.toEncoded()); } function mockPinEncryptedKeyDataByPinLockType( diff --git a/libs/common/src/auth/services/device-trust.service.implementation.ts b/libs/common/src/auth/services/device-trust.service.implementation.ts index fe43df53c48..0c516ad3263 100644 --- a/libs/common/src/auth/services/device-trust.service.implementation.ts +++ b/libs/common/src/auth/services/device-trust.service.implementation.ts @@ -156,7 +156,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { deviceKeyEncryptedDevicePrivateKey, ] = await Promise.all([ // Encrypt user key with the DevicePublicKey - this.encryptService.rsaEncrypt(userKey.key, devicePublicKey), + this.encryptService.rsaEncrypt(userKey.toEncoded(), devicePublicKey), // Encrypt devicePublicKey with user key this.encryptService.encrypt(devicePublicKey, userKey), @@ -224,7 +224,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { // Encrypt the brand new user key with the now-decrypted public key for the device const encryptedNewUserKey = await this.encryptService.rsaEncrypt( - newUserKey.key, + newUserKey.toEncoded(), decryptedDevicePublicKey, ); diff --git a/libs/common/src/auth/services/device-trust.service.spec.ts b/libs/common/src/auth/services/device-trust.service.spec.ts index e689c93395d..33197bfe09e 100644 --- a/libs/common/src/auth/services/device-trust.service.spec.ts +++ b/libs/common/src/auth/services/device-trust.service.spec.ts @@ -731,8 +731,8 @@ describe("deviceTrustService", () => { // Mock the decryption of the public key with the old user key encryptService.decryptToBytes.mockImplementationOnce((_encValue, privateKeyValue) => { - expect(privateKeyValue.key.byteLength).toBe(64); - expect(new Uint8Array(privateKeyValue.key)[0]).toBe(FakeOldUserKeyMarker); + expect(privateKeyValue.inner().type).toBe(EncryptionType.AesCbc256_HmacSha256_B64); + expect(new Uint8Array(privateKeyValue.toEncoded())[0]).toBe(FakeOldUserKeyMarker); const data = new Uint8Array(250); data.fill(FakeDecryptedPublicKeyMarker, 0, 1); return Promise.resolve(data); @@ -752,7 +752,7 @@ describe("deviceTrustService", () => { expect(plainValue).toBeInstanceOf(Uint8Array); expect(new Uint8Array(plainValue as Uint8Array)[0]).toBe(FakeDecryptedPublicKeyMarker); - expect(new Uint8Array(key.key)[0]).toBe(FakeNewUserKeyMarker); + expect(new Uint8Array(key.toEncoded())[0]).toBe(FakeNewUserKeyMarker); return Promise.resolve( new EncString("2.ZW5jcnlwdGVkcHVibGlj|ZW5jcnlwdGVkcHVibGlj|ZW5jcnlwdGVkcHVibGlj"), ); diff --git a/libs/common/src/auth/services/master-password/master-password.service.ts b/libs/common/src/auth/services/master-password/master-password.service.ts index 9b5ce588bd3..22ea0addbef 100644 --- a/libs/common/src/auth/services/master-password/master-password.service.ts +++ b/libs/common/src/auth/services/master-password/master-password.service.ts @@ -8,7 +8,10 @@ import { LogService } from "../../../platform/abstractions/log.service"; import { StateService } from "../../../platform/abstractions/state.service"; import { EncryptionType } from "../../../platform/enums"; import { EncryptedString, EncString } from "../../../platform/models/domain/enc-string"; -import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; +import { + Aes256CbcKey, + SymmetricCryptoKey, +} from "../../../platform/models/domain/symmetric-crypto-key"; import { MASTER_PASSWORD_DISK, MASTER_PASSWORD_MEMORY, @@ -185,7 +188,7 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr "Content: User Key; Encrypting Key: Master Key", ); } else if (userKey.encryptionType === EncryptionType.AesCbc256_HmacSha256_B64) { - const newKey = await this.keyGenerationService.stretchKey(masterKey); + const newKey = await this.keyGenerationService.stretchKey(masterKey.inner() as Aes256CbcKey); decUserKey = await this.encryptService.decryptToBytes( userKey, newKey, diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts index 8516400fe09..51df99c45e1 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.spec.ts @@ -2,6 +2,8 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { UserKey } from "@bitwarden/common/types/key"; import { KeyService } from "@bitwarden/key-management"; import { OrganizationApiServiceAbstraction } from "../../admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -99,7 +101,9 @@ describe("PasswordResetEnrollmentServiceImplementation", () => { }; activeAccountSubject.next(Object.assign(user1AccountInfo, { id: "userId" as UserId })); - keyService.getUserKey.mockResolvedValue({ key: "key" } as any); + keyService.getUserKey.mockResolvedValue( + new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, + ); encryptService.rsaEncrypt.mockResolvedValue(encryptedKey as any); await service.enroll("orgId"); @@ -124,7 +128,11 @@ describe("PasswordResetEnrollmentServiceImplementation", () => { organizationApiService.getKeys.mockResolvedValue(orgKeyResponse as any); encryptService.rsaEncrypt.mockResolvedValue(encryptedKey as any); - await service.enroll("orgId", "userId", { key: "key" } as any); + await service.enroll( + "orgId", + "userId", + new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, + ); expect( organizationUserApiService.putOrganizationUserResetPasswordEnrollment, diff --git a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts index 824521d8a2e..d7cf530fb85 100644 --- a/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts +++ b/libs/common/src/auth/services/password-reset-enrollment.service.implementation.ts @@ -51,7 +51,7 @@ export class PasswordResetEnrollmentServiceImplementation userId ?? (await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)))); userKey = userKey ?? (await this.keyService.getUserKey(userId)); // RSA Encrypt user's userKey.key with organization public key - const encryptedKey = await this.encryptService.rsaEncrypt(userKey.key, orgPublicKey); + const encryptedKey = await this.encryptService.rsaEncrypt(userKey.toEncoded(), orgPublicKey); const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest(); resetRequest.resetPasswordKey = encryptedKey.encryptedString; diff --git a/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.spec.ts b/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.spec.ts index f1fe07a996f..24ace8c1d87 100644 --- a/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.spec.ts +++ b/libs/common/src/auth/services/webauthn-login/webauthn-login-prf-key.service.spec.ts @@ -1,5 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; +import { EncryptionType } from "@bitwarden/common/platform/enums"; + import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service"; import { WebAuthnLoginPrfKeyService } from "./webauthn-login-prf-key.service"; @@ -21,7 +23,7 @@ describe("WebAuthnLoginPrfKeyService", () => { const result = await service.createSymmetricKeyFromPrf(randomBytes(32)); - expect(result.key.length).toBe(64); + expect(result.inner().type).toBe(EncryptionType.AesCbc256_HmacSha256_B64); }); }); }); diff --git a/libs/common/src/platform/abstractions/key-generation.service.ts b/libs/common/src/platform/abstractions/key-generation.service.ts index 8314efe3469..19ed2cd8dcf 100644 --- a/libs/common/src/platform/abstractions/key-generation.service.ts +++ b/libs/common/src/platform/abstractions/key-generation.service.ts @@ -1,7 +1,7 @@ import { KdfConfig } from "@bitwarden/key-management"; import { CsprngArray } from "../../types/csprng"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { Aes256CbcKey, SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; export abstract class KeyGenerationService { /** @@ -60,5 +60,5 @@ export abstract class KeyGenerationService { * @param key 32 byte key. * @returns 64 byte derived key. */ - abstract stretchKey(key: SymmetricCryptoKey): Promise; + abstract stretchKey(key: Aes256CbcKey): Promise; } 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 2ca255d962e..492993716a5 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 @@ -19,7 +19,6 @@ describe("SymmetricCryptoKey", () => { const cryptoKey = new SymmetricCryptoKey(key); expect(cryptoKey).toEqual({ - key: key, keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=", innerKey: { type: EncryptionType.AesCbc256_B64, @@ -33,7 +32,6 @@ describe("SymmetricCryptoKey", () => { const cryptoKey = new SymmetricCryptoKey(key); expect(cryptoKey).toEqual({ - key: key, keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==", innerKey: { diff --git a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts index 010483eefd2..1e4c8e9db79 100644 --- a/libs/common/src/platform/models/domain/symmetric-crypto-key.ts +++ b/libs/common/src/platform/models/domain/symmetric-crypto-key.ts @@ -24,7 +24,6 @@ export type Aes256CbcKey = { export class SymmetricCryptoKey { private innerKey: Aes256CbcHmacKey | Aes256CbcKey; - key: Uint8Array; keyB64: string; /** @@ -40,7 +39,6 @@ export class SymmetricCryptoKey { type: EncryptionType.AesCbc256_B64, encryptionKey: key, }; - this.key = key; this.keyB64 = this.toBase64(); } else if (key.byteLength === 64) { this.innerKey = { @@ -48,7 +46,6 @@ export class SymmetricCryptoKey { encryptionKey: key.slice(0, 32), authenticationKey: key.slice(32), }; - this.key = key; this.keyB64 = this.toBase64(); } else { throw new Error(`Unsupported encType/key length ${key.byteLength}`); diff --git a/libs/common/src/platform/services/key-generation.service.spec.ts b/libs/common/src/platform/services/key-generation.service.spec.ts index 7bc547dac17..4983630a0ef 100644 --- a/libs/common/src/platform/services/key-generation.service.spec.ts +++ b/libs/common/src/platform/services/key-generation.service.spec.ts @@ -4,6 +4,7 @@ import { PBKDF2KdfConfig, Argon2KdfConfig } from "@bitwarden/key-management"; import { CsprngArray } from "../../types/csprng"; import { CryptoFunctionService } from "../abstractions/crypto-function.service"; +import { EncryptionType } from "../enums"; import { KeyGenerationService } from "./key-generation.service"; @@ -52,7 +53,7 @@ describe("KeyGenerationService", () => { expect(salt).toEqual(inputSalt); expect(material).toEqual(inputMaterial); - expect(derivedKey.key.length).toEqual(64); + expect(derivedKey.inner().type).toEqual(EncryptionType.AesCbc256_HmacSha256_B64); }, ); }); @@ -67,7 +68,7 @@ describe("KeyGenerationService", () => { const key = await sut.deriveKeyFromMaterial(material, salt, purpose); - expect(key.key.length).toEqual(64); + expect(key.inner().type).toEqual(EncryptionType.AesCbc256_HmacSha256_B64); }); }); @@ -81,7 +82,7 @@ describe("KeyGenerationService", () => { const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig); - expect(key.key.length).toEqual(32); + expect(key.inner().type).toEqual(EncryptionType.AesCbc256_B64); }); it("should derive a 32 byte key from a password using argon2id", async () => { @@ -94,7 +95,7 @@ describe("KeyGenerationService", () => { const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig); - expect(key.key.length).toEqual(32); + expect(key.inner().type).toEqual(EncryptionType.AesCbc256_B64); }); }); }); diff --git a/libs/common/src/platform/services/key-generation.service.ts b/libs/common/src/platform/services/key-generation.service.ts index 8b8ba6cb355..2e87077b48a 100644 --- a/libs/common/src/platform/services/key-generation.service.ts +++ b/libs/common/src/platform/services/key-generation.service.ts @@ -6,7 +6,7 @@ import { CsprngArray } from "../../types/csprng"; import { CryptoFunctionService } from "../abstractions/crypto-function.service"; import { KeyGenerationService as KeyGenerationServiceAbstraction } from "../abstractions/key-generation.service"; import { Utils } from "../misc/utils"; -import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; +import { Aes256CbcKey, SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; export class KeyGenerationService implements KeyGenerationServiceAbstraction { constructor(private cryptoFunctionService: CryptoFunctionService) {} @@ -78,10 +78,20 @@ export class KeyGenerationService implements KeyGenerationServiceAbstraction { return new SymmetricCryptoKey(key); } - async stretchKey(key: SymmetricCryptoKey): Promise { + async stretchKey(key: Aes256CbcKey): Promise { const newKey = new Uint8Array(64); - const encKey = await this.cryptoFunctionService.hkdfExpand(key.key, "enc", 32, "sha256"); - const macKey = await this.cryptoFunctionService.hkdfExpand(key.key, "mac", 32, "sha256"); + const encKey = await this.cryptoFunctionService.hkdfExpand( + key.encryptionKey, + "enc", + 32, + "sha256", + ); + const macKey = await this.cryptoFunctionService.hkdfExpand( + key.encryptionKey, + "mac", + 32, + "sha256", + ); newKey.set(new Uint8Array(encKey)); newKey.set(new Uint8Array(macKey), 32); diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index e66b6be0524..0e1012524b9 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -271,7 +271,7 @@ export class CipherService implements CipherServiceAbstraction { key, ).then(async () => { if (model.key != null) { - attachment.key = await this.encryptService.encrypt(model.key.key, key); + attachment.key = await this.encryptService.encrypt(model.key.toEncoded(), key); } encAttachments.push(attachment); }); @@ -1812,7 +1812,7 @@ export class CipherService implements CipherServiceAbstraction { // Then, we have to encrypt the cipher key with the proper key. cipher.key = await this.encryptService.encrypt( - decryptedCipherKey.key, + decryptedCipherKey.toEncoded(), keyForCipherKeyEncryption, ); diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts index 0b56273d3bd..a7ca9715f96 100644 --- a/libs/key-management/src/key.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -505,7 +505,7 @@ describe("keyService", () => { const output = new Uint8Array(64); output.set(encryptedPrivateKey.dataBytes); output.set( - key.key.subarray(0, 64 - encryptedPrivateKey.dataBytes.length), + key.toEncoded().subarray(0, 64 - encryptedPrivateKey.dataBytes.length), encryptedPrivateKey.dataBytes.length, ); return output; @@ -789,7 +789,7 @@ describe("keyService", () => { masterPasswordService.masterKeyHashSubject.next(storedMasterKeyHash); cryptoFunctionService.pbkdf2 - .calledWith(masterKey.key, masterPassword as string, "sha256", 2) + .calledWith(masterKey.inner().encryptionKey, masterPassword as string, "sha256", 2) .mockResolvedValue(Utils.fromB64ToArray(mockReturnedHash)); const actualDidMatch = await keyService.compareKeyHash( diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index b174087b766..39221992d13 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -24,7 +24,7 @@ import { KeyGenerationService } from "@bitwarden/common/platform/abstractions/ke import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { KeySuffixOptions, HashPurpose } from "@bitwarden/common/platform/enums"; +import { KeySuffixOptions, HashPurpose, EncryptionType } from "@bitwarden/common/platform/enums"; import { convertValues } from "@bitwarden/common/platform/misc/convert-values"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EFFLongWordList } from "@bitwarden/common/platform/misc/wordlist"; @@ -232,7 +232,7 @@ export class DefaultKeyService implements KeyServiceAbstraction { } const newUserKey = await this.keyGenerationService.createKey(512); - return this.buildProtectedSymmetricKey(masterKey, newUserKey.key); + return this.buildProtectedSymmetricKey(masterKey, newUserKey); } /** @@ -320,7 +320,7 @@ export class DefaultKeyService implements KeyServiceAbstraction { userKey?: UserKey, ): Promise<[UserKey, EncString]> { userKey ||= await this.getUserKey(); - return await this.buildProtectedSymmetricKey(masterKey, userKey.key); + return await this.buildProtectedSymmetricKey(masterKey, userKey); } // TODO: move to MasterPasswordService @@ -343,7 +343,12 @@ export class DefaultKeyService implements KeyServiceAbstraction { } const iterations = hashPurpose === HashPurpose.LocalAuthorization ? 2 : 1; - const hash = await this.cryptoFunctionService.pbkdf2(key.key, password, "sha256", iterations); + const hash = await this.cryptoFunctionService.pbkdf2( + key.inner().encryptionKey, + password, + "sha256", + iterations, + ); return Utils.fromBufferToB64(hash); } @@ -430,7 +435,7 @@ export class DefaultKeyService implements KeyServiceAbstraction { } const newSymKey = await this.keyGenerationService.createKey(512); - return this.buildProtectedSymmetricKey(key, newSymKey.key); + return this.buildProtectedSymmetricKey(key, newSymKey); } private async clearOrgKeys(userId: UserId): Promise { @@ -490,7 +495,7 @@ export class DefaultKeyService implements KeyServiceAbstraction { throw new Error("No public key found."); } - const encShareKey = await this.encryptService.rsaEncrypt(shareKey.key, publicKey); + const encShareKey = await this.encryptService.rsaEncrypt(shareKey.toEncoded(), publicKey); return [encShareKey, shareKey as T]; } @@ -821,19 +826,23 @@ export class DefaultKeyService implements KeyServiceAbstraction { } private async buildProtectedSymmetricKey( - encryptionKey: SymmetricCryptoKey, - newSymKey: Uint8Array, + protectingKey: SymmetricCryptoKey, + protectedKey: SymmetricCryptoKey, ): Promise<[T, EncString]> { let protectedSymKey: EncString; - if (encryptionKey.key.byteLength === 32) { - const stretchedEncryptionKey = await this.keyGenerationService.stretchKey(encryptionKey); - protectedSymKey = await this.encryptService.encrypt(newSymKey, stretchedEncryptionKey); - } else if (encryptionKey.key.byteLength === 64) { - protectedSymKey = await this.encryptService.encrypt(newSymKey, encryptionKey); + const protectingKeyInner = protectingKey.inner(); + if (protectingKeyInner.type === EncryptionType.AesCbc256_B64) { + const stretchedEncryptionKey = await this.keyGenerationService.stretchKey(protectingKeyInner); + protectedSymKey = await this.encryptService.encrypt( + protectedKey.toEncoded(), + stretchedEncryptionKey, + ); + } else if (protectingKeyInner.type === EncryptionType.AesCbc256_HmacSha256_B64) { + protectedSymKey = await this.encryptService.encrypt(protectedKey.toEncoded(), protectingKey); } else { - throw new Error("Invalid key size."); + throw new Error("Unsupported key type"); } - return [new SymmetricCryptoKey(newSymKey) as T, protectedSymKey]; + return [new SymmetricCryptoKey(protectedKey.toEncoded()) as T, protectedSymKey]; } // --LEGACY METHODS--