diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -18,7 +18,10 @@ calendarQueryUpdateResponder, } from './responders/entry-responders.js'; import type { JSONResponder } from './responders/handlers.js'; -import { getSessionPublicKeysResponder } from './responders/keys-responders.js'; +import { + getSessionPublicKeysResponder, + getOlmNotifsSessionInitializationDataResponder, +} from './responders/keys-responders.js'; import { messageReportCreationResponder } from './responders/message-report-responder.js'; import { textMessageCreationResponder, @@ -262,6 +265,10 @@ responder: updateUserAvatarResponder, requiredPolicies: baseLegalPolicies, }, + get_olm_notifs_session_initialization_data: { + responder: getOlmNotifsSessionInitializationDataResponder, + requiredPolicies: [], + }, }; export { jsonEndpoints }; 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,13 +1,21 @@ // @flow +import type { Account as OlmAccount } from '@commapp/olm'; import t from 'tcomb'; -import type { GetSessionPublicKeysArgs } from 'lib/types/request-types.js'; +import type { + GetOlmNotifsSessionInitializationDataResponse, + GetOlmNotifsSessionInitializationDataRequest, + GetSessionPublicKeysArgs, +} from 'lib/types/request-types.js'; import type { SessionPublicKeys } from 'lib/types/session-types.js'; +import { ServerError } from 'lib/utils/errors.js'; import { tShape } from 'lib/utils/validation-utils.js'; import { fetchSessionPublicKeys } from '../fetchers/key-fetchers.js'; import type { Viewer } from '../session/viewer.js'; +import { fetchCallUpdateOlmAccount } from '../updaters/olm-account-updater.js'; +import { validateAccountPrekey } from '../utils/olm-utils.js'; import { validateInput } from '../utils/validation-utils.js'; const getSessionPublicKeysInputValidator = tShape({ @@ -26,4 +34,74 @@ return await fetchSessionPublicKeys(request.session); } -export { getSessionPublicKeysResponder }; +const getOlmNotifsSessionInitializationDataRequestInputValidator = tShape({ + oneTimeKeysCount: t.Number, +}); + +async function getOlmNotifsSessionInitializationDataResponder( + viewer: Viewer, + input: any, +): Promise { + await validateInput( + viewer, + getOlmNotifsSessionInitializationDataRequestInputValidator, + input, + ); + const request: GetOlmNotifsSessionInitializationDataRequest = input; + + let prekey, notificationsIdentityKeysString, oneTimeKeys; + const notificationsAccountCallback = async (account: OlmAccount) => { + notificationsIdentityKeysString = account.identity_keys(); + + await validateAccountPrekey(account); + prekey = account.prekey(); + // Until transfer of prekeys to the identity service is not implemented + // notifs prekey will be marked as published each time it is accessed + // to establish olm notifs session to mitigate the risk of notifs + // prekey being in use long enough to cause security concerns + account.mark_prekey_as_published(); + + account.generate_one_time_keys(request.oneTimeKeysCount); + oneTimeKeys = account.one_time_keys(); + account.mark_keys_as_published(); + }; + + await fetchCallUpdateOlmAccount( + 'notifications', + notificationsAccountCallback, + ); + if (!prekey || !oneTimeKeys) { + throw new ServerError('olm_account_query_failure'); + } + + let identityKeysBlob, identityKeysBlobPayload, signedIdentityKeysBlob; + const primaryAccountCallback = async (account: OlmAccount) => { + identityKeysBlob = { + primaryIdentityPublicKeys: JSON.parse(account.identity_keys()), + notificationIdentityPublicKeys: JSON.parse( + notificationsIdentityKeysString, + ), + }; + identityKeysBlobPayload = JSON.stringify(identityKeysBlob); + signedIdentityKeysBlob = { + payload: identityKeysBlobPayload, + signature: account.sign(identityKeysBlobPayload), + }; + }; + + await fetchCallUpdateOlmAccount('primary', primaryAccountCallback); + if (!signedIdentityKeysBlob) { + throw new ServerError('olm_account_query_failure'); + } + + return { + signedIdentityKeysBlob, + prekey, + oneTimeKeys, + }; +} + +export { + getSessionPublicKeysResponder, + getOlmNotifsSessionInitializationDataResponder, +}; diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -14,7 +14,11 @@ ClientAvatar, UpdateUserAvatarRequest, } from '../types/avatar-types.js'; -import type { GetSessionPublicKeysArgs } from '../types/request-types.js'; +import type { + GetSessionPublicKeysArgs, + GetOlmNotifsSessionInitializationDataRequest, + GetOlmNotifsSessionInitializationDataResponse, +} from '../types/request-types.js'; import type { UserSearchResult } from '../types/search-types.js'; import type { SessionPublicKeys, @@ -235,6 +239,25 @@ return await callServerEndpoint('get_session_public_keys', data); }; +const getOlmNotifsSessionInitializationDataActionTypes = Object.freeze({ + started: 'GET_OLM_NOTIFS_SESSION_INITIALIZATION_DATA_STARTED', + success: 'GET_OLM_NOTIFS_SESSION_INITIALIZATION_DATA_SUCCESS', + failed: 'GET_OLM_NOTIFS_SESSION_INITIALIZATION_DATA_FAILED', +}); + +const getOlmNotifsSessionInitializationData = + ( + callServerEndpoint: CallServerEndpoint, + ): (( + data: GetOlmNotifsSessionInitializationDataRequest, + ) => Promise) => + async data => { + return await callServerEndpoint( + 'get_olm_notifs_session_initialization_data', + data, + ); + }; + const policyAcknowledgmentActionTypes = Object.freeze({ started: 'POLICY_ACKNOWLEDGMENT_STARTED', success: 'POLICY_ACKNOWLEDGMENT_SUCCESS', @@ -267,6 +290,8 @@ deleteAccount, deleteAccountActionTypes, getSessionPublicKeys, + getOlmNotifsSessionInitializationDataActionTypes, + getOlmNotifsSessionInitializationData, mergeUserInfos, logIn, logInActionTypes, diff --git a/lib/types/endpoints.js b/lib/types/endpoints.js --- a/lib/types/endpoints.js +++ b/lib/types/endpoints.js @@ -87,6 +87,8 @@ SIWE_NONCE: 'siwe_nonce', SIWE_AUTH: 'siwe_auth', UPDATE_USER_AVATAR: 'update_user_avatar', + GET_OLM_NOTIFS_SESSION_INITIALIZATION_DATA: + 'get_olm_notifs_session_initialization_data', }); type SocketPreferredEndpoint = $Values; diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -66,7 +66,10 @@ QueueReportsPayload, ReportStore, } from './report-types.js'; -import type { ProcessServerRequestsPayload } from './request-types.js'; +import type { + ProcessServerRequestsPayload, + GetOlmNotifsSessionInitializationDataResponse, +} from './request-types.js'; import type { UserSearchResult } from './search-types.js'; import type { SetSessionPayload } from './session-types.js'; import type { @@ -988,6 +991,22 @@ +error: true, +payload: Error, +loadingInfo: LoadingInfo, + } + | { + +type: 'GET_OLM_NOTIFS_SESSION_INITIALIZATION_DATA_STARTED', + +loadingInfo?: LoadingInfo, + +payload?: void, + } + | { + +type: 'GET_OLM_NOTIFS_SESSION_INITIALIZATION_DATA_SUCCESS', + +payload: GetOlmNotifsSessionInitializationDataResponse, + +loadingInfo: LoadingInfo, + } + | { + +type: 'GET_OLM_NOTIFS_SESSION_INITIALIZATION_DATA_FAILED', + +error: true, + +payload: Error, + +loadingInfo: LoadingInfo, }; export type ActionPayload = ?(Object | Array<*> | $ReadOnlyArray<*> | string); diff --git a/lib/types/request-types.js b/lib/types/request-types.js --- a/lib/types/request-types.js +++ b/lib/types/request-types.js @@ -197,3 +197,13 @@ export type GetSessionPublicKeysArgs = { +session: string, }; + +export type GetOlmNotifsSessionInitializationDataRequest = { + +oneTimeKeysCount: number, +}; + +export type GetOlmNotifsSessionInitializationDataResponse = { + +signedIdentityKeysBlob: SignedIdentityKeysBlob, + +prekey: string, + +oneTimeKeys: string, +};