mirror of
https://github.com/bitwarden/browser
synced 2026-01-08 03:23:50 +00:00
[PM-5735] Create kdf Service (#8715)
* key connector migration initial * migrator complete * fix dependencies * finalized tests * fix deps and sync main * clean up definition file * fixing tests * fixed tests * fixing CLI, Browser, Desktop builds * fixed factory options * reverting exports * implemented UserKeyDefinition clearOn * Initial Kdf Service Changes * rename and account setting kdfconfig * fixing tests and renaming migration * fixed DI ordering for browser * rename and fix DI * Clean up Migrations * fixing migrations * begin data structure changes for kdf config * Make KDF more type safe; co-author: jlf0dev * fixing tests * Fixed CLI login and comments * set now accepts userId and test updates --------- Co-authored-by: Jake Fink <jfink@bitwarden.com>
This commit is contained in:
@@ -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<AuthRequestServiceAbstraction>;
|
||||
let userDecryptionOptionsService: MockProxy<InternalUserDecryptionOptionsServiceAbstraction>;
|
||||
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
|
||||
let kdfConfigService: MockProxy<KdfConfigService>;
|
||||
|
||||
let stateProvider: FakeGlobalStateProvider;
|
||||
let loginStrategyCacheExpirationState: FakeGlobalState<Date | null>;
|
||||
@@ -95,6 +97,7 @@ describe("LoginStrategyService", () => {
|
||||
userDecryptionOptionsService = mock<UserDecryptionOptionsService>();
|
||||
billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
|
||||
stateProvider = new FakeGlobalStateProvider();
|
||||
kdfConfigService = mock<KdfConfigService>();
|
||||
|
||||
sut = new LoginStrategyService(
|
||||
accountService,
|
||||
@@ -119,6 +122,7 @@ describe("LoginStrategyService", () => {
|
||||
userDecryptionOptionsService,
|
||||
stateProvider,
|
||||
billingAccountProfileStateService,
|
||||
kdfConfigService,
|
||||
);
|
||||
|
||||
loginStrategyCacheExpirationState = stateProvider.getFake(CACHE_EXPIRATION_KEY);
|
||||
|
||||
@@ -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<MasterKey> {
|
||||
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,
|
||||
);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -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<UserKey | null> {
|
||||
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,
|
||||
);
|
||||
|
||||
@@ -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<CryptoService>();
|
||||
const vaultTimeoutSettingsService = mock<VaultTimeoutSettingsService>();
|
||||
const logService = mock<LogService>();
|
||||
const kdfConfigService = mock<KdfConfigService>();
|
||||
|
||||
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") {
|
||||
|
||||
Reference in New Issue
Block a user