diff --git a/lib/components/peer-olm-session-creator-provider.react.js b/lib/components/peer-olm-session-creator-provider.react.js --- a/lib/components/peer-olm-session-creator-provider.react.js +++ b/lib/components/peer-olm-session-creator-provider.react.js @@ -6,7 +6,7 @@ import { IdentityClientContext } from '../shared/identity-client-context.js'; import { useTunnelbroker } from '../tunnelbroker/tunnelbroker-context.js'; import { - createOlmSessionWithPeer, + createOlmSessionsWithUser, type SessionCreationOptions, } from '../utils/crypto-utils.js'; @@ -50,13 +50,12 @@ const promise = (async () => { const authMetadata = await getAuthMetadata(); - await createOlmSessionWithPeer( + await createOlmSessionsWithUser( authMetadata, identityClient, sendMessageToDevice, userID, - deviceID, - sessionCreationOptions, + [{ deviceID, sessionCreationOptions }], ); runningPromises.current[userID][deviceID] = null; diff --git a/lib/utils/crypto-utils.js b/lib/utils/crypto-utils.js --- a/lib/utils/crypto-utils.js +++ b/lib/utils/crypto-utils.js @@ -4,6 +4,7 @@ import { type TInterface } from 'tcomb'; import { getConfig } from './config.js'; +import { getMessageForException } from './errors.js'; import { primaryIdentityPublicKeyRegex } from './siwe-utils.js'; import { tRegex, tShape } from './validation-utils.js'; import type { AuthMetadata } from '../shared/identity-client-context.js'; @@ -111,34 +112,19 @@ +overwriteNotifSession?: boolean, +overwriteContentSession?: boolean, }; -async function createOlmSessionWithPeer( +export type DeviceSessionCreationRequest = { + +deviceID: string, + +sessionCreationOptions?: SessionCreationOptions, +}; +async function createOlmSessionsWithUser( authMetadata: AuthMetadata, identityClient: IdentityServiceClient, sendMessage: (message: TunnelbrokerClientMessageToDevice) => Promise, userID: string, - deviceID: string, - options?: SessionCreationOptions, + devices: $ReadOnlyArray, ): Promise { const { olmAPI } = getConfig(); await olmAPI.initializeCryptoAccount(); - const [hasContentSession, hasNotifsSession] = await Promise.all([ - (async () => { - if (options?.overwriteContentSession) { - return false; - } - return await olmAPI.isContentSessionInitialized(deviceID); - })(), - (async () => { - if (options?.overwriteNotifSession) { - return false; - } - return await olmAPI.isDeviceNotificationsSessionInitialized(deviceID); - })(), - ]); - - if (hasContentSession && hasNotifsSession) { - return; - } const { userID: authUserID, @@ -149,67 +135,123 @@ throw new Error('CommServicesAuthMetadata is missing'); } - const keysResponse = await identityClient.getOutboundKeysForUser(userID); - const deviceKeysResponse = keysResponse.find( - keys => keys.deviceID === deviceID, + const filteredDevices: $ReadOnlyArray<{ + +deviceID: string, + +hasContentSession: boolean, + +hasNotifsSession: boolean, + }> = await Promise.all( + devices.map(request => + (async () => { + const [hasContentSession, hasNotifsSession] = await Promise.all([ + (async () => { + if (request.sessionCreationOptions?.overwriteContentSession) { + return false; + } + return await olmAPI.isContentSessionInitialized(request.deviceID); + })(), + (async () => { + if (request.sessionCreationOptions?.overwriteNotifSession) { + return false; + } + return await olmAPI.isDeviceNotificationsSessionInitialized( + request.deviceID, + ); + })(), + ]); + return { + deviceID: request.deviceID, + hasContentSession, + hasNotifsSession, + }; + })(), + ), ); - if (!deviceKeysResponse || !deviceKeysResponse.keys) { - throw new Error(`Keys missing for device ${deviceID}`); - } - const { keys } = deviceKeysResponse; - - const { primaryIdentityPublicKeys, notificationIdentityPublicKeys } = - keys.identityKeysBlob; - const recipientDeviceID = primaryIdentityPublicKeys.ed25519; - - if (hasContentSession) { - await olmAPI.notificationsOutboundSessionCreator( - recipientDeviceID, - notificationIdentityPublicKeys, - keys.notifInitializationInfo, - ); + + const sessionCreationNeeded = filteredDevices.some( + device => !device.hasContentSession || !device.hasNotifsSession, + ); + if (!sessionCreationNeeded) { return; } - let outboundSessionCreationResult: OutboundSessionCreationResult; - if (hasNotifsSession) { - outboundSessionCreationResult = await olmAPI.contentOutboundSessionCreator( - primaryIdentityPublicKeys, - keys.contentInitializationInfo, - ); - } else { - [outboundSessionCreationResult] = await Promise.all([ - await olmAPI.contentOutboundSessionCreator( - primaryIdentityPublicKeys, - keys.contentInitializationInfo, - ), - olmAPI.notificationsOutboundSessionCreator( - recipientDeviceID, - notificationIdentityPublicKeys, - keys.notifInitializationInfo, - ), - ]); - } + const keysResponse = await identityClient.getOutboundKeysForUser(userID); + + const devicePromises = filteredDevices.map(async sessionRequest => { + try { + const { deviceID, hasContentSession, hasNotifsSession } = sessionRequest; + const deviceKeysResponse = keysResponse.find( + keys => keys.deviceID === deviceID, + ); + + if (!deviceKeysResponse || !deviceKeysResponse.keys) { + console.log(`Keys missing for device ${deviceID}`); + return; + } + const { keys } = deviceKeysResponse; - const { sessionVersion, encryptedData } = outboundSessionCreationResult; - - const sessionCreationMessage: OutboundSessionCreation = { - type: peerToPeerMessageTypes.OUTBOUND_SESSION_CREATION, - senderInfo: { - userID: authUserID, - deviceID: authDeviceID, - }, - encryptedData, - sessionVersion, - }; - - await sendMessage({ - deviceID: recipientDeviceID, - payload: JSON.stringify(sessionCreationMessage), + const { primaryIdentityPublicKeys, notificationIdentityPublicKeys } = + keys.identityKeysBlob; + const recipientDeviceID = primaryIdentityPublicKeys.ed25519; + + if (hasContentSession) { + await olmAPI.notificationsOutboundSessionCreator( + recipientDeviceID, + notificationIdentityPublicKeys, + keys.notifInitializationInfo, + ); + return; + } + + let outboundSessionCreationResult: OutboundSessionCreationResult; + if (hasNotifsSession) { + outboundSessionCreationResult = + await olmAPI.contentOutboundSessionCreator( + primaryIdentityPublicKeys, + keys.contentInitializationInfo, + ); + } else { + [outboundSessionCreationResult] = await Promise.all([ + await olmAPI.contentOutboundSessionCreator( + primaryIdentityPublicKeys, + keys.contentInitializationInfo, + ), + olmAPI.notificationsOutboundSessionCreator( + recipientDeviceID, + notificationIdentityPublicKeys, + keys.notifInitializationInfo, + ), + ]); + } + + const { sessionVersion, encryptedData } = outboundSessionCreationResult; + + const sessionCreationMessage: OutboundSessionCreation = { + type: peerToPeerMessageTypes.OUTBOUND_SESSION_CREATION, + senderInfo: { + userID: authUserID, + deviceID: authDeviceID, + }, + encryptedData, + sessionVersion, + }; + + await sendMessage({ + deviceID: recipientDeviceID, + payload: JSON.stringify(sessionCreationMessage), + }); + console.log( + `Request to create a session with device ${recipientDeviceID} sent.`, + ); + } catch (e) { + console.error( + `Error creating session with ${sessionRequest.deviceID}: ${ + getMessageForException(e) ?? 'unknown error' + }`, + e, + ); + } }); - console.log( - `Request to create a session with device ${recipientDeviceID} sent.`, - ); + await Promise.all(devicePromises); } export { @@ -217,5 +259,5 @@ identityKeysBlobValidator, getContentSigningKey, createOlmSessionsWithOwnDevices, - createOlmSessionWithPeer, + createOlmSessionsWithUser, };