diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 762c755485a..816d317f411 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -39,6 +39,7 @@ import { MainCryptoFunctionService } from "./platform/main/main-crypto-function. import { DesktopSettingsService } from "./platform/services/desktop-settings.service"; import { ElectronLogMainService } from "./platform/services/electron-log.main.service"; import { ElectronStorageService } from "./platform/services/electron-storage.service"; +import { EphemeralValueStorageService } from "./platform/services/ephemeral-value-storage.main.service"; import { I18nMainService } from "./platform/services/i18n.main.service"; import { ElectronMainMessagingService } from "./services/electron-main-messaging.service"; import { isMacAppStore } from "./utils"; @@ -224,6 +225,8 @@ export class Main { this.clipboardMain = new ClipboardMain(); this.clipboardMain.init(); + + new EphemeralValueStorageService(); } bootstrap() { diff --git a/apps/desktop/src/platform/preload.ts b/apps/desktop/src/platform/preload.ts index b3007753eed..5dced6227bc 100644 --- a/apps/desktop/src/platform/preload.ts +++ b/apps/desktop/src/platform/preload.ts @@ -99,6 +99,14 @@ const crypto = { ipcRenderer.invoke("crypto.argon2", { password, salt, iterations, memory, parallelism }), }; +const ephemeralStore = { + setEphemeralValue: (key: string, value: string): Promise => + ipcRenderer.invoke("setEphemeralValue", { key, value }), + getEphemeralValue: (key: string): Promise => ipcRenderer.invoke("getEphemeralValue", key), + removeEphemeralValue: (key: string): Promise => + ipcRenderer.invoke("deleteEphemeralValue", key), +}; + export default { versions: { app: (): Promise => ipcRenderer.invoke("appVersion"), @@ -156,6 +164,7 @@ export default { powermonitor, nativeMessaging, crypto, + ephemeralStore, }; function deviceType(): DeviceType { diff --git a/apps/desktop/src/platform/services/ephemeral-value-storage.main.service.ts b/apps/desktop/src/platform/services/ephemeral-value-storage.main.service.ts new file mode 100644 index 00000000000..b59b48be1e1 --- /dev/null +++ b/apps/desktop/src/platform/services/ephemeral-value-storage.main.service.ts @@ -0,0 +1,21 @@ +import { ipcMain } from "electron"; + +/** + * The ephemeral value store holds values that should be accessible to the renderer past a process reload. + * In the current state, this store must not contain any keys that can decrypt a vault by themselves. + */ +export class EphemeralValueStorageService { + private ephemeralValues = new Map(); + + constructor() { + ipcMain.handle("setEphemeralValue", async (event, { key, value }) => { + this.ephemeralValues.set(key, value); + }); + ipcMain.handle("getEphemeralValue", async (event, key: string) => { + return this.ephemeralValues.get(key); + }); + ipcMain.handle("deleteEphemeralValue", async (event, key: string) => { + this.ephemeralValues.delete(key); + }); + } +} diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts index 2a5c341ee7b..ce97b9b7ac4 100644 --- a/apps/desktop/src/services/native-messaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -30,8 +30,6 @@ const HashAlgorithmForAsymmetricEncryption = "sha1"; @Injectable() export class NativeMessagingService { - private sharedSecrets = new Map(); - constructor( private cryptoFunctionService: CryptoFunctionService, private cryptoService: CryptoService, @@ -104,7 +102,7 @@ export class NativeMessagingService { return; } - if (this.sharedSecrets.get(appId) == null) { + if ((await ipc.platform.ephemeralStore.getEphemeralValue(appId)) == null) { ipc.platform.nativeMessaging.sendMessage({ command: "invalidateEncryption", appId: appId, @@ -115,7 +113,7 @@ export class NativeMessagingService { const message: LegacyMessage = JSON.parse( await this.cryptoService.decryptToUtf8( rawMessage as EncString, - this.sharedSecrets.get(appId), + SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)), ), ); @@ -205,7 +203,7 @@ export class NativeMessagingService { const encrypted = await this.cryptoService.encrypt( JSON.stringify(message), - this.sharedSecrets.get(appId), + SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)), ); ipc.platform.nativeMessaging.sendMessage({ appId: appId, message: encrypted }); @@ -213,7 +211,10 @@ export class NativeMessagingService { private async secureCommunication(remotePublicKey: Uint8Array, appId: string) { const secret = await this.cryptoFunctionService.randomBytes(64); - this.sharedSecrets.set(appId, new SymmetricCryptoKey(secret)); + await ipc.platform.ephemeralStore.setEphemeralValue( + appId, + new SymmetricCryptoKey(secret).keyB64, + ); const encryptedSecret = await this.cryptoFunctionService.rsaEncrypt( secret,