1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-06-27 10:46:02 +02:00

[EC-272] Web workers using EncryptionService (#3532)

* Add item decryption to encryptService
* Create multithreadEncryptService subclass to handle web workers
* Create encryption web worker
* Refactor cipherService to use new interface
* Update dependencies
This commit is contained in:
Thomas Rittson 2022-10-28 07:38:54 +10:00 committed by GitHub
parent e972e905c8
commit da47992a22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 419 additions and 136 deletions

View File

@ -235,7 +235,6 @@
./libs/common/src/abstractions/fileDownload/fileDownloadBuilder.ts ./libs/common/src/abstractions/fileDownload/fileDownloadBuilder.ts
./libs/common/src/abstractions/fileDownload/fileDownload.service.ts ./libs/common/src/abstractions/fileDownload/fileDownload.service.ts
./libs/common/src/abstractions/fileDownload/fileDownloadRequest.ts ./libs/common/src/abstractions/fileDownload/fileDownloadRequest.ts
./libs/common/src/abstractions/abstractEncrypt.service.ts
./libs/common/src/abstractions/passwordGeneration.service.ts ./libs/common/src/abstractions/passwordGeneration.service.ts
./libs/common/src/abstractions/passwordReprompt.service.ts ./libs/common/src/abstractions/passwordReprompt.service.ts
./libs/common/src/abstractions/formValidationErrors.service.ts ./libs/common/src/abstractions/formValidationErrors.service.ts

View File

@ -6,6 +6,7 @@ import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/abs
import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/abstractions/collection.service"; import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/abstractions/collection.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service"; import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service";
import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service"; import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service";
import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/abstractions/fileUpload.service"; import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/abstractions/fileUpload.service";
@ -51,7 +52,8 @@ import { CipherService } from "@bitwarden/common/services/cipher.service";
import { CollectionService } from "@bitwarden/common/services/collection.service"; import { CollectionService } from "@bitwarden/common/services/collection.service";
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
import { ContainerService } from "@bitwarden/common/services/container.service"; import { ContainerService } from "@bitwarden/common/services/container.service";
import { EncryptService } from "@bitwarden/common/services/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation";
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/services/cryptography/multithread-encrypt.service.implementation";
import { EventService } from "@bitwarden/common/services/event.service"; import { EventService } from "@bitwarden/common/services/event.service";
import { ExportService } from "@bitwarden/common/services/export.service"; import { ExportService } from "@bitwarden/common/services/export.service";
import { FileUploadService } from "@bitwarden/common/services/fileUpload.service"; import { FileUploadService } from "@bitwarden/common/services/fileUpload.service";
@ -82,6 +84,7 @@ import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFu
import { BrowserApi } from "../browser/browserApi"; import { BrowserApi } from "../browser/browserApi";
import { SafariApp } from "../browser/safariApp"; import { SafariApp } from "../browser/safariApp";
import { flagEnabled } from "../flags";
import { UpdateBadge } from "../listeners/update-badge"; import { UpdateBadge } from "../listeners/update-badge";
import { Account } from "../models/account"; import { Account } from "../models/account";
import { PopupUtilsService } from "../popup/services/popup-utils.service"; import { PopupUtilsService } from "../popup/services/popup-utils.service";
@ -215,7 +218,7 @@ export default class MainBackground {
this.memoryStorageService = this.memoryStorageService =
BrowserApi.manifestVersion === 3 BrowserApi.manifestVersion === 3
? new LocalBackedSessionStorageService( ? new LocalBackedSessionStorageService(
new EncryptService(this.cryptoFunctionService, this.logService, false), new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
new KeyGenerationService(this.cryptoFunctionService) new KeyGenerationService(this.cryptoFunctionService)
) )
: new MemoryStorageService(); : new MemoryStorageService();
@ -255,7 +258,13 @@ export default class MainBackground {
window window
); );
this.i18nService = new I18nService(BrowserApi.getUILanguage(window)); this.i18nService = new I18nService(BrowserApi.getUILanguage(window));
this.encryptService = new EncryptService(this.cryptoFunctionService, this.logService, true); this.encryptService = flagEnabled("multithreadDecryption")
? new MultithreadEncryptServiceImplementation(
this.cryptoFunctionService,
this.logService,
true
)
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
this.cryptoService = new BrowserCryptoService( this.cryptoService = new BrowserCryptoService(
this.cryptoFunctionService, this.cryptoFunctionService,
this.encryptService, this.encryptService,
@ -283,7 +292,8 @@ export default class MainBackground {
this.i18nService, this.i18nService,
() => this.searchService, () => this.searchService,
this.logService, this.logService,
this.stateService this.stateService,
this.encryptService
); );
this.folderService = new FolderService( this.folderService = new FolderService(
this.cryptoService, this.cryptoService,

View File

@ -4,6 +4,7 @@ import { CipherService } from "@bitwarden/common/services/cipher.service";
import { apiServiceFactory, ApiServiceInitOptions } from "./api-service.factory"; import { apiServiceFactory, ApiServiceInitOptions } from "./api-service.factory";
import { cryptoServiceFactory, CryptoServiceInitOptions } from "./crypto-service.factory"; import { cryptoServiceFactory, CryptoServiceInitOptions } from "./crypto-service.factory";
import { encryptServiceFactory, EncryptServiceInitOptions } from "./encrypt-service.factory";
import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { CachedServices, factory, FactoryOptions } from "./factory-options";
import { import {
FileUploadServiceInitOptions, FileUploadServiceInitOptions,
@ -27,7 +28,8 @@ export type CipherServiceInitOptions = CipherServiceFactoryOptions &
FileUploadServiceInitOptions & FileUploadServiceInitOptions &
I18nServiceInitOptions & I18nServiceInitOptions &
LogServiceInitOptions & LogServiceInitOptions &
StateServiceInitOptions; StateServiceInitOptions &
EncryptServiceInitOptions;
export function cipherServiceFactory( export function cipherServiceFactory(
cache: { cipherService?: AbstractCipherService } & CachedServices, cache: { cipherService?: AbstractCipherService } & CachedServices,
@ -48,7 +50,8 @@ export function cipherServiceFactory(
? () => cache.searchService ? () => cache.searchService
: opts.cipherServiceOptions.searchServiceFactory, : opts.cipherServiceOptions.searchServiceFactory,
await logServiceFactory(cache, opts), await logServiceFactory(cache, opts),
await stateServiceFactory(cache, opts) await stateServiceFactory(cache, opts),
await encryptServiceFactory(cache, opts)
) )
); );
} }

View File

@ -1,4 +1,7 @@
import { EncryptService } from "@bitwarden/common/services/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation";
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/services/cryptography/multithread-encrypt.service.implementation";
import { flagEnabled } from "../../flags";
import { import {
cryptoFunctionServiceFactory, cryptoFunctionServiceFactory,
@ -18,18 +21,20 @@ export type EncryptServiceInitOptions = EncryptServiceFactoryOptions &
LogServiceInitOptions; LogServiceInitOptions;
export function encryptServiceFactory( export function encryptServiceFactory(
cache: { encryptService?: EncryptService } & CachedServices, cache: { encryptService?: EncryptServiceImplementation } & CachedServices,
opts: EncryptServiceInitOptions opts: EncryptServiceInitOptions
): Promise<EncryptService> { ): Promise<EncryptServiceImplementation> {
return factory( return factory(cache, "encryptService", opts, async () =>
cache, flagEnabled("multithreadDecryption")
"encryptService", ? new MultithreadEncryptServiceImplementation(
opts, await cryptoFunctionServiceFactory(cache, opts),
async () => await logServiceFactory(cache, opts),
new EncryptService( opts.encryptServiceOptions.logMacFailures
await cryptoFunctionServiceFactory(cache, opts), )
await logServiceFactory(cache, opts), : new EncryptServiceImplementation(
opts.encryptServiceOptions.logMacFailures await cryptoFunctionServiceFactory(cache, opts),
) await logServiceFactory(cache, opts),
opts.encryptServiceOptions.logMacFailures
)
); );
} }

View File

@ -1,7 +1,7 @@
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { StateFactory } from "@bitwarden/common/factories/stateFactory";
import { Utils } from "@bitwarden/common/misc/utils"; import { Utils } from "@bitwarden/common/misc/utils";
@ -259,7 +259,7 @@ export class UpdateBadge {
if (!self.bitwardenContainerService) { if (!self.bitwardenContainerService) {
new ContainerService( new ContainerService(
serviceCache.cryptoService as CryptoService, serviceCache.cryptoService as CryptoService,
serviceCache.encryptService as AbstractEncryptService serviceCache.encryptService as EncryptService
).attachToGlobal(self); ).attachToGlobal(self);
} }

View File

@ -4,7 +4,7 @@ import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { Utils } from "@bitwarden/common/misc/utils"; import { Utils } from "@bitwarden/common/misc/utils";
import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { EncString } from "@bitwarden/common/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
import { EncryptService } from "@bitwarden/common/src/services/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation";
import BrowserLocalStorageService from "./browserLocalStorage.service"; import BrowserLocalStorageService from "./browserLocalStorage.service";
import BrowserMemoryStorageService from "./browserMemoryStorage.service"; import BrowserMemoryStorageService from "./browserMemoryStorage.service";
@ -12,7 +12,7 @@ import { KeyGenerationService } from "./keyGeneration.service";
import { LocalBackedSessionStorageService } from "./localBackedSessionStorage.service"; import { LocalBackedSessionStorageService } from "./localBackedSessionStorage.service";
describe("Browser Session Storage Service", () => { describe("Browser Session Storage Service", () => {
let encryptService: SubstituteOf<EncryptService>; let encryptService: SubstituteOf<EncryptServiceImplementation>;
let keyGenerationService: SubstituteOf<KeyGenerationService>; let keyGenerationService: SubstituteOf<KeyGenerationService>;
let cache: Map<string, any>; let cache: Map<string, any>;

View File

@ -1,6 +1,6 @@
import { Jsonify } from "type-fest"; import { Jsonify } from "type-fest";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service"; import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { import {
AbstractCachedStorageService, AbstractCachedStorageService,
MemoryStorageServiceInterface, MemoryStorageServiceInterface,
@ -30,7 +30,7 @@ export class LocalBackedSessionStorageService
private sessionStorage = new BrowserMemoryStorageService(); private sessionStorage = new BrowserMemoryStorageService();
constructor( constructor(
private encryptService: AbstractEncryptService, private encryptService: EncryptService,
private keyGenerationService: AbstractKeyGenerationService private keyGenerationService: AbstractKeyGenerationService
) { ) {
super(); super();

View File

@ -4,7 +4,7 @@
"noImplicitAny": true, "noImplicitAny": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"module": "es6", "module": "ES2020",
"target": "ES2016", "target": "ES2016",
"allowJs": true, "allowJs": true,
"sourceMap": true, "sourceMap": true,
@ -17,5 +17,5 @@
"angularCompilerOptions": { "angularCompilerOptions": {
"preserveWhitespaces": true "preserveWhitespaces": true
}, },
"include": ["src"] "include": ["src", "../../libs/common/src/services/**/*.worker.ts"]
} }

View File

@ -20,7 +20,7 @@ import { CipherService } from "@bitwarden/common/services/cipher.service";
import { CollectionService } from "@bitwarden/common/services/collection.service"; import { CollectionService } from "@bitwarden/common/services/collection.service";
import { ContainerService } from "@bitwarden/common/services/container.service"; import { ContainerService } from "@bitwarden/common/services/container.service";
import { CryptoService } from "@bitwarden/common/services/crypto.service"; import { CryptoService } from "@bitwarden/common/services/crypto.service";
import { EncryptService } from "@bitwarden/common/services/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation";
import { EnvironmentService } from "@bitwarden/common/services/environment.service"; import { EnvironmentService } from "@bitwarden/common/services/environment.service";
import { ExportService } from "@bitwarden/common/services/export.service"; import { ExportService } from "@bitwarden/common/services/export.service";
import { FileUploadService } from "@bitwarden/common/services/fileUpload.service"; import { FileUploadService } from "@bitwarden/common/services/fileUpload.service";
@ -94,7 +94,7 @@ export class Main {
exportService: ExportService; exportService: ExportService;
searchService: SearchService; searchService: SearchService;
cryptoFunctionService: NodeCryptoFunctionService; cryptoFunctionService: NodeCryptoFunctionService;
encryptService: EncryptService; encryptService: EncryptServiceImplementation;
authService: AuthService; authService: AuthService;
policyService: PolicyService; policyService: PolicyService;
program: Program; program: Program;
@ -140,7 +140,11 @@ export class Main {
(level) => process.env.BITWARDENCLI_DEBUG !== "true" && level <= LogLevelType.Info (level) => process.env.BITWARDENCLI_DEBUG !== "true" && level <= LogLevelType.Info
); );
this.cryptoFunctionService = new NodeCryptoFunctionService(); this.cryptoFunctionService = new NodeCryptoFunctionService();
this.encryptService = new EncryptService(this.cryptoFunctionService, this.logService, true); this.encryptService = new EncryptServiceImplementation(
this.cryptoFunctionService,
this.logService,
true
);
this.storageService = new LowdbStorageService(this.logService, null, p, false, true); this.storageService = new LowdbStorageService(this.logService, null, p, false, true);
this.secureStorageService = new NodeEnvSecureStorageService( this.secureStorageService = new NodeEnvSecureStorageService(
this.storageService, this.storageService,
@ -211,7 +215,8 @@ export class Main {
this.i18nService, this.i18nService,
null, null,
this.logService, this.logService,
this.stateService this.stateService,
this.encryptService
); );
this.broadcasterService = new BroadcasterService(); this.broadcasterService = new BroadcasterService();

View File

@ -1,4 +1,6 @@
{ {
"dev_flags": {}, "dev_flags": {},
"flags": {} "flags": {
"multithreadDecryption": false
}
} }

View File

@ -6,7 +6,7 @@ import { Utils } from "@bitwarden/common/misc/utils";
import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { EncString } from "@bitwarden/common/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
import { EncryptService } from "@bitwarden/common/services/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation";
import { NodeCryptoFunctionService } from "@bitwarden/node/services/nodeCryptoFunction.service"; import { NodeCryptoFunctionService } from "@bitwarden/node/services/nodeCryptoFunction.service";
import { DecryptedCommandData } from "../../src/models/nativeMessaging/decryptedCommandData"; import { DecryptedCommandData } from "../../src/models/nativeMessaging/decryptedCommandData";
@ -32,7 +32,7 @@ const CONFIRMATION_MESSAGE_TIMEOUT = 100 * 1000; // 100 seconds
export default class NativeMessageService { export default class NativeMessageService {
private ipcService: IPCService; private ipcService: IPCService;
private nodeCryptoFunctionService: NodeCryptoFunctionService; private nodeCryptoFunctionService: NodeCryptoFunctionService;
private encryptService: EncryptService; private encryptService: EncryptServiceImplementation;
constructor(private apiVersion: number) { constructor(private apiVersion: number) {
console.log("Starting native messaging service"); console.log("Starting native messaging service");
@ -41,7 +41,7 @@ export default class NativeMessageService {
}); });
this.nodeCryptoFunctionService = new NodeCryptoFunctionService(); this.nodeCryptoFunctionService = new NodeCryptoFunctionService();
this.encryptService = new EncryptService( this.encryptService = new EncryptServiceImplementation(
this.nodeCryptoFunctionService, this.nodeCryptoFunctionService,
new ConsoleLogService(false), new ConsoleLogService(false),
false false

View File

@ -2,8 +2,8 @@ import { Inject, Injectable } from "@angular/core";
import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction"; import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/abstractions/environment.service"; import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/abstractions/environment.service";
import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service"; import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
@ -36,7 +36,7 @@ export class InitService {
private cryptoService: CryptoServiceAbstraction, private cryptoService: CryptoServiceAbstraction,
private nativeMessagingService: NativeMessagingService, private nativeMessagingService: NativeMessagingService,
private themingService: AbstractThemingService, private themingService: AbstractThemingService,
private encryptService: AbstractEncryptService private encryptService: EncryptService
) {} ) {}
init() { init() {

View File

@ -11,12 +11,12 @@ import {
} from "@bitwarden/angular/services/injection-tokens"; } from "@bitwarden/angular/services/injection-tokens";
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction"; import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/abstractions/auth.service"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/abstractions/auth.service";
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/abstractions/broadcaster.service"; import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/abstractions/broadcaster.service";
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/abstractions/cipher.service"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/abstractions/cipher.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service"; import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
import { import {
@ -115,7 +115,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
useClass: ElectronCryptoService, useClass: ElectronCryptoService,
deps: [ deps: [
CryptoFunctionServiceAbstraction, CryptoFunctionServiceAbstraction,
AbstractEncryptService, EncryptService,
PlatformUtilsServiceAbstraction, PlatformUtilsServiceAbstraction,
LogServiceAbstraction, LogServiceAbstraction,
StateServiceAbstraction, StateServiceAbstraction,

View File

@ -4,7 +4,7 @@
"noImplicitAny": true, "noImplicitAny": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"module": "es6", "module": "ES2020",
"target": "ES2016", "target": "ES2016",
"sourceMap": true, "sourceMap": true,
"types": [], "types": [],
@ -18,5 +18,5 @@
"angularCompilerOptions": { "angularCompilerOptions": {
"preserveWhitespaces": true "preserveWhitespaces": true
}, },
"include": ["src"] "include": ["src", "../../libs/common/src/services/**/*.worker.ts"]
} }

View File

@ -6,7 +6,7 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { InfiniteScrollModule } from "ngx-infinite-scroll"; import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { AppComponent } from "./app.component"; import { AppComponent } from "./app.component";
import { CoreModule } from "./core"; import { CoreModule } from "./core/core.module";
import { OssRoutingModule } from "./oss-routing.module"; import { OssRoutingModule } from "./oss-routing.module";
import { OssModule } from "./oss.module"; import { OssModule } from "./oss.module";
import { WildcardRoutingModule } from "./wildcard-routing.module"; import { WildcardRoutingModule } from "./wildcard-routing.module";

View File

@ -1,4 +1,7 @@
export * from "./core.module"; // Do not export this here or it will import MultithreadEncryptService (via JslibServicesModule) into test code.
// MultithreadEncryptService contains ES2020 features (import.meta) which are not supported in Node and Jest.
// Revisit this when Node & Jest get stable support for ESM.
// export * from "./core.module";
export * from "./event.service"; export * from "./event.service";
export * from "./policy-list.service"; export * from "./policy-list.service";
export * from "./router.service"; export * from "./router.service";

View File

@ -2,8 +2,8 @@ import { Inject, Injectable } from "@angular/core";
import { WINDOW } from "@bitwarden/angular/services/injection-tokens"; import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction"; import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { import {
EnvironmentService as EnvironmentServiceAbstraction, EnvironmentService as EnvironmentServiceAbstraction,
Urls, Urls,
@ -33,7 +33,7 @@ export class InitService {
private stateService: StateServiceAbstraction, private stateService: StateServiceAbstraction,
private cryptoService: CryptoServiceAbstraction, private cryptoService: CryptoServiceAbstraction,
private themingService: AbstractThemingService, private themingService: AbstractThemingService,
private encryptService: AbstractEncryptService private encryptService: EncryptService
) {} ) {}
init() { init() {

View File

@ -3,7 +3,7 @@ import { APP_INITIALIZER, NgModule } from "@angular/core";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { I18nService as BaseI18nService } from "@bitwarden/common/services/i18n.service"; import { I18nService as BaseI18nService } from "@bitwarden/common/services/i18n.service";
import * as eng from "../../locales/en/messages.json"; import eng from "../../locales/en/messages.json";
class PreloadedEnglishI18nService extends BaseI18nService { class PreloadedEnglishI18nService extends BaseI18nService {
constructor() { constructor() {

View File

@ -15,5 +15,10 @@
"preserveWhitespaces": true "preserveWhitespaces": true
}, },
"files": ["src/polyfills.ts", "src/main.ts", "../../bitwarden_license/bit-web/src/main.ts"], "files": ["src/polyfills.ts", "src/main.ts", "../../bitwarden_license/bit-web/src/main.ts"],
"include": ["src/connectors/*.ts", "src/**/*.stories.ts", "src/**/*.spec.ts"] "include": [
"src/connectors/*.ts",
"src/**/*.stories.ts",
"src/**/*.spec.ts",
"../../libs/common/src/services/**/*.worker.ts"
]
} }

View File

@ -7,7 +7,7 @@ import { RouterModule } from "@angular/router";
import { InfiniteScrollModule } from "ngx-infinite-scroll"; import { InfiniteScrollModule } from "ngx-infinite-scroll";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { CoreModule } from "@bitwarden/web-vault/app/core"; import { CoreModule } from "@bitwarden/web-vault/app/core/core.module";
import { OssRoutingModule } from "@bitwarden/web-vault/app/oss-routing.module"; import { OssRoutingModule } from "@bitwarden/web-vault/app/oss-routing.module";
import { OssModule } from "@bitwarden/web-vault/app/oss.module"; import { OssModule } from "@bitwarden/web-vault/app/oss.module";
import { WildcardRoutingModule } from "@bitwarden/web-vault/app/wildcard-routing.module"; import { WildcardRoutingModule } from "@bitwarden/web-vault/app/wildcard-routing.module";

View File

@ -1,6 +1,5 @@
import { Injector, LOCALE_ID, NgModule } from "@angular/core"; import { Injector, LOCALE_ID, NgModule } from "@angular/core";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/abstractions/account/account-api.service"; import { AccountApiService as AccountApiServiceAbstraction } from "@bitwarden/common/abstractions/account/account-api.service";
import { import {
InternalAccountService, InternalAccountService,
@ -18,6 +17,7 @@ import { ConfigApiServiceAbstraction } from "@bitwarden/common/abstractions/conf
import { ConfigServiceAbstraction } from "@bitwarden/common/abstractions/config/config.service.abstraction"; import { ConfigServiceAbstraction } from "@bitwarden/common/abstractions/config/config.service.abstraction";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/abstractions/environment.service"; import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/abstractions/environment.service";
import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service"; import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service";
import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service"; import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service";
@ -62,6 +62,7 @@ import { ValidationService as ValidationServiceAbstraction } from "@bitwarden/co
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { StateFactory } from "@bitwarden/common/factories/stateFactory";
import { flagEnabled } from "@bitwarden/common/misc/flags";
import { Account } from "@bitwarden/common/models/domain/account"; import { Account } from "@bitwarden/common/models/domain/account";
import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { GlobalState } from "@bitwarden/common/models/domain/global-state";
import { AccountApiServiceImplementation } from "@bitwarden/common/services/account/account-api.service"; import { AccountApiServiceImplementation } from "@bitwarden/common/services/account/account-api.service";
@ -77,7 +78,8 @@ import { ConfigApiService } from "@bitwarden/common/services/config/config-api.s
import { ConfigService } from "@bitwarden/common/services/config/config.service"; import { ConfigService } from "@bitwarden/common/services/config/config.service";
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
import { CryptoService } from "@bitwarden/common/services/crypto.service"; import { CryptoService } from "@bitwarden/common/services/crypto.service";
import { EncryptService } from "@bitwarden/common/services/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation";
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/services/cryptography/multithread-encrypt.service.implementation";
import { EnvironmentService } from "@bitwarden/common/services/environment.service"; import { EnvironmentService } from "@bitwarden/common/services/environment.service";
import { EventService } from "@bitwarden/common/services/event.service"; import { EventService } from "@bitwarden/common/services/event.service";
import { ExportService } from "@bitwarden/common/services/export.service"; import { ExportService } from "@bitwarden/common/services/export.service";
@ -216,7 +218,8 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
i18nService: I18nServiceAbstraction, i18nService: I18nServiceAbstraction,
injector: Injector, injector: Injector,
logService: LogService, logService: LogService,
stateService: StateServiceAbstraction stateService: StateServiceAbstraction,
encryptService: EncryptService
) => ) =>
new CipherService( new CipherService(
cryptoService, cryptoService,
@ -226,7 +229,8 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
i18nService, i18nService,
() => injector.get(SearchServiceAbstraction), () => injector.get(SearchServiceAbstraction),
logService, logService,
stateService stateService,
encryptService
), ),
deps: [ deps: [
CryptoServiceAbstraction, CryptoServiceAbstraction,
@ -237,6 +241,7 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
Injector, // TODO: Get rid of this circular dependency! Injector, // TODO: Get rid of this circular dependency!
LogService, LogService,
StateServiceAbstraction, StateServiceAbstraction,
EncryptService,
], ],
}, },
{ {
@ -299,7 +304,7 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
useClass: CryptoService, useClass: CryptoService,
deps: [ deps: [
CryptoFunctionServiceAbstraction, CryptoFunctionServiceAbstraction,
AbstractEncryptService, EncryptService,
PlatformUtilsServiceAbstraction, PlatformUtilsServiceAbstraction,
LogService, LogService,
StateServiceAbstraction, StateServiceAbstraction,
@ -442,8 +447,8 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
deps: [WINDOW], deps: [WINDOW],
}, },
{ {
provide: AbstractEncryptService, provide: EncryptService,
useClass: EncryptService, useFactory: encryptServiceFactory,
deps: [CryptoFunctionServiceAbstraction, LogService, LOG_MAC_FAILURES], deps: [CryptoFunctionServiceAbstraction, LogService, LOG_MAC_FAILURES],
}, },
{ {
@ -576,3 +581,13 @@ import { AbstractThemingService } from "./theming/theming.service.abstraction";
], ],
}) })
export class JslibServicesModule {} export class JslibServicesModule {}
function encryptServiceFactory(
cryptoFunctionservice: CryptoFunctionServiceAbstraction,
logService: LogService,
logMacFailures: boolean
): EncryptService {
return flagEnabled("multithreadDecryption")
? new MultithreadEncryptServiceImplementation(cryptoFunctionservice, logService, logMacFailures)
: new EncryptServiceImplementation(cryptoFunctionservice, logService, logMacFailures);
}

View File

@ -1,7 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { AttachmentData } from "@bitwarden/common/models/data/attachment.data"; import { AttachmentData } from "@bitwarden/common/models/data/attachment.data";
import { Attachment } from "@bitwarden/common/models/domain/attachment"; import { Attachment } from "@bitwarden/common/models/domain/attachment";
import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { EncString } from "@bitwarden/common/models/domain/enc-string";
@ -58,11 +58,11 @@ describe("Attachment", () => {
describe("decrypt", () => { describe("decrypt", () => {
let cryptoService: MockProxy<CryptoService>; let cryptoService: MockProxy<CryptoService>;
let encryptService: MockProxy<AbstractEncryptService>; let encryptService: MockProxy<EncryptService>;
beforeEach(() => { beforeEach(() => {
cryptoService = mock<CryptoService>(); cryptoService = mock<CryptoService>();
encryptService = mock<AbstractEncryptService>(); encryptService = mock<EncryptService>();
(window as any).bitwardenContainerService = new ContainerService( (window as any).bitwardenContainerService = new ContainerService(
cryptoService, cryptoService,

View File

@ -20,6 +20,7 @@ import { SecureNote } from "@bitwarden/common/models/domain/secure-note";
import { CardView } from "@bitwarden/common/models/view/card.view"; import { CardView } from "@bitwarden/common/models/view/card.view";
import { IdentityView } from "@bitwarden/common/models/view/identity.view"; import { IdentityView } from "@bitwarden/common/models/view/identity.view";
import { LoginView } from "@bitwarden/common/models/view/login.view"; import { LoginView } from "@bitwarden/common/models/view/login.view";
import { InitializerKey } from "@bitwarden/common/services/cryptography/initializer-key";
import { mockEnc, mockFromJson } from "../../utils"; import { mockEnc, mockFromJson } from "../../utils";
@ -29,6 +30,7 @@ describe("Cipher DTO", () => {
const cipher = new Cipher(data); const cipher = new Cipher(data);
expect(cipher).toEqual({ expect(cipher).toEqual({
initializerKey: InitializerKey.Cipher,
id: null, id: null,
organizationId: null, organizationId: null,
folderId: null, folderId: null,
@ -120,6 +122,7 @@ describe("Cipher DTO", () => {
const cipher = new Cipher(cipherData); const cipher = new Cipher(cipherData);
expect(cipher).toEqual({ expect(cipher).toEqual({
initializerKey: InitializerKey.Cipher,
id: "id", id: "id",
organizationId: "orgId", organizationId: "orgId",
folderId: "folderId", folderId: "folderId",
@ -271,6 +274,7 @@ describe("Cipher DTO", () => {
const cipher = new Cipher(cipherData); const cipher = new Cipher(cipherData);
expect(cipher).toEqual({ expect(cipher).toEqual({
initializerKey: InitializerKey.Cipher,
id: "id", id: "id",
organizationId: "orgId", organizationId: "orgId",
folderId: "folderId", folderId: "folderId",
@ -379,6 +383,7 @@ describe("Cipher DTO", () => {
const cipher = new Cipher(cipherData); const cipher = new Cipher(cipherData);
expect(cipher).toEqual({ expect(cipher).toEqual({
initializerKey: InitializerKey.Cipher,
id: "id", id: "id",
organizationId: "orgId", organizationId: "orgId",
folderId: "folderId", folderId: "folderId",
@ -512,6 +517,7 @@ describe("Cipher DTO", () => {
const cipher = new Cipher(cipherData); const cipher = new Cipher(cipherData);
expect(cipher).toEqual({ expect(cipher).toEqual({
initializerKey: InitializerKey.Cipher,
id: "id", id: "id",
organizationId: "orgId", organizationId: "orgId",
folderId: "folderId", folderId: "folderId",

View File

@ -2,8 +2,8 @@
import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { Substitute, Arg } from "@fluffy-spoon/substitute";
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { EncryptionType } from "@bitwarden/common/enums/encryptionType"; import { EncryptionType } from "@bitwarden/common/enums/encryptionType";
import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { EncString } from "@bitwarden/common/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
@ -52,7 +52,7 @@ describe("EncString", () => {
const cryptoService = Substitute.for<CryptoService>(); const cryptoService = Substitute.for<CryptoService>();
cryptoService.getOrgKey(null).resolves(null); cryptoService.getOrgKey(null).resolves(null);
const encryptService = Substitute.for<AbstractEncryptService>(); const encryptService = Substitute.for<EncryptService>();
encryptService.decryptToUtf8(encString, Arg.any()).resolves("decrypted"); encryptService.decryptToUtf8(encString, Arg.any()).resolves("decrypted");
beforeEach(() => { beforeEach(() => {
@ -157,12 +157,12 @@ describe("EncString", () => {
describe("decrypt", () => { describe("decrypt", () => {
let cryptoService: MockProxy<CryptoService>; let cryptoService: MockProxy<CryptoService>;
let encryptService: MockProxy<AbstractEncryptService>; let encryptService: MockProxy<EncryptService>;
let encString: EncString; let encString: EncString;
beforeEach(() => { beforeEach(() => {
cryptoService = mock<CryptoService>(); cryptoService = mock<CryptoService>();
encryptService = mock<AbstractEncryptService>(); encryptService = mock<EncryptService>();
encString = new EncString(null); encString = new EncString(null);
(window as any).bitwardenContainerService = new ContainerService( (window as any).bitwardenContainerService = new ContainerService(

View File

@ -1,8 +1,8 @@
// eslint-disable-next-line no-restricted-imports // eslint-disable-next-line no-restricted-imports
import { Substitute, Arg, SubstituteOf } from "@fluffy-spoon/substitute"; import { Substitute, Arg, SubstituteOf } from "@fluffy-spoon/substitute";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { SendType } from "@bitwarden/common/enums/sendType"; import { SendType } from "@bitwarden/common/enums/sendType";
import { SendData } from "@bitwarden/common/models/data/send.data"; import { SendData } from "@bitwarden/common/models/data/send.data";
import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { EncString } from "@bitwarden/common/models/domain/enc-string";
@ -112,7 +112,7 @@ describe("Send", () => {
cryptoService.decryptToBytes(send.key, null).resolves(makeStaticByteArray(32)); cryptoService.decryptToBytes(send.key, null).resolves(makeStaticByteArray(32));
cryptoService.makeSendKey(Arg.any()).resolves("cryptoKey" as any); cryptoService.makeSendKey(Arg.any()).resolves("cryptoKey" as any);
const encryptService = Substitute.for<AbstractEncryptService>(); const encryptService = Substitute.for<EncryptService>();
(window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService); (window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService);

View File

@ -3,6 +3,7 @@ import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { FileUploadService } from "@bitwarden/common/abstractions/fileUpload.service"; import { FileUploadService } from "@bitwarden/common/abstractions/fileUpload.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service"; import { LogService } from "@bitwarden/common/abstractions/log.service";
@ -27,6 +28,7 @@ describe("Cipher Service", () => {
let i18nService: SubstituteOf<I18nService>; let i18nService: SubstituteOf<I18nService>;
let searchService: SubstituteOf<SearchService>; let searchService: SubstituteOf<SearchService>;
let logService: SubstituteOf<LogService>; let logService: SubstituteOf<LogService>;
let encryptService: SubstituteOf<EncryptService>;
let cipherService: CipherService; let cipherService: CipherService;
@ -39,6 +41,7 @@ describe("Cipher Service", () => {
i18nService = Substitute.for<I18nService>(); i18nService = Substitute.for<I18nService>();
searchService = Substitute.for<SearchService>(); searchService = Substitute.for<SearchService>();
logService = Substitute.for<LogService>(); logService = Substitute.for<LogService>();
encryptService = Substitute.for<EncryptService>();
cryptoService.encryptToBytes(Arg.any(), Arg.any()).resolves(ENCRYPTED_BYTES); cryptoService.encryptToBytes(Arg.any(), Arg.any()).resolves(ENCRYPTED_BYTES);
cryptoService.encrypt(Arg.any(), Arg.any()).resolves(new EncString(ENCRYPTED_TEXT)); cryptoService.encrypt(Arg.any(), Arg.any()).resolves(new EncString(ENCRYPTED_TEXT));
@ -51,7 +54,8 @@ describe("Cipher Service", () => {
i18nService, i18nService,
() => searchService, () => searchService,
logService, logService,
stateService stateService,
encryptService
); );
}); });

View File

@ -1,7 +1,7 @@
import { mock, mockReset } from "jest-mock-extended"; import { mock, mockReset } from "jest-mock-extended";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/abstractions/log.service"; import { LogService } from "@bitwarden/common/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { StateService } from "@bitwarden/common/abstractions/state.service"; import { StateService } from "@bitwarden/common/abstractions/state.service";
@ -11,7 +11,7 @@ describe("cryptoService", () => {
let cryptoService: CryptoService; let cryptoService: CryptoService;
const cryptoFunctionService = mock<CryptoFunctionService>(); const cryptoFunctionService = mock<CryptoFunctionService>();
const encryptService = mock<AbstractEncryptService>(); const encryptService = mock<EncryptService>();
const platformUtilService = mock<PlatformUtilsService>(); const platformUtilService = mock<PlatformUtilsService>();
const logService = mock<LogService>(); const logService = mock<LogService>();
const stateService = mock<StateService>(); const stateService = mock<StateService>();

View File

@ -6,7 +6,7 @@ import { EncryptionType } from "@bitwarden/common/enums/encryptionType";
import { EncArrayBuffer } from "@bitwarden/common/models/domain/enc-array-buffer"; import { EncArrayBuffer } from "@bitwarden/common/models/domain/enc-array-buffer";
import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { EncString } from "@bitwarden/common/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
import { EncryptService } from "@bitwarden/common/services/encrypt.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation";
import { makeStaticByteArray } from "../utils"; import { makeStaticByteArray } from "../utils";
@ -14,13 +14,13 @@ describe("EncryptService", () => {
const cryptoFunctionService = mock<CryptoFunctionService>(); const cryptoFunctionService = mock<CryptoFunctionService>();
const logService = mock<LogService>(); const logService = mock<LogService>();
let encryptService: EncryptService; let encryptService: EncryptServiceImplementation;
beforeEach(() => { beforeEach(() => {
mockReset(cryptoFunctionService); mockReset(cryptoFunctionService);
mockReset(logService); mockReset(logService);
encryptService = new EncryptService(cryptoFunctionService, logService, true); encryptService = new EncryptServiceImplementation(cryptoFunctionService, logService, true);
}); });
describe("encryptToBytes", () => { describe("encryptToBytes", () => {

View File

@ -2,9 +2,9 @@
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { BehaviorSubject, firstValueFrom } from "rxjs"; import { BehaviorSubject, firstValueFrom } from "rxjs";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { FolderData } from "@bitwarden/common/models/data/folder.data"; import { FolderData } from "@bitwarden/common/models/data/folder.data";
import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { EncString } from "@bitwarden/common/models/domain/enc-string";
@ -17,7 +17,7 @@ describe("Folder Service", () => {
let folderService: FolderService; let folderService: FolderService;
let cryptoService: SubstituteOf<CryptoService>; let cryptoService: SubstituteOf<CryptoService>;
let encryptService: SubstituteOf<AbstractEncryptService>; let encryptService: SubstituteOf<EncryptService>;
let i18nService: SubstituteOf<I18nService>; let i18nService: SubstituteOf<I18nService>;
let cipherService: SubstituteOf<CipherService>; let cipherService: SubstituteOf<CipherService>;
let stateService: SubstituteOf<StateService>; let stateService: SubstituteOf<StateService>;

View File

@ -3,6 +3,7 @@ import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { BehaviorSubject, firstValueFrom } from "rxjs"; import { BehaviorSubject, firstValueFrom } from "rxjs";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType"; import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType";
import { PolicyType } from "@bitwarden/common/enums/policyType"; import { PolicyType } from "@bitwarden/common/enums/policyType";
@ -16,7 +17,6 @@ import { ResetPasswordPolicyOptions } from "@bitwarden/common/models/domain/rese
import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { PolicyResponse } from "@bitwarden/common/models/response/policy.response"; import { PolicyResponse } from "@bitwarden/common/models/response/policy.response";
import { ContainerService } from "@bitwarden/common/services/container.service"; import { ContainerService } from "@bitwarden/common/services/container.service";
import { EncryptService } from "@bitwarden/common/services/encrypt.service";
import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; import { PolicyService } from "@bitwarden/common/services/policy/policy.service";
import { StateService } from "@bitwarden/common/services/state.service"; import { StateService } from "@bitwarden/common/services/state.service";

View File

@ -2,8 +2,8 @@
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";
import { BehaviorSubject, firstValueFrom } from "rxjs"; import { BehaviorSubject, firstValueFrom } from "rxjs";
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { ContainerService } from "@bitwarden/common/services/container.service"; import { ContainerService } from "@bitwarden/common/services/container.service";
import { SettingsService } from "@bitwarden/common/services/settings.service"; import { SettingsService } from "@bitwarden/common/services/settings.service";
import { StateService } from "@bitwarden/common/services/state.service"; import { StateService } from "@bitwarden/common/services/state.service";
@ -12,7 +12,7 @@ describe("SettingsService", () => {
let settingsService: SettingsService; let settingsService: SettingsService;
let cryptoService: SubstituteOf<CryptoService>; let cryptoService: SubstituteOf<CryptoService>;
let encryptService: SubstituteOf<AbstractEncryptService>; let encryptService: SubstituteOf<EncryptService>;
let stateService: SubstituteOf<StateService>; let stateService: SubstituteOf<StateService>;
let activeAccount: BehaviorSubject<string>; let activeAccount: BehaviorSubject<string>;
let activeAccountUnlocked: BehaviorSubject<boolean>; let activeAccountUnlocked: BehaviorSubject<boolean>;

View File

@ -1,9 +1,11 @@
import { IEncrypted } from "../interfaces/IEncrypted"; import { IEncrypted } from "../interfaces/IEncrypted";
import { Decryptable } from "../interfaces/decryptable.interface";
import { InitializerMetadata } from "../interfaces/initializer-metadata.interface";
import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
import { EncString } from "../models/domain/enc-string"; import { EncString } from "../models/domain/enc-string";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
export abstract class AbstractEncryptService { export abstract class EncryptService {
abstract encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString>; abstract encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString>;
abstract encryptToBytes: ( abstract encryptToBytes: (
plainValue: ArrayBuffer, plainValue: ArrayBuffer,
@ -12,4 +14,8 @@ export abstract class AbstractEncryptService {
abstract decryptToUtf8: (encString: EncString, key: SymmetricCryptoKey) => Promise<string>; abstract decryptToUtf8: (encString: EncString, key: SymmetricCryptoKey) => Promise<string>;
abstract decryptToBytes: (encThing: IEncrypted, key: SymmetricCryptoKey) => Promise<ArrayBuffer>; abstract decryptToBytes: (encThing: IEncrypted, key: SymmetricCryptoKey) => Promise<ArrayBuffer>;
abstract resolveLegacyKey: (key: SymmetricCryptoKey, encThing: IEncrypted) => SymmetricCryptoKey; abstract resolveLegacyKey: (key: SymmetricCryptoKey, encThing: IEncrypted) => SymmetricCryptoKey;
abstract decryptItems: <T extends InitializerMetadata>(
items: Decryptable<T>[],
key: SymmetricCryptoKey
) => Promise<T[]>;
} }

View File

@ -0,0 +1,12 @@
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
import { InitializerMetadata } from "./initializer-metadata.interface";
/**
* An object that contains EncStrings and knows how to decrypt them. This is usually a domain object with the
* corresponding view object as the type argument.
* @example Cipher implements Decryptable<CipherView>
*/
export interface Decryptable<TDecrypted extends InitializerMetadata> extends InitializerMetadata {
decrypt: (key?: SymmetricCryptoKey) => Promise<TDecrypted>;
}

View File

@ -0,0 +1,11 @@
import { InitializerKey } from "../services/cryptography/initializer-key";
/**
* This interface enables deserialization of arbitrary objects by recording their class name as an enum, which
* will survive serialization. The enum can then be matched to a constructor or factory method for deserialization.
* See get-class-initializer.ts for the initializer map.
*/
export interface InitializerMetadata {
initializerKey: InitializerKey;
toJSON?: () => { initializerKey: InitializerKey };
}

View File

@ -1,6 +1,8 @@
// required to avoid linting errors when there are no flags // required to avoid linting errors when there are no flags
/* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable @typescript-eslint/ban-types */
export type SharedFlags = {}; export type SharedFlags = {
multithreadDecryption: boolean;
};
// required to avoid linting errors when there are no flags // required to avoid linting errors when there are no flags
/* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable @typescript-eslint/ban-types */

View File

@ -1,8 +1,8 @@
/* eslint-disable no-useless-escape */ /* eslint-disable no-useless-escape */
import { getHostname, parse } from "tldts"; import { getHostname, parse } from "tldts";
import { AbstractEncryptService } from "../abstractions/abstractEncrypt.service";
import { CryptoService } from "../abstractions/crypto.service"; import { CryptoService } from "../abstractions/crypto.service";
import { EncryptService } from "../abstractions/encrypt.service";
import { I18nService } from "../abstractions/i18n.service"; import { I18nService } from "../abstractions/i18n.service";
const nodeURL = typeof window === "undefined" ? require("url") : null; const nodeURL = typeof window === "undefined" ? require("url") : null;
@ -14,7 +14,7 @@ declare global {
interface BitwardenContainerService { interface BitwardenContainerService {
getCryptoService: () => CryptoService; getCryptoService: () => CryptoService;
getEncryptService: () => AbstractEncryptService; getEncryptService: () => EncryptService;
} }
export class Utils { export class Utils {

View File

@ -2,6 +2,8 @@ import { Jsonify } from "type-fest";
import { CipherRepromptType } from "../../enums/cipherRepromptType"; import { CipherRepromptType } from "../../enums/cipherRepromptType";
import { CipherType } from "../../enums/cipherType"; import { CipherType } from "../../enums/cipherType";
import { Decryptable } from "../../interfaces/decryptable.interface";
import { InitializerKey } from "../../services/cryptography/initializer-key";
import { CipherData } from "../data/cipher.data"; import { CipherData } from "../data/cipher.data";
import { LocalData } from "../data/local.data"; import { LocalData } from "../data/local.data";
import { CipherView } from "../view/cipher.view"; import { CipherView } from "../view/cipher.view";
@ -17,7 +19,9 @@ import { Password } from "./password";
import { SecureNote } from "./secure-note"; import { SecureNote } from "./secure-note";
import { SymmetricCryptoKey } from "./symmetric-crypto-key"; import { SymmetricCryptoKey } from "./symmetric-crypto-key";
export class Cipher extends Domain { export class Cipher extends Domain implements Decryptable<CipherView> {
readonly initializerKey = InitializerKey.Cipher;
id: string; id: string;
organizationId: string; organizationId: string;
folderId: string; folderId: string;

View File

@ -3,6 +3,8 @@ import { Jsonify } from "type-fest";
import { CipherRepromptType } from "../../enums/cipherRepromptType"; import { CipherRepromptType } from "../../enums/cipherRepromptType";
import { CipherType } from "../../enums/cipherType"; import { CipherType } from "../../enums/cipherType";
import { LinkedIdType } from "../../enums/linkedIdType"; import { LinkedIdType } from "../../enums/linkedIdType";
import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface";
import { InitializerKey } from "../../services/cryptography/initializer-key";
import { LocalData } from "../data/local.data"; import { LocalData } from "../data/local.data";
import { Cipher } from "../domain/cipher"; import { Cipher } from "../domain/cipher";
@ -15,7 +17,9 @@ import { PasswordHistoryView } from "./password-history.view";
import { SecureNoteView } from "./secure-note.view"; import { SecureNoteView } from "./secure-note.view";
import { View } from "./view"; import { View } from "./view";
export class CipherView implements View { export class CipherView implements View, InitializerMetadata {
readonly initializerKey = InitializerKey.CipherView;
id: string = null; id: string = null;
organizationId: string = null; organizationId: string = null;
folderId: string = null; folderId: string = null;

View File

@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs";
import { ApiService } from "../abstractions/api.service"; import { ApiService } from "../abstractions/api.service";
import { CipherService as CipherServiceAbstraction } from "../abstractions/cipher.service"; import { CipherService as CipherServiceAbstraction } from "../abstractions/cipher.service";
import { CryptoService } from "../abstractions/crypto.service"; import { CryptoService } from "../abstractions/crypto.service";
import { EncryptService } from "../abstractions/encrypt.service";
import { FileUploadService } from "../abstractions/fileUpload.service"; import { FileUploadService } from "../abstractions/fileUpload.service";
import { I18nService } from "../abstractions/i18n.service"; import { I18nService } from "../abstractions/i18n.service";
import { LogService } from "../abstractions/log.service"; import { LogService } from "../abstractions/log.service";
@ -65,7 +66,8 @@ export class CipherService implements CipherServiceAbstraction {
private i18nService: I18nService, private i18nService: I18nService,
private searchService: () => SearchService, private searchService: () => SearchService,
private logService: LogService, private logService: LogService,
private stateService: StateService private stateService: StateService,
private encryptService: EncryptService
) {} ) {}
async getDecryptedCipherCache(): Promise<CipherView[]> { async getDecryptedCipherCache(): Promise<CipherView[]> {
@ -329,35 +331,50 @@ export class CipherService implements CipherServiceAbstraction {
@sequentialize(() => "getAllDecrypted") @sequentialize(() => "getAllDecrypted")
async getAllDecrypted(): Promise<CipherView[]> { async getAllDecrypted(): Promise<CipherView[]> {
const userId = await this.stateService.getUserId();
if ((await this.getDecryptedCipherCache()) != null) { if ((await this.getDecryptedCipherCache()) != null) {
if ( await this.reindexCiphers();
this.searchService != null &&
(this.searchService().indexedEntityId ?? userId) !== userId
) {
await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache());
}
return await this.getDecryptedCipherCache(); return await this.getDecryptedCipherCache();
} }
const decCiphers: CipherView[] = [];
const hasKey = await this.cryptoService.hasKey(); const hasKey = await this.cryptoService.hasKey();
if (!hasKey) { if (!hasKey) {
throw new Error("No key."); throw new Error("No key.");
} }
const promises: Promise<number>[] = [];
const ciphers = await this.getAll(); const ciphers = await this.getAll();
ciphers.forEach(async (cipher) => { const orgKeys = await this.cryptoService.getOrgKeys();
promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); const userKey = await this.cryptoService.getKeyForUserEncryption();
});
// Group ciphers by orgId or under 'null' for the user's ciphers
const grouped = ciphers.reduce((agg, c) => {
agg[c.organizationId] ??= [];
agg[c.organizationId].push(c);
return agg;
}, {} as Record<string, Cipher[]>);
const decCiphers = (
await Promise.all(
Object.entries(grouped).map(([orgId, groupedCiphers]) =>
this.encryptService.decryptItems(groupedCiphers, orgKeys.get(orgId) ?? userKey)
)
)
)
.flat()
.sort(this.getLocaleSortingFunction());
await Promise.all(promises);
decCiphers.sort(this.getLocaleSortingFunction());
await this.setDecryptedCipherCache(decCiphers); await this.setDecryptedCipherCache(decCiphers);
return decCiphers; return decCiphers;
} }
private async reindexCiphers() {
const userId = await this.stateService.getUserId();
const reindexRequired =
this.searchService != null && (this.searchService().indexedEntityId ?? userId) !== userId;
if (reindexRequired) {
await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache());
}
}
async getAllDecryptedForGrouping(groupingId: string, folder = true): Promise<CipherView[]> { async getAllDecryptedForGrouping(groupingId: string, folder = true): Promise<CipherView[]> {
const ciphers = await this.getAllDecrypted(); const ciphers = await this.getAllDecrypted();
@ -488,21 +505,17 @@ export class CipherService implements CipherServiceAbstraction {
} }
async getAllFromApiForOrganization(organizationId: string): Promise<CipherView[]> { async getAllFromApiForOrganization(organizationId: string): Promise<CipherView[]> {
const ciphers = await this.apiService.getCiphersOrganization(organizationId); const response = await this.apiService.getCiphersOrganization(organizationId);
if (ciphers != null && ciphers.data != null && ciphers.data.length) { if (response?.data == null || response.data.length < 1) {
const decCiphers: CipherView[] = [];
const promises: any[] = [];
ciphers.data.forEach((r) => {
const data = new CipherData(r);
const cipher = new Cipher(data);
promises.push(cipher.decrypt().then((c) => decCiphers.push(c)));
});
await Promise.all(promises);
decCiphers.sort(this.getLocaleSortingFunction());
return decCiphers;
} else {
return []; return [];
} }
const ciphers = response.data.map((cr) => new Cipher(new CipherData(cr)));
const key = await this.cryptoService.getOrgKey(organizationId);
const decCiphers = await this.encryptService.decryptItems(ciphers, key);
decCiphers.sort(this.getLocaleSortingFunction());
return decCiphers;
} }
async getLastUsedForUrl(url: string, autofillOnPageLoad = false): Promise<CipherView> { async getLastUsedForUrl(url: string, autofillOnPageLoad = false): Promise<CipherView> {

View File

@ -1,11 +1,8 @@
import { AbstractEncryptService } from "../abstractions/abstractEncrypt.service";
import { CryptoService } from "../abstractions/crypto.service"; import { CryptoService } from "../abstractions/crypto.service";
import { EncryptService } from "../abstractions/encrypt.service";
export class ContainerService { export class ContainerService {
constructor( constructor(private cryptoService: CryptoService, private encryptService: EncryptService) {}
private cryptoService: CryptoService,
private encryptService: AbstractEncryptService
) {}
attachToGlobal(global: any) { attachToGlobal(global: any) {
if (!global.bitwardenContainerService) { if (!global.bitwardenContainerService) {
@ -26,7 +23,7 @@ export class ContainerService {
/** /**
* @throws Will throw if EncryptService was not instantiated and provided to the ContainerService constructor * @throws Will throw if EncryptService was not instantiated and provided to the ContainerService constructor
*/ */
getEncryptService(): AbstractEncryptService { getEncryptService(): EncryptService {
if (this.encryptService == null) { if (this.encryptService == null) {
throw new Error("ContainerService.encryptService not initialized."); throw new Error("ContainerService.encryptService not initialized.");
} }

View File

@ -1,8 +1,8 @@
import * as bigInt from "big-integer"; import * as bigInt from "big-integer";
import { AbstractEncryptService } from "../abstractions/abstractEncrypt.service";
import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service"; import { CryptoService as CryptoServiceAbstraction } from "../abstractions/crypto.service";
import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; import { CryptoFunctionService } from "../abstractions/cryptoFunction.service";
import { EncryptService } from "../abstractions/encrypt.service";
import { LogService } from "../abstractions/log.service"; import { LogService } from "../abstractions/log.service";
import { PlatformUtilsService } from "../abstractions/platformUtils.service"; import { PlatformUtilsService } from "../abstractions/platformUtils.service";
import { StateService } from "../abstractions/state.service"; import { StateService } from "../abstractions/state.service";
@ -25,7 +25,7 @@ import { ProfileProviderResponse } from "../models/response/profile-provider.res
export class CryptoService implements CryptoServiceAbstraction { export class CryptoService implements CryptoServiceAbstraction {
constructor( constructor(
private cryptoFunctionService: CryptoFunctionService, private cryptoFunctionService: CryptoFunctionService,
private encryptService: AbstractEncryptService, private encryptService: EncryptService,
protected platformUtilService: PlatformUtilsService, protected platformUtilService: PlatformUtilsService,
protected logService: LogService, protected logService: LogService,
protected stateService: StateService protected stateService: StateService

View File

@ -1,19 +1,21 @@
import { AbstractEncryptService } from "../abstractions/abstractEncrypt.service"; import { CryptoFunctionService } from "../../abstractions/cryptoFunction.service";
import { CryptoFunctionService } from "../abstractions/cryptoFunction.service"; import { EncryptService } from "../../abstractions/encrypt.service";
import { LogService } from "../abstractions/log.service"; import { LogService } from "../../abstractions/log.service";
import { EncryptionType } from "../enums/encryptionType"; import { EncryptionType } from "../../enums/encryptionType";
import { IEncrypted } from "../interfaces/IEncrypted"; import { IEncrypted } from "../../interfaces/IEncrypted";
import { Utils } from "../misc/utils"; import { Decryptable } from "../../interfaces/decryptable.interface";
import { EncArrayBuffer } from "../models/domain/enc-array-buffer"; import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface";
import { EncString } from "../models/domain/enc-string"; import { Utils } from "../../misc/utils";
import { EncryptedObject } from "../models/domain/encrypted-object"; import { EncArrayBuffer } from "../../models/domain/enc-array-buffer";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key"; import { EncString } from "../../models/domain/enc-string";
import { EncryptedObject } from "../../models/domain/encrypted-object";
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
export class EncryptService implements AbstractEncryptService { export class EncryptServiceImplementation implements EncryptService {
constructor( constructor(
private cryptoFunctionService: CryptoFunctionService, protected cryptoFunctionService: CryptoFunctionService,
private logService: LogService, protected logService: LogService,
private logMacFailures: boolean protected logMacFailures: boolean
) {} ) {}
async encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString> { async encrypt(plainValue: string | ArrayBuffer, key: SymmetricCryptoKey): Promise<EncString> {
@ -148,6 +150,17 @@ export class EncryptService implements AbstractEncryptService {
return result ?? null; return result ?? null;
} }
async decryptItems<T extends InitializerMetadata>(
items: Decryptable<T>[],
key: SymmetricCryptoKey
): Promise<T[]> {
if (items == null || items.length < 1) {
return [];
}
return await Promise.all(items.map((item) => item.decrypt(key)));
}
private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> { private async aesEncrypt(data: ArrayBuffer, key: SymmetricCryptoKey): Promise<EncryptedObject> {
const obj = new EncryptedObject(); const obj = new EncryptedObject();
obj.key = key; obj.key = key;

View File

@ -0,0 +1,56 @@
import { Jsonify } from "type-fest";
import { Decryptable } from "../../interfaces/decryptable.interface";
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
import { ConsoleLogService } from "../../services/consoleLog.service";
import { ContainerService } from "../../services/container.service";
import { WebCryptoFunctionService } from "../../services/webCryptoFunction.service";
import { EncryptServiceImplementation } from "./encrypt.service.implementation";
import { getClassInitializer } from "./get-class-initializer";
const workerApi: Worker = self as any;
let inited = false;
let encryptService: EncryptServiceImplementation;
/**
* Bootstrap the worker environment with services required for decryption
*/
export function init() {
const cryptoFunctionService = new WebCryptoFunctionService(self);
const logService = new ConsoleLogService(false);
encryptService = new EncryptServiceImplementation(cryptoFunctionService, logService, true);
const bitwardenContainerService = new ContainerService(null, encryptService);
bitwardenContainerService.attachToGlobal(self);
inited = true;
}
/**
* Listen for messages and decrypt their contents
*/
workerApi.addEventListener("message", async (event: { data: string }) => {
if (!inited) {
init();
}
const request: {
id: string;
items: Jsonify<Decryptable<any>>[];
key: Jsonify<SymmetricCryptoKey>;
} = JSON.parse(event.data);
const key = SymmetricCryptoKey.fromJSON(request.key);
const items = request.items.map((jsonItem) => {
const initializer = getClassInitializer<Decryptable<any>>(jsonItem.initializerKey);
return initializer(jsonItem);
});
const result = await encryptService.decryptItems(items, key);
workerApi.postMessage({
id: request.id,
items: JSON.stringify(result),
});
});

View File

@ -0,0 +1,22 @@
import { Jsonify } from "type-fest";
import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface";
import { Cipher } from "../../models/domain/cipher";
import { CipherView } from "../../models/view/cipher.view";
import { InitializerKey } from "./initializer-key";
/**
* Internal reference of classes so we can reconstruct objects properly.
* Each entry should be keyed using the Decryptable.initializerKey property
*/
const classInitializers: Record<InitializerKey, (obj: any) => any> = {
[InitializerKey.Cipher]: Cipher.fromJSON,
[InitializerKey.CipherView]: CipherView.fromJSON,
};
export function getClassInitializer<T extends InitializerMetadata>(
className: InitializerKey
): (obj: Jsonify<T>) => T {
return classInitializers[className];
}

View File

@ -0,0 +1,4 @@
export enum InitializerKey {
Cipher = 0,
CipherView = 1,
}

View File

@ -0,0 +1,82 @@
import { defaultIfEmpty, filter, firstValueFrom, fromEvent, map, Subject, takeUntil } from "rxjs";
import { Jsonify } from "type-fest";
import { Decryptable } from "../../interfaces/decryptable.interface";
import { InitializerMetadata } from "../../interfaces/initializer-metadata.interface";
import { Utils } from "../../misc/utils";
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
import { EncryptServiceImplementation } from "./encrypt.service.implementation";
import { getClassInitializer } from "./get-class-initializer";
// TTL (time to live) is not strictly required but avoids tying up memory resources if inactive
const workerTTL = 3 * 60000; // 3 minutes
export class MultithreadEncryptServiceImplementation extends EncryptServiceImplementation {
private worker: Worker;
private timeout: any;
private clear$ = new Subject<void>();
/**
* Sends items to a web worker to decrypt them.
* This utilises multithreading to decrypt items faster without interrupting other operations (e.g. updating UI).
*/
async decryptItems<T extends InitializerMetadata>(
items: Decryptable<T>[],
key: SymmetricCryptoKey
): Promise<T[]> {
if (items == null || items.length < 1) {
return [];
}
this.logService.info("Starting decryption using multithreading");
this.worker ??= new Worker(
new URL("@bitwarden/common/services/cryptography/encrypt.worker.ts", import.meta.url)
);
this.restartTimeout();
const request = {
id: Utils.newGuid(),
items: items,
key: key,
};
this.worker.postMessage(JSON.stringify(request));
return await firstValueFrom(
fromEvent(this.worker, "message").pipe(
filter((response: MessageEvent) => response.data?.id === request.id),
map((response) => JSON.parse(response.data.items)),
map((items) =>
items.map((jsonItem: Jsonify<T>) => {
const initializer = getClassInitializer<T>(jsonItem.initializerKey);
return initializer(jsonItem);
})
),
takeUntil(this.clear$),
defaultIfEmpty([])
)
);
}
private clear() {
this.clear$.next();
this.worker?.terminate();
this.worker = null;
this.clearTimeout();
}
private restartTimeout() {
this.clearTimeout();
this.timeout = setTimeout(() => this.clear(), workerTTL);
}
private clearTimeout() {
if (this.timeout != null) {
clearTimeout(this.timeout);
}
}
}

View File

@ -1,5 +1,5 @@
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/abstractions/log.service"; import { LogService } from "@bitwarden/common/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { StateService } from "@bitwarden/common/abstractions/state.service"; import { StateService } from "@bitwarden/common/abstractions/state.service";
@ -10,7 +10,7 @@ import { CryptoService } from "@bitwarden/common/services/crypto.service";
export class ElectronCryptoService extends CryptoService { export class ElectronCryptoService extends CryptoService {
constructor( constructor(
cryptoFunctionService: CryptoFunctionService, cryptoFunctionService: CryptoFunctionService,
encryptService: AbstractEncryptService, encryptService: EncryptService,
platformUtilService: PlatformUtilsService, platformUtilService: PlatformUtilsService,
logService: LogService, logService: LogService,
stateService: StateService stateService: StateService

View File

@ -4,7 +4,7 @@
"moduleResolution": "node", "moduleResolution": "node",
"noImplicitAny": true, "noImplicitAny": true,
"target": "ES6", "target": "ES6",
"module": "commonjs", "module": "es2020",
"lib": ["es5", "es6", "es7", "dom"], "lib": ["es5", "es6", "es7", "dom"],
"sourceMap": true, "sourceMap": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,

View File

@ -4,7 +4,7 @@
"moduleResolution": "node", "moduleResolution": "node",
"noImplicitAny": true, "noImplicitAny": true,
"target": "ES6", "target": "ES6",
"module": "commonjs", "module": "ES2020",
"lib": ["es5", "es6", "es7", "dom"], "lib": ["es5", "es6", "es7", "dom"],
"sourceMap": true, "sourceMap": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,