mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 13:40:06 +00:00
Key Management in crypto with TS strict
This commit is contained in:
@@ -350,10 +350,10 @@ export class NativeMessagingBackground {
|
||||
await this.secureCommunication();
|
||||
}
|
||||
|
||||
return await this.encryptService.encrypt(
|
||||
return (await this.encryptService.encrypt(
|
||||
JSON.stringify(message),
|
||||
this.secureChannel!.sharedSecret!,
|
||||
);
|
||||
))!;
|
||||
}
|
||||
|
||||
private postMessage(message: OuterMessage, messageId?: number) {
|
||||
@@ -394,11 +394,11 @@ export class NativeMessagingBackground {
|
||||
return;
|
||||
}
|
||||
message = JSON.parse(
|
||||
await this.encryptService.decryptToUtf8(
|
||||
(await this.encryptService.decryptToUtf8(
|
||||
rawMessage as EncString,
|
||||
this.secureChannel.sharedSecret,
|
||||
"ipc-desktop-ipc-channel-key",
|
||||
),
|
||||
))!,
|
||||
);
|
||||
} else {
|
||||
message = rawMessage as ReceiveMessage;
|
||||
|
||||
@@ -27,7 +27,7 @@ export class RotateableKeySetService {
|
||||
const rawPublicKey = Utils.fromB64ToArray(publicKey);
|
||||
const encryptedUserKey = await this.encryptService.rsaEncrypt(userKey.key, rawPublicKey);
|
||||
const encryptedPublicKey = await this.encryptService.encrypt(rawPublicKey, userKey);
|
||||
return new RotateableKeySet(encryptedUserKey, encryptedPublicKey, encryptedPrivateKey);
|
||||
return new RotateableKeySet(encryptedUserKey, encryptedPublicKey!, encryptedPrivateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +64,7 @@ export class RotateableKeySetService {
|
||||
|
||||
const newRotateableKeySet = new RotateableKeySet<ExternalKey>(
|
||||
newEncryptedUserKey,
|
||||
newEncryptedPublicKey,
|
||||
newEncryptedPublicKey!,
|
||||
keySet.encryptedPrivateKey,
|
||||
);
|
||||
|
||||
|
||||
@@ -180,6 +180,6 @@ export class UserKeyRotationService {
|
||||
if (privateKey == null) {
|
||||
throw new Error("No private key found for user key rotation");
|
||||
}
|
||||
return (await this.encryptService.encrypt(privateKey, newUserKey)).encryptedString;
|
||||
return (await this.encryptService.encrypt(privateKey, newUserKey))!.encryptedString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,34 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
export abstract class EncryptService {
|
||||
abstract encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise<EncString>;
|
||||
/**
|
||||
* Encrypts a string to an EncString.
|
||||
* @throws Error when {@link key} is null
|
||||
* @param plainValue - The string to encrypt
|
||||
* @param key - The key to encrypt the string with
|
||||
* @returns The encrypted EncString. Returns null when key has mac key but payload is missing mac bytes or when key encryption type does not match payload encryption type or when MAC comparison failed.
|
||||
*/
|
||||
abstract encrypt(
|
||||
plainValue: string | Uint8Array,
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<EncString | null>;
|
||||
|
||||
abstract encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise<EncArrayBuffer>;
|
||||
|
||||
/**
|
||||
* Decrypts an EncString to a string
|
||||
* @param encString - The EncString to decrypt
|
||||
* @param key - The key to decrypt the EncString with
|
||||
* @param decryptTrace - A string to identify the context of the object being decrypted. This can include: field name, encryption type, cipher id, key type, but should not include
|
||||
* sensitive information like encryption keys or data. This is used for logging when decryption errors occur in order to identify what failed to decrypt
|
||||
* @returns The decrypted string
|
||||
* @returns The decrypted string. Returns null when {@link key} is null or when {@link encString}'s ${@link EncString.data} or ${@link EncString.iv} is null or when key has mac key but payload is missing mac bytes or when key encryption type does not match payload encryption type or when MAC comparison failed.
|
||||
*/
|
||||
abstract decryptToUtf8(
|
||||
encString: EncString,
|
||||
key: SymmetricCryptoKey,
|
||||
decryptTrace?: string,
|
||||
): Promise<string>;
|
||||
): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* Decrypts an Encrypted object to a Uint8Array
|
||||
* @param encThing - The Encrypted object to decrypt
|
||||
@@ -34,9 +47,13 @@ export abstract class EncryptService {
|
||||
key: SymmetricCryptoKey,
|
||||
decryptTrace?: string,
|
||||
): Promise<Uint8Array | null>;
|
||||
|
||||
abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise<EncString>;
|
||||
|
||||
abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise<Uint8Array>;
|
||||
|
||||
abstract resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey;
|
||||
|
||||
/**
|
||||
* @deprecated Replaced by BulkEncryptService, remove once the feature is tested and the featureflag PM-4154-multi-worker-encryption-service is removed
|
||||
* @param items The items to decrypt
|
||||
@@ -46,6 +63,7 @@ export abstract class EncryptService {
|
||||
items: Decryptable<T>[],
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<T[]>;
|
||||
|
||||
/**
|
||||
* Generates a base64-encoded hash of the given value
|
||||
* @param value The value to hash
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom, fromEvent, filter, map, takeUntil, defaultIfEmpty, Subject } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
@@ -117,7 +115,7 @@ export class BulkEncryptServiceImplementation implements BulkEncryptService {
|
||||
worker.postMessage(JSON.stringify(request));
|
||||
results.push(
|
||||
firstValueFrom(
|
||||
fromEvent(worker, "message").pipe(
|
||||
fromEvent<MessageEvent>(worker, "message").pipe(
|
||||
filter((response: MessageEvent) => response.data?.id === request.id),
|
||||
map((response) => JSON.parse(response.data.items)),
|
||||
map((items) =>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import {
|
||||
@@ -24,13 +22,16 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
protected logMacFailures: boolean,
|
||||
) {}
|
||||
|
||||
async encrypt(plainValue: string | Uint8Array, key: SymmetricCryptoKey): Promise<EncString> {
|
||||
async encrypt(
|
||||
plainValue: string | Uint8Array,
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<EncString | null> {
|
||||
if (key == null) {
|
||||
throw new Error("No encryption key provided.");
|
||||
}
|
||||
|
||||
if (plainValue == null) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
let plainBuf: Uint8Array;
|
||||
@@ -43,7 +44,7 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
const encObj = await this.aesEncrypt(plainBuf, key);
|
||||
const iv = Utils.fromBufferToB64(encObj.iv);
|
||||
const data = Utils.fromBufferToB64(encObj.data);
|
||||
const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : null;
|
||||
const mac = encObj.mac != null ? Utils.fromBufferToB64(encObj.mac) : undefined;
|
||||
return new EncString(encObj.key.encType, data, iv, mac);
|
||||
}
|
||||
|
||||
@@ -73,10 +74,16 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
encString: EncString,
|
||||
key: SymmetricCryptoKey,
|
||||
decryptContext: string = "no context",
|
||||
): Promise<string> {
|
||||
): Promise<string | null> {
|
||||
if (key == null) {
|
||||
throw new Error("No key provided for decryption.");
|
||||
}
|
||||
if (encString?.data == null) {
|
||||
throw new Error("No data provided for decryption.");
|
||||
}
|
||||
if (encString?.iv == null) {
|
||||
throw new Error("No initialization vector provided for decryption.");
|
||||
}
|
||||
|
||||
key = this.resolveLegacyKey(key, encString);
|
||||
|
||||
@@ -106,7 +113,7 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
const fastParams = this.cryptoFunctionService.aesDecryptFastParameters(
|
||||
encString.data,
|
||||
encString.iv,
|
||||
encString.mac,
|
||||
encString.mac ?? null,
|
||||
key,
|
||||
);
|
||||
if (fastParams.macKey != null && fastParams.mac != null) {
|
||||
@@ -301,6 +308,7 @@ export class EncryptServiceImplementation implements EncryptService {
|
||||
|
||||
/**
|
||||
* Transform into new key for the old encrypt-then-mac scheme if required, otherwise return the current key unchanged
|
||||
* @param key The key to transform
|
||||
* @param encThing The encrypted object (e.g. encString or encArrayBuffer) that you want to decrypt
|
||||
*/
|
||||
resolveLegacyKey(key: SymmetricCryptoKey, encThing: Encrypted): SymmetricCryptoKey {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface";
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abstractions/bulk-encrypt.service";
|
||||
import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface";
|
||||
import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface";
|
||||
@@ -11,7 +9,7 @@ import { EncryptService } from "../abstractions/encrypt.service";
|
||||
* @deprecated For the feature flag from PM-4154, remove once feature is rolled out
|
||||
*/
|
||||
export class FallbackBulkEncryptService implements BulkEncryptService {
|
||||
private featureFlagEncryptService: BulkEncryptService;
|
||||
private featureFlagEncryptService?: BulkEncryptService;
|
||||
|
||||
constructor(protected encryptService: EncryptService) {}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { defaultIfEmpty, filter, firstValueFrom, fromEvent, map, Subject, takeUntil } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
@@ -18,7 +16,7 @@ const workerTTL = 3 * 60000; // 3 minutes
|
||||
* @deprecated Replaced by BulkEncryptionService (PM-4154)
|
||||
*/
|
||||
export class MultithreadEncryptServiceImplementation extends EncryptServiceImplementation {
|
||||
private worker: Worker;
|
||||
private worker?: Worker;
|
||||
private timeout: any;
|
||||
|
||||
private clear$ = new Subject<void>();
|
||||
@@ -56,7 +54,7 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple
|
||||
this.worker.postMessage(JSON.stringify(request));
|
||||
|
||||
return await firstValueFrom(
|
||||
fromEvent(this.worker, "message").pipe(
|
||||
fromEvent<MessageEvent>(this.worker, "message").pipe(
|
||||
filter((response: MessageEvent) => response.data?.id === request.id),
|
||||
map((response) => JSON.parse(response.data.items)),
|
||||
map((items) =>
|
||||
@@ -74,7 +72,7 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple
|
||||
private clear() {
|
||||
this.clear$.next();
|
||||
this.worker?.terminate();
|
||||
this.worker = null;
|
||||
this.worker = undefined;
|
||||
this.clearTimeout();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom, map, timeout } from "rxjs";
|
||||
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
@@ -21,7 +19,7 @@ export class DefaultProcessReloadService implements ProcessReloadServiceAbstract
|
||||
constructor(
|
||||
private pinService: PinServiceAbstraction,
|
||||
private messagingService: MessagingService,
|
||||
private reloadCallback: () => Promise<void> = null,
|
||||
private reloadCallback: (() => Promise<void>) | null = null,
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private accountService: AccountService,
|
||||
|
||||
@@ -49,7 +49,7 @@ export abstract class CryptoFunctionService {
|
||||
abstract aesDecryptFastParameters(
|
||||
data: string,
|
||||
iv: string,
|
||||
mac: string,
|
||||
mac: string | null,
|
||||
key: SymmetricCryptoKey,
|
||||
): CbcDecryptParameters<Uint8Array | string>;
|
||||
abstract aesDecryptFast({
|
||||
|
||||
@@ -8,8 +8,8 @@ export enum EncryptionType {
|
||||
Rsa2048_OaepSha1_HmacSha256_B64 = 6,
|
||||
}
|
||||
|
||||
export function encryptionTypeToString(encryptionType: EncryptionType): string {
|
||||
if (encryptionType in EncryptionType) {
|
||||
export function encryptionTypeToString(encryptionType?: EncryptionType): string {
|
||||
if (encryptionType != null && encryptionType in EncryptionType) {
|
||||
return EncryptionType[encryptionType];
|
||||
} else {
|
||||
return "Unknown encryption type " + encryptionType;
|
||||
|
||||
@@ -4,8 +4,8 @@ import { EncryptService } from "../../key-management/crypto/abstractions/encrypt
|
||||
|
||||
export class ContainerService {
|
||||
constructor(
|
||||
private keyService: KeyService,
|
||||
private encryptService: EncryptService,
|
||||
private keyService: KeyService | null,
|
||||
private encryptService: EncryptService | null,
|
||||
) {}
|
||||
|
||||
attachToGlobal(global: any) {
|
||||
|
||||
@@ -545,7 +545,7 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
const keyPair = await this.cryptoFunctionService.rsaGenerateKeyPair(2048);
|
||||
const publicB64 = Utils.fromBufferToB64(keyPair[0]);
|
||||
const privateEnc = await this.encryptService.encrypt(keyPair[1], key);
|
||||
return [publicB64, privateEnc];
|
||||
return [publicB64, privateEnc!];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -732,10 +732,10 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
const storePin = await this.shouldStoreKey(KeySuffixOptions.Pin, userId);
|
||||
if (storePin) {
|
||||
// Decrypt userKeyEncryptedPin with user key
|
||||
const pin = await this.encryptService.decryptToUtf8(
|
||||
const pin = (await this.encryptService.decryptToUtf8(
|
||||
(await this.pinService.getUserKeyEncryptedPin(userId))!,
|
||||
key,
|
||||
);
|
||||
))!;
|
||||
|
||||
const pinKeyEncryptedUserKey = await this.pinService.createPinKeyEncryptedUserKey(
|
||||
pin,
|
||||
@@ -827,9 +827,9 @@ export class DefaultKeyService implements KeyServiceAbstraction {
|
||||
let protectedSymKey: EncString;
|
||||
if (encryptionKey.key.byteLength === 32) {
|
||||
const stretchedEncryptionKey = await this.keyGenerationService.stretchKey(encryptionKey);
|
||||
protectedSymKey = await this.encryptService.encrypt(newSymKey, stretchedEncryptionKey);
|
||||
protectedSymKey = (await this.encryptService.encrypt(newSymKey, stretchedEncryptionKey))!;
|
||||
} else if (encryptionKey.key.byteLength === 64) {
|
||||
protectedSymKey = await this.encryptService.encrypt(newSymKey, encryptionKey);
|
||||
protectedSymKey = (await this.encryptService.encrypt(newSymKey, encryptionKey))!;
|
||||
} else {
|
||||
throw new Error("Invalid key size.");
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export class LegacyPasswordHistoryDecryptor {
|
||||
|
||||
const promises = (history ?? []).map(async (item) => {
|
||||
const encrypted = new EncString(item.password);
|
||||
const decrypted = await this.encryptService.decryptToUtf8(encrypted, key);
|
||||
const decrypted = (await this.encryptService.decryptToUtf8(encrypted, key))!;
|
||||
return new GeneratedPasswordHistory(decrypted, item.date);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user