diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts index 7241e50d35..a972753205 100644 --- a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts +++ b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.spec.ts @@ -3,9 +3,8 @@ import { BehaviorSubject } from "rxjs"; import { OrganizationUserResetPasswordWithIdRequest } from "@bitwarden/common/admin-console/abstractions/organization-user/requests"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { WebauthnRotateCredentialRequest } from "@bitwarden/common/auth/models/request/webauthn-rotate-credential.request"; -import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -32,6 +31,7 @@ import { UserKeyRotationService } from "./user-key-rotation.service"; describe("KeyRotationService", () => { let keyRotationService: UserKeyRotationService; + let mockUserVerificationService: MockProxy; let mockApiService: MockProxy; let mockCipherService: MockProxy; let mockFolderService: MockProxy; @@ -42,10 +42,8 @@ describe("KeyRotationService", () => { let mockCryptoService: MockProxy; let mockEncryptService: MockProxy; let mockConfigService: MockProxy; - let mockKdfConfigService: MockProxy; let mockSyncService: MockProxy; let mockWebauthnLoginAdminService: MockProxy; - let mockMasterPasswordService: FakeMasterPasswordService = new FakeMasterPasswordService(); const mockUser = { id: "mockUserId" as UserId, @@ -55,7 +53,7 @@ describe("KeyRotationService", () => { }; beforeAll(() => { - mockMasterPasswordService = new FakeMasterPasswordService(); + mockUserVerificationService = mock(); mockApiService = mock(); mockCipherService = mock(); mockFolderService = mock(); @@ -66,12 +64,11 @@ describe("KeyRotationService", () => { mockCryptoService = mock(); mockEncryptService = mock(); mockConfigService = mock(); - mockKdfConfigService = mock(); mockSyncService = mock(); mockWebauthnLoginAdminService = mock(); keyRotationService = new UserKeyRotationService( - mockMasterPasswordService, + mockUserVerificationService, mockApiService, mockCipherService, mockFolderService, @@ -81,7 +78,6 @@ describe("KeyRotationService", () => { mockDeviceTrustService, mockCryptoService, mockEncryptService, - mockKdfConfigService, mockSyncService, mockWebauthnLoginAdminService, ); @@ -95,7 +91,6 @@ describe("KeyRotationService", () => { let privateKey: BehaviorSubject; beforeEach(() => { - mockCryptoService.makeMasterKey.mockResolvedValue("mockMasterKey" as any); mockCryptoService.makeUserKey.mockResolvedValue([ new SymmetricCryptoKey(new Uint8Array(64)) as UserKey, { @@ -109,6 +104,12 @@ describe("KeyRotationService", () => { encryptedString: "mockEncryptedData", } as any); + // Mock user verification + mockUserVerificationService.verifyUserByMasterPassword.mockResolvedValue({ + masterKey: "mockMasterKey" as any, + policyOptions: null, + }); + // Mock user key mockCryptoService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); @@ -162,14 +163,6 @@ describe("KeyRotationService", () => { ).rejects.toThrow(); }); - it("throws if master key creation fails", async () => { - mockCryptoService.makeMasterKey.mockResolvedValueOnce(null); - - await expect( - keyRotationService.rotateUserKeyAndEncryptedData("mockMasterPassword", mockUser), - ).rejects.toThrow(); - }); - it("throws if user key creation fails", async () => { mockCryptoService.makeUserKey.mockResolvedValueOnce([null, null]); @@ -186,13 +179,14 @@ describe("KeyRotationService", () => { ).rejects.toThrow(); }); - it("saves the master key in state after creation", async () => { - await keyRotationService.rotateUserKeyAndEncryptedData("mockMasterPassword", mockUser); - - expect(mockMasterPasswordService.mock.setMasterKey).toHaveBeenCalledWith( - "mockMasterKey" as any, - mockUser.id, + it("throws if master password is incorrect", async () => { + mockUserVerificationService.verifyUserByMasterPassword.mockRejectedValueOnce( + new Error("Invalid master password"), ); + + await expect( + keyRotationService.rotateUserKeyAndEncryptedData("mockMasterPassword", mockUser), + ).rejects.toThrow(); }); it("throws if server rotation fails", async () => { diff --git a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts index 0453b10cba..f2ec25d9c5 100644 --- a/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/auth/key-rotation/user-key-rotation.service.ts @@ -3,8 +3,9 @@ import { firstValueFrom } from "rxjs"; import { AccountInfo } from "@bitwarden/common/auth/abstractions/account.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; +import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; +import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; +import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; @@ -25,7 +26,7 @@ import { UserKeyRotationApiService } from "./user-key-rotation-api.service"; @Injectable() export class UserKeyRotationService { constructor( - private masterPasswordService: InternalMasterPasswordServiceAbstraction, + private userVerificationService: UserVerificationService, private apiService: UserKeyRotationApiService, private cipherService: CipherService, private folderService: FolderService, @@ -35,7 +36,6 @@ export class UserKeyRotationService { private deviceTrustService: DeviceTrustServiceAbstraction, private cryptoService: CryptoService, private encryptService: EncryptService, - private kdfConfigService: KdfConfigService, private syncService: SyncService, private webauthnLoginAdminService: WebauthnLoginAdminService, ) {} @@ -58,19 +58,19 @@ export class UserKeyRotationService { ); } - // Create master key to validate the master password - const masterKey = await this.cryptoService.makeMasterKey( - masterPassword, + // Verify master password + // UV service sets master key on success since it is stored in memory and can be lost on refresh + const verification = { + type: VerificationType.MasterPassword, + secret: masterPassword, + } as MasterPasswordVerification; + + const { masterKey } = await this.userVerificationService.verifyUserByMasterPassword( + verification, + user.id, user.email, - await this.kdfConfigService.getKdfConfig(), ); - if (!masterKey) { - throw new Error("Master key could not be created"); - } - - // Set master key again in case it was lost (could be lost on refresh) - await this.masterPasswordService.setMasterKey(masterKey, user.id); const [newUserKey, newEncUserKey] = await this.cryptoService.makeUserKey(masterKey); if (!newUserKey || !newEncUserKey) {