diff --git a/libs/common/src/key-management/crypto/abstractions/bulk-encrypt.service.ts b/libs/common/src/key-management/crypto/abstractions/bulk-encrypt.service.ts index 3e47ccdb5f2..a88a29513a8 100644 --- a/libs/common/src/key-management/crypto/abstractions/bulk-encrypt.service.ts +++ b/libs/common/src/key-management/crypto/abstractions/bulk-encrypt.service.ts @@ -1,10 +1,13 @@ -import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface"; -import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { OnServerConfigChange } from "../../../platform/abstractions/config/config.service"; +import { ServerConfig } from "../../../platform/abstractions/config/server-config"; +import { Decryptable } from "../../../platform/interfaces/decryptable.interface"; +import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface"; +import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; -export abstract class BulkEncryptService { +export abstract class BulkEncryptService implements OnServerConfigChange { abstract decryptItems( items: Decryptable[], key: SymmetricCryptoKey, ): Promise; + abstract onServerConfigChange(newConfig: ServerConfig): void; } 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..17accdb5dff 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 @@ -12,6 +12,10 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer"; +import { ServerConfig } from "../../../platform/abstractions/config/server-config"; + +import { buildDecryptMessage, buildSetConfigMessage } from "./encrypt.worker"; + // TTL (time to live) is not strictly required but avoids tying up memory resources if inactive const workerTTL = 60000; // 1 minute const maxWorkers = 8; @@ -57,6 +61,13 @@ export class BulkEncryptServiceImplementation implements BulkEncryptService { return decryptedItems; } + onServerConfigChange(newConfig: ServerConfig): void { + this.workers.forEach((worker) => { + const request = buildSetConfigMessage({ newConfig }); + worker.postMessage(request); + }); + } + /** * Sends items to a set of web workers to decrypt them. This utilizes multiple workers to decrypt items * faster without interrupting other operations (e.g. updating UI). @@ -108,17 +119,18 @@ export class BulkEncryptServiceImplementation implements BulkEncryptService { itemsForWorker.push(...items.slice(end)); } - const request = { - id: Utils.newGuid(), + const id = Utils.newGuid(); + const request = buildDecryptMessage({ + id, items: itemsForWorker, key: key, - }; + }); - worker.postMessage(JSON.stringify(request)); + worker.postMessage(request); results.push( firstValueFrom( fromEvent(worker, "message").pipe( - filter((response: MessageEvent) => response.data?.id === request.id), + filter((response: MessageEvent) => response.data?.id === id), map((response) => JSON.parse(response.data.items)), map((items) => items.map((jsonItem: Jsonify) => { 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..fcde1c83367 100644 --- a/libs/common/src/key-management/crypto/services/encrypt.worker.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.worker.ts @@ -9,19 +9,48 @@ import { ContainerService } from "@bitwarden/common/platform/services/container. import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer"; import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; +import { ServerConfig } from "../../../platform/abstractions/config/server-config"; +import { LogService } from "../../../platform/abstractions/log.service"; + import { EncryptServiceImplementation } from "./encrypt.service.implementation"; const workerApi: Worker = self as any; let inited = false; let encryptService: EncryptServiceImplementation; +let logService: LogService; + +const DECRYPT_COMMAND_SHELL = Object.freeze({ command: "decrypt" }); +const SET_CONFIG_COMMAND_SHELL = Object.freeze({ command: "setConfig" }); + +type DecryptCommandData = { + id: string; + items: Jsonify>[]; + key: Jsonify; +}; + +type SetConfigCommandData = { newConfig: ServerConfig }; + +export function buildDecryptMessage(data: DecryptCommandData): string { + return JSON.stringify({ + ...data, + ...DECRYPT_COMMAND_SHELL, + }); +} + +export function buildSetConfigMessage(data: SetConfigCommandData): string { + return JSON.stringify({ + ...data, + ...SET_CONFIG_COMMAND_SHELL, + }); +} /** * Bootstrap the worker environment with services required for decryption */ export function init() { const cryptoFunctionService = new WebCryptoFunctionService(self); - const logService = new ConsoleLogService(false); + logService = new ConsoleLogService(false); encryptService = new EncryptServiceImplementation(cryptoFunctionService, logService, true); const bitwardenContainerService = new ContainerService(null, encryptService); @@ -39,11 +68,20 @@ workerApi.addEventListener("message", async (event: { data: string }) => { } const request: { - id: string; - items: Jsonify>[]; - key: Jsonify; + command: string; } = JSON.parse(event.data); + switch (request.command) { + case DECRYPT_COMMAND_SHELL.command: + return await handleDecrypt(request as unknown as DecryptCommandData); + case SET_CONFIG_COMMAND_SHELL.command: + return await handleSetConfig(request as unknown as SetConfigCommandData); + default: + logService.error(`unknown worker command`, request.command, request); + } +}); + +async function handleDecrypt(request: DecryptCommandData) { const key = SymmetricCryptoKey.fromJSON(request.key); const items = request.items.map((jsonItem) => { const initializer = getClassInitializer>(jsonItem.initializerKey); @@ -55,4 +93,8 @@ workerApi.addEventListener("message", async (event: { data: string }) => { id: request.id, items: JSON.stringify(result), }); -}); +} + +async function handleSetConfig(request: SetConfigCommandData) { + encryptService.onServerConfigChange(request.newConfig); +} 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..852ff08165e 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,10 +1,10 @@ // 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"; -import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; - +import { BulkEncryptService } from "../../../key-management/crypto/abstractions/bulk-encrypt.service"; +import { ServerConfig } from "../../../platform/abstractions/config/server-config"; +import { Decryptable } from "../../../platform/interfaces/decryptable.interface"; +import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface"; +import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { EncryptService } from "../abstractions/encrypt.service"; /** @@ -33,4 +33,8 @@ export class FallbackBulkEncryptService implements BulkEncryptService { async setFeatureFlagEncryptService(featureFlagEncryptService: BulkEncryptService) { this.featureFlagEncryptService = featureFlagEncryptService; } + + onServerConfigChange(newConfig: ServerConfig): void { + (this.featureFlagEncryptService ?? this.encryptService).onServerConfigChange(newConfig); + } } 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..921f881408d 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 @@ -9,7 +9,10 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { getClassInitializer } from "@bitwarden/common/platform/services/cryptography/get-class-initializer"; +import { ServerConfig } from "../../../platform/abstractions/config/server-config"; + import { EncryptServiceImplementation } from "./encrypt.service.implementation"; +import { buildDecryptMessage, buildSetConfigMessage } from "./encrypt.worker"; // TTL (time to live) is not strictly required but avoids tying up memory resources if inactive const workerTTL = 3 * 60000; // 3 minutes @@ -47,17 +50,18 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple this.restartTimeout(); - const request = { - id: Utils.newGuid(), + const id = Utils.newGuid(); + const request = buildDecryptMessage({ + id, items: items, key: key, - }; + }); - this.worker.postMessage(JSON.stringify(request)); + this.worker.postMessage(request); return await firstValueFrom( fromEvent(this.worker, "message").pipe( - filter((response: MessageEvent) => response.data?.id === request.id), + filter((response: MessageEvent) => response.data?.id === id), map((response) => JSON.parse(response.data.items)), map((items) => items.map((jsonItem: Jsonify) => { @@ -71,6 +75,15 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple ); } + override onServerConfigChange(newConfig: ServerConfig): void { + super.onServerConfigChange(newConfig); + + if (this.worker !== null) { + const request = buildSetConfigMessage({ newConfig }); + this.worker.postMessage(request); + } + } + private clear() { this.clear$.next(); this.worker?.terminate();