From 51d29f777e38a7fec15b355579696d2873388647 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 11 Dec 2025 13:01:09 +0100 Subject: [PATCH] [PM-24353] Drop legacy pin support (#17328) * Drop legacy pin support * Fix cli build * Fix browser build * Remove pin key * Fix comment * Fix CI / tests * Add migration to remove key * Inline export key * Extract vault export key generation * Cleanup * Add migrator * Fix mv2 build --- .../browser/src/background/main.background.ts | 9 +- .../service-container/service-container.ts | 9 +- .../src/services/jslib-services.module.ts | 15 +-- .../default-key-generation.service.ts | 8 ++ .../key-generation/key-generation.service.ts | 15 +++ .../pin/pin-state.service.abstraction.ts | 8 -- .../pin/pin-state.service.implementation.ts | 18 +-- .../pin/pin-state.service.spec.ts | 106 ---------------- .../pin/pin.service.abstraction.ts | 11 +- .../pin/pin.service.implementation.ts | 119 ++++-------------- .../key-management/pin/pin.service.spec.ts | 94 +------------- .../src/key-management/pin/pin.state.ts | 16 --- .../default-process-reload.service.ts | 2 +- libs/common/src/types/key.ts | 2 - .../src/components/importer-providers.ts | 4 +- ...warden-password-protected-importer.spec.ts | 10 +- .../bitwarden-password-protected-importer.ts | 6 +- .../src/services/import.service.spec.ts | 8 +- libs/importer/src/services/import.service.ts | 6 +- libs/state/src/state-migrations/migrate.ts | 6 +- .../migrations/74-remove-legacy-pin.spec.ts | 50 ++++++++ .../migrations/74-remove-legacy-pin.ts | 30 +++++ .../src/services/base-vault-export.service.ts | 7 +- .../individual-vault-export.service.spec.ts | 8 +- .../individual-vault-export.service.ts | 6 +- .../src/services/org-vault-export.service.ts | 6 +- 26 files changed, 175 insertions(+), 404 deletions(-) create mode 100644 libs/state/src/state-migrations/migrations/74-remove-legacy-pin.spec.ts create mode 100644 libs/state/src/state-migrations/migrations/74-remove-legacy-pin.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 1bd47186914..2540571abb0 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -841,10 +841,7 @@ export default class MainBackground { ); this.pinService = new PinService( - this.accountService, this.encryptService, - this.kdfConfigService, - this.keyGenerationService, this.logService, this.keyService, this.sdkService, @@ -1112,7 +1109,7 @@ export default class MainBackground { this.collectionService, this.keyService, this.encryptService, - this.pinService, + this.keyGenerationService, this.accountService, this.restrictedItemTypesService, ); @@ -1120,7 +1117,7 @@ export default class MainBackground { this.individualVaultExportService = new IndividualVaultExportService( this.folderService, this.cipherService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, @@ -1134,7 +1131,7 @@ export default class MainBackground { this.organizationVaultExportService = new OrganizationVaultExportService( this.cipherService, this.exportApiService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 83c64c61423..2d4ea7d00b5 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -492,10 +492,7 @@ export class ServiceContainer { const pinStateService = new PinStateService(this.stateProvider); this.pinService = new PinService( - this.accountService, this.encryptService, - this.kdfConfigService, - this.keyGenerationService, this.logService, this.keyService, this.sdkService, @@ -908,7 +905,7 @@ export class ServiceContainer { this.collectionService, this.keyService, this.encryptService, - this.pinService, + this.keyGenerationService, this.accountService, this.restrictedItemTypesService, ); @@ -916,7 +913,7 @@ export class ServiceContainer { this.individualExportService = new IndividualVaultExportService( this.folderService, this.cipherService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, @@ -930,7 +927,7 @@ export class ServiceContainer { this.organizationExportService = new OrganizationVaultExportService( this.cipherService, this.vaultExportApiService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index b26db7e9056..816e09fd45d 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -952,7 +952,7 @@ const safeProviders: SafeProvider[] = [ deps: [ FolderServiceAbstraction, CipherServiceAbstraction, - PinServiceAbstraction, + KeyGenerationService, KeyService, EncryptService, CryptoFunctionServiceAbstraction, @@ -972,7 +972,7 @@ const safeProviders: SafeProvider[] = [ deps: [ CipherServiceAbstraction, VaultExportApiService, - PinServiceAbstraction, + KeyGenerationService, KeyService, EncryptService, CryptoFunctionServiceAbstraction, @@ -1357,16 +1357,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: PinServiceAbstraction, useClass: PinService, - deps: [ - AccountServiceAbstraction, - EncryptService, - KdfConfigService, - KeyGenerationService, - LogService, - KeyService, - SdkService, - PinStateServiceAbstraction, - ], + deps: [EncryptService, LogService, KeyService, SdkService, PinStateServiceAbstraction], }), safeProvider({ provide: WebAuthnLoginPrfKeyServiceAbstraction, diff --git a/libs/common/src/key-management/crypto/key-generation/default-key-generation.service.ts b/libs/common/src/key-management/crypto/key-generation/default-key-generation.service.ts index 8e8d2de1ce4..5f5da741707 100644 --- a/libs/common/src/key-management/crypto/key-generation/default-key-generation.service.ts +++ b/libs/common/src/key-management/crypto/key-generation/default-key-generation.service.ts @@ -91,4 +91,12 @@ export class DefaultKeyGenerationService implements KeyGenerationService { return new SymmetricCryptoKey(newKey); } + + async deriveVaultExportKey( + password: string, + salt: string, + kdfConfig: KdfConfig, + ): Promise { + return await this.stretchKey(await this.deriveKeyFromPassword(password, salt, kdfConfig)); + } } diff --git a/libs/common/src/key-management/crypto/key-generation/key-generation.service.ts b/libs/common/src/key-management/crypto/key-generation/key-generation.service.ts index d6be436384e..ddc3829aa9f 100644 --- a/libs/common/src/key-management/crypto/key-generation/key-generation.service.ts +++ b/libs/common/src/key-management/crypto/key-generation/key-generation.service.ts @@ -87,4 +87,19 @@ export abstract class KeyGenerationService { * @returns 64 byte derived key. */ abstract stretchKey(key: SymmetricCryptoKey): Promise; + + /** + * Derives a 64 byte key for encrypting and decrypting vault exports. + * + * @deprecated Do not use this for new use-cases. + * @param password Password to derive the key from. + * @param salt Salt for the key derivation function. + * @param kdfConfig Configuration for the key derivation function. + * @returns 64 byte derived key. + */ + abstract deriveVaultExportKey( + password: string, + salt: string, + kdfConfig: KdfConfig, + ): Promise; } diff --git a/libs/common/src/key-management/pin/pin-state.service.abstraction.ts b/libs/common/src/key-management/pin/pin-state.service.abstraction.ts index cd10b8b7fe2..4aef268c1c4 100644 --- a/libs/common/src/key-management/pin/pin-state.service.abstraction.ts +++ b/libs/common/src/key-management/pin/pin-state.service.abstraction.ts @@ -45,14 +45,6 @@ export abstract class PinStateServiceAbstraction { pinLockType: PinLockType, ): Promise; - /** - * Gets the user's legacy PIN-protected UserKey - * @deprecated Use {@link getPinProtectedUserKeyEnvelope} instead. Only for migration support. - * @param userId The user's id - * @throws If the user id is not provided - */ - abstract getLegacyPinKeyEncryptedUserKeyPersistent(userId: UserId): Promise; - /** * Sets the PIN state for the user * @deprecated - This is not a public API. DO NOT USE IT diff --git a/libs/common/src/key-management/pin/pin-state.service.implementation.ts b/libs/common/src/key-management/pin/pin-state.service.implementation.ts index 0bf6cb60fb0..d5b2608f280 100644 --- a/libs/common/src/key-management/pin/pin-state.service.implementation.ts +++ b/libs/common/src/key-management/pin/pin-state.service.implementation.ts @@ -13,7 +13,6 @@ import { PIN_PROTECTED_USER_KEY_ENVELOPE_PERSISTENT, PIN_PROTECTED_USER_KEY_ENVELOPE_EPHEMERAL, USER_KEY_ENCRYPTED_PIN, - PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, } from "./pin.state"; export class PinStateService implements PinStateServiceAbstraction { @@ -36,9 +35,7 @@ export class PinStateService implements PinStateServiceAbstraction { assertNonNullish(userId, "userId"); const isPersistentPinSet = - (await this.getPinProtectedUserKeyEnvelope(userId, "PERSISTENT")) != null || - // Deprecated - (await this.getLegacyPinKeyEncryptedUserKeyPersistent(userId)) != null; + (await this.getPinProtectedUserKeyEnvelope(userId, "PERSISTENT")) != null; const isPinSet = (await firstValueFrom(this.stateProvider.getUserState$(USER_KEY_ENCRYPTED_PIN, userId))) != null; @@ -71,16 +68,6 @@ export class PinStateService implements PinStateServiceAbstraction { } } - async getLegacyPinKeyEncryptedUserKeyPersistent(userId: UserId): Promise { - assertNonNullish(userId, "userId"); - - return await firstValueFrom( - this.stateProvider - .getUserState$(PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, userId) - .pipe(map((value) => (value ? new EncString(value) : null))), - ); - } - async setPinState( userId: UserId, pinProtectedUserKeyEnvelope: PasswordProtectedKeyEnvelope, @@ -116,9 +103,6 @@ export class PinStateService implements PinStateServiceAbstraction { await this.stateProvider.setUserState(USER_KEY_ENCRYPTED_PIN, null, userId); await this.stateProvider.setUserState(PIN_PROTECTED_USER_KEY_ENVELOPE_EPHEMERAL, null, userId); await this.stateProvider.setUserState(PIN_PROTECTED_USER_KEY_ENVELOPE_PERSISTENT, null, userId); - - // Note: This can be deleted after sufficiently many PINs are migrated and the state is removed. - await this.stateProvider.setUserState(PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, null, userId); } async clearEphemeralPinState(userId: UserId): Promise { diff --git a/libs/common/src/key-management/pin/pin-state.service.spec.ts b/libs/common/src/key-management/pin/pin-state.service.spec.ts index be85a15e6d3..7406701c28d 100644 --- a/libs/common/src/key-management/pin/pin-state.service.spec.ts +++ b/libs/common/src/key-management/pin/pin-state.service.spec.ts @@ -13,7 +13,6 @@ import { USER_KEY_ENCRYPTED_PIN, PIN_PROTECTED_USER_KEY_ENVELOPE_EPHEMERAL, PIN_PROTECTED_USER_KEY_ENVELOPE_PERSISTENT, - PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, } from "./pin.state"; describe("PinStateService", () => { @@ -121,21 +120,6 @@ describe("PinStateService", () => { expect(result).toBe("PERSISTENT"); }); - it("should return 'PERSISTENT' if a legacy pin key encrypted user key (persistent) is found", async () => { - // Arrange - await stateProvider.setUserState( - PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, - mockUserKeyEncryptedPin, - mockUserId, - ); - - // Act - const result = await sut.getPinLockType(mockUserId); - - // Assert - expect(result).toBe("PERSISTENT"); - }); - it("should return 'EPHEMERAL' if only user key encrypted pin is found", async () => { // Arrange await stateProvider.setUserState(USER_KEY_ENCRYPTED_PIN, mockUserKeyEncryptedPin, mockUserId); @@ -164,7 +148,6 @@ describe("PinStateService", () => { null, mockUserId, ); - await stateProvider.setUserState(PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, null, mockUserId); await stateProvider.setUserState(USER_KEY_ENCRYPTED_PIN, null, mockUserId); // Act @@ -290,45 +273,6 @@ describe("PinStateService", () => { }); }); - describe("getLegacyPinKeyEncryptedUserKeyPersistent()", () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - test.each([null, undefined])("throws if userId is %p", async (userId) => { - // Act & Assert - await expect(() => - sut.getLegacyPinKeyEncryptedUserKeyPersistent(userId as any), - ).rejects.toThrow("userId is null or undefined."); - }); - - it("should return EncString when legacy key is set", async () => { - // Arrange - await stateProvider.setUserState( - PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, - mockUserKeyEncryptedPin, - mockUserId, - ); - - // Act - const result = await sut.getLegacyPinKeyEncryptedUserKeyPersistent(mockUserId); - - // Assert - expect(result?.encryptedString).toEqual(mockUserKeyEncryptedPin); - }); - - test.each([null, undefined])("should return null when legacy key is %p", async (value) => { - // Arrange - await stateProvider.setUserState(PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, value, mockUserId); - - // Act - const result = await sut.getLegacyPinKeyEncryptedUserKeyPersistent(mockUserId); - - // Assert - expect(result).toBeNull(); - }); - }); - describe("setPinState()", () => { beforeEach(() => { jest.clearAllMocks(); @@ -464,22 +408,6 @@ describe("PinStateService", () => { expect(result).toBeNull(); }); - it("clears legacy PIN key encrypted user key persistent", async () => { - // Arrange - await stateProvider.setUserState( - PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, - mockUserKeyEncryptedPin, - mockUserId, - ); - - // Act - await sut.clearPinState(mockUserId); - - // Assert - const result = await sut.getLegacyPinKeyEncryptedUserKeyPersistent(mockUserId); - expect(result).toBeNull(); - }); - it("clears all PIN state when all types are set", async () => { // Arrange - set up all possible PIN state await sut.setPinState( @@ -494,17 +422,11 @@ describe("PinStateService", () => { mockUserKeyEncryptedPin, "EPHEMERAL", ); - await stateProvider.setUserState( - PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, - mockUserKeyEncryptedPin, - mockUserId, - ); // Verify all state is set before clearing expect(await firstValueFrom(sut.userKeyEncryptedPin$(mockUserId))).not.toBeNull(); expect(await sut.getPinProtectedUserKeyEnvelope(mockUserId, "EPHEMERAL")).not.toBeNull(); expect(await sut.getPinProtectedUserKeyEnvelope(mockUserId, "PERSISTENT")).not.toBeNull(); - expect(await sut.getLegacyPinKeyEncryptedUserKeyPersistent(mockUserId)).not.toBeNull(); // Act await sut.clearPinState(mockUserId); @@ -513,7 +435,6 @@ describe("PinStateService", () => { expect(await firstValueFrom(sut.userKeyEncryptedPin$(mockUserId))).toBeNull(); expect(await sut.getPinProtectedUserKeyEnvelope(mockUserId, "EPHEMERAL")).toBeNull(); expect(await sut.getPinProtectedUserKeyEnvelope(mockUserId, "PERSISTENT")).toBeNull(); - expect(await sut.getLegacyPinKeyEncryptedUserKeyPersistent(mockUserId)).toBeNull(); }); it("results in PIN lock type DISABLED after clearing", async () => { @@ -545,7 +466,6 @@ describe("PinStateService", () => { expect(await firstValueFrom(sut.userKeyEncryptedPin$(mockUserId))).toBeNull(); expect(await sut.getPinProtectedUserKeyEnvelope(mockUserId, "EPHEMERAL")).toBeNull(); expect(await sut.getPinProtectedUserKeyEnvelope(mockUserId, "PERSISTENT")).toBeNull(); - expect(await sut.getLegacyPinKeyEncryptedUserKeyPersistent(mockUserId)).toBeNull(); expect(await sut.getPinLockType(mockUserId)).toBe("DISABLED"); }); }); @@ -623,32 +543,6 @@ describe("PinStateService", () => { expect(ephemeralResult).toBeNull(); }); - it("does not clear legacy PIN key encrypted user key persistent", async () => { - // Arrange - set up ephemeral state and legacy state - await sut.setPinState( - mockUserId, - mockEphemeralEnvelope, - mockUserKeyEncryptedPin, - "EPHEMERAL", - ); - await stateProvider.setUserState( - PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT, - mockUserKeyEncryptedPin, - mockUserId, - ); - - // Act - await sut.clearEphemeralPinState(mockUserId); - - // Assert - legacy PIN should still be present - const legacyResult = await sut.getLegacyPinKeyEncryptedUserKeyPersistent(mockUserId); - expect(legacyResult?.encryptedString).toEqual(mockUserKeyEncryptedPin); - - // Assert - ephemeral envelope should be cleared - const ephemeralResult = await sut.getPinProtectedUserKeyEnvelope(mockUserId, "EPHEMERAL"); - expect(ephemeralResult).toBeNull(); - }); - it("changes PIN lock type from EPHEMERAL to DISABLED when no other PIN state exists", async () => { // Arrange - set up only ephemeral PIN state await sut.setPinState( diff --git a/libs/common/src/key-management/pin/pin.service.abstraction.ts b/libs/common/src/key-management/pin/pin.service.abstraction.ts index b242320c06e..c4551d3522f 100644 --- a/libs/common/src/key-management/pin/pin.service.abstraction.ts +++ b/libs/common/src/key-management/pin/pin.service.abstraction.ts @@ -1,8 +1,5 @@ -// eslint-disable-next-line no-restricted-imports -import { KdfConfig } from "@bitwarden/key-management"; - import { UserId } from "../../types/guid"; -import { PinKey, UserKey } from "../../types/key"; +import { UserKey } from "../../types/key"; import { PinLockType } from "./pin-lock-type"; @@ -69,10 +66,4 @@ export abstract class PinServiceAbstraction { * @deprecated This is not deprecated, but only meant to be called by KeyService. DO NOT USE IT. */ abstract userUnlocked(userId: UserId): Promise; - - /** - * Makes a PinKey from the provided PIN. - * @deprecated - Note: This is currently re-used by vault exports, which is still permitted but should be refactored out to use a different construct. - */ - abstract makePinKey(pin: string, salt: string, kdfConfig: KdfConfig): Promise; } diff --git a/libs/common/src/key-management/pin/pin.service.implementation.ts b/libs/common/src/key-management/pin/pin.service.implementation.ts index 29a10d1a0a4..da6d3f20eaf 100644 --- a/libs/common/src/key-management/pin/pin.service.implementation.ts +++ b/libs/common/src/key-management/pin/pin.service.implementation.ts @@ -1,18 +1,15 @@ import { firstValueFrom, map } from "rxjs"; // eslint-disable-next-line no-restricted-imports -import { KdfConfig, KdfConfigService, KeyService } from "@bitwarden/key-management"; +import { KeyService } from "@bitwarden/key-management"; -import { AccountService } from "../../auth/abstractions/account.service"; import { assertNonNullish } from "../../auth/utils"; import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service"; -import { EncString } from "../../key-management/crypto/models/enc-string"; import { LogService } from "../../platform/abstractions/log.service"; import { SdkService } from "../../platform/abstractions/sdk/sdk.service"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; import { UserId } from "../../types/guid"; -import { PinKey, UserKey } from "../../types/key"; -import { KeyGenerationService } from "../crypto"; +import { UserKey } from "../../types/key"; import { firstValueFromOrThrow } from "../utils"; import { PinLockType } from "./pin-lock-type"; @@ -21,10 +18,7 @@ import { PinServiceAbstraction } from "./pin.service.abstraction"; export class PinService implements PinServiceAbstraction { constructor( - private accountService: AccountService, private encryptService: EncryptService, - private kdfConfigService: KdfConfigService, - private keyGenerationService: KeyGenerationService, private logService: LogService, private keyService: KeyService, private sdkService: SdkService, @@ -56,19 +50,6 @@ export class PinService implements PinServiceAbstraction { // On first unlock, set the ephemeral pin envelope, if it is not set yet const pin = await this.getPin(userId); await this.setPin(pin, "EPHEMERAL", userId); - } else if ((await this.pinStateService.getPinLockType(userId)) === "PERSISTENT") { - // Encrypted migration for persistent pin unlock to pin envelopes. - // This will be removed at the earliest in 2026.1.0 - // - // ----- ENCRYPTION MIGRATION ----- - // Pin-key encrypted user-keys are eagerly migrated to the new pin-protected user key envelope format. - if ((await this.pinStateService.getLegacyPinKeyEncryptedUserKeyPersistent(userId)) != null) { - this.logService.info( - "[Pin Service] Migrating legacy PIN key to PinProtectedUserKeyEnvelope", - ); - const pin = await this.getPin(userId); - await this.setPin(pin, "PERSISTENT", userId); - } } } @@ -144,86 +125,30 @@ export class PinService implements PinServiceAbstraction { assertNonNullish(pin, "pin"); assertNonNullish(userId, "userId"); - const hasPinProtectedKeyEnvelopeSet = - (await this.pinStateService.getPinProtectedUserKeyEnvelope(userId, "EPHEMERAL")) != null || - (await this.pinStateService.getPinProtectedUserKeyEnvelope(userId, "PERSISTENT")) != null; + this.logService.info("[Pin Service] Pin-unlock via PinProtectedUserKeyEnvelope"); - if (hasPinProtectedKeyEnvelopeSet) { - this.logService.info("[Pin Service] Pin-unlock via PinProtectedUserKeyEnvelope"); + const pinLockType = await this.pinStateService.getPinLockType(userId); + const envelope = await this.pinStateService.getPinProtectedUserKeyEnvelope(userId, pinLockType); - const pinLockType = await this.pinStateService.getPinLockType(userId); - const envelope = await this.pinStateService.getPinProtectedUserKeyEnvelope( - userId, - pinLockType, + try { + // Use the sdk to create an enrollment, not yet persisting it to state + const startTime = performance.now(); + const userKeyBytes = await firstValueFrom( + this.sdkService.client$.pipe( + map((sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + return sdk.crypto().unseal_password_protected_key_envelope(pin, envelope!); + }), + ), ); + this.logService.measure(startTime, "Crypto", "PinService", "UnsealPinEnvelope"); - try { - // Use the sdk to create an enrollment, not yet persisting it to state - const startTime = performance.now(); - const userKeyBytes = await firstValueFrom( - this.sdkService.client$.pipe( - map((sdk) => { - if (!sdk) { - throw new Error("SDK not available"); - } - return sdk.crypto().unseal_password_protected_key_envelope(pin, envelope!); - }), - ), - ); - this.logService.measure(startTime, "Crypto", "PinService", "UnsealPinEnvelope"); - - return new SymmetricCryptoKey(userKeyBytes) as UserKey; - } catch (error) { - this.logService.error(`Failed to unseal pin: ${error}`); - return null; - } - } else { - this.logService.info("[Pin Service] Pin-unlock via legacy PinKeyEncryptedUserKey"); - - // This branch is deprecated and will be removed in the future, but is kept for migration. - try { - const pinKeyEncryptedUserKey = - await this.pinStateService.getLegacyPinKeyEncryptedUserKeyPersistent(userId); - const email = await firstValueFrom( - this.accountService.accounts$.pipe(map((accounts) => accounts[userId].email)), - ); - const kdfConfig = await this.kdfConfigService.getKdfConfig(userId); - return await this.decryptUserKey(pin, email, kdfConfig, pinKeyEncryptedUserKey!); - } catch (error) { - this.logService.error(`Error decrypting user key with pin: ${error}`); - return null; - } + return new SymmetricCryptoKey(userKeyBytes) as UserKey; + } catch (error) { + this.logService.error(`Failed to unseal pin: ${error}`); + return null; } } - - /// Anything below here is deprecated and will be removed subsequently - - async makePinKey(pin: string, salt: string, kdfConfig: KdfConfig): Promise { - const startTime = performance.now(); - const pinKey = await this.keyGenerationService.deriveKeyFromPassword(pin, salt, kdfConfig); - this.logService.measure(startTime, "Crypto", "PinService", "makePinKey"); - - return (await this.keyGenerationService.stretchKey(pinKey)) as PinKey; - } - - /** - * Decrypts the UserKey with the provided PIN. - * @deprecated - * @throws If the PIN does not match the PIN that was used to encrypt the user key - * @throws If the salt, or KDF don't match the salt / KDF used to encrypt the user key - */ - private async decryptUserKey( - pin: string, - salt: string, - kdfConfig: KdfConfig, - pinKeyEncryptedUserKey: EncString, - ): Promise { - assertNonNullish(pin, "pin"); - assertNonNullish(salt, "salt"); - assertNonNullish(kdfConfig, "kdfConfig"); - assertNonNullish(pinKeyEncryptedUserKey, "pinKeyEncryptedUserKey"); - const pinKey = await this.makePinKey(pin, salt, kdfConfig); - const userKey = await this.encryptService.unwrapSymmetricKey(pinKeyEncryptedUserKey, pinKey); - return userKey as UserKey; - } } diff --git a/libs/common/src/key-management/pin/pin.service.spec.ts b/libs/common/src/key-management/pin/pin.service.spec.ts index 8d78255bb1b..644fd1d8d75 100644 --- a/libs/common/src/key-management/pin/pin.service.spec.ts +++ b/libs/common/src/key-management/pin/pin.service.spec.ts @@ -2,17 +2,15 @@ import { mock } from "jest-mock-extended"; import { BehaviorSubject, filter } from "rxjs"; // eslint-disable-next-line no-restricted-imports -import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management"; +import { KeyService } from "@bitwarden/key-management"; import { PasswordProtectedKeyEnvelope } from "@bitwarden/sdk-internal"; import { MockSdkService } from "../..//platform/spec/mock-sdk.service"; -import { FakeAccountService, mockAccountServiceWith, mockEnc } from "../../../spec"; import { LogService } from "../../platform/abstractions/log.service"; import { Utils } from "../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; import { UserId } from "../../types/guid"; -import { PinKey, UserKey } from "../../types/key"; -import { KeyGenerationService } from "../crypto"; +import { UserKey } from "../../types/key"; import { EncryptService } from "../crypto/abstractions/encrypt.service"; import { EncryptedString, EncString } from "../crypto/models/enc-string"; @@ -22,16 +20,10 @@ import { PinService } from "./pin.service.implementation"; describe("PinService", () => { let sut: PinService; - let accountService: FakeAccountService; - const encryptService = mock(); - const kdfConfigService = mock(); - const keyGenerationService = mock(); const logService = mock(); const mockUserId = Utils.newGuid() as UserId; const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; - const mockPinKey = new SymmetricCryptoKey(randomBytes(32)) as PinKey; - const mockUserEmail = "user@example.com"; const mockPin = "1234"; const mockUserKeyEncryptedPin = new EncString("userKeyEncryptedPin"); const mockEphemeralEnvelope = "mock-ephemeral-envelope" as PasswordProtectedKeyEnvelope; @@ -42,7 +34,6 @@ describe("PinService", () => { const behaviorSubject = new BehaviorSubject<{ userId: UserId; userKey: UserKey }>(null); beforeEach(() => { - accountService = mockAccountServiceWith(mockUserId, { email: mockUserEmail }); (keyService as any)["unlockedUserKeys$"] = behaviorSubject .asObservable() .pipe(filter((x) => x != null)); @@ -50,16 +41,7 @@ describe("PinService", () => { .mockDeep() .unseal_password_protected_key_envelope.mockReturnValue(new Uint8Array(64)); - sut = new PinService( - accountService, - encryptService, - kdfConfigService, - keyGenerationService, - logService, - keyService, - sdkService, - pinStateService, - ); + sut = new PinService(encryptService, logService, keyService, sdkService, pinStateService); }); it("should instantiate the PinService", () => { @@ -89,26 +71,6 @@ describe("PinService", () => { ); }); - it("should migrate legacy persistent PIN if needed", async () => { - // Arrange - pinStateService.getPinLockType.mockResolvedValue("PERSISTENT"); - pinStateService.getLegacyPinKeyEncryptedUserKeyPersistent.mockResolvedValue( - mockEnc("legacy-key"), - ); - const getPinSpy = jest.spyOn(sut, "getPin").mockResolvedValue(mockPin); - const setPinSpy = jest.spyOn(sut, "setPin").mockResolvedValue(); - - // Act - await sut.userUnlocked(mockUserId); - - // Assert - expect(getPinSpy).toHaveBeenCalledWith(mockUserId); - expect(setPinSpy).toHaveBeenCalledWith(mockPin, "PERSISTENT", mockUserId); - expect(logService.info).toHaveBeenCalledWith( - "[Pin Service] Migrating legacy PIN key to PinProtectedUserKeyEnvelope", - ); - }); - it("should do nothing if no migration or setup is needed", async () => { // Arrange pinStateService.getPinLockType.mockResolvedValue("DISABLED"); @@ -124,28 +86,6 @@ describe("PinService", () => { }); }); - describe("makePinKey()", () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it("should make a PinKey", async () => { - // Arrange - keyGenerationService.deriveKeyFromPassword.mockResolvedValue(mockPinKey); - - // Act - await sut.makePinKey(mockPin, mockUserEmail, DEFAULT_KDF_CONFIG); - - // Assert - expect(keyGenerationService.deriveKeyFromPassword).toHaveBeenCalledWith( - mockPin, - mockUserEmail, - DEFAULT_KDF_CONFIG, - ); - expect(keyGenerationService.stretchKey).toHaveBeenCalledWith(mockPinKey); - }); - }); - describe("getPin()", () => { beforeEach(() => { jest.clearAllMocks(); @@ -383,7 +323,6 @@ describe("PinService", () => { jest.clearAllMocks(); pinStateService.userKeyEncryptedPin$.mockReset(); pinStateService.getPinProtectedUserKeyEnvelope.mockReset(); - pinStateService.getLegacyPinKeyEncryptedUserKeyPersistent.mockReset(); }); it("should throw an error if userId is null", async () => { @@ -423,32 +362,5 @@ describe("PinService", () => { // Assert expect(result).toEqual(mockUserKey); }); - - it("should return userkey with legacy pin PERSISTENT", async () => { - keyGenerationService.deriveKeyFromPassword.mockResolvedValue(mockPinKey); - keyGenerationService.stretchKey.mockResolvedValue(mockPinKey); - kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); - encryptService.unwrapSymmetricKey.mockResolvedValue(mockUserKey); - - // Arrange - const mockPin = "1234"; - pinStateService.userKeyEncryptedPin$.mockReturnValueOnce( - new BehaviorSubject(mockUserKeyEncryptedPin), - ); - pinStateService.getLegacyPinKeyEncryptedUserKeyPersistent.mockResolvedValueOnce( - mockUserKeyEncryptedPin, - ); - - // Act - const result = await sut.decryptUserKeyWithPin(mockPin, mockUserId); - - // Assert - expect(result).toEqual(mockUserKey); - }); }); }); - -// Test helpers -function randomBytes(length: number): Uint8Array { - return new Uint8Array(Array.from({ length }, (_, k) => k % 255)); -} diff --git a/libs/common/src/key-management/pin/pin.state.ts b/libs/common/src/key-management/pin/pin.state.ts index 4ad0524035f..c3bbad7644c 100644 --- a/libs/common/src/key-management/pin/pin.state.ts +++ b/libs/common/src/key-management/pin/pin.state.ts @@ -3,22 +3,6 @@ import { PasswordProtectedKeyEnvelope } from "@bitwarden/sdk-internal"; import { EncryptedString } from "../crypto/models/enc-string"; -/** - * The persistent (stored on disk) version of the UserKey, encrypted by the PinKey. - * - * @deprecated - * @remarks Persists through a client reset. Used when `requireMasterPasswordOnClientRestart` is disabled. - * @see SetPinComponent.setPinForm.requireMasterPasswordOnClientRestart - */ -export const PIN_KEY_ENCRYPTED_USER_KEY_PERSISTENT = new UserKeyDefinition( - PIN_DISK, - "pinKeyEncryptedUserKeyPersistent", - { - deserializer: (jsonValue) => jsonValue, - clearOn: ["logout"], - }, -); - /** * The persistent (stored on disk) version of the UserKey, stored in a `PasswordProtectedKeyEnvelope`. * diff --git a/libs/common/src/key-management/services/default-process-reload.service.ts b/libs/common/src/key-management/services/default-process-reload.service.ts index ddbcf7e5530..ecd9ddafa40 100644 --- a/libs/common/src/key-management/services/default-process-reload.service.ts +++ b/libs/common/src/key-management/services/default-process-reload.service.ts @@ -56,7 +56,7 @@ export class DefaultProcessReloadService implements ProcessReloadServiceAbstract return; } - // If there is an active user, check if they have a pinKeyEncryptedUserKeyEphemeral. If so, prevent process reload upon lock. + // If there is an active user, check if they have an ephemeral PIN. If so, prevent process reload upon lock. const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; if (userId != null) { if ((await this.pinService.getPinLockType(userId)) === "EPHEMERAL") { diff --git a/libs/common/src/types/key.ts b/libs/common/src/types/key.ts index 46dcc14a8b2..3c5487b30e0 100644 --- a/libs/common/src/types/key.ts +++ b/libs/common/src/types/key.ts @@ -9,8 +9,6 @@ export type PrfKey = Opaque; export type UserKey = Opaque; /** @deprecated Interacting with the master key directly is prohibited. Use a high level function from MasterPasswordService instead. */ export type MasterKey = Opaque; -/** @deprecated */ -export type PinKey = Opaque; export type OrgKey = Opaque; export type ProviderKey = Opaque; export type CipherKey = Opaque; diff --git a/libs/importer/src/components/importer-providers.ts b/libs/importer/src/components/importer-providers.ts index c48f7c1b096..18c148ebe2e 100644 --- a/libs/importer/src/components/importer-providers.ts +++ b/libs/importer/src/components/importer-providers.ts @@ -7,8 +7,8 @@ import { safeProvider, SafeProvider } from "@bitwarden/angular/platform/utils/sa import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -84,7 +84,7 @@ export const ImporterProviders: SafeProvider[] = [ CollectionService, KeyService, EncryptService, - PinServiceAbstraction, + KeyGenerationService, AccountService, RestrictedItemTypesService, ], diff --git a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts index 44a55af8f62..6e98b21977d 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.spec.ts @@ -2,8 +2,8 @@ import { mock, MockProxy } from "jest-mock-extended"; import { of } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { emptyGuid, OrganizationId } from "@bitwarden/common/types/guid"; @@ -24,7 +24,7 @@ describe("BitwardenPasswordProtectedImporter", () => { let encryptService: MockProxy; let i18nService: MockProxy; let cipherService: MockProxy; - let pinService: MockProxy; + let keyGenerationService: MockProxy; let accountService: MockProxy; const password = Utils.newGuid(); const promptForPassword_callback = async () => { @@ -36,7 +36,7 @@ describe("BitwardenPasswordProtectedImporter", () => { encryptService = mock(); i18nService = mock(); cipherService = mock(); - pinService = mock(); + keyGenerationService = mock(); accountService = mock(); accountService.activeAccount$ = of({ @@ -71,7 +71,7 @@ describe("BitwardenPasswordProtectedImporter", () => { encryptService, i18nService, cipherService, - pinService, + keyGenerationService, accountService, promptForPassword_callback, ); @@ -105,7 +105,7 @@ describe("BitwardenPasswordProtectedImporter", () => { encryptService, i18nService, cipherService, - pinService, + keyGenerationService, accountService, promptForPassword_callback, ); diff --git a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts index 7062089482d..b685ddf0fb5 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts @@ -1,9 +1,9 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -29,7 +29,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im encryptService: EncryptService, i18nService: I18nService, cipherService: CipherService, - private pinService: PinServiceAbstraction, + private keyGenerationService: KeyGenerationService, accountService: AccountService, private promptForPassword_callback: () => Promise, ) { @@ -86,7 +86,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im ? new PBKDF2KdfConfig(jdoc.kdfIterations) : new Argon2KdfConfig(jdoc.kdfIterations, jdoc.kdfMemory, jdoc.kdfParallelism); - this.key = await this.pinService.makePinKey(password, jdoc.salt, kdfConfig); + this.key = await this.keyGenerationService.deriveVaultExportKey(password, jdoc.salt, kdfConfig); const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT); diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts index b1c028ff063..b82772669de 100644 --- a/libs/importer/src/services/import.service.spec.ts +++ b/libs/importer/src/services/import.service.spec.ts @@ -8,8 +8,8 @@ import { CollectionView, } from "@bitwarden/admin-console/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; @@ -36,7 +36,7 @@ describe("ImportService", () => { let collectionService: MockProxy; let keyService: MockProxy; let encryptService: MockProxy; - let pinService: MockProxy; + let keyGenerationService: MockProxy; let accountService: MockProxy; let restrictedItemTypesService: MockProxy; @@ -48,7 +48,7 @@ describe("ImportService", () => { collectionService = mock(); keyService = mock(); encryptService = mock(); - pinService = mock(); + keyGenerationService = mock(); restrictedItemTypesService = mock(); importService = new ImportService( @@ -59,7 +59,7 @@ describe("ImportService", () => { collectionService, keyService, encryptService, - pinService, + keyGenerationService, accountService, restrictedItemTypesService, ); diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index f62054f9414..400beae5179 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -12,8 +12,8 @@ import { } from "@bitwarden/admin-console/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { ImportCiphersRequest } from "@bitwarden/common/models/request/import-ciphers.request"; import { ImportOrganizationCiphersRequest } from "@bitwarden/common/models/request/import-organization-ciphers.request"; import { KvpRequest } from "@bitwarden/common/models/request/kvp.request"; @@ -119,7 +119,7 @@ export class ImportService implements ImportServiceAbstraction { private collectionService: CollectionService, private keyService: KeyService, private encryptService: EncryptService, - private pinService: PinServiceAbstraction, + private keyGenerationService: KeyGenerationService, private accountService: AccountService, private restrictedItemTypesService: RestrictedItemTypesService, ) {} @@ -238,7 +238,7 @@ export class ImportService implements ImportServiceAbstraction { this.encryptService, this.i18nService, this.cipherService, - this.pinService, + this.keyGenerationService, this.accountService, promptForPassword_callback, ); diff --git a/libs/state/src/state-migrations/migrate.ts b/libs/state/src/state-migrations/migrate.ts index bf4cd17adba..a051c25695a 100644 --- a/libs/state/src/state-migrations/migrate.ts +++ b/libs/state/src/state-migrations/migrate.ts @@ -70,12 +70,13 @@ import { RemoveAcBannersDismissed } from "./migrations/70-remove-ac-banner-dismi import { RemoveNewCustomizationOptionsCalloutDismissed } from "./migrations/71-remove-new-customization-options-callout-dismissed"; import { RemoveAccountDeprovisioningBannerDismissed } from "./migrations/72-remove-account-deprovisioning-banner-dismissed"; import { AddMasterPasswordUnlockData } from "./migrations/73-add-master-password-unlock-data"; +import { RemoveLegacyPin } from "./migrations/74-remove-legacy-pin"; import { MoveStateVersionMigrator } from "./migrations/8-move-state-version"; import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-settings-to-global"; import { MinVersionMigrator } from "./migrations/min-version"; export const MIN_VERSION = 3; -export const CURRENT_VERSION = 73; +export const CURRENT_VERSION = 74; export type MinVersion = typeof MIN_VERSION; export function createMigrationBuilder() { @@ -150,7 +151,8 @@ export function createMigrationBuilder() { .with(RemoveAcBannersDismissed, 69, 70) .with(RemoveNewCustomizationOptionsCalloutDismissed, 70, 71) .with(RemoveAccountDeprovisioningBannerDismissed, 71, 72) - .with(AddMasterPasswordUnlockData, 72, CURRENT_VERSION); + .with(AddMasterPasswordUnlockData, 72, 73) + .with(RemoveLegacyPin, 73, CURRENT_VERSION); } export async function currentVersion( diff --git a/libs/state/src/state-migrations/migrations/74-remove-legacy-pin.spec.ts b/libs/state/src/state-migrations/migrations/74-remove-legacy-pin.spec.ts new file mode 100644 index 00000000000..842410b2706 --- /dev/null +++ b/libs/state/src/state-migrations/migrations/74-remove-legacy-pin.spec.ts @@ -0,0 +1,50 @@ +import { runMigrator } from "../migration-helper.spec"; +import { IRREVERSIBLE } from "../migrator"; + +import { RemoveLegacyPin } from "./74-remove-legacy-pin"; + +describe("RemoveLegacyPin", () => { + const sut = new RemoveLegacyPin(73, 74); + + describe("migrate", () => { + it("deletes legacy pin from all users", async () => { + const output = await runMigrator(sut, { + global_account_accounts: { + user1: { + email: "user1@email.com", + name: "User 1", + emailVerified: true, + }, + user2: { + email: "user2@email.com", + name: "User 2", + emailVerified: true, + }, + }, + user_user1_pinUnlock_pinKeyEncryptedUserKeyPersistent: "abc", + user_user2_pinUnlock_pinKeyEncryptedUserKeyPersistent: "def", + }); + + expect(output).toEqual({ + global_account_accounts: { + user1: { + email: "user1@email.com", + name: "User 1", + emailVerified: true, + }, + user2: { + email: "user2@email.com", + name: "User 2", + emailVerified: true, + }, + }, + }); + }); + }); + + describe("rollback", () => { + it("is irreversible", async () => { + await expect(runMigrator(sut, {}, "rollback")).rejects.toThrow(IRREVERSIBLE); + }); + }); +}); diff --git a/libs/state/src/state-migrations/migrations/74-remove-legacy-pin.ts b/libs/state/src/state-migrations/migrations/74-remove-legacy-pin.ts new file mode 100644 index 00000000000..277ae5832b5 --- /dev/null +++ b/libs/state/src/state-migrations/migrations/74-remove-legacy-pin.ts @@ -0,0 +1,30 @@ +import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; +import { IRREVERSIBLE, Migrator } from "../migrator"; + +type ExpectedAccountType = NonNullable; + +export const PinProtectedUserKey: KeyDefinitionLike = { + key: "pinKeyEncryptedUserKeyPersistent", + stateDefinition: { + name: "pinUnlock", + }, +}; + +export class RemoveLegacyPin extends Migrator<73, 74> { + async migrate(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + async function migrateAccount(userId: string, account: ExpectedAccountType): Promise { + const pinProtectedUserKey = await helper.getFromUser(userId, PinProtectedUserKey); + + if (pinProtectedUserKey != null) { + await helper.removeFromUser(userId, PinProtectedUserKey); + } + } + + await Promise.all([...accounts.map(({ userId, account }) => migrateAccount(userId, account))]); + } + + async rollback(helper: MigrationHelper): Promise { + throw IRREVERSIBLE; + } +} diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts index c11ab3d9e6b..620f465789c 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts @@ -1,8 +1,8 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -12,7 +12,7 @@ import { KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management" import { BitwardenCsvExportType, BitwardenPasswordProtectedFileFormat } from "../types"; export class BaseVaultExportService { constructor( - protected pinService: PinServiceAbstraction, + protected keyGenerationService: KeyGenerationService, protected encryptService: EncryptService, private cryptoFunctionService: CryptoFunctionService, private kdfConfigService: KdfConfigService, @@ -26,7 +26,8 @@ export class BaseVaultExportService { const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig(userId); const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16)); - const key = await this.pinService.makePinKey(password, salt, kdfConfig); + + const key = await this.keyGenerationService.deriveVaultExportKey(password, salt, kdfConfig); const encKeyValidation = await this.encryptService.encryptString(Utils.newGuid(), key); const encText = await this.encryptService.encryptString(clearText, key); diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts index 4214873feed..33dde9ae51a 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts @@ -3,13 +3,13 @@ import * as JSZip from "jszip"; import { BehaviorSubject, of } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncryptedString, EncString, } from "@bitwarden/common/key-management/crypto/models/enc-string"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, emptyGuid, UserId } from "@bitwarden/common/types/guid"; @@ -169,7 +169,7 @@ describe("VaultExportService", () => { let exportService: IndividualVaultExportService; let cryptoFunctionService: MockProxy; let cipherService: MockProxy; - let pinService: MockProxy; + let keyGenerationService: MockProxy; let folderService: MockProxy; let keyService: MockProxy; let encryptService: MockProxy; @@ -184,7 +184,7 @@ describe("VaultExportService", () => { beforeEach(() => { cryptoFunctionService = mock(); cipherService = mock(); - pinService = mock(); + keyGenerationService = mock(); folderService = mock(); keyService = mock(); encryptService = mock(); @@ -220,7 +220,7 @@ describe("VaultExportService", () => { exportService = new IndividualVaultExportService( folderService, cipherService, - pinService, + keyGenerationService, keyService, encryptService, cryptoFunctionService, diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts index e7a97801e09..ddda96b21e0 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts @@ -5,9 +5,9 @@ import * as papa from "papaparse"; import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, UserId } from "@bitwarden/common/types/guid"; @@ -42,7 +42,7 @@ export class IndividualVaultExportService constructor( private folderService: FolderService, private cipherService: CipherService, - pinService: PinServiceAbstraction, + keyGenerationService: KeyGenerationService, private keyService: KeyService, encryptService: EncryptService, cryptoFunctionService: CryptoFunctionService, @@ -50,7 +50,7 @@ export class IndividualVaultExportService private apiService: ApiService, private restrictedItemTypesService: RestrictedItemTypesService, ) { - super(pinService, encryptService, cryptoFunctionService, kdfConfigService); + super(keyGenerationService, encryptService, cryptoFunctionService, kdfConfigService); } /** Creates an export of an individual vault (My Vault). Based on the provided format it will either be unencrypted, encrypted or password protected and in case zip is selected will include attachments diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts index 678dd600f94..ed3a16516f2 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts @@ -10,9 +10,9 @@ import { CollectionDetailsResponse, CollectionView, } from "@bitwarden/admin-console/common"; +import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -46,7 +46,7 @@ export class OrganizationVaultExportService constructor( private cipherService: CipherService, private vaultExportApiService: VaultExportApiService, - pinService: PinServiceAbstraction, + keyGenerationService: KeyGenerationService, private keyService: KeyService, encryptService: EncryptService, cryptoFunctionService: CryptoFunctionService, @@ -54,7 +54,7 @@ export class OrganizationVaultExportService kdfConfigService: KdfConfigService, private restrictedItemTypesService: RestrictedItemTypesService, ) { - super(pinService, encryptService, cryptoFunctionService, kdfConfigService); + super(keyGenerationService, encryptService, cryptoFunctionService, kdfConfigService); } /** Creates a password protected export of an organizational vault.