From c527ce85e808e6e6c7148898a060600d7f84d7b3 Mon Sep 17 00:00:00 2001 From: addison Date: Fri, 26 Nov 2021 20:37:55 -0500 Subject: [PATCH] laptop issue --- angular/src/components/lock.component.ts | 1 + common/src/abstractions/state.service.ts | 12 +- common/src/models/domain/account.ts | 8 +- common/src/models/domain/globalState.ts | 4 + common/src/models/domain/storageOptions.ts | 1 + common/src/services/crypto.service.ts | 59 ++++---- common/src/services/state.service.ts | 126 ++++++++---------- common/src/services/vaultTimeout.service.ts | 13 +- electron/src/keytarStorageListener.ts | 1 + .../src/services/electronCrypto.service.ts | 19 ++- .../electronRendererSecureStorage.service.ts | 4 + electron/src/utils.ts | 6 +- 12 files changed, 127 insertions(+), 127 deletions(-) diff --git a/angular/src/components/lock.component.ts b/angular/src/components/lock.component.ts index 67fb2f3d..cc83621f 100644 --- a/angular/src/components/lock.component.ts +++ b/angular/src/components/lock.component.ts @@ -155,6 +155,7 @@ export class LockComponent implements OnInit { } const success = (await this.cryptoService.getKey(KeySuffixOptions.Biometric)) != null; + console.debug('unlockBiometric', success) if (success) { await this.doContinue(); diff --git a/common/src/abstractions/state.service.ts b/common/src/abstractions/state.service.ts index b0aa7ebd..93135528 100644 --- a/common/src/abstractions/state.service.ts +++ b/common/src/abstractions/state.service.ts @@ -65,12 +65,12 @@ export abstract class StateService { setConvertAccountToKeyConnector: (value: boolean, options?: StorageOptions) => Promise; getCryptoMasterKey: (options?: StorageOptions) => Promise; setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; - getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise; - setCryptoMasterKeyAuto: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; - getCryptoMasterKeyB64: (options: StorageOptions) => Promise; - setCryptoMasterKeyB64: (value: string, options: StorageOptions) => Promise; - getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; - setCryptoMasterKeyBiometric: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise; + getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise; + setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise; + getCryptoMasterKeyB64: (options?: StorageOptions) => Promise; + setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise; + getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise; + setCryptoMasterKeyBiometric: (value: string, options?: StorageOptions) => Promise; getDecodedToken: (options?: StorageOptions) => Promise; setDecodedToken: (value: any, options?: StorageOptions) => Promise; getDecryptedCiphers: (options?: StorageOptions) => Promise; diff --git a/common/src/models/domain/account.ts b/common/src/models/domain/account.ts index a1cb1be3..e96d24fb 100644 --- a/common/src/models/domain/account.ts +++ b/common/src/models/domain/account.ts @@ -49,9 +49,9 @@ export class AccountData { export class AccountKeys { cryptoMasterKey: SymmetricCryptoKey; - cryptoMasterKeyAuto: SymmetricCryptoKey; + cryptoMasterKeyAuto: string; cryptoMasterKeyB64: string; - cryptoMasterKeyBiometric: SymmetricCryptoKey; + cryptoMasterKeyBiometric: string; cryptoSymmetricKey: EncryptionPair = new EncryptionPair(); organizationKeys: EncryptionPair> = new EncryptionPair>(); providerKeys: EncryptionPair> = new EncryptionPair>(); @@ -89,7 +89,6 @@ export class AccountSettings { autoConfirmFingerPrints: boolean; autoFillOnPageLoadDefault: boolean; biometricLocked: boolean; - biometricText: string; biometricUnlock: boolean; clearClipboard: number; defaultUriMatch: UriMatchType; @@ -105,7 +104,6 @@ export class AccountSettings { enableAlwaysOnTop: boolean; enableAutoFillOnPageLoad: boolean; enableBiometric: boolean; - enableBiometrics: boolean; enableBrowserIntegration: boolean; enableBrowserIntegrationFingerprint: boolean; enableCloseToTray: boolean; @@ -119,8 +117,6 @@ export class AccountSettings { locale: string; minimizeOnCopyToClipboard: boolean; neverDomains: { [id: string]: any }; - noAutoPromptBiometrics: boolean; - noAutoPromptBiometricsText: string; openAtLogin: boolean; passwordGenerationOptions: any; pinProtected: EncryptionPair = new EncryptionPair(); diff --git a/common/src/models/domain/globalState.ts b/common/src/models/domain/globalState.ts index 7f415bbf..e5a87981 100644 --- a/common/src/models/domain/globalState.ts +++ b/common/src/models/domain/globalState.ts @@ -16,4 +16,8 @@ export class GlobalState { vaultTimeoutAction: string; loginRedirect: any; mainWindowSize: number; + enableBiometrics: boolean; + biometricText: string; + noAutoPromptBiometrics: boolean; + noAutoPromptBiometricsText: string; } diff --git a/common/src/models/domain/storageOptions.ts b/common/src/models/domain/storageOptions.ts index f63f75eb..d9924795 100644 --- a/common/src/models/domain/storageOptions.ts +++ b/common/src/models/domain/storageOptions.ts @@ -6,4 +6,5 @@ export type StorageOptions = { useSecureStorage?: boolean; userId?: string; htmlStorageLocation?: HtmlStorageLocation; + keySuffix?: string, }; diff --git a/common/src/services/crypto.service.ts b/common/src/services/crypto.service.ts index bec4992b..7f5ca50e 100644 --- a/common/src/services/crypto.service.ts +++ b/common/src/services/crypto.service.ts @@ -87,10 +87,12 @@ export class CryptoService implements CryptoServiceAbstraction { const inMemoryKey = await this.stateService.getCryptoMasterKey({ userId: userId }); if (inMemoryKey != null) { + console.debug('found another key. not clearing correctly on lock probably'); return inMemoryKey; } keySuffix ||= KeySuffixOptions.Auto; + console.debug('getKey', { keySuffix , key: await this.getKeyFromStorage(keySuffix, userId)}); const symmetricKey = await this.getKeyFromStorage(keySuffix, userId); if (symmetricKey != null) { @@ -103,7 +105,7 @@ export class CryptoService implements CryptoServiceAbstraction { async getKeyFromStorage(keySuffix: KeySuffixOptions, userId?: string): Promise { const key = await this.retrieveKeyFromStorage(keySuffix, userId); if (key != null) { - const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key.keyB64).buffer); + const symmetricKey = new SymmetricCryptoKey(Utils.fromB64ToArray(key).buffer); if (!await this.validateKey(symmetricKey)) { this.logService.warning('Wrong key, throwing away stored key'); @@ -345,8 +347,8 @@ export class CryptoService implements CryptoServiceAbstraction { await this.stateService.setCryptoMasterKeyBiometric(null); } - async clearKeyHash(): Promise { - return await this.stateService.setKeyHash(null); + async clearKeyHash(userId?: string): Promise { + return await this.stateService.setKeyHash(null, { userId: userId }); } async clearEncKey(memoryOnly?: boolean, userId?: string): Promise { @@ -374,25 +376,25 @@ export class CryptoService implements CryptoServiceAbstraction { } } - async clearProviderKeys(memoryOnly?: boolean): Promise { - await this.stateService.setDecryptedProviderKeys(null); + async clearProviderKeys(memoryOnly?: boolean, userId?: string): Promise { + await this.stateService.setDecryptedProviderKeys(null, { userId: userId }); if (!memoryOnly) { - await this.stateService.setEncryptedProviderKeys(null); + await this.stateService.setEncryptedProviderKeys(null, { userId: userId }); } } - async clearPinProtectedKey(): Promise { - return await this.stateService.setDecryptedPinProtected(null); + async clearPinProtectedKey(userId?: string): Promise { + return await this.stateService.setDecryptedPinProtected(null, { userId: userId }); } - async clearKeys(): Promise { - await this.clearKey(); - await this.clearKeyHash(); - await this.clearOrgKeys(); - await this.clearProviderKeys(); - await this.clearEncKey(); - await this.clearKeyPair(); - await this.clearPinProtectedKey(); + async clearKeys(userId?: string): Promise { + await this.clearKey(true, userId); + await this.clearKeyHash(userId); + await this.clearOrgKeys(false, userId); + await this.clearProviderKeys(false, userId); + await this.clearEncKey(false, userId); + await this.clearKeyPair(false, userId); + await this.clearPinProtectedKey(userId); } async toggleKey(): Promise { @@ -694,7 +696,13 @@ export class CryptoService implements CryptoServiceAbstraction { } // Helpers - + protected async storeKey(key: SymmetricCryptoKey, userId?: string) { + if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId) || await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) { + await this.stateService.setCryptoMasterKeyB64(key.keyB64, { userId: userId }); + } else { + await this.stateService.setCryptoMasterKeyB64(null, { userId: userId }); + } + } protected async shouldStoreKey(keySuffix: KeySuffixOptions, userId?: string) { let shouldStoreKey = false; @@ -862,25 +870,8 @@ export class CryptoService implements CryptoServiceAbstraction { return [new SymmetricCryptoKey(encKey), encKeyEnc]; } - private async getSuffix(userId?: string): Promise { - return await this.shouldStoreKey(KeySuffixOptions.Auto, userId) ? - KeySuffixOptions.Auto : - await this.shouldStoreKey(KeySuffixOptions.Biometric, userId) ? - KeySuffixOptions.Biometric : - null; - } - private async clearSecretKeyStore(userId?: string): Promise { await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId }); await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId }); } - - private async storeKey(key: SymmetricCryptoKey, userId?: string) { - const shouldStoreAuto = await this.shouldStoreKey(KeySuffixOptions.Auto, userId); - await this.stateService.setCryptoMasterKeyAuto(shouldStoreAuto ? key : null, { userId: userId }); - - const shouldStoreBiometric = await this.shouldStoreKey(KeySuffixOptions.Biometric, userId); - await this.stateService.setCryptoMasterKeyBiometric(shouldStoreBiometric ? key : null, { userId: userId }); - } - } diff --git a/common/src/services/state.service.ts b/common/src/services/state.service.ts index 7056a287..9d450564 100644 --- a/common/src/services/state.service.ts +++ b/common/src/services/state.service.ts @@ -53,7 +53,16 @@ export class StateService implements StateServiceAbstraction { if (await this.getActiveUserIdFromStorage() != null) { const diskState = await this.storageService.get('state', await this.defaultOnDiskOptions()); this.state = diskState; - await this.saveStateToStorage(diskState, await this.defaultOnDiskMemoryOptions()); + + // if (this.state.accounts != null && Object.keys(this.state.accounts).length > 0) { + // // Long term storage mechanisms may return undefined for nested account objects that we always want initilized + // // Reconstruct them here to ensure things like serverUrl are always accessible for all accounts + // for (const userId in this.state.accounts) { + // this.state.accounts[userId] = new Account(this.state.accounts[userId]); + // } + // } + + await this.saveStateToStorage(this.state, await this.defaultOnDiskMemoryOptions()); } } @@ -177,19 +186,19 @@ export class StateService implements StateServiceAbstraction { } async setBiometricLocked(value: boolean, options?: StorageOptions): Promise { - const account = await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())); + const account = await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)); account.settings.biometricLocked = value; - await this.saveAccount(account, this.reconcileOptions(options, await this.defaultOnDiskOptions())); + await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); } async getBiometricText(options?: StorageOptions): Promise { - return (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.settings?.biometricText; + return (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.biometricText; } async setBiometricText(value: string, options?: StorageOptions): Promise { - const account = await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())); - account.settings.biometricText = value; - await this.saveAccount(account, this.reconcileOptions(options, await this.defaultOnDiskOptions())); + const globals = await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())); + globals.biometricText = value; + await this.saveGlobals(globals, this.reconcileOptions(options, await this.defaultOnDiskOptions())); } async getBiometricUnlock(options?: StorageOptions): Promise { @@ -263,44 +272,47 @@ export class StateService implements StateServiceAbstraction { async setCryptoMasterKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise { const account = await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)); + console.debug('setting crypto master key', { + email: account.profile.email, + requestedUserId: options?.userId, + requestedUserEmail: await this.getEmail({ userId: options?.userId }), + account: account, + vaule: value, + }) account.keys.cryptoMasterKey = value; await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions)); } - async getCryptoMasterKeyAuto(options?: StorageOptions): Promise { + async getCryptoMasterKeyAuto(options?: StorageOptions): Promise { return (await this.getAccount(this.reconcileOptions(options, await this.defaultSecureStorageOptions())))?.keys?.cryptoMasterKeyAuto; } - async setCryptoMasterKeyAuto(value: SymmetricCryptoKey, options?: StorageOptions): Promise { + async setCryptoMasterKeyAuto(value: string, options?: StorageOptions): Promise { const account = await this.getAccount(this.reconcileOptions(options, await this.defaultSecureStorageOptions())); account.keys.cryptoMasterKeyAuto = value; await this.saveAccount(account, this.reconcileOptions(options, await this.defaultSecureStorageOptions())); } - async getCryptoMasterKeyB64(options: StorageOptions): Promise { + async getCryptoMasterKeyB64(options?: StorageOptions): Promise { const value = (await this.getAccount(this.reconcileOptions(options, await this.defaultSecureStorageOptions())))?.keys?.cryptoMasterKeyB64; return value; } - async setCryptoMasterKeyB64(value: string, options: StorageOptions): Promise { - try { - const account = await this.getAccount(this.reconcileOptions(options, await this.defaultSecureStorageOptions())); - if (account != null) { - account.keys.cryptoMasterKeyB64 = value; - await this.saveAccount(account, this.reconcileOptions(options, await this.defaultSecureStorageOptions())); - } - } catch (e) { - this.logService.error(e); - } + async setCryptoMasterKeyB64(value: string, options?: StorageOptions): Promise { + const account = await this.getAccount(this.reconcileOptions(options, await this.defaultSecureStorageOptions())); + account.keys.cryptoMasterKeyB64 = value; + await this.saveAccount(account, this.reconcileOptions(options, await this.defaultSecureStorageOptions())); } - async getCryptoMasterKeyBiometric(options?: StorageOptions): Promise { + async getCryptoMasterKeyBiometric(options?: StorageOptions): Promise { + options = this.reconcileOptions(options, { keySuffix: 'biometric' }) return (await this.getAccount(this.reconcileOptions(options, await this.defaultSecureStorageOptions())))?.keys?.cryptoMasterKeyBiometric; } - async setCryptoMasterKeyBiometric(value: SymmetricCryptoKey, options?: StorageOptions): Promise { + async setCryptoMasterKeyBiometric(value: string, options?: StorageOptions): Promise { const account = await this.getAccount(this.reconcileOptions(options, await this.defaultSecureStorageOptions())); account.keys.cryptoMasterKeyBiometric = value; + console.debug('saving biometric key', value); await this.saveAccount(account, this.reconcileOptions(options, await this.defaultSecureStorageOptions())); } @@ -577,12 +589,12 @@ export class StateService implements StateServiceAbstraction { } async getEnableBiometric(options?: StorageOptions): Promise { - return (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.settings?.enableBiometrics ?? false; + return (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.enableBiometrics ?? false; } async setEnableBiometric(value: boolean, options?: StorageOptions): Promise { - const account = await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())); - account.settings.enableBiometrics = value; - await this.saveAccount(account, this.reconcileOptions(options, await this.defaultOnDiskOptions())); + const globals = await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())); + globals.enableBiometrics = value; + await this.saveGlobals(globals, this.reconcileOptions(options, await this.defaultOnDiskOptions())); } async getEnableBrowserIntegration(options?: StorageOptions): Promise { @@ -950,21 +962,21 @@ export class StateService implements StateServiceAbstraction { } async getNoAutoPromptBiometrics(options?: StorageOptions): Promise { - return (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.settings?.noAutoPromptBiometrics ?? false; + return (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.noAutoPromptBiometrics ?? false; } async setNoAutoPromptBiometrics(value: boolean, options?: StorageOptions): Promise { - const account = await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())); - account.settings.noAutoPromptBiometrics = value; - await this.saveAccount(account, this.reconcileOptions(options, await this.defaultOnDiskOptions())); + const globals = await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())); + globals.noAutoPromptBiometrics = value; + await this.saveGlobals(globals, this.reconcileOptions(options, await this.defaultOnDiskOptions())); } async getNoAutoPromptBiometricsText(options?: StorageOptions): Promise { - return (await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.settings?.noAutoPromptBiometricsText; + return (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))?.noAutoPromptBiometricsText; } async setNoAutoPromptBiometricsText(value: string, options?: StorageOptions): Promise { - const account = await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())); - account.settings.noAutoPromptBiometricsText = value; - await this.saveAccount(account, this.reconcileOptions(options, await this.defaultOnDiskOptions())); + const globals = await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())); + globals.noAutoPromptBiometricsText = value; + await this.saveGlobals(globals, this.reconcileOptions(options, await this.defaultOnDiskOptions())); } async getOpenAtLogin(options?: StorageOptions): Promise { @@ -1339,30 +1351,9 @@ export class StateService implements StateServiceAbstraction { private async pushAccounts(): Promise { if (this.state?.accounts == null || Object.keys(this.state.accounts).length < 1) { + this.accounts.next(null); return; } - - for (const i in this.state.accounts) { - if (this.state.accounts[i]?.profile?.userId == null) { - continue; - } - if (this.state.accounts[i].profile.userId === this.state.activeUserId) { - this.state.accounts[i].profile.authenticationStatus = AuthenticationStatus.Active; - } else { - const vaultTimeout = await this.getVaultTimeout({ - storageLocation: StorageLocation.Disk, - userId: this.state.accounts[i].profile.userId, - }); - const lastActive = await this.getLastActive({ - storageLocation: StorageLocation.Disk, - userId: this.state.accounts[i].profile.userId, - }); - const diffSeconds = ((new Date()).getTime() - lastActive) / 1000; - this.state.accounts[i].profile.authenticationStatus = diffSeconds < (vaultTimeout * 60) ? - AuthenticationStatus.Unlocked : - AuthenticationStatus.Locked; - } - } this.accounts.next(this.state.accounts); } @@ -1374,6 +1365,7 @@ export class StateService implements StateServiceAbstraction { requestedOptions.storageLocation = requestedOptions?.storageLocation ?? defaultOptions.storageLocation; requestedOptions.useSecureStorage = requestedOptions?.useSecureStorage ?? defaultOptions.useSecureStorage; requestedOptions.htmlStorageLocation = requestedOptions?.htmlStorageLocation ?? defaultOptions.htmlStorageLocation; + requestedOptions.keySuffix = requestedOptions?.keySuffix ?? defaultOptions.keySuffix; return requestedOptions; } @@ -1427,12 +1419,7 @@ export class StateService implements StateServiceAbstraction { return; } - state.accounts[userId] = new Account({ - profile: state.accounts[userId].profile, - settings: state.accounts[userId].settings, - data: state.accounts[userId].data, - }); - + delete state.accounts[userId]; await this.saveStateToStorage(state, await this.defaultOnDiskLocalOptions()); } @@ -1442,12 +1429,7 @@ export class StateService implements StateServiceAbstraction { return; } - state.accounts[userId] = new Account({ - profile: state.accounts[userId].profile, - settings: state.accounts[userId].settings, - data: state.accounts[userId].data, - }); - + delete state.accounts[userId]; await this.saveStateToStorage(state, await this.defaultOnDiskOptions()); } @@ -1456,15 +1438,13 @@ export class StateService implements StateServiceAbstraction { if (state?.accounts[userId] == null) { return; } - state.accounts[userId] = null; + + delete state.accounts[userId]; await this.secureStorageService.save('state', state); } private removeAccountFromMemory(userId: string = this.state.activeUserId): void { - if (this.state?.accounts[userId] == null) { - return; - } - delete this.state.accounts[userId ?? this.state.activeUserId]; + delete this.state.accounts[userId]; } private async saveStateToStorage(state: State, options: StorageOptions): Promise { diff --git a/common/src/services/vaultTimeout.service.ts b/common/src/services/vaultTimeout.service.ts index 30142fe5..bded4060 100644 --- a/common/src/services/vaultTimeout.service.ts +++ b/common/src/services/vaultTimeout.service.ts @@ -47,7 +47,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { const neverLock = await this.cryptoService.hasKeyStored(KeySuffixOptions.Auto, userId) && !(await this.stateService.getEverBeenUnlocked({ userId: userId })); if (neverLock) { - // TODO: This also sets the key so when we check memory in the next line it finds a key. + // TODO: This also _sets_ the key so when we check memory in the next line it finds a key. // We should refactor here. await this.cryptoService.getKey(KeySuffixOptions.Auto, userId); } @@ -86,17 +86,20 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { this.searchService.clearIndex(); } - await this.folderService.clearCache(userId); - await this.cipherService.clearCache(userId); - await this.collectionService.clearCache(userId); await this.stateService.setEverBeenUnlocked(true, { userId: userId }); + await this.stateService.setBiometricLocked(true, { userId: userId }); + await this.cryptoService.clearKey(false, userId); await this.cryptoService.clearOrgKeys(true, userId); await this.cryptoService.clearKeyPair(true, userId); await this.cryptoService.clearEncKey(true, userId); - await this.stateService.setBiometricLocked(true, { userId: userId }); + + await this.folderService.clearCache(userId); + await this.cipherService.clearCache(userId); + await this.collectionService.clearCache(userId); this.messagingService.send('locked', { userId: userId }); + if (this.lockedCallback != null) { await this.lockedCallback(); } diff --git a/electron/src/keytarStorageListener.ts b/electron/src/keytarStorageListener.ts index fb263cb3..651252ca 100644 --- a/electron/src/keytarStorageListener.ts +++ b/electron/src/keytarStorageListener.ts @@ -17,6 +17,7 @@ export class KeytarStorageListener { init() { ipcMain.on('keytar', async (event: any, message: any) => { try { + console.debug('recieved message', message); let serviceName = this.serviceName; message.keySuffix = '_' + (message.keySuffix ?? ''); if (message.keySuffix !== '_') { diff --git a/electron/src/services/electronCrypto.service.ts b/electron/src/services/electronCrypto.service.ts index 23c08a90..36834549 100644 --- a/electron/src/services/electronCrypto.service.ts +++ b/electron/src/services/electronCrypto.service.ts @@ -7,6 +7,7 @@ import { CryptoService } from 'jslib-common/services/crypto.service'; import { KeySuffixOptions } from 'jslib-common/enums/keySuffixOptions'; import { StorageLocation } from 'jslib-common/enums/storageLocation'; +import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey'; export class ElectronCryptoService extends CryptoService { @@ -20,6 +21,20 @@ export class ElectronCryptoService extends CryptoService { return super.hasKeyStored(keySuffix); } + protected async storeKey(key: SymmetricCryptoKey, userId?: string) { + if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) { + await this.stateService.setCryptoMasterKeyAuto(key.keyB64, { userId: userId }); + } else { + this.clearStoredKey(KeySuffixOptions.Auto); + } + + if (await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) { + await this.stateService.setCryptoMasterKeyBiometric(key.keyB64, { userId: userId }); + } else { + this.clearStoredKey(KeySuffixOptions.Biometric); + } + } + protected async retrieveKeyFromStorage(keySuffix: KeySuffixOptions) { await this.upgradeSecurelyStoredKey(); return super.retrieveKeyFromStorage(keySuffix); @@ -31,7 +46,7 @@ export class ElectronCryptoService extends CryptoService { */ private async upgradeSecurelyStoredKey() { // attempt key upgrade, but if we fail just delete it. Keys will be stored property upon unlock anyway. - const key = await this.stateService.getCryptoMasterKey({ storageLocation: StorageLocation.Disk, useSecureStorage: true }); + const key = await this.stateService.getCryptoMasterKeyB64(); if (key == null) { return; @@ -49,6 +64,6 @@ export class ElectronCryptoService extends CryptoService { this.logService.error(e); } - await this.stateService.setCryptoMasterKey(null, { storageLocation: StorageLocation.Disk, useSecureStorage: true }); + await this.stateService.setCryptoMasterKeyB64(null); } } diff --git a/electron/src/services/electronRendererSecureStorage.service.ts b/electron/src/services/electronRendererSecureStorage.service.ts index 51b8951c..13a7459a 100644 --- a/electron/src/services/electronRendererSecureStorage.service.ts +++ b/electron/src/services/electronRendererSecureStorage.service.ts @@ -9,6 +9,7 @@ export class ElectronRendererSecureStorageService implements StorageService { const val = ipcRenderer.sendSync('keytar', { action: 'getPassword', key: key, + keySuffix: options?.keySuffix ?? '', }); return Promise.resolve(val != null ? JSON.parse(val) as T : null); } @@ -17,6 +18,7 @@ export class ElectronRendererSecureStorageService implements StorageService { const val = ipcRenderer.sendSync('keytar', { action: 'hasPassword', key: key, + keySuffix: options?.keySuffix ?? '', }); return Promise.resolve(!!val); } @@ -25,6 +27,7 @@ export class ElectronRendererSecureStorageService implements StorageService { ipcRenderer.sendSync('keytar', { action: 'setPassword', key: key, + keySuffix: options?.keySuffix ?? '', value: JSON.stringify(obj), }); return Promise.resolve(); @@ -34,6 +37,7 @@ export class ElectronRendererSecureStorageService implements StorageService { ipcRenderer.sendSync('keytar', { action: 'deletePassword', key: key, + keySuffix: options?.keySuffix ?? '', }); return Promise.resolve(); } diff --git a/electron/src/utils.ts b/electron/src/utils.ts index ce4de605..7a33142f 100644 --- a/electron/src/utils.ts +++ b/electron/src/utils.ts @@ -25,8 +25,12 @@ export function isAppImage() { return process.platform === 'linux' && 'APPIMAGE' in process.env; } +export function isMac() { + return process.platform === 'darwin'; +} + export function isMacAppStore() { - return process.platform === 'darwin' && process.mas && process.mas === true; + return isMac() && process.mas && process.mas === true; } export function isWindowsStore() {