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:
@@ -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);
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -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" }),
|
||||||
|
|||||||
Reference in New Issue
Block a user