mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
[PM-24128] New Pin service, using PasswordProtectedKeyEnvelope (#15863)
* fix: broken SDK interface * Fix all compile errors related to uuids * Update usages of sdk to type-safe SDK type * Update sdk version * Update to "toSdk" * Move pin service to km ownership * Run format * Eslint * Fix tsconfig * Fix imports and test * Clean up imports * Pin tmp * Initial version of updated pin service * Add tests * Rename function * Clean up logging * Fix imports * Fix cli build * Fix browser desktop * Fix tests * Attempt to fix * Fix build * Fix tests * Fix browser build * Add missing empty line * Fix linting * Remove non-required change * Missing newline * Re-add comment * Undo change to file * Fix missing empty line * Cleanup * Cleanup * Cleanup * Cleanup * Switch to replaysubject * Add comments * Fix tests * Run prettier * Undo change * Fix browser * Fix circular dependency on browser * Add missing clear ephemeral pin * Address feedback * Update docs * Simplify sdk usage in pin service * Replace with mock sdk * Update sdk * Initialize pin service via unlock instead of listening to keyservice * Cleanup * Fix test * Prevent race condition with userkey not being set * Filter null userkeys * [PM-24124] Pin State Service (#16641) * add pin-state.service * add remaining tests * improve description for clearEphemeralPinState * rename getUserKeyWrappedPin$ to userKeyWrappedPin$ * drop temp variable in setPinState * add new test and remove copied one * Fix dep cycle * Fix tests and remaining build issues * Fix cli build * Add comments about functions not being public API --------- Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com> Co-authored-by: Hinton <hinton@users.noreply.github.com> Co-authored-by: Jake Fink <jfink@bitwarden.com>
This commit is contained in:
@@ -27,7 +27,6 @@ import {
|
||||
EncryptedString,
|
||||
} from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
|
||||
import { WrappedSigningKey } from "@bitwarden/common/key-management/types";
|
||||
import { VaultTimeoutStringType } from "@bitwarden/common/key-management/vault-timeout";
|
||||
import { VAULT_TIMEOUT } from "@bitwarden/common/key-management/vault-timeout/services/vault-timeout-settings.state";
|
||||
@@ -72,7 +71,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
readonly activeUserOrgKeys$: Observable<Record<OrganizationId, OrgKey>>;
|
||||
|
||||
constructor(
|
||||
protected pinService: PinServiceAbstraction,
|
||||
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
protected keyGenerationService: KeyGenerationService,
|
||||
protected cryptoFunctionService: CryptoFunctionService,
|
||||
@@ -105,6 +103,13 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
await this.stateProvider.setUserState(USER_EVER_HAD_USER_KEY, true, userId);
|
||||
|
||||
await this.storeAdditionalKeys(key, userId);
|
||||
|
||||
// Await the key actually being set. This ensures that any subsequent callers know the key is already in state.
|
||||
// There were bugs related to the stateprovider observables in the past that caused issues around this.
|
||||
const userKey = await firstValueFrom(this.userKey$(userId).pipe(filter((k) => k != null)));
|
||||
if (userKey == null) {
|
||||
throw new Error("Failed to set user key");
|
||||
}
|
||||
}
|
||||
|
||||
async setUserKeys(
|
||||
@@ -223,17 +228,12 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
await this.clearAllStoredUserKeys(userId);
|
||||
}
|
||||
|
||||
async clearStoredUserKey(keySuffix: KeySuffixOptions, userId: UserId): Promise<void> {
|
||||
async clearStoredUserKey(userId: UserId): Promise<void> {
|
||||
if (userId == null) {
|
||||
throw new Error("UserId is required");
|
||||
}
|
||||
|
||||
if (keySuffix === KeySuffixOptions.Auto) {
|
||||
await this.stateService.setUserKeyAutoUnlock(null, { userId: userId });
|
||||
}
|
||||
if (keySuffix === KeySuffixOptions.Pin) {
|
||||
await this.pinService.clearPinKeyEncryptedUserKeyEphemeral(userId);
|
||||
}
|
||||
await this.stateService.setUserKeyAutoUnlock(null, { userId: userId });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -513,16 +513,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
await this.stateProvider.setUserState(USER_KEY_ENCRYPTED_SIGNING_KEY, null, userId);
|
||||
}
|
||||
|
||||
async clearPinKeys(userId: UserId): Promise<void> {
|
||||
if (userId == null) {
|
||||
throw new Error("UserId is required");
|
||||
}
|
||||
|
||||
await this.pinService.clearPinKeyEncryptedUserKeyPersistent(userId);
|
||||
await this.pinService.clearPinKeyEncryptedUserKeyEphemeral(userId);
|
||||
await this.pinService.clearUserKeyEncryptedPin(userId);
|
||||
}
|
||||
|
||||
async makeSendKey(keyMaterial: CsprngArray): Promise<SymmetricCryptoKey> {
|
||||
return await this.keyGenerationService.deriveKeyFromMaterial(
|
||||
keyMaterial,
|
||||
@@ -546,7 +536,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
await this.clearProviderKeys(userId);
|
||||
await this.clearKeyPair(userId);
|
||||
await this.clearSigningKey(userId);
|
||||
await this.clearPinKeys(userId);
|
||||
await this.stateProvider.setUserState(USER_EVER_HAD_USER_KEY, null, userId);
|
||||
}
|
||||
|
||||
@@ -678,32 +667,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
} else {
|
||||
await this.stateService.setUserKeyAutoUnlock(null, { userId: userId });
|
||||
}
|
||||
|
||||
const storePin = await this.shouldStoreKey(KeySuffixOptions.Pin, userId);
|
||||
if (storePin) {
|
||||
// Decrypt userKeyEncryptedPin with user key
|
||||
const pin = await this.encryptService.decryptString(
|
||||
(await this.pinService.getUserKeyEncryptedPin(userId))!,
|
||||
key,
|
||||
);
|
||||
|
||||
const pinKeyEncryptedUserKey = await this.pinService.createPinKeyEncryptedUserKey(
|
||||
pin,
|
||||
key,
|
||||
userId,
|
||||
);
|
||||
const noPreExistingPersistentKey =
|
||||
(await this.pinService.getPinKeyEncryptedUserKeyPersistent(userId)) == null;
|
||||
|
||||
await this.pinService.storePinKeyEncryptedUserKey(
|
||||
pinKeyEncryptedUserKey,
|
||||
noPreExistingPersistentKey,
|
||||
userId,
|
||||
);
|
||||
} else {
|
||||
await this.pinService.clearPinKeyEncryptedUserKeyPersistent(userId);
|
||||
await this.pinService.clearPinKeyEncryptedUserKeyEphemeral(userId);
|
||||
}
|
||||
}
|
||||
|
||||
protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId: UserId) {
|
||||
@@ -720,11 +683,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
shouldStoreKey = vaultTimeout == VaultTimeoutStringType.Never;
|
||||
break;
|
||||
}
|
||||
case KeySuffixOptions.Pin: {
|
||||
const userKeyEncryptedPin = await this.pinService.getUserKeyEncryptedPin(userId);
|
||||
shouldStoreKey = !!userKeyEncryptedPin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return shouldStoreKey;
|
||||
}
|
||||
@@ -744,7 +702,6 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
|
||||
protected async clearAllStoredUserKeys(userId: UserId): Promise<void> {
|
||||
await this.stateService.setUserKeyAutoUnlock(null, { userId: userId });
|
||||
await this.pinService.clearPinKeyEncryptedUserKeyEphemeral(userId);
|
||||
}
|
||||
|
||||
private async hashPhrase(hash: Uint8Array, minimumEntropy = 64) {
|
||||
|
||||
Reference in New Issue
Block a user