Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3383709
D11338.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D11338.diff
View Options
diff --git a/lib/components/prekeys-handler.react.js b/lib/components/prekeys-handler.react.js
--- a/lib/components/prekeys-handler.react.js
+++ b/lib/components/prekeys-handler.react.js
@@ -24,8 +24,10 @@
const timeoutID = setTimeout(async () => {
try {
+ const authMetadata = await identityContext.getAuthMetadata();
+
const { olmAPI } = getConfig();
- await olmAPI.validateAndUploadPrekeys(identityContext);
+ await olmAPI.validateAndUploadPrekeys(authMetadata);
} catch (e) {
console.log('Prekey validation error: ', e.message);
}
diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js
--- a/lib/types/crypto-types.js
+++ b/lib/types/crypto-types.js
@@ -2,7 +2,7 @@
import t, { type TInterface } from 'tcomb';
-import { type IdentityClientContextType } from '../shared/identity-client-context.js';
+import { type AuthMetadata } from '../shared/identity-client-context.js';
import { tShape } from '../utils/validation-utils.js';
export type OLMIdentityKeys = {
@@ -131,7 +131,5 @@
initialEncryptedContent: string,
) => Promise<string>,
+getOneTimeKeys: (numberOfKeys: number) => Promise<OneTimeKeysResultValues>,
- +validateAndUploadPrekeys: (
- identityContext: IdentityClientContextType,
- ) => Promise<void>,
+ +validateAndUploadPrekeys: (authMetadata: AuthMetadata) => Promise<void>,
};
diff --git a/native/crypto/olm-api.js b/native/crypto/olm-api.js
--- a/native/crypto/olm-api.js
+++ b/native/crypto/olm-api.js
@@ -1,7 +1,7 @@
// @flow
import { getOneTimeKeyValues } from 'lib/shared/crypto-utils.js';
-import { type IdentityClientContextType } from 'lib/shared/identity-client-context.js';
+import { type AuthMetadata } from 'lib/shared/identity-client-context.js';
import type {
OneTimeKeysResultValues,
OlmAPI,
@@ -38,15 +38,7 @@
notificationsOneTimeKeys: getOneTimeKeyValues(notificationsOneTimeKeys),
};
},
- async validateAndUploadPrekeys(
- identityContext: IdentityClientContextType,
- ): Promise<void> {
- let authMetadata;
- try {
- authMetadata = await identityContext.getAuthMetadata();
- } catch (e) {
- return;
- }
+ async validateAndUploadPrekeys(authMetadata: AuthMetadata): Promise<void> {
const { userID, deviceID, accessToken } = authMetadata;
if (!userID || !deviceID || !accessToken) {
return;
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
@@ -1,43 +1,43 @@
// @flow
import olm from '@commapp/olm';
-import type { Account, Session } from '@commapp/olm';
-import { type IdentityClientContextType } from 'lib/shared/identity-client-context.js';
-import {
- type OlmAPI,
- olmEncryptedMessageTypes,
- type OLMIdentityKeys,
- type OneTimeKeysResultValues,
-} from 'lib/types/crypto-types.js';
-import {
- getAccountOneTimeKeys,
- getAccountPrekeysSet,
- shouldForgetPrekey,
- shouldRotatePrekey,
-} from 'lib/utils/olm-utils.js';
+import { type OlmAPI } from 'lib/types/crypto-types.js';
import { getCommSharedWorker } from '../shared-worker/shared-worker-provider.js';
import { getOlmWasmPath } from '../shared-worker/utils/constants.js';
-import { workerRequestMessageTypes } from '../types/worker-types.js';
+import {
+ workerRequestMessageTypes,
+ workerResponseMessageTypes,
+} from '../types/worker-types.js';
const usingSharedWorker = false;
-// methods below are just mocks to SQLite API
-// implement proper methods tracked in ENG-6462
-function getOlmAccount(): Account {
- const account = new olm.Account();
- account.create();
- return account;
-}
-// eslint-disable-next-line no-unused-vars
-function getOlmSession(deviceID: string): Session {
- return new olm.Session();
+function proxyToWorker<T>(
+ method: $Keys<OlmAPI>,
+): (...args: $ReadOnlyArray<mixed>) => Promise<T> {
+ return async (...args: $ReadOnlyArray<mixed>) => {
+ const sharedWorker = await getCommSharedWorker();
+ const result = await sharedWorker.schedule({
+ type: workerRequestMessageTypes.CALL_OLM_API_METHOD,
+ method,
+ args,
+ });
+
+ if (!result) {
+ throw new Error(`Worker OlmAPI call didn't return expected message`);
+ } else if (result.type !== workerResponseMessageTypes.CALL_OLM_API_METHOD) {
+ throw new Error(
+ `Worker OlmAPI call didn't return expected message. Instead got: ${JSON.stringify(
+ result,
+ )}`,
+ );
+ }
+
+ // Worker should return a message with the corresponding return type
+ return (result.result: any);
+ };
}
-// eslint-disable-next-line no-unused-vars
-function storeOlmAccount(account: Account): void {}
-// eslint-disable-next-line no-unused-vars
-function storeOlmSession(session: Session): void {}
const olmAPI: OlmAPI = {
async initializeCryptoAccount(): Promise<void> {
@@ -51,105 +51,11 @@
await olm.init();
}
},
- async encrypt(content: string, deviceID: string): Promise<string> {
- const session = getOlmSession(deviceID);
- const { body } = session.encrypt(content);
- storeOlmSession(session);
- return body;
- },
- async decrypt(encryptedContent: string, deviceID: string): Promise<string> {
- const session = getOlmSession(deviceID);
- const result = session.decrypt(
- olmEncryptedMessageTypes.TEXT,
- encryptedContent,
- );
- storeOlmSession(session);
- return result;
- },
- async contentInboundSessionCreator(
- contentIdentityKeys: OLMIdentityKeys,
- initialEncryptedContent: string,
- ): Promise<string> {
- const account = getOlmAccount();
- const session = new olm.Session();
- session.create_inbound_from(
- account,
- contentIdentityKeys.curve25519,
- initialEncryptedContent,
- );
-
- account.remove_one_time_keys(session);
- const initialEncryptedMessage = session.decrypt(
- olmEncryptedMessageTypes.PREKEY,
- initialEncryptedContent,
- );
- storeOlmAccount(account);
- storeOlmSession(session);
- return initialEncryptedMessage;
- },
- async getOneTimeKeys(numberOfKeys: number): Promise<OneTimeKeysResultValues> {
- const contentAccount = getOlmAccount();
- const notifAccount = getOlmAccount();
- const contentOneTimeKeys = getAccountOneTimeKeys(
- contentAccount,
- numberOfKeys,
- );
- contentAccount.mark_keys_as_published();
- storeOlmAccount(contentAccount);
-
- const notificationsOneTimeKeys = getAccountOneTimeKeys(
- notifAccount,
- numberOfKeys,
- );
- notifAccount.mark_keys_as_published();
- storeOlmAccount(notifAccount);
-
- return { contentOneTimeKeys, notificationsOneTimeKeys };
- },
- async validateAndUploadPrekeys(
- identityContext: IdentityClientContextType,
- ): Promise<void> {
- const authMetadata = await identityContext.getAuthMetadata();
- const { userID, deviceID, accessToken } = authMetadata;
- if (!userID || !deviceID || !accessToken) {
- return;
- }
-
- const contentAccount = getOlmAccount();
- if (shouldRotatePrekey(contentAccount)) {
- contentAccount.generate_prekey();
- }
- if (shouldForgetPrekey(contentAccount)) {
- contentAccount.forget_old_prekey();
- }
- await storeOlmAccount(contentAccount);
-
- if (!contentAccount.unpublished_prekey()) {
- return;
- }
-
- const notifAccount = getOlmAccount();
- const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } =
- getAccountPrekeysSet(notifAccount);
- const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } =
- getAccountPrekeysSet(contentAccount);
-
- if (!notifPrekeySignature || !contentPrekeySignature) {
- throw new Error('Prekey signature is missing');
- }
-
- if (!identityContext.identityClient.publishWebPrekeys) {
- throw new Error('Publish prekeys method unimplemented');
- }
- await identityContext.identityClient.publishWebPrekeys({
- contentPrekey,
- contentPrekeySignature,
- notifPrekey,
- notifPrekeySignature,
- });
- contentAccount.mark_keys_as_published();
- await storeOlmAccount(contentAccount);
- },
+ encrypt: proxyToWorker('encrypt'),
+ decrypt: proxyToWorker('decrypt'),
+ contentInboundSessionCreator: proxyToWorker('contentInboundSessionCreator'),
+ getOneTimeKeys: proxyToWorker('getOneTimeKeys'),
+ validateAndUploadPrekeys: proxyToWorker('validateAndUploadPrekeys'),
};
export { olmAPI, usingSharedWorker };
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
@@ -253,7 +253,7 @@
let result;
if (isOlmAPIRequest) {
- await processAppOlmApiRequest(message);
+ result = await processAppOlmApiRequest(message);
} else if (isIdentityClientRequest) {
result = await processAppIdentityClientRequest(
sqliteQueryExecutor,
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,11 +3,15 @@
import olm from '@commapp/olm';
import uuid from 'uuid';
-import type {
- CryptoStore,
- PickledOLMAccount,
- IdentityKeysBlob,
- SignedIdentityKeysBlob,
+import {
+ olmEncryptedMessageTypes,
+ type OLMIdentityKeys,
+ type CryptoStore,
+ type PickledOLMAccount,
+ type IdentityKeysBlob,
+ type SignedIdentityKeysBlob,
+ type OlmAPI,
+ type OneTimeKeysResultValues,
} from 'lib/types/crypto-types.js';
import type {
IdentityNewDeviceKeyUpload,
@@ -15,16 +19,22 @@
} from 'lib/types/identity-service-types.js';
import { entries } from 'lib/utils/objects.js';
import {
- retrieveIdentityKeysAndPrekeys,
retrieveAccountKeysSet,
+ getAccountOneTimeKeys,
+ getAccountPrekeysSet,
+ shouldForgetPrekey,
+ shouldRotatePrekey,
+ retrieveIdentityKeysAndPrekeys,
} from 'lib/utils/olm-utils.js';
+import { getIdentityClient } from './identity-client.js';
import { getProcessingStoreOpsExceptionMessage } from './process-operations.js';
import { getDBModule, getSQLiteQueryExecutor } from './worker-database.js';
import {
type WorkerRequestMessage,
type WorkerResponseMessage,
workerRequestMessageTypes,
+ workerResponseMessageTypes,
} from '../../types/worker-types.js';
import type { OlmPersistSession } from '../types/sqlite-query-executor.js';
@@ -194,23 +204,7 @@
return;
}
- const contentAccountResult = getOrCreateOlmAccount(
- sqliteQueryExecutor.getContentAccountID(),
- );
- const contentSessions = getOlmSessions(contentAccountResult.picklingKey);
- const notificationAccountResult = getOrCreateOlmAccount(
- sqliteQueryExecutor.getNotifsAccountID(),
- );
-
- cryptoStore = {
- contentAccountPickleKey: contentAccountResult.picklingKey,
- contentAccount: contentAccountResult.account,
- contentSessions,
- notificationAccountPickleKey: notificationAccountResult.picklingKey,
- notificationAccount: notificationAccountResult.account,
- };
-
- persistCryptoStore();
+ await olmAPI.initializeCryptoAccount();
}
async function processAppOlmApiRequest(
@@ -221,7 +215,19 @@
message.olmWasmPath,
message.initialCryptoStore,
);
+ } else if (message.type === workerRequestMessageTypes.CALL_OLM_API_METHOD) {
+ const method: (...$ReadOnlyArray<mixed>) => mixed = (olmAPI[
+ message.method
+ ]: any);
+ // Flow doesn't allow us to bind the (stringified) method name with
+ // the argument types so we need to pass the args as mixed.
+ const result = await method(...message.args);
+ return {
+ type: workerResponseMessageTypes.CALL_OLM_API_METHOD,
+ result,
+ };
}
+ return undefined;
}
function getSignedIdentityKeysBlob(): SignedIdentityKeysBlob {
@@ -298,6 +304,167 @@
};
}
+const olmAPI: OlmAPI = {
+ async initializeCryptoAccount(): Promise<void> {
+ const sqliteQueryExecutor = getSQLiteQueryExecutor();
+ if (!sqliteQueryExecutor) {
+ throw new Error('Database not initialized');
+ }
+
+ const contentAccountResult = getOrCreateOlmAccount(
+ sqliteQueryExecutor.getContentAccountID(),
+ );
+ const notificationAccountResult = getOrCreateOlmAccount(
+ sqliteQueryExecutor.getNotifsAccountID(),
+ );
+ const contentSessions = getOlmSessions(contentAccountResult.picklingKey);
+
+ cryptoStore = {
+ contentAccountPickleKey: contentAccountResult.picklingKey,
+ contentAccount: contentAccountResult.account,
+ contentSessions,
+ notificationAccountPickleKey: notificationAccountResult.picklingKey,
+ notificationAccount: notificationAccountResult.account,
+ };
+
+ persistCryptoStore();
+ },
+ async encrypt(content: string, deviceID: string): Promise<string> {
+ if (!cryptoStore) {
+ throw new Error('Crypto account not initialized');
+ }
+ const session = cryptoStore.contentSessions[deviceID];
+ if (!session) {
+ throw new Error(`No session for deviceID: ${deviceID}`);
+ }
+ const { body } = session.encrypt(content);
+
+ persistCryptoStore();
+
+ return body;
+ },
+ async decrypt(encryptedContent: string, deviceID: string): Promise<string> {
+ if (!cryptoStore) {
+ throw new Error('Crypto account not initialized');
+ }
+
+ const session = cryptoStore.contentSessions[deviceID];
+ if (!session) {
+ throw new Error(`No session for deviceID: ${deviceID}`);
+ }
+
+ const result = session.decrypt(
+ olmEncryptedMessageTypes.TEXT,
+ encryptedContent,
+ );
+
+ persistCryptoStore();
+
+ return result;
+ },
+ async contentInboundSessionCreator(
+ contentIdentityKeys: OLMIdentityKeys,
+ initialEncryptedContent: string,
+ ): Promise<string> {
+ if (!cryptoStore) {
+ throw new Error('Crypto account not initialized');
+ }
+ const { contentAccount, contentSessions } = cryptoStore;
+
+ const session = new olm.Session();
+ session.create_inbound_from(
+ contentAccount,
+ contentIdentityKeys.curve25519,
+ initialEncryptedContent,
+ );
+
+ contentAccount.remove_one_time_keys(session);
+ const initialEncryptedMessage = session.decrypt(
+ olmEncryptedMessageTypes.PREKEY,
+ initialEncryptedContent,
+ );
+
+ contentSessions[contentIdentityKeys.ed25519] = session;
+ persistCryptoStore();
+
+ return initialEncryptedMessage;
+ },
+ async getOneTimeKeys(numberOfKeys: number): Promise<OneTimeKeysResultValues> {
+ if (!cryptoStore) {
+ throw new Error('Crypto account not initialized');
+ }
+ const { contentAccount, notificationAccount } = cryptoStore;
+
+ const contentOneTimeKeys = getAccountOneTimeKeys(
+ contentAccount,
+ numberOfKeys,
+ );
+ contentAccount.mark_keys_as_published();
+
+ const notificationsOneTimeKeys = getAccountOneTimeKeys(
+ notificationAccount,
+ numberOfKeys,
+ );
+ notificationAccount.mark_keys_as_published();
+
+ persistCryptoStore();
+
+ return { contentOneTimeKeys, notificationsOneTimeKeys };
+ },
+ async validateAndUploadPrekeys(authMetadata): Promise<void> {
+ const { userID, deviceID, accessToken } = authMetadata;
+ if (!userID || !deviceID || !accessToken) {
+ return;
+ }
+ const identityClient = getIdentityClient();
+
+ if (!identityClient) {
+ throw new Error('Identity client not initialized');
+ }
+
+ if (!cryptoStore) {
+ throw new Error('Crypto account not initialized');
+ }
+ const { contentAccount, notificationAccount } = cryptoStore;
+
+ // Content and notification accounts' keys are always rotated at the same
+ // time so we only need to check one of them.
+ if (shouldRotatePrekey(contentAccount)) {
+ contentAccount.generate_prekey();
+ notificationAccount.generate_prekey();
+ }
+ if (shouldForgetPrekey(contentAccount)) {
+ contentAccount.forget_old_prekey();
+ notificationAccount.forget_old_prekey();
+ }
+ persistCryptoStore();
+
+ if (!contentAccount.unpublished_prekey()) {
+ return;
+ }
+
+ const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } =
+ getAccountPrekeysSet(notificationAccount);
+ const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } =
+ getAccountPrekeysSet(contentAccount);
+
+ if (!notifPrekeySignature || !contentPrekeySignature) {
+ throw new Error('Prekey signature is missing');
+ }
+
+ await identityClient.publishWebPrekeys({
+ contentPrekey,
+ contentPrekeySignature,
+ notifPrekey,
+ notifPrekeySignature,
+ });
+ contentAccount.mark_prekey_as_published();
+ notificationAccount.mark_prekey_as_published();
+
+ persistCryptoStore();
+ },
+};
+
export {
clearCryptoStore,
processAppOlmApiRequest,
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,7 +1,7 @@
// @flow
import type { AuthMetadata } from 'lib/shared/identity-client-context.js';
-import type { CryptoStore } from 'lib/types/crypto-types.js';
+import type { OlmAPI, CryptoStore } from 'lib/types/crypto-types.js';
import type { PlatformDetails } from 'lib/types/device-types.js';
import type {
IdentityServiceClient,
@@ -29,6 +29,7 @@
INITIALIZE_CRYPTO_ACCOUNT: 12,
CREATE_IDENTITY_SERVICE_CLIENT: 13,
CALL_IDENTITY_CLIENT_METHOD: 14,
+ CALL_OLM_API_METHOD: 15,
});
export const workerWriteRequests: $ReadOnlyArray<number> = [
@@ -42,6 +43,7 @@
export const workerOlmAPIRequests: $ReadOnlyArray<number> = [
workerRequestMessageTypes.INITIALIZE_CRYPTO_ACCOUNT,
+ workerRequestMessageTypes.CALL_OLM_API_METHOD,
];
export const workerIdentityClientRequests: $ReadOnlyArray<number> = [
@@ -131,6 +133,12 @@
+args: $ReadOnlyArray<mixed>,
};
+export type CallOLMApiMethodRequestMessage = {
+ +type: 15,
+ +method: $Keys<OlmAPI>,
+ +args: $ReadOnlyArray<mixed>,
+};
+
export type WorkerRequestMessage =
| PingWorkerRequestMessage
| InitWorkerRequestMessage
@@ -146,7 +154,8 @@
| BackupRestoreRequestMessage
| InitializeCryptoAccountRequestMessage
| CreateIdentityServiceClientRequestMessage
- | CallIdentityClientMethodRequestMessage;
+ | CallIdentityClientMethodRequestMessage
+ | CallOLMApiMethodRequestMessage;
export type WorkerRequestProxyMessage = {
+id: number,
@@ -160,6 +169,7 @@
GET_CURRENT_USER_ID: 2,
GET_PERSIST_STORAGE_ITEM: 3,
CALL_IDENTITY_CLIENT_METHOD: 4,
+ CALL_OLM_API_METHOD: 5,
});
export type PongWorkerResponseMessage = {
@@ -187,12 +197,18 @@
+result: mixed,
};
+export type CallOLMApiMethodResponseMessage = {
+ +type: 5,
+ +result: mixed,
+};
+
export type WorkerResponseMessage =
| PongWorkerResponseMessage
| ClientStoreResponseMessage
| GetCurrentUserIDResponseMessage
| GetPersistStorageItemResponseMessage
- | CallIdentityClientMethodResponseMessage;
+ | CallIdentityClientMethodResponseMessage
+ | CallOLMApiMethodResponseMessage;
export type WorkerResponseProxyMessage = {
+id?: number,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 29, 5:23 PM (20 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2597611
Default Alt Text
D11338.diff (18 KB)
Attached To
Mode
D11338: [web] olmAPI on shared worker
Attached
Detach File
Event Timeline
Log In to Comment