Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion keyserver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@
]
},
"transformIgnorePatterns": [
"/node_modules/(?!@babel/runtime)"
"/node_modules/(?!(@babel/runtime|@commapp/vodozemac))"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only for tests. @commapp/vodozemac was configured with "type": "module" (using ES6), while Jest runs in Node's CommonJS environment by default, so without transformation, it can't understand these ES6 export statements

],
"setupFiles": [
"<rootDir>/jest-setup.js"
Expand Down
2 changes: 1 addition & 1 deletion keyserver/src/creators/olm-session-creator.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type { Account as OlmAccount } from '@commapp/olm';
import type { Account as OlmAccount } from '@commapp/vodozemac';

import { ServerError } from 'lib/utils/errors.js';

Expand Down
30 changes: 1 addition & 29 deletions keyserver/src/cron/cron.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
// @flow

import type { Account as OlmAccount } from '@commapp/olm';
import olm from '@commapp/olm';
import type { Account as OlmAccount } from '@commapp/vodozemac';
import cluster from 'cluster';
import schedule from 'node-schedule';

import {
getOlmMemory,
compareAndLogOlmMemory,
} from 'lib/utils/olm-memory-utils.js';

import { backupDB } from './backups.js';
import { createDailyUpdatesThread } from './daily-updates.js';
import { postMetrics } from './metrics.js';
Expand Down Expand Up @@ -88,7 +82,6 @@ if (cluster.isMaster) {
schedule.scheduleJob(
'0 0 * * *', // every day at midnight in the keyserver's timezone
async () => {
const memBefore = getOlmMemory();
try {
await fetchCallUpdateOlmAccount(
'content',
Expand All @@ -101,40 +94,19 @@ if (cluster.isMaster) {
);
} catch (e) {
console.warn('encountered error while trying to validate prekeys', e);
} finally {
compareAndLogOlmMemory(memBefore, 'prekey upload cronjob');
}
},
);
schedule.scheduleJob(
'0 2 * * *', // every day at 2:00 AM in the keyserver's timezone
async () => {
const memBefore = getOlmMemory();
try {
await synchronizeInviteLinksWithBlobs();
} catch (e) {
console.warn(
'encountered an error while trying to synchronize invite links with blobs',
e,
);
} finally {
compareAndLogOlmMemory(memBefore, 'invite links cronjob');
}
},
);
schedule.scheduleJob(
'0,15,30,45 * * * *', // every 15 minutes
async () => {
const memBefore = getOlmMemory();
try {
await olm.init();
} catch (e) {
console.warn(
'encountered an error while executing olm init cron job',
e,
);
} finally {
compareAndLogOlmMemory(memBefore, 'olm init cronjob');
}
},
);
Expand Down
2 changes: 1 addition & 1 deletion keyserver/src/database/migration-config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type { Account as OlmAccount } from '@commapp/olm';
import type { Account as OlmAccount } from '@commapp/vodozemac';
import fs from 'fs';

import bots from 'lib/facts/bots.js';
Expand Down
3 changes: 1 addition & 2 deletions keyserver/src/push/encrypted-notif-utils-api.js
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
29 changes: 24 additions & 5 deletions keyserver/src/responders/keys-responders.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type { Account as OlmAccount } from '@commapp/olm';
import type { Account as OlmAccount } from '@commapp/vodozemac';

import type {
OlmSessionInitializationInfo,
Expand All @@ -18,20 +18,39 @@ type SessionInitializationKeysSet = {
function retrieveSessionInitializationKeysSet(
account: OlmAccount,
): SessionInitializationKeysSet {
const identityKeys = account.identity_keys();
const identityKeys = JSON.stringify({
ed25519: account.ed25519_key,
curve25519: account.curve25519_key,
});
Comment on lines +21 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we need these conversions on the other platforms? Should we have functions to implent this conversion, as wella s the prekey and one-time key conversions below?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prekey and one-time key conversions are deprecated, the only exception is this particular endpoint.

For identity keys, this is something that can be improved even further, created ENG-11553 because here it gets too messy.


const prekey = account.prekey();
const prekeySignature = account.prekey_signature();
if (!prekey) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding this check?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Olm, prekey was typed as defined because it was always returned. Olm just read bytes of memory allocated for the prekey struct, even though it wasn't generated, so there was a chance to get some trash bytes as prekey. We even had an issue with it (link).

In Rust, it is not easy to just read a chunk of memory where the prekey should be, so to make it work, I've made it optional, and we need to check if this exists here.

Prekey is set during account creation, so this couldn't have happened. Taking a step back, probably throwing an error was a better fit than making it optional, but fixing this now is probably too much work

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> {
Expand Down
2 changes: 1 addition & 1 deletion keyserver/src/responders/user-responders.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type { Utility as OlmUtility } from '@commapp/olm';
import type { Utility as OlmUtility } from '@commapp/vodozemac';
import invariant from 'invariant';
import { SiweErrorType, SiweMessage } from 'siwe';
import t, { type TInterface } from 'tcomb';
Expand Down
7 changes: 0 additions & 7 deletions keyserver/src/socket/tunnelbroker.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ import {
convertObjToBytes,
} from 'lib/utils/conversion-utils.js';
import { getMessageForException } from 'lib/utils/errors.js';
import {
compareAndLogOlmMemory,
getOlmMemory,
} from 'lib/utils/olm-memory-utils.js';
import sleep from 'lib/utils/sleep.js';

import {
Expand Down Expand Up @@ -391,14 +387,11 @@ class TunnelbrokerSocket {
refreshOneTimeKeys: (numberOfKeys: number) => void = numberOfKeys => {
const oldOneTimeKeysPromise = this.oneTimeKeysPromise;
this.oneTimeKeysPromise = (async () => {
const memBefore = getOlmMemory();
try {
await oldOneTimeKeysPromise;
await uploadNewOneTimeKeys(numberOfKeys);
} catch (e) {
console.error('Encountered error when trying to upload new OTKs:', e);
} finally {
compareAndLogOlmMemory(memBefore, 'otk refresh');
}
})();
};
Expand Down
2 changes: 1 addition & 1 deletion keyserver/src/updaters/olm-account-updater.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type { Account as OlmAccount } from '@commapp/olm';
import { type Account as OlmAccount } from '@commapp/vodozemac';

import { ServerError } from 'lib/utils/errors.js';
import sleep from 'lib/utils/sleep.js';
Expand Down
11 changes: 7 additions & 4 deletions keyserver/src/updaters/olm-session-updater.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// @flow

import type { EncryptResult, Session as OlmSession } from '@commapp/olm';
import type { Session as OlmSession } from '@commapp/vodozemac';

import type { EncryptResult } from 'lib/types/encrypted-type.js';
import { ServerError } from 'lib/utils/errors.js';
import sleep from 'lib/utils/sleep.js';

Expand Down Expand Up @@ -56,9 +57,11 @@ async function encryptAndUpdateOlmSession(
},
(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,
};
}
},
);
Expand Down
2 changes: 1 addition & 1 deletion keyserver/src/user/login.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import type { Account as OlmAccount } from '@commapp/olm';
import type { Account as OlmAccount } from '@commapp/vodozemac';
import { getRustAPI } from 'rust-node-addon';

import { getCommConfig } from 'lib/utils/comm-config.js';
Expand Down
62 changes: 33 additions & 29 deletions keyserver/src/utils/olm-objects.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
// @flow

import olm, {
import initVodozemac, {
type Account as OlmAccount,
Account,
OlmMessage,
type Session as OlmSession,
} from '@commapp/olm';
} from '@commapp/vodozemac';
import uuid from 'uuid';

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';

Expand All @@ -20,16 +27,14 @@ async function unpickleAccountAndUseCallback<T>(
pickledOlmAccount: PickledOlmAccount,
callback: (account: OlmAccount, picklingKey: string) => Promise<T> | T,
): Promise<{ +result: T, +pickledOlmAccount: PickledOlmAccount }> {
const { picklingKey, pickledAccount } = pickledOlmAccount;
await initVodozemac();

await olm.init();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need an equivalent init call for Vodozemac?

Copy link
Collaborator Author

@xsanm xsanm Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really sure why this call was added here. Both Olm before, and Vodozemac now are initialized at keyserver start here, I think this is unnecessary no-op, but I can add it if you think it is better to keep it as it is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's bring these back so that standalone scripts can still work


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: {
Expand All @@ -45,14 +50,12 @@ async function unpickleAccountAndUseCallback<T>(
}

async function createPickledOlmAccount(): Promise<PickledOlmAccount> {
await olm.init();
await initVodozemac();

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 {
Expand All @@ -69,16 +72,14 @@ async function unpickleSessionAndUseCallback<T>(
pickledOlmSession: PickledOlmSession,
callback: (session: OlmSession) => Promise<T> | T,
): Promise<{ +result: T, +pickledOlmSession: PickledOlmSession }> {
const { picklingKey, pickledSession } = pickledOlmSession;

await olm.init();
await initVodozemac();

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: {
Expand All @@ -99,19 +100,22 @@ async function createPickledOlmSession(
initialEncryptedMessage: string,
theirCurve25519Key: string,
): Promise<string> {
await olm.init();
const session = new olm.Session();
await initVodozemac();

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;
Expand Down
15 changes: 5 additions & 10 deletions keyserver/src/utils/olm-utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// @flow

import type { Account as OlmAccount } from '@commapp/olm';
import { type Account as OlmAccount } from '@commapp/vodozemac';
import invariant from 'invariant';

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 {
Expand Down Expand Up @@ -134,16 +133,12 @@ async function uploadNewOneTimeKeys(numberOfKeys: number) {
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();
}),
]);
Expand All @@ -164,7 +159,7 @@ async function getContentSigningKey(): Promise<string> {
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,
Expand Down Expand Up @@ -211,7 +206,7 @@ async function publishPrekeysToIdentity(
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);
Expand Down
Loading