1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

migrate biometrics key

- migrate only on retrieval
This commit is contained in:
Jacob Fink
2023-06-08 14:26:59 -04:00
parent 56c750d375
commit 9a12cb099a
7 changed files with 70 additions and 36 deletions

View File

@@ -404,7 +404,7 @@ export class SettingsComponent implements OnInit {
await this.cryptoService.toggleKey(); await this.cryptoService.toggleKey();
// Validate the key is stored in case biometrics fail. // 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); this.form.controls.biometric.setValue(biometricSet);
if (!biometricSet) { if (!biometricSet) {
await this.stateService.setBiometricUnlock(null); await this.stateService.setBiometricUnlock(null);

View File

@@ -4,7 +4,9 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { import {
MasterKey,
SymmetricCryptoKey, SymmetricCryptoKey,
UserSymKey, UserSymKey,
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
@@ -32,16 +34,32 @@ export class ElectronCryptoService extends CryptoService {
if (storeBiometricKey) { if (storeBiometricKey) {
await this.storeBiometricKey(key, userId); await this.storeBiometricKey(key, userId);
} else { } else {
await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); await this.stateService.setUserSymKeyBiometric(null, { userId: userId });
} }
} }
protected async storeBiometricKey(key: SymmetricCryptoKey, userId?: string): Promise<void> { protected override async retrieveUserKeyFromStorage(
keySuffix: KeySuffixOptions,
userId?: string
): Promise<UserSymKey> {
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<void> {
let clientEncKeyHalf: CsprngString = null; let clientEncKeyHalf: CsprngString = null;
if (await this.stateService.getBiometricRequirePasswordOnStart({ userId })) { if (await this.stateService.getBiometricRequirePasswordOnStart({ userId })) {
clientEncKeyHalf = await this.getBiometricEncryptionClientKeyHalf(userId); clientEncKeyHalf = await this.getBiometricEncryptionClientKeyHalf(userId);
} }
await this.stateService.setCryptoMasterKeyBiometric( await this.stateService.setUserSymKeyBiometric(
{ key: key.keyB64, clientEncKeyHalf }, { key: key.keyB64, clientEncKeyHalf },
{ userId: userId } { userId: userId }
); );
@@ -66,4 +84,21 @@ export class ElectronCryptoService extends CryptoService {
return null; 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 });
}
}
} }

View File

@@ -136,7 +136,7 @@ export class NativeMessagingService {
}); });
} }
const key = await this.cryptoService.getKeyFromStorage( const key = await this.cryptoService.getUserKeyFromStorage(
KeySuffixOptions.Biometric, KeySuffixOptions.Biometric,
message.userId message.userId
); );

View File

@@ -116,13 +116,13 @@ export class LockComponent implements OnInit, OnDestroy {
return; return;
} }
const success = (await this.cryptoService.getKey(KeySuffixOptions.Biometric)) != null; const userKey = await this.cryptoService.getUserKeyFromStorage(KeySuffixOptions.Biometric);
if (success) { if (userKey) {
await this.doContinue(false); await this.setKeyAndContinue(userKey, false);
} }
return success; return !!userKey;
} }
togglePassword() { togglePassword() {
@@ -337,7 +337,7 @@ export class LockComponent implements OnInit, OnDestroy {
this.supportsBiometric = await this.platformUtilsService.supportsBiometric(); this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
this.biometricLock = this.biometricLock =
(await this.vaultTimeoutSettingsService.isBiometricLockSet()) && (await this.vaultTimeoutSettingsService.isBiometricLockSet()) &&
((await this.cryptoService.hasKeyStored(KeySuffixOptions.Biometric)) || ((await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric)) ||
!this.platformUtilsService.supportsSecureStorage()); !this.platformUtilsService.supportsSecureStorage());
this.biometricText = await this.stateService.getBiometricText(); this.biometricText = await this.stateService.getBiometricText();
this.email = await this.stateService.getEmail(); this.email = await this.stateService.getEmail();

View File

@@ -124,12 +124,27 @@ export abstract class StateService<T extends Account = Account> {
) => Promise<void>; ) => Promise<void>;
getCryptoMasterKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>; getCryptoMasterKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise<void>; setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise<void>;
/**
* @deprecated For migration purposes only, use getUserSymKeyAuto instead
*/
getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise<string>; getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise<string>;
/**
* @deprecated For migration purposes only, use setUserSymKeyAuto instead
*/
setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise<void>; setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise<void>;
getCryptoMasterKeyB64: (options?: StorageOptions) => Promise<string>; getCryptoMasterKeyB64: (options?: StorageOptions) => Promise<string>;
setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise<void>; setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise<void>;
/**
* @deprecated For migration purposes only, use getUserSymKeyBiometric instead
*/
getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<string>; getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<string>;
/**
* @deprecated For migration purposes only, use hasUserSymKeyBiometric instead
*/
hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<boolean>; hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<boolean>;
/**
* @deprecated For migration purposes only, use setUserSymKeyBiometric instead
*/
setCryptoMasterKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise<void>; setCryptoMasterKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise<void>;
// end deprecated keys // end deprecated keys

View File

@@ -136,14 +136,11 @@ export class CryptoService implements CryptoServiceAbstraction {
keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric, keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric,
userId?: string userId?: string
): Promise<boolean> { ): Promise<boolean> {
switch (keySuffix) { if (keySuffix === KeySuffixOptions.Biometric) {
case KeySuffixOptions.Auto: const oldKey = await this.stateService.hasCryptoMasterKeyBiometric({ userId: userId });
return (await this.retrieveUserKeyFromStorage(keySuffix, userId)) != null; return oldKey || (await this.stateService.hasUserSymKeyBiometric({ userId: userId }));
case KeySuffixOptions.Biometric:
return (await this.stateService.hasUserSymKeyBiometric({ userId: userId })) === true;
default:
return false;
} }
return (await this.retrieveUserKeyFromStorage(keySuffix, userId)) != null;
} }
/** /**
@@ -947,23 +944,16 @@ export class CryptoService implements CryptoServiceAbstraction {
} }
protected async retrieveUserKeyFromStorage( protected async retrieveUserKeyFromStorage(
keySuffix: KeySuffixOptions.Auto | KeySuffixOptions.Biometric, keySuffix: KeySuffixOptions,
userId?: string userId?: string
): Promise<UserSymKey> { ): Promise<UserSymKey> {
let userKey: string; if (keySuffix === KeySuffixOptions.Pin) {
switch (keySuffix) {
case KeySuffixOptions.Auto: {
await this.migrateAutoKeyIfNeeded(userId); await this.migrateAutoKeyIfNeeded(userId);
userKey = await this.stateService.getUserSymKeyAuto({ userId: userId }); const userKey = await this.stateService.getUserSymKeyAuto({ userId: userId });
break;
}
case KeySuffixOptions.Biometric: {
userKey = await this.stateService.getUserSymKeyBiometric({ userId: userId });
break;
}
}
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) { private async migrateAutoKeyIfNeeded(userId?: string) {
const oldAutoKey = await this.stateService.getCryptoMasterKeyAuto({ userId: userId }); const oldAutoKey = await this.stateService.getCryptoMasterKeyAuto({ userId: userId });

View File

@@ -689,9 +689,6 @@ export class StateService<
); );
} }
/**
* User's encrypted symmetric key when using biometrics
*/
async hasUserSymKeyBiometric(options?: StorageOptions): Promise<boolean> { async hasUserSymKeyBiometric(options?: StorageOptions): Promise<boolean> {
options = this.reconcileOptions( options = this.reconcileOptions(
this.reconcileOptions(options, { keySuffix: "biometric" }), 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<void> { async setUserSymKeyBiometric(value: BiometricKey, options?: StorageOptions): Promise<void> {
options = this.reconcileOptions( options = this.reconcileOptions(
this.reconcileOptions(options, { keySuffix: "biometric" }), this.reconcileOptions(options, { keySuffix: "biometric" }),