diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 3dfc6f4c1c..518f6aa529 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -17,7 +17,12 @@ "**/*", "!**/node_modules/@bitwarden/desktop-native/**/*", "**/node_modules/@bitwarden/desktop-native/index.js", - "**/node_modules/@bitwarden/desktop-native/desktop_native.${platform}-${arch}*.node" + "**/node_modules/@bitwarden/desktop-native/desktop_native.${platform}-${arch}*.node", + + "!**/node_modules/argon2/**/*", + "**/node_modules/argon2/argon2.js", + "**/node_modules/argon2/package.json", + "**/node_modules/argon2/lib/binding/napi-v3/argon2.node" ], "electronVersion": "28.2.4", "generateUpdatesFilesForAllChannels": true, diff --git a/apps/desktop/src/app/services/renderer-crypto-function.service.ts b/apps/desktop/src/app/services/renderer-crypto-function.service.ts new file mode 100644 index 0000000000..604e70b1eb --- /dev/null +++ b/apps/desktop/src/app/services/renderer-crypto-function.service.ts @@ -0,0 +1,24 @@ +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; + +export class RendererCryptoFunctionService + extends WebCryptoFunctionService + implements CryptoFunctionService +{ + constructor(win: Window | typeof global) { + super(win); + } + + // We can't use the `argon2-browser` implementation because it loads WASM and the Content Security Policy doesn't allow it. + // Rather than trying to weaken the policy, we'll just use the Node.js implementation though the IPC channel. + // Note that the rest of the functions on this service will be inherited from the WebCryptoFunctionService, as those work just fine. + async argon2( + password: string | Uint8Array, + salt: string | Uint8Array, + iterations: number, + memory: number, + parallelism: number, + ): Promise { + return await ipc.platform.crypto.argon2(password, salt, iterations, memory, parallelism); + } +} diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 274564489c..93e25a76f8 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -10,6 +10,7 @@ import { MEMORY_STORAGE, OBSERVABLE_MEMORY_STORAGE, OBSERVABLE_DISK_STORAGE, + WINDOW, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; @@ -66,6 +67,7 @@ import { SearchBarService } from "../layout/search/search-bar.service"; import { DesktopFileDownloadService } from "./desktop-file-download.service"; import { DesktopThemingService } from "./desktop-theming.service"; import { InitService } from "./init.service"; +import { RendererCryptoFunctionService } from "./renderer-crypto-function.service"; const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); @@ -175,6 +177,11 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); useClass: LoginService, deps: [StateServiceAbstraction], }, + { + provide: CryptoFunctionServiceAbstraction, + useClass: RendererCryptoFunctionService, + deps: [WINDOW], + }, { provide: CryptoServiceAbstraction, useClass: ElectronCryptoService, diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 5cc2eb4d09..093da81f18 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -32,6 +32,7 @@ import { Account } from "./models/account"; import { BiometricsService, BiometricsServiceAbstraction } from "./platform/main/biometric/index"; import { ClipboardMain } from "./platform/main/clipboard.main"; import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener"; +import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service"; import { ElectronLogMainService } from "./platform/services/electron-log.main.service"; import { ElectronStateService } from "./platform/services/electron-state.service"; import { ElectronStorageService } from "./platform/services/electron-storage.service"; @@ -47,6 +48,7 @@ export class Main { messagingService: ElectronMainMessagingService; stateService: StateService; environmentService: EnvironmentService; + mainCryptoFunctionService: MainCryptoFunctionService; desktopCredentialStorageListener: DesktopCredentialStorageListener; migrationRunner: MigrationRunner; @@ -198,6 +200,9 @@ export class Main { this.clipboardMain = new ClipboardMain(); this.clipboardMain.init(); + + this.mainCryptoFunctionService = new MainCryptoFunctionService(); + this.mainCryptoFunctionService.init(); } bootstrap() { diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index f3bdcb734c..68d1fbe112 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -12,6 +12,7 @@ "url": "git+https://github.com/bitwarden/clients.git" }, "dependencies": { - "@bitwarden/desktop-native": "file:../desktop_native" + "@bitwarden/desktop-native": "file:../desktop_native", + "argon2": "0.31.0" } } diff --git a/apps/desktop/src/platform/main/main-crypto-function.service.ts b/apps/desktop/src/platform/main/main-crypto-function.service.ts new file mode 100644 index 0000000000..848e33113e --- /dev/null +++ b/apps/desktop/src/platform/main/main-crypto-function.service.ts @@ -0,0 +1,33 @@ +import { ipcMain } from "electron"; + +import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service"; +import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; + +export class MainCryptoFunctionService + extends NodeCryptoFunctionService + implements CryptoFunctionService +{ + init() { + ipcMain.handle( + "crypto.argon2", + async ( + event, + opts: { + password: string | Uint8Array; + salt: string | Uint8Array; + iterations: number; + memory: number; + parallelism: number; + }, + ) => { + return await this.argon2( + opts.password, + opts.salt, + opts.iterations, + opts.memory, + opts.parallelism, + ); + }, + ); + } +} diff --git a/apps/desktop/src/platform/preload.ts b/apps/desktop/src/platform/preload.ts index a958105fda..1f6bd200e0 100644 --- a/apps/desktop/src/platform/preload.ts +++ b/apps/desktop/src/platform/preload.ts @@ -76,6 +76,17 @@ const nativeMessaging = { }, }; +const crypto = { + argon2: ( + password: string | Uint8Array, + salt: string | Uint8Array, + iterations: number, + memory: number, + parallelism: number, + ): Promise => + ipcRenderer.invoke("crypto.argon2", { password, salt, iterations, memory, parallelism }), +}; + export default { versions: { app: (): Promise => ipcRenderer.invoke("appVersion"), @@ -121,6 +132,7 @@ export default { biometric, clipboard, nativeMessaging, + crypto, }; function deviceType(): DeviceType { diff --git a/apps/desktop/webpack.main.js b/apps/desktop/webpack.main.js index 59e043fa12..9d683457d9 100644 --- a/apps/desktop/webpack.main.js +++ b/apps/desktop/webpack.main.js @@ -72,8 +72,6 @@ const main = { "./src/package.json", { from: "./src/images", to: "images" }, { from: "./src/locales", to: "locales" }, - "../../node_modules/argon2-browser/dist/argon2.wasm", - "../../node_modules/argon2-browser/dist/argon2-simd.wasm", ], }), new EnvironmentPlugin({ @@ -84,6 +82,8 @@ const main = { externals: { "electron-reload": "commonjs2 electron-reload", "@bitwarden/desktop-native": "commonjs2 @bitwarden/desktop-native", + + argon2: "commonjs2 argon2", }, };