From e3cd3cdd51ce0315efd3b479b15c2c827b27217c Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 5 Feb 2025 10:52:32 +0100 Subject: [PATCH] Properly split old and new code --- .../settings/change-password.component.ts | 150 ++++++++++++++++-- 1 file changed, 137 insertions(+), 13 deletions(-) diff --git a/apps/web/src/app/auth/settings/change-password.component.ts b/apps/web/src/app/auth/settings/change-password.component.ts index 241361289ea..ee1232010aa 100644 --- a/apps/web/src/app/auth/settings/change-password.component.ts +++ b/apps/web/src/app/auth/settings/change-password.component.ts @@ -12,11 +12,16 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { HashPurpose } from "@bitwarden/common/platform/enums"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { UserId } from "@bitwarden/common/types/guid"; +import { MasterKey, UserKey } from "@bitwarden/common/types/key"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; @@ -137,6 +142,23 @@ export class ChangePasswordComponent } async submit() { + if (this.userkeyRotationV2) { + await this.submitNew(); + } else { + await this.submitOld(); + } + } + + async submitNew() { + if (this.currentMasterPassword == null || this.currentMasterPassword === "") { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("masterPasswordRequired"), + }); + return false; + } + this.loading = true; if ( this.masterPasswordHint != null && @@ -165,19 +187,12 @@ export class ChangePasswordComponent if (this.rotateUserKey) { await this.syncService.fullSync(true); const user = await firstValueFrom(this.accountService.activeAccount$); - if (this.userkeyRotationV2) { - await this.keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( - this.currentMasterPassword, - this.masterPassword, - user, - this.masterPasswordHint, - ); - } else { - await this.keyRotationService.rotateUserKeyAndEncryptedDataLegacy( - this.currentMasterPassword, - user, - ); - } + await this.keyRotationService.rotateUserKeyMasterPasswordAndEncryptedData( + this.currentMasterPassword, + this.masterPassword, + user, + this.masterPasswordHint, + ); } else { await this.updatePassword(this.masterPassword); } @@ -251,4 +266,113 @@ export class ChangePasswordComponent }); } } + + async submitOld() { + if ( + this.masterPasswordHint != null && + this.masterPasswordHint.toLowerCase() === this.masterPassword.toLowerCase() + ) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("hintEqualsPassword"), + }); + return; + } + + this.leakedPassword = false; + if (this.checkForBreaches) { + this.leakedPassword = (await this.auditService.passwordLeaked(this.masterPassword)) > 0; + } + + await super.submit(); + } + + async setupSubmitActions() { + if (this.currentMasterPassword == null || this.currentMasterPassword === "") { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("masterPasswordRequired"), + }); + return false; + } + + if (this.rotateUserKey) { + await this.syncService.fullSync(true); + } + + return super.setupSubmitActions(); + } + + async performSubmitActions( + newMasterPasswordHash: string, + newMasterKey: MasterKey, + newUserKey: [UserKey, EncString], + ) { + const masterKey = await this.keyService.makeMasterKey( + this.currentMasterPassword, + await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))), + await this.kdfConfigService.getKdfConfig(), + ); + + const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); + const newLocalKeyHash = await this.keyService.hashMasterKey( + this.masterPassword, + newMasterKey, + HashPurpose.LocalAuthorization, + ); + + const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey(masterKey, userId); + if (userKey == null) { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("invalidMasterPassword"), + }); + return; + } + + const request = new PasswordRequest(); + request.masterPasswordHash = await this.keyService.hashMasterKey( + this.currentMasterPassword, + masterKey, + ); + request.masterPasswordHint = this.masterPasswordHint; + request.newMasterPasswordHash = newMasterPasswordHash; + request.key = newUserKey[1].encryptedString; + + try { + if (this.rotateUserKey) { + this.formPromise = this.apiService.postPassword(request).then(async () => { + // we need to save this for local masterkey verification during rotation + await this.masterPasswordService.setMasterKeyHash(newLocalKeyHash, userId as UserId); + await this.masterPasswordService.setMasterKey(newMasterKey, userId as UserId); + return this.updateKey(); + }); + } else { + this.formPromise = this.apiService.postPassword(request); + } + + await this.formPromise; + + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("masterPasswordChanged"), + message: this.i18nService.t("logBackIn"), + }); + this.messagingService.send("logout"); + } catch { + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("errorOccurred"), + }); + } + } + + private async updateKey() { + const user = await firstValueFrom(this.accountService.activeAccount$); + await this.keyRotationService.rotateUserKeyAndEncryptedDataLegacy(this.masterPassword, user); + } }