1
0
mirror of https://github.com/bitwarden/browser synced 2026-03-01 02:51:24 +00:00

[BEEEP|PM-32521] Remove compare key hash and move to proof of decryption (#19101)

* Remove compare key hash and move to proof of decryption

* Fix cli build

* Fix mv2

* Fix provider

* Prettier
This commit is contained in:
Bernd Schoolmann
2026-02-25 17:02:04 +01:00
committed by GitHub
parent 385ba70b5c
commit cf7f9cfc7e
9 changed files with 40 additions and 175 deletions

View File

@@ -15,6 +15,7 @@ import {
} from "@bitwarden/key-management";
import { FakeAccountService, mockAccountServiceWith } from "../../../../spec";
import { MasterPasswordUnlockService } from "../../../key-management/master-password/abstractions/master-password-unlock.service";
import { InternalMasterPasswordServiceAbstraction } from "../../../key-management/master-password/abstractions/master-password.service.abstraction";
import { PinLockType } from "../../../key-management/pin/pin-lock-type";
import { PinServiceAbstraction } from "../../../key-management/pin/pin.service.abstraction";
@@ -43,6 +44,7 @@ describe("UserVerificationService", () => {
const vaultTimeoutSettingsService = mock<VaultTimeoutSettingsService>();
const kdfConfigService = mock<KdfConfigService>();
const biometricsService = mock<BiometricsService>();
const masterPasswordUnlockService = mock<MasterPasswordUnlockService>();
const mockUserId = Utils.newGuid() as UserId;
let accountService: FakeAccountService;
@@ -61,6 +63,7 @@ describe("UserVerificationService", () => {
pinService,
kdfConfigService,
biometricsService,
masterPasswordUnlockService,
);
});
@@ -328,11 +331,10 @@ describe("UserVerificationService", () => {
describe("client-side verification", () => {
beforeEach(() => {
setMasterPasswordAvailability(true);
masterPasswordUnlockService.proofOfDecryption.mockResolvedValue(true);
});
it("returns if verification is successful", async () => {
keyService.compareKeyHash.mockResolvedValueOnce(true);
const result = await sut.verifyUserByMasterPassword(
{
type: VerificationType.MasterPassword,
@@ -342,7 +344,10 @@ describe("UserVerificationService", () => {
"email",
);
expect(keyService.compareKeyHash).toHaveBeenCalled();
expect(masterPasswordUnlockService.proofOfDecryption).toHaveBeenCalledWith(
"password",
mockUserId,
);
expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith(
"localHash",
mockUserId,
@@ -356,7 +361,7 @@ describe("UserVerificationService", () => {
});
it("throws if verification fails", async () => {
keyService.compareKeyHash.mockResolvedValueOnce(false);
masterPasswordUnlockService.proofOfDecryption.mockResolvedValueOnce(false);
await expect(
sut.verifyUserByMasterPassword(
@@ -369,7 +374,10 @@ describe("UserVerificationService", () => {
),
).rejects.toThrow("Invalid master password");
expect(keyService.compareKeyHash).toHaveBeenCalled();
expect(masterPasswordUnlockService.proofOfDecryption).toHaveBeenCalledWith(
"password",
mockUserId,
);
expect(masterPasswordService.setMasterKeyHash).not.toHaveBeenCalledWith();
expect(masterPasswordService.setMasterKey).not.toHaveBeenCalledWith();
});
@@ -401,7 +409,7 @@ describe("UserVerificationService", () => {
"email",
);
expect(keyService.compareKeyHash).not.toHaveBeenCalled();
expect(masterPasswordUnlockService.proofOfDecryption).not.toHaveBeenCalled();
expect(masterPasswordService.setMasterKeyHash).toHaveBeenCalledWith(
"localHash",
mockUserId,
@@ -435,7 +443,7 @@ describe("UserVerificationService", () => {
),
).rejects.toThrow("Invalid master password");
expect(keyService.compareKeyHash).not.toHaveBeenCalled();
expect(masterPasswordUnlockService.proofOfDecryption).not.toHaveBeenCalled();
expect(masterPasswordService.setMasterKeyHash).not.toHaveBeenCalledWith();
expect(masterPasswordService.setMasterKey).not.toHaveBeenCalledWith();
});

View File

@@ -14,6 +14,7 @@ import {
KeyService,
} from "@bitwarden/key-management";
import { MasterPasswordUnlockService } from "../../../key-management/master-password/abstractions/master-password-unlock.service";
import { InternalMasterPasswordServiceAbstraction } from "../../../key-management/master-password/abstractions/master-password.service.abstraction";
import { PinServiceAbstraction } from "../../../key-management/pin/pin.service.abstraction";
import { I18nService } from "../../../platform/abstractions/i18n.service";
@@ -54,6 +55,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
private pinService: PinServiceAbstraction,
private kdfConfigService: KdfConfigService,
private biometricsService: BiometricsService,
private masterPasswordUnlockService: MasterPasswordUnlockService,
) {}
async getAvailableVerificationOptions(
@@ -202,9 +204,8 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
let policyOptions: MasterPasswordPolicyResponse | null;
// Client-side verification
if (await this.hasMasterPasswordAndMasterKeyHash(userId)) {
const passwordValid = await this.keyService.compareKeyHash(
const passwordValid = await this.masterPasswordUnlockService.proofOfDecryption(
verification.secret,
masterKey,
userId,
);
if (!passwordValid) {
@@ -214,12 +215,13 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
} else {
// Server-side verification
const request = new SecretVerificationRequest();
const serverKeyHash = await this.keyService.hashMasterKey(
verification.secret,
masterKey,
HashPurpose.ServerAuthorization,
);
request.masterPasswordHash = serverKeyHash;
const authenticationData =
await this.masterPasswordService.makeMasterPasswordAuthenticationData(
verification.secret,
kdfConfig,
await firstValueFrom(this.masterPasswordService.saltForUser$(userId)),
);
request.authenticateWith(authenticationData);
try {
policyOptions = await this.userVerificationApiService.postAccountVerifyPassword(request);
// FIXME: Remove when updating file. Eslint update