bitwarden-estensione-browser/libs/auth/src/common/services/pin-crypto/pin-crypto.service.implemen...

105 lines
4.1 KiB
TypeScript

import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { PinLockType } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
import { UserKey } from "@bitwarden/common/types/key";
import { PinCryptoServiceAbstraction } from "../../abstractions/pin-crypto.service.abstraction";
export class PinCryptoService implements PinCryptoServiceAbstraction {
constructor(
private stateService: StateService,
private cryptoService: CryptoService,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
private logService: LogService,
private kdfConfigService: KdfConfigService,
) {}
async decryptUserKeyWithPin(pin: string): Promise<UserKey | null> {
try {
const pinLockType: PinLockType = await this.vaultTimeoutSettingsService.isPinLockSet();
const { pinKeyEncryptedUserKey, oldPinKeyEncryptedMasterKey } =
await this.getPinKeyEncryptedKeys(pinLockType);
const kdfConfig: KdfConfig = await this.kdfConfigService.getKdfConfig();
let userKey: UserKey;
const email = await this.stateService.getEmail();
if (oldPinKeyEncryptedMasterKey) {
userKey = await this.cryptoService.decryptAndMigrateOldPinKey(
pinLockType === "TRANSIENT",
pin,
email,
kdfConfig,
oldPinKeyEncryptedMasterKey,
);
} else {
userKey = await this.cryptoService.decryptUserKeyWithPin(
pin,
email,
kdfConfig,
pinKeyEncryptedUserKey,
);
}
if (!userKey) {
this.logService.warning(`User key null after pin key decryption.`);
return null;
}
if (!(await this.validatePin(userKey, pin))) {
this.logService.warning(`Pin key decryption successful but pin validation failed.`);
return null;
}
return userKey;
} catch (error) {
this.logService.error(`Error decrypting user key with pin: ${error}`);
return null;
}
}
// Note: oldPinKeyEncryptedMasterKey is only used for migrating old pin keys
// and will be null for all migrated accounts
private async getPinKeyEncryptedKeys(
pinLockType: PinLockType,
): Promise<{ pinKeyEncryptedUserKey: EncString; oldPinKeyEncryptedMasterKey?: EncString }> {
switch (pinLockType) {
case "PERSISTANT": {
const pinKeyEncryptedUserKey = await this.stateService.getPinKeyEncryptedUserKey();
const oldPinKeyEncryptedMasterKey = await this.stateService.getEncryptedPinProtected();
return {
pinKeyEncryptedUserKey,
oldPinKeyEncryptedMasterKey: oldPinKeyEncryptedMasterKey
? new EncString(oldPinKeyEncryptedMasterKey)
: undefined,
};
}
case "TRANSIENT": {
const pinKeyEncryptedUserKey = await this.stateService.getPinKeyEncryptedUserKeyEphemeral();
const oldPinKeyEncryptedMasterKey = await this.stateService.getDecryptedPinProtected();
return { pinKeyEncryptedUserKey, oldPinKeyEncryptedMasterKey };
}
case "DISABLED":
throw new Error("Pin is disabled");
default: {
// Compile-time check for exhaustive switch
const _exhaustiveCheck: never = pinLockType;
return _exhaustiveCheck;
}
}
}
private async validatePin(userKey: UserKey, pin: string): Promise<boolean> {
const protectedPin = await this.stateService.getProtectedPin();
const decryptedPin = await this.cryptoService.decryptToUtf8(
new EncString(protectedPin),
userKey,
);
return decryptedPin === pin;
}
}