Page MenuHomePhabricator

D11338.diff
No OneTemporary

D11338.diff

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

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)

Event Timeline