diff --git a/angular/src/components/export.component.ts b/angular/src/components/export.component.ts index 5ea15fb358..2f82d7e380 100644 --- a/angular/src/components/export.component.ts +++ b/angular/src/components/export.component.ts @@ -42,9 +42,8 @@ export class ExportComponent { return; } - const keyHash = await this.cryptoService.hashPassword(this.masterPassword, null, HashPurpose.LocalAuthorization); - const storedKeyHash = await this.cryptoService.getKeyHash(); - if (storedKeyHash != null && keyHash != null && storedKeyHash === keyHash) { + const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null); + if (passwordValid) { try { this.formPromise = this.getExportData(); const data = await this.formPromise; diff --git a/angular/src/components/lock.component.ts b/angular/src/components/lock.component.ts index 597bbca965..1afaf13a2b 100644 --- a/angular/src/components/lock.component.ts +++ b/angular/src/components/lock.component.ts @@ -112,27 +112,25 @@ export class LockComponent implements OnInit { } } else { const key = await this.cryptoService.makeKey(this.masterPassword, this.email, kdf, kdfIterations); - const localKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key, - HashPurpose.LocalAuthorization); + const storedKeyHash = await this.cryptoService.getKeyHash(); let passwordValid = false; - if (localKeyHash != null) { - const storedKeyHash = await this.cryptoService.getKeyHash(); - if (storedKeyHash != null) { - passwordValid = storedKeyHash === localKeyHash; - } else { - const request = new PasswordVerificationRequest(); - const serverKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key, - HashPurpose.ServerAuthorization); - request.masterPasswordHash = serverKeyHash; - try { - this.formPromise = this.apiService.postAccountVerifyPassword(request); - await this.formPromise; - passwordValid = true; - await this.cryptoService.setKeyHash(localKeyHash); - } catch { } - } + if (storedKeyHash != null) { + passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, key); + } else { + const request = new PasswordVerificationRequest(); + const serverKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key, + HashPurpose.ServerAuthorization); + request.masterPasswordHash = serverKeyHash; + try { + this.formPromise = this.apiService.postAccountVerifyPassword(request); + await this.formPromise; + passwordValid = true; + const localKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key, + HashPurpose.LocalAuthorization); + await this.cryptoService.setKeyHash(localKeyHash); + } catch { } } if (passwordValid) { diff --git a/common/src/abstractions/crypto.service.ts b/common/src/abstractions/crypto.service.ts index f26479fe10..17a8294a21 100644 --- a/common/src/abstractions/crypto.service.ts +++ b/common/src/abstractions/crypto.service.ts @@ -17,6 +17,7 @@ export abstract class CryptoService { getKey: (keySuffix?: KeySuffixOptions) => Promise; getKeyFromStorage: (keySuffix: KeySuffixOptions) => Promise; getKeyHash: () => Promise; + compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise; getEncKey: (key?: SymmetricCryptoKey) => Promise; getPublicKey: () => Promise; getPrivateKey: () => Promise; diff --git a/common/src/services/crypto.service.ts b/common/src/services/crypto.service.ts index 23330c943d..7e106d69c2 100644 --- a/common/src/services/crypto.service.ts +++ b/common/src/services/crypto.service.ts @@ -141,6 +141,25 @@ export class CryptoService implements CryptoServiceAbstraction { return keyHash == null ? null : this.keyHash; } + async compareAndUpdateKeyHash(masterPassword: string, key: SymmetricCryptoKey): Promise { + const storedKeyHash = await this.getKeyHash(); + if (masterPassword != null && storedKeyHash != null) { + const localKeyHash = await this.hashPassword(masterPassword, key, HashPurpose.LocalAuthorization); + if (localKeyHash != null && storedKeyHash === localKeyHash) { + return true; + } + + // TODO: remove serverKeyHash check in 1-2 releases after everyone's keyHash has been updated + const serverKeyHash = await this.hashPassword(masterPassword, key, HashPurpose.ServerAuthorization); + if (serverKeyHash != null && storedKeyHash === serverKeyHash) { + await this.setKeyHash(localKeyHash); + return true; + } + } + + return false; + } + @sequentialize(() => 'getEncKey') async getEncKey(key: SymmetricCryptoKey = null): Promise { if (this.encKey != null) { diff --git a/common/src/services/passwordReprompt.service.ts b/common/src/services/passwordReprompt.service.ts index d5955f79f0..ae3e1a5120 100644 --- a/common/src/services/passwordReprompt.service.ts +++ b/common/src/services/passwordReprompt.service.ts @@ -15,14 +15,8 @@ export class PasswordRepromptService implements PasswordRepromptServiceAbstracti } async showPasswordPrompt() { - const passwordValidator = async (value: string) => { - const keyHash = await this.cryptoService.hashPassword(value, null, HashPurpose.LocalAuthorization); - const storedKeyHash = await this.cryptoService.getKeyHash(); - - if (storedKeyHash == null || keyHash == null || storedKeyHash !== keyHash) { - return false; - } - return true; + const passwordValidator = (value: string) => { + return this.cryptoService.compareAndUpdateKeyHash(value, null); }; return this.platformUtilService.showPasswordDialog(this.i18nService.t('passwordConfirmation'), this.i18nService.t('passwordConfirmationDesc'), passwordValidator);