From 9a12cb099a89d2e2e4488f9645c743139d243f32 Mon Sep 17 00:00:00 2001 From: Jacob Fink Date: Thu, 8 Jun 2023 14:26:59 -0400 Subject: [PATCH] migrate biometrics key - migrate only on retrieval --- .../src/app/accounts/settings.component.ts | 2 +- .../services/electron-crypto.service.ts | 41 +++++++++++++++++-- .../src/services/native-messaging.service.ts | 2 +- .../src/auth/components/lock.component.ts | 10 ++--- .../platform/abstractions/state.service.ts | 15 +++++++ .../src/platform/services/crypto.service.ts | 30 +++++--------- .../src/platform/services/state.service.ts | 6 --- 7 files changed, 70 insertions(+), 36 deletions(-) diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index 162cb338bc..5631fb500e 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -404,7 +404,7 @@ export class SettingsComponent implements OnInit { await this.cryptoService.toggleKey(); // Validate the key is stored in case biometrics fail. - const biometricSet = await this.cryptoService.hasKeyStored(KeySuffixOptions.Biometric); + const biometricSet = await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric); this.form.controls.biometric.setValue(biometricSet); if (!biometricSet) { await this.stateService.setBiometricUnlock(null); diff --git a/apps/desktop/src/platform/services/electron-crypto.service.ts b/apps/desktop/src/platform/services/electron-crypto.service.ts index 6e49874f51..e97087fd44 100644 --- a/apps/desktop/src/platform/services/electron-crypto.service.ts +++ b/apps/desktop/src/platform/services/electron-crypto.service.ts @@ -4,7 +4,9 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt. import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { + MasterKey, SymmetricCryptoKey, UserSymKey, } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; @@ -32,16 +34,32 @@ export class ElectronCryptoService extends CryptoService { if (storeBiometricKey) { await this.storeBiometricKey(key, userId); } else { - await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); + await this.stateService.setUserSymKeyBiometric(null, { userId: userId }); } } - protected async storeBiometricKey(key: SymmetricCryptoKey, userId?: string): Promise { + protected override async retrieveUserKeyFromStorage( + keySuffix: KeySuffixOptions, + userId?: string + ): Promise { + const userKey = super.retrieveUserKeyFromStorage(keySuffix, userId); + if (userKey) { + return userKey; + } + if (keySuffix === KeySuffixOptions.Biometric) { + await this.migrateBiometricKeyIfNeeded(userId); + const userKey = await this.stateService.getUserSymKeyBiometric({ userId: userId }); + return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey).buffer) as UserSymKey; + } + return null; + } + + protected async storeBiometricKey(key: UserSymKey, userId?: string): Promise { let clientEncKeyHalf: CsprngString = null; if (await this.stateService.getBiometricRequirePasswordOnStart({ userId })) { clientEncKeyHalf = await this.getBiometricEncryptionClientKeyHalf(userId); } - await this.stateService.setCryptoMasterKeyBiometric( + await this.stateService.setUserSymKeyBiometric( { key: key.keyB64, clientEncKeyHalf }, { userId: userId } ); @@ -66,4 +84,21 @@ export class ElectronCryptoService extends CryptoService { return null; } } + + private async migrateBiometricKeyIfNeeded(userId?: string) { + const oldBiometricKey = await this.stateService.getCryptoMasterKeyBiometric({ userId }); + if (oldBiometricKey) { + // decrypt + const masterKey = new SymmetricCryptoKey( + Utils.fromB64ToArray(oldBiometricKey).buffer + ) as MasterKey; + const userSymKey = await this.decryptUserSymKeyWithMasterKey( + masterKey, + new EncString(await this.stateService.getEncryptedCryptoSymmetricKey()) + ); + // migrate + await this.storeBiometricKey(userSymKey, userId); + await this.stateService.setCryptoMasterKeyBiometric(null, { userId }); + } + } } diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 494a65b670..c566dcb706 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -136,7 +136,7 @@ export class NativeMessagingService { }); } - const key = await this.cryptoService.getKeyFromStorage( + const key = await this.cryptoService.getUserKeyFromStorage( KeySuffixOptions.Biometric, message.userId ); diff --git a/libs/angular/src/auth/components/lock.component.ts b/libs/angular/src/auth/components/lock.component.ts index 684c6c1ff8..a0610a7ff5 100644 --- a/libs/angular/src/auth/components/lock.component.ts +++ b/libs/angular/src/auth/components/lock.component.ts @@ -116,13 +116,13 @@ export class LockComponent implements OnInit, OnDestroy { return; } - const success = (await this.cryptoService.getKey(KeySuffixOptions.Biometric)) != null; + const userKey = await this.cryptoService.getUserKeyFromStorage(KeySuffixOptions.Biometric); - if (success) { - await this.doContinue(false); + if (userKey) { + await this.setKeyAndContinue(userKey, false); } - return success; + return !!userKey; } togglePassword() { @@ -337,7 +337,7 @@ export class LockComponent implements OnInit, OnDestroy { this.supportsBiometric = await this.platformUtilsService.supportsBiometric(); this.biometricLock = (await this.vaultTimeoutSettingsService.isBiometricLockSet()) && - ((await this.cryptoService.hasKeyStored(KeySuffixOptions.Biometric)) || + ((await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric)) || !this.platformUtilsService.supportsSecureStorage()); this.biometricText = await this.stateService.getBiometricText(); this.email = await this.stateService.getEmail(); diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 465a14f07d..e36190f12c 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -124,12 +124,27 @@ export abstract class StateService { ) => Promise; getCryptoMasterKey: (options?: StorageOptions) => Promise; setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; + /** + * @deprecated For migration purposes only, use getUserSymKeyAuto instead + */ getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise; + /** + * @deprecated For migration purposes only, use setUserSymKeyAuto instead + */ setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise; getCryptoMasterKeyB64: (options?: StorageOptions) => Promise; setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise; + /** + * @deprecated For migration purposes only, use getUserSymKeyBiometric instead + */ getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; + /** + * @deprecated For migration purposes only, use hasUserSymKeyBiometric instead + */ hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; + /** + * @deprecated For migration purposes only, use setUserSymKeyBiometric instead + */ setCryptoMasterKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise; // end deprecated keys diff --git a/libs/common/src/platform/services/crypto.service.ts b/libs/common/src/platform/services/crypto.service.ts index 6a72409594..efe9615019 100644 --- a/libs/common/src/platform/services/crypto.service.ts +++ b/libs/common/src/platform/services/crypto.service.ts @@ -136,14 +136,11 @@ export class CryptoService implements CryptoServiceAbstraction { keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric, userId?: string ): Promise { - switch (keySuffix) { - case KeySuffixOptions.Auto: - return (await this.retrieveUserKeyFromStorage(keySuffix, userId)) != null; - case KeySuffixOptions.Biometric: - return (await this.stateService.hasUserSymKeyBiometric({ userId: userId })) === true; - default: - return false; + if (keySuffix === KeySuffixOptions.Biometric) { + const oldKey = await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId }); + return oldKey || (await this.stateService.hasUserSymKeyBiometric({ userId: userId })); } + return (await this.retrieveUserKeyFromStorage(keySuffix, userId)) != null; } /** @@ -947,22 +944,15 @@ export class CryptoService implements CryptoServiceAbstraction { } protected async retrieveUserKeyFromStorage( - keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric, + keySuffix: KeySuffixOptions, userId?: string ): Promise { - let userKey: string; - switch (keySuffix) { - case KeySuffixOptions.Auto: { - await this.migrateAutoKeyIfNeeded(userId); - userKey = await this.stateService.getUserSymKeyAuto({ userId: userId }); - break; - } - case KeySuffixOptions.Biometric: { - userKey = await this.stateService.getUserSymKeyBiometric({ userId: userId }); - break; - } + if (keySuffix === KeySuffixOptions.Pin) { + await this.migrateAutoKeyIfNeeded(userId); + const userKey = await this.stateService.getUserSymKeyAuto({ userId: userId }); + return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey).buffer) as UserSymKey; } - return new SymmetricCryptoKey(Utils.fromB64ToArray(userKey).buffer) as UserSymKey; + return null; } private async migrateAutoKeyIfNeeded(userId?: string) { diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index 0d535e9d6d..f729c1ea7d 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -689,9 +689,6 @@ export class StateService< ); } - /** - * User's encrypted symmetric key when using biometrics - */ async hasUserSymKeyBiometric(options?: StorageOptions): Promise { options = this.reconcileOptions( this.reconcileOptions(options, { keySuffix: "biometric" }), @@ -706,9 +703,6 @@ export class StateService< ); } - /** - * User's encrypted symmetric key when using biometrics - */ async setUserSymKeyBiometric(value: BiometricKey, options?: StorageOptions): Promise { options = this.reconcileOptions( this.reconcileOptions(options, { keySuffix: "biometric" }),