1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 08:43:33 +00:00

migrate pin to use user's symmetric key instead of master key

- set up new state
- migrate on lock component
- use new crypto service methods
This commit is contained in:
Jacob Fink
2023-06-06 12:48:56 -04:00
parent 91ac281da0
commit 7837202180
10 changed files with 255 additions and 57 deletions

View File

@@ -11,9 +11,10 @@ import { InternalPolicyService } from "@bitwarden/common/admin-console/abstracti
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { ForceResetPasswordReason } from "@bitwarden/common/auth/models/domain/force-reset-password-reason";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { MasterPasswordPolicyResponse } from "@bitwarden/common/auth/models/response/master-password-policy.response";
import { HashPurpose, KeySuffixOptions } from "@bitwarden/common/enums";
import { HashPurpose, KdfType, KeySuffixOptions } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -23,8 +24,8 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { UserSymKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { DialogServiceAbstraction, SimpleDialogType } from "../../services/dialog";
@@ -152,25 +153,42 @@ export class LockComponent implements OnInit, OnDestroy {
try {
const kdf = await this.stateService.getKdfType();
const kdfConfig = await this.stateService.getKdfConfig();
let oldPinProtected: EncString;
let userSymKeyPin: EncString;
if (this.pinSet[0]) {
const key = await this.cryptoService.makeKeyFromPin(
// MP on restart enabled
userSymKeyPin = await this.stateService.getDecryptedUserSymKeyPin();
oldPinProtected = await this.stateService.getDecryptedPinProtected();
} else {
// MP on restart disabled
userSymKeyPin = new EncString(await this.stateService.getEncryptedUserSymKeyPin());
oldPinProtected = new EncString(await this.stateService.getEncryptedPinProtected());
}
let userSymKey: UserSymKey;
if (oldPinProtected) {
userSymKey = await this.decryptAndMigrateOldPinKey(true, kdf, kdfConfig, oldPinProtected);
} else {
userSymKey = await this.cryptoService.decryptUserSymKeyWithPin(
this.pin,
this.email,
kdf,
kdfConfig,
await this.stateService.getDecryptedPinProtected()
userSymKeyPin
);
const encKey = await this.cryptoService.getEncKey(key);
}
if (this.pinSet[0]) {
const protectedPin = await this.stateService.getProtectedPin();
const decPin = await this.cryptoService.decryptToUtf8(new EncString(protectedPin), encKey);
failed = decPin !== this.pin;
if (!failed) {
await this.setKeyAndContinue(key);
}
} else {
const key = await this.cryptoService.makeKeyFromPin(this.pin, this.email, kdf, kdfConfig);
failed = false;
await this.setKeyAndContinue(key);
const decryptedPin = await this.cryptoService.decryptToUtf8(
new EncString(protectedPin),
userSymKey
);
failed = decryptedPin !== this.pin;
}
if (!failed) {
await this.setKeyAndContinue(userSymKey);
}
} catch {
failed = true;
@@ -257,8 +275,9 @@ export class LockComponent implements OnInit, OnDestroy {
}
await this.setKeyAndContinue(key, true);
}
private async setKeyAndContinue(key: SymmetricCryptoKey, evaluatePasswordAfterUnlock = false) {
await this.cryptoService.setKey(key);
private async setKeyAndContinue(key: UserSymKey, evaluatePasswordAfterUnlock = false) {
await this.cryptoService.setUserKey(key);
await this.doContinue(evaluatePasswordAfterUnlock);
}
@@ -297,9 +316,12 @@ export class LockComponent implements OnInit, OnDestroy {
private async load() {
this.pinSet = await this.vaultTimeoutSettingsService.isPinLockSet();
this.pinLock =
(this.pinSet[0] && (await this.stateService.getDecryptedPinProtected()) != null) ||
this.pinSet[1];
let decryptedPinSet = await this.stateService.getDecryptedUserSymKeyPin();
decryptedPinSet ||= await this.stateService.getDecryptedPinProtected();
// (is MP on Restart enabled && MP already entered) || (Pin is set and MP on Restart disabled)
this.pinLock = (this.pinSet[0] && decryptedPinSet != null) || this.pinSet[1];
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
this.biometricLock =
(await this.vaultTimeoutSettingsService.isBiometricLockSet()) &&
@@ -344,4 +366,46 @@ export class LockComponent implements OnInit, OnDestroy {
this.enforcedMasterPasswordOptions
);
}
/**
* Migrates the Pin key from encrypting the user's master key to encrypting
* the user's symmetric key
* @param masterPasswordOnRestart True if Master Password on Restart is enabled
* @param kdf User's KdfType
* @param kdfConfig User's KdfConfig
* @param oldPinProtected The old Pin key from state (retrieved from different
* places depending on if Master Password on Restart was enabled)
* @returns The user's symmetric key
*/
private async decryptAndMigrateOldPinKey(
masterPasswordOnRestart: boolean,
kdf: KdfType,
kdfConfig: KdfConfig,
oldPinProtected?: EncString
): Promise<UserSymKey> {
// decrypt
const masterKey = await this.cryptoService.decryptMasterKeyWithPin(
this.pin,
this.email,
kdf,
kdfConfig,
oldPinProtected
);
const encUserSymKey = await this.stateService.getEncryptedCryptoSymmetricKey();
const userSymKey = await this.cryptoService.decryptUserSymKeyWithMasterKey(
masterKey,
new EncString(encUserSymKey)
);
// migrate
const pinKey = await this.cryptoService.makePinKey(this.pin, this.email, kdf, kdfConfig);
const pinProtectedKey = await this.cryptoService.encrypt(userSymKey.key, pinKey);
if (masterPasswordOnRestart) {
await this.stateService.setDecryptedPinProtected(null);
await this.stateService.setDecryptedUserSymKeyPin(pinProtectedKey);
} else {
await this.stateService.setEncryptedPinProtected(null);
await this.stateService.setEncryptedUserSymKeyPin(pinProtectedKey.encryptedString);
}
return userSymKey;
}
}

View File

@@ -39,14 +39,14 @@ export class SetPinComponent implements OnInit {
const kdfConfig = await this.stateService.getKdfConfig();
const email = await this.stateService.getEmail();
const pinKey = await this.cryptoService.makePinKey(this.pin, email, kdf, kdfConfig);
const key = await this.cryptoService.getKey();
const pinProtectedKey = await this.cryptoService.encrypt(key.key, pinKey);
const userKey = await this.cryptoService.getUserKeyFromMemory();
const pinProtectedKey = await this.cryptoService.encrypt(userKey.key, pinKey);
if (this.masterPassOnRestart) {
const encPin = await this.cryptoService.encrypt(this.pin);
const encPin = await this.cryptoService.encrypt(this.pin, userKey);
await this.stateService.setProtectedPin(encPin.encryptedString);
await this.stateService.setDecryptedPinProtected(pinProtectedKey);
await this.stateService.setDecryptedUserSymKeyPin(pinProtectedKey);
} else {
await this.stateService.setEncryptedPinProtected(pinProtectedKey.encryptedString);
await this.stateService.setEncryptedUserSymKeyPin(pinProtectedKey.encryptedString);
}
this.modalRef.close(true);