@@ -204,6 +204,117 @@ function getSyncResponse(roomMembers: string[]): ISyncResponse {
204
204
} ;
205
205
}
206
206
207
+ /**
208
+ * Establish an Olm Session with the test user
209
+ *
210
+ * Waits for the test user to upload their keys, then sends a /sync response with a to-device message which will
211
+ * establish an Olm session.
212
+ *
213
+ * @param testClient: a TestClient for the user under test, which we expect to upload account keys, and to make a
214
+ * /sync request which we will respond to.
215
+ * @param peerOlmAccount: an OlmAccount which will be used to initiate the Olm session.
216
+ */
217
+ async function establishOlmSession ( testClient : TestClient , peerOlmAccount : Olm . Account ) : Promise < Olm . Session > {
218
+ const peerE2EKeys = JSON . parse ( peerOlmAccount . identity_keys ( ) ) ;
219
+ const p2pSession = await createOlmSession ( peerOlmAccount , testClient ) ;
220
+ const olmEvent = encryptOlmEvent ( {
221
+ senderKey : peerE2EKeys . curve25519 ,
222
+ recipient : testClient ,
223
+ p2pSession : p2pSession ,
224
+ } ) ;
225
+ testClient . httpBackend . when ( "GET" , "/sync" ) . respond ( 200 , {
226
+ next_batch : 1 ,
227
+ to_device : { events : [ olmEvent ] } ,
228
+ } ) ;
229
+ await testClient . flushSync ( ) ;
230
+ return p2pSession ;
231
+ }
232
+
233
+ /**
234
+ * Expect that the client shares keys with the given recipient
235
+ *
236
+ * Waits for an HTTP request to send the encrypted m.room_key to-device message; decrypts it and uses it
237
+ * to establish an Olm InboundGroupSession.
238
+ *
239
+ * @param senderMockHttpBackend - MockHttpBackend for the sender
240
+ *
241
+ * @param recipientUserID - the user id of the expected recipient
242
+ *
243
+ * @param recipientOlmAccount - Olm.Account for the recipient
244
+ *
245
+ * @param recipientOlmSession - an Olm.Session for the recipient, which must already have exchanged pre-key
246
+ * messages with the sender. Alternatively, null, in which case we will expect a pre-key message.
247
+ *
248
+ * @returns the established inbound group session
249
+ */
250
+ async function expectSendRoomKey (
251
+ senderMockHttpBackend : MockHttpBackend ,
252
+ recipientUserID : string ,
253
+ recipientOlmAccount : Olm . Account ,
254
+ recipientOlmSession : Olm . Session | null = null ,
255
+ ) : Promise < Olm . InboundGroupSession > {
256
+ const Olm = global . Olm ;
257
+ const testRecipientKey = JSON . parse ( recipientOlmAccount . identity_keys ( ) ) [ "curve25519" ] ;
258
+
259
+ let inboundGroupSession : Olm . InboundGroupSession ;
260
+
261
+ senderMockHttpBackend . when ( "PUT" , "/sendToDevice/m.room.encrypted/" ) . respond ( 200 , ( _path , content : any ) => {
262
+ const m = content . messages [ recipientUserID ] . DEVICE_ID ;
263
+ const ct = m . ciphertext [ testRecipientKey ] ;
264
+
265
+ if ( ! recipientOlmSession ) {
266
+ expect ( ct . type ) . toEqual ( 0 ) ; // pre-key message
267
+ recipientOlmSession = new Olm . Session ( ) ;
268
+ recipientOlmSession . create_inbound ( recipientOlmAccount , ct . body ) ;
269
+ } else {
270
+ expect ( ct . type ) . toEqual ( 1 ) ; // regular message
271
+ }
272
+
273
+ const decrypted = JSON . parse ( recipientOlmSession . decrypt ( ct . type , ct . body ) ) ;
274
+ expect ( decrypted . type ) . toEqual ( "m.room_key" ) ;
275
+ inboundGroupSession = new Olm . InboundGroupSession ( ) ;
276
+ inboundGroupSession . create ( decrypted . content . session_key ) ;
277
+ return { } ;
278
+ } ) ;
279
+
280
+ expect ( await senderMockHttpBackend . flush ( "/sendToDevice/m.room.encrypted/" , 1 , 1000 ) ) . toEqual ( 1 ) ;
281
+ return inboundGroupSession ! ;
282
+ }
283
+
284
+ /**
285
+ * Expect that the client sends an encrypted event
286
+ *
287
+ * Waits for an HTTP request to send an encrypted message in the test room.
288
+ *
289
+ * @param senderMockHttpBackend - MockHttpBackend for the sender
290
+ *
291
+ * @param inboundGroupSessionPromise - a promise for an Olm InboundGroupSession, which will
292
+ * be used to decrypt the event. We will wait for this to resolve once the HTTP request has been processed.
293
+ *
294
+ * @returns The content of the successfully-decrypted event
295
+ */
296
+ async function expectSendMegolmMessage (
297
+ senderMockHttpBackend : MockHttpBackend ,
298
+ inboundGroupSessionPromise : Promise < Olm . InboundGroupSession > ,
299
+ ) : Promise < Partial < IEvent > > {
300
+ let encryptedMessageContent : IContent | null = null ;
301
+ senderMockHttpBackend . when ( "PUT" , "/send/m.room.encrypted/" ) . respond ( 200 , function ( _path , content : IContent ) {
302
+ encryptedMessageContent = content ;
303
+ return {
304
+ event_id : "$event_id" ,
305
+ } ;
306
+ } ) ;
307
+
308
+ expect ( await senderMockHttpBackend . flush ( "/send/m.room.encrypted/" , 1 , 1000 ) ) . toEqual ( 1 ) ;
309
+
310
+ // In some of the tests, the room key is sent *after* the actual event, so we may need to wait for it now.
311
+ const inboundGroupSession = await inboundGroupSessionPromise ;
312
+
313
+ const r : any = inboundGroupSession . decrypt ( encryptedMessageContent ! . ciphertext ) ;
314
+ logger . log ( "Decrypted received megolm message" , r ) ;
315
+ return JSON . parse ( r . plaintext ) ;
316
+ }
317
+
207
318
describe ( "megolm" , ( ) => {
208
319
if ( ! global . Olm ) {
209
320
logger . warn ( "not running megolm tests: Olm not present" ) ;
@@ -491,9 +602,16 @@ describe("megolm", () => {
491
602
const room = aliceTestClient . client . getRoom ( ROOM_ID ) ! ;
492
603
const pendingMsg = room . getPendingEvents ( ) [ 0 ] ;
493
604
605
+ const inboundGroupSessionPromise = expectSendRoomKey (
606
+ aliceTestClient . httpBackend ,
607
+ "@bob:xyz" ,
608
+ testOlmAccount ,
609
+ p2pSession ,
610
+ ) ;
611
+
494
612
await Promise . all ( [
495
613
aliceTestClient . client . resendEvent ( pendingMsg , room ) ,
496
- expectSendKeyAndMessage ( aliceTestClient . httpBackend , "@bob:xyz" , testOlmAccount , p2pSession ) ,
614
+ expectSendMegolmMessage ( aliceTestClient . httpBackend , inboundGroupSessionPromise ) ,
497
615
] ) ;
498
616
} ) ;
499
617
0 commit comments