Page MenuHomePhorge

D15548.1765007495.diff
No OneTemporary

Size
42 KB
Referenced Files
None
Subscribers
None

D15548.1765007495.diff

diff --git a/keyserver/package.json b/keyserver/package.json
--- a/keyserver/package.json
+++ b/keyserver/package.json
@@ -118,7 +118,7 @@
]
},
"transformIgnorePatterns": [
- "/node_modules/(?!@babel/runtime)"
+ "/node_modules/(?!(@babel/runtime|vodozemac))"
],
"setupFiles": [
"<rootDir>/jest-setup.js"
diff --git a/keyserver/src/creators/olm-session-creator.js b/keyserver/src/creators/olm-session-creator.js
--- a/keyserver/src/creators/olm-session-creator.js
+++ b/keyserver/src/creators/olm-session-creator.js
@@ -1,6 +1,6 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
+import type { Account as OlmAccount } from 'vodozemac';
import { ServerError } from 'lib/utils/errors.js';
diff --git a/keyserver/src/cron/cron.js b/keyserver/src/cron/cron.js
--- a/keyserver/src/cron/cron.js
+++ b/keyserver/src/cron/cron.js
@@ -1,9 +1,9 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
import olm from '@commapp/olm';
import cluster from 'cluster';
import schedule from 'node-schedule';
+import type { Account as OlmAccount } from 'vodozemac';
import {
getOlmMemory,
diff --git a/keyserver/src/database/migration-config.js b/keyserver/src/database/migration-config.js
--- a/keyserver/src/database/migration-config.js
+++ b/keyserver/src/database/migration-config.js
@@ -1,7 +1,7 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
import fs from 'fs';
+import type { Account as OlmAccount } from 'vodozemac';
import bots from 'lib/facts/bots.js';
import genesis from 'lib/facts/genesis.js';
diff --git a/keyserver/src/push/encrypted-notif-utils-api.js b/keyserver/src/push/encrypted-notif-utils-api.js
--- a/keyserver/src/push/encrypted-notif-utils-api.js
+++ b/keyserver/src/push/encrypted-notif-utils-api.js
@@ -1,7 +1,6 @@
// @flow
-import type { EncryptResult } from '@commapp/olm';
-
+import type { EncryptResult } from 'lib/types/encrypted-type.js';
import type { EncryptedNotifUtilsAPI } from 'lib/types/notif-types.js';
import { getOlmUtility } from 'lib/utils/olm-utility.js';
diff --git a/keyserver/src/responders/keys-responders.js b/keyserver/src/responders/keys-responders.js
--- a/keyserver/src/responders/keys-responders.js
+++ b/keyserver/src/responders/keys-responders.js
@@ -1,6 +1,6 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
+import type { Account as OlmAccount } from 'vodozemac';
import type {
OlmSessionInitializationInfo,
@@ -18,20 +18,39 @@
function retrieveSessionInitializationKeysSet(
account: OlmAccount,
): SessionInitializationKeysSet {
- const identityKeys = account.identity_keys();
+ const identityKeys = JSON.stringify({
+ ed25519: account.ed25519_key,
+ curve25519: account.curve25519_key,
+ });
const prekey = account.prekey();
- const prekeySignature = account.prekey_signature();
+ if (!prekey) {
+ throw new ServerError('missing_prekey');
+ }
+
+ // Wrap prekey in old Olm format to match expected structure on all clients
+ const prekeyWrapped = JSON.stringify({
+ curve25519: { AAAAAA: prekey },
+ });
+ const prekeySignature = account.prekey_signature();
if (!prekeySignature) {
throw new ServerError('invalid_prekey');
}
account.generate_one_time_keys(1);
- const oneTimeKey = account.one_time_keys();
+ const oneTimeKeysMap = account.one_time_keys();
+ const oneTimeKeysEntries = Array.from(oneTimeKeysMap.entries());
+ const oneTimeKeysObject = Object.fromEntries(oneTimeKeysEntries);
+ const oneTimeKey = JSON.stringify({ curve25519: oneTimeKeysObject });
account.mark_keys_as_published();
- return { identityKeys, oneTimeKey, prekey, prekeySignature };
+ return {
+ identityKeys,
+ oneTimeKey,
+ prekey: prekeyWrapped,
+ prekeySignature,
+ };
}
async function getOlmSessionInitializationDataResponder(): Promise<GetOlmSessionInitializationDataResponse> {
diff --git a/keyserver/src/responders/user-responders.js b/keyserver/src/responders/user-responders.js
--- a/keyserver/src/responders/user-responders.js
+++ b/keyserver/src/responders/user-responders.js
@@ -1,10 +1,10 @@
// @flow
-import type { Utility as OlmUtility } from '@commapp/olm';
import invariant from 'invariant';
import { SiweErrorType, SiweMessage } from 'siwe';
import t, { type TInterface } from 'tcomb';
import bcrypt from 'twin-bcrypt';
+import type { Utility as OlmUtility } from 'vodozemac';
import {
baseLegalPolicies,
diff --git a/keyserver/src/updaters/olm-account-updater.js b/keyserver/src/updaters/olm-account-updater.js
--- a/keyserver/src/updaters/olm-account-updater.js
+++ b/keyserver/src/updaters/olm-account-updater.js
@@ -1,6 +1,6 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
+import { type Account as OlmAccount } from 'vodozemac';
import { ServerError } from 'lib/utils/errors.js';
import sleep from 'lib/utils/sleep.js';
diff --git a/keyserver/src/updaters/olm-session-updater.js b/keyserver/src/updaters/olm-session-updater.js
--- a/keyserver/src/updaters/olm-session-updater.js
+++ b/keyserver/src/updaters/olm-session-updater.js
@@ -1,7 +1,8 @@
// @flow
-import type { EncryptResult, Session as OlmSession } from '@commapp/olm';
+import type { Session as OlmSession } from 'vodozemac';
+import type { EncryptResult } from 'lib/types/encrypted-type.js';
import { ServerError } from 'lib/utils/errors.js';
import sleep from 'lib/utils/sleep.js';
@@ -56,9 +57,11 @@
},
(olmSession: OlmSession) => {
for (const messageName in messagesToEncrypt) {
- encryptedMessages[messageName] = olmSession.encrypt(
- messagesToEncrypt[messageName],
- );
+ const olmMessage = olmSession.encrypt(messagesToEncrypt[messageName]);
+ encryptedMessages[messageName] = {
+ type: olmMessage.message_type,
+ body: olmMessage.ciphertext,
+ };
}
},
);
diff --git a/keyserver/src/user/login.js b/keyserver/src/user/login.js
--- a/keyserver/src/user/login.js
+++ b/keyserver/src/user/login.js
@@ -1,7 +1,7 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
import { getRustAPI } from 'rust-node-addon';
+import type { Account as OlmAccount } from 'vodozemac';
import { getCommConfig } from 'lib/utils/comm-config.js';
import { ServerError } from 'lib/utils/errors.js';
diff --git a/keyserver/src/utils/olm-objects.js b/keyserver/src/utils/olm-objects.js
--- a/keyserver/src/utils/olm-objects.js
+++ b/keyserver/src/utils/olm-objects.js
@@ -1,13 +1,20 @@
// @flow
-import olm, {
+import uuid from 'uuid';
+import {
type Account as OlmAccount,
+ Account,
+ OlmMessage,
type Session as OlmSession,
-} from '@commapp/olm';
-import uuid from 'uuid';
+} from 'vodozemac';
import { olmEncryptedMessageTypes } from 'lib/types/crypto-types.js';
import { ServerError } from 'lib/utils/errors.js';
+import {
+ getVodozemacPickleKey,
+ unpickleVodozemacAccount,
+ unpickleVodozemacSession,
+} from 'lib/utils/vodozemac-utils.js';
import { getMessageForException } from '../responders/utils.js';
@@ -20,16 +27,12 @@
pickledOlmAccount: PickledOlmAccount,
callback: (account: OlmAccount, picklingKey: string) => Promise<T> | T,
): Promise<{ +result: T, +pickledOlmAccount: PickledOlmAccount }> {
- const { picklingKey, pickledAccount } = pickledOlmAccount;
-
- await olm.init();
-
- const account = new olm.Account();
- account.unpickle(picklingKey, pickledAccount);
+ const { picklingKey } = pickledOlmAccount;
+ const account = unpickleVodozemacAccount(pickledOlmAccount);
try {
const result = await callback(account, picklingKey);
- const updatedAccount = account.pickle(picklingKey);
+ const updatedAccount = account.pickle(getVodozemacPickleKey(picklingKey));
return {
result,
pickledOlmAccount: {
@@ -45,14 +48,10 @@
}
async function createPickledOlmAccount(): Promise<PickledOlmAccount> {
- await olm.init();
-
- const account = new olm.Account();
- account.create();
+ const account = new Account();
const picklingKey = uuid.v4();
- const pickledAccount = account.pickle(picklingKey);
-
+ const pickledAccount = account.pickle(getVodozemacPickleKey(picklingKey));
account.free();
return {
@@ -69,16 +68,12 @@
pickledOlmSession: PickledOlmSession,
callback: (session: OlmSession) => Promise<T> | T,
): Promise<{ +result: T, +pickledOlmSession: PickledOlmSession }> {
- const { picklingKey, pickledSession } = pickledOlmSession;
-
- await olm.init();
-
- const session = new olm.Session();
- session.unpickle(picklingKey, pickledSession);
+ const { picklingKey } = pickledOlmSession;
+ const session = unpickleVodozemacSession(pickledOlmSession);
try {
const result = await callback(session);
- const updatedSession = session.pickle(picklingKey);
+ const updatedSession = session.pickle(getVodozemacPickleKey(picklingKey));
return {
result,
pickledOlmSession: {
@@ -99,19 +94,20 @@
initialEncryptedMessage: string,
theirCurve25519Key: string,
): Promise<string> {
- await olm.init();
- const session = new olm.Session();
-
- session.create_inbound_from(
- account,
- theirCurve25519Key,
+ const olmMessage = new OlmMessage(
+ olmEncryptedMessageTypes.PREKEY,
initialEncryptedMessage,
);
-
- account.remove_one_time_keys(session);
- session.decrypt(olmEncryptedMessageTypes.PREKEY, initialEncryptedMessage);
- const pickledSession = session.pickle(accountPicklingKey);
-
+ const inboundCreationResult = account.create_inbound_session(
+ theirCurve25519Key,
+ olmMessage,
+ );
+ // into_session() is consuming object.
+ // There is no need to call free() on inboundCreationResult
+ const session = inboundCreationResult.into_session();
+ const pickledSession = session.pickle(
+ getVodozemacPickleKey(accountPicklingKey),
+ );
session.free();
return pickledSession;
diff --git a/keyserver/src/utils/olm-utils.js b/keyserver/src/utils/olm-utils.js
--- a/keyserver/src/utils/olm-utils.js
+++ b/keyserver/src/utils/olm-utils.js
@@ -1,9 +1,8 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
import invariant from 'invariant';
+import { type Account as OlmAccount } from 'vodozemac';
-import { getOneTimeKeyValuesFromBlob } from 'lib/shared/crypto-utils.js';
import type { IdentityNewDeviceKeyUpload } from 'lib/types/identity-service-types.js';
import { ServerError } from 'lib/utils/errors.js';
import {
@@ -134,16 +133,12 @@
await Promise.all([
fetchCallUpdateOlmAccount('content', (contentAccount: OlmAccount) => {
contentAccount.generate_one_time_keys(numberOfKeys);
- contentOneTimeKeys = getOneTimeKeyValuesFromBlob(
- contentAccount.one_time_keys(),
- );
+ contentOneTimeKeys = [...contentAccount.one_time_keys().values()];
contentAccount.mark_keys_as_published();
}),
fetchCallUpdateOlmAccount('notifications', (notifAccount: OlmAccount) => {
notifAccount.generate_one_time_keys(numberOfKeys);
- notifOneTimeKeys = getOneTimeKeyValuesFromBlob(
- notifAccount.one_time_keys(),
- );
+ notifOneTimeKeys = [...notifAccount.one_time_keys().values()];
notifAccount.mark_keys_as_published();
}),
]);
@@ -164,7 +159,7 @@
const pickledOlmAccount = await fetchPickledOlmAccount('content');
const getAccountEd25519Key: (account: OlmAccount) => string = (
account: OlmAccount,
- ) => JSON.parse(account.identity_keys()).ed25519;
+ ) => account.ed25519_key;
const { result } = await unpickleAccountAndUseCallback(
pickledOlmAccount,
@@ -211,7 +206,7 @@
contentAccount: OlmAccount,
notifAccount: OlmAccount,
): Promise<void> {
- const deviceID = JSON.parse(contentAccount.identity_keys()).ed25519;
+ const deviceID = contentAccount.ed25519_key;
const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } =
getAccountPrekeysSet(contentAccount);
diff --git a/keyserver/src/utils/olm-utils.test.js b/keyserver/src/utils/olm-utils.test.js
--- a/keyserver/src/utils/olm-utils.test.js
+++ b/keyserver/src/utils/olm-utils.test.js
@@ -2,8 +2,6 @@
import olm from '@commapp/olm';
-import { getOlmUtility } from 'lib/utils/olm-utility.js';
-
describe('olm.Account', () => {
const alphabet =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ';
@@ -220,12 +218,6 @@
return true;
};
- it('should get Olm Utility', async () => {
- await olm.init();
- const utility = getOlmUtility();
- expect(utility).toBeDefined();
- });
-
it('should generate, regenerate, forget, and publish prekey', async () => {
await olm.init();
const account = initAccount(false);
diff --git a/lib/shared/crypto-utils.js b/lib/shared/crypto-utils.js
--- a/lib/shared/crypto-utils.js
+++ b/lib/shared/crypto-utils.js
@@ -84,6 +84,10 @@
);
}
+// Methods below are now considered to be deprecated. Vodozemac uses a different
+// API and there is no need to parse prekey and OTKs. The only exception is
+// `get_olm_session_initialization_data` which still returns keys in the old
+// format to support older clients.
function getOneTimeKeyValues(
oneTimeKeys: OLMOneTimeKeys,
): $ReadOnlyArray<string> {
diff --git a/lib/types/encrypted-type.js b/lib/types/encrypted-type.js
new file mode 100644
--- /dev/null
+++ b/lib/types/encrypted-type.js
@@ -0,0 +1,6 @@
+// @flow
+
+export type EncryptResult = {
+ +type: 0 | 1, // 0: PreKey, 1: Message
+ +body: string,
+};
diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js
--- a/lib/types/notif-types.js
+++ b/lib/types/notif-types.js
@@ -1,8 +1,8 @@
// @flow
-import type { EncryptResult } from '@commapp/olm';
import t, { type TInterface, type TUnion } from 'tcomb';
+import type { EncryptResult } from './encrypted-type.js';
import type { MessageData, RawMessageInfo } from './message-types.js';
import type { ThickRawThreadInfos } from './thread-types.js';
import type { EntityText, ThreadEntity } from '../utils/entity-text.js';
diff --git a/lib/utils/olm-utility.js b/lib/utils/olm-utility.js
--- a/lib/utils/olm-utility.js
+++ b/lib/utils/olm-utility.js
@@ -1,16 +1,15 @@
// @flow
-import type { Utility as OlmUtility } from '@commapp/olm';
-import olm from '@commapp/olm';
+import { Utility } from 'vodozemac';
-let cachedOlmUtility: OlmUtility;
-function getOlmUtility(): OlmUtility {
+let cachedOlmUtility: Utility;
+function getOlmUtility(): Utility {
if (cachedOlmUtility) {
return cachedOlmUtility;
}
- // This `olm.Utility` is created once and is cached for the entire
+ // This `Utility` is created once and is cached for the entire
// program lifetime, there is no need to free the memory.
- cachedOlmUtility = new olm.Utility();
+ cachedOlmUtility = new Utility();
return cachedOlmUtility;
}
diff --git a/lib/utils/olm-utils.js b/lib/utils/olm-utils.js
--- a/lib/utils/olm-utils.js
+++ b/lib/utils/olm-utils.js
@@ -1,11 +1,7 @@
// @flow
-import type { Account as OlmAccount } from '@commapp/olm';
+import type { Account as VodozemacAccount } from 'vodozemac';
-import {
- getOneTimeKeyValuesFromBlob,
- getPrekeyValueFromBlob,
-} from '../shared/crypto-utils.js';
import { ONE_TIME_KEYS_NUMBER } from '../types/identity-service-types.js';
const maxPublishedPrekeyAge = 30 * 24 * 60 * 60 * 1000; // 30 days
@@ -18,7 +14,7 @@
+oneTimeKeys: $ReadOnlyArray<string>,
};
-function validateAccountPrekey(account: OlmAccount) {
+function validateAccountPrekey(account: VodozemacAccount) {
if (shouldRotatePrekey(account)) {
account.generate_prekey();
}
@@ -27,7 +23,7 @@
}
}
-function shouldRotatePrekey(account: OlmAccount): boolean {
+function shouldRotatePrekey(account: VodozemacAccount): boolean {
// Our fork of Olm only remembers two prekeys at a time.
// If the new one hasn't been published, then the old one is still active.
// In that scenario, we need to avoid rotating the prekey because it will
@@ -45,7 +41,7 @@
);
}
-function shouldForgetPrekey(account: OlmAccount): boolean {
+function shouldForgetPrekey(account: VodozemacAccount): boolean {
// Our fork of Olm only remembers two prekeys at a time.
// We have to hold onto the old one until the new one is published.
if (account.unpublished_prekey()) {
@@ -60,36 +56,42 @@
);
}
-function getLastPrekeyPublishTime(account: OlmAccount): Date {
- const olmLastPrekeyPublishTime = account.last_prekey_publish_time();
+function getLastPrekeyPublishTime(account: VodozemacAccount): Date {
+ const vodozemacLastPrekeyPublishTime = account.last_prekey_publish_time();
- // Olm uses seconds, while the Date() constructor expects milliseconds.
- return new Date(olmLastPrekeyPublishTime * 1000);
+ // Vodozemac uses seconds, while the Date() constructor expects milliseconds.
+ return new Date(Number(vodozemacLastPrekeyPublishTime) * 1000);
}
-function getAccountPrekeysSet(account: OlmAccount): {
+function getAccountPrekeysSet(account: VodozemacAccount): {
+prekey: string,
+prekeySignature: ?string,
} {
- const prekey = getPrekeyValueFromBlob(account.prekey());
+ const prekey = account.prekey();
+ if (!prekey) {
+ throw Error('Prekey is missing');
+ }
const prekeySignature = account.prekey_signature();
return { prekey, prekeySignature };
}
function getAccountOneTimeKeys(
- account: OlmAccount,
+ account: VodozemacAccount,
numberOfKeys: number = ONE_TIME_KEYS_NUMBER,
): $ReadOnlyArray<string> {
- let oneTimeKeys = getOneTimeKeyValuesFromBlob(account.one_time_keys());
+ let oneTimeKeys = [...account.one_time_keys().values()];
if (oneTimeKeys.length < numberOfKeys) {
account.generate_one_time_keys(numberOfKeys - oneTimeKeys.length);
- oneTimeKeys = getOneTimeKeyValuesFromBlob(account.one_time_keys());
+ oneTimeKeys = [...account.one_time_keys().values()];
}
return oneTimeKeys;
}
-function retrieveAccountKeysSet(account: OlmAccount): AccountKeysSet {
- const identityKeys = account.identity_keys();
+function retrieveAccountKeysSet(account: VodozemacAccount): AccountKeysSet {
+ const identityKeys = JSON.stringify({
+ ed25519: account.ed25519_key,
+ curve25519: account.curve25519_key,
+ });
validateAccountPrekey(account);
const { prekey, prekeySignature } = getAccountPrekeysSet(account);
diff --git a/lib/utils/vodozemac-utils.js b/lib/utils/vodozemac-utils.js
new file mode 100644
--- /dev/null
+++ b/lib/utils/vodozemac-utils.js
@@ -0,0 +1,61 @@
+// @flow
+
+import { Account, Session } from 'vodozemac';
+
+// Helper function to get 32-byte pickle key for vodozemac
+function getVodozemacPickleKey(picklingKey: string): Uint8Array {
+ const fullKeyBytes = new TextEncoder().encode(picklingKey);
+ // NOTE: vodozemac works only with 32-byte keys.
+ // We have sessions pickled with 64-byte keys. Additionally, this key
+ // is used in backup, so it can't simply be migrated. Instead, we're going
+ // to just use the first 32 bytes of the existing secret key.
+ return fullKeyBytes.slice(0, 32);
+}
+
+function unpickleVodozemacAccount({
+ picklingKey,
+ pickledAccount,
+}: {
+ +picklingKey: string,
+ +pickledAccount: string,
+}): Account {
+ const fullKeyBytes = new TextEncoder().encode(picklingKey);
+ const keyBytes = getVodozemacPickleKey(picklingKey);
+ try {
+ // First try vodozemac native format
+ console.log('Account unpickle');
+ return Account.from_pickle(pickledAccount, keyBytes);
+ } catch (e) {
+ console.log(e);
+ // Fall back to libolm format
+ console.log('Account unpickle: fallback to libolm');
+ return Account.from_libolm_pickle(pickledAccount, fullKeyBytes);
+ }
+}
+
+function unpickleVodozemacSession({
+ picklingKey,
+ pickledSession,
+}: {
+ +picklingKey: string,
+ +pickledSession: string,
+}): Session {
+ const fullKeyBytes = new TextEncoder().encode(picklingKey);
+ const keyBytes = getVodozemacPickleKey(picklingKey);
+ try {
+ // First try vodozemac native format
+ console.log('Session unpickle');
+ return Session.from_pickle(pickledSession, keyBytes);
+ } catch (e) {
+ console.log(e);
+ // Fall back to libolm format
+ console.log('Session unpickle: fallback to libolm');
+ return Session.from_libolm_pickle(pickledSession, fullKeyBytes);
+ }
+}
+
+export {
+ getVodozemacPickleKey,
+ unpickleVodozemacAccount,
+ unpickleVodozemacSession,
+};
diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js
--- a/web/push-notif/notif-crypto-utils.js
+++ b/web/push-notif/notif-crypto-utils.js
@@ -1,11 +1,13 @@
// @flow
-import olm from '@commapp/olm';
import type { EncryptResult } from '@commapp/olm';
import invariant from 'invariant';
import localforage from 'localforage';
import uuid from 'uuid';
-import initVodozemac from 'vodozemac';
+import initVodozemac, {
+ OlmMessage,
+ type Account as VodozemacAccount,
+} from 'vodozemac';
import {
olmEncryptedMessageTypes,
@@ -23,6 +25,11 @@
import { getMessageForException } from 'lib/utils/errors.js';
import { promiseAll } from 'lib/utils/promises.js';
import { assertWithValidator } from 'lib/utils/validation-utils.js';
+import {
+ getVodozemacPickleKey,
+ unpickleVodozemacAccount,
+ unpickleVodozemacSession,
+} from 'lib/utils/vodozemac-utils.js';
import {
fetchAuthMetadata,
@@ -58,7 +65,7 @@
};
export type NotificationAccountWithPicklingKey = {
- +notificationAccount: olm.Account,
+ +notificationAccount: VodozemacAccount,
+picklingKey: string,
+synchronizationValue: ?string,
+accountEncryptionKey?: CryptoKey,
@@ -723,11 +730,10 @@
if (notificationsOlmData) {
// Memory is freed below in this condition.
- const session = new olm.Session();
- session.unpickle(
- notificationsOlmData.picklingKey,
- notificationsOlmData.pendingSessionUpdate,
- );
+ const session = unpickleVodozemacSession({
+ picklingKey: notificationsOlmData.picklingKey,
+ pickledSession: notificationsOlmData.pendingSessionUpdate,
+ });
isSenderChainEmpty = session.is_sender_chain_empty();
hasReceivedMessage = session.has_received_message();
@@ -762,14 +768,12 @@
);
// Memory is freed below after pickling.
- const account = new olm.Account();
- const session = new olm.Session();
+ let account;
+ let session;
+ let decryptedNotification: T;
try {
- account.unpickle(
- notificationAccount.picklingKey,
- notificationAccount.pickledAccount,
- );
+ account = unpickleVodozemacAccount(notificationAccount);
if (notifInboundKeys.error) {
throw new Error(notifInboundKeys.error);
@@ -780,20 +784,26 @@
'curve25519 must be present in notifs inbound keys',
);
- session.create_inbound_from(
- account,
+ const olmMessage = new OlmMessage(messageType, encryptedPayload);
+ const inboundCreationResult = account.create_inbound_session(
notifInboundKeys.curve25519,
- encryptedPayload,
+ olmMessage,
);
- account.remove_one_time_keys(session);
+ const decryptedString = inboundCreationResult.plaintext;
+ // into_session() is consuming object.
+ // There is no need to call free() on inboundCreationResult
+ session = inboundCreationResult.into_session();
+ olmMessage.free();
- const decryptedNotification: T = JSON.parse(
- session.decrypt(messageType, encryptedPayload),
- );
+ decryptedNotification = JSON.parse(decryptedString);
- const pickledOlmSession = session.pickle(notificationAccount.picklingKey);
- const pickledAccount = account.pickle(notificationAccount.picklingKey);
+ const pickledOlmSession = session.pickle(
+ getVodozemacPickleKey(notificationAccount.picklingKey),
+ );
+ const pickledAccount = account.pickle(
+ getVodozemacPickleKey(notificationAccount.picklingKey),
+ );
// session reset attempt or session initialization - handled the same
const sessionResetAttempt =
@@ -834,8 +844,8 @@
// any session state
return { decryptedNotification };
} finally {
- session.free();
- account.free();
+ session?.free();
+ account?.free();
}
}
@@ -846,12 +856,16 @@
type: OlmEncryptedMessageTypes,
): DecryptionResult<T> {
// Memory is freed below after pickling.
- const session = new olm.Session();
- session.unpickle(picklingKey, pickledSession);
- const decryptedNotification: T = JSON.parse(
- session.decrypt(type, encryptedPayload),
+ const session = unpickleVodozemacSession({ picklingKey, pickledSession });
+
+ const olmMessage = new OlmMessage(type, encryptedPayload);
+ const decryptedString = session.decrypt(olmMessage);
+ olmMessage.free();
+
+ const decryptedNotification: T = JSON.parse(decryptedString);
+ const newPendingSessionUpdate = session.pickle(
+ getVodozemacPickleKey(picklingKey),
);
- const newPendingSessionUpdate = session.pickle(picklingKey);
session.free();
const newUpdateCreationTimestamp = Date.now();
@@ -970,10 +984,22 @@
);
// Memory is freed below after pickling.
- const session = new olm.Session();
- session.unpickle(picklingKey, pendingSessionUpdate);
- const encryptedNotification = session.encrypt(payload);
- const newPendingSessionUpdate = session.pickle(picklingKey);
+
+ const session = unpickleVodozemacSession({
+ picklingKey,
+ pickledSession: pendingSessionUpdate,
+ });
+
+ const olmMessage = session.encrypt(payload);
+ const encryptedNotification = {
+ body: olmMessage.ciphertext,
+ type: olmMessage.message_type,
+ };
+ olmMessage.free();
+
+ const newPendingSessionUpdate = session.pickle(
+ getVodozemacPickleKey(picklingKey),
+ );
session.free();
const updatedOlmData: NotificationsOlmDataType = {
@@ -1040,10 +1066,9 @@
validatedNotifsAccountEncryptionKey,
);
- const { pickledAccount, picklingKey } = pickledOLMAccount;
+ const { picklingKey } = pickledOLMAccount;
- const notificationAccount = new olm.Account();
- notificationAccount.unpickle(picklingKey, pickledAccount);
+ const notificationAccount = unpickleVodozemacAccount(pickledOLMAccount);
return {
notificationAccount,
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
@@ -1,27 +1,29 @@
// @flow
-import olm, {
- type Account as OlmAccount,
- type Utility as OlmUtility,
-} from '@commapp/olm';
import base64 from 'base-64';
import localforage from 'localforage';
import uuid from 'uuid';
-import initVodozemac from 'vodozemac';
+import initVodozemac, {
+ Account,
+ type Account as VodozemacAccount,
+ OlmMessage,
+ Session,
+ Utility,
+} from 'vodozemac';
import { initialEncryptedMessageContent } from 'lib/shared/crypto-utils.js';
import { hasMinCodeVersion } from 'lib/shared/version-utils.js';
import {
- type OLMIdentityKeys,
- type PickledOLMAccount,
+ type ClientPublicKeys,
+ type EncryptedData,
type IdentityKeysBlob,
- type SignedIdentityKeysBlob,
+ type NotificationsOlmDataType,
type OlmAPI,
+ type OLMIdentityKeys,
type OneTimeKeysResultValues,
- type ClientPublicKeys,
- type NotificationsOlmDataType,
- type EncryptedData,
type OutboundSessionCreationResult,
+ type PickledOLMAccount,
+ type SignedIdentityKeysBlob,
} from 'lib/types/crypto-types.js';
import type { PlatformDetails } from 'lib/types/device-types.js';
import type { IdentityNewDeviceKeyUpload } from 'lib/types/identity-service-types.js';
@@ -29,52 +31,56 @@
import type { InboundP2PMessage } from 'lib/types/sqlite-types.js';
import { getMessageForException } from 'lib/utils/errors.js';
import { entries } from 'lib/utils/objects.js';
-import { verifyMemoryUsage } from 'lib/utils/olm-memory-utils.js';
import { getOlmUtility } from 'lib/utils/olm-utility.js';
import {
- retrieveAccountKeysSet,
getAccountOneTimeKeys,
getAccountPrekeysSet,
+ OLM_ERROR_FLAG,
+ olmSessionErrors,
+ retrieveAccountKeysSet,
shouldForgetPrekey,
shouldRotatePrekey,
- olmSessionErrors,
- OLM_ERROR_FLAG,
} from 'lib/utils/olm-utils.js';
+import {
+ getVodozemacPickleKey,
+ unpickleVodozemacAccount,
+ unpickleVodozemacSession,
+} from 'lib/utils/vodozemac-utils.js';
import { getIdentityClient } from './identity-client.js';
import { getProcessingStoreOpsExceptionMessage } from './process-operations.js';
import {
getDBModule,
- getSQLiteQueryExecutor,
getPlatformDetails,
+ getSQLiteQueryExecutor,
} from './worker-database.js';
import {
+ encryptNotification,
+ getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT,
getOlmDataKeyForCookie,
- getOlmEncryptionKeyDBLabelForCookie,
getOlmDataKeyForDeviceID,
+ getOlmEncryptionKeyDBLabelForCookie,
getOlmEncryptionKeyDBLabelForDeviceID,
- encryptNotification,
type NotificationAccountWithPicklingKey,
- getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT,
persistNotifsAccountWithOlmData,
} from '../../push-notif/notif-crypto-utils.js';
import {
+ type LegacyCryptoStore,
type WorkerRequestMessage,
- type WorkerResponseMessage,
workerRequestMessageTypes,
+ type WorkerResponseMessage,
workerResponseMessageTypes,
- type LegacyCryptoStore,
} from '../../types/worker-types.js';
import type { OlmPersistSession } from '../types/sqlite-query-executor.js';
-type OlmSession = { +session: olm.Session, +version: number };
+type OlmSession = { +session: Session, +version: number };
type OlmSessions = {
[deviceID: string]: OlmSession,
};
type WorkerCryptoStore = {
+contentAccountPickleKey: string,
- +contentAccount: olm.Account,
+ +contentAccount: VodozemacAccount,
+contentSessions: OlmSessions,
};
@@ -114,14 +120,18 @@
const pickledContentAccount: PickledOLMAccount = {
picklingKey: contentAccountPickleKey,
- pickledAccount: contentAccount.pickle(contentAccountPickleKey),
+ pickledAccount: contentAccount.pickle(
+ getVodozemacPickleKey(contentAccountPickleKey),
+ ),
};
const pickledContentSessions: OlmPersistSession[] = entries(
contentSessions,
).map(([targetDeviceID, sessionData]) => ({
targetDeviceID,
- sessionData: sessionData.session.pickle(contentAccountPickleKey),
+ sessionData: sessionData.session.pickle(
+ getVodozemacPickleKey(contentAccountPickleKey),
+ ),
version: sessionData.version,
}));
@@ -144,7 +154,9 @@
accountEncryptionKey,
} = notifsCryptoAccount;
- const pickledAccount = notificationAccount.pickle(picklingKey);
+ const pickledAccount = notificationAccount.pickle(
+ getVodozemacPickleKey(picklingKey),
+ );
const accountWithPicklingKey: PickledOLMAccount = {
pickledAccount,
picklingKey,
@@ -190,31 +202,23 @@
const notificationsPrekey = notificationsInitializationInfo.prekey;
// Memory is freed below after persisting.
- const session = new olm.Session();
- if (notificationsInitializationInfo.oneTimeKey) {
- session.create_outbound(
- notificationAccount,
- notificationsIdentityKeys.curve25519,
- notificationsIdentityKeys.ed25519,
- notificationsPrekey,
- notificationsInitializationInfo.prekeySignature,
- notificationsInitializationInfo.oneTimeKey,
- );
- } else {
- session.create_outbound_without_otk(
- notificationAccount,
- notificationsIdentityKeys.curve25519,
- notificationsIdentityKeys.ed25519,
- notificationsPrekey,
- notificationsInitializationInfo.prekeySignature,
- );
- }
- const { body: message, type: messageType } = session.encrypt(
+ const session = notificationAccount.create_outbound_session(
+ notificationsIdentityKeys.curve25519,
+ notificationsIdentityKeys.ed25519,
+ notificationsInitializationInfo.oneTimeKey || '',
+ notificationsPrekey,
+ notificationsInitializationInfo.prekeySignature,
+ true, // olmCompatibilityMode
+ );
+
+ const encryptedMessage = session.encrypt(
JSON.stringify(initialEncryptedMessageContent),
);
+ const message = encryptedMessage.ciphertext;
+ const messageType = encryptedMessage.message_type;
const mainSession = session.pickle(
- notificationAccountWithPicklingKey.picklingKey,
+ getVodozemacPickleKey(notificationAccountWithPicklingKey.picklingKey),
);
const notificationsOlmData: NotificationsOlmDataType = {
mainSession,
@@ -223,7 +227,9 @@
picklingKey,
};
- const pickledAccount = notificationAccount.pickle(picklingKey);
+ const pickledAccount = notificationAccount.pickle(
+ getVodozemacPickleKey(picklingKey),
+ );
const accountWithPicklingKey: PickledOLMAccount = {
pickledAccount,
picklingKey,
@@ -247,7 +253,7 @@
async function getOrCreateOlmAccount(accountIDInDB: number): Promise<{
+picklingKey: string,
- +account: olm.Account,
+ +account: VodozemacAccount,
+synchronizationValue?: ?string,
}> {
const sqliteQueryExecutor = getSQLiteQueryExecutor();
@@ -289,18 +295,18 @@
};
}
- // This `olm.Account` is created once and is cached for the entire
+ // This `Account` is created once and is cached for the entire
// program lifetime. Freeing is done as part of `clearCryptoStore`.
- const account = new olm.Account();
+ let account;
let picklingKey;
if (!accountDBString) {
picklingKey = uuid.v4();
- account.create();
+ account = new Account();
} else {
const dbAccount: PickledOLMAccount = JSON.parse(accountDBString);
picklingKey = dbAccount.picklingKey;
- account.unpickle(picklingKey, dbAccount.pickledAccount);
+ account = unpickleVodozemacAccount(dbAccount);
}
if (accountIDInDB === sqliteQueryExecutor.getNotifsAccountID()) {
@@ -329,10 +335,12 @@
const sessionsData: OlmSessions = {};
for (const persistedSession: OlmPersistSession of dbSessionsData) {
const { sessionData, version } = persistedSession;
- // This `olm.Session` is created once and is cached for the entire
+ // This `Session` is created once and is cached for the entire
// program lifetime. Freeing is done as part of `clearCryptoStore`.
- const session = new olm.Session();
- session.unpickle(picklingKey, sessionData);
+ const session = unpickleVodozemacSession({
+ picklingKey,
+ pickledSession: sessionData,
+ });
sessionsData[persistedSession.targetDeviceID] = {
session,
version,
@@ -344,13 +352,10 @@
function unpickleInitialCryptoStoreAccount(
account: PickledOLMAccount,
-): olm.Account {
- const { picklingKey, pickledAccount } = account;
- // This `olm.Account` is created once and is cached for the entire
+): VodozemacAccount {
+ // This `Account` is created once and is cached for the entire
// program lifetime. Freeing is done as part of `clearCryptoStore`.
- const olmAccount = new olm.Account();
- olmAccount.unpickle(picklingKey, pickledAccount);
- return olmAccount;
+ return unpickleVodozemacAccount(account);
}
async function initializeCryptoAccount(
@@ -397,7 +402,6 @@
message.vodozemacWasmPath,
message.initialCryptoStore,
);
- verifyMemoryUsage('INITIALIZE_CRYPTO_ACCOUNT');
} else if (message.type === workerRequestMessageTypes.CALL_OLM_API_METHOD) {
const method: (...$ReadOnlyArray<mixed>) => mixed = (olmAPI[
message.method
@@ -405,7 +409,6 @@
// 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);
- verifyMemoryUsage(message.method);
return {
type: workerResponseMessageTypes.CALL_OLM_API_METHOD,
result,
@@ -424,10 +427,14 @@
await getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT();
const identityKeysBlob: IdentityKeysBlob = {
- notificationIdentityPublicKeys: JSON.parse(
- notificationAccount.identity_keys(),
- ),
- primaryIdentityPublicKeys: JSON.parse(contentAccount.identity_keys()),
+ notificationIdentityPublicKeys: {
+ ed25519: notificationAccount.ed25519_key,
+ curve25519: notificationAccount.curve25519_key,
+ },
+ primaryIdentityPublicKeys: {
+ ed25519: contentAccount.ed25519_key,
+ curve25519: contentAccount.curve25519_key,
+ },
};
const payloadToBeSigned: string = JSON.stringify(identityKeysBlob);
@@ -447,13 +454,13 @@
signature: string,
signingPublicKey: string,
) {
- const olmUtility: OlmUtility = getOlmUtility();
+ const olmUtility: Utility = getOlmUtility();
try {
olmUtility.ed25519_verify(signingPublicKey, message, signature);
return true;
} catch (err) {
const isSignatureInvalid =
- getMessageForException(err)?.includes('BAD_MESSAGE_MAC');
+ getMessageForException(err)?.includes('Signature');
if (isSignatureInvalid) {
return false;
}
@@ -461,8 +468,8 @@
}
}
-function isPrekeySignatureValid(account: OlmAccount): boolean {
- const signingPublicKey = JSON.parse(account.identity_keys()).ed25519;
+function isPrekeySignatureValid(account: VodozemacAccount): boolean {
+ const signingPublicKey = account.ed25519_key;
const { prekey, prekeySignature } = getAccountPrekeysSet(account);
if (!prekey || !prekeySignature) {
return false;
@@ -589,10 +596,14 @@
);
const result = {
- primaryIdentityPublicKeys: JSON.parse(contentAccount.identity_keys()),
- notificationIdentityPublicKeys: JSON.parse(
- notificationAccount.identity_keys(),
- ),
+ primaryIdentityPublicKeys: {
+ ed25519: contentAccount.ed25519_key,
+ curve25519: contentAccount.curve25519_key,
+ },
+ notificationIdentityPublicKeys: {
+ ed25519: notificationAccount.ed25519_key,
+ curve25519: notificationAccount.curve25519_key,
+ },
blobPayload: payload,
signature,
};
@@ -608,15 +619,17 @@
if (!olmSession) {
throw new Error(olmSessionErrors.sessionDoesNotExist);
}
- const encryptedContent = olmSession.session.encrypt(content);
+ const olmMessage = olmSession.session.encrypt(content);
+ const encryptedData = {
+ message: olmMessage.ciphertext,
+ messageType: olmMessage.message_type,
+ sessionVersion: olmSession.version,
+ };
+ olmMessage.free();
await persistCryptoStore();
- return {
- message: encryptedContent.body,
- messageType: encryptedContent.type,
- sessionVersion: olmSession.version,
- };
+ return encryptedData;
},
async encryptAndPersist(
content: string,
@@ -630,8 +643,12 @@
if (!olmSession) {
throw new Error(olmSessionErrors.sessionDoesNotExist);
}
-
- const encryptedContent = olmSession.session.encrypt(content);
+ const olmMessage = olmSession.session.encrypt(content);
+ const encryptedContent = {
+ body: olmMessage.ciphertext,
+ type: olmMessage.message_type,
+ };
+ olmMessage.free();
const sqliteQueryExecutor = getSQLiteQueryExecutor();
const dbModule = getDBModule();
@@ -693,16 +710,20 @@
throw new Error(olmSessionErrors.invalidSessionVersion);
}
+ const olmMessage = new OlmMessage(
+ encryptedData.messageType,
+ encryptedData.message,
+ );
+
let result;
try {
- result = olmSession.session.decrypt(
- encryptedData.messageType,
- encryptedData.message,
- );
+ result = olmSession.session.decrypt(olmMessage);
} catch (e) {
+ olmMessage.free();
throw new Error(`error decrypt => ${OLM_ERROR_FLAG} ` + e.message);
}
+ olmMessage.free();
await persistCryptoStore();
return result;
@@ -729,16 +750,20 @@
throw new Error(olmSessionErrors.invalidSessionVersion);
}
+ const olmMessage = new OlmMessage(
+ encryptedData.messageType,
+ encryptedData.message,
+ );
+
let result;
try {
- result = olmSession.session.decrypt(
- encryptedData.messageType,
- encryptedData.message,
- );
+ result = olmSession.session.decrypt(olmMessage);
} catch (e) {
+ olmMessage.free();
throw new Error(`error decrypt => ${OLM_ERROR_FLAG} ` + e.message);
}
+ olmMessage.free();
const sqliteQueryExecutor = getSQLiteQueryExecutor();
const dbModule = getDBModule();
if (!sqliteQueryExecutor || !dbModule) {
@@ -787,27 +812,30 @@
}
}
- // This `olm.Session` is created once and is cached for the entire
+ // This `Session` is created once and is cached for the entire
// program lifetime. Freeing is done as part of `clearCryptoStore`.
- const session = new olm.Session();
- session.create_inbound_from(
- contentAccount,
- contentIdentityKeys.curve25519,
+ const olmMessage = new OlmMessage(
+ initialEncryptedData.messageType,
initialEncryptedData.message,
);
- contentAccount.remove_one_time_keys(session);
- let initialEncryptedMessage;
+ let inboundCreationResult;
try {
- initialEncryptedMessage = session.decrypt(
- initialEncryptedData.messageType,
- initialEncryptedData.message,
+ inboundCreationResult = contentAccount.create_inbound_session(
+ contentIdentityKeys.curve25519,
+ olmMessage,
);
} catch (e) {
- session.free();
+ olmMessage.free();
throw new Error(`error decrypt => ${OLM_ERROR_FLAG} ` + e.message);
}
+ // into_session() is consuming object.
+ // There is no need to call free() on inboundCreationResult
+ const initialEncryptedMessage = inboundCreationResult.plaintext;
+ const session = inboundCreationResult.into_session();
+ olmMessage.free();
+
if (existingSession) {
existingSession.session.free();
}
@@ -829,30 +857,25 @@
const { contentAccount, contentSessions } = cryptoStore;
const existingSession = contentSessions[contentIdentityKeys.ed25519];
- // This `olm.Session` is created once and is cached for the entire
+ // This `Session` is created once and is cached for the entire
// program lifetime. Freeing is done as part of `clearCryptoStore`.
- const session = new olm.Session();
- if (contentInitializationInfo.oneTimeKey) {
- session.create_outbound(
- contentAccount,
- contentIdentityKeys.curve25519,
- contentIdentityKeys.ed25519,
- contentInitializationInfo.prekey,
- contentInitializationInfo.prekeySignature,
- contentInitializationInfo.oneTimeKey,
- );
- } else {
- session.create_outbound_without_otk(
- contentAccount,
- contentIdentityKeys.curve25519,
- contentIdentityKeys.ed25519,
- contentInitializationInfo.prekey,
- contentInitializationInfo.prekeySignature,
- );
- }
- const initialEncryptedData = session.encrypt(
+ const session = contentAccount.create_outbound_session(
+ contentIdentityKeys.curve25519,
+ contentIdentityKeys.ed25519,
+ contentInitializationInfo.oneTimeKey || '',
+ contentInitializationInfo.prekey,
+ contentInitializationInfo.prekeySignature,
+ true, // olmCompatibilityMode
+ );
+
+ const olmMessage = session.encrypt(
JSON.stringify(initialEncryptedMessageContent),
);
+ const initialEncryptedData = {
+ body: olmMessage.ciphertext,
+ type: olmMessage.message_type,
+ };
+ olmMessage.free();
const newSessionVersion = existingSession ? existingSession.version + 1 : 1;
if (existingSession) {

File Metadata

Mime Type
text/plain
Expires
Sat, Dec 6, 7:51 AM (22 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5830818
Default Alt Text
D15548.1765007495.diff (42 KB)

Event Timeline