diff --git a/web/crypto/olm-api.js b/web/crypto/olm-api.js --- a/web/crypto/olm-api.js +++ b/web/crypto/olm-api.js @@ -17,6 +17,11 @@ shouldRotatePrekey, } from 'lib/utils/olm-utils.js'; +import { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js'; +import { olmWasmPath } from '../shared-worker/utils/constants.js'; +import { workerRequestMessageTypes } from '../types/worker-types.js'; + +const usingSharedWorker = false; // methods below are just mocks to SQLite API // implement proper methods tracked in ENG-6462 @@ -36,7 +41,15 @@ const olmAPI: OlmAPI = { async initializeCryptoAccount(): Promise { - await olm.init(); + if (usingSharedWorker) { + const sharedWorker = await getCommSharedWorker(); + await sharedWorker.schedule({ + type: workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT, + olmWasmPath: olmWasmPath(), + }); + } else { + await olm.init(); + } }, async encrypt(content: string, deviceID: string): Promise { const session = getOlmSession(deviceID); diff --git a/web/push-notif/push-notifs-handler.js b/web/push-notif/push-notifs-handler.js --- a/web/push-notif/push-notifs-handler.js +++ b/web/push-notif/push-notifs-handler.js @@ -22,15 +22,9 @@ import PushNotifModal from '../modals/push-notif-modal.react.js'; import { updateNavInfoActionType } from '../redux/action-types.js'; import { useSelector } from '../redux/redux-utils.js'; -import { - WORKERS_MODULES_DIR_PATH, - DEFAULT_OLM_FILENAME, -} from '../shared-worker/utils/constants.js'; +import { olmWasmPath } from '../shared-worker/utils/constants.js'; import { useStaffCanSee } from '../utils/staff-utils.js'; -declare var baseURL: string; -declare var olmFilename: string; - function useCreateDesktopPushSubscription() { const dispatchActionPromise = useDispatchActionPromise(); const callSetDeviceToken = useSetDeviceTokenFanout(); @@ -106,10 +100,6 @@ return; } - const origin = window.location.origin; - const olmWasmDirPath = `${origin}${baseURL}${WORKERS_MODULES_DIR_PATH}`; - const olmWasmFilename = olmFilename ? olmFilename : DEFAULT_OLM_FILENAME; - const olmWasmPath = `${olmWasmDirPath}/${olmWasmFilename}`; workerRegistration.active?.postMessage({ olmWasmPath, staffCanSee }); const subscription = await workerRegistration.pushManager.subscribe({ diff --git a/web/shared-worker/utils/constants.js b/web/shared-worker/utils/constants.js --- a/web/shared-worker/utils/constants.js +++ b/web/shared-worker/utils/constants.js @@ -46,3 +46,12 @@ description: 'Comm encrypted database storage', version: '1.0', }; + +declare var baseURL: string; +declare var olmFilename: string; +export function olmWasmPath(): string { + const origin = window.location.origin; + const olmWasmDirPath = `${origin}${baseURL}${WORKERS_MODULES_DIR_PATH}`; + const olmWasmFilename = olmFilename ? olmFilename : DEFAULT_OLM_FILENAME; + return `${olmWasmDirPath}/${olmWasmFilename}`; +} diff --git a/web/shared-worker/worker/shared-worker.js b/web/shared-worker/worker/shared-worker.js --- a/web/shared-worker/worker/shared-worker.js +++ b/web/shared-worker/worker/shared-worker.js @@ -7,6 +7,7 @@ getClientStoreFromQueryExecutor, processDBStoreOperations, } from './process-operations.js'; +import { clearCryptoStore, initializeCryptoAccount } from './worker-crypto.js'; import initBackupClientModule from '../../backup-client-wasm/wasm/backup-client-wasm.js'; import { decryptData, @@ -182,6 +183,7 @@ await Promise.all(promises); return undefined; } else if (message.type === workerRequestMessageTypes.CLEAR_SENSITIVE_DATA) { + clearCryptoStore(); encryptionKey = null; await localforage.clear(); if (dbModule && sqliteQueryExecutor) { @@ -256,6 +258,15 @@ message.backupDataKey, message.backupLogDataKey, ); + } else if ( + message.type === workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT + ) { + await initializeCryptoAccount( + sqliteQueryExecutor, + dbModule, + message.olmWasmPath, + message.initialCryptoStore, + ); } persistNeeded = true; diff --git a/web/shared-worker/worker/worker-crypto.js b/web/shared-worker/worker/worker-crypto.js new file mode 100644 --- /dev/null +++ b/web/shared-worker/worker/worker-crypto.js @@ -0,0 +1,120 @@ +// @flow + +import olm from '@commapp/olm'; +import uuid from 'uuid'; + +import type { + CryptoStore, + PickledOLMAccount, + OLMIdentityKeys, +} from 'lib/types/crypto-types.js'; + +import { getProcessingStoreOpsExceptionMessage } from './process-operations.js'; +import type { EmscriptenModule } from '../types/module'; +import type { SQLiteQueryExecutor } from '../types/sqlite-query-executor.js'; + +let cryptoStore: ?CryptoStore = null; + +function clearCryptoStore() { + cryptoStore = null; +} + +function persistCryptoStore( + sqliteQueryExecutor: SQLiteQueryExecutor, + dbModule: EmscriptenModule, +) { + if (!cryptoStore) { + throw new Error("CryptoStore doesn't exist"); + } + + const { primaryAccount, notificationAccount } = cryptoStore; + + try { + sqliteQueryExecutor.storeOlmPersistAccount( + true, + JSON.stringify(primaryAccount), + ); + sqliteQueryExecutor.storeOlmPersistAccount( + false, + JSON.stringify(notificationAccount), + ); + } catch (err) { + throw new Error(getProcessingStoreOpsExceptionMessage(err, dbModule)); + } +} + +function getOlmPersistedAccount( + sqliteQueryExecutor: SQLiteQueryExecutor, + dbModule: EmscriptenModule, + isContentAccount: boolean, +): { + account: PickledOLMAccount, + identityKeys: OLMIdentityKeys, +} { + const olmAccount = new olm.Account(); + let account: PickledOLMAccount; + + let accountDBString; + try { + accountDBString = + sqliteQueryExecutor.getOlmPersistAccountDataWeb(isContentAccount); + } catch (err) { + throw new Error(getProcessingStoreOpsExceptionMessage(err, dbModule)); + } + + if (accountDBString.isNull) { + olmAccount.create(); + const accountPicklingKey = uuid.v4(); + account = { + picklingKey: accountPicklingKey, + pickledAccount: olmAccount.pickle(accountPicklingKey), + }; + } else { + account = JSON.parse(accountDBString.value); + olmAccount.unpickle(account.picklingKey, account.pickledAccount); + } + + const identityKeys: OLMIdentityKeys = JSON.parse(olmAccount.identity_keys()); + + return { + account, + identityKeys, + }; +} + +async function initializeCryptoAccount( + sqliteQueryExecutor: SQLiteQueryExecutor, + dbModule: EmscriptenModule, + olmWasmPath: string, + initialCryptoStore: ?CryptoStore, +) { + await olm.init({ locateFile: () => olmWasmPath }); + + if (initialCryptoStore) { + cryptoStore = initialCryptoStore; + persistCryptoStore(sqliteQueryExecutor, dbModule); + return; + } + + const primaryAccountResult = getOlmPersistedAccount( + sqliteQueryExecutor, + dbModule, + true, + ); + const notifAccountResult = getOlmPersistedAccount( + sqliteQueryExecutor, + dbModule, + false, + ); + + cryptoStore = { + primaryAccount: primaryAccountResult.account, + primaryIdentityKeys: primaryAccountResult.identityKeys, + notificationAccount: notifAccountResult.account, + notificationIdentityKeys: notifAccountResult.identityKeys, + }; + + persistCryptoStore(sqliteQueryExecutor, dbModule); +} + +export { clearCryptoStore, initializeCryptoAccount }; diff --git a/web/types/worker-types.js b/web/types/worker-types.js --- a/web/types/worker-types.js +++ b/web/types/worker-types.js @@ -1,6 +1,7 @@ // @flow import type { AuthMetadata } from 'lib/shared/identity-client-context.js'; +import type { CryptoStore } from 'lib/types/crypto-types.js'; import type { ClientDBStore, ClientDBStoreOperations, @@ -20,6 +21,7 @@ REMOVE_PERSIST_STORAGE_ITEM: 9, CLEAR_SENSITIVE_DATA: 10, BACKUP_RESTORE: 11, + INITIALIZE_CRYPTO_ACCOUNT: 12, }); export const workerWriteRequests: $ReadOnlyArray = [ @@ -28,6 +30,7 @@ workerRequestMessageTypes.SET_PERSIST_STORAGE_ITEM, workerRequestMessageTypes.REMOVE_PERSIST_STORAGE_ITEM, workerRequestMessageTypes.BACKUP_RESTORE, + workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT, ]; export type PingWorkerRequestMessage = { @@ -41,6 +44,7 @@ +commQueryExecutorFilename: ?string, +encryptionKey?: ?SubtleCrypto$JsonWebKey, +backupClientFilename?: ?string, + +initialCryptoStore?: CryptoStore, }; export type GenerateDatabaseEncryptionKeyRequestMessage = { @@ -93,6 +97,12 @@ +backupLogDataKey: string, }; +export type InitializeCryptoAccountRequestMessage = { + +type: 12, + +olmWasmPath: string, + +initialCryptoStore?: CryptoStore, +}; + export type WorkerRequestMessage = | PingWorkerRequestMessage | InitWorkerRequestMessage @@ -105,7 +115,8 @@ | SetPersistStorageItemRequestMessage | RemovePersistStorageItemRequestMessage | ClearSensitiveDataRequestMessage - | BackupRestoreRequestMessage; + | BackupRestoreRequestMessage + | InitializeCryptoAccountRequestMessage; export type WorkerRequestProxyMessage = { +id: number,