diff --git a/lib/src/key_manager.dart b/lib/src/key_manager.dart index 32c6554..abe5b37 100644 --- a/lib/src/key_manager.dart +++ b/lib/src/key_manager.dart @@ -1,8 +1,15 @@ +import 'dart:core'; +import 'dart:convert'; + +import 'package:olm/olm.dart' as olm; + import 'client.dart'; import 'room.dart'; import 'utils/to_device_event.dart'; import 'utils/device_keys_list.dart'; +const MEGOLM_KEY = 'm.megolm_backup.v1'; + class KeyManager { final Client client; final outgoingShareRequests = {}; @@ -10,8 +17,109 @@ class KeyManager { KeyManager(this.client); + bool get enabled => client.accountData[MEGOLM_KEY] != null; + + Future> getRoomKeysInfo() async { + return await client.jsonRequest( + type: HTTPType.GET, + action: '/client/r0/room_keys/version', + ); + } + + Future isCached() async { + if (!enabled) { + return false; + } + return (await client.ssss.getCached(MEGOLM_KEY)) != null; + } + + Future loadFromResponse(Map payload) async { + if (!(await isCached())) { + return; + } + if (!(payload['rooms'] is Map)) { + return; + } + final privateKey = base64.decode(await client.ssss.getCached(MEGOLM_KEY)); + final decryption = olm.PkDecryption(); + String backupPubKey; + try { + backupPubKey = decryption.init_with_private_key(privateKey); + } catch (_) { + decryption.free(); + rethrow; + } + if (backupPubKey == null) { + decryption.free(); + return; + } + // TODO: check if pubkey is valid + for (final roomEntries in payload['rooms'].entries) { + final roomId = roomEntries.key; + if (!(roomEntries.value is Map) || !(roomEntries.value['sessions'] is Map)) { + continue; + } + for (final sessionEntries in roomEntries.value['sessions'].entries) { + final sessionId = sessionEntries.key; + final rawEncryptedSession = sessionEntries.value; + if (!(rawEncryptedSession is Map)) { + continue; + } + final firstMessageIndex = rawEncryptedSession['first_message_index'] is int ? rawEncryptedSession['first_message_index'] : null; + final forwardedCount = rawEncryptedSession['forwarded_count'] is int ? rawEncryptedSession['forwarded_count'] : null; + final isVerified = rawEncryptedSession['is_verified'] is bool ? rawEncryptedSession['is_verified'] : null; + final sessionData = rawEncryptedSession['session_data']; + if (firstMessageIndex == null || forwardedCount == null || isVerified == null || !(sessionData is Map)) { + continue; + } + final senderKey = sessionData['sender_key']; + Map decrypted; + try { + decrypted = json.decode(decryption.decrypt(sessionData['ephemeral'], sessionData['mac'], sessionData['ciphertext'])); + } catch (err) { + print('[LibOlm] Error decrypting room key: ' + err.toString()); + } + if (decrypted != null) { + decrypted['session_id'] = sessionId; + decrypted['room_id'] = roomId; + final room = client.getRoomById(roomId) ?? Room(id: roomId, client: client); + room.setInboundGroupSession(sessionId, decrypted, forwarded: true); + } + } + } + } + + Future loadSingleKey(String roomId, String sessionId) async { + final info = await getRoomKeysInfo(); + final ret = await client.jsonRequest( + type: HTTPType.GET, + action: '/client/r0/room_keys/keys/${Uri.encodeComponent(roomId)}/${Uri.encodeComponent(sessionId)}?version=${info['version']}', + ); + await loadFromResponse({ + 'rooms': { + roomId: { + 'sessions': { + sessionId: ret, + }, + }, + }, + }); + } + /// Request a certain key from another device Future request(Room room, String sessionId, String senderKey) async { + // let's first check our online key backup store thingy... + var hadPreviously = room.inboundGroupSessions.containsKey(sessionId); + try { + await loadSingleKey(room.id, sessionId); + } catch (err, stacktrace) { + print('++++++++++++++++++'); + print(err.toString()); + print(stacktrace); + } + if (!hadPreviously && room.inboundGroupSessions.containsKey(sessionId)) { + return; // we managed to load the session from online backup, no need to care about it now + } // while we just send the to-device event to '*', we still need to save the // devices themself to know where to send the cancel to after receiving a reply final devices = await room.getUserDeviceKeys(); diff --git a/lib/src/room.dart b/lib/src/room.dart index a17b5b3..4c7048a 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -251,6 +251,7 @@ class Room { inboundGroupSession = null; print('[LibOlm] Could not create new InboundGroupSession: ' + e.toString()); + return; } } _inboundGroupSessions[sessionId] = SessionKey( diff --git a/pubspec.yaml b/pubspec.yaml index c1a1765..c37e405 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,9 +22,10 @@ dependencies: password_hash: ^2.0.0 olm: - git: - url: https://gitlab.com/famedly/libraries/dart-olm.git - ref: 0c612a525511652a7760126b058de8c924fe8900 + path: /home/sorunome/repos/famedly/dart-olm +# git: +# url: https://gitlab.com/famedly/libraries/dart-olm.git +# ref: 0c612a525511652a7760126b058de8c924fe8900 matrix_file_e2ee: path: /home/sorunome/repos/famedly/matrix_file_e2ee