Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32088496
D15548.1765007495.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
42 KB
Referenced Files
None
Subscribers
None
D15548.1765007495.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D15548: [lib][web][keyserver] replace Olm with Vodozemac's Olm
Attached
Detach File
Event Timeline
Log In to Comment