diff --git a/keyserver/src/creators/olm-session-creator.js b/keyserver/src/creators/olm-session-creator.js new file mode 100644 --- /dev/null +++ b/keyserver/src/creators/olm-session-creator.js @@ -0,0 +1,43 @@ +// @flow + +import type { Account as OlmAccount } from '@commapp/olm'; + +import { ServerError } from 'lib/utils/errors.js'; + +import { dbQuery, SQL } from '../database/database.js'; +import { fetchCallUpdateOlmAccount } from '../updaters/olm-account-updater.js'; +import { createPickledOlmSession } from '../utils/olm-utils.js'; + +async function createOlmSession( + initialEncryptedMessage: string, + olmSessionType: 'content' | 'notifications', + cookieID: string, +): Promise { + const callback = (account: OlmAccount, picklingKey: string) => + createPickledOlmSession(account, picklingKey, initialEncryptedMessage); + + let pickledOlmSession; + try { + pickledOlmSession = await fetchCallUpdateOlmAccount( + olmSessionType, + callback, + ); + } catch (e) { + throw new ServerError('olm_session_creation_failure'); + } + + const isContent = olmSessionType === 'content'; + // We match the native client behavior here where olm session is overwritten + // in case it is initialized twice for the same pair of identities + await dbQuery( + SQL` + INSERT INTO olm_sessions (cookie_id, is_content, + version, pickled_olm_session) + VALUES (${cookieID}, ${isContent}, 0, ${pickledOlmSession}) + ON DUPLICATE KEY UPDATE + pickled_olm_session = ${pickledOlmSession} + `, + ); +} + +export { createOlmSession }; 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 @@ -4,9 +4,12 @@ import type { Account as OlmAccount, Utility as OlmUtility, + Session as OlmSession, } from '@commapp/olm'; import uuid from 'uuid'; +import { olmEncryptedMessageTypes } from 'lib/types/crypto-types.js'; + type PickledOlmAccount = { +picklingKey: string, +pickledAccount: string, @@ -42,6 +45,29 @@ return account; } +async function createPickledOlmSession( + account: OlmAccount, + accountPicklingKey: string, + initialEncryptedMessage: string, +): Promise { + await olm.init(); + const session = new olm.Session(); + session.create_inbound(account, initialEncryptedMessage); + account.remove_one_time_keys(session); + session.decrypt(olmEncryptedMessageTypes.PREKEY, initialEncryptedMessage); + return session.pickle(accountPicklingKey); +} + +async function unpickleOlmSession( + pickledSession: string, + picklingKey: string, +): Promise { + await olm.init(); + const session = new olm.Session(); + session.unpickle(picklingKey, pickledSession); + return session; +} + let cachedOLMUtility: OlmUtility; function getOlmUtility(): OlmUtility { if (cachedOLMUtility) { @@ -75,7 +101,9 @@ export { createPickledOlmAccount, + createPickledOlmSession, getOlmUtility, unpickleOlmAccount, + unpickleOlmSession, validateAccountPrekey, }; diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js --- a/lib/types/crypto-types.js +++ b/lib/types/crypto-types.js @@ -37,3 +37,8 @@ +payload: string, +signature: string, }; + +export const olmEncryptedMessageTypes = Object.freeze({ + PREKEY: 0, + TEXT: 1, +});