Merge branch 'soru/olm-session-recovery' into 'master'
Adds olm session recovery See merge request famedly/famedlysdk!355
This commit is contained in:
		
						commit
						62f63ebf1f
					
				|  | @ -18,11 +18,13 @@ | ||||||
| 
 | 
 | ||||||
| import 'dart:convert'; | import 'dart:convert'; | ||||||
| 
 | 
 | ||||||
|  | import 'package:pedantic/pedantic.dart'; | ||||||
| import 'package:canonical_json/canonical_json.dart'; | import 'package:canonical_json/canonical_json.dart'; | ||||||
| import 'package:famedlysdk/famedlysdk.dart'; | import 'package:famedlysdk/famedlysdk.dart'; | ||||||
| import 'package:famedlysdk/matrix_api.dart'; | import 'package:famedlysdk/matrix_api.dart'; | ||||||
| import 'package:olm/olm.dart' as olm; | import 'package:olm/olm.dart' as olm; | ||||||
| import './encryption.dart'; | import './encryption.dart'; | ||||||
|  | import './utils/olm_session.dart'; | ||||||
| 
 | 
 | ||||||
| class OlmManager { | class OlmManager { | ||||||
|   final Encryption encryption; |   final Encryption encryption; | ||||||
|  | @ -43,8 +45,8 @@ class OlmManager { | ||||||
|   OlmManager(this.encryption); |   OlmManager(this.encryption); | ||||||
| 
 | 
 | ||||||
|   /// A map from Curve25519 identity keys to existing olm sessions. |   /// A map from Curve25519 identity keys to existing olm sessions. | ||||||
|   Map<String, List<olm.Session>> get olmSessions => _olmSessions; |   Map<String, List<OlmSession>> get olmSessions => _olmSessions; | ||||||
|   final Map<String, List<olm.Session>> _olmSessions = {}; |   final Map<String, List<OlmSession>> _olmSessions = {}; | ||||||
| 
 | 
 | ||||||
|   Future<void> init(String olmAccount) async { |   Future<void> init(String olmAccount) async { | ||||||
|     if (olmAccount == null) { |     if (olmAccount == null) { | ||||||
|  | @ -204,25 +206,24 @@ class OlmManager { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void storeOlmSession(String curve25519IdentityKey, olm.Session session) { |   void storeOlmSession(OlmSession session) { | ||||||
|     if (client.database == null) { |     if (client.database == null) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     if (!_olmSessions.containsKey(curve25519IdentityKey)) { |     if (!_olmSessions.containsKey(session.identityKey)) { | ||||||
|       _olmSessions[curve25519IdentityKey] = []; |       _olmSessions[session.identityKey] = []; | ||||||
|     } |     } | ||||||
|     final ix = _olmSessions[curve25519IdentityKey] |     final ix = _olmSessions[session.identityKey] | ||||||
|         .indexWhere((s) => s.session_id() == session.session_id()); |         .indexWhere((s) => s.sessionId == session.sessionId); | ||||||
|     if (ix == -1) { |     if (ix == -1) { | ||||||
|       // add a new session |       // add a new session | ||||||
|       _olmSessions[curve25519IdentityKey].add(session); |       _olmSessions[session.identityKey].add(session); | ||||||
|     } else { |     } else { | ||||||
|       // update an existing session |       // update an existing session | ||||||
|       _olmSessions[curve25519IdentityKey][ix] = session; |       _olmSessions[session.identityKey][ix] = session; | ||||||
|     } |     } | ||||||
|     final pickle = session.pickle(client.userID); |     client.database.storeOlmSession(client.id, session.identityKey, | ||||||
|     client.database.storeOlmSession( |         session.sessionId, session.pickledSession, session.lastReceived); | ||||||
|         client.id, curve25519IdentityKey, session.session_id(), pickle); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ToDeviceEvent _decryptToDeviceEvent(ToDeviceEvent event) { |   ToDeviceEvent _decryptToDeviceEvent(ToDeviceEvent event) { | ||||||
|  | @ -245,14 +246,16 @@ class OlmManager { | ||||||
|     var existingSessions = olmSessions[senderKey]; |     var existingSessions = olmSessions[senderKey]; | ||||||
|     if (existingSessions != null) { |     if (existingSessions != null) { | ||||||
|       for (var session in existingSessions) { |       for (var session in existingSessions) { | ||||||
|         if (type == 0 && session.matches_inbound(body) == true) { |         if (type == 0 && session.session.matches_inbound(body) == true) { | ||||||
|           plaintext = session.decrypt(type, body); |           plaintext = session.session.decrypt(type, body); | ||||||
|           storeOlmSession(senderKey, session); |           session.lastReceived = DateTime.now(); | ||||||
|  |           storeOlmSession(session); | ||||||
|           break; |           break; | ||||||
|         } else if (type == 1) { |         } else if (type == 1) { | ||||||
|           try { |           try { | ||||||
|             plaintext = session.decrypt(type, body); |             plaintext = session.session.decrypt(type, body); | ||||||
|             storeOlmSession(senderKey, session); |             session.lastReceived = DateTime.now(); | ||||||
|  |             storeOlmSession(session); | ||||||
|             break; |             break; | ||||||
|           } catch (_) { |           } catch (_) { | ||||||
|             plaintext = null; |             plaintext = null; | ||||||
|  | @ -271,7 +274,13 @@ class OlmManager { | ||||||
|         _olmAccount.remove_one_time_keys(newSession); |         _olmAccount.remove_one_time_keys(newSession); | ||||||
|         client.database?.updateClientKeys(pickledOlmAccount, client.id); |         client.database?.updateClientKeys(pickledOlmAccount, client.id); | ||||||
|         plaintext = newSession.decrypt(type, body); |         plaintext = newSession.decrypt(type, body); | ||||||
|         storeOlmSession(senderKey, newSession); |         storeOlmSession(OlmSession( | ||||||
|  |           key: client.userID, | ||||||
|  |           identityKey: senderKey, | ||||||
|  |           sessionId: newSession.session_id(), | ||||||
|  |           session: newSession, | ||||||
|  |           lastReceived: DateTime.now(), | ||||||
|  |         )); | ||||||
|       } catch (_) { |       } catch (_) { | ||||||
|         newSession?.free(); |         newSession?.free(); | ||||||
|         rethrow; |         rethrow; | ||||||
|  | @ -299,6 +308,35 @@ class OlmManager { | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   Future<List<OlmSession>> getOlmSessionsFromDatabase(String senderKey) async { | ||||||
|  |     if (client.database == null) { | ||||||
|  |       return []; | ||||||
|  |     } | ||||||
|  |     final rows = | ||||||
|  |         await client.database.dbGetOlmSessions(client.id, senderKey).get(); | ||||||
|  |     final res = <OlmSession>[]; | ||||||
|  |     for (final row in rows) { | ||||||
|  |       final sess = OlmSession.fromDb(row, client.userID); | ||||||
|  |       if (sess.isValid) { | ||||||
|  |         res.add(sess); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> restoreOlmSession(String userId, String senderKey) async { | ||||||
|  |     if (!client.userDeviceKeys.containsKey(userId)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     final device = client.userDeviceKeys[userId].deviceKeys.values | ||||||
|  |         .firstWhere((d) => d.curve25519Key == senderKey, orElse: () => null); | ||||||
|  |     if (device == null) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     await startOutgoingOlmSessions([device]); | ||||||
|  |     await client.sendToDevice([device], 'm.dummy', {}); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async { |   Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async { | ||||||
|     if (event.type != EventTypes.Encrypted) { |     if (event.type != EventTypes.Encrypted) { | ||||||
|       return event; |       return event; | ||||||
|  | @ -308,8 +346,7 @@ class OlmManager { | ||||||
|       if (client.database == null) { |       if (client.database == null) { | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
|       final sessions = await client.database |       final sessions = await getOlmSessionsFromDatabase(senderKey); | ||||||
|           .getSingleOlmSessions(client.id, senderKey, client.userID); |  | ||||||
|       if (sessions.isEmpty) { |       if (sessions.isEmpty) { | ||||||
|         return false; // okay, can't do anything |         return false; // okay, can't do anything | ||||||
|       } |       } | ||||||
|  | @ -319,12 +356,20 @@ class OlmManager { | ||||||
|     if (!_olmSessions.containsKey(senderKey)) { |     if (!_olmSessions.containsKey(senderKey)) { | ||||||
|       await loadFromDb(); |       await loadFromDb(); | ||||||
|     } |     } | ||||||
|     event = _decryptToDeviceEvent(event); |     try { | ||||||
|     if (event.type != EventTypes.Encrypted || !(await loadFromDb())) { |       event = _decryptToDeviceEvent(event); | ||||||
|       return event; |       if (event.type != EventTypes.Encrypted || !(await loadFromDb())) { | ||||||
|  |         return event; | ||||||
|  |       } | ||||||
|  |       // retry to decrypt! | ||||||
|  |       return _decryptToDeviceEvent(event); | ||||||
|  |     } catch (_) { | ||||||
|  |       // okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one | ||||||
|  |       if (client.enableE2eeRecovery) { | ||||||
|  |         unawaited(restoreOlmSession(event.senderId, senderKey)); | ||||||
|  |       } | ||||||
|  |       rethrow; | ||||||
|     } |     } | ||||||
|     // retry to decrypt! |  | ||||||
|     return _decryptToDeviceEvent(event); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<void> startOutgoingOlmSessions(List<DeviceKeys> deviceKeys) async { |   Future<void> startOutgoingOlmSessions(List<DeviceKeys> deviceKeys) async { | ||||||
|  | @ -352,11 +397,19 @@ class OlmManager { | ||||||
|               fingerprintKey, deviceKey, userId, deviceId)) { |               fingerprintKey, deviceKey, userId, deviceId)) { | ||||||
|             continue; |             continue; | ||||||
|           } |           } | ||||||
|  |           var session = olm.Session(); | ||||||
|           try { |           try { | ||||||
|             var session = olm.Session(); |  | ||||||
|             session.create_outbound(_olmAccount, identityKey, deviceKey['key']); |             session.create_outbound(_olmAccount, identityKey, deviceKey['key']); | ||||||
|             await storeOlmSession(identityKey, session); |             await storeOlmSession(OlmSession( | ||||||
|  |               key: client.userID, | ||||||
|  |               identityKey: identityKey, | ||||||
|  |               sessionId: session.session_id(), | ||||||
|  |               session: session, | ||||||
|  |               lastReceived: | ||||||
|  |                   DateTime.now(), // we want to use a newly created session | ||||||
|  |             )); | ||||||
|           } catch (e) { |           } catch (e) { | ||||||
|  |             session.free(); | ||||||
|             print('[LibOlm] Could not create new outbound olm session: ' + |             print('[LibOlm] Could not create new outbound olm session: ' + | ||||||
|                 e.toString()); |                 e.toString()); | ||||||
|           } |           } | ||||||
|  | @ -369,14 +422,15 @@ class OlmManager { | ||||||
|       DeviceKeys device, String type, Map<String, dynamic> payload) async { |       DeviceKeys device, String type, Map<String, dynamic> payload) async { | ||||||
|     var sess = olmSessions[device.curve25519Key]; |     var sess = olmSessions[device.curve25519Key]; | ||||||
|     if (sess == null || sess.isEmpty) { |     if (sess == null || sess.isEmpty) { | ||||||
|       final sessions = await client.database |       final sessions = await getOlmSessionsFromDatabase(device.curve25519Key); | ||||||
|           .getSingleOlmSessions(client.id, device.curve25519Key, client.userID); |  | ||||||
|       if (sessions.isEmpty) { |       if (sessions.isEmpty) { | ||||||
|         throw ('No olm session found'); |         throw ('No olm session found'); | ||||||
|       } |       } | ||||||
|       sess = _olmSessions[device.curve25519Key] = sessions; |       sess = _olmSessions[device.curve25519Key] = sessions; | ||||||
|     } |     } | ||||||
|     sess.sort((a, b) => a.session_id().compareTo(b.session_id())); |     sess.sort((a, b) => a.lastReceived == b.lastReceived | ||||||
|  |         ? a.sessionId.compareTo(b.sessionId) | ||||||
|  |         : b.lastReceived.compareTo(a.lastReceived)); | ||||||
|     final fullPayload = { |     final fullPayload = { | ||||||
|       'type': type, |       'type': type, | ||||||
|       'content': payload, |       'content': payload, | ||||||
|  | @ -385,8 +439,8 @@ class OlmManager { | ||||||
|       'recipient': device.userId, |       'recipient': device.userId, | ||||||
|       'recipient_keys': {'ed25519': device.ed25519Key}, |       'recipient_keys': {'ed25519': device.ed25519Key}, | ||||||
|     }; |     }; | ||||||
|     final encryptResult = sess.first.encrypt(json.encode(fullPayload)); |     final encryptResult = sess.first.session.encrypt(json.encode(fullPayload)); | ||||||
|     storeOlmSession(device.curve25519Key, sess.first); |     storeOlmSession(sess.first); | ||||||
|     final encryptedBody = <String, dynamic>{ |     final encryptedBody = <String, dynamic>{ | ||||||
|       'algorithm': 'm.olm.v1.curve25519-aes-sha2', |       'algorithm': 'm.olm.v1.curve25519-aes-sha2', | ||||||
|       'sender_key': identityKey, |       'sender_key': identityKey, | ||||||
|  | @ -408,8 +462,8 @@ class OlmManager { | ||||||
|     if (client.database != null) { |     if (client.database != null) { | ||||||
|       for (final device in deviceKeys) { |       for (final device in deviceKeys) { | ||||||
|         if (!olmSessions.containsKey(device.curve25519Key)) { |         if (!olmSessions.containsKey(device.curve25519Key)) { | ||||||
|           final sessions = await client.database.getSingleOlmSessions( |           final sessions = | ||||||
|               client.id, device.curve25519Key, client.userID); |               await getOlmSessionsFromDatabase(device.curve25519Key); | ||||||
|           if (sessions.isNotEmpty) { |           if (sessions.isNotEmpty) { | ||||||
|             _olmSessions[device.curve25519Key] = sessions; |             _olmSessions[device.curve25519Key] = sessions; | ||||||
|           } |           } | ||||||
|  | @ -440,7 +494,7 @@ class OlmManager { | ||||||
|   void dispose() { |   void dispose() { | ||||||
|     for (final sessions in olmSessions.values) { |     for (final sessions in olmSessions.values) { | ||||||
|       for (final sess in sessions) { |       for (final sess in sessions) { | ||||||
|         sess.free(); |         sess.dispose(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     _olmAccount?.free(); |     _olmAccount?.free(); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | /* | ||||||
|  |  *   Famedly Matrix SDK | ||||||
|  |  *   Copyright (C) 2020 Famedly GmbH | ||||||
|  |  * | ||||||
|  |  *   This program is free software: you can redistribute it and/or modify | ||||||
|  |  *   it under the terms of the GNU Affero General Public License as | ||||||
|  |  *   published by the Free Software Foundation, either version 3 of the | ||||||
|  |  *   License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *   This program is distributed in the hope that it will be useful, | ||||||
|  |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  |  *   GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *   You should have received a copy of the GNU Affero General Public License | ||||||
|  |  *   along with this program.  If not, see <https://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | import 'package:olm/olm.dart' as olm; | ||||||
|  | import '../../src/database/database.dart' show DbOlmSessions; | ||||||
|  | 
 | ||||||
|  | class OlmSession { | ||||||
|  |   String identityKey; | ||||||
|  |   String sessionId; | ||||||
|  |   olm.Session session; | ||||||
|  |   DateTime lastReceived; | ||||||
|  |   final String key; | ||||||
|  |   String get pickledSession => session.pickle(key); | ||||||
|  | 
 | ||||||
|  |   bool get isValid => session != null; | ||||||
|  | 
 | ||||||
|  |   OlmSession({ | ||||||
|  |     this.key, | ||||||
|  |     this.identityKey, | ||||||
|  |     this.sessionId, | ||||||
|  |     this.session, | ||||||
|  |     this.lastReceived, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   OlmSession.fromDb(DbOlmSessions dbEntry, String key) : key = key { | ||||||
|  |     session = olm.Session(); | ||||||
|  |     try { | ||||||
|  |       session.unpickle(key, dbEntry.pickle); | ||||||
|  |       identityKey = dbEntry.identityKey; | ||||||
|  |       sessionId = dbEntry.sessionId; | ||||||
|  |       lastReceived = | ||||||
|  |           dbEntry.lastReceived ?? DateTime.fromMillisecondsSinceEpoch(0); | ||||||
|  |       assert(sessionId == session.session_id()); | ||||||
|  |     } catch (e) { | ||||||
|  |       print('[LibOlm] Could not unpickle olm session: ' + e.toString()); | ||||||
|  |       dispose(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void dispose() { | ||||||
|  |     session?.free(); | ||||||
|  |     session = null; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -16,7 +16,7 @@ class Database extends _$Database { | ||||||
|   Database(QueryExecutor e) : super(e); |   Database(QueryExecutor e) : super(e); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get schemaVersion => 4; |   int get schemaVersion => 5; | ||||||
| 
 | 
 | ||||||
|   int get maxFileSize => 1 * 1024 * 1024; |   int get maxFileSize => 1 * 1024 * 1024; | ||||||
| 
 | 
 | ||||||
|  | @ -55,6 +55,10 @@ class Database extends _$Database { | ||||||
|                 'UPDATE user_device_keys SET outdated = true'); |                 'UPDATE user_device_keys SET outdated = true'); | ||||||
|             from++; |             from++; | ||||||
|           } |           } | ||||||
|  |           if (from == 4) { | ||||||
|  |             await m.addColumn(olmSessions, olmSessions.lastReceived); | ||||||
|  |             from++; | ||||||
|  |           } | ||||||
|         }, |         }, | ||||||
|         beforeOpen: (_) async { |         beforeOpen: (_) async { | ||||||
|           if (executor.dialect == SqlDialect.sqlite) { |           if (executor.dialect == SqlDialect.sqlite) { | ||||||
|  | @ -114,22 +118,6 @@ class Database extends _$Database { | ||||||
|     return res; |     return res; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<List<olm.Session>> getSingleOlmSessions( |  | ||||||
|       int clientId, String identityKey, String userId) async { |  | ||||||
|     final rows = await dbGetOlmSessions(clientId, identityKey).get(); |  | ||||||
|     final res = <olm.Session>[]; |  | ||||||
|     for (final row in rows) { |  | ||||||
|       try { |  | ||||||
|         var session = olm.Session(); |  | ||||||
|         session.unpickle(userId, row.pickle); |  | ||||||
|         res.add(session); |  | ||||||
|       } catch (e) { |  | ||||||
|         print('[LibOlm] Could not unpickle olm session: ' + e.toString()); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return res; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   Future<DbOutboundGroupSession> getDbOutboundGroupSession( |   Future<DbOutboundGroupSession> getDbOutboundGroupSession( | ||||||
|       int clientId, String roomId) async { |       int clientId, String roomId) async { | ||||||
|     final res = await dbGetOutboundGroupSession(clientId, roomId).get(); |     final res = await dbGetOutboundGroupSession(clientId, roomId).get(); | ||||||
|  |  | ||||||
|  | @ -1391,17 +1391,20 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|   final String identityKey; |   final String identityKey; | ||||||
|   final String sessionId; |   final String sessionId; | ||||||
|   final String pickle; |   final String pickle; | ||||||
|  |   final DateTime lastReceived; | ||||||
|   DbOlmSessions( |   DbOlmSessions( | ||||||
|       {@required this.clientId, |       {@required this.clientId, | ||||||
|       @required this.identityKey, |       @required this.identityKey, | ||||||
|       @required this.sessionId, |       @required this.sessionId, | ||||||
|       @required this.pickle}); |       @required this.pickle, | ||||||
|  |       this.lastReceived}); | ||||||
|   factory DbOlmSessions.fromData( |   factory DbOlmSessions.fromData( | ||||||
|       Map<String, dynamic> data, GeneratedDatabase db, |       Map<String, dynamic> data, GeneratedDatabase db, | ||||||
|       {String prefix}) { |       {String prefix}) { | ||||||
|     final effectivePrefix = prefix ?? ''; |     final effectivePrefix = prefix ?? ''; | ||||||
|     final intType = db.typeSystem.forDartType<int>(); |     final intType = db.typeSystem.forDartType<int>(); | ||||||
|     final stringType = db.typeSystem.forDartType<String>(); |     final stringType = db.typeSystem.forDartType<String>(); | ||||||
|  |     final dateTimeType = db.typeSystem.forDartType<DateTime>(); | ||||||
|     return DbOlmSessions( |     return DbOlmSessions( | ||||||
|       clientId: |       clientId: | ||||||
|           intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), |           intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), | ||||||
|  | @ -1411,6 +1414,8 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|           .mapFromDatabaseResponse(data['${effectivePrefix}session_id']), |           .mapFromDatabaseResponse(data['${effectivePrefix}session_id']), | ||||||
|       pickle: |       pickle: | ||||||
|           stringType.mapFromDatabaseResponse(data['${effectivePrefix}pickle']), |           stringType.mapFromDatabaseResponse(data['${effectivePrefix}pickle']), | ||||||
|  |       lastReceived: dateTimeType | ||||||
|  |           .mapFromDatabaseResponse(data['${effectivePrefix}last_received']), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   @override |   @override | ||||||
|  | @ -1428,6 +1433,9 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|     if (!nullToAbsent || pickle != null) { |     if (!nullToAbsent || pickle != null) { | ||||||
|       map['pickle'] = Variable<String>(pickle); |       map['pickle'] = Variable<String>(pickle); | ||||||
|     } |     } | ||||||
|  |     if (!nullToAbsent || lastReceived != null) { | ||||||
|  |       map['last_received'] = Variable<DateTime>(lastReceived); | ||||||
|  |     } | ||||||
|     return map; |     return map; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1439,6 +1447,7 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|       identityKey: serializer.fromJson<String>(json['identity_key']), |       identityKey: serializer.fromJson<String>(json['identity_key']), | ||||||
|       sessionId: serializer.fromJson<String>(json['session_id']), |       sessionId: serializer.fromJson<String>(json['session_id']), | ||||||
|       pickle: serializer.fromJson<String>(json['pickle']), |       pickle: serializer.fromJson<String>(json['pickle']), | ||||||
|  |       lastReceived: serializer.fromJson<DateTime>(json['last_received']), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   @override |   @override | ||||||
|  | @ -1449,6 +1458,7 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|       'identity_key': serializer.toJson<String>(identityKey), |       'identity_key': serializer.toJson<String>(identityKey), | ||||||
|       'session_id': serializer.toJson<String>(sessionId), |       'session_id': serializer.toJson<String>(sessionId), | ||||||
|       'pickle': serializer.toJson<String>(pickle), |       'pickle': serializer.toJson<String>(pickle), | ||||||
|  |       'last_received': serializer.toJson<DateTime>(lastReceived), | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1456,12 +1466,14 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|           {int clientId, |           {int clientId, | ||||||
|           String identityKey, |           String identityKey, | ||||||
|           String sessionId, |           String sessionId, | ||||||
|           String pickle}) => |           String pickle, | ||||||
|  |           DateTime lastReceived}) => | ||||||
|       DbOlmSessions( |       DbOlmSessions( | ||||||
|         clientId: clientId ?? this.clientId, |         clientId: clientId ?? this.clientId, | ||||||
|         identityKey: identityKey ?? this.identityKey, |         identityKey: identityKey ?? this.identityKey, | ||||||
|         sessionId: sessionId ?? this.sessionId, |         sessionId: sessionId ?? this.sessionId, | ||||||
|         pickle: pickle ?? this.pickle, |         pickle: pickle ?? this.pickle, | ||||||
|  |         lastReceived: lastReceived ?? this.lastReceived, | ||||||
|       ); |       ); | ||||||
|   @override |   @override | ||||||
|   String toString() { |   String toString() { | ||||||
|  | @ -1469,14 +1481,19 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|           ..write('clientId: $clientId, ') |           ..write('clientId: $clientId, ') | ||||||
|           ..write('identityKey: $identityKey, ') |           ..write('identityKey: $identityKey, ') | ||||||
|           ..write('sessionId: $sessionId, ') |           ..write('sessionId: $sessionId, ') | ||||||
|           ..write('pickle: $pickle') |           ..write('pickle: $pickle, ') | ||||||
|  |           ..write('lastReceived: $lastReceived') | ||||||
|           ..write(')')) |           ..write(')')) | ||||||
|         .toString(); |         .toString(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   int get hashCode => $mrjf($mrjc(clientId.hashCode, |   int get hashCode => $mrjf($mrjc( | ||||||
|       $mrjc(identityKey.hashCode, $mrjc(sessionId.hashCode, pickle.hashCode)))); |       clientId.hashCode, | ||||||
|  |       $mrjc( | ||||||
|  |           identityKey.hashCode, | ||||||
|  |           $mrjc(sessionId.hashCode, | ||||||
|  |               $mrjc(pickle.hashCode, lastReceived.hashCode))))); | ||||||
|   @override |   @override | ||||||
|   bool operator ==(dynamic other) => |   bool operator ==(dynamic other) => | ||||||
|       identical(this, other) || |       identical(this, other) || | ||||||
|  | @ -1484,7 +1501,8 @@ class DbOlmSessions extends DataClass implements Insertable<DbOlmSessions> { | ||||||
|           other.clientId == this.clientId && |           other.clientId == this.clientId && | ||||||
|           other.identityKey == this.identityKey && |           other.identityKey == this.identityKey && | ||||||
|           other.sessionId == this.sessionId && |           other.sessionId == this.sessionId && | ||||||
|           other.pickle == this.pickle); |           other.pickle == this.pickle && | ||||||
|  |           other.lastReceived == this.lastReceived); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> { | class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> { | ||||||
|  | @ -1492,17 +1510,20 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> { | ||||||
|   final Value<String> identityKey; |   final Value<String> identityKey; | ||||||
|   final Value<String> sessionId; |   final Value<String> sessionId; | ||||||
|   final Value<String> pickle; |   final Value<String> pickle; | ||||||
|  |   final Value<DateTime> lastReceived; | ||||||
|   const OlmSessionsCompanion({ |   const OlmSessionsCompanion({ | ||||||
|     this.clientId = const Value.absent(), |     this.clientId = const Value.absent(), | ||||||
|     this.identityKey = const Value.absent(), |     this.identityKey = const Value.absent(), | ||||||
|     this.sessionId = const Value.absent(), |     this.sessionId = const Value.absent(), | ||||||
|     this.pickle = const Value.absent(), |     this.pickle = const Value.absent(), | ||||||
|  |     this.lastReceived = const Value.absent(), | ||||||
|   }); |   }); | ||||||
|   OlmSessionsCompanion.insert({ |   OlmSessionsCompanion.insert({ | ||||||
|     @required int clientId, |     @required int clientId, | ||||||
|     @required String identityKey, |     @required String identityKey, | ||||||
|     @required String sessionId, |     @required String sessionId, | ||||||
|     @required String pickle, |     @required String pickle, | ||||||
|  |     this.lastReceived = const Value.absent(), | ||||||
|   })  : clientId = Value(clientId), |   })  : clientId = Value(clientId), | ||||||
|         identityKey = Value(identityKey), |         identityKey = Value(identityKey), | ||||||
|         sessionId = Value(sessionId), |         sessionId = Value(sessionId), | ||||||
|  | @ -1512,12 +1533,14 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> { | ||||||
|     Expression<String> identityKey, |     Expression<String> identityKey, | ||||||
|     Expression<String> sessionId, |     Expression<String> sessionId, | ||||||
|     Expression<String> pickle, |     Expression<String> pickle, | ||||||
|  |     Expression<DateTime> lastReceived, | ||||||
|   }) { |   }) { | ||||||
|     return RawValuesInsertable({ |     return RawValuesInsertable({ | ||||||
|       if (clientId != null) 'client_id': clientId, |       if (clientId != null) 'client_id': clientId, | ||||||
|       if (identityKey != null) 'identity_key': identityKey, |       if (identityKey != null) 'identity_key': identityKey, | ||||||
|       if (sessionId != null) 'session_id': sessionId, |       if (sessionId != null) 'session_id': sessionId, | ||||||
|       if (pickle != null) 'pickle': pickle, |       if (pickle != null) 'pickle': pickle, | ||||||
|  |       if (lastReceived != null) 'last_received': lastReceived, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1525,12 +1548,14 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> { | ||||||
|       {Value<int> clientId, |       {Value<int> clientId, | ||||||
|       Value<String> identityKey, |       Value<String> identityKey, | ||||||
|       Value<String> sessionId, |       Value<String> sessionId, | ||||||
|       Value<String> pickle}) { |       Value<String> pickle, | ||||||
|  |       Value<DateTime> lastReceived}) { | ||||||
|     return OlmSessionsCompanion( |     return OlmSessionsCompanion( | ||||||
|       clientId: clientId ?? this.clientId, |       clientId: clientId ?? this.clientId, | ||||||
|       identityKey: identityKey ?? this.identityKey, |       identityKey: identityKey ?? this.identityKey, | ||||||
|       sessionId: sessionId ?? this.sessionId, |       sessionId: sessionId ?? this.sessionId, | ||||||
|       pickle: pickle ?? this.pickle, |       pickle: pickle ?? this.pickle, | ||||||
|  |       lastReceived: lastReceived ?? this.lastReceived, | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -1549,6 +1574,9 @@ class OlmSessionsCompanion extends UpdateCompanion<DbOlmSessions> { | ||||||
|     if (pickle.present) { |     if (pickle.present) { | ||||||
|       map['pickle'] = Variable<String>(pickle.value); |       map['pickle'] = Variable<String>(pickle.value); | ||||||
|     } |     } | ||||||
|  |     if (lastReceived.present) { | ||||||
|  |       map['last_received'] = Variable<DateTime>(lastReceived.value); | ||||||
|  |     } | ||||||
|     return map; |     return map; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -1591,9 +1619,19 @@ class OlmSessions extends Table with TableInfo<OlmSessions, DbOlmSessions> { | ||||||
|         $customConstraints: 'NOT NULL'); |         $customConstraints: 'NOT NULL'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   final VerificationMeta _lastReceivedMeta = | ||||||
|  |       const VerificationMeta('lastReceived'); | ||||||
|  |   GeneratedDateTimeColumn _lastReceived; | ||||||
|  |   GeneratedDateTimeColumn get lastReceived => | ||||||
|  |       _lastReceived ??= _constructLastReceived(); | ||||||
|  |   GeneratedDateTimeColumn _constructLastReceived() { | ||||||
|  |     return GeneratedDateTimeColumn('last_received', $tableName, true, | ||||||
|  |         $customConstraints: ''); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   List<GeneratedColumn> get $columns => |   List<GeneratedColumn> get $columns => | ||||||
|       [clientId, identityKey, sessionId, pickle]; |       [clientId, identityKey, sessionId, pickle, lastReceived]; | ||||||
|   @override |   @override | ||||||
|   OlmSessions get asDslTable => this; |   OlmSessions get asDslTable => this; | ||||||
|   @override |   @override | ||||||
|  | @ -1631,6 +1669,12 @@ class OlmSessions extends Table with TableInfo<OlmSessions, DbOlmSessions> { | ||||||
|     } else if (isInserting) { |     } else if (isInserting) { | ||||||
|       context.missing(_pickleMeta); |       context.missing(_pickleMeta); | ||||||
|     } |     } | ||||||
|  |     if (data.containsKey('last_received')) { | ||||||
|  |       context.handle( | ||||||
|  |           _lastReceivedMeta, | ||||||
|  |           lastReceived.isAcceptableOrUnknown( | ||||||
|  |               data['last_received'], _lastReceivedMeta)); | ||||||
|  |     } | ||||||
|     return context; |     return context; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -5520,6 +5564,7 @@ abstract class _$Database extends GeneratedDatabase { | ||||||
|       identityKey: row.readString('identity_key'), |       identityKey: row.readString('identity_key'), | ||||||
|       sessionId: row.readString('session_id'), |       sessionId: row.readString('session_id'), | ||||||
|       pickle: row.readString('pickle'), |       pickle: row.readString('pickle'), | ||||||
|  |       lastReceived: row.readDateTime('last_received'), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -5543,15 +5588,16 @@ abstract class _$Database extends GeneratedDatabase { | ||||||
|         }).map(_rowToDbOlmSessions); |         }).map(_rowToDbOlmSessions); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<int> storeOlmSession( |   Future<int> storeOlmSession(int client_id, String identitiy_key, | ||||||
|       int client_id, String identitiy_key, String session_id, String pickle) { |       String session_id, String pickle, DateTime last_received) { | ||||||
|     return customInsert( |     return customInsert( | ||||||
|       'INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle) VALUES (:client_id, :identitiy_key, :session_id, :pickle)', |       'INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle, last_received) VALUES (:client_id, :identitiy_key, :session_id, :pickle, :last_received)', | ||||||
|       variables: [ |       variables: [ | ||||||
|         Variable.withInt(client_id), |         Variable.withInt(client_id), | ||||||
|         Variable.withString(identitiy_key), |         Variable.withString(identitiy_key), | ||||||
|         Variable.withString(session_id), |         Variable.withString(session_id), | ||||||
|         Variable.withString(pickle) |         Variable.withString(pickle), | ||||||
|  |         Variable.withDateTime(last_received) | ||||||
|       ], |       ], | ||||||
|       updates: {olmSessions}, |       updates: {olmSessions}, | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ CREATE TABLE olm_sessions ( | ||||||
|   identity_key TEXT NOT NULL, |   identity_key TEXT NOT NULL, | ||||||
|   session_id TEXT NOT NULL, |   session_id TEXT NOT NULL, | ||||||
|   pickle TEXT NOT NULL, |   pickle TEXT NOT NULL, | ||||||
|  |   last_received DATETIME, | ||||||
|   UNIQUE(client_id, identity_key, session_id) |   UNIQUE(client_id, identity_key, session_id) | ||||||
| ) AS DbOlmSessions; | ) AS DbOlmSessions; | ||||||
| CREATE INDEX olm_sessions_index ON olm_sessions(client_id); | CREATE INDEX olm_sessions_index ON olm_sessions(client_id); | ||||||
|  | @ -177,7 +178,7 @@ getAllUserDeviceKeysKeys: SELECT * FROM user_device_keys_key WHERE client_id = : | ||||||
| getAllUserCrossSigningKeys: SELECT * FROM user_cross_signing_keys WHERE client_id = :client_id; | getAllUserCrossSigningKeys: SELECT * FROM user_cross_signing_keys WHERE client_id = :client_id; | ||||||
| getAllOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id; | getAllOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id; | ||||||
| dbGetOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id AND identity_key = :identity_key; | dbGetOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id AND identity_key = :identity_key; | ||||||
| storeOlmSession: INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle) VALUES (:client_id, :identitiy_key, :session_id, :pickle); | storeOlmSession: INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle, last_received) VALUES (:client_id, :identitiy_key, :session_id, :pickle, :last_received); | ||||||
| getAllOutboundGroupSessions: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id; | getAllOutboundGroupSessions: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id; | ||||||
| dbGetOutboundGroupSession: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id; | dbGetOutboundGroupSession: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id; | ||||||
| storeOutboundGroupSession: INSERT OR REPLACE INTO outbound_group_sessions (client_id, room_id, pickle, device_ids, creation_time, sent_messages) VALUES (:client_id, :room_id, :pickle, :device_ids, :creation_time, :sent_messages); | storeOutboundGroupSession: INSERT OR REPLACE INTO outbound_group_sessions (client_id, room_id, pickle, device_ids, creation_time, sent_messages) VALUES (:client_id, :room_id, :pickle, :device_ids, :creation_time, :sent_messages); | ||||||
|  |  | ||||||
|  | @ -99,8 +99,26 @@ void main() { | ||||||
|           false); |           false); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     test('restoreOlmSession', () async { | ||||||
|  |       client.encryption.olmManager.olmSessions.clear(); | ||||||
|  |       await client.encryption.olmManager | ||||||
|  |           .restoreOlmSession(client.userID, client.identityKey); | ||||||
|  |       expect(client.encryption.olmManager.olmSessions.length, 1); | ||||||
|  | 
 | ||||||
|  |       client.encryption.olmManager.olmSessions.clear(); | ||||||
|  |       await client.encryption.olmManager | ||||||
|  |           .restoreOlmSession(client.userID, 'invalid'); | ||||||
|  |       expect(client.encryption.olmManager.olmSessions.length, 0); | ||||||
|  | 
 | ||||||
|  |       client.encryption.olmManager.olmSessions.clear(); | ||||||
|  |       await client.encryption.olmManager | ||||||
|  |           .restoreOlmSession('invalid', client.identityKey); | ||||||
|  |       expect(client.encryption.olmManager.olmSessions.length, 0); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     test('startOutgoingOlmSessions', () async { |     test('startOutgoingOlmSessions', () async { | ||||||
|       // start an olm session.....with ourself! |       // start an olm session.....with ourself! | ||||||
|  |       client.encryption.olmManager.olmSessions.clear(); | ||||||
|       await client.encryption.olmManager.startOutgoingOlmSessions( |       await client.encryption.olmManager.startOutgoingOlmSessions( | ||||||
|           [client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]]); |           [client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]]); | ||||||
|       expect( |       expect( | ||||||
|  |  | ||||||
|  | @ -139,12 +139,10 @@ void test() async { | ||||||
|   assert(testClientB |   assert(testClientB | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].length == |           .encryption.olmManager.olmSessions[testClientA.identityKey].length == | ||||||
|       1); |       1); | ||||||
|   assert(testClientA |   assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey] | ||||||
|           .encryption.olmManager.olmSessions[testClientB.identityKey].first |           .first.sessionId == | ||||||
|           .session_id() == |       testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] | ||||||
|       testClientB |           .first.sessionId); | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].first |  | ||||||
|           .session_id()); |  | ||||||
|   assert(inviteRoom.client.encryption.keyManager |   assert(inviteRoom.client.encryption.keyManager | ||||||
|           .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != |           .getInboundGroupSession(inviteRoom.id, currentSessionIdA, '') != | ||||||
|       null); |       null); | ||||||
|  | @ -162,12 +160,10 @@ void test() async { | ||||||
|   assert(testClientB |   assert(testClientB | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].length == |           .encryption.olmManager.olmSessions[testClientA.identityKey].length == | ||||||
|       1); |       1); | ||||||
|   assert(testClientA |   assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey] | ||||||
|           .encryption.olmManager.olmSessions[testClientB.identityKey].first |           .first.sessionId == | ||||||
|           .session_id() == |       testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] | ||||||
|       testClientB |           .first.sessionId); | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].first |  | ||||||
|           .session_id()); |  | ||||||
| 
 | 
 | ||||||
|   assert(room.client.encryption.keyManager |   assert(room.client.encryption.keyManager | ||||||
|           .getOutboundGroupSession(room.id) |           .getOutboundGroupSession(room.id) | ||||||
|  | @ -231,24 +227,20 @@ void test() async { | ||||||
|   assert(testClientB |   assert(testClientB | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].length == |           .encryption.olmManager.olmSessions[testClientA.identityKey].length == | ||||||
|       1); |       1); | ||||||
|   assert(testClientA |   assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey] | ||||||
|           .encryption.olmManager.olmSessions[testClientB.identityKey].first |           .first.sessionId == | ||||||
|           .session_id() == |       testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] | ||||||
|       testClientB |           .first.sessionId); | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].first |  | ||||||
|           .session_id()); |  | ||||||
|   assert(testClientA |   assert(testClientA | ||||||
|           .encryption.olmManager.olmSessions[testClientC.identityKey].length == |           .encryption.olmManager.olmSessions[testClientC.identityKey].length == | ||||||
|       1); |       1); | ||||||
|   assert(testClientC |   assert(testClientC | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].length == |           .encryption.olmManager.olmSessions[testClientA.identityKey].length == | ||||||
|       1); |       1); | ||||||
|   assert(testClientA |   assert(testClientA.encryption.olmManager.olmSessions[testClientC.identityKey] | ||||||
|           .encryption.olmManager.olmSessions[testClientC.identityKey].first |           .first.sessionId == | ||||||
|           .session_id() == |       testClientC.encryption.olmManager.olmSessions[testClientA.identityKey] | ||||||
|       testClientC |           .first.sessionId); | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].first |  | ||||||
|           .session_id()); |  | ||||||
|   assert(room.client.encryption.keyManager |   assert(room.client.encryption.keyManager | ||||||
|           .getOutboundGroupSession(room.id) |           .getOutboundGroupSession(room.id) | ||||||
|           .outboundGroupSession |           .outboundGroupSession | ||||||
|  | @ -281,12 +273,10 @@ void test() async { | ||||||
|   assert(testClientB |   assert(testClientB | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].length == |           .encryption.olmManager.olmSessions[testClientA.identityKey].length == | ||||||
|       1); |       1); | ||||||
|   assert(testClientA |   assert(testClientA.encryption.olmManager.olmSessions[testClientB.identityKey] | ||||||
|           .encryption.olmManager.olmSessions[testClientB.identityKey].first |           .first.sessionId == | ||||||
|           .session_id() == |       testClientB.encryption.olmManager.olmSessions[testClientA.identityKey] | ||||||
|       testClientB |           .first.sessionId); | ||||||
|           .encryption.olmManager.olmSessions[testClientA.identityKey].first |  | ||||||
|           .session_id()); |  | ||||||
|   assert(room.client.encryption.keyManager |   assert(room.client.encryption.keyManager | ||||||
|           .getOutboundGroupSession(room.id) |           .getOutboundGroupSession(room.id) | ||||||
|           .outboundGroupSession |           .outboundGroupSession | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue