@@ -295,8 +295,8 @@ OlmDevice.prototype._storeAccount = function(txn, account) {
295
295
*/
296
296
OlmDevice . prototype . _getSession = function ( deviceKey , sessionId , txn , func ) {
297
297
this . _cryptoStore . getEndToEndSession (
298
- deviceKey , sessionId , txn , ( pickledSession ) => {
299
- this . _unpickleSession ( pickledSession , func ) ;
298
+ deviceKey , sessionId , txn , ( sessionInfo ) => {
299
+ this . _unpickleSession ( sessionInfo , func ) ;
300
300
} ,
301
301
) ;
302
302
} ;
@@ -306,15 +306,17 @@ OlmDevice.prototype._getSession = function(deviceKey, sessionId, txn, func) {
306
306
* function with it. The session object is destroyed once the function
307
307
* returns.
308
308
*
309
- * @param {string } pickledSession
309
+ * @param {object } sessionInfo
310
310
* @param {function } func
311
311
* @private
312
312
*/
313
- OlmDevice . prototype . _unpickleSession = function ( pickledSession , func ) {
313
+ OlmDevice . prototype . _unpickleSession = function ( sessionInfo , func ) {
314
314
const session = new global . Olm . Session ( ) ;
315
315
try {
316
- session . unpickle ( this . _pickleKey , pickledSession ) ;
317
- func ( session ) ;
316
+ session . unpickle ( this . _pickleKey , sessionInfo . session ) ;
317
+ const unpickledSessInfo = Object . assign ( { } , sessionInfo , { session} ) ;
318
+
319
+ func ( unpickledSessInfo ) ;
318
320
} finally {
319
321
session . free ( ) ;
320
322
}
@@ -324,14 +326,17 @@ OlmDevice.prototype._unpickleSession = function(pickledSession, func) {
324
326
* store our OlmSession in the session store
325
327
*
326
328
* @param {string } deviceKey
327
- * @param {OlmSession } session
329
+ * @param {object } sessionInfo { session: OlmSession, lastReceivedMessageTs: int}
328
330
* @param {* } txn Opaque transaction object from cryptoStore.doTxn()
329
331
* @private
330
332
*/
331
- OlmDevice . prototype . _saveSession = function ( deviceKey , session , txn ) {
332
- const pickledSession = session . pickle ( this . _pickleKey ) ;
333
+ OlmDevice . prototype . _saveSession = function ( deviceKey , sessionInfo , txn ) {
334
+ const sessionId = sessionInfo . session . session_id ( ) ;
335
+ const pickledSessionInfo = Object . assign ( sessionInfo , {
336
+ session : sessionInfo . session . pickle ( this . _pickleKey ) ,
337
+ } ) ;
333
338
this . _cryptoStore . storeEndToEndSession (
334
- deviceKey , session . session_id ( ) , pickledSession , txn ,
339
+ deviceKey , sessionId , pickledSessionInfo , txn ,
335
340
) ;
336
341
} ;
337
342
@@ -461,7 +466,14 @@ OlmDevice.prototype.createOutboundSession = async function(
461
466
session . create_outbound ( account , theirIdentityKey , theirOneTimeKey ) ;
462
467
newSessionId = session . session_id ( ) ;
463
468
this . _storeAccount ( txn , account ) ;
464
- this . _saveSession ( theirIdentityKey , session , txn ) ;
469
+ const sessionInfo = {
470
+ session,
471
+ // Pretend we've received a message at this point, otherwise
472
+ // if we try to send a message to the device, it won't use
473
+ // this session
474
+ lastReceivedMessageTs : Date . now ( ) ,
475
+ } ;
476
+ this . _saveSession ( theirIdentityKey , sessionInfo , txn ) ;
465
477
} finally {
466
478
session . free ( ) ;
467
479
}
@@ -510,7 +522,13 @@ OlmDevice.prototype.createInboundSession = async function(
510
522
511
523
const payloadString = session . decrypt ( messageType , ciphertext ) ;
512
524
513
- this . _saveSession ( theirDeviceIdentityKey , session , txn ) ;
525
+ const sessionInfo = {
526
+ session,
527
+ // this counts as a received message: set last received message time
528
+ // to now
529
+ lastReceivedMessageTs : Date . now ( ) ,
530
+ } ;
531
+ this . _saveSession ( theirDeviceIdentityKey , sessionInfo , txn ) ;
514
532
515
533
result = {
516
534
payload : payloadString ,
@@ -558,13 +576,30 @@ OlmDevice.prototype.getSessionIdsForDevice = async function(theirDeviceIdentityK
558
576
* @return {Promise<?string> } session id, or null if no established session
559
577
*/
560
578
OlmDevice . prototype . getSessionIdForDevice = async function ( theirDeviceIdentityKey ) {
561
- const sessionIds = await this . getSessionIdsForDevice ( theirDeviceIdentityKey ) ;
562
- if ( sessionIds . length === 0 ) {
579
+ const sessionInfos = await this . getSessionInfoForDevice ( theirDeviceIdentityKey ) ;
580
+ if ( sessionInfos . length === 0 ) {
563
581
return null ;
564
582
}
565
- // Use the session with the lowest ID.
566
- sessionIds . sort ( ) ;
567
- return sessionIds [ 0 ] ;
583
+ // Use the session that has most recently received a message
584
+ let idxOfBest = 0 ;
585
+ for ( let i = 1 ; i < sessionInfos . length ; i ++ ) {
586
+ const thisSessInfo = sessionInfos [ i ] ;
587
+ const thisLastReceived = thisSessInfo . lastReceivedMessageTs === undefined ?
588
+ 0 : thisSessInfo . lastReceivedMessageTs ;
589
+
590
+ const bestSessInfo = sessionInfos [ idxOfBest ] ;
591
+ const bestLastReceived = bestSessInfo . lastReceivedMessageTs === undefined ?
592
+ 0 : bestSessInfo . lastReceivedMessageTs ;
593
+ if (
594
+ thisLastReceived > bestLastReceived || (
595
+ thisLastReceived === bestLastReceived &&
596
+ thisSessInfo . sessionId < bestSessInfo . sessionId
597
+ )
598
+ ) {
599
+ idxOfBest = i ;
600
+ }
601
+ }
602
+ return sessionInfos [ idxOfBest ] . sessionId ;
568
603
} ;
569
604
570
605
/**
@@ -587,9 +622,10 @@ OlmDevice.prototype.getSessionInfoForDevice = async function(deviceIdentityKey)
587
622
this . _cryptoStore . getEndToEndSessions ( deviceIdentityKey , txn , ( sessions ) => {
588
623
const sessionIds = Object . keys ( sessions ) . sort ( ) ;
589
624
for ( const sessionId of sessionIds ) {
590
- this . _unpickleSession ( sessions [ sessionId ] , ( session ) => {
625
+ this . _unpickleSession ( sessions [ sessionId ] , ( sessInfo ) => {
591
626
info . push ( {
592
- hasReceivedMessage : session . has_received_message ( ) ,
627
+ lastReceivedMessageTs : sessInfo . lastReceivedMessageTs ,
628
+ hasReceivedMessage : sessInfo . session . has_received_message ( ) ,
593
629
sessionId : sessionId ,
594
630
} ) ;
595
631
} ) ;
@@ -620,9 +656,9 @@ OlmDevice.prototype.encryptMessage = async function(
620
656
await this . _cryptoStore . doTxn (
621
657
'readwrite' , [ IndexedDBCryptoStore . STORE_SESSIONS ] ,
622
658
( txn ) => {
623
- this . _getSession ( theirDeviceIdentityKey , sessionId , txn , ( session ) => {
624
- res = session . encrypt ( payloadString ) ;
625
- this . _saveSession ( theirDeviceIdentityKey , session , txn ) ;
659
+ this . _getSession ( theirDeviceIdentityKey , sessionId , txn , ( sessionInfo ) => {
660
+ res = sessionInfo . session . encrypt ( payloadString ) ;
661
+ this . _saveSession ( theirDeviceIdentityKey , sessionInfo , txn ) ;
626
662
} ) ;
627
663
} ,
628
664
) ;
@@ -647,9 +683,10 @@ OlmDevice.prototype.decryptMessage = async function(
647
683
await this . _cryptoStore . doTxn (
648
684
'readwrite' , [ IndexedDBCryptoStore . STORE_SESSIONS ] ,
649
685
( txn ) => {
650
- this . _getSession ( theirDeviceIdentityKey , sessionId , txn , ( session ) => {
651
- payloadString = session . decrypt ( messageType , ciphertext ) ;
652
- this . _saveSession ( theirDeviceIdentityKey , session , txn ) ;
686
+ this . _getSession ( theirDeviceIdentityKey , sessionId , txn , ( sessionInfo ) => {
687
+ payloadString = sessionInfo . session . decrypt ( messageType , ciphertext ) ;
688
+ sessionInfo . lastReceivedMessageTs = Date . now ( ) ;
689
+ this . _saveSession ( theirDeviceIdentityKey , sessionInfo , txn ) ;
653
690
} ) ;
654
691
} ,
655
692
) ;
@@ -679,8 +716,8 @@ OlmDevice.prototype.matchesSession = async function(
679
716
await this . _cryptoStore . doTxn (
680
717
'readonly' , [ IndexedDBCryptoStore . STORE_SESSIONS ] ,
681
718
( txn ) => {
682
- this . _getSession ( theirDeviceIdentityKey , sessionId , txn , ( session ) => {
683
- matches = session . matches_inbound ( ciphertext ) ;
719
+ this . _getSession ( theirDeviceIdentityKey , sessionId , txn , ( sessionInfo ) => {
720
+ matches = sessionInfo . session . matches_inbound ( ciphertext ) ;
684
721
} ) ;
685
722
} ,
686
723
) ;
0 commit comments