mirror of
https://github.com/bitwarden/browser
synced 2025-12-22 03:03:43 +00:00
[PS-2365] Kdf Configuration Options for Argon2 (#4578)
* Implement argon2 config * Remove argon2 webassembly warning * Replace magic numbers by enum * Implement kdf configuration * Update UI according to design feedback * Further updates to follow design feedback * Add oxford comma in argon2 description * Fix typos in argon2 descriptions * move key creation into promise with API call * change casing on PBKDF2 * general improvements * kdf config on set pin component * SHA-256 hash argon2 salt * Change argon2 defaults * Change argon2 salt hash to cryptoFunctionService * Fix isLowKdfIteration check --------- Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com> Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -32,43 +32,94 @@
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
<select id="kdf" name="Kdf" [(ngModel)]="kdf" class="form-control" required>
|
||||
<select
|
||||
id="kdf"
|
||||
name="Kdf"
|
||||
[(ngModel)]="kdf"
|
||||
(ngModelChange)="onChangeKdf($event)"
|
||||
class="form-control mb-3"
|
||||
required
|
||||
>
|
||||
<option *ngFor="let o of kdfOptions" [ngValue]="o.value">{{ o.name }}</option>
|
||||
</select>
|
||||
<ng-container *ngIf="kdf == kdfType.Argon2id">
|
||||
<label for="kdfMemory">{{ "kdfMemory" | i18n }}</label>
|
||||
<input
|
||||
id="kdfMemory"
|
||||
type="number"
|
||||
min="16"
|
||||
max="1024"
|
||||
name="Memory"
|
||||
class="form-control mb-3"
|
||||
[(ngModel)]="kdfConfig.memory"
|
||||
required
|
||||
/>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="form-group mb-0">
|
||||
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
||||
<a
|
||||
class="ml-auto"
|
||||
href="https://bitwarden.com/help/what-encryption-is-used/#pbkdf2"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
<input
|
||||
id="kdfIterations"
|
||||
type="number"
|
||||
min="5000"
|
||||
max="2000000"
|
||||
name="KdfIterations"
|
||||
class="form-control"
|
||||
[(ngModel)]="kdfIterations"
|
||||
required
|
||||
/>
|
||||
<ng-container *ngIf="kdf == kdfType.PBKDF2_SHA256">
|
||||
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
||||
<a
|
||||
class="ml-auto"
|
||||
href="https://bitwarden.com/help/kdf-algorithms"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||
</a>
|
||||
<input
|
||||
id="kdfIterations"
|
||||
type="number"
|
||||
min="5000"
|
||||
max="2000000"
|
||||
name="KdfIterations"
|
||||
class="form-control"
|
||||
[(ngModel)]="kdfConfig.iterations"
|
||||
required
|
||||
/>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="kdf == kdfType.Argon2id">
|
||||
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
||||
<input
|
||||
id="iterations"
|
||||
type="number"
|
||||
min="1"
|
||||
max="1024"
|
||||
name="Iterations"
|
||||
class="form-control mb-3"
|
||||
[(ngModel)]="kdfConfig.iterations"
|
||||
required
|
||||
/>
|
||||
<label for="kdfParallelism">{{ "kdfParallelism" | i18n }}</label>
|
||||
<input
|
||||
id="kdfParallelism"
|
||||
type="number"
|
||||
min="1"
|
||||
max="16"
|
||||
name="Parallelism"
|
||||
class="form-control"
|
||||
[(ngModel)]="kdfConfig.parallelism"
|
||||
required
|
||||
/>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="form-group">
|
||||
<div class="small form-text text-muted">
|
||||
<p>{{ "kdfIterationsDesc" | i18n: (recommendedKdfIterations | number) }}</p>
|
||||
<strong>{{ "warning" | i18n }}</strong
|
||||
>: {{ "kdfIterationsWarning" | i18n: (50000 | number) }}
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="kdf == kdfType.PBKDF2_SHA256">
|
||||
<p class="small form-text text-muted">
|
||||
{{ "kdfIterationsDesc" | i18n: (recommendedPbkdf2Iterations | number) }}
|
||||
</p>
|
||||
<bit-callout type="warning">
|
||||
{{ "kdfIterationsWarning" | i18n: (100000 | number) }}
|
||||
</bit-callout>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="kdf == kdfType.Argon2id">
|
||||
<p class="small form-text text-muted">{{ "argon2Desc" | i18n }}</p>
|
||||
<bit-callout type="warning"> {{ "argon2Warning" | i18n }}</bit-callout>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
||||
|
||||
@@ -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<any>;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user