1
0
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:
Bernd Schoolmann
2023-01-30 15:07:51 +01:00
committed by GitHub
parent b1a1068906
commit 01091fe260
35 changed files with 329 additions and 143 deletions

View File

@@ -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,

View File

@@ -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">

View File

@@ -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);
}
}

View File

@@ -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);