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 { Argon2KdfConfig, DEFAULT_KDF_CONFIG, KdfConfig, PBKDF2KdfConfig, } from "@bitwarden/common/auth/models/domain/kdf-config"; import { KdfType } from "@bitwarden/common/platform/enums"; import { DialogService } from "@bitwarden/components"; import { ChangeKdfConfirmationComponent } from "./change-kdf-confirmation.component"; @Component({ selector: "app-change-kdf", templateUrl: "change-kdf.component.html", }) export class ChangeKdfComponent implements OnInit { kdfConfig: KdfConfig = DEFAULT_KDF_CONFIG; kdfOptions: any[] = []; private destroy$ = new Subject(); protected formGroup = this.formBuilder.group({ kdf: new FormControl(KdfType.PBKDF2_SHA256, [Validators.required]), kdfConfig: this.formBuilder.group({ iterations: [this.kdfConfig.iterations], memory: [null as number], parallelism: [null as number], }), }); // Default values for template protected PBKDF2_ITERATIONS = PBKDF2KdfConfig.ITERATIONS; protected ARGON2_ITERATIONS = Argon2KdfConfig.ITERATIONS; protected ARGON2_MEMORY = Argon2KdfConfig.MEMORY; protected ARGON2_PARALLELISM = Argon2KdfConfig.PARALLELISM; constructor( private dialogService: DialogService, private kdfConfigService: KdfConfigService, private formBuilder: FormBuilder, ) { this.kdfOptions = [ { name: "PBKDF2 SHA-256", value: KdfType.PBKDF2_SHA256 }, { name: "Argon2id", value: KdfType.Argon2id }, ]; } async ngOnInit() { this.kdfConfig = await this.kdfConfigService.getKdfConfig(); this.formGroup.get("kdf").setValue(this.kdfConfig.kdfType); 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(PBKDF2KdfConfig.ITERATIONS.min), Validators.max(PBKDF2KdfConfig.ITERATIONS.max), ]; break; case KdfType.Argon2id: config = new Argon2KdfConfig(); validators.iterations = [ Validators.required, Validators.min(Argon2KdfConfig.ITERATIONS.min), Validators.max(Argon2KdfConfig.ITERATIONS.max), ]; validators.memory = [ Validators.required, Validators.min(Argon2KdfConfig.MEMORY.min), Validators.max(Argon2KdfConfig.MEMORY.max), ]; validators.parallelism = [ Validators.required, Validators.min(Argon2KdfConfig.PARALLELISM.min), Validators.max(Argon2KdfConfig.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 { return t instanceof PBKDF2KdfConfig; } isArgon2(t: KdfConfig): t is Argon2KdfConfig { return t instanceof Argon2KdfConfig; } 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, { data: { kdfConfig: this.kdfConfig, }, }); } }