1
0
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:
Bernd Schoolmann
2025-10-17 16:30:29 +02:00
committed by GitHub
parent 7015663c38
commit a860f218bd
33 changed files with 1610 additions and 1029 deletions

View File

@@ -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) {