use user verification as a part of key rotation (#9722)
This commit is contained in:
parent
5965f779b9
commit
9531d1c655
|
@ -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<UserVerificationService>;
|
||||
let mockApiService: MockProxy<UserKeyRotationApiService>;
|
||||
let mockCipherService: MockProxy<CipherService>;
|
||||
let mockFolderService: MockProxy<FolderService>;
|
||||
|
@ -42,10 +42,8 @@ describe("KeyRotationService", () => {
|
|||
let mockCryptoService: MockProxy<CryptoService>;
|
||||
let mockEncryptService: MockProxy<EncryptService>;
|
||||
let mockConfigService: MockProxy<ConfigService>;
|
||||
let mockKdfConfigService: MockProxy<KdfConfigService>;
|
||||
let mockSyncService: MockProxy<SyncService>;
|
||||
let mockWebauthnLoginAdminService: MockProxy<WebauthnLoginAdminService>;
|
||||
let mockMasterPasswordService: FakeMasterPasswordService = new FakeMasterPasswordService();
|
||||
|
||||
const mockUser = {
|
||||
id: "mockUserId" as UserId,
|
||||
|
@ -55,7 +53,7 @@ describe("KeyRotationService", () => {
|
|||
};
|
||||
|
||||
beforeAll(() => {
|
||||
mockMasterPasswordService = new FakeMasterPasswordService();
|
||||
mockUserVerificationService = mock<UserVerificationService>();
|
||||
mockApiService = mock<UserKeyRotationApiService>();
|
||||
mockCipherService = mock<CipherService>();
|
||||
mockFolderService = mock<FolderService>();
|
||||
|
@ -66,12 +64,11 @@ describe("KeyRotationService", () => {
|
|||
mockCryptoService = mock<CryptoService>();
|
||||
mockEncryptService = mock<EncryptService>();
|
||||
mockConfigService = mock<ConfigService>();
|
||||
mockKdfConfigService = mock<KdfConfigService>();
|
||||
mockSyncService = mock<SyncService>();
|
||||
mockWebauthnLoginAdminService = mock<WebauthnLoginAdminService>();
|
||||
|
||||
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<UserPrivateKey>;
|
||||
|
||||
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 () => {
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue