diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index b8b70ce..6821dcf 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -18,6 +18,7 @@ import 'dart:convert'; +import 'package:pedantic/pedantic.dart'; import 'package:canonical_json/canonical_json.dart'; import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/matrix_api.dart'; @@ -323,6 +324,19 @@ class OlmManager { return res; } + Future 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 decryptToDeviceEvent(ToDeviceEvent event) async { if (event.type != EventTypes.Encrypted) { return event; @@ -342,12 +356,20 @@ class OlmManager { if (!_olmSessions.containsKey(senderKey)) { await loadFromDb(); } - event = _decryptToDeviceEvent(event); - if (event.type != EventTypes.Encrypted || !(await loadFromDb())) { - return event; + try { + event = _decryptToDeviceEvent(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 startOutgoingOlmSessions(List deviceKeys) async { @@ -383,7 +405,8 @@ class OlmManager { identityKey: identityKey, sessionId: session.session_id(), session: session, - lastReceived: DateTime.fromMillisecondsSinceEpoch(0), + lastReceived: + DateTime.now(), // we want to use a newly created session )); } catch (e) { session.free(); diff --git a/test/encryption/olm_manager_test.dart b/test/encryption/olm_manager_test.dart index 047d0ec..bf0e7a3 100644 --- a/test/encryption/olm_manager_test.dart +++ b/test/encryption/olm_manager_test.dart @@ -99,8 +99,26 @@ void main() { 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 { // start an olm session.....with ourself! + client.encryption.olmManager.olmSessions.clear(); await client.encryption.olmManager.startOutgoingOlmSessions( [client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]]); expect(