mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-29 12:55:21 +01:00
[PM 4973] migrate change kdf component (#8485)
* chnage kdf component migration * chnage kdf component migration * change kdf component migration * migrating change-kdf component * migrating change-kdf component * migrating change-kdf component --------- Co-authored-by: Todd Martin <tmartin@bitwarden.com>
This commit is contained in:
parent
dfd4479a9c
commit
97002c8852
@ -1,117 +1,110 @@
|
|||||||
<div class="tabbed-header">
|
<h2 bitTypography="h2">{{ "encKeySettings" | i18n }}</h2>
|
||||||
<h1>{{ "encKeySettings" | i18n }}</h1>
|
|
||||||
</div>
|
|
||||||
<bit-callout type="warning">{{ "kdfSettingsChangeLogoutWarning" | i18n }}</bit-callout>
|
<bit-callout type="warning">{{ "kdfSettingsChangeLogoutWarning" | i18n }}</bit-callout>
|
||||||
<form #form ngNativeValidate autocomplete="off">
|
<p bitTypography="body1">
|
||||||
<div class="row">
|
{{ "higherKDFIterations" | i18n }}
|
||||||
<div class="col-6">
|
</p>
|
||||||
<div class="form-group mb-0">
|
<p bitTypography="body1">
|
||||||
<label for="kdf">{{ "kdfAlgorithm" | i18n }}</label>
|
{{
|
||||||
<a
|
"kdfToHighWarningIncreaseInIncrements"
|
||||||
class="ml-auto"
|
| i18n: (isPBKDF2(kdfConfig) ? ("incrementsOf100,000" | i18n) : ("smallIncrements" | i18n))
|
||||||
href="https://bitwarden.com/help/kdf-algorithms"
|
}}
|
||||||
target="_blank"
|
</p>
|
||||||
rel="noreferrer"
|
<form [formGroup]="formGroup" autocomplete="off">
|
||||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||||
>
|
<div class="tw-col-span-6">
|
||||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
<bit-form-field>
|
||||||
</a>
|
<bit-label
|
||||||
<select
|
>{{ "kdfAlgorithm" | i18n }}
|
||||||
id="kdf"
|
|
||||||
name="Kdf"
|
|
||||||
[(ngModel)]="kdfConfig.kdfType"
|
|
||||||
(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="isArgon2(kdfConfig)">
|
|
||||||
<label for="kdfMemory">{{ "kdfMemory" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="kdfMemory"
|
|
||||||
type="number"
|
|
||||||
[min]="ARGON2_MEMORY.min"
|
|
||||||
[max]="ARGON2_MEMORY.max"
|
|
||||||
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">
|
|
||||||
<ng-container *ngIf="isPBKDF2(kdfConfig)">
|
|
||||||
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
|
||||||
<a
|
<a
|
||||||
class="ml-auto"
|
class="tw-ml-auto"
|
||||||
href="https://bitwarden.com/help/what-encryption-is-used/#changing-kdf-iterations"
|
bitLink
|
||||||
|
href="https://bitwarden.com/help/kdf-algorithms"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
appA11yTitle="{{ 'learnMore' | i18n }}"
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
>
|
>
|
||||||
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
|
</bit-label>
|
||||||
|
<bit-select formControlName="kdf">
|
||||||
|
<bit-option
|
||||||
|
*ngFor="let option of kdfOptions"
|
||||||
|
[value]="option.value"
|
||||||
|
[label]="option.name"
|
||||||
|
></bit-option>
|
||||||
|
</bit-select>
|
||||||
|
</bit-form-field>
|
||||||
|
<bit-form-field formGroupName="kdfConfig" *ngIf="isArgon2(kdfConfig)">
|
||||||
|
<bit-label>{{ "kdfMemory" | i18n }}</bit-label>
|
||||||
|
<input
|
||||||
|
bitInput
|
||||||
|
formControlName="memory"
|
||||||
|
type="number"
|
||||||
|
[min]="ARGON2_MEMORY.min"
|
||||||
|
[max]="ARGON2_MEMORY.max"
|
||||||
|
/>
|
||||||
|
</bit-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="tw-col-span-6">
|
||||||
|
<div class="tw-mb-0">
|
||||||
|
<bit-form-field formGroupName="kdfConfig" *ngIf="isPBKDF2(kdfConfig)">
|
||||||
|
<bit-label>
|
||||||
|
{{ "kdfIterations" | i18n }}
|
||||||
|
<a
|
||||||
|
bitLink
|
||||||
|
class="tw-ml-auto"
|
||||||
|
href="https://bitwarden.com/help/what-encryption-is-used/#changing-kdf-iterations"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
appA11yTitle="{{ 'learnMore' | i18n }}"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</bit-label>
|
||||||
<input
|
<input
|
||||||
id="kdfIterations"
|
bitInput
|
||||||
type="number"
|
type="number"
|
||||||
|
formControlName="iterations"
|
||||||
[min]="PBKDF2_ITERATIONS.min"
|
[min]="PBKDF2_ITERATIONS.min"
|
||||||
[max]="PBKDF2_ITERATIONS.max"
|
[max]="PBKDF2_ITERATIONS.max"
|
||||||
name="KdfIterations"
|
|
||||||
class="form-control"
|
|
||||||
[(ngModel)]="kdfConfig.iterations"
|
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
</ng-container>
|
<bit-hint>{{ "kdfIterationRecommends" | i18n }}</bit-hint>
|
||||||
|
</bit-form-field>
|
||||||
<ng-container *ngIf="isArgon2(kdfConfig)">
|
<ng-container *ngIf="isArgon2(kdfConfig)">
|
||||||
<label for="kdfIterations">{{ "kdfIterations" | i18n }}</label>
|
<bit-form-field formGroupName="kdfConfig">
|
||||||
<input
|
<bit-label>
|
||||||
id="iterations"
|
{{ "kdfIterations" | i18n }}
|
||||||
type="number"
|
</bit-label>
|
||||||
[min]="ARGON2_ITERATIONS.min"
|
<input
|
||||||
[max]="ARGON2_ITERATIONS.max"
|
bitInput
|
||||||
name="Iterations"
|
type="number"
|
||||||
class="form-control mb-3"
|
formControlName="iterations"
|
||||||
[(ngModel)]="kdfConfig.iterations"
|
[min]="ARGON2_ITERATIONS.min"
|
||||||
required
|
[max]="ARGON2_ITERATIONS.max"
|
||||||
/>
|
/>
|
||||||
<label for="kdfParallelism">{{ "kdfParallelism" | i18n }}</label>
|
</bit-form-field>
|
||||||
<input
|
<bit-form-field formGroupName="kdfConfig">
|
||||||
id="kdfParallelism"
|
<bit-label>
|
||||||
type="number"
|
{{ "kdfParallelism" | i18n }}
|
||||||
[min]="ARGON2_PARALLELISM.min"
|
</bit-label>
|
||||||
[max]="ARGON2_PARALLELISM.max"
|
<input
|
||||||
name="Parallelism"
|
bitInput
|
||||||
class="form-control"
|
type="number"
|
||||||
[(ngModel)]="kdfConfig.parallelism"
|
formControlName="parallelism"
|
||||||
required
|
[min]="ARGON2_PARALLELISM.min"
|
||||||
/>
|
[max]="ARGON2_PARALLELISM.max"
|
||||||
|
/>
|
||||||
|
</bit-form-field>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
|
||||||
<ng-container *ngIf="isPBKDF2(kdfConfig)">
|
|
||||||
<p class="small form-text text-muted">
|
|
||||||
{{ "kdfIterationsDesc" | i18n: (PBKDF2_ITERATIONS.defaultValue | number) }}
|
|
||||||
</p>
|
|
||||||
<bit-callout type="warning">
|
|
||||||
{{ "kdfIterationsWarning" | i18n: (100000 | number) }}
|
|
||||||
</bit-callout>
|
|
||||||
</ng-container>
|
|
||||||
<ng-container *ngIf="isArgon2(kdfConfig)">
|
|
||||||
<p class="small form-text text-muted">{{ "argon2Desc" | i18n }}</p>
|
|
||||||
<bit-callout type="warning"> {{ "argon2Warning" | i18n }}</bit-callout>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
(click)="openConfirmationModal()"
|
(click)="openConfirmationModal()"
|
||||||
type="button"
|
type="button"
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
bitButton
|
bitButton
|
||||||
[loading]="form.loading"
|
bitFormButton
|
||||||
>
|
>
|
||||||
{{ "changeKdf" | i18n }}
|
{{ "changeKdf" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { FormBuilder, FormControl, ValidatorFn, Validators } from "@angular/forms";
|
||||||
|
import { Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||||
import {
|
import {
|
||||||
@ -24,8 +26,34 @@ import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.compon
|
|||||||
})
|
})
|
||||||
export class ChangeKdfComponent implements OnInit {
|
export class ChangeKdfComponent implements OnInit {
|
||||||
kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG;
|
kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG;
|
||||||
kdfType = KdfType;
|
|
||||||
kdfOptions: any[] = [];
|
kdfOptions: any[] = [];
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
protected formGroup = this.formBuilder.group({
|
||||||
|
kdf: new FormControl(KdfType.PBKDF2_SHA256, [Validators.required]),
|
||||||
|
kdfConfig: this.formBuilder.group({
|
||||||
|
iterations: [
|
||||||
|
this.kdfConfig.iterations,
|
||||||
|
[
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(PBKDF2_ITERATIONS.min),
|
||||||
|
Validators.max(PBKDF2_ITERATIONS.max),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
memory: [
|
||||||
|
null as number,
|
||||||
|
[Validators.required, Validators.min(ARGON2_MEMORY.min), Validators.max(ARGON2_MEMORY.max)],
|
||||||
|
],
|
||||||
|
parallelism: [
|
||||||
|
null as number,
|
||||||
|
[
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(ARGON2_PARALLELISM.min),
|
||||||
|
Validators.max(ARGON2_PARALLELISM.max),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
// Default values for template
|
// Default values for template
|
||||||
protected PBKDF2_ITERATIONS = PBKDF2_ITERATIONS;
|
protected PBKDF2_ITERATIONS = PBKDF2_ITERATIONS;
|
||||||
@ -36,6 +64,7 @@ export class ChangeKdfComponent implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private kdfConfigService: KdfConfigService,
|
private kdfConfigService: KdfConfigService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
) {
|
) {
|
||||||
this.kdfOptions = [
|
this.kdfOptions = [
|
||||||
{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 },
|
{ name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 },
|
||||||
@ -45,6 +74,86 @@ export class ChangeKdfComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||||
|
this.formGroup.get("kdf").setValue(this.kdfConfig.kdfType, { emitEvent: false });
|
||||||
|
this.setFormControlValues(this.kdfConfig);
|
||||||
|
|
||||||
|
this.formGroup
|
||||||
|
.get("kdf")
|
||||||
|
.valueChanges.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe((newValue) => {
|
||||||
|
this.updateKdfConfig(newValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private updateKdfConfig(newValue: KdfType) {
|
||||||
|
let config: KdfConfig;
|
||||||
|
const validators: { [key: string]: ValidatorFn[] } = {
|
||||||
|
iterations: [],
|
||||||
|
memory: [],
|
||||||
|
parallelism: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (newValue) {
|
||||||
|
case KdfType.PBKDF2_SHA256:
|
||||||
|
config = new PBKDF2KdfConfig();
|
||||||
|
validators.iterations = [
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(PBKDF2_ITERATIONS.min),
|
||||||
|
Validators.max(PBKDF2_ITERATIONS.max),
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
case KdfType.Argon2id:
|
||||||
|
config = new Argon2KdfConfig();
|
||||||
|
validators.iterations = [
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(ARGON2_ITERATIONS.min),
|
||||||
|
Validators.max(ARGON2_ITERATIONS.max),
|
||||||
|
];
|
||||||
|
validators.memory = [
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(ARGON2_MEMORY.min),
|
||||||
|
Validators.max(ARGON2_MEMORY.max),
|
||||||
|
];
|
||||||
|
validators.parallelism = [
|
||||||
|
Validators.required,
|
||||||
|
Validators.min(ARGON2_PARALLELISM.min),
|
||||||
|
Validators.max(ARGON2_PARALLELISM.max),
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown KDF type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.kdfConfig = config;
|
||||||
|
this.setFormValidators(validators);
|
||||||
|
this.setFormControlValues(this.kdfConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setFormValidators(validators: { [key: string]: ValidatorFn[] }) {
|
||||||
|
this.setValidators("kdfConfig.iterations", validators.iterations);
|
||||||
|
this.setValidators("kdfConfig.memory", validators.memory);
|
||||||
|
this.setValidators("kdfConfig.parallelism", validators.parallelism);
|
||||||
|
}
|
||||||
|
private setValidators(controlName: string, validators: ValidatorFn[]) {
|
||||||
|
const control = this.formGroup.get(controlName);
|
||||||
|
if (control) {
|
||||||
|
control.setValidators(validators);
|
||||||
|
control.updateValueAndValidity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private setFormControlValues(kdfConfig: KdfConfig) {
|
||||||
|
this.formGroup.get("kdfConfig").reset();
|
||||||
|
if (kdfConfig.kdfType === KdfType.PBKDF2_SHA256) {
|
||||||
|
this.formGroup.get("kdfConfig.iterations").setValue(kdfConfig.iterations);
|
||||||
|
} else if (kdfConfig.kdfType === KdfType.Argon2id) {
|
||||||
|
this.formGroup.get("kdfConfig.iterations").setValue(kdfConfig.iterations);
|
||||||
|
this.formGroup.get("kdfConfig.memory").setValue(kdfConfig.memory);
|
||||||
|
this.formGroup.get("kdfConfig.parallelism").setValue(kdfConfig.parallelism);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
isPBKDF2(t: KdfConfig): t is PBKDF2KdfConfig {
|
isPBKDF2(t: KdfConfig): t is PBKDF2KdfConfig {
|
||||||
@ -55,17 +164,18 @@ export class ChangeKdfComponent implements OnInit {
|
|||||||
return t instanceof Argon2KdfConfig;
|
return t instanceof Argon2KdfConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onChangeKdf(newValue: KdfType) {
|
|
||||||
if (newValue === KdfType.PBKDF2_SHA256) {
|
|
||||||
this.kdfConfig = new PBKDF2KdfConfig();
|
|
||||||
} else if (newValue === KdfType.Argon2id) {
|
|
||||||
this.kdfConfig = new Argon2KdfConfig();
|
|
||||||
} else {
|
|
||||||
throw new Error("Unknown KDF type.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async openConfirmationModal() {
|
async openConfirmationModal() {
|
||||||
|
this.formGroup.markAllAsTouched();
|
||||||
|
if (this.formGroup.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.kdfConfig.kdfType === KdfType.PBKDF2_SHA256) {
|
||||||
|
this.kdfConfig.iterations = this.formGroup.get("kdfConfig.iterations").value;
|
||||||
|
} else if (this.kdfConfig.kdfType === KdfType.Argon2id) {
|
||||||
|
this.kdfConfig.iterations = this.formGroup.get("kdfConfig.iterations").value;
|
||||||
|
this.kdfConfig.memory = this.formGroup.get("kdfConfig.memory").value;
|
||||||
|
this.kdfConfig.parallelism = this.formGroup.get("kdfConfig.parallelism").value;
|
||||||
|
}
|
||||||
this.dialogService.open(ChangeKdfConfirmationComponent, {
|
this.dialogService.open(ChangeKdfConfirmationComponent, {
|
||||||
data: {
|
data: {
|
||||||
kdfConfig: this.kdfConfig,
|
kdfConfig: this.kdfConfig,
|
||||||
|
@ -8405,5 +8405,26 @@
|
|||||||
},
|
},
|
||||||
"memberAccessReportDesc": {
|
"memberAccessReportDesc": {
|
||||||
"message": "Ensure members have access to the right credentials and their accounts are secure. Use this report to obtain a CSV of member access and account configurations."
|
"message": "Ensure members have access to the right credentials and their accounts are secure. Use this report to obtain a CSV of member access and account configurations."
|
||||||
|
},
|
||||||
|
"higherKDFIterations": {
|
||||||
|
"message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker."
|
||||||
|
},
|
||||||
|
"incrementsOf100,000": {
|
||||||
|
"message": "increments of 100,000"
|
||||||
|
},
|
||||||
|
"smallIncrements": {
|
||||||
|
"message": "small increments"
|
||||||
|
},
|
||||||
|
"kdfIterationRecommends": {
|
||||||
|
"message": "We recommend 600,000 or more"
|
||||||
|
},
|
||||||
|
"kdfToHighWarningIncreaseInIncrements": {
|
||||||
|
"message": "For older devices, setting your KDF too high may lead to performance issues. Increase the value in $VALUE$ and test your devices.",
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"content": "$1",
|
||||||
|
"example":"increments of 100,000"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user