diff --git a/keyserver/src/responders/website-responders.js b/keyserver/src/responders/website-responders.js
--- a/keyserver/src/responders/website-responders.js
+++ b/keyserver/src/responders/website-responders.js
@@ -45,6 +45,7 @@
+commQueryExecutorFilename: string,
+opaqueURL: string,
+backupClientFilename: string,
+ +webworkersOpaqueFilename: string,
};
let assetInfo: ?AssetInfo = null;
async function getAssetInfo() {
@@ -61,6 +62,7 @@
commQueryExecutorFilename: '',
opaqueURL: 'http://localhost:8080/opaque-ke.wasm',
backupClientFilename: '',
+ webworkersOpaqueFilename: '',
};
return assetInfo;
}
@@ -86,6 +88,7 @@
commQueryExecutorFilename: webworkersManifest['comm_query_executor.wasm'],
opaqueURL: `compiled/${manifest['comm_opaque2_wasm_bg.wasm']}`,
backupClientFilename: webworkersManifest['backup-client-wasm_bg.wasm'],
+ webworkersOpaqueFilename: webworkersManifest['comm_opaque2_wasm_bg.wasm'],
};
return assetInfo;
} catch {
@@ -138,6 +141,7 @@
opaqueURL,
commQueryExecutorFilename,
backupClientFilename,
+ webworkersOpaqueFilename,
} = await assetInfoPromise;
// prettier-ignore
@@ -190,6 +194,7 @@
var olmFilename = "${olmFilename}";
var commQueryExecutorFilename = "${commQueryExecutorFilename}";
var backupClientFilename = "${backupClientFilename}";
+ var webworkersOpaqueFilename = "${webworkersOpaqueFilename}"
var opaqueURL = "${opaqueURL}";
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
@@ -15,6 +15,8 @@
export const DEFAULT_OLM_FILENAME = 'olm.wasm';
+export const DEFAULT_WEBWORKERS_OPAQUE_FILENAME = 'comm_opaque2_wasm_bg.wasm';
+
export const COMM_SQLITE_DATABASE_PATH = 'comm.sqlite';
export const COMM_SQLITE_BACKUP_RESTORE_DATABASE_PATH =
'comm_backup_restore.sqlite';
@@ -55,3 +57,13 @@
const olmWasmFilename = olmFilename ? olmFilename : DEFAULT_OLM_FILENAME;
return `${olmWasmDirPath}/${olmWasmFilename}`;
}
+
+declare var webworkersOpaqueFilename: string;
+export function getOpaqueWasmPath(): string {
+ const origin = window.location.origin;
+ const opaqueWasmDirPath = `${origin}${baseURL}${WORKERS_MODULES_DIR_PATH}`;
+ const opaqueWasmFilename = webworkersOpaqueFilename
+ ? webworkersOpaqueFilename
+ : DEFAULT_WEBWORKERS_OPAQUE_FILENAME;
+ return `${opaqueWasmDirPath}/${opaqueWasmFilename}`;
+}
diff --git a/web/shared-worker/worker/identity-client.js b/web/shared-worker/worker/identity-client.js
new file mode 100644
--- /dev/null
+++ b/web/shared-worker/worker/identity-client.js
@@ -0,0 +1,36 @@
+// @flow
+
+import { getDeviceKeyUpload } from './worker-crypto.js';
+import { IdentityServiceClientWrapper } from '../../grpc/identity-service-client-wrapper.js';
+import { workerRequestMessageTypes } from '../../types/worker-types.js';
+import type {
+ WorkerResponseMessage,
+ WorkerRequestMessage,
+} from '../../types/worker-types.js';
+import type { EmscriptenModule } from '../types/module.js';
+import type { SQLiteQueryExecutor } from '../types/sqlite-query-executor.js';
+
+let identityClient: ?IdentityServiceClientWrapper = null;
+
+async function processAppIdentityClientRequest(
+ sqliteQueryExecutor: SQLiteQueryExecutor,
+ dbModule: EmscriptenModule,
+ message: WorkerRequestMessage,
+): Promise {
+ if (
+ message.type === workerRequestMessageTypes.CREATE_IDENTITY_SERVICE_CLIENT
+ ) {
+ identityClient = new IdentityServiceClientWrapper(
+ message.platformDetails,
+ message.opaqueWasmPath,
+ message.authLayer,
+ async () => getDeviceKeyUpload(),
+ );
+ }
+}
+
+function getIdentityClient(): ?IdentityServiceClientWrapper {
+ return identityClient;
+}
+
+export { processAppIdentityClientRequest, getIdentityClient };
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
@@ -3,6 +3,7 @@
import localforage from 'localforage';
import { restoreBackup } from './backup.js';
+import { processAppIdentityClientRequest } from './identity-client.js';
import {
getClientStoreFromQueryExecutor,
processDBStoreOperations,
@@ -32,6 +33,7 @@
workerWriteRequests,
workerOlmAPIRequests,
} from '../../types/worker-types.js';
+import { workerIdentityClientRequests } from '../../types/worker-types.js';
import { getDatabaseModule } from '../db-module.js';
import {
COMM_SQLITE_DATABASE_PATH,
@@ -233,7 +235,14 @@
// write operations
const isOlmAPIRequest = workerOlmAPIRequests.includes(message.type);
- if (!workerWriteRequests.includes(message.type) && !isOlmAPIRequest) {
+ const isIdentityClientRequest = workerIdentityClientRequests.includes(
+ message.type,
+ );
+ if (
+ !workerWriteRequests.includes(message.type) &&
+ !isOlmAPIRequest &&
+ !isIdentityClientRequest
+ ) {
throw new Error(`Request type ${message.type} not supported`);
}
if (!sqliteQueryExecutor || !dbModule) {
@@ -242,8 +251,15 @@
);
}
+ let result;
if (isOlmAPIRequest) {
await processAppOlmApiRequest(message);
+ } else if (isIdentityClientRequest) {
+ result = await processAppIdentityClientRequest(
+ sqliteQueryExecutor,
+ dbModule,
+ message,
+ );
} else if (
message.type === workerRequestMessageTypes.PROCESS_STORE_OPERATIONS
) {
@@ -278,7 +294,7 @@
void persist();
}
- return undefined;
+ return result;
}
function connectHandler(event: SharedWorkerMessageEvent) {
diff --git a/web/shared-worker/worker/worker-crypto.js b/web/shared-worker/worker/worker-crypto.js
--- a/web/shared-worker/worker/worker-crypto.js
+++ b/web/shared-worker/worker/worker-crypto.js
@@ -3,7 +3,14 @@
import olm from '@commapp/olm';
import uuid from 'uuid';
-import type { CryptoStore, PickledOLMAccount } from 'lib/types/crypto-types.js';
+import type {
+ CryptoStore,
+ PickledOLMAccount,
+ IdentityKeysBlob,
+ SignedIdentityKeysBlob,
+} from 'lib/types/crypto-types.js';
+import type { IdentityDeviceKeyUpload } from 'lib/types/identity-service-types.js';
+import { retrieveAccountKeysSet } from 'lib/utils/olm-utils.js';
import { getProcessingStoreOpsExceptionMessage } from './process-operations.js';
import { getDBModule, getSQLiteQueryExecutor } from './worker-database.js';
@@ -166,4 +173,58 @@
}
}
-export { clearCryptoStore, processAppOlmApiRequest };
+function getSignedIdentityKeysBlob(): SignedIdentityKeysBlob {
+ if (!cryptoStore) {
+ throw new Error('Crypto account not initialized');
+ }
+
+ const { contentAccount, notificationAccount } = cryptoStore;
+
+ const identityKeysBlob: IdentityKeysBlob = {
+ notificationIdentityPublicKeys: JSON.parse(
+ notificationAccount.identity_keys(),
+ ),
+ primaryIdentityPublicKeys: JSON.parse(contentAccount.identity_keys()),
+ };
+
+ const payloadToBeSigned: string = JSON.stringify(identityKeysBlob);
+ const signedIdentityKeysBlob: SignedIdentityKeysBlob = {
+ payload: payloadToBeSigned,
+ signature: contentAccount.sign(payloadToBeSigned),
+ };
+
+ return signedIdentityKeysBlob;
+}
+
+function getDeviceKeyUpload(): IdentityDeviceKeyUpload {
+ if (!cryptoStore) {
+ throw new Error('Crypto account not initialized');
+ }
+ const { contentAccount, notificationAccount } = cryptoStore;
+
+ const signedIdentityKeysBlob = getSignedIdentityKeysBlob();
+
+ const primaryAccountKeysSet = retrieveAccountKeysSet(contentAccount);
+ const notificationAccountKeysSet =
+ retrieveAccountKeysSet(notificationAccount);
+
+ persistCryptoStore();
+
+ return {
+ keyPayload: signedIdentityKeysBlob.payload,
+ keyPayloadSignature: signedIdentityKeysBlob.signature,
+ contentPrekey: primaryAccountKeysSet.prekey,
+ contentPrekeySignature: primaryAccountKeysSet.prekeySignature,
+ notifPrekey: notificationAccountKeysSet.prekey,
+ notifPrekeySignature: notificationAccountKeysSet.prekeySignature,
+ contentOneTimeKeys: primaryAccountKeysSet.oneTimeKeys,
+ notifOneTimeKeys: notificationAccountKeysSet.oneTimeKeys,
+ };
+}
+
+export {
+ clearCryptoStore,
+ processAppOlmApiRequest,
+ getSignedIdentityKeysBlob,
+ getDeviceKeyUpload,
+};
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
@@ -2,6 +2,8 @@
import type { AuthMetadata } from 'lib/shared/identity-client-context.js';
import type { CryptoStore } from 'lib/types/crypto-types.js';
+import type { PlatformDetails } from 'lib/types/device-types.js';
+import type { IdentityServiceAuthLayer } from 'lib/types/identity-service-types.js';
import type {
ClientDBStore,
ClientDBStoreOperations,
@@ -22,6 +24,7 @@
CLEAR_SENSITIVE_DATA: 10,
BACKUP_RESTORE: 11,
INITIALIZE_CRYPTO_ACCOUNT: 12,
+ CREATE_IDENTITY_SERVICE_CLIENT: 13,
});
export const workerWriteRequests: $ReadOnlyArray = [
@@ -37,6 +40,10 @@
workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT,
];
+export const workerIdentityClientRequests: $ReadOnlyArray = [
+ workerRequestMessageTypes.CREATE_IDENTITY_SERVICE_CLIENT,
+];
+
export type PingWorkerRequestMessage = {
+type: 0,
+text: string,
@@ -106,6 +113,13 @@
+initialCryptoStore?: CryptoStore,
};
+export type CreateIdentityServiceClientRequestMessage = {
+ +type: 13,
+ +opaqueWasmPath: string,
+ +platformDetails: PlatformDetails,
+ +authLayer: ?IdentityServiceAuthLayer,
+};
+
export type WorkerRequestMessage =
| PingWorkerRequestMessage
| InitWorkerRequestMessage
@@ -119,7 +133,8 @@
| RemovePersistStorageItemRequestMessage
| ClearSensitiveDataRequestMessage
| BackupRestoreRequestMessage
- | InitializeCryptoAccountRequestMessage;
+ | InitializeCryptoAccountRequestMessage
+ | CreateIdentityServiceClientRequestMessage;
export type WorkerRequestProxyMessage = {
+id: number,
diff --git a/web/webpack.config.cjs b/web/webpack.config.cjs
--- a/web/webpack.config.cjs
+++ b/web/webpack.config.cjs
@@ -160,6 +160,16 @@
},
],
}),
+ new CopyPlugin({
+ patterns: [
+ {
+ from:
+ 'node_modules/@commapp/opaque-ke-wasm' +
+ '/pkg/comm_opaque2_wasm_bg.wasm',
+ to: path.join(__dirname, 'dist', 'webworkers'),
+ },
+ ],
+ }),
];
const prodWebWorkersPlugins = [
@@ -202,6 +212,21 @@
},
],
}),
+ new CopyPlugin({
+ patterns: [
+ {
+ from:
+ 'node_modules/@commapp/opaque-ke-wasm' +
+ '/pkg/comm_opaque2_wasm_bg.wasm',
+ to: path.join(
+ __dirname,
+ 'dist',
+ 'webworkers',
+ 'opaque-ke.[contenthash:12].wasm',
+ ),
+ },
+ ],
+ }),
new WebpackManifestPlugin({
publicPath: '',
}),