diff --git a/apps/cli/src/commands/login.command.ts b/apps/cli/src/commands/login.command.ts index 6284f8b94b5..b6ec7f39590 100644 --- a/apps/cli/src/commands/login.command.ts +++ b/apps/cli/src/commands/login.command.ts @@ -410,7 +410,7 @@ export class LoginCommand { this.policyService.masterPasswordPolicyOptions$() ); const kdf = await this.stateService.getKdfType(); - const kdfIterations = await this.stateService.getKdfIterations(); + const kdfConfig = await this.stateService.getKdfConfig(); if ( enforcedPolicyOptions != null && @@ -431,7 +431,7 @@ export class LoginCommand { masterPassword, this.email.trim().toLowerCase(), kdf, - kdfIterations + kdfConfig ); const newPasswordHash = await this.cryptoService.hashPassword(masterPassword, newKey); diff --git a/apps/cli/src/commands/unlock.command.ts b/apps/cli/src/commands/unlock.command.ts index 7e2136e6e3e..28c892b58b4 100644 --- a/apps/cli/src/commands/unlock.command.ts +++ b/apps/cli/src/commands/unlock.command.ts @@ -44,8 +44,8 @@ export class UnlockCommand { await this.setNewSessionKey(); const email = await this.stateService.getEmail(); const kdf = await this.stateService.getKdfType(); - const kdfIterations = await this.stateService.getKdfIterations(); - const key = await this.cryptoService.makeKey(password, email, kdf, kdfIterations); + const kdfConfig = await this.stateService.getKdfConfig(); + const key = await this.cryptoService.makeKey(password, email, kdf, kdfConfig); const storedKeyHash = await this.cryptoService.getKeyHash(); let passwordValid = false; diff --git a/apps/web/src/app/organizations/members/components/reset-password.component.ts b/apps/web/src/app/organizations/members/components/reset-password.component.ts index 3d96426f707..391420c48a1 100644 --- a/apps/web/src/app/organizations/members/components/reset-password.component.ts +++ b/apps/web/src/app/organizations/members/components/reset-password.component.ts @@ -20,6 +20,7 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwo import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; +import { KdfConfig } from "@bitwarden/common/models/domain/kdf-config"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/master-password-policy-options"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; @@ -156,6 +157,8 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { const kdfType = response.kdf; const kdfIterations = response.kdfIterations; + const kdfMemory = response.kdfMemory; + const kdfParallelism = response.kdfParallelism; const resetPasswordKey = response.resetPasswordKey; const encryptedPrivateKey = response.encryptedPrivateKey; @@ -175,7 +178,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { this.newPassword, this.email.trim().toLowerCase(), kdfType, - kdfIterations + new KdfConfig(kdfIterations, kdfMemory, kdfParallelism) ); const newPasswordHash = await this.cryptoService.hashPassword(this.newPassword, newKey); diff --git a/apps/web/src/app/settings/change-email.component.ts b/apps/web/src/app/settings/change-email.component.ts index 2c41bd0b78b..134698584ee 100644 --- a/apps/web/src/app/settings/change-email.component.ts +++ b/apps/web/src/app/settings/change-email.component.ts @@ -66,12 +66,12 @@ export class ChangeEmailComponent implements OnInit { request.newEmail = this.newEmail; request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null); const kdf = await this.stateService.getKdfType(); - const kdfIterations = await this.stateService.getKdfIterations(); + const kdfConfig = await this.stateService.getKdfConfig(); const newKey = await this.cryptoService.makeKey( this.masterPassword, this.newEmail, kdf, - kdfIterations + kdfConfig ); request.newMasterPasswordHash = await this.cryptoService.hashPassword( this.masterPassword, diff --git a/apps/web/src/app/settings/change-kdf.component.html b/apps/web/src/app/settings/change-kdf.component.html index 1b3b62a03fb..b01b83a954c 100644 --- a/apps/web/src/app/settings/change-kdf.component.html +++ b/apps/web/src/app/settings/change-kdf.component.html @@ -32,43 +32,94 @@ > - + {{ o.name }} + + {{ "kdfMemory" | i18n }} + + - {{ "kdfIterations" | i18n }} - - - - + + {{ "kdfIterations" | i18n }} + + + + + + + {{ "kdfIterations" | i18n }} + + {{ "kdfParallelism" | i18n }} + + - - - {{ "kdfIterationsDesc" | i18n: (recommendedKdfIterations | number) }} - {{ "warning" | i18n }}: {{ "kdfIterationsWarning" | i18n: (50000 | number) }} - - + + + {{ "kdfIterationsDesc" | i18n: (recommendedPbkdf2Iterations | number) }} + + + {{ "kdfIterationsWarning" | i18n: (100000 | number) }} + + + + {{ "argon2Desc" | i18n }} + {{ "argon2Warning" | i18n }} + diff --git a/apps/web/src/app/settings/change-kdf.component.ts b/apps/web/src/app/settings/change-kdf.component.ts index 88e017eb63f..3f41c888d10 100644 --- a/apps/web/src/app/settings/change-kdf.component.ts +++ b/apps/web/src/app/settings/change-kdf.component.ts @@ -7,7 +7,15 @@ import { LogService } from "@bitwarden/common/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; -import { DEFAULT_PBKDF2_ITERATIONS, KdfType } from "@bitwarden/common/enums/kdfType"; +import { + DEFAULT_KDF_CONFIG, + DEFAULT_PBKDF2_ITERATIONS, + DEFAULT_ARGON2_ITERATIONS, + DEFAULT_ARGON2_MEMORY, + DEFAULT_ARGON2_PARALLELISM, + KdfType, +} from "@bitwarden/common/enums/kdfType"; +import { KdfConfig } from "@bitwarden/common/models/domain/kdf-config"; import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; @Component({ @@ -16,11 +24,12 @@ import { KdfRequest } from "@bitwarden/common/models/request/kdf.request"; }) export class ChangeKdfComponent implements OnInit { masterPassword: string; - kdfIterations: number; kdf = KdfType.PBKDF2_SHA256; + kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG; + kdfType = KdfType; kdfOptions: any[] = []; formPromise: Promise; - recommendedKdfIterations = DEFAULT_PBKDF2_ITERATIONS; + recommendedPbkdf2Iterations = DEFAULT_PBKDF2_ITERATIONS; constructor( private apiService: ApiService, @@ -31,12 +40,15 @@ export class ChangeKdfComponent implements OnInit { private logService: LogService, private stateService: StateService ) { - this.kdfOptions = [{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 }]; + this.kdfOptions = [ + { name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 }, + { name: "Argon2id", value: KdfType.Argon2id }, + ]; } async ngOnInit() { this.kdf = await this.stateService.getKdfType(); - this.kdfIterations = await this.stateService.getKdfIterations(); + this.kdfConfig = await this.stateService.getKdfConfig(); } async submit() { @@ -46,25 +58,8 @@ export class ChangeKdfComponent implements OnInit { return; } - const request = new KdfRequest(); - request.kdf = this.kdf; - request.kdfIterations = this.kdfIterations; - request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null); - const email = await this.stateService.getEmail(); - const newKey = await this.cryptoService.makeKey( - this.masterPassword, - email, - this.kdf, - this.kdfIterations - ); - request.newMasterPasswordHash = await this.cryptoService.hashPassword( - this.masterPassword, - newKey - ); - const newEncKey = await this.cryptoService.remakeEncKey(newKey); - request.key = newEncKey[1].encryptedString; try { - this.formPromise = this.apiService.postAccountKdf(request); + this.formPromise = this.makeKeyAndSaveAsync(); await this.formPromise; this.platformUtilsService.showToast( "success", @@ -76,4 +71,42 @@ export class ChangeKdfComponent implements OnInit { this.logService.error(e); } } + + async onChangeKdf(newValue: KdfType) { + if (newValue === KdfType.PBKDF2_SHA256) { + this.kdfConfig = new KdfConfig(DEFAULT_PBKDF2_ITERATIONS); + } else if (newValue === KdfType.Argon2id) { + this.kdfConfig = new KdfConfig( + DEFAULT_ARGON2_ITERATIONS, + DEFAULT_ARGON2_MEMORY, + DEFAULT_ARGON2_PARALLELISM + ); + } else { + throw new Error("Unknown KDF type."); + } + } + + private async makeKeyAndSaveAsync() { + const request = new KdfRequest(); + request.kdf = this.kdf; + request.kdfIterations = this.kdfConfig.iterations; + request.kdfMemory = this.kdfConfig.memory; + request.kdfParallelism = this.kdfConfig.parallelism; + request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null); + const email = await this.stateService.getEmail(); + const newKey = await this.cryptoService.makeKey( + this.masterPassword, + email, + this.kdf, + this.kdfConfig + ); + request.newMasterPasswordHash = await this.cryptoService.hashPassword( + this.masterPassword, + newKey + ); + const newEncKey = await this.cryptoService.remakeEncKey(newKey); + request.key = newEncKey[1].encryptedString; + + await this.apiService.postAccountKdf(request); + } } diff --git a/apps/web/src/app/settings/emergency-access-takeover.component.ts b/apps/web/src/app/settings/emergency-access-takeover.component.ts index 618c134b567..303496d2a66 100644 --- a/apps/web/src/app/settings/emergency-access-takeover.component.ts +++ b/apps/web/src/app/settings/emergency-access-takeover.component.ts @@ -13,6 +13,7 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.serv import { StateService } from "@bitwarden/common/abstractions/state.service"; import { KdfType } from "@bitwarden/common/enums/kdfType"; import { PolicyData } from "@bitwarden/common/models/data/policy.data"; +import { KdfConfig } from "@bitwarden/common/models/domain/kdf-config"; import { Policy } from "@bitwarden/common/models/domain/policy"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { EmergencyAccessPasswordRequest } from "@bitwarden/common/models/request/emergency-access-password.request"; @@ -102,7 +103,11 @@ export class EmergencyAccessTakeoverComponent this.masterPassword, this.email, takeoverResponse.kdf, - takeoverResponse.kdfIterations + new KdfConfig( + takeoverResponse.kdfIterations, + takeoverResponse.kdfMemory, + takeoverResponse.kdfParallelism + ) ); const masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key); diff --git a/apps/web/src/app/vault/vault.component.ts b/apps/web/src/app/vault/vault.component.ts index 616444ebadc..a3efe05921f 100644 --- a/apps/web/src/app/vault/vault.component.ts +++ b/apps/web/src/app/vault/vault.component.ts @@ -23,7 +23,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { StateService } from "@bitwarden/common/abstractions/state.service"; import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; -import { DEFAULT_PBKDF2_ITERATIONS } from "@bitwarden/common/enums/kdfType"; +import { KdfType, DEFAULT_PBKDF2_ITERATIONS } from "@bitwarden/common/enums/kdfType"; import { ServiceUtils } from "@bitwarden/common/misc/serviceUtils"; import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; @@ -393,9 +393,9 @@ export class VaultComponent implements OnInit, OnDestroy { } async isLowKdfIteration() { - const kdfIterations = await this.stateService.getKdfIterations(); - - return kdfIterations < DEFAULT_PBKDF2_ITERATIONS; + const kdfType = await this.stateService.getKdfType(); + const kdfOptions = await this.stateService.getKdfConfig(); + return kdfType === KdfType.PBKDF2_SHA256 && kdfOptions.iterations < DEFAULT_PBKDF2_ITERATIONS; } get breadcrumbs(): TreeNode[] { diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 0772ef4521f..67f6dc3814b 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -1150,7 +1150,7 @@ } }, "kdfIterationsWarning": { - "message": "Setting your KDF iterations too high could result in poor performance when logging into (and unlocking) Bitwarden on devices with slower CPUs. We recommend that you increase the value in increments of $INCREMENT$ and then test all of your devices.", + "message": "Setting your KDF iterations too high could result in poor performance when logging into (and unlocking) Bitwarden on slower or older devices. We recommend that you increase the value in increments of $INCREMENT$ and then test all of your devices.", "placeholders": { "increment": { "content": "$1", @@ -1158,6 +1158,19 @@ } } }, + "kdfMemory": { + "message": "KDF memory (MB)", + "description": "Memory refers to computer memory (RAM). MB is short for megabytes." + }, + "argon2Warning": { + "message": "Setting your KDF iterations, memory, and parallelism too high could result in poor performance when logging into (and unlocking) Bitwarden on slower or older devices. We recommend changing these individually in small increments and then test all of your devices." + }, + "kdfParallelism": { + "message": "KDF parallelism" + }, + "argon2Desc": { + "message": "Higher KDF iterations, memory, and parallelism can help protect your master password from being brute forced by an attacker." + }, "changeKdf": { "message": "Change KDF" }, diff --git a/libs/angular/src/components/change-password.component.ts b/libs/angular/src/components/change-password.component.ts index 77403fbe67a..f6eac733099 100644 --- a/libs/angular/src/components/change-password.component.ts +++ b/libs/angular/src/components/change-password.component.ts @@ -10,6 +10,7 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.serv import { StateService } from "@bitwarden/common/abstractions/state.service"; import { KdfType } from "@bitwarden/common/enums/kdfType"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; +import { KdfConfig } from "@bitwarden/common/models/domain/kdf-config"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/master-password-policy-options"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; @@ -27,7 +28,7 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { protected email: string; protected kdf: KdfType; - protected kdfIterations: number; + protected kdfConfig: KdfConfig; protected destroy$ = new Subject(); @@ -70,14 +71,14 @@ export class ChangePasswordComponent implements OnInit, OnDestroy { if (this.kdf == null) { this.kdf = await this.stateService.getKdfType(); } - if (this.kdfIterations == null) { - this.kdfIterations = await this.stateService.getKdfIterations(); + if (this.kdfConfig == null) { + this.kdfConfig = await this.stateService.getKdfConfig(); } const key = await this.cryptoService.makeKey( this.masterPassword, email.trim().toLowerCase(), this.kdf, - this.kdfIterations + this.kdfConfig ); const masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, key); diff --git a/libs/angular/src/components/lock.component.ts b/libs/angular/src/components/lock.component.ts index cb709c46dd0..f48254660a2 100644 --- a/libs/angular/src/components/lock.component.ts +++ b/libs/angular/src/components/lock.component.ts @@ -136,13 +136,13 @@ export class LockComponent implements OnInit, OnDestroy { let failed = true; try { const kdf = await this.stateService.getKdfType(); - const kdfIterations = await this.stateService.getKdfIterations(); + const kdfConfig = await this.stateService.getKdfConfig(); if (this.pinSet[0]) { const key = await this.cryptoService.makeKeyFromPin( this.pin, this.email, kdf, - kdfIterations, + kdfConfig, await this.stateService.getDecryptedPinProtected() ); const encKey = await this.cryptoService.getEncKey(key); @@ -153,12 +153,7 @@ export class LockComponent implements OnInit, OnDestroy { await this.setKeyAndContinue(key); } } else { - const key = await this.cryptoService.makeKeyFromPin( - this.pin, - this.email, - kdf, - kdfIterations - ); + const key = await this.cryptoService.makeKeyFromPin(this.pin, this.email, kdf, kdfConfig); failed = false; await this.setKeyAndContinue(key); } @@ -194,14 +189,9 @@ export class LockComponent implements OnInit, OnDestroy { private async doUnlockWithMasterPassword() { const kdf = await this.stateService.getKdfType(); - const kdfIterations = await this.stateService.getKdfIterations(); + const kdfConfig = await this.stateService.getKdfConfig(); - const key = await this.cryptoService.makeKey( - this.masterPassword, - this.email, - kdf, - kdfIterations - ); + const key = await this.cryptoService.makeKey(this.masterPassword, this.email, kdf, kdfConfig); const storedKeyHash = await this.cryptoService.getKeyHash(); let passwordValid = false; @@ -244,7 +234,7 @@ export class LockComponent implements OnInit, OnDestroy { const protectedPin = await this.stateService.getProtectedPin(); const encKey = await this.cryptoService.getEncKey(key); const decPin = await this.cryptoService.decryptToUtf8(new EncString(protectedPin), encKey); - const pinKey = await this.cryptoService.makePinKey(decPin, this.email, kdf, kdfIterations); + const pinKey = await this.cryptoService.makePinKey(decPin, this.email, kdf, kdfConfig); await this.stateService.setDecryptedPinProtected( await this.cryptoService.encrypt(key.key, pinKey) ); diff --git a/libs/angular/src/components/register.component.ts b/libs/angular/src/components/register.component.ts index dc64b97a852..6971cb5d7c3 100644 --- a/libs/angular/src/components/register.component.ts +++ b/libs/angular/src/components/register.component.ts @@ -16,7 +16,7 @@ import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; -import { DEFAULT_KDF_TYPE, DEFAULT_PBKDF2_ITERATIONS } from "@bitwarden/common/enums/kdfType"; +import { DEFAULT_KDF_CONFIG, DEFAULT_KDF_TYPE } from "@bitwarden/common/enums/kdfType"; import { PasswordLogInCredentials } from "@bitwarden/common/models/domain/log-in-credentials"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request"; @@ -269,8 +269,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn ): Promise { const hint = this.formGroup.value.hint; const kdf = DEFAULT_KDF_TYPE; - const kdfIterations = DEFAULT_PBKDF2_ITERATIONS; - const key = await this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations); + const kdfConfig = DEFAULT_KDF_CONFIG; + const key = await this.cryptoService.makeKey(masterPassword, email, kdf, kdfConfig); const encKey = await this.cryptoService.makeEncKey(key); const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key); const keys = await this.cryptoService.makeKeyPair(encKey[0]); @@ -280,10 +280,12 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn hashedPassword, hint, encKey[1].encryptedString, - kdf, - kdfIterations, this.referenceData, - this.captchaToken + this.captchaToken, + kdf, + 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/components/set-password.component.ts b/libs/angular/src/components/set-password.component.ts index 408ca7e6621..1459e9fd03e 100644 --- a/libs/angular/src/components/set-password.component.ts +++ b/libs/angular/src/components/set-password.component.ts @@ -16,7 +16,7 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.serv import { StateService } from "@bitwarden/common/abstractions/state.service"; import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { HashPurpose } from "@bitwarden/common/enums/hashPurpose"; -import { DEFAULT_KDF_TYPE, DEFAULT_PBKDF2_ITERATIONS } from "@bitwarden/common/enums/kdfType"; +import { DEFAULT_KDF_TYPE, DEFAULT_KDF_CONFIG } from "@bitwarden/common/enums/kdfType"; import { Utils } from "@bitwarden/common/misc/utils"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; @@ -93,7 +93,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { async setupSubmitActions() { this.kdf = DEFAULT_KDF_TYPE; - this.kdfIterations = DEFAULT_PBKDF2_ITERATIONS; + this.kdfConfig = DEFAULT_KDF_CONFIG; return true; } @@ -107,10 +107,12 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { masterPasswordHash, encKey[1].encryptedString, this.hint, - this.kdf, - this.kdfIterations, this.identifier, - new KeysRequest(keys[0], keys[1].encryptedString) + new KeysRequest(keys[0], keys[1].encryptedString), + this.kdf, + this.kdfConfig.iterations, + this.kdfConfig.memory, + this.kdfConfig.parallelism ); try { if (this.resetPasswordAutoEnroll) { @@ -173,7 +175,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { keys: [string, EncString] ) { await this.stateService.setKdfType(this.kdf); - await this.stateService.setKdfIterations(this.kdfIterations); + await this.stateService.setKdfConfig(this.kdfConfig); await this.cryptoService.setKey(key); await this.cryptoService.setEncKey(encKey[1].encryptedString); await this.cryptoService.setEncPrivateKey(keys[1].encryptedString); diff --git a/libs/angular/src/components/set-pin.component.ts b/libs/angular/src/components/set-pin.component.ts index c6db38a85d8..5819e6c5243 100644 --- a/libs/angular/src/components/set-pin.component.ts +++ b/libs/angular/src/components/set-pin.component.ts @@ -36,9 +36,9 @@ export class SetPinComponent implements OnInit { } const kdf = await this.stateService.getKdfType(); - const kdfIterations = await this.stateService.getKdfIterations(); + const kdfConfig = await this.stateService.getKdfConfig(); const email = await this.stateService.getEmail(); - const pinKey = await this.cryptoService.makePinKey(this.pin, email, kdf, kdfIterations); + const pinKey = await this.cryptoService.makePinKey(this.pin, email, kdf, kdfConfig); const key = await this.cryptoService.getKey(); const pinProtectedKey = await this.cryptoService.encrypt(key.key, pinKey); if (this.masterPassOnRestart) { diff --git a/libs/angular/src/components/update-password.component.ts b/libs/angular/src/components/update-password.component.ts index e923a100da3..9b8d3c0aa11 100644 --- a/libs/angular/src/components/update-password.component.ts +++ b/libs/angular/src/components/update-password.component.ts @@ -86,7 +86,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent { } this.kdf = await this.stateService.getKdfType(); - this.kdfIterations = await this.stateService.getKdfIterations(); + this.kdfConfig = await this.stateService.getKdfConfig(); return true; } diff --git a/libs/angular/src/components/update-temp-password.component.ts b/libs/angular/src/components/update-temp-password.component.ts index f1c3ce15491..618d6816db0 100644 --- a/libs/angular/src/components/update-temp-password.component.ts +++ b/libs/angular/src/components/update-temp-password.component.ts @@ -62,7 +62,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { async setupSubmitActions(): Promise { this.email = await this.stateService.getEmail(); this.kdf = await this.stateService.getKdfType(); - this.kdfIterations = await this.stateService.getKdfIterations(); + this.kdfConfig = await this.stateService.getKdfConfig(); return true; } @@ -82,7 +82,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent { this.masterPassword, this.email.trim().toLowerCase(), this.kdf, - this.kdfIterations + this.kdfConfig ); const newPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, newKey); diff --git a/libs/common/src/abstractions/crypto.service.ts b/libs/common/src/abstractions/crypto.service.ts index e4dd43cdf23..25f1a07ba04 100644 --- a/libs/common/src/abstractions/crypto.service.ts +++ b/libs/common/src/abstractions/crypto.service.ts @@ -3,6 +3,7 @@ import { KdfType } from "../enums/kdfType"; import { KeySuffixOptions } from "../enums/keySuffixOptions"; import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; import { EncString } from "../models/domain/enc-string"; +import { KdfConfig } from "../models/domain/kdf-config"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { ProfileOrganizationResponse } from "../models/response/profile-organization.response"; import { ProfileProviderOrganizationResponse } from "../models/response/profile-provider-organization.response"; @@ -47,13 +48,13 @@ export abstract class CryptoService { password: string, salt: string, kdf: KdfType, - kdfIterations: number + kdfConfig: KdfConfig ) => Promise; makeKeyFromPin: ( pin: string, salt: string, kdf: KdfType, - kdfIterations: number, + kdfConfig: KdfConfig, protectedKeyCs?: EncString ) => Promise; makeShareKey: () => Promise<[EncString, SymmetricCryptoKey]>; @@ -62,7 +63,7 @@ export abstract class CryptoService { pin: string, salt: string, kdf: KdfType, - kdfIterations: number + kdfConfig: KdfConfig ) => Promise; makeSendKey: (keyMaterial: ArrayBuffer) => Promise; hashPassword: ( diff --git a/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts b/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts index 7231c57a59f..745d8e98445 100644 --- a/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts +++ b/libs/common/src/abstractions/organization-user/responses/organization-user.response.ts @@ -61,6 +61,8 @@ export class OrganizationUserDetailsResponse extends OrganizationUserResponse { export class OrganizationUserResetPasswordDetailsReponse extends BaseResponse { kdf: KdfType; kdfIterations: number; + kdfMemory?: number; + kdfParallelism?: number; resetPasswordKey: string; encryptedPrivateKey: string; @@ -68,6 +70,8 @@ export class OrganizationUserResetPasswordDetailsReponse extends BaseResponse { super(response); this.kdf = this.getResponseProperty("Kdf"); this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.kdfMemory = this.getResponseProperty("KdfMemory"); + this.kdfParallelism = this.getResponseProperty("KdfParallelism"); this.resetPasswordKey = this.getResponseProperty("ResetPasswordKey"); this.encryptedPrivateKey = this.getResponseProperty("EncryptedPrivateKey"); } diff --git a/libs/common/src/abstractions/state.service.ts b/libs/common/src/abstractions/state.service.ts index e7974e6852c..a2d2e3960e4 100644 --- a/libs/common/src/abstractions/state.service.ts +++ b/libs/common/src/abstractions/state.service.ts @@ -18,6 +18,7 @@ import { Account, AccountSettingsSettings } from "../models/domain/account"; import { EncString } from "../models/domain/enc-string"; import { EnvironmentUrls } from "../models/domain/environment-urls"; import { GeneratedPasswordHistory } from "../models/domain/generated-password-history"; +import { KdfConfig } from "../models/domain/kdf-config"; import { Policy } from "../models/domain/policy"; import { StorageOptions } from "../models/domain/storage-options"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; @@ -252,8 +253,8 @@ export abstract class StateService { getInstalledVersion: (options?: StorageOptions) => Promise; setInstalledVersion: (value: string, options?: StorageOptions) => Promise; getIsAuthenticated: (options?: StorageOptions) => Promise; - getKdfIterations: (options?: StorageOptions) => Promise; - setKdfIterations: (value: number, options?: StorageOptions) => Promise; + getKdfConfig: (options?: StorageOptions) => Promise; + setKdfConfig: (kdfConfig: KdfConfig, options?: StorageOptions) => Promise; getKdfType: (options?: StorageOptions) => Promise; setKdfType: (value: KdfType, options?: StorageOptions) => Promise; getKeyHash: (options?: StorageOptions) => Promise; diff --git a/libs/common/src/enums/kdfType.ts b/libs/common/src/enums/kdfType.ts index 484d139ae5b..c4230ca7ddb 100644 --- a/libs/common/src/enums/kdfType.ts +++ b/libs/common/src/enums/kdfType.ts @@ -1,12 +1,15 @@ +import { KdfConfig } from "../models/domain/kdf-config"; + export enum KdfType { PBKDF2_SHA256 = 0, Argon2id = 1, } -export const DEFAULT_ARGON2_MEMORY = 19; -export const DEFAULT_ARGON2_PARALLELISM = 1; -export const DEFAULT_ARGON2_ITERATIONS = 2; +export const DEFAULT_ARGON2_MEMORY = 64; +export const DEFAULT_ARGON2_PARALLELISM = 4; +export const DEFAULT_ARGON2_ITERATIONS = 3; export const DEFAULT_KDF_TYPE = KdfType.PBKDF2_SHA256; export const DEFAULT_PBKDF2_ITERATIONS = 600000; +export const DEFAULT_KDF_CONFIG = new KdfConfig(DEFAULT_PBKDF2_ITERATIONS); export const SEND_KDF_ITERATIONS = 100000; diff --git a/libs/common/src/importers/bitwarden-password-protected-importer.ts b/libs/common/src/importers/bitwarden-password-protected-importer.ts index 19e1aac0aec..10eeb4b9fed 100644 --- a/libs/common/src/importers/bitwarden-password-protected-importer.ts +++ b/libs/common/src/importers/bitwarden-password-protected-importer.ts @@ -3,6 +3,7 @@ import { I18nService } from "../abstractions/i18n.service"; import { KdfType } from "../enums/kdfType"; import { EncString } from "../models/domain/enc-string"; import { ImportResult } from "../models/domain/import-result"; +import { KdfConfig } from "../models/domain/kdf-config"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { BitwardenJsonImporter } from "./bitwarden-json-importer"; @@ -49,7 +50,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im this.password, jdoc.salt, KdfType.PBKDF2_SHA256, - jdoc.kdfIterations + new KdfConfig(jdoc.kdfIterations) ); const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT); diff --git a/libs/common/src/misc/logInStrategies/logIn.strategy.ts b/libs/common/src/misc/logInStrategies/logIn.strategy.ts index bf16ff0e5a4..84d8074ddc8 100644 --- a/libs/common/src/misc/logInStrategies/logIn.strategy.ts +++ b/libs/common/src/misc/logInStrategies/logIn.strategy.ts @@ -107,6 +107,8 @@ export abstract class LogInStrategy { email: accountInformation.email, hasPremiumPersonally: accountInformation.premium, kdfIterations: tokenResponse.kdfIterations, + kdfMemory: tokenResponse.kdfMemory, + kdfParallelism: tokenResponse.kdfParallelism, kdfType: tokenResponse.kdf, }, }, diff --git a/libs/common/src/models/domain/account.ts b/libs/common/src/models/domain/account.ts index 40de21b3b68..4a65f35a8e7 100644 --- a/libs/common/src/models/domain/account.ts +++ b/libs/common/src/models/domain/account.ts @@ -189,6 +189,8 @@ export class AccountProfile { usesKeyConnector?: boolean; keyHash?: string; kdfIterations?: number; + kdfMemory?: number; + kdfParallelism?: number; kdfType?: KdfType; static fromJSON(obj: Jsonify): AccountProfile { diff --git a/libs/common/src/models/domain/kdf-config.ts b/libs/common/src/models/domain/kdf-config.ts new file mode 100644 index 00000000000..a25ba586e90 --- /dev/null +++ b/libs/common/src/models/domain/kdf-config.ts @@ -0,0 +1,11 @@ +export class KdfConfig { + iterations: number; + memory?: number; + parallelism?: number; + + constructor(iterations: number, memory?: number, parallelism?: number) { + this.iterations = iterations; + this.memory = memory; + this.parallelism = parallelism; + } +} diff --git a/libs/common/src/models/request/kdf.request.ts b/libs/common/src/models/request/kdf.request.ts index 7377d01d6c3..d71bb2cdcb3 100644 --- a/libs/common/src/models/request/kdf.request.ts +++ b/libs/common/src/models/request/kdf.request.ts @@ -5,4 +5,6 @@ import { PasswordRequest } from "./password.request"; export class KdfRequest extends PasswordRequest { kdf: KdfType; kdfIterations: number; + kdfMemory?: number; + kdfParallelism?: number; } diff --git a/libs/common/src/models/request/register.request.ts b/libs/common/src/models/request/register.request.ts index 646d3e78f22..34002b907d0 100644 --- a/libs/common/src/models/request/register.request.ts +++ b/libs/common/src/models/request/register.request.ts @@ -16,10 +16,12 @@ export class RegisterRequest implements CaptchaProtectedRequest { public masterPasswordHash: string, masterPasswordHint: string, public key: string, + public referenceData: ReferenceEventRequest, + public captchaResponse: string, public kdf: KdfType, public kdfIterations: number, - public referenceData: ReferenceEventRequest, - public captchaResponse: string + public kdfMemory?: number, + public kdfParallelism?: number ) { this.masterPasswordHint = masterPasswordHint ? masterPasswordHint : null; } diff --git a/libs/common/src/models/request/set-password.request.ts b/libs/common/src/models/request/set-password.request.ts index b87e42e2f03..33bfdf198a9 100644 --- a/libs/common/src/models/request/set-password.request.ts +++ b/libs/common/src/models/request/set-password.request.ts @@ -9,22 +9,28 @@ export class SetPasswordRequest { keys: KeysRequest; kdf: KdfType; kdfIterations: number; + kdfMemory?: number; + kdfParallelism?: number; orgIdentifier: string; constructor( masterPasswordHash: string, key: string, masterPasswordHint: string, + orgIdentifier: string, + keys: KeysRequest, kdf: KdfType, kdfIterations: number, - orgIdentifier: string, - keys: KeysRequest + kdfMemory?: number, + kdfParallelism?: number ) { this.masterPasswordHash = masterPasswordHash; this.key = key; this.masterPasswordHint = masterPasswordHint; this.kdf = kdf; this.kdfIterations = kdfIterations; + this.kdfMemory = kdfMemory; + this.kdfParallelism = kdfParallelism; this.orgIdentifier = orgIdentifier; this.keys = keys; } diff --git a/libs/common/src/models/response/emergency-access.response.ts b/libs/common/src/models/response/emergency-access.response.ts index 4aa969a953f..2259b4f0292 100644 --- a/libs/common/src/models/response/emergency-access.response.ts +++ b/libs/common/src/models/response/emergency-access.response.ts @@ -59,6 +59,8 @@ export class EmergencyAccessTakeoverResponse extends BaseResponse { keyEncrypted: string; kdf: KdfType; kdfIterations: number; + kdfMemory?: number; + kdfParallelism?: number; constructor(response: any) { super(response); @@ -66,6 +68,8 @@ export class EmergencyAccessTakeoverResponse extends BaseResponse { this.keyEncrypted = this.getResponseProperty("KeyEncrypted"); this.kdf = this.getResponseProperty("Kdf"); this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.kdfMemory = this.getResponseProperty("KdfMemory"); + this.kdfParallelism = this.getResponseProperty("KdfParallelism"); } } diff --git a/libs/common/src/models/response/identity-token.response.ts b/libs/common/src/models/response/identity-token.response.ts index accadd0b378..3e23e026dca 100644 --- a/libs/common/src/models/response/identity-token.response.ts +++ b/libs/common/src/models/response/identity-token.response.ts @@ -14,6 +14,8 @@ export class IdentityTokenResponse extends BaseResponse { twoFactorToken: string; kdf: KdfType; kdfIterations: number; + kdfMemory?: number; + kdfParallelism?: number; forcePasswordReset: boolean; apiUseKeyConnector: boolean; keyConnectorUrl: string; @@ -31,6 +33,8 @@ export class IdentityTokenResponse extends BaseResponse { this.twoFactorToken = this.getResponseProperty("TwoFactorToken"); this.kdf = this.getResponseProperty("Kdf"); this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.kdfMemory = this.getResponseProperty("KdfMemory"); + this.kdfParallelism = this.getResponseProperty("KdfParallelism"); this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset"); this.apiUseKeyConnector = this.getResponseProperty("ApiUseKeyConnector"); this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); diff --git a/libs/common/src/models/response/prelogin.response.ts b/libs/common/src/models/response/prelogin.response.ts index 18c3ff73fc7..d790d625a20 100644 --- a/libs/common/src/models/response/prelogin.response.ts +++ b/libs/common/src/models/response/prelogin.response.ts @@ -5,10 +5,14 @@ import { BaseResponse } from "./base.response"; export class PreloginResponse extends BaseResponse { kdf: KdfType; kdfIterations: number; + kdfMemory?: number; + kdfParallelism?: number; constructor(response: any) { super(response); this.kdf = this.getResponseProperty("Kdf"); this.kdfIterations = this.getResponseProperty("KdfIterations"); + this.kdfMemory = this.getResponseProperty("KdfMemory"); + this.kdfParallelism = this.getResponseProperty("KdfParallelism"); } } diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index 4e97f701113..c37b2c27087 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -22,6 +22,7 @@ import { PasswordlessLogInStrategy } from "../misc/logInStrategies/passwordlessL import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy"; import { UserApiLogInStrategy } from "../misc/logInStrategies/user-api-login.strategy"; import { AuthResult } from "../models/domain/auth-result"; +import { KdfConfig } from "../models/domain/kdf-config"; import { UserApiLogInCredentials, PasswordLogInCredentials, @@ -247,19 +248,23 @@ export class AuthService implements AuthServiceAbstraction { async makePreloginKey(masterPassword: string, email: string): Promise { email = email.trim().toLowerCase(); let kdf: KdfType = null; - let kdfIterations: number = null; + let kdfConfig: KdfConfig = null; try { const preloginResponse = await this.apiService.postPrelogin(new PreloginRequest(email)); if (preloginResponse != null) { kdf = preloginResponse.kdf; - kdfIterations = preloginResponse.kdfIterations; + kdfConfig = new KdfConfig( + preloginResponse.kdfIterations, + preloginResponse.kdfMemory, + preloginResponse.kdfParallelism + ); } } catch (e) { if (e == null || e.statusCode !== 404) { throw e; } } - return this.cryptoService.makeKey(masterPassword, email, kdf, kdfIterations); + return this.cryptoService.makeKey(masterPassword, email, kdf, kdfConfig); } async authResponsePushNotifiction(notification: AuthRequestPushNotification): Promise { diff --git a/libs/common/src/services/crypto.service.ts b/libs/common/src/services/crypto.service.ts index 8398fd07577..3ae87c1965f 100644 --- a/libs/common/src/services/crypto.service.ts +++ b/libs/common/src/services/crypto.service.ts @@ -8,7 +8,12 @@ import { PlatformUtilsService } from "../abstractions/platformUtils.service"; import { StateService } from "../abstractions/state.service"; import { EncryptionType } from "../enums/encryptionType"; import { HashPurpose } from "../enums/hashPurpose"; -import { DEFAULT_ARGON2_ITERATIONS, KdfType } from "../enums/kdfType"; +import { + DEFAULT_ARGON2_ITERATIONS, + DEFAULT_ARGON2_MEMORY, + DEFAULT_ARGON2_PARALLELISM, + KdfType, +} from "../enums/kdfType"; import { KeySuffixOptions } from "../enums/keySuffixOptions"; import { sequentialize } from "../misc/sequentialize"; import { Utils } from "../misc/utils"; @@ -17,6 +22,7 @@ import { EncryptedOrganizationKeyData } from "../models/data/encrypted-organizat import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; import { EncString } from "../models/domain/enc-string"; import { BaseEncryptedOrganizationKey } from "../models/domain/encrypted-organization-key"; +import { KdfConfig } from "../models/domain/kdf-config"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { ProfileOrganizationResponse } from "../models/response/profile-organization.response"; import { ProfileProviderOrganizationResponse } from "../models/response/profile-provider-organization.response"; @@ -408,28 +414,44 @@ export class CryptoService implements CryptoServiceAbstraction { password: string, salt: string, kdf: KdfType, - kdfIterations: number + kdfConfig: KdfConfig ): Promise { let key: ArrayBuffer = null; if (kdf == null || kdf === KdfType.PBKDF2_SHA256) { - if (kdfIterations == null) { - kdfIterations = 5000; - } else if (kdfIterations < 5000) { + if (kdfConfig.iterations == null) { + kdfConfig.iterations = 5000; + } else if (kdfConfig.iterations < 5000) { throw new Error("PBKDF2 iteration minimum is 5000."); } - key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfIterations); + key = await this.cryptoFunctionService.pbkdf2(password, salt, "sha256", kdfConfig.iterations); } else if (kdf == KdfType.Argon2id) { - if (kdfIterations == null) { - kdfIterations = DEFAULT_ARGON2_ITERATIONS; - } else if (kdfIterations < 2) { + if (kdfConfig.iterations == null) { + kdfConfig.iterations = DEFAULT_ARGON2_ITERATIONS; + } else if (kdfConfig.iterations < 2) { throw new Error("Argon2 iteration minimum is 2."); } + + if (kdfConfig.memory == null) { + kdfConfig.memory = DEFAULT_ARGON2_MEMORY; + } else if (kdfConfig.memory < 16) { + throw new Error("Argon2 memory minimum is 16 MB"); + } else if (kdfConfig.memory > 1024) { + throw new Error("Argon2 memory maximum is 1024 MB"); + } + + if (kdfConfig.parallelism == null) { + kdfConfig.parallelism = DEFAULT_ARGON2_PARALLELISM; + } else if (kdfConfig.parallelism < 1) { + throw new Error("Argon2 parallelism minimum is 1."); + } + + const saltHash = await this.cryptoFunctionService.hash(salt, "sha256"); key = await this.cryptoFunctionService.argon2( password, - salt, - kdfIterations, - 16 * 1024, // convert to KiB from MiB - 2 + saltHash, + kdfConfig.iterations, + kdfConfig.memory * 1024, // convert to KiB from MiB + kdfConfig.parallelism ); } else { throw new Error("Unknown Kdf."); @@ -441,7 +463,7 @@ export class CryptoService implements CryptoServiceAbstraction { pin: string, salt: string, kdf: KdfType, - kdfIterations: number, + kdfConfig: KdfConfig, protectedKeyCs: EncString = null ): Promise { if (protectedKeyCs == null) { @@ -451,7 +473,7 @@ export class CryptoService implements CryptoServiceAbstraction { } protectedKeyCs = new EncString(pinProtectedKey); } - const pinKey = await this.makePinKey(pin, salt, kdf, kdfIterations); + const pinKey = await this.makePinKey(pin, salt, kdf, kdfConfig); const decKey = await this.decryptToBytes(protectedKeyCs, pinKey); return new SymmetricCryptoKey(decKey); } @@ -474,9 +496,9 @@ export class CryptoService implements CryptoServiceAbstraction { pin: string, salt: string, kdf: KdfType, - kdfIterations: number + kdfConfig: KdfConfig ): Promise { - const pinKey = await this.makeKey(pin, salt, kdf, kdfIterations); + const pinKey = await this.makeKey(pin, salt, kdf, kdfConfig); return await this.stretchKey(pinKey); } diff --git a/libs/common/src/services/export.service.ts b/libs/common/src/services/export.service.ts index 68706e26c7f..b385440448d 100644 --- a/libs/common/src/services/export.service.ts +++ b/libs/common/src/services/export.service.ts @@ -17,6 +17,7 @@ import { CollectionData } from "../models/data/collection.data"; import { Cipher } from "../models/domain/cipher"; import { Collection } from "../models/domain/collection"; import { Folder } from "../models/domain/folder"; +import { KdfConfig } from "../models/domain/kdf-config"; import { CipherWithIdExport as CipherExport } from "../models/export/cipher-with-ids.export"; import { CollectionWithIdExport as CollectionExport } from "../models/export/collection-with-id.export"; import { EventExport } from "../models/export/event.export"; @@ -54,12 +55,12 @@ export class ExportService implements ExportServiceAbstraction { : await this.getExport("json"); const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16)); - const kdfIterations = DEFAULT_PBKDF2_ITERATIONS; + const kdfConfig = new KdfConfig(DEFAULT_PBKDF2_ITERATIONS); const key = await this.cryptoService.makePinKey( password, salt, KdfType.PBKDF2_SHA256, - kdfIterations + kdfConfig ); const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), key); @@ -69,7 +70,7 @@ export class ExportService implements ExportServiceAbstraction { encrypted: true, passwordProtected: true, salt: salt, - kdfIterations: kdfIterations, + kdfIterations: kdfConfig.iterations, kdfType: KdfType.PBKDF2_SHA256, encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, data: encText.encryptedString, diff --git a/libs/common/src/services/keyConnector.service.ts b/libs/common/src/services/keyConnector.service.ts index 2430ba27c2f..07e9ad4e5b1 100644 --- a/libs/common/src/services/keyConnector.service.ts +++ b/libs/common/src/services/keyConnector.service.ts @@ -8,6 +8,7 @@ import { StateService } from "../abstractions/state.service"; import { TokenService } from "../abstractions/token.service"; import { OrganizationUserType } from "../enums/organizationUserType"; import { Utils } from "../misc/utils"; +import { KdfConfig } from "../models/domain/kdf-config"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { SetKeyConnectorKeyRequest } from "../models/request/account/set-key-connector-key.request"; import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request"; @@ -82,14 +83,14 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { } async convertNewSsoUserToKeyConnector(tokenResponse: IdentityTokenResponse, orgId: string) { - const { kdf, kdfIterations, keyConnectorUrl } = tokenResponse; + const { kdf, kdfIterations, kdfMemory, kdfParallelism, keyConnectorUrl } = tokenResponse; const password = await this.cryptoFunctionService.randomBytes(64); const k = await this.cryptoService.makeKey( Utils.fromBufferToB64(password), await this.tokenService.getEmail(), kdf, - kdfIterations + new KdfConfig(kdfIterations, kdfMemory, kdfParallelism) ); const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64); await this.cryptoService.setKey(k); diff --git a/libs/common/src/services/state.service.ts b/libs/common/src/services/state.service.ts index eeb97c6531c..1637913afe6 100644 --- a/libs/common/src/services/state.service.ts +++ b/libs/common/src/services/state.service.ts @@ -36,6 +36,7 @@ import { EncString } from "../models/domain/enc-string"; import { EnvironmentUrls } from "../models/domain/environment-urls"; import { GeneratedPasswordHistory } from "../models/domain/generated-password-history"; import { GlobalState } from "../models/domain/global-state"; +import { KdfConfig } from "../models/domain/kdf-config"; import { Policy } from "../models/domain/policy"; import { State } from "../models/domain/state"; import { StorageOptions } from "../models/domain/storage-options"; @@ -1657,17 +1658,26 @@ export class StateService< return (await this.getAccessToken(options)) != null && (await this.getUserId(options)) != null; } - async getKdfIterations(options?: StorageOptions): Promise { - return ( + 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 setKdfIterations(value: number, options?: StorageOptions): Promise { + async setKdfConfig(config: KdfConfig, options?: StorageOptions): Promise { const account = await this.getAccount( this.reconcileOptions(options, await this.defaultOnDiskOptions()) ); - account.profile.kdfIterations = value; + 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())
{{ "kdfIterationsDesc" | i18n: (recommendedKdfIterations | number) }}
+ {{ "kdfIterationsDesc" | i18n: (recommendedPbkdf2Iterations | number) }} +
{{ "argon2Desc" | i18n }}