diff --git a/apps/browser/src/auth/background/service-factories/kdf-config-service.factory.ts b/apps/browser/src/auth/background/service-factories/kdf-config-service.factory.ts new file mode 100644 index 0000000000..eb5ba3a264 --- /dev/null +++ b/apps/browser/src/auth/background/service-factories/kdf-config-service.factory.ts @@ -0,0 +1,28 @@ +import { KdfConfigService as AbstractKdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; +import { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service"; + +import { + FactoryOptions, + CachedServices, + factory, +} from "../../../platform/background/service-factories/factory-options"; +import { + StateProviderInitOptions, + stateProviderFactory, +} from "../../../platform/background/service-factories/state-provider.factory"; + +type KdfConfigServiceFactoryOptions = FactoryOptions; + +export type KdfConfigServiceInitOptions = KdfConfigServiceFactoryOptions & StateProviderInitOptions; + +export function kdfConfigServiceFactory( + cache: { kdfConfigService?: AbstractKdfConfigService } & CachedServices, + opts: KdfConfigServiceInitOptions, +): Promise { + return factory( + cache, + "kdfConfigService", + opts, + async () => new KdfConfigService(await stateProviderFactory(cache, opts)), + ); +} diff --git a/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts b/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts index 075ba614b7..c414300431 100644 --- a/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/login-strategy-service.factory.ts @@ -68,6 +68,7 @@ import { deviceTrustServiceFactory, DeviceTrustServiceInitOptions, } from "./device-trust-service.factory"; +import { kdfConfigServiceFactory, KdfConfigServiceInitOptions } from "./kdf-config-service.factory"; import { keyConnectorServiceFactory, KeyConnectorServiceInitOptions, @@ -106,7 +107,8 @@ export type LoginStrategyServiceInitOptions = LoginStrategyServiceFactoryOptions AuthRequestServiceInitOptions & UserDecryptionOptionsServiceInitOptions & GlobalStateProviderInitOptions & - BillingAccountProfileStateServiceInitOptions; + BillingAccountProfileStateServiceInitOptions & + KdfConfigServiceInitOptions; export function loginStrategyServiceFactory( cache: { loginStrategyService?: LoginStrategyServiceAbstraction } & CachedServices, @@ -140,6 +142,7 @@ export function loginStrategyServiceFactory( await internalUserDecryptionOptionServiceFactory(cache, opts), await globalStateProviderFactory(cache, opts), await billingAccountProfileStateServiceFactory(cache, opts), + await kdfConfigServiceFactory(cache, opts), ), ); } diff --git a/apps/browser/src/auth/background/service-factories/pin-crypto-service.factory.ts b/apps/browser/src/auth/background/service-factories/pin-crypto-service.factory.ts index f5360f48fa..db16245f67 100644 --- a/apps/browser/src/auth/background/service-factories/pin-crypto-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/pin-crypto-service.factory.ts @@ -22,13 +22,16 @@ import { stateServiceFactory, } from "../../../platform/background/service-factories/state-service.factory"; +import { KdfConfigServiceInitOptions, kdfConfigServiceFactory } from "./kdf-config-service.factory"; + type PinCryptoServiceFactoryOptions = FactoryOptions; export type PinCryptoServiceInitOptions = PinCryptoServiceFactoryOptions & StateServiceInitOptions & CryptoServiceInitOptions & VaultTimeoutSettingsServiceInitOptions & - LogServiceInitOptions; + LogServiceInitOptions & + KdfConfigServiceInitOptions; export function pinCryptoServiceFactory( cache: { pinCryptoService?: PinCryptoServiceAbstraction } & CachedServices, @@ -44,6 +47,7 @@ export function pinCryptoServiceFactory( await cryptoServiceFactory(cache, opts), await vaultTimeoutSettingsServiceFactory(cache, opts), await logServiceFactory(cache, opts), + await kdfConfigServiceFactory(cache, opts), ), ); } diff --git a/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts index a8b67b21ca..d6f9ce7624 100644 --- a/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts +++ b/apps/browser/src/auth/background/service-factories/user-verification-service.factory.ts @@ -32,6 +32,7 @@ import { } from "../../../platform/background/service-factories/state-service.factory"; import { accountServiceFactory, AccountServiceInitOptions } from "./account-service.factory"; +import { KdfConfigServiceInitOptions, kdfConfigServiceFactory } from "./kdf-config-service.factory"; import { internalMasterPasswordServiceFactory, MasterPasswordServiceInitOptions, @@ -59,7 +60,8 @@ export type UserVerificationServiceInitOptions = UserVerificationServiceFactoryO PinCryptoServiceInitOptions & LogServiceInitOptions & VaultTimeoutSettingsServiceInitOptions & - PlatformUtilsServiceInitOptions; + PlatformUtilsServiceInitOptions & + KdfConfigServiceInitOptions; export function userVerificationServiceFactory( cache: { userVerificationService?: AbstractUserVerificationService } & CachedServices, @@ -82,6 +84,7 @@ export function userVerificationServiceFactory( await logServiceFactory(cache, opts), await vaultTimeoutSettingsServiceFactory(cache, opts), await platformUtilsServiceFactory(cache, opts), + await kdfConfigServiceFactory(cache, opts), ), ); } diff --git a/apps/browser/src/auth/popup/lock.component.ts b/apps/browser/src/auth/popup/lock.component.ts index d9fd237e5f..86352e2c82 100644 --- a/apps/browser/src/auth/popup/lock.component.ts +++ b/apps/browser/src/auth/popup/lock.component.ts @@ -12,6 +12,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -66,6 +67,7 @@ export class LockComponent extends BaseLockComponent { private routerService: BrowserRouterService, biometricStateService: BiometricStateService, accountService: AccountService, + kdfConfigService: KdfConfigService, ) { super( masterPasswordService, @@ -91,6 +93,7 @@ export class LockComponent extends BaseLockComponent { biometricStateService, accountService, authService, + kdfConfigService, ); this.successRoute = "/tabs/current"; this.isInitialLockScreen = (window as any).previousPopupUrl == null; diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 51db5dc401..2c825bc3e1 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -33,6 +33,7 @@ import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/aut import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; +import { KdfConfigService as kdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; @@ -48,6 +49,7 @@ import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; +import { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service"; import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service"; @@ -339,6 +341,7 @@ export default class MainBackground { intraprocessMessagingSubject: Subject>; userKeyInitService: UserKeyInitService; scriptInjectorService: BrowserScriptInjectorService; + kdfConfigService: kdfConfigServiceAbstraction; onUpdatedRan: boolean; onReplacedRan: boolean; @@ -542,6 +545,9 @@ export default class MainBackground { this.masterPasswordService = new MasterPasswordService(this.stateProvider); this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider); + + this.kdfConfigService = new KdfConfigService(this.stateProvider); + this.cryptoService = new BrowserCryptoService( this.masterPasswordService, this.keyGenerationService, @@ -553,6 +559,7 @@ export default class MainBackground { this.accountService, this.stateProvider, this.biometricStateService, + this.kdfConfigService, ); this.appIdService = new AppIdService(this.globalStateProvider); @@ -675,6 +682,7 @@ export default class MainBackground { this.userDecryptionOptionsService, this.globalStateProvider, this.billingAccountProfileStateService, + this.kdfConfigService, ); this.ssoLoginService = new SsoLoginService(this.stateProvider); @@ -725,6 +733,7 @@ export default class MainBackground { this.cryptoService, this.vaultTimeoutSettingsService, this.logService, + this.kdfConfigService, ); this.userVerificationService = new UserVerificationService( @@ -739,6 +748,7 @@ export default class MainBackground { this.logService, this.vaultTimeoutSettingsService, this.platformUtilsService, + this.kdfConfigService, ); this.vaultFilterService = new VaultFilterService( @@ -861,7 +871,7 @@ export default class MainBackground { this.cipherService, this.cryptoService, this.cryptoFunctionService, - this.stateService, + this.kdfConfigService, ); this.organizationVaultExportService = new OrganizationVaultExportService( @@ -869,8 +879,8 @@ export default class MainBackground { this.apiService, this.cryptoService, this.cryptoFunctionService, - this.stateService, this.collectionService, + this.kdfConfigService, ); this.exportService = new VaultExportService( diff --git a/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts b/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts index ed4fde162c..1f848e1d0f 100644 --- a/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts +++ b/apps/browser/src/platform/background/service-factories/crypto-service.factory.ts @@ -4,6 +4,10 @@ import { AccountServiceInitOptions, accountServiceFactory, } from "../../../auth/background/service-factories/account-service.factory"; +import { + KdfConfigServiceInitOptions, + kdfConfigServiceFactory, +} from "../../../auth/background/service-factories/kdf-config-service.factory"; import { internalMasterPasswordServiceFactory, MasterPasswordServiceInitOptions, @@ -18,7 +22,10 @@ import { } from "../../background/service-factories/log-service.factory"; import { BrowserCryptoService } from "../../services/browser-crypto.service"; -import { biometricStateServiceFactory } from "./biometric-state-service.factory"; +import { + BiometricStateServiceInitOptions, + biometricStateServiceFactory, +} from "./biometric-state-service.factory"; import { cryptoFunctionServiceFactory, CryptoFunctionServiceInitOptions, @@ -46,7 +53,9 @@ export type CryptoServiceInitOptions = CryptoServiceFactoryOptions & LogServiceInitOptions & StateServiceInitOptions & AccountServiceInitOptions & - StateProviderInitOptions; + StateProviderInitOptions & + BiometricStateServiceInitOptions & + KdfConfigServiceInitOptions; export function cryptoServiceFactory( cache: { cryptoService?: AbstractCryptoService } & CachedServices, @@ -68,6 +77,7 @@ export function cryptoServiceFactory( await accountServiceFactory(cache, opts), await stateProviderFactory(cache, opts), await biometricStateServiceFactory(cache, opts), + await kdfConfigServiceFactory(cache, opts), ), ); } diff --git a/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts b/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts index 64935ab591..259d6f154a 100644 --- a/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts +++ b/apps/browser/src/platform/services/abstractions/abstract-chrome-storage-api.service.ts @@ -78,6 +78,11 @@ export default abstract class AbstractChromeStorageService async save(key: string, obj: any): Promise { obj = objToStore(obj); + if (obj == null) { + // Safari does not support set of null values + return this.remove(key); + } + const keyedObj = { [key]: obj }; return new Promise((resolve) => { this.chromeStorageApi.set(keyedObj, () => { diff --git a/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts b/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts index 812901879d..ceadc16a58 100644 --- a/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts +++ b/apps/browser/src/platform/services/abstractions/chrome-storage-api.service.spec.ts @@ -62,6 +62,17 @@ describe("ChromeStorageApiService", () => { expect.any(Function), ); }); + + it("removes the key when the value is null", async () => { + const removeMock = chrome.storage.local.remove as jest.Mock; + removeMock.mockImplementation((key, callback) => { + delete store[key]; + callback(); + }); + const key = "key"; + await service.save(key, null); + expect(removeMock).toHaveBeenCalledWith(key, expect.any(Function)); + }); }); describe("get", () => { diff --git a/apps/browser/src/platform/services/browser-crypto.service.ts b/apps/browser/src/platform/services/browser-crypto.service.ts index d7533a22d6..cd23c916c6 100644 --- a/apps/browser/src/platform/services/browser-crypto.service.ts +++ b/apps/browser/src/platform/services/browser-crypto.service.ts @@ -1,6 +1,7 @@ import { firstValueFrom } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -28,6 +29,7 @@ export class BrowserCryptoService extends CryptoService { accountService: AccountService, stateProvider: StateProvider, private biometricStateService: BiometricStateService, + kdfConfigService: KdfConfigService, ) { super( masterPasswordService, @@ -39,6 +41,7 @@ export class BrowserCryptoService extends CryptoService { stateService, accountService, stateProvider, + kdfConfigService, ); } override async hasUserKeyStored(keySuffix: KeySuffixOptions, userId?: UserId): Promise { diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index a91e876e92..3606285c72 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -16,6 +16,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; @@ -68,6 +69,7 @@ export class LoginCommand { protected policyApiService: PolicyApiServiceAbstraction, protected orgService: OrganizationService, protected logoutCallback: () => Promise, + protected kdfConfigService: KdfConfigService, ) {} async run(email: string, password: string, options: OptionValues) { @@ -563,14 +565,12 @@ export class LoginCommand { message: "Master Password Hint (optional):", }); const masterPasswordHint = hint.input; - const kdf = await this.stateService.getKdfType(); - const kdfConfig = await this.stateService.getKdfConfig(); + const kdfConfig = await this.kdfConfigService.getKdfConfig(); // Create new key and hash new password const newMasterKey = await this.cryptoService.makeMasterKey( masterPassword, this.email.trim().toLowerCase(), - kdf, kdfConfig, ); const newPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, newMasterKey); diff --git a/apps/cli/src/auth/commands/unlock.command.ts b/apps/cli/src/auth/commands/unlock.command.ts index d52468139a..6b97b59c88 100644 --- a/apps/cli/src/auth/commands/unlock.command.ts +++ b/apps/cli/src/auth/commands/unlock.command.ts @@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request"; @@ -34,6 +35,7 @@ export class UnlockCommand { private syncService: SyncService, private organizationApiService: OrganizationApiServiceAbstraction, private logout: () => Promise, + private kdfConfigService: KdfConfigService, ) {} async run(password: string, cmdOptions: Record) { @@ -48,9 +50,8 @@ export class UnlockCommand { await this.setNewSessionKey(); const email = await this.stateService.getEmail(); - const kdf = await this.stateService.getKdfType(); - const kdfConfig = await this.stateService.getKdfConfig(); - const masterKey = await this.cryptoService.makeMasterKey(password, email, kdf, kdfConfig); + const kdfConfig = await this.kdfConfigService.getKdfConfig(); + const masterKey = await this.cryptoService.makeMasterKey(password, email, kdfConfig); const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; const storedMasterKeyHash = await firstValueFrom( this.masterPasswordService.masterKeyHash$(userId), diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index 7826fe2ee4..cfb3dc8b75 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -30,12 +30,14 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; +import { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; +import { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service"; import { TokenService } from "@bitwarden/common/auth/services/token.service"; @@ -235,6 +237,7 @@ export class Main { billingAccountProfileStateService: BillingAccountProfileStateService; providerApiService: ProviderApiServiceAbstraction; userKeyInitService: UserKeyInitService; + kdfConfigService: KdfConfigServiceAbstraction; constructor() { let p = null; @@ -357,6 +360,8 @@ export class Main { this.masterPasswordService = new MasterPasswordService(this.stateProvider); + this.kdfConfigService = new KdfConfigService(this.stateProvider); + this.cryptoService = new CryptoService( this.masterPasswordService, this.keyGenerationService, @@ -367,6 +372,7 @@ export class Main { this.stateService, this.accountService, this.stateProvider, + this.kdfConfigService, ); this.appIdService = new AppIdService(this.globalStateProvider); @@ -512,6 +518,7 @@ export class Main { this.userDecryptionOptionsService, this.globalStateProvider, this.billingAccountProfileStateService, + this.kdfConfigService, ); this.authService = new AuthService( @@ -574,6 +581,7 @@ export class Main { this.cryptoService, this.vaultTimeoutSettingsService, this.logService, + this.kdfConfigService, ); this.userVerificationService = new UserVerificationService( @@ -588,6 +596,7 @@ export class Main { this.logService, this.vaultTimeoutSettingsService, this.platformUtilsService, + this.kdfConfigService, ); this.vaultTimeoutService = new VaultTimeoutService( @@ -654,7 +663,7 @@ export class Main { this.cipherService, this.cryptoService, this.cryptoFunctionService, - this.stateService, + this.kdfConfigService, ); this.organizationExportService = new OrganizationVaultExportService( @@ -662,8 +671,8 @@ export class Main { this.apiService, this.cryptoService, this.cryptoFunctionService, - this.stateService, this.collectionService, + this.kdfConfigService, ); this.exportService = new VaultExportService( diff --git a/apps/cli/src/commands/serve.command.ts b/apps/cli/src/commands/serve.command.ts index 76447f769c..7a11dc4b4a 100644 --- a/apps/cli/src/commands/serve.command.ts +++ b/apps/cli/src/commands/serve.command.ts @@ -134,6 +134,7 @@ export class ServeCommand { this.main.syncService, this.main.organizationApiService, async () => await this.main.logout(), + this.main.kdfConfigService, ); this.sendCreateCommand = new SendCreateCommand( diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index fa71a88f54..5d26b0850e 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -156,6 +156,7 @@ export class Program { this.main.policyApiService, this.main.organizationService, async () => await this.main.logout(), + this.main.kdfConfigService, ); const response = await command.run(email, password, options); this.processResponse(response, true); @@ -265,6 +266,7 @@ export class Program { this.main.syncService, this.main.organizationApiService, async () => await this.main.logout(), + this.main.kdfConfigService, ); const response = await command.run(password, cmd); this.processResponse(response); @@ -627,6 +629,7 @@ export class Program { this.main.syncService, this.main.organizationApiService, this.main.logout, + this.main.kdfConfigService, ); const response = await command.run(null, null); if (!response.success) { diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index be4769ddf1..0c950eb4cc 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -21,6 +21,7 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaul import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; +import { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; @@ -257,6 +258,7 @@ const safeProviders: SafeProvider[] = [ AccountServiceAbstraction, StateProvider, BiometricStateService, + KdfConfigServiceAbstraction, ], }), safeProvider({ diff --git a/apps/desktop/src/auth/lock.component.spec.ts b/apps/desktop/src/auth/lock.component.spec.ts index a9188ed463..2137b707f6 100644 --- a/apps/desktop/src/auth/lock.component.spec.ts +++ b/apps/desktop/src/auth/lock.component.spec.ts @@ -15,6 +15,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.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 { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; @@ -169,6 +170,10 @@ describe("LockComponent", () => { provide: AuthService, useValue: mock(), }, + { + provide: KdfConfigService, + useValue: mock(), + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/apps/desktop/src/auth/lock.component.ts b/apps/desktop/src/auth/lock.component.ts index e7363cb80e..d95df419e1 100644 --- a/apps/desktop/src/auth/lock.component.ts +++ b/apps/desktop/src/auth/lock.component.ts @@ -12,6 +12,7 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.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 { DeviceType } from "@bitwarden/common/enums"; @@ -65,6 +66,7 @@ export class LockComponent extends BaseLockComponent { biometricStateService: BiometricStateService, accountService: AccountService, authService: AuthService, + kdfConfigService: KdfConfigService, ) { super( masterPasswordService, @@ -90,6 +92,7 @@ export class LockComponent extends BaseLockComponent { biometricStateService, accountService, authService, + kdfConfigService, ); } diff --git a/apps/desktop/src/auth/set-password.component.ts b/apps/desktop/src/auth/set-password.component.ts index 93dfe0abd8..feea5edd86 100644 --- a/apps/desktop/src/auth/set-password.component.ts +++ b/apps/desktop/src/auth/set-password.component.ts @@ -9,6 +9,7 @@ import { OrganizationUserService } from "@bitwarden/common/admin-console/abstrac import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; @@ -52,6 +53,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, ssoLoginService: SsoLoginServiceAbstraction, dialogService: DialogService, + kdfConfigService: KdfConfigService, ) { super( accountService, @@ -73,6 +75,7 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On userDecryptionOptionsService, ssoLoginService, dialogService, + kdfConfigService, ); } diff --git a/apps/desktop/src/platform/services/electron-crypto.service.spec.ts b/apps/desktop/src/platform/services/electron-crypto.service.spec.ts index 3d9171b52e..86463dccaa 100644 --- a/apps/desktop/src/platform/services/electron-crypto.service.spec.ts +++ b/apps/desktop/src/platform/services/electron-crypto.service.spec.ts @@ -1,6 +1,7 @@ import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider"; import { mock } from "jest-mock-extended"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { FakeMasterPasswordService } from "@bitwarden/common/auth/services/master-password/fake-master-password.service"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -35,6 +36,7 @@ describe("electronCryptoService", () => { let accountService: FakeAccountService; let stateProvider: FakeStateProvider; const biometricStateService = mock(); + const kdfConfigService = mock(); const mockUserId = "mock user id" as UserId; @@ -54,6 +56,7 @@ describe("electronCryptoService", () => { accountService, stateProvider, biometricStateService, + kdfConfigService, ); }); diff --git a/apps/desktop/src/platform/services/electron-crypto.service.ts b/apps/desktop/src/platform/services/electron-crypto.service.ts index d113a18200..0ed0f73d41 100644 --- a/apps/desktop/src/platform/services/electron-crypto.service.ts +++ b/apps/desktop/src/platform/services/electron-crypto.service.ts @@ -1,6 +1,7 @@ import { firstValueFrom } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; @@ -31,6 +32,7 @@ export class ElectronCryptoService extends CryptoService { accountService: AccountService, stateProvider: StateProvider, private biometricStateService: BiometricStateService, + kdfConfigService: KdfConfigService, ) { super( masterPasswordService, @@ -42,6 +44,7 @@ export class ElectronCryptoService extends CryptoService { stateService, accountService, stateProvider, + kdfConfigService, ); } diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index cd94513f19..fcdbe1e496 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -7,10 +7,15 @@ import { OrganizationUserResetPasswordRequest, OrganizationUserResetPasswordWithIdRequest, } from "@bitwarden/common/admin-console/abstractions/organization-user/requests"; -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; +import { + Argon2KdfConfig, + KdfConfig, + PBKDF2KdfConfig, +} from "@bitwarden/common/auth/models/domain/kdf-config"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { KdfType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -90,12 +95,17 @@ export class OrganizationUserResetPasswordService { const decValue = await this.cryptoService.rsaDecrypt(response.resetPasswordKey, decPrivateKey); const existingUserKey = new SymmetricCryptoKey(decValue) as UserKey; + // determine Kdf Algorithm + const kdfConfig: KdfConfig = + response.kdf === KdfType.PBKDF2_SHA256 + ? new PBKDF2KdfConfig(response.kdfIterations) + : new Argon2KdfConfig(response.kdfIterations, response.kdfMemory, response.kdfParallelism); + // Create new master key and hash new password const newMasterKey = await this.cryptoService.makeMasterKey( newMasterPassword, email.trim().toLowerCase(), - response.kdf, - new KdfConfig(response.kdfIterations, response.kdfMemory, response.kdfParallelism), + kdfConfig, ); const newMasterKeyHash = await this.cryptoService.hashMasterKey( newMasterPassword, diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts index 6bcb933e51..dbc1ce820c 100644 --- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts +++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts @@ -3,10 +3,15 @@ import { Injectable } from "@angular/core"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; +import { + Argon2KdfConfig, + KdfConfig, + PBKDF2KdfConfig, +} from "@bitwarden/common/auth/models/domain/kdf-config"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { KdfType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -231,16 +236,22 @@ export class EmergencyAccessService { const grantorUserKey = new SymmetricCryptoKey(grantorKeyBuffer) as UserKey; - const masterKey = await this.cryptoService.makeMasterKey( - masterPassword, - email, - takeoverResponse.kdf, - new KdfConfig( - takeoverResponse.kdfIterations, - takeoverResponse.kdfMemory, - takeoverResponse.kdfParallelism, - ), - ); + let config: KdfConfig; + + switch (takeoverResponse.kdf) { + case KdfType.PBKDF2_SHA256: + config = new PBKDF2KdfConfig(takeoverResponse.kdfIterations); + break; + case KdfType.Argon2id: + config = new Argon2KdfConfig( + takeoverResponse.kdfIterations, + takeoverResponse.kdfMemory, + takeoverResponse.kdfParallelism, + ); + break; + } + + const masterKey = await this.cryptoService.makeMasterKey(masterPassword, email, config); const masterKeyHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey); const encKey = await this.cryptoService.encryptUserKeyWithMasterKey(masterKey, grantorUserKey); 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 ed665fe773..ec68556931 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 @@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; 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"; @@ -47,6 +48,7 @@ describe("KeyRotationService", () => { let mockEncryptService: MockProxy; let mockStateService: MockProxy; let mockConfigService: MockProxy; + let mockKdfConfigService: MockProxy; const mockUserId = Utils.newGuid() as UserId; const mockAccountService: FakeAccountService = mockAccountServiceWith(mockUserId); @@ -65,6 +67,7 @@ describe("KeyRotationService", () => { mockEncryptService = mock(); mockStateService = mock(); mockConfigService = mock(); + mockKdfConfigService = mock(); keyRotationService = new UserKeyRotationService( mockMasterPasswordService, @@ -80,6 +83,7 @@ describe("KeyRotationService", () => { mockStateService, mockAccountService, mockConfigService, + mockKdfConfigService, ); }); 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 2ff48809a0..94c6208115 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,6 +3,7 @@ import { firstValueFrom } from "rxjs"; import { AccountService } 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 { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -39,6 +40,7 @@ export class UserKeyRotationService { private stateService: StateService, private accountService: AccountService, private configService: ConfigService, + private kdfConfigService: KdfConfigService, ) {} /** @@ -54,8 +56,7 @@ export class UserKeyRotationService { const masterKey = await this.cryptoService.makeMasterKey( masterPassword, await this.stateService.getEmail(), - await this.stateService.getKdfType(), - await this.stateService.getKdfConfig(), + await this.kdfConfigService.getKdfConfig(), ); if (!masterKey) { diff --git a/apps/web/src/app/auth/settings/account/change-email.component.ts b/apps/web/src/app/auth/settings/account/change-email.component.ts index 372b344b10..e5a3c72337 100644 --- a/apps/web/src/app/auth/settings/account/change-email.component.ts +++ b/apps/web/src/app/auth/settings/account/change-email.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { EmailTokenRequest } from "@bitwarden/common/auth/models/request/email-token.request"; import { EmailRequest } from "@bitwarden/common/auth/models/request/email.request"; @@ -37,6 +38,7 @@ export class ChangeEmailComponent implements OnInit { private logService: LogService, private stateService: StateService, private formBuilder: FormBuilder, + private kdfConfigService: KdfConfigService, ) {} async ngOnInit() { @@ -83,12 +85,10 @@ export class ChangeEmailComponent implements OnInit { step1Value.masterPassword, await this.cryptoService.getOrDeriveMasterKey(step1Value.masterPassword), ); - const kdf = await this.stateService.getKdfType(); - const kdfConfig = await this.stateService.getKdfConfig(); + const kdfConfig = await this.kdfConfigService.getKdfConfig(); const newMasterKey = await this.cryptoService.makeMasterKey( step1Value.masterPassword, newEmail, - kdf, kdfConfig, ); request.newMasterPasswordHash = await this.cryptoService.hashMasterKey( diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts index 6d16893170..454d96f2bd 100644 --- a/apps/web/src/app/auth/settings/change-password.component.ts +++ b/apps/web/src/app/auth/settings/change-password.component.ts @@ -5,6 +5,7 @@ import { ChangePasswordComponent as BaseChangePasswordComponent } from "@bitward import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.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 { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -48,6 +49,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { dialogService: DialogService, private userVerificationService: UserVerificationService, private keyRotationService: UserKeyRotationService, + kdfConfigService: KdfConfigService, ) { super( i18nService, @@ -58,6 +60,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { policyService, stateService, dialogService, + kdfConfigService, ); } diff --git a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts index 575c6f4a23..73b1fa775d 100644 --- a/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts +++ b/apps/web/src/app/auth/settings/emergency-access/takeover/emergency-access-takeover.component.ts @@ -5,6 +5,7 @@ import { takeUntil } from "rxjs"; import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -58,6 +59,7 @@ export class EmergencyAccessTakeoverComponent private logService: LogService, dialogService: DialogService, private dialogRef: DialogRef, + kdfConfigService: KdfConfigService, ) { super( i18nService, @@ -68,6 +70,7 @@ export class EmergencyAccessTakeoverComponent policyService, stateService, dialogService, + kdfConfigService, ); } diff --git a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts index 0284c665d8..985fb3e038 100644 --- a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts +++ b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf-confirmation.component.ts @@ -3,6 +3,7 @@ import { Component, Inject } from "@angular/core"; import { FormGroup, FormControl, Validators } from "@angular/forms"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -18,7 +19,6 @@ import { KdfType } from "@bitwarden/common/platform/enums"; templateUrl: "change-kdf-confirmation.component.html", }) export class ChangeKdfConfirmationComponent { - kdf: KdfType; kdfConfig: KdfConfig; form = new FormGroup({ @@ -37,9 +37,9 @@ export class ChangeKdfConfirmationComponent { private messagingService: MessagingService, private stateService: StateService, private logService: LogService, - @Inject(DIALOG_DATA) params: { kdf: KdfType; kdfConfig: KdfConfig }, + private kdfConfigService: KdfConfigService, + @Inject(DIALOG_DATA) params: { kdfConfig: KdfConfig }, ) { - this.kdf = params.kdf; this.kdfConfig = params.kdfConfig; this.masterPassword = null; } @@ -65,22 +65,24 @@ export class ChangeKdfConfirmationComponent { private async makeKeyAndSaveAsync() { const masterPassword = this.form.value.masterPassword; + + // Ensure the KDF config is valid. + this.kdfConfig.validateKdfConfig(); + const request = new KdfRequest(); - request.kdf = this.kdf; + request.kdf = this.kdfConfig.kdfType; request.kdfIterations = this.kdfConfig.iterations; - request.kdfMemory = this.kdfConfig.memory; - request.kdfParallelism = this.kdfConfig.parallelism; + if (this.kdfConfig.kdfType === KdfType.Argon2id) { + request.kdfMemory = this.kdfConfig.memory; + request.kdfParallelism = this.kdfConfig.parallelism; + } const masterKey = await this.cryptoService.getOrDeriveMasterKey(masterPassword); request.masterPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey); const email = await this.stateService.getEmail(); - // Ensure the KDF config is valid. - this.cryptoService.validateKdfConfig(this.kdf, this.kdfConfig); - const newMasterKey = await this.cryptoService.makeMasterKey( masterPassword, email, - this.kdf, this.kdfConfig, ); request.newMasterPasswordHash = await this.cryptoService.hashMasterKey( diff --git a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html index 9b16c446be..8b1dec8e13 100644 --- a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html +++ b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.html @@ -19,14 +19,14 @@ - +
- + - +
- +

{{ "kdfIterationsDesc" | i18n: (PBKDF2_ITERATIONS.defaultValue | number) }}

@@ -100,7 +100,7 @@ {{ "kdfIterationsWarning" | i18n: (100000 | number) }}
- +

{{ "argon2Desc" | i18n }}

{{ "argon2Warning" | i18n }}
diff --git a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts index d91fb8d083..5c05f1ba2a 100644 --- a/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts +++ b/apps/web/src/app/auth/settings/security/change-kdf/change-kdf.component.ts @@ -1,7 +1,11 @@ import { Component, OnInit } from "@angular/core"; -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; +import { + Argon2KdfConfig, + KdfConfig, + PBKDF2KdfConfig, +} from "@bitwarden/common/auth/models/domain/kdf-config"; import { DEFAULT_KDF_CONFIG, PBKDF2_ITERATIONS, @@ -19,7 +23,6 @@ import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.compon templateUrl: "change-kdf.component.html", }) export class ChangeKdfComponent implements OnInit { - kdf = KdfType.PBKDF2_SHA256; kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG; kdfType = KdfType; kdfOptions: any[] = []; @@ -31,8 +34,8 @@ export class ChangeKdfComponent implements OnInit { protected ARGON2_PARALLELISM = ARGON2_PARALLELISM; constructor( - private stateService: StateService, private dialogService: DialogService, + private kdfConfigService: KdfConfigService, ) { this.kdfOptions = [ { name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 }, @@ -41,19 +44,22 @@ export class ChangeKdfComponent implements OnInit { } async ngOnInit() { - this.kdf = await this.stateService.getKdfType(); - this.kdfConfig = await this.stateService.getKdfConfig(); + this.kdfConfig = await this.kdfConfigService.getKdfConfig(); + } + + isPBKDF2(t: KdfConfig): t is PBKDF2KdfConfig { + return t instanceof PBKDF2KdfConfig; + } + + isArgon2(t: KdfConfig): t is Argon2KdfConfig { + return t instanceof Argon2KdfConfig; } async onChangeKdf(newValue: KdfType) { if (newValue === KdfType.PBKDF2_SHA256) { - this.kdfConfig = new KdfConfig(PBKDF2_ITERATIONS.defaultValue); + this.kdfConfig = new PBKDF2KdfConfig(); } else if (newValue === KdfType.Argon2id) { - this.kdfConfig = new KdfConfig( - ARGON2_ITERATIONS.defaultValue, - ARGON2_MEMORY.defaultValue, - ARGON2_PARALLELISM.defaultValue, - ); + this.kdfConfig = new Argon2KdfConfig(); } else { throw new Error("Unknown KDF type."); } @@ -62,7 +68,6 @@ export class ChangeKdfComponent implements OnInit { async openConfirmationModal() { this.dialogService.open(ChangeKdfConfirmationComponent, { data: { - kdf: this.kdf, kdfConfig: this.kdfConfig, }, }); diff --git a/apps/web/src/app/auth/update-password.component.ts b/apps/web/src/app/auth/update-password.component.ts index 2844d2d862..123e3e4ac1 100644 --- a/apps/web/src/app/auth/update-password.component.ts +++ b/apps/web/src/app/auth/update-password.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.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 { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -32,6 +33,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent { stateService: StateService, userVerificationService: UserVerificationService, dialogService: DialogService, + kdfConfigService: KdfConfigService, ) { super( router, @@ -46,6 +48,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent { userVerificationService, logService, dialogService, + kdfConfigService, ); } } diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 2c20328336..c97dd93d76 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -35,6 +35,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; @@ -184,6 +185,7 @@ export class VaultComponent implements OnInit, OnDestroy { private apiService: ApiService, private userVerificationService: UserVerificationService, private billingAccountProfileStateService: BillingAccountProfileStateService, + protected kdfConfigService: KdfConfigService, ) {} async ngOnInit() { @@ -972,10 +974,10 @@ export class VaultComponent implements OnInit, OnDestroy { } async isLowKdfIteration() { - const kdfType = await this.stateService.getKdfType(); - const kdfOptions = await this.stateService.getKdfConfig(); + const kdfConfig = await this.kdfConfigService.getKdfConfig(); return ( - kdfType === KdfType.PBKDF2_SHA256 && kdfOptions.iterations < PBKDF2_ITERATIONS.defaultValue + kdfConfig.kdfType === KdfType.PBKDF2_SHA256 && + kdfConfig.iterations < PBKDF2_ITERATIONS.defaultValue ); } diff --git a/libs/angular/src/auth/components/change-password.component.ts b/libs/angular/src/auth/components/change-password.component.ts index 1086428f4c..b1f75de58c 100644 --- a/libs/angular/src/auth/components/change-password.component.ts +++ b/libs/angular/src/auth/components/change-password.component.ts @@ -3,13 +3,13 @@ import { Subject, takeUntil } from "rxjs"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { KdfType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; @@ -31,7 +31,6 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { minimumLength = Utils.minimumPasswordLength; protected email: string; - protected kdf: KdfType; protected kdfConfig: KdfConfig; protected destroy$ = new Subject(); @@ -45,6 +44,7 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { protected policyService: PolicyService, protected stateService: StateService, protected dialogService: DialogService, + protected kdfConfigService: KdfConfigService, ) {} async ngOnInit() { @@ -73,18 +73,14 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { } const email = await this.stateService.getEmail(); - if (this.kdf == null) { - this.kdf = await this.stateService.getKdfType(); - } if (this.kdfConfig == null) { - this.kdfConfig = await this.stateService.getKdfConfig(); + this.kdfConfig = await this.kdfConfigService.getKdfConfig(); } // Create new master key const newMasterKey = await this.cryptoService.makeMasterKey( this.masterPassword, email.trim().toLowerCase(), - this.kdf, this.kdfConfig, ); const newMasterKeyHash = await this.cryptoService.hashMasterKey( diff --git a/libs/angular/src/auth/components/lock.component.ts b/libs/angular/src/auth/components/lock.component.ts index c32871b5c6..7eb30d759a 100644 --- a/libs/angular/src/auth/components/lock.component.ts +++ b/libs/angular/src/auth/components/lock.component.ts @@ -13,6 +13,7 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.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 { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -84,6 +85,7 @@ export class LockComponent implements OnInit, OnDestroy { protected biometricStateService: BiometricStateService, protected accountService: AccountService, protected authService: AuthService, + protected kdfConfigService: KdfConfigService, ) {} async ngOnInit() { @@ -214,14 +216,12 @@ export class LockComponent implements OnInit, OnDestroy { } private async doUnlockWithMasterPassword() { + const kdfConfig = await this.kdfConfigService.getKdfConfig(); const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - const kdf = await this.stateService.getKdfType(); - const kdfConfig = await this.stateService.getKdfConfig(); const masterKey = await this.cryptoService.makeMasterKey( this.masterPassword, this.email, - kdf, kdfConfig, ); const storedMasterKeyHash = await firstValueFrom( diff --git a/libs/angular/src/auth/components/register.component.ts b/libs/angular/src/auth/components/register.component.ts index 3cffebe71b..2ba7669290 100644 --- a/libs/angular/src/auth/components/register.component.ts +++ b/libs/angular/src/auth/components/register.component.ts @@ -15,7 +15,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { DEFAULT_KDF_CONFIG, DEFAULT_KDF_TYPE } from "@bitwarden/common/platform/enums"; +import { DEFAULT_KDF_CONFIG } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { DialogService } from "@bitwarden/components"; @@ -273,9 +273,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn name: string, ): Promise { const hint = this.formGroup.value.hint; - const kdf = DEFAULT_KDF_TYPE; const kdfConfig = DEFAULT_KDF_CONFIG; - const key = await this.cryptoService.makeMasterKey(masterPassword, email, kdf, kdfConfig); + const key = await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig); const newUserKey = await this.cryptoService.makeUserKey(key); const masterKeyHash = await this.cryptoService.hashMasterKey(masterPassword, key); const keys = await this.cryptoService.makeKeyPair(newUserKey[0]); @@ -287,10 +286,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn newUserKey[1].encryptedString, this.referenceData, this.captchaToken, - kdf, + kdfConfig.kdfType, kdfConfig.iterations, - kdfConfig.memory, - kdfConfig.parallelism, ); request.keys = new KeysRequest(keys[0], keys[1].encryptedString); const orgInvite = await this.stateService.getOrganizationInvitation(); diff --git a/libs/angular/src/auth/components/set-password.component.ts b/libs/angular/src/auth/components/set-password.component.ts index eebf87655b..00a36434b0 100644 --- a/libs/angular/src/auth/components/set-password.component.ts +++ b/libs/angular/src/auth/components/set-password.component.ts @@ -13,6 +13,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { OrganizationAutoEnrollStatusResponse } from "@bitwarden/common/admin-console/models/response/organization-auto-enroll-status.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; @@ -23,11 +24,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { - HashPurpose, - DEFAULT_KDF_TYPE, - DEFAULT_KDF_CONFIG, -} from "@bitwarden/common/platform/enums"; +import { HashPurpose, DEFAULT_KDF_CONFIG } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; @@ -73,6 +70,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { private userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, private ssoLoginService: SsoLoginServiceAbstraction, dialogService: DialogService, + kdfConfigService: KdfConfigService, ) { super( i18nService, @@ -83,6 +81,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { policyService, stateService, dialogService, + kdfConfigService, ); } @@ -139,7 +138,6 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { } async setupSubmitActions() { - this.kdf = DEFAULT_KDF_TYPE; this.kdfConfig = DEFAULT_KDF_CONFIG; return true; } @@ -169,10 +167,8 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { this.hint, this.orgSsoIdentifier, keysRequest, - this.kdf, + this.kdfConfig.kdfType, //always PBKDF2 --> see this.setupSubmitActions this.kdfConfig.iterations, - this.kdfConfig.memory, - this.kdfConfig.parallelism, ); try { if (this.resetPasswordAutoEnroll) { @@ -246,9 +242,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { ); userDecryptionOpts.hasMasterPassword = true; await this.userDecryptionOptionsService.setUserDecryptionOptions(userDecryptionOpts); - - await this.stateService.setKdfType(this.kdf); - await this.stateService.setKdfConfig(this.kdfConfig); + await this.kdfConfigService.setKdfConfig(this.userId, this.kdfConfig); await this.masterPasswordService.setMasterKey(masterKey, this.userId); await this.cryptoService.setUserKey(userKey[0]); diff --git a/libs/angular/src/auth/components/set-pin.component.ts b/libs/angular/src/auth/components/set-pin.component.ts index ade23f4fef..f0b66b8e70 100644 --- a/libs/angular/src/auth/components/set-pin.component.ts +++ b/libs/angular/src/auth/components/set-pin.component.ts @@ -2,6 +2,7 @@ import { DialogRef } from "@angular/cdk/dialog"; import { Directive, OnInit } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; @@ -22,6 +23,7 @@ export class SetPinComponent implements OnInit { private userVerificationService: UserVerificationService, private stateService: StateService, private formBuilder: FormBuilder, + private kdfConfigService: KdfConfigService, ) {} async ngOnInit() { @@ -43,8 +45,7 @@ export class SetPinComponent implements OnInit { const pinKey = await this.cryptoService.makePinKey( pin, await this.stateService.getEmail(), - await this.stateService.getKdfType(), - await this.stateService.getKdfConfig(), + await this.kdfConfigService.getKdfConfig(), ); const userKey = await this.cryptoService.getUserKey(); const pinProtectedKey = await this.cryptoService.encrypt(userKey.key, pinKey); diff --git a/libs/angular/src/auth/components/update-password.component.ts b/libs/angular/src/auth/components/update-password.component.ts index 2ffffb6c5d..264f351542 100644 --- a/libs/angular/src/auth/components/update-password.component.ts +++ b/libs/angular/src/auth/components/update-password.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; @@ -44,6 +45,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent { private userVerificationService: UserVerificationService, private logService: LogService, dialogService: DialogService, + kdfConfigService: KdfConfigService, ) { super( i18nService, @@ -54,6 +56,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent { policyService, stateService, dialogService, + kdfConfigService, ); } @@ -90,8 +93,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent { return false; } - this.kdf = await this.stateService.getKdfType(); - this.kdfConfig = await this.stateService.getKdfConfig(); + this.kdfConfig = await this.kdfConfigService.getKdfConfig(); return true; } diff --git a/libs/angular/src/auth/components/update-temp-password.component.ts b/libs/angular/src/auth/components/update-temp-password.component.ts index 54fdc83239..bd6da6b760 100644 --- a/libs/angular/src/auth/components/update-temp-password.component.ts +++ b/libs/angular/src/auth/components/update-temp-password.component.ts @@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +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"; @@ -59,6 +60,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { private userVerificationService: UserVerificationService, protected router: Router, dialogService: DialogService, + kdfConfigService: KdfConfigService, private accountService: AccountService, private masterPasswordService: InternalMasterPasswordServiceAbstraction, ) { @@ -71,6 +73,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { policyService, stateService, dialogService, + kdfConfigService, ); } @@ -104,8 +107,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { async setupSubmitActions(): Promise { this.email = await this.stateService.getEmail(); - this.kdf = await this.stateService.getKdfType(); - this.kdfConfig = await this.stateService.getKdfConfig(); + this.kdfConfig = await this.kdfConfigService.getKdfConfig(); return true; } @@ -124,7 +126,6 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { const newMasterKey = await this.cryptoService.makeMasterKey( this.masterPassword, this.email.trim().toLowerCase(), - this.kdf, this.kdfConfig, ); const newPasswordHash = await this.cryptoService.hashMasterKey( diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 42879a8424..88494a1cbb 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -63,6 +63,7 @@ import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/aut import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction"; +import { KdfConfigService as KdfConfigServiceAbstraction } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction, @@ -85,6 +86,7 @@ import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation"; import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation"; import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation"; +import { KdfConfigService } from "@bitwarden/common/auth/services/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service"; import { MasterPasswordService } from "@bitwarden/common/auth/services/master-password/master-password.service"; import { PasswordResetEnrollmentServiceImplementation } from "@bitwarden/common/auth/services/password-reset-enrollment.service.implementation"; @@ -390,6 +392,7 @@ const safeProviders: SafeProvider[] = [ InternalUserDecryptionOptionsServiceAbstraction, GlobalStateProvider, BillingAccountProfileStateService, + KdfConfigServiceAbstraction, ], }), safeProvider({ @@ -543,6 +546,7 @@ const safeProviders: SafeProvider[] = [ StateServiceAbstraction, AccountServiceAbstraction, StateProvider, + KdfConfigServiceAbstraction, ], }), safeProvider({ @@ -713,7 +717,7 @@ const safeProviders: SafeProvider[] = [ CipherServiceAbstraction, CryptoServiceAbstraction, CryptoFunctionServiceAbstraction, - StateServiceAbstraction, + KdfConfigServiceAbstraction, ], }), safeProvider({ @@ -724,8 +728,8 @@ const safeProviders: SafeProvider[] = [ ApiServiceAbstraction, CryptoServiceAbstraction, CryptoFunctionServiceAbstraction, - StateServiceAbstraction, CollectionServiceAbstraction, + KdfConfigServiceAbstraction, ], }), safeProvider({ @@ -834,6 +838,7 @@ const safeProviders: SafeProvider[] = [ LogService, VaultTimeoutSettingsServiceAbstraction, PlatformUtilsServiceAbstraction, + KdfConfigServiceAbstraction, ], }), safeProvider({ @@ -985,6 +990,7 @@ const safeProviders: SafeProvider[] = [ CryptoServiceAbstraction, VaultTimeoutSettingsServiceAbstraction, LogService, + KdfConfigServiceAbstraction, ], }), safeProvider({ @@ -1150,6 +1156,11 @@ const safeProviders: SafeProvider[] = [ useClass: ProviderApiService, deps: [ApiServiceAbstraction], }), + safeProvider({ + provide: KdfConfigServiceAbstraction, + useClass: KdfConfigService, + deps: [StateProvider], + }), ]; function encryptServiceFactory( diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts index 6ec63ef4f6..e165e3456d 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts @@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response"; @@ -44,6 +45,7 @@ describe("AuthRequestLoginStrategy", () => { let userDecryptionOptions: MockProxy; let deviceTrustService: MockProxy; let billingAccountProfileStateService: MockProxy; + let kdfConfigService: MockProxy; const mockUserId = Utils.newGuid() as UserId; let accountService: FakeAccountService; @@ -77,6 +79,7 @@ describe("AuthRequestLoginStrategy", () => { userDecryptionOptions = mock(); deviceTrustService = mock(); billingAccountProfileStateService = mock(); + kdfConfigService = mock(); accountService = mockAccountServiceWith(mockUserId); masterPasswordService = new FakeMasterPasswordService(); @@ -101,6 +104,7 @@ describe("AuthRequestLoginStrategy", () => { userDecryptionOptions, deviceTrustService, billingAccountProfileStateService, + kdfConfigService, ); tokenResponse = identityTokenResponseFactory(); diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts index 5220e432de..a66d987984 100644 --- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.ts @@ -3,6 +3,7 @@ import { Jsonify } from "type-fest"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; @@ -63,6 +64,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy { userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, private deviceTrustService: DeviceTrustServiceAbstraction, billingAccountProfileStateService: BillingAccountProfileStateService, + kdfConfigService: KdfConfigService, ) { super( accountService, @@ -78,6 +80,7 @@ export class AuthRequestLoginStrategy extends LoginStrategy { twoFactorService, userDecryptionOptionsService, billingAccountProfileStateService, + kdfConfigService, ); this.cache = new BehaviorSubject(data); diff --git a/libs/auth/src/common/login-strategies/login.strategy.spec.ts b/libs/auth/src/common/login-strategies/login.strategy.spec.ts index e0833342ce..7c022db23b 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.spec.ts @@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; @@ -117,6 +118,7 @@ describe("LoginStrategy", () => { let policyService: MockProxy; let passwordStrengthService: MockProxy; let billingAccountProfileStateService: MockProxy; + let kdfConfigService: MockProxy; let passwordLoginStrategy: PasswordLoginStrategy; let credentials: PasswordLoginCredentials; @@ -136,6 +138,7 @@ describe("LoginStrategy", () => { stateService = mock(); twoFactorService = mock(); userDecryptionOptionsService = mock(); + kdfConfigService = mock(); policyService = mock(); passwordStrengthService = mock(); billingAccountProfileStateService = mock(); @@ -162,6 +165,7 @@ describe("LoginStrategy", () => { policyService, loginStrategyService, billingAccountProfileStateService, + kdfConfigService, ); credentials = new PasswordLoginCredentials(email, masterPassword); }); @@ -208,8 +212,6 @@ describe("LoginStrategy", () => { userId: userId, name: name, email: email, - kdfIterations: kdfIterations, - kdfType: kdf, }, }, keys: new AccountKeys(), @@ -404,6 +406,7 @@ describe("LoginStrategy", () => { policyService, loginStrategyService, billingAccountProfileStateService, + kdfConfigService, ); apiService.postIdentityToken.mockResolvedValue(identityTokenResponseFactory()); diff --git a/libs/auth/src/common/login-strategies/login.strategy.ts b/libs/auth/src/common/login-strategies/login.strategy.ts index 6985fbf093..ff687a9ece 100644 --- a/libs/auth/src/common/login-strategies/login.strategy.ts +++ b/libs/auth/src/common/login-strategies/login.strategy.ts @@ -2,12 +2,14 @@ import { BehaviorSubject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; +import { Argon2KdfConfig, PBKDF2KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { DeviceRequest } from "@bitwarden/common/auth/models/request/identity-token/device.request"; import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request"; import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/sso-token.request"; @@ -27,6 +29,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { KdfType } from "@bitwarden/common/platform/enums"; import { Account, AccountProfile } from "@bitwarden/common/platform/models/domain/account"; import { UserId } from "@bitwarden/common/types/guid"; @@ -72,6 +75,7 @@ export abstract class LoginStrategy { protected twoFactorService: TwoFactorService, protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, protected billingAccountProfileStateService: BillingAccountProfileStateService, + protected KdfConfigService: KdfConfigService, ) {} abstract exportCache(): CacheData; @@ -190,10 +194,6 @@ export abstract class LoginStrategy { userId, name: accountInformation.name, email: accountInformation.email, - kdfIterations: tokenResponse.kdfIterations, - kdfMemory: tokenResponse.kdfMemory, - kdfParallelism: tokenResponse.kdfParallelism, - kdfType: tokenResponse.kdf, }, }, }), @@ -203,6 +203,17 @@ export abstract class LoginStrategy { UserDecryptionOptions.fromResponse(tokenResponse), ); + await this.KdfConfigService.setKdfConfig( + userId as UserId, + tokenResponse.kdf === KdfType.PBKDF2_SHA256 + ? new PBKDF2KdfConfig(tokenResponse.kdfIterations) + : new Argon2KdfConfig( + tokenResponse.kdfIterations, + tokenResponse.kdfMemory, + tokenResponse.kdfParallelism, + ), + ); + await this.billingAccountProfileStateService.setHasPremium(accountInformation.premium, false); return userId as UserId; } diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts index 7e499075e1..4456e8e178 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts @@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type"; @@ -71,6 +72,7 @@ describe("PasswordLoginStrategy", () => { let policyService: MockProxy; let passwordStrengthService: MockProxy; let billingAccountProfileStateService: MockProxy; + let kdfConfigService: MockProxy; let passwordLoginStrategy: PasswordLoginStrategy; let credentials: PasswordLoginCredentials; @@ -94,6 +96,7 @@ describe("PasswordLoginStrategy", () => { policyService = mock(); passwordStrengthService = mock(); billingAccountProfileStateService = mock(); + kdfConfigService = mock(); appIdService.getAppId.mockResolvedValue(deviceId); tokenService.decodeAccessToken.mockResolvedValue({}); @@ -127,6 +130,7 @@ describe("PasswordLoginStrategy", () => { policyService, loginStrategyService, billingAccountProfileStateService, + kdfConfigService, ); credentials = new PasswordLoginCredentials(email, masterPassword); tokenResponse = identityTokenResponseFactory(masterPasswordPolicy); diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.ts b/libs/auth/src/common/login-strategies/password-login.strategy.ts index 2490c35a00..d3ce8fa9e8 100644 --- a/libs/auth/src/common/login-strategies/password-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/password-login.strategy.ts @@ -5,6 +5,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; @@ -89,6 +90,7 @@ export class PasswordLoginStrategy extends LoginStrategy { private policyService: PolicyService, private loginStrategyService: LoginStrategyServiceAbstraction, billingAccountProfileStateService: BillingAccountProfileStateService, + kdfConfigService: KdfConfigService, ) { super( accountService, @@ -104,6 +106,7 @@ export class PasswordLoginStrategy extends LoginStrategy { twoFactorService, userDecryptionOptionsService, billingAccountProfileStateService, + kdfConfigService, ); this.cache = new BehaviorSubject(data); diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts index df33415247..3439a1c199 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts @@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; @@ -54,6 +55,7 @@ describe("SsoLoginStrategy", () => { let authRequestService: MockProxy; let i18nService: MockProxy; let billingAccountProfileStateService: MockProxy; + let kdfConfigService: MockProxy; let ssoLoginStrategy: SsoLoginStrategy; let credentials: SsoLoginCredentials; @@ -86,6 +88,7 @@ describe("SsoLoginStrategy", () => { authRequestService = mock(); i18nService = mock(); billingAccountProfileStateService = mock(); + kdfConfigService = mock(); tokenService.getTwoFactorToken.mockResolvedValue(null); appIdService.getAppId.mockResolvedValue(deviceId); @@ -110,6 +113,7 @@ describe("SsoLoginStrategy", () => { authRequestService, i18nService, billingAccountProfileStateService, + kdfConfigService, ); credentials = new SsoLoginCredentials(ssoCode, ssoCodeVerifier, ssoRedirectUrl, ssoOrgId); }); diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.ts index dc63f0fae1..c7cd9052f8 100644 --- a/libs/auth/src/common/login-strategies/sso-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/sso-login.strategy.ts @@ -3,6 +3,7 @@ import { Jsonify } from "type-fest"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; @@ -98,6 +99,7 @@ export class SsoLoginStrategy extends LoginStrategy { private authRequestService: AuthRequestServiceAbstraction, private i18nService: I18nService, billingAccountProfileStateService: BillingAccountProfileStateService, + kdfConfigService: KdfConfigService, ) { super( accountService, @@ -113,6 +115,7 @@ export class SsoLoginStrategy extends LoginStrategy { twoFactorService, userDecryptionOptionsService, billingAccountProfileStateService, + kdfConfigService, ); this.cache = new BehaviorSubject(data); diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts index 5e7d7985b1..5fce8b0b82 100644 --- a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts @@ -2,6 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; @@ -49,6 +50,7 @@ describe("UserApiLoginStrategy", () => { let keyConnectorService: MockProxy; let environmentService: MockProxy; let billingAccountProfileStateService: MockProxy; + let kdfConfigService: MockProxy; let apiLogInStrategy: UserApiLoginStrategy; let credentials: UserApiLoginCredentials; @@ -76,6 +78,7 @@ describe("UserApiLoginStrategy", () => { keyConnectorService = mock(); environmentService = mock(); billingAccountProfileStateService = mock(); + kdfConfigService = mock(); appIdService.getAppId.mockResolvedValue(deviceId); tokenService.getTwoFactorToken.mockResolvedValue(null); @@ -98,6 +101,7 @@ describe("UserApiLoginStrategy", () => { environmentService, keyConnectorService, billingAccountProfileStateService, + kdfConfigService, ); credentials = new UserApiLoginCredentials(apiClientId, apiClientSecret); diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.ts index 4a0d005b1c..d7ee6fdc4b 100644 --- a/libs/auth/src/common/login-strategies/user-api-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.ts @@ -3,6 +3,7 @@ import { Jsonify } from "type-fest"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; @@ -57,6 +58,7 @@ export class UserApiLoginStrategy extends LoginStrategy { private environmentService: EnvironmentService, private keyConnectorService: KeyConnectorService, billingAccountProfileStateService: BillingAccountProfileStateService, + protected kdfConfigService: KdfConfigService, ) { super( accountService, @@ -72,6 +74,7 @@ export class UserApiLoginStrategy extends LoginStrategy { twoFactorService, userDecryptionOptionsService, billingAccountProfileStateService, + kdfConfigService, ); this.cache = new BehaviorSubject(data); } diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts index 1d96921286..d75e194980 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts @@ -1,6 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; @@ -42,6 +43,7 @@ describe("WebAuthnLoginStrategy", () => { let twoFactorService!: MockProxy; let userDecryptionOptionsService: MockProxy; let billingAccountProfileStateService: MockProxy; + let kdfConfigService: MockProxy; let webAuthnLoginStrategy!: WebAuthnLoginStrategy; @@ -81,6 +83,7 @@ describe("WebAuthnLoginStrategy", () => { twoFactorService = mock(); userDecryptionOptionsService = mock(); billingAccountProfileStateService = mock(); + kdfConfigService = mock(); tokenService.getTwoFactorToken.mockResolvedValue(null); appIdService.getAppId.mockResolvedValue(deviceId); @@ -101,6 +104,7 @@ describe("WebAuthnLoginStrategy", () => { twoFactorService, userDecryptionOptionsService, billingAccountProfileStateService, + kdfConfigService, ); // Create credentials diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts index 8a62a8fb3c..ac487b3a82 100644 --- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts +++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts @@ -3,6 +3,7 @@ import { Jsonify } from "type-fest"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; @@ -57,6 +58,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy { twoFactorService: TwoFactorService, userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, billingAccountProfileStateService: BillingAccountProfileStateService, + kdfConfigService: KdfConfigService, ) { super( accountService, @@ -72,6 +74,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy { twoFactorService, userDecryptionOptionsService, billingAccountProfileStateService, + kdfConfigService, ); this.cache = new BehaviorSubject(data); diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts index 33708885e2..f1b5590404 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.spec.ts @@ -3,6 +3,7 @@ import { MockProxy, mock } from "jest-mock-extended"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; @@ -66,6 +67,7 @@ describe("LoginStrategyService", () => { let authRequestService: MockProxy; let userDecryptionOptionsService: MockProxy; let billingAccountProfileStateService: MockProxy; + let kdfConfigService: MockProxy; let stateProvider: FakeGlobalStateProvider; let loginStrategyCacheExpirationState: FakeGlobalState; @@ -95,6 +97,7 @@ describe("LoginStrategyService", () => { userDecryptionOptionsService = mock(); billingAccountProfileStateService = mock(); stateProvider = new FakeGlobalStateProvider(); + kdfConfigService = mock(); sut = new LoginStrategyService( accountService, @@ -119,6 +122,7 @@ describe("LoginStrategyService", () => { userDecryptionOptionsService, stateProvider, billingAccountProfileStateService, + kdfConfigService, ); loginStrategyCacheExpirationState = stateProvider.getFake(CACHE_EXPIRATION_KEY); diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index aee74e6607..13cca69b3a 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -10,13 +10,18 @@ import { 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 { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; +import { + Argon2KdfConfig, + KdfConfig, + PBKDF2KdfConfig, +} from "@bitwarden/common/auth/models/domain/kdf-config"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request"; import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; @@ -32,7 +37,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { KdfType } from "@bitwarden/common/platform/enums"; +import { KdfType } from "@bitwarden/common/platform/enums/kdf-type.enum"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { GlobalState, GlobalStateProvider } from "@bitwarden/common/platform/state"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/device-trust.service.abstraction"; @@ -105,6 +110,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, protected stateProvider: GlobalStateProvider, protected billingAccountProfileStateService: BillingAccountProfileStateService, + protected kdfConfigService: KdfConfigService, ) { this.currentAuthnTypeState = this.stateProvider.get(CURRENT_LOGIN_STRATEGY_KEY); this.loginStrategyCacheState = this.stateProvider.get(CACHE_KEY); @@ -233,24 +239,25 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { async makePreloginKey(masterPassword: string, email: string): Promise { email = email.trim().toLowerCase(); - let kdf: KdfType = null; let kdfConfig: KdfConfig = null; try { const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email)); if (preloginResponse != null) { - kdf = preloginResponse.kdf; - kdfConfig = new KdfConfig( - preloginResponse.kdfIterations, - preloginResponse.kdfMemory, - preloginResponse.kdfParallelism, - ); + kdfConfig = + preloginResponse.kdf === KdfType.PBKDF2_SHA256 + ? new PBKDF2KdfConfig(preloginResponse.kdfIterations) + : new Argon2KdfConfig( + preloginResponse.kdfIterations, + preloginResponse.kdfMemory, + preloginResponse.kdfParallelism, + ); } } catch (e) { if (e == null || e.statusCode !== 404) { throw e; } } - return await this.cryptoService.makeMasterKey(masterPassword, email, kdf, kdfConfig); + return await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig); } // TODO: move to auth request service @@ -354,6 +361,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { this.policyService, this, this.billingAccountProfileStateService, + this.kdfConfigService, ); case AuthenticationType.Sso: return new SsoLoginStrategy( @@ -375,6 +383,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { this.authRequestService, this.i18nService, this.billingAccountProfileStateService, + this.kdfConfigService, ); case AuthenticationType.UserApiKey: return new UserApiLoginStrategy( @@ -394,6 +403,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { this.environmentService, this.keyConnectorService, this.billingAccountProfileStateService, + this.kdfConfigService, ); case AuthenticationType.AuthRequest: return new AuthRequestLoginStrategy( @@ -412,6 +422,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { this.userDecryptionOptionsService, this.deviceTrustService, this.billingAccountProfileStateService, + this.kdfConfigService, ); case AuthenticationType.WebAuthn: return new WebAuthnLoginStrategy( @@ -429,6 +440,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { this.twoFactorService, this.userDecryptionOptionsService, this.billingAccountProfileStateService, + this.kdfConfigService, ); } }), diff --git a/libs/auth/src/common/services/pin-crypto/pin-crypto.service.implementation.ts b/libs/auth/src/common/services/pin-crypto/pin-crypto.service.implementation.ts index 149d5d9a53..85d36b8d73 100644 --- a/libs/auth/src/common/services/pin-crypto/pin-crypto.service.implementation.ts +++ b/libs/auth/src/common/services/pin-crypto/pin-crypto.service.implementation.ts @@ -1,9 +1,9 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { KdfType } from "@bitwarden/common/platform/enums"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { PinLockType } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service"; import { UserKey } from "@bitwarden/common/types/key"; @@ -16,6 +16,7 @@ export class PinCryptoService implements PinCryptoServiceAbstraction { private cryptoService: CryptoService, private vaultTimeoutSettingsService: VaultTimeoutSettingsService, private logService: LogService, + private kdfConfigService: KdfConfigService, ) {} async decryptUserKeyWithPin(pin: string): Promise { try { @@ -24,8 +25,7 @@ export class PinCryptoService implements PinCryptoServiceAbstraction { const { pinKeyEncryptedUserKey, oldPinKeyEncryptedMasterKey } = await this.getPinKeyEncryptedKeys(pinLockType); - const kdf: KdfType = await this.stateService.getKdfType(); - const kdfConfig: KdfConfig = await this.stateService.getKdfConfig(); + const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig(); let userKey: UserKey; const email = await this.stateService.getEmail(); if (oldPinKeyEncryptedMasterKey) { @@ -33,7 +33,6 @@ export class PinCryptoService implements PinCryptoServiceAbstraction { pinLockType === "TRANSIENT", pin, email, - kdf, kdfConfig, oldPinKeyEncryptedMasterKey, ); @@ -41,7 +40,6 @@ export class PinCryptoService implements PinCryptoServiceAbstraction { userKey = await this.cryptoService.decryptUserKeyWithPin( pin, email, - kdf, kdfConfig, pinKeyEncryptedUserKey, ); diff --git a/libs/auth/src/common/services/pin-crypto/pin-crypto.service.spec.ts b/libs/auth/src/common/services/pin-crypto/pin-crypto.service.spec.ts index 17e0e14c51..c6fddf8efb 100644 --- a/libs/auth/src/common/services/pin-crypto/pin-crypto.service.spec.ts +++ b/libs/auth/src/common/services/pin-crypto/pin-crypto.service.spec.ts @@ -1,9 +1,10 @@ import { mock } from "jest-mock-extended"; -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { DEFAULT_KDF_CONFIG } from "@bitwarden/common/platform/enums"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { @@ -13,6 +14,7 @@ import { import { UserKey } from "@bitwarden/common/types/key"; import { PinCryptoService } from "./pin-crypto.service.implementation"; + describe("PinCryptoService", () => { let pinCryptoService: PinCryptoService; @@ -20,6 +22,7 @@ describe("PinCryptoService", () => { const cryptoService = mock(); const vaultTimeoutSettingsService = mock(); const logService = mock(); + const kdfConfigService = mock(); beforeEach(() => { jest.clearAllMocks(); @@ -29,6 +32,7 @@ describe("PinCryptoService", () => { cryptoService, vaultTimeoutSettingsService, logService, + kdfConfigService, ); }); @@ -39,7 +43,6 @@ describe("PinCryptoService", () => { describe("decryptUserKeyWithPin(...)", () => { const mockPin = "1234"; const mockProtectedPin = "protectedPin"; - const DEFAULT_PBKDF2_ITERATIONS = 600000; const mockUserEmail = "user@example.com"; const mockUserKey = new SymmetricCryptoKey(randomBytes(32)) as UserKey; @@ -49,7 +52,7 @@ describe("PinCryptoService", () => { ) { vaultTimeoutSettingsService.isPinLockSet.mockResolvedValue(pinLockType); - stateService.getKdfConfig.mockResolvedValue(new KdfConfig(DEFAULT_PBKDF2_ITERATIONS)); + kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); stateService.getEmail.mockResolvedValue(mockUserEmail); if (migrationStatus === "PRE") { diff --git a/libs/common/src/auth/abstractions/kdf-config.service.ts b/libs/common/src/auth/abstractions/kdf-config.service.ts new file mode 100644 index 0000000000..6b41979e1b --- /dev/null +++ b/libs/common/src/auth/abstractions/kdf-config.service.ts @@ -0,0 +1,7 @@ +import { UserId } from "../../types/guid"; +import { KdfConfig } from "../models/domain/kdf-config"; + +export abstract class KdfConfigService { + setKdfConfig: (userId: UserId, KdfConfig: KdfConfig) => Promise; + getKdfConfig: () => Promise; +} diff --git a/libs/common/src/auth/models/domain/kdf-config.ts b/libs/common/src/auth/models/domain/kdf-config.ts index a25ba586e9..ce01f09702 100644 --- a/libs/common/src/auth/models/domain/kdf-config.ts +++ b/libs/common/src/auth/models/domain/kdf-config.ts @@ -1,11 +1,86 @@ -export class KdfConfig { - iterations: number; - memory?: number; - parallelism?: number; +import { Jsonify } from "type-fest"; - constructor(iterations: number, memory?: number, parallelism?: number) { - this.iterations = iterations; - this.memory = memory; - this.parallelism = parallelism; +import { + ARGON2_ITERATIONS, + ARGON2_MEMORY, + ARGON2_PARALLELISM, + KdfType, + PBKDF2_ITERATIONS, +} from "../../../platform/enums/kdf-type.enum"; + +/** + * Represents a type safe KDF configuration. + */ +export type KdfConfig = PBKDF2KdfConfig | Argon2KdfConfig; + +/** + * Password-Based Key Derivation Function 2 (PBKDF2) KDF configuration. + */ +export class PBKDF2KdfConfig { + kdfType: KdfType.PBKDF2_SHA256 = KdfType.PBKDF2_SHA256; + iterations: number; + + constructor(iterations?: number) { + this.iterations = iterations ?? PBKDF2_ITERATIONS.defaultValue; + } + + /** + * Validates the PBKDF2 KDF configuration. + * A Valid PBKDF2 KDF configuration has KDF iterations between the 600_000 and 2_000_000. + */ + validateKdfConfig(): void { + if (!PBKDF2_ITERATIONS.inRange(this.iterations)) { + throw new Error( + `PBKDF2 iterations must be between ${PBKDF2_ITERATIONS.min} and ${PBKDF2_ITERATIONS.max}`, + ); + } + } + + static fromJSON(json: Jsonify): PBKDF2KdfConfig { + return new PBKDF2KdfConfig(json.iterations); + } +} + +/** + * Argon2 KDF configuration. + */ +export class Argon2KdfConfig { + kdfType: KdfType.Argon2id = KdfType.Argon2id; + iterations: number; + memory: number; + parallelism: number; + + constructor(iterations?: number, memory?: number, parallelism?: number) { + this.iterations = iterations ?? ARGON2_ITERATIONS.defaultValue; + this.memory = memory ?? ARGON2_MEMORY.defaultValue; + this.parallelism = parallelism ?? ARGON2_PARALLELISM.defaultValue; + } + + /** + * Validates the Argon2 KDF configuration. + * A Valid Argon2 KDF configuration has iterations between 2 and 10, memory between 16mb and 1024mb, and parallelism between 1 and 16. + */ + validateKdfConfig(): void { + if (!ARGON2_ITERATIONS.inRange(this.iterations)) { + throw new Error( + `Argon2 iterations must be between ${ARGON2_ITERATIONS.min} and ${ARGON2_ITERATIONS.max}`, + ); + } + + if (!ARGON2_MEMORY.inRange(this.memory)) { + throw new Error( + `Argon2 memory must be between ${ARGON2_MEMORY.min}mb and ${ARGON2_MEMORY.max}mb`, + ); + } + + if (!ARGON2_PARALLELISM.inRange(this.parallelism)) { + throw new Error( + `Argon2 parallelism must be between ${ARGON2_PARALLELISM.min} and ${ARGON2_PARALLELISM.max}.`, + ); + } + } + + static fromJSON(json: Jsonify): Argon2KdfConfig { + return new Argon2KdfConfig(json.iterations, json.memory, json.parallelism); } } diff --git a/libs/common/src/auth/models/request/set-key-connector-key.request.ts b/libs/common/src/auth/models/request/set-key-connector-key.request.ts index dfd32689d8..c8081bdec2 100644 --- a/libs/common/src/auth/models/request/set-key-connector-key.request.ts +++ b/libs/common/src/auth/models/request/set-key-connector-key.request.ts @@ -11,18 +11,14 @@ export class SetKeyConnectorKeyRequest { kdfParallelism?: number; orgIdentifier: string; - constructor( - key: string, - kdf: KdfType, - kdfConfig: KdfConfig, - orgIdentifier: string, - keys: KeysRequest, - ) { + constructor(key: string, kdfConfig: KdfConfig, orgIdentifier: string, keys: KeysRequest) { this.key = key; - this.kdf = kdf; + this.kdf = kdfConfig.kdfType; this.kdfIterations = kdfConfig.iterations; - this.kdfMemory = kdfConfig.memory; - this.kdfParallelism = kdfConfig.parallelism; + if (kdfConfig.kdfType === KdfType.Argon2id) { + this.kdfMemory = kdfConfig.memory; + this.kdfParallelism = kdfConfig.parallelism; + } this.orgIdentifier = orgIdentifier; this.keys = keys; } diff --git a/libs/common/src/auth/services/kdf-config.service.spec.ts b/libs/common/src/auth/services/kdf-config.service.spec.ts new file mode 100644 index 0000000000..67bcf721bc --- /dev/null +++ b/libs/common/src/auth/services/kdf-config.service.spec.ts @@ -0,0 +1,104 @@ +import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec"; +import { + ARGON2_ITERATIONS, + ARGON2_MEMORY, + ARGON2_PARALLELISM, + PBKDF2_ITERATIONS, +} from "../../platform/enums/kdf-type.enum"; +import { Utils } from "../../platform/misc/utils"; +import { UserId } from "../../types/guid"; +import { Argon2KdfConfig, PBKDF2KdfConfig } from "../models/domain/kdf-config"; + +import { KdfConfigService } from "./kdf-config.service"; + +describe("KdfConfigService", () => { + let sutKdfConfigService: KdfConfigService; + + let fakeStateProvider: FakeStateProvider; + let fakeAccountService: FakeAccountService; + const mockUserId = Utils.newGuid() as UserId; + + beforeEach(() => { + jest.clearAllMocks(); + + fakeAccountService = mockAccountServiceWith(mockUserId); + fakeStateProvider = new FakeStateProvider(fakeAccountService); + sutKdfConfigService = new KdfConfigService(fakeStateProvider); + }); + + it("setKdfConfig(): should set the KDF config", async () => { + const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(600_000); + await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig); + await expect(sutKdfConfigService.getKdfConfig()).resolves.toEqual(kdfConfig); + }); + + it("setKdfConfig(): should get the KDF config", async () => { + const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 4); + await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig); + await expect(sutKdfConfigService.getKdfConfig()).resolves.toEqual(kdfConfig); + }); + + it("setKdfConfig(): should throw error KDF cannot be null", async () => { + const kdfConfig: Argon2KdfConfig = null; + try { + await sutKdfConfigService.setKdfConfig(mockUserId, kdfConfig); + } catch (e) { + expect(e).toEqual(new Error("kdfConfig cannot be null")); + } + }); + + it("setKdfConfig(): should throw error userId cannot be null", async () => { + const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 4); + try { + await sutKdfConfigService.setKdfConfig(null, kdfConfig); + } catch (e) { + expect(e).toEqual(new Error("userId cannot be null")); + } + }); + + it("getKdfConfig(): should throw error KdfConfig for active user account state is null", async () => { + try { + await sutKdfConfigService.getKdfConfig(); + } catch (e) { + expect(e).toEqual(new Error("KdfConfig for active user account state is null")); + } + }); + + it("validateKdfConfig(): should validate the PBKDF2 KDF config", () => { + const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(600_000); + expect(() => kdfConfig.validateKdfConfig()).not.toThrow(); + }); + + it("validateKdfConfig(): should validate the Argon2id KDF config", () => { + const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 4); + expect(() => kdfConfig.validateKdfConfig()).not.toThrow(); + }); + + it("validateKdfConfig(): should throw an error for invalid PBKDF2 iterations", () => { + const kdfConfig: PBKDF2KdfConfig = new PBKDF2KdfConfig(100); + expect(() => kdfConfig.validateKdfConfig()).toThrow( + `PBKDF2 iterations must be between ${PBKDF2_ITERATIONS.min} and ${PBKDF2_ITERATIONS.max}`, + ); + }); + + it("validateKdfConfig(): should throw an error for invalid Argon2 iterations", () => { + const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(11, 64, 4); + expect(() => kdfConfig.validateKdfConfig()).toThrow( + `Argon2 iterations must be between ${ARGON2_ITERATIONS.min} and ${ARGON2_ITERATIONS.max}`, + ); + }); + + it("validateKdfConfig(): should throw an error for invalid Argon2 memory", () => { + const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 1025, 4); + expect(() => kdfConfig.validateKdfConfig()).toThrow( + `Argon2 memory must be between ${ARGON2_MEMORY.min}mb and ${ARGON2_MEMORY.max}mb`, + ); + }); + + it("validateKdfConfig(): should throw an error for invalid Argon2 parallelism", () => { + const kdfConfig: Argon2KdfConfig = new Argon2KdfConfig(3, 64, 17); + expect(() => kdfConfig.validateKdfConfig()).toThrow( + `Argon2 parallelism must be between ${ARGON2_PARALLELISM.min} and ${ARGON2_PARALLELISM.max}`, + ); + }); +}); diff --git a/libs/common/src/auth/services/kdf-config.service.ts b/libs/common/src/auth/services/kdf-config.service.ts new file mode 100644 index 0000000000..cfd2a3e1de --- /dev/null +++ b/libs/common/src/auth/services/kdf-config.service.ts @@ -0,0 +1,41 @@ +import { firstValueFrom } from "rxjs"; + +import { KdfType } from "../../platform/enums/kdf-type.enum"; +import { KDF_CONFIG_DISK, StateProvider, UserKeyDefinition } from "../../platform/state"; +import { UserId } from "../../types/guid"; +import { KdfConfigService as KdfConfigServiceAbstraction } from "../abstractions/kdf-config.service"; +import { Argon2KdfConfig, KdfConfig, PBKDF2KdfConfig } from "../models/domain/kdf-config"; + +export const KDF_CONFIG = new UserKeyDefinition(KDF_CONFIG_DISK, "kdfConfig", { + deserializer: (kdfConfig: KdfConfig) => { + if (kdfConfig == null) { + return null; + } + return kdfConfig.kdfType === KdfType.PBKDF2_SHA256 + ? PBKDF2KdfConfig.fromJSON(kdfConfig) + : Argon2KdfConfig.fromJSON(kdfConfig); + }, + clearOn: ["logout"], +}); + +export class KdfConfigService implements KdfConfigServiceAbstraction { + constructor(private stateProvider: StateProvider) {} + async setKdfConfig(userId: UserId, kdfConfig: KdfConfig) { + if (!userId) { + throw new Error("userId cannot be null"); + } + if (kdfConfig === null) { + throw new Error("kdfConfig cannot be null"); + } + await this.stateProvider.setUserState(KDF_CONFIG, kdfConfig, userId); + } + + async getKdfConfig(): Promise { + const userId = await firstValueFrom(this.stateProvider.activeUserId$); + const state = await firstValueFrom(this.stateProvider.getUser(userId, KDF_CONFIG).state$); + if (state === null) { + throw new Error("KdfConfig for active user account state is null"); + } + return state; + } +} diff --git a/libs/common/src/auth/services/key-connector.service.ts b/libs/common/src/auth/services/key-connector.service.ts index f8e523cce4..c19185ae91 100644 --- a/libs/common/src/auth/services/key-connector.service.ts +++ b/libs/common/src/auth/services/key-connector.service.ts @@ -7,6 +7,7 @@ import { KeysRequest } from "../../models/request/keys.request"; import { CryptoService } from "../../platform/abstractions/crypto.service"; import { KeyGenerationService } from "../../platform/abstractions/key-generation.service"; import { LogService } from "../../platform/abstractions/log.service"; +import { KdfType } from "../../platform/enums/kdf-type.enum"; import { Utils } from "../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key"; import { @@ -20,7 +21,7 @@ import { AccountService } from "../abstractions/account.service"; import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/key-connector.service"; import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction"; import { TokenService } from "../abstractions/token.service"; -import { KdfConfig } from "../models/domain/kdf-config"; +import { Argon2KdfConfig, KdfConfig, PBKDF2KdfConfig } from "../models/domain/kdf-config"; import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request"; import { SetKeyConnectorKeyRequest } from "../models/request/set-key-connector-key.request"; import { IdentityTokenResponse } from "../models/response/identity-token.response"; @@ -133,12 +134,14 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { userDecryptionOptions, } = tokenResponse; const password = await this.keyGenerationService.createKey(512); - const kdfConfig = new KdfConfig(kdfIterations, kdfMemory, kdfParallelism); + const kdfConfig: KdfConfig = + kdf === KdfType.PBKDF2_SHA256 + ? new PBKDF2KdfConfig(kdfIterations) + : new Argon2KdfConfig(kdfIterations, kdfMemory, kdfParallelism); const masterKey = await this.cryptoService.makeMasterKey( password.keyB64, await this.tokenService.getEmail(), - kdf, kdfConfig, ); const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64); @@ -162,7 +165,6 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { const keys = new KeysRequest(pubKey, privKey.encryptedString); const setPasswordRequest = new SetKeyConnectorKeyRequest( userKey[1].encryptedString, - kdf, kdfConfig, orgId, keys, diff --git a/libs/common/src/auth/services/user-verification/user-verification.service.ts b/libs/common/src/auth/services/user-verification/user-verification.service.ts index 5a443b784d..94adad8bc7 100644 --- a/libs/common/src/auth/services/user-verification/user-verification.service.ts +++ b/libs/common/src/auth/services/user-verification/user-verification.service.ts @@ -13,6 +13,7 @@ import { KeySuffixOptions } from "../../../platform/enums/key-suffix-options.enu import { UserId } from "../../../types/guid"; import { UserKey } from "../../../types/key"; import { AccountService } from "../../abstractions/account.service"; +import { KdfConfigService } from "../../abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "../../abstractions/master-password.service.abstraction"; import { UserVerificationApiServiceAbstraction } from "../../abstractions/user-verification/user-verification-api.service.abstraction"; import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/user-verification/user-verification.service.abstraction"; @@ -47,6 +48,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti private logService: LogService, private vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction, private platformUtilsService: PlatformUtilsService, + private kdfConfigService: KdfConfigService, ) {} async getAvailableVerificationOptions( @@ -118,8 +120,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti masterKey = await this.cryptoService.makeMasterKey( verification.secret, await this.stateService.getEmail(), - await this.stateService.getKdfType(), - await this.stateService.getKdfConfig(), + await this.kdfConfigService.getKdfConfig(), ); } request.masterPasswordHash = alreadyHashed @@ -176,8 +177,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti masterKey = await this.cryptoService.makeMasterKey( verification.secret, await this.stateService.getEmail(), - await this.stateService.getKdfType(), - await this.stateService.getKdfConfig(), + await this.kdfConfigService.getKdfConfig(), ); } const passwordValid = await this.cryptoService.compareAndUpdateKeyHash( diff --git a/libs/common/src/platform/abstractions/crypto.service.ts b/libs/common/src/platform/abstractions/crypto.service.ts index 6609a1014e..79a58f9d57 100644 --- a/libs/common/src/platform/abstractions/crypto.service.ts +++ b/libs/common/src/platform/abstractions/crypto.service.ts @@ -6,7 +6,7 @@ import { ProfileProviderResponse } from "../../admin-console/models/response/pro import { KdfConfig } from "../../auth/models/domain/kdf-config"; import { OrganizationId, ProviderId, UserId } from "../../types/guid"; import { UserKey, MasterKey, OrgKey, ProviderKey, PinKey, CipherKey } from "../../types/key"; -import { KeySuffixOptions, KdfType, HashPurpose } from "../enums"; +import { KeySuffixOptions, HashPurpose } from "../enums"; import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; import { EncString } from "../models/domain/enc-string"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; @@ -114,16 +114,10 @@ export abstract class CryptoService { * Generates a master key from the provided password * @param password The user's master password * @param email The user's email - * @param kdf The user's selected key derivation function to use * @param KdfConfig The user's key derivation function configuration * @returns A master key derived from the provided password */ - abstract makeMasterKey( - password: string, - email: string, - kdf: KdfType, - KdfConfig: KdfConfig, - ): Promise; + abstract makeMasterKey(password: string, email: string, KdfConfig: KdfConfig): Promise; /** * Encrypts the existing (or provided) user key with the * provided master key @@ -258,16 +252,10 @@ export abstract class CryptoService { /** * @param pin The user's pin * @param salt The user's salt - * @param kdf The user's kdf * @param kdfConfig The user's kdf config * @returns A key derived from the user's pin */ - abstract makePinKey( - pin: string, - salt: string, - kdf: KdfType, - kdfConfig: KdfConfig, - ): Promise; + abstract makePinKey(pin: string, salt: string, kdfConfig: KdfConfig): Promise; /** * Clears the user's pin keys from storage * Note: This will remove the stored pin and as a result, @@ -279,7 +267,6 @@ export abstract class CryptoService { * Decrypts the user key with their pin * @param pin The user's PIN * @param salt The user's salt - * @param kdf The user's KDF * @param kdfConfig The user's KDF config * @param pinProtectedUserKey The user's PIN protected symmetric key, if not provided * it will be retrieved from storage @@ -288,7 +275,6 @@ export abstract class CryptoService { abstract decryptUserKeyWithPin( pin: string, salt: string, - kdf: KdfType, kdfConfig: KdfConfig, protectedKeyCs?: EncString, ): Promise; @@ -298,7 +284,6 @@ export abstract class CryptoService { * @param masterPasswordOnRestart True if Master Password on Restart is enabled * @param pin User's PIN * @param email User's email - * @param kdf User's KdfType * @param kdfConfig User's KdfConfig * @param oldPinKey The old Pin key from state (retrieved from different * places depending on if Master Password on Restart was enabled) @@ -308,7 +293,6 @@ export abstract class CryptoService { masterPasswordOnRestart: boolean, pin: string, email: string, - kdf: KdfType, kdfConfig: KdfConfig, oldPinKey: EncString, ): Promise; @@ -358,21 +342,12 @@ export abstract class CryptoService { privateKey: EncString; }>; - /** - * Validate that the KDF config follows the requirements for the given KDF type. - * - * @remarks - * Should always be called before updating a users KDF config. - */ - abstract validateKdfConfig(kdf: KdfType, kdfConfig: KdfConfig): void; - /** * @deprecated Left for migration purposes. Use decryptUserKeyWithPin instead. */ abstract decryptMasterKeyWithPin( pin: string, salt: string, - kdf: KdfType, kdfConfig: KdfConfig, protectedKeyCs?: EncString, ): Promise; diff --git a/libs/common/src/platform/abstractions/key-generation.service.ts b/libs/common/src/platform/abstractions/key-generation.service.ts index 223eb75038..3a6971ba5d 100644 --- a/libs/common/src/platform/abstractions/key-generation.service.ts +++ b/libs/common/src/platform/abstractions/key-generation.service.ts @@ -1,6 +1,5 @@ import { KdfConfig } from "../../auth/models/domain/kdf-config"; import { CsprngArray } from "../../types/csprng"; -import { KdfType } from "../enums"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; export abstract class KeyGenerationService { @@ -46,14 +45,12 @@ export abstract class KeyGenerationService { * Derives a 32 byte key from a password using a key derivation function. * @param password Password to derive the key from. * @param salt Salt for the key derivation function. - * @param kdf Key derivation function to use. * @param kdfConfig Configuration for the key derivation function. * @returns 32 byte derived key. */ abstract deriveKeyFromPassword( password: string | Uint8Array, salt: string | Uint8Array, - kdf: KdfType, kdfConfig: KdfConfig, ): Promise; } diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 31bab36411..5ca604b526 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -1,12 +1,10 @@ import { Observable } from "rxjs"; -import { KdfConfig } from "../../auth/models/domain/kdf-config"; import { BiometricKey } from "../../auth/types/biometric-key"; import { GeneratorOptions } from "../../tools/generator/generator-options"; import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password"; import { UsernameGeneratorOptions } from "../../tools/generator/username"; import { UserId } from "../../types/guid"; -import { KdfType } from "../enums"; import { Account } from "../models/domain/account"; import { EncString } from "../models/domain/enc-string"; import { StorageOptions } from "../models/domain/storage-options"; @@ -146,10 +144,6 @@ export abstract class StateService { */ setEncryptedPinProtected: (value: string, options?: StorageOptions) => Promise; getIsAuthenticated: (options?: StorageOptions) => Promise; - getKdfConfig: (options?: StorageOptions) => Promise; - setKdfConfig: (kdfConfig: KdfConfig, options?: StorageOptions) => Promise; - getKdfType: (options?: StorageOptions) => Promise; - setKdfType: (value: KdfType, options?: StorageOptions) => Promise; getLastSync: (options?: StorageOptions) => Promise; setLastSync: (value: string, options?: StorageOptions) => Promise; getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise; diff --git a/libs/common/src/platform/enums/kdf-type.enum.ts b/libs/common/src/platform/enums/kdf-type.enum.ts index 97157910f5..fd29bf308c 100644 --- a/libs/common/src/platform/enums/kdf-type.enum.ts +++ b/libs/common/src/platform/enums/kdf-type.enum.ts @@ -1,4 +1,4 @@ -import { KdfConfig } from "../../auth/models/domain/kdf-config"; +import { PBKDF2KdfConfig } from "../../auth/models/domain/kdf-config"; import { RangeWithDefault } from "../misc/range-with-default"; export enum KdfType { @@ -12,4 +12,4 @@ export const ARGON2_ITERATIONS = new RangeWithDefault(2, 10, 3); export const DEFAULT_KDF_TYPE = KdfType.PBKDF2_SHA256; export const PBKDF2_ITERATIONS = new RangeWithDefault(600_000, 2_000_000, 600_000); -export const DEFAULT_KDF_CONFIG = new KdfConfig(PBKDF2_ITERATIONS.defaultValue); +export const DEFAULT_KDF_CONFIG = new PBKDF2KdfConfig(PBKDF2_ITERATIONS.defaultValue); diff --git a/libs/common/src/platform/services/crypto.service.spec.ts b/libs/common/src/platform/services/crypto.service.spec.ts index 2f68cf2ce7..d9992adb57 100644 --- a/libs/common/src/platform/services/crypto.service.spec.ts +++ b/libs/common/src/platform/services/crypto.service.spec.ts @@ -4,6 +4,7 @@ import { firstValueFrom, of, tap } from "rxjs"; import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service"; import { FakeActiveUserState, FakeSingleUserState } from "../../../spec/fake-state"; import { FakeStateProvider } from "../../../spec/fake-state-provider"; +import { KdfConfigService } from "../../auth/abstractions/kdf-config.service"; import { FakeMasterPasswordService } from "../../auth/services/master-password/fake-master-password.service"; import { CsprngArray } from "../../types/csprng"; import { UserId } from "../../types/guid"; @@ -37,6 +38,7 @@ describe("cryptoService", () => { const platformUtilService = mock(); const logService = mock(); const stateService = mock(); + const kdfConfigService = mock(); let stateProvider: FakeStateProvider; const mockUserId = Utils.newGuid() as UserId; @@ -58,6 +60,7 @@ describe("cryptoService", () => { stateService, accountService, stateProvider, + kdfConfigService, ); }); diff --git a/libs/common/src/platform/services/crypto.service.ts b/libs/common/src/platform/services/crypto.service.ts index 3cd443c073..798173f513 100644 --- a/libs/common/src/platform/services/crypto.service.ts +++ b/libs/common/src/platform/services/crypto.service.ts @@ -6,6 +6,7 @@ import { ProfileOrganizationResponse } from "../../admin-console/models/response import { ProfileProviderOrganizationResponse } from "../../admin-console/models/response/profile-provider-organization.response"; import { ProfileProviderResponse } from "../../admin-console/models/response/profile-provider.response"; import { AccountService } from "../../auth/abstractions/account.service"; +import { KdfConfigService } from "../../auth/abstractions/kdf-config.service"; import { InternalMasterPasswordServiceAbstraction } from "../../auth/abstractions/master-password.service.abstraction"; import { KdfConfig } from "../../auth/models/domain/kdf-config"; import { Utils } from "../../platform/misc/utils"; @@ -28,16 +29,7 @@ import { KeyGenerationService } from "../abstractions/key-generation.service"; import { LogService } from "../abstractions/log.service"; import { PlatformUtilsService } from "../abstractions/platform-utils.service"; import { StateService } from "../abstractions/state.service"; -import { - KeySuffixOptions, - HashPurpose, - KdfType, - ARGON2_ITERATIONS, - ARGON2_MEMORY, - ARGON2_PARALLELISM, - EncryptionType, - PBKDF2_ITERATIONS, -} from "../enums"; +import { KeySuffixOptions, HashPurpose, EncryptionType } from "../enums"; import { sequentialize } from "../misc/sequentialize"; import { EFFLongWordList } from "../misc/wordlist"; import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; @@ -91,6 +83,7 @@ export class CryptoService implements CryptoServiceAbstraction { protected stateService: StateService, protected accountService: AccountService, protected stateProvider: StateProvider, + protected kdfConfigService: KdfConfigService, ) { // User Key this.activeUserKeyState = stateProvider.getActive(USER_KEY); @@ -283,8 +276,7 @@ export class CryptoService implements CryptoServiceAbstraction { return (masterKey ||= await this.makeMasterKey( password, await this.stateService.getEmail({ userId: userId }), - await this.stateService.getKdfType({ userId: userId }), - await this.stateService.getKdfConfig({ userId: userId }), + await this.kdfConfigService.getKdfConfig(), )); } @@ -295,16 +287,10 @@ export class CryptoService implements CryptoServiceAbstraction { * Does not validate the kdf config to ensure it satisfies the minimum requirements for the given kdf type. * TODO: Move to MasterPasswordService */ - async makeMasterKey( - password: string, - email: string, - kdf: KdfType, - KdfConfig: KdfConfig, - ): Promise { + async makeMasterKey(password: string, email: string, KdfConfig: KdfConfig): Promise { return (await this.keyGenerationService.deriveKeyFromPassword( password, email, - kdf, KdfConfig, )) as MasterKey; } @@ -560,8 +546,8 @@ export class CryptoService implements CryptoServiceAbstraction { await this.stateProvider.setUserState(USER_ENCRYPTED_PRIVATE_KEY, null, userId); } - async makePinKey(pin: string, salt: string, kdf: KdfType, kdfConfig: KdfConfig): Promise { - const pinKey = await this.keyGenerationService.deriveKeyFromPassword(pin, salt, kdf, kdfConfig); + async makePinKey(pin: string, salt: string, kdfConfig: KdfConfig): Promise { + const pinKey = await this.keyGenerationService.deriveKeyFromPassword(pin, salt, kdfConfig); return (await this.stretchKey(pinKey)) as PinKey; } @@ -575,7 +561,6 @@ export class CryptoService implements CryptoServiceAbstraction { async decryptUserKeyWithPin( pin: string, salt: string, - kdf: KdfType, kdfConfig: KdfConfig, pinProtectedUserKey?: EncString, ): Promise { @@ -584,7 +569,7 @@ export class CryptoService implements CryptoServiceAbstraction { if (!pinProtectedUserKey) { throw new Error("No PIN protected key found."); } - const pinKey = await this.makePinKey(pin, salt, kdf, kdfConfig); + const pinKey = await this.makePinKey(pin, salt, kdfConfig); const userKey = await this.encryptService.decryptToBytes(pinProtectedUserKey, pinKey); return new SymmetricCryptoKey(userKey) as UserKey; } @@ -593,7 +578,6 @@ export class CryptoService implements CryptoServiceAbstraction { async decryptMasterKeyWithPin( pin: string, salt: string, - kdf: KdfType, kdfConfig: KdfConfig, pinProtectedMasterKey?: EncString, ): Promise { @@ -604,7 +588,7 @@ export class CryptoService implements CryptoServiceAbstraction { } pinProtectedMasterKey = new EncString(pinProtectedMasterKeyString); } - const pinKey = await this.makePinKey(pin, salt, kdf, kdfConfig); + const pinKey = await this.makePinKey(pin, salt, kdfConfig); const masterKey = await this.encryptService.decryptToBytes(pinProtectedMasterKey, pinKey); return new SymmetricCryptoKey(masterKey) as MasterKey; } @@ -831,8 +815,7 @@ export class CryptoService implements CryptoServiceAbstraction { const pinKey = await this.makePinKey( pin, await this.stateService.getEmail({ userId: userId }), - await this.stateService.getKdfType({ userId: userId }), - await this.stateService.getKdfConfig({ userId: userId }), + await this.kdfConfigService.getKdfConfig(), ); const encPin = await this.encryptService.encrypt(key.key, pinKey); @@ -873,43 +856,6 @@ export class CryptoService implements CryptoServiceAbstraction { return null; } - /** - * Validate that the KDF config follows the requirements for the given KDF type. - * - * @remarks - * Should always be called before updating a users KDF config. - */ - validateKdfConfig(kdf: KdfType, kdfConfig: KdfConfig): void { - switch (kdf) { - case KdfType.PBKDF2_SHA256: - if (!PBKDF2_ITERATIONS.inRange(kdfConfig.iterations)) { - throw new Error( - `PBKDF2 iterations must be between ${PBKDF2_ITERATIONS.min} and ${PBKDF2_ITERATIONS.max}`, - ); - } - break; - case KdfType.Argon2id: - if (!ARGON2_ITERATIONS.inRange(kdfConfig.iterations)) { - throw new Error( - `Argon2 iterations must be between ${ARGON2_ITERATIONS.min} and ${ARGON2_ITERATIONS.max}`, - ); - } - - if (!ARGON2_MEMORY.inRange(kdfConfig.memory)) { - throw new Error( - `Argon2 memory must be between ${ARGON2_MEMORY.min}mb and ${ARGON2_MEMORY.max}mb`, - ); - } - - if (!ARGON2_PARALLELISM.inRange(kdfConfig.parallelism)) { - throw new Error( - `Argon2 parallelism must be between ${ARGON2_PARALLELISM.min} and ${ARGON2_PARALLELISM.max}.`, - ); - } - break; - } - } - protected async clearAllStoredUserKeys(userId?: UserId): Promise { await this.stateService.setUserKeyAutoUnlock(null, { userId: userId }); await this.stateService.setPinKeyEncryptedUserKeyEphemeral(null, { userId: userId }); @@ -1007,16 +953,15 @@ export class CryptoService implements CryptoServiceAbstraction { masterPasswordOnRestart: boolean, pin: string, email: string, - kdf: KdfType, kdfConfig: KdfConfig, oldPinKey: EncString, ): Promise { // Decrypt - const masterKey = await this.decryptMasterKeyWithPin(pin, email, kdf, kdfConfig, oldPinKey); + const masterKey = await this.decryptMasterKeyWithPin(pin, email, kdfConfig, oldPinKey); const encUserKey = await this.stateService.getEncryptedCryptoSymmetricKey(); const userKey = await this.decryptUserKeyWithMasterKey(masterKey, new EncString(encUserKey)); // Migrate - const pinKey = await this.makePinKey(pin, email, kdf, kdfConfig); + const pinKey = await this.makePinKey(pin, email, kdfConfig); const pinProtectedKey = await this.encryptService.encrypt(userKey.key, pinKey); if (masterPasswordOnRestart) { await this.stateService.setDecryptedPinProtected(null); diff --git a/libs/common/src/platform/services/key-generation.service.spec.ts b/libs/common/src/platform/services/key-generation.service.spec.ts index b3e0aa6d4e..4f04eebd04 100644 --- a/libs/common/src/platform/services/key-generation.service.spec.ts +++ b/libs/common/src/platform/services/key-generation.service.spec.ts @@ -1,9 +1,8 @@ import { mock } from "jest-mock-extended"; -import { KdfConfig } from "../../auth/models/domain/kdf-config"; +import { Argon2KdfConfig, PBKDF2KdfConfig } from "../../auth/models/domain/kdf-config"; import { CsprngArray } from "../../types/csprng"; import { CryptoFunctionService } from "../abstractions/crypto-function.service"; -import { KdfType } from "../enums"; import { KeyGenerationService } from "./key-generation.service"; @@ -75,12 +74,11 @@ describe("KeyGenerationService", () => { it("should derive a 32 byte key from a password using pbkdf2", async () => { const password = "password"; const salt = "salt"; - const kdf = KdfType.PBKDF2_SHA256; - const kdfConfig = new KdfConfig(600_000); + const kdfConfig = new PBKDF2KdfConfig(600_000); cryptoFunctionService.pbkdf2.mockResolvedValue(new Uint8Array(32)); - const key = await sut.deriveKeyFromPassword(password, salt, kdf, kdfConfig); + const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig); expect(key.key.length).toEqual(32); }); @@ -88,13 +86,12 @@ describe("KeyGenerationService", () => { it("should derive a 32 byte key from a password using argon2id", async () => { const password = "password"; const salt = "salt"; - const kdf = KdfType.Argon2id; - const kdfConfig = new KdfConfig(600_000, 15); + const kdfConfig = new Argon2KdfConfig(3, 16, 4); cryptoFunctionService.hash.mockResolvedValue(new Uint8Array(32)); cryptoFunctionService.argon2.mockResolvedValue(new Uint8Array(32)); - const key = await sut.deriveKeyFromPassword(password, salt, kdf, kdfConfig); + const key = await sut.deriveKeyFromPassword(password, salt, kdfConfig); expect(key.key.length).toEqual(32); }); diff --git a/libs/common/src/platform/services/key-generation.service.ts b/libs/common/src/platform/services/key-generation.service.ts index c592f35e5f..9202b37100 100644 --- a/libs/common/src/platform/services/key-generation.service.ts +++ b/libs/common/src/platform/services/key-generation.service.ts @@ -46,17 +46,16 @@ export class KeyGenerationService implements KeyGenerationServiceAbstraction { async deriveKeyFromPassword( password: string | Uint8Array, salt: string | Uint8Array, - kdf: KdfType, kdfConfig: KdfConfig, ): Promise { let key: Uint8Array = null; - if (kdf == null || kdf === KdfType.PBKDF2_SHA256) { + if (kdfConfig.kdfType == null || kdfConfig.kdfType === KdfType.PBKDF2_SHA256) { if (kdfConfig.iterations == null) { kdfConfig.iterations = PBKDF2_ITERATIONS.defaultValue; } key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfConfig.iterations); - } else if (kdf == KdfType.Argon2id) { + } else if (kdfConfig.kdfType == KdfType.Argon2id) { if (kdfConfig.iterations == null) { kdfConfig.iterations = ARGON2_ITERATIONS.defaultValue; } diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index f61b9017f7..9479d64710 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -3,7 +3,6 @@ import { Jsonify, JsonValue } from "type-fest"; import { AccountService } from "../../auth/abstractions/account.service"; import { TokenService } from "../../auth/abstractions/token.service"; -import { KdfConfig } from "../../auth/models/domain/kdf-config"; import { BiometricKey } from "../../auth/types/biometric-key"; import { GeneratorOptions } from "../../tools/generator/generator-options"; import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password"; @@ -19,7 +18,7 @@ import { AbstractMemoryStorageService, AbstractStorageService, } from "../abstractions/storage.service"; -import { HtmlStorageLocation, KdfType, StorageLocation } from "../enums"; +import { HtmlStorageLocation, StorageLocation } from "../enums"; import { StateFactory } from "../factories/state-factory"; import { Utils } from "../misc/utils"; import { Account, AccountData, AccountSettings } from "../models/domain/account"; @@ -563,49 +562,6 @@ export class StateService< ); } - async getKdfConfig(options?: StorageOptions): Promise { - const iterations = ( - await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) - )?.profile?.kdfIterations; - const memory = ( - await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) - )?.profile?.kdfMemory; - const parallelism = ( - await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) - )?.profile?.kdfParallelism; - return new KdfConfig(iterations, memory, parallelism); - } - - async setKdfConfig(config: KdfConfig, options?: StorageOptions): Promise { - const account = await this.getAccount( - this.reconcileOptions(options, await this.defaultOnDiskOptions()), - ); - account.profile.kdfIterations = config.iterations; - account.profile.kdfMemory = config.memory; - account.profile.kdfParallelism = config.parallelism; - await this.saveAccount( - account, - this.reconcileOptions(options, await this.defaultOnDiskOptions()), - ); - } - - async getKdfType(options?: StorageOptions): Promise { - return ( - await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())) - )?.profile?.kdfType; - } - - async setKdfType(value: KdfType, options?: StorageOptions): Promise { - const account = await this.getAccount( - this.reconcileOptions(options, await this.defaultOnDiskOptions()), - ); - account.profile.kdfType = value; - await this.saveAccount( - account, - this.reconcileOptions(options, await this.defaultOnDiskOptions()), - ); - } - async getLastSync(options?: StorageOptions): Promise { return ( await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions())) diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 8b12b4f657..bf68dd0a3a 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -35,6 +35,7 @@ export const BILLING_DISK = new StateDefinition("billing", "disk"); // Auth +export const KDF_CONFIG_DISK = new StateDefinition("kdfConfig", "disk"); export const KEY_CONNECTOR_DISK = new StateDefinition("keyConnector", "disk"); export const ACCOUNT_MEMORY = new StateDefinition("account", "memory"); export const ACCOUNT_DISK = new StateDefinition("account", "disk"); diff --git a/libs/common/src/state-migrations/migrate.ts b/libs/common/src/state-migrations/migrate.ts index a647710d84..a44639d81b 100644 --- a/libs/common/src/state-migrations/migrate.ts +++ b/libs/common/src/state-migrations/migrate.ts @@ -55,15 +55,16 @@ import { MoveMasterKeyStateToProviderMigrator } from "./migrations/55-move-maste import { AuthRequestMigrator } from "./migrations/56-move-auth-requests"; import { CipherServiceMigrator } from "./migrations/57-move-cipher-service-to-state-provider"; import { RemoveRefreshTokenMigratedFlagMigrator } from "./migrations/58-remove-refresh-token-migrated-state-provider-flag"; -import { KnownAccountsMigrator } from "./migrations/59-known-accounts"; +import { KdfConfigMigrator } from "./migrations/59-move-kdf-config-to-state-provider"; import { RemoveLegacyEtmKeyMigrator } from "./migrations/6-remove-legacy-etm-key"; +import { KnownAccountsMigrator } from "./migrations/60-known-accounts"; import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account"; 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 = 58; +export const CURRENT_VERSION = 60; export type MinVersion = typeof MIN_VERSION; export function createMigrationBuilder() { @@ -124,7 +125,8 @@ export function createMigrationBuilder() { .with(AuthRequestMigrator, 55, 56) .with(CipherServiceMigrator, 56, 57) .with(RemoveRefreshTokenMigratedFlagMigrator, 57, 58) - .with(KnownAccountsMigrator, 58, 59); + .with(KdfConfigMigrator, 58, 59) + .with(KnownAccountsMigrator, 59, 60); } export async function currentVersion( diff --git a/libs/common/src/state-migrations/migration-helper.ts b/libs/common/src/state-migrations/migration-helper.ts index cc147b2b83..42505d56f2 100644 --- a/libs/common/src/state-migrations/migration-helper.ts +++ b/libs/common/src/state-migrations/migration-helper.ts @@ -172,8 +172,8 @@ export class MigrationHelper { * Helper method to read known users ids. */ async getKnownUserIds(): Promise { - if (this.currentVersion < 60) { - return knownAccountUserIdsBuilderPre60(this.storageService); + if (this.currentVersion < 61) { + return knownAccountUserIdsBuilderPre61(this.storageService); } else { return knownAccountUserIdsBuilder(this.storageService); } @@ -242,7 +242,7 @@ function globalKeyBuilderPre9(): string { throw Error("No key builder should be used for versions prior to 9."); } -async function knownAccountUserIdsBuilderPre60( +async function knownAccountUserIdsBuilderPre61( storageService: AbstractStorageService, ): Promise { return (await storageService.get("authenticatedAccounts")) ?? []; diff --git a/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.spec.ts b/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.spec.ts new file mode 100644 index 0000000000..dbce750a7e --- /dev/null +++ b/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.spec.ts @@ -0,0 +1,153 @@ +import { MockProxy } from "jest-mock-extended"; + +import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; +import { mockMigrationHelper } from "../migration-helper.spec"; + +import { KdfConfigMigrator } from "./59-move-kdf-config-to-state-provider"; + +function exampleJSON() { + return { + global: { + otherStuff: "otherStuff1", + }, + authenticatedAccounts: ["FirstAccount", "SecondAccount"], + FirstAccount: { + profile: { + kdfIterations: 3, + kdfMemory: 64, + kdfParallelism: 5, + kdfType: 1, + otherStuff: "otherStuff1", + }, + otherStuff: "otherStuff2", + }, + SecondAccount: { + profile: { + kdfIterations: 600_001, + kdfMemory: null as number, + kdfParallelism: null as number, + kdfType: 0, + otherStuff: "otherStuff3", + }, + otherStuff: "otherStuff4", + }, + }; +} + +function rollbackJSON() { + return { + user_FirstAccount_kdfConfig_kdfConfig: { + iterations: 3, + memory: 64, + parallelism: 5, + kdfType: 1, + }, + user_SecondAccount_kdfConfig_kdfConfig: { + iterations: 600_001, + memory: null as number, + parallelism: null as number, + kdfType: 0, + }, + global: { + otherStuff: "otherStuff1", + }, + authenticatedAccounts: ["FirstAccount", "SecondAccount"], + FirstAccount: { + profile: { + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }, + SecondAccount: { + profile: { + otherStuff: "otherStuff4", + }, + otherStuff: "otherStuff5", + }, + }; +} + +const kdfConfigKeyDefinition: KeyDefinitionLike = { + key: "kdfConfig", + stateDefinition: { + name: "kdfConfig", + }, +}; + +describe("KdfConfigMigrator", () => { + let helper: MockProxy; + let sut: KdfConfigMigrator; + + describe("migrate", () => { + beforeEach(() => { + helper = mockMigrationHelper(exampleJSON(), 59); + sut = new KdfConfigMigrator(58, 59); + }); + + it("should remove kdfType and kdfConfig from Account.Profile", async () => { + await sut.migrate(helper); + + expect(helper.set).toHaveBeenCalledTimes(2); + expect(helper.set).toHaveBeenCalledWith("FirstAccount", { + profile: { + otherStuff: "otherStuff1", + }, + otherStuff: "otherStuff2", + }); + expect(helper.set).toHaveBeenCalledWith("SecondAccount", { + profile: { + otherStuff: "otherStuff3", + }, + otherStuff: "otherStuff4", + }); + expect(helper.setToUser).toHaveBeenCalledWith("FirstAccount", kdfConfigKeyDefinition, { + iterations: 3, + memory: 64, + parallelism: 5, + kdfType: 1, + }); + expect(helper.setToUser).toHaveBeenCalledWith("SecondAccount", kdfConfigKeyDefinition, { + iterations: 600_001, + memory: null as number, + parallelism: null as number, + kdfType: 0, + }); + }); + }); + + describe("rollback", () => { + beforeEach(() => { + helper = mockMigrationHelper(rollbackJSON(), 59); + sut = new KdfConfigMigrator(58, 59); + }); + + it("should null out new KdfConfig account value and set account.profile", async () => { + await sut.rollback(helper); + + expect(helper.setToUser).toHaveBeenCalledTimes(2); + expect(helper.setToUser).toHaveBeenCalledWith("FirstAccount", kdfConfigKeyDefinition, null); + expect(helper.setToUser).toHaveBeenCalledWith("SecondAccount", kdfConfigKeyDefinition, null); + expect(helper.set).toHaveBeenCalledTimes(2); + expect(helper.set).toHaveBeenCalledWith("FirstAccount", { + profile: { + kdfIterations: 3, + kdfMemory: 64, + kdfParallelism: 5, + kdfType: 1, + otherStuff: "otherStuff2", + }, + otherStuff: "otherStuff3", + }); + expect(helper.set).toHaveBeenCalledWith("SecondAccount", { + profile: { + kdfIterations: 600_001, + kdfMemory: null as number, + kdfParallelism: null as number, + kdfType: 0, + otherStuff: "otherStuff4", + }, + otherStuff: "otherStuff5", + }); + }); + }); +}); diff --git a/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.ts b/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.ts new file mode 100644 index 0000000000..332306c6d4 --- /dev/null +++ b/libs/common/src/state-migrations/migrations/59-move-kdf-config-to-state-provider.ts @@ -0,0 +1,78 @@ +import { KeyDefinitionLike, MigrationHelper } from "../migration-helper"; +import { Migrator } from "../migrator"; + +enum KdfType { + PBKDF2_SHA256 = 0, + Argon2id = 1, +} + +class KdfConfig { + iterations: number; + kdfType: KdfType; + memory?: number; + parallelism?: number; +} + +type ExpectedAccountType = { + profile?: { + kdfIterations: number; + kdfType: KdfType; + kdfMemory?: number; + kdfParallelism?: number; + }; +}; + +const kdfConfigKeyDefinition: KeyDefinitionLike = { + key: "kdfConfig", + stateDefinition: { + name: "kdfConfig", + }, +}; + +export class KdfConfigMigrator extends Migrator<58, 59> { + async migrate(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + async function migrateAccount(userId: string, account: ExpectedAccountType): Promise { + const iterations = account?.profile?.kdfIterations; + const kdfType = account?.profile?.kdfType; + const memory = account?.profile?.kdfMemory; + const parallelism = account?.profile?.kdfParallelism; + + const kdfConfig: KdfConfig = { + iterations: iterations, + kdfType: kdfType, + memory: memory, + parallelism: parallelism, + }; + + if (kdfConfig != null) { + await helper.setToUser(userId, kdfConfigKeyDefinition, kdfConfig); + delete account?.profile?.kdfIterations; + delete account?.profile?.kdfType; + delete account?.profile?.kdfMemory; + delete account?.profile?.kdfParallelism; + } + + await helper.set(userId, account); + } + await Promise.all([...accounts.map(({ userId, account }) => migrateAccount(userId, account))]); + } + + async rollback(helper: MigrationHelper): Promise { + const accounts = await helper.getAccounts(); + async function rollbackAccount(userId: string, account: ExpectedAccountType): Promise { + const kdfConfig: KdfConfig = await helper.getFromUser(userId, kdfConfigKeyDefinition); + + if (kdfConfig != null) { + account.profile.kdfIterations = kdfConfig.iterations; + account.profile.kdfType = kdfConfig.kdfType; + account.profile.kdfMemory = kdfConfig.memory; + account.profile.kdfParallelism = kdfConfig.parallelism; + await helper.setToUser(userId, kdfConfigKeyDefinition, null); + } + await helper.set(userId, account); + } + + await Promise.all([...accounts.map(({ userId, account }) => rollbackAccount(userId, account))]); + } +} diff --git a/libs/common/src/state-migrations/migrations/59-known-accounts.spec.ts b/libs/common/src/state-migrations/migrations/60-known-accounts.spec.ts similarity index 93% rename from libs/common/src/state-migrations/migrations/59-known-accounts.spec.ts rename to libs/common/src/state-migrations/migrations/60-known-accounts.spec.ts index 0abe45c2c3..28dedb3c39 100644 --- a/libs/common/src/state-migrations/migrations/59-known-accounts.spec.ts +++ b/libs/common/src/state-migrations/migrations/60-known-accounts.spec.ts @@ -8,7 +8,7 @@ import { ACCOUNT_ACTIVE_ACCOUNT_ID, ACCOUNT_ACTIVITY, KnownAccountsMigrator, -} from "./59-known-accounts"; +} from "./60-known-accounts"; const migrateJson = () => { return { @@ -78,8 +78,8 @@ describe("ReplicateKnownAccounts", () => { describe("migrate", () => { beforeEach(() => { - helper = mockMigrationHelper(migrateJson(), 58); - sut = new KnownAccountsMigrator(58, 59); + helper = mockMigrationHelper(migrateJson(), 59); + sut = new KnownAccountsMigrator(59, 60); }); it("migrates accounts", async () => { @@ -117,8 +117,8 @@ describe("ReplicateKnownAccounts", () => { describe("rollback", () => { beforeEach(() => { - helper = mockMigrationHelper(rollbackJson(), 59); - sut = new KnownAccountsMigrator(58, 59); + helper = mockMigrationHelper(rollbackJson(), 60); + sut = new KnownAccountsMigrator(59, 60); }); it("rolls back authenticated accounts", async () => { diff --git a/libs/common/src/state-migrations/migrations/59-known-accounts.ts b/libs/common/src/state-migrations/migrations/60-known-accounts.ts similarity index 98% rename from libs/common/src/state-migrations/migrations/59-known-accounts.ts rename to libs/common/src/state-migrations/migrations/60-known-accounts.ts index 7c0b6143d9..75117da5b4 100644 --- a/libs/common/src/state-migrations/migrations/59-known-accounts.ts +++ b/libs/common/src/state-migrations/migrations/60-known-accounts.ts @@ -30,7 +30,7 @@ type ExpectedAccountType = { }; }; -export class KnownAccountsMigrator extends Migrator<58, 59> { +export class KnownAccountsMigrator extends Migrator<59, 60> { async migrate(helper: MigrationHelper): Promise { await this.migrateAuthenticatedAccounts(helper); await this.migrateActiveAccountId(helper); diff --git a/libs/common/src/tools/send/services/send.service.ts b/libs/common/src/tools/send/services/send.service.ts index 33b1f28be0..fb67de5501 100644 --- a/libs/common/src/tools/send/services/send.service.ts +++ b/libs/common/src/tools/send/services/send.service.ts @@ -1,10 +1,10 @@ import { Observable, concatMap, distinctUntilChanged, firstValueFrom, map } from "rxjs"; +import { PBKDF2KdfConfig } from "../../../auth/models/domain/kdf-config"; import { CryptoService } from "../../../platform/abstractions/crypto.service"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service"; -import { KdfType } from "../../../platform/enums"; import { Utils } from "../../../platform/misc/utils"; import { EncArrayBuffer } from "../../../platform/models/domain/enc-array-buffer"; import { EncString } from "../../../platform/models/domain/enc-string"; @@ -69,8 +69,7 @@ export class SendService implements InternalSendServiceAbstraction { const passwordKey = await this.keyGenerationService.deriveKeyFromPassword( password, model.key, - KdfType.PBKDF2_SHA256, - { iterations: SEND_KDF_ITERATIONS }, + new PBKDF2KdfConfig(SEND_KDF_ITERATIONS), ); send.password = passwordKey.keyB64; } 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 9e047b063c..8f9e1abaf1 100644 --- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts +++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts @@ -1,4 +1,8 @@ -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; +import { + Argon2KdfConfig, + KdfConfig, + PBKDF2KdfConfig, +} from "@bitwarden/common/auth/models/domain/kdf-config"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { KdfType } from "@bitwarden/common/platform/enums"; @@ -69,12 +73,12 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im return false; } - this.key = await this.cryptoService.makePinKey( - password, - jdoc.salt, - jdoc.kdfType, - new KdfConfig(jdoc.kdfIterations, jdoc.kdfMemory, jdoc.kdfParallelism), - ); + const kdfConfig: KdfConfig = + jdoc.kdfType === KdfType.PBKDF2_SHA256 + ? new PBKDF2KdfConfig(jdoc.kdfIterations) + : new Argon2KdfConfig(jdoc.kdfIterations, jdoc.kdfMemory, jdoc.kdfParallelism); + + this.key = await this.cryptoService.makePinKey(password, jdoc.salt, kdfConfig); const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT); 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 1865c94c7d..dd5a210bf8 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,7 +1,7 @@ +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { KdfType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -12,15 +12,14 @@ export class BaseVaultExportService { constructor( protected cryptoService: CryptoService, private cryptoFunctionService: CryptoFunctionService, - private stateService: StateService, + private kdfConfigService: KdfConfigService, ) {} protected async buildPasswordExport(clearText: string, password: string): Promise { - const kdfType: KdfType = await this.stateService.getKdfType(); - const kdfConfig: KdfConfig = await this.stateService.getKdfConfig(); + const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig(); const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16)); - const key = await this.cryptoService.makePinKey(password, salt, kdfType, kdfConfig); + const key = await this.cryptoService.makePinKey(password, salt, kdfConfig); const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), key); const encText = await this.cryptoService.encrypt(clearText, key); @@ -29,14 +28,17 @@ export class BaseVaultExportService { encrypted: true, passwordProtected: true, salt: salt, - kdfType: kdfType, + kdfType: kdfConfig.kdfType, kdfIterations: kdfConfig.iterations, - kdfMemory: kdfConfig.memory, - kdfParallelism: kdfConfig.parallelism, encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, data: encText.encryptedString, }; + if (kdfConfig.kdfType === KdfType.Argon2id) { + jsonDoc.kdfMemory = kdfConfig.memory; + jsonDoc.kdfParallelism = kdfConfig.parallelism; + } + return JSON.stringify(jsonDoc, null, " "); } 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 fc8faa4b5b..b30384f9f4 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 @@ -1,13 +1,12 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; -import { KdfType, PBKDF2_ITERATIONS } from "@bitwarden/common/platform/enums"; +import { DEFAULT_KDF_CONFIG, KdfType, PBKDF2_ITERATIONS } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { StateService } from "@bitwarden/common/platform/services/state.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -110,10 +109,10 @@ function expectEqualCiphers(ciphers: CipherView[] | Cipher[], jsonResult: string expect(actual).toEqual(JSON.stringify(items)); } -function expectEqualFolderViews(folderviews: FolderView[] | Folder[], jsonResult: string) { +function expectEqualFolderViews(folderViews: FolderView[] | Folder[], jsonResult: string) { const actual = JSON.stringify(JSON.parse(jsonResult).folders); const folders: FolderResponse[] = []; - folderviews.forEach((c) => { + folderViews.forEach((c) => { const folder = new FolderResponse(); folder.id = c.id; folder.name = c.name.toString(); @@ -144,19 +143,18 @@ describe("VaultExportService", () => { let cipherService: MockProxy; let folderService: MockProxy; let cryptoService: MockProxy; - let stateService: MockProxy; + let kdfConfigService: MockProxy; beforeEach(() => { cryptoFunctionService = mock(); cipherService = mock(); folderService = mock(); cryptoService = mock(); - stateService = mock(); + kdfConfigService = mock(); folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews); folderService.getAllFromState.mockResolvedValue(UserFolders); - stateService.getKdfType.mockResolvedValue(KdfType.PBKDF2_SHA256); - stateService.getKdfConfig.mockResolvedValue(new KdfConfig(PBKDF2_ITERATIONS.defaultValue)); + kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); cryptoService.encrypt.mockResolvedValue(new EncString("encrypted")); exportService = new IndividualVaultExportService( @@ -164,7 +162,7 @@ describe("VaultExportService", () => { cipherService, cryptoService, cryptoFunctionService, - stateService, + kdfConfigService, ); }); 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 5f3bd9de52..ee178767f4 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 @@ -1,9 +1,9 @@ import * as papa from "papaparse"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -32,9 +32,9 @@ export class IndividualVaultExportService private cipherService: CipherService, cryptoService: CryptoService, cryptoFunctionService: CryptoFunctionService, - stateService: StateService, + kdfConfigService: KdfConfigService, ) { - super(cryptoService, cryptoFunctionService, stateService); + super(cryptoService, cryptoFunctionService, kdfConfigService); } async getExport(format: ExportFormat = "csv"): Promise { 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 2346545231..98baf5dca3 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 @@ -1,10 +1,10 @@ import * as papa from "papaparse"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; @@ -36,10 +36,10 @@ export class OrganizationVaultExportService private apiService: ApiService, cryptoService: CryptoService, cryptoFunctionService: CryptoFunctionService, - stateService: StateService, private collectionService: CollectionService, + kdfConfigService: KdfConfigService, ) { - super(cryptoService, cryptoFunctionService, stateService); + super(cryptoService, cryptoFunctionService, kdfConfigService); } async getPasswordProtectedExport( diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts index fc8faa4b5b..b30384f9f4 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts @@ -1,13 +1,12 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config"; +import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service"; import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export"; import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; -import { KdfType, PBKDF2_ITERATIONS } from "@bitwarden/common/platform/enums"; +import { DEFAULT_KDF_CONFIG, KdfType, PBKDF2_ITERATIONS } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string"; -import { StateService } from "@bitwarden/common/platform/services/state.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -110,10 +109,10 @@ function expectEqualCiphers(ciphers: CipherView[] | Cipher[], jsonResult: string expect(actual).toEqual(JSON.stringify(items)); } -function expectEqualFolderViews(folderviews: FolderView[] | Folder[], jsonResult: string) { +function expectEqualFolderViews(folderViews: FolderView[] | Folder[], jsonResult: string) { const actual = JSON.stringify(JSON.parse(jsonResult).folders); const folders: FolderResponse[] = []; - folderviews.forEach((c) => { + folderViews.forEach((c) => { const folder = new FolderResponse(); folder.id = c.id; folder.name = c.name.toString(); @@ -144,19 +143,18 @@ describe("VaultExportService", () => { let cipherService: MockProxy; let folderService: MockProxy; let cryptoService: MockProxy; - let stateService: MockProxy; + let kdfConfigService: MockProxy; beforeEach(() => { cryptoFunctionService = mock(); cipherService = mock(); folderService = mock(); cryptoService = mock(); - stateService = mock(); + kdfConfigService = mock(); folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews); folderService.getAllFromState.mockResolvedValue(UserFolders); - stateService.getKdfType.mockResolvedValue(KdfType.PBKDF2_SHA256); - stateService.getKdfConfig.mockResolvedValue(new KdfConfig(PBKDF2_ITERATIONS.defaultValue)); + kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); cryptoService.encrypt.mockResolvedValue(new EncString("encrypted")); exportService = new IndividualVaultExportService( @@ -164,7 +162,7 @@ describe("VaultExportService", () => { cipherService, cryptoService, cryptoFunctionService, - stateService, + kdfConfigService, ); });