diff --git a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts index aa96f06c504..750c584510a 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.spec.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.spec.ts @@ -237,51 +237,46 @@ describe("MasterPasswordService", () => { masterPasswordAuthenticationHash: Utils.fromBufferToB64(masterKeyHash), }); }); + }); - describe("makeMasterPasswordUnlockData", () => { - const password = "test-password"; - const kdf: KdfConfig = new PBKDF2KdfConfig(600_000); - const salt = "test@bitwarden.com" as MasterPasswordSalt; - const userKey = makeSymmetricCryptoKey(32, 2) as UserKey; + describe("wrapUnwrapUserKeyWithPassword", () => { + const password = "test-password"; + const kdf: KdfConfig = new PBKDF2KdfConfig(600_000); + const salt = "test@bitwarden.com" as MasterPasswordSalt; + const userKey = makeSymmetricCryptoKey(64, 2) as UserKey; - beforeEach(() => { - // Mock makeMasterKeyWrappedUserKey to return a known value - jest - .spyOn(sut, "makeMasterKeyWrappedUserKey") - .mockResolvedValue(makeEncString("wrapped-key") as any); + it("wraps and unwraps user key with password", async () => { + const wrappedKey = await sut.makeMasterKeyWrappedUserKey(password, kdf, salt, userKey); + const unwrappedUserkey = await sut.unwrapUserKeyFromMasterPasswordUnlockData(password, { + kdf, + salt, + masterKeyWrappedUserKey: wrappedKey, }); + expect(unwrappedUserkey).toEqual(userKey); + }); + }); - it("returns MasterPasswordUnlockData with correct fields", async () => { - const result = await sut.makeMasterPasswordUnlockData(password, kdf, salt, userKey); + describe("makeMasterPasswordUnlockData", () => { + const password = "test-password"; + const kdf: KdfConfig = new PBKDF2KdfConfig(600_000); + const salt = "test@bitwarden.com" as MasterPasswordSalt; + const userKey = makeSymmetricCryptoKey(32, 2) as UserKey; - expect(sut.makeMasterKeyWrappedUserKey).toHaveBeenCalledWith( - password, - kdf, - salt, - userKey, - ); - expect(result).toEqual({ - salt, - kdf, - masterKeyWrappedUserKey: makeEncString("wrapped-key"), - }); - }); + beforeEach(() => { + // Mock makeMasterKeyWrappedUserKey to return a known value + jest + .spyOn(sut, "makeMasterKeyWrappedUserKey") + .mockResolvedValue(makeEncString("wrapped-key") as any); }); - describe("wrapUnwrapUserKeyWithPassword", () => { - const password = "test-password"; - const kdf: KdfConfig = new PBKDF2KdfConfig(600_000); - const salt = "test@bitwarden.com" as MasterPasswordSalt; - const userKey = makeSymmetricCryptoKey(64, 2) as UserKey; + it("returns MasterPasswordUnlockData with correct fields", async () => { + const result = await sut.makeMasterPasswordUnlockData(password, kdf, salt, userKey); - it("wraps and unwraps user key with password", async () => { - const wrappedKey = await sut.makeMasterKeyWrappedUserKey(password, kdf, salt, userKey); - const unwrappedUserkey = await sut.unwrapUserKeyFromMasterPasswordUnlockData(password, { - kdf, - salt, - masterKeyWrappedUserKey: wrappedKey, - }); - expect(unwrappedUserkey).toEqual(userKey); + expect(sut.makeMasterKeyWrappedUserKey).toHaveBeenCalledWith(password, kdf, salt, userKey); + expect(result).toEqual({ + salt, + kdf, + masterKeyWrappedUserKey: makeEncString("wrapped-key"), }); }); }); diff --git a/libs/common/src/key-management/master-password/services/master-password.service.ts b/libs/common/src/key-management/master-password/services/master-password.service.ts index c20d3494877..3756d20a54d 100644 --- a/libs/common/src/key-management/master-password/services/master-password.service.ts +++ b/libs/common/src/key-management/master-password/services/master-password.service.ts @@ -2,10 +2,11 @@ // @ts-strict-ignore import { firstValueFrom, map, Observable } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; // eslint-disable-next-line no-restricted-imports -import { KdfConfig } from "@bitwarden/key-management"; +import { KdfConfig, KdfConfigService } from "@bitwarden/key-management"; import { PureCrypto } from "@bitwarden/sdk-internal"; import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason"; @@ -74,6 +75,8 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr private encryptService: EncryptService, private logService: LogService, private cryptoFunctionService: CryptoFunctionService, + private kdfConfigService: KdfConfigService, + private accountService: AccountService, ) {} /** @@ -127,10 +130,27 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr throw new Error("User ID is required."); } - const masterKey = await firstValueFrom(this.masterKey$(userId)); - const userKey = await this.getMasterKeyEncryptedUserKey(userId); + const masterKeyWrappedUserKey = EncString.fromJSON( + await firstValueFrom( + this.stateProvider.getUser(userId, MASTER_KEY_ENCRYPTED_USER_KEY).state$, + ), + ) as MasterKeyWrappedUserKey; + const kdf = await firstValueFrom(this.kdfConfigService.getKdfConfig$(userId)); + const salt = this.emailToSalt( + await firstValueFrom( + this.accountService.accounts$.pipe(map((accounts) => accounts[userId].email)), + ), + ); - return this.decryptUserKeyWithMasterKey(masterKey, userId, userKey); + return this.unwrapUserKeyFromMasterPasswordUnlockData(password, { + salt, + kdf, + masterKeyWrappedUserKey, + }); + } + + emailToSalt(email: string): MasterPasswordSalt { + return email.toLowerCase().trim() as MasterPasswordSalt; } /**