diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts index 8100ff3cffa..7eb26d1e7f1 100644 --- a/apps/browser/src/background/nativeMessaging.background.ts +++ b/apps/browser/src/background/nativeMessaging.background.ts @@ -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; diff --git a/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts b/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts index 64a8bdfbfb4..574f6b56135 100644 --- a/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts +++ b/apps/web/src/app/auth/core/services/rotateable-key-set.service.ts @@ -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( newEncryptedUserKey, - newEncryptedPublicKey, + newEncryptedPublicKey!, keySet.encryptedPrivateKey, ); diff --git a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts index dd257282cfa..029e9c83054 100644 --- a/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts +++ b/apps/web/src/app/key-management/key-rotation/user-key-rotation.service.ts @@ -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; } } diff --git a/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts index 484327bcd27..f06fd5fdeeb 100644 --- a/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts +++ b/libs/common/src/key-management/crypto/abstractions/encrypt.service.ts @@ -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; + /** + * 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; + abstract encryptToBytes(plainValue: Uint8Array, key: SymmetricCryptoKey): Promise; + /** * 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; + ): Promise; + /** * 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; + abstract rsaEncrypt(data: Uint8Array, publicKey: Uint8Array): Promise; + abstract rsaDecrypt(data: EncString, privateKey: Uint8Array): Promise; + 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[], key: SymmetricCryptoKey, ): Promise; + /** * Generates a base64-encoded hash of the given value * @param value The value to hash diff --git a/libs/common/src/key-management/crypto/services/bulk-encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/bulk-encrypt.service.implementation.ts index 1d1e0f52279..4f5234a2aaf 100644 --- a/libs/common/src/key-management/crypto/services/bulk-encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/bulk-encrypt.service.implementation.ts @@ -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(worker, "message").pipe( filter((response: MessageEvent) => response.data?.id === request.id), map((response) => JSON.parse(response.data.items)), map((items) => diff --git a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts index 8a001886837..1ce915048e0 100644 --- a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts @@ -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 { + async encrypt( + plainValue: string | Uint8Array, + key: SymmetricCryptoKey, + ): Promise { 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 { + ): Promise { 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 { diff --git a/libs/common/src/key-management/crypto/services/encrypt.worker.ts b/libs/common/src/key-management/crypto/services/encrypt.worker.ts index 84ffcf56934..feeba89e1b0 100644 --- a/libs/common/src/key-management/crypto/services/encrypt.worker.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.worker.ts @@ -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"; diff --git a/libs/common/src/key-management/crypto/services/fallback-bulk-encrypt.service.ts b/libs/common/src/key-management/crypto/services/fallback-bulk-encrypt.service.ts index 80fdd27895d..8e61eb39f8e 100644 --- a/libs/common/src/key-management/crypto/services/fallback-bulk-encrypt.service.ts +++ b/libs/common/src/key-management/crypto/services/fallback-bulk-encrypt.service.ts @@ -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) {} diff --git a/libs/common/src/key-management/crypto/services/multithread-encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/multithread-encrypt.service.implementation.ts index 0bf96851563..d0f04d25934 100644 --- a/libs/common/src/key-management/crypto/services/multithread-encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/multithread-encrypt.service.implementation.ts @@ -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(); @@ -56,7 +54,7 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple this.worker.postMessage(JSON.stringify(request)); return await firstValueFrom( - fromEvent(this.worker, "message").pipe( + fromEvent(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(); } diff --git a/libs/common/src/key-management/services/default-process-reload.service.ts b/libs/common/src/key-management/services/default-process-reload.service.ts index 09fe4e7e10c..c4087845cbd 100644 --- a/libs/common/src/key-management/services/default-process-reload.service.ts +++ b/libs/common/src/key-management/services/default-process-reload.service.ts @@ -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 = null, + private reloadCallback: (() => Promise) | null = null, private vaultTimeoutSettingsService: VaultTimeoutSettingsService, private biometricStateService: BiometricStateService, private accountService: AccountService, diff --git a/libs/common/src/platform/abstractions/crypto-function.service.ts b/libs/common/src/platform/abstractions/crypto-function.service.ts index 56b0ee55afe..cfca75fe388 100644 --- a/libs/common/src/platform/abstractions/crypto-function.service.ts +++ b/libs/common/src/platform/abstractions/crypto-function.service.ts @@ -49,7 +49,7 @@ export abstract class CryptoFunctionService { abstract aesDecryptFastParameters( data: string, iv: string, - mac: string, + mac: string | null, key: SymmetricCryptoKey, ): CbcDecryptParameters; abstract aesDecryptFast({ diff --git a/libs/common/src/platform/enums/encryption-type.enum.ts b/libs/common/src/platform/enums/encryption-type.enum.ts index a0ffe679279..890d29b2659 100644 --- a/libs/common/src/platform/enums/encryption-type.enum.ts +++ b/libs/common/src/platform/enums/encryption-type.enum.ts @@ -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; diff --git a/libs/common/src/platform/services/container.service.ts b/libs/common/src/platform/services/container.service.ts index 1428b2bbd7c..4f056902b06 100644 --- a/libs/common/src/platform/services/container.service.ts +++ b/libs/common/src/platform/services/container.service.ts @@ -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) { diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index b174087b766..9ef6982b8a3 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -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."); } diff --git a/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts b/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts index 06113ee9b99..221d00ca4e5 100644 --- a/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts +++ b/libs/tools/generator/extensions/history/src/legacy-password-history-decryptor.ts @@ -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); });