From d63ee1858d4770dfb8d640013cdcc2e8f25bec09 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Mon, 14 Jun 2021 14:35:58 -0700 Subject: [PATCH] Add backwards compatability for new local hashing method (#407) * Add backwards compatability for existing keyHash * Minor changes for review comments --- angular/src/components/export.component.ts | 5 ++- angular/src/components/lock.component.ts | 34 +++++++++---------- common/src/abstractions/crypto.service.ts | 1 + common/src/services/crypto.service.ts | 19 +++++++++++ .../src/services/passwordReprompt.service.ts | 10 ++---- 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/angular/src/components/export.component.ts b/angular/src/components/export.component.ts index 5ea15fb3585..2f82d7e3801 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 597bbca9650..1afaf13a2b2 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 f26479fe10e..17a8294a21a 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 23330c943d4..7e106d69c22 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 d5955f79f0c..ae3e1a5120f 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);