diff --git a/lib/src/client.dart b/lib/src/client.dart index bbd7775..b6facac 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -1216,6 +1216,54 @@ class Client { } } + void _handleRoomKeyVerificationRequest(EventUpdate update) { + final event = update.content; + final type = event['type'].startsWith('m.key.verification.') + ? event['type'] + : event['content']['msgtype']; + if (!type.startsWith('m.key.verification.')) { + return; + } + if (type == 'm.key.verification.request') { + event['content']['timestamp'] = event['origin_server_ts']; + } + final transactionId = + KeyVerification.getTransactionId(event['content']) ?? event['event_id']; + if (_keyVerificationRequests.containsKey(transactionId)) { + final req = _keyVerificationRequests[transactionId]; + if (event['sender'] != userID) { + req.handlePayload(type, event['content'], event['event_id']); + } else if (req.userId == userID && req.deviceId == null) { + req + .handlePayload(type, event['content'], event['event_id']) + .then((ret) { + if (req.deviceId != deviceID) { + req.otherDeviceAccepted(); + req.dispose(); + _keyVerificationRequests.remove(transactionId); + } + }); + } + } else if (event['sender'] != userID) { + final room = + getRoomById(update.roomID) ?? Room(id: update.roomID, client: this); + final newKeyRequest = + KeyVerification(client: this, userId: event['sender'], room: room); + newKeyRequest + .handlePayload(type, event['content'], event['event_id']) + .then((res) { + if (newKeyRequest.state != KeyVerificationState.askAccept) { + // something went wrong, let's just dispose the request + newKeyRequest.dispose(); + } else { + // new request! Let's notify it and stuff + _keyVerificationRequests[transactionId] = newKeyRequest; + onKeyVerificationRequest.add(newKeyRequest); + } + }); + } + } + Future _handleRooms( Map rooms, Membership membership) async { for (final entry in rooms.entries) { @@ -1416,6 +1464,11 @@ class Client { await database.storeEventUpdate(id, update); } _updateRoomsByEventUpdate(update); + if (event['type'].startsWith('m.key.verification.') || + (event['type'] == 'm.room.message' && + event['content']['msgtype'].startsWith('m.key.verification.'))) { + _handleRoomKeyVerificationRequest(update); + } onEvent.add(update); if (event['type'] == 'm.call.invite') { diff --git a/lib/src/cross_signing.dart b/lib/src/cross_signing.dart index 67803c3..efd0f76 100644 --- a/lib/src/cross_signing.dart +++ b/lib/src/cross_signing.dart @@ -39,7 +39,10 @@ class CrossSigning { } finally { keyObj.free(); } - if (masterPubkey == null || !client.userDeviceKeys.containsKey(client.userID) || !client.userDeviceKeys[client.userID].deviceKeys.containsKey(client.deviceID)) { + if (masterPubkey == null || + !client.userDeviceKeys.containsKey(client.userID) || + !client.userDeviceKeys[client.userID].deviceKeys + .containsKey(client.deviceID)) { throw 'Master or user keys not found'; } final masterKey = client.userDeviceKeys[client.userID].masterKey; @@ -49,7 +52,10 @@ class CrossSigning { // master key is valid, set it to verified masterKey.setVerified(true, false); // and now sign bout our own key and our master key - await sign([masterKey, client.userDeviceKeys[client.userID].deviceKeys[client.deviceID]]); + await sign([ + masterKey, + client.userDeviceKeys[client.userID].deviceKeys[client.deviceID] + ]); } bool signable(List keys) { @@ -82,6 +88,7 @@ class CrossSigning { if (!signatures[key.userId].containsKey(key.identifier)) { signatures[key.userId][key.identifier] = Map.from(key.toJson()); + // we don't need to send all old signatures, so let's just remove them signatures[key.userId][key.identifier].remove('signatures'); } if (!signatures[key.userId][key.identifier].containsKey('signatures')) { diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index 9559218..4a89f2b 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -3,6 +3,8 @@ import 'package:canonical_json/canonical_json.dart'; import 'package:olm/olm.dart' as olm; import '../client.dart'; +import '../user.dart'; +import '../room.dart'; import '../database/database.dart' show DbUserDeviceKey, DbUserDeviceKeysKey, DbUserCrossSigningKey; import '../event.dart'; @@ -49,6 +51,20 @@ class DeviceKeysList { return UserVerifiedStatus.unknown; } + Future startVerification() async { + final roomId = + await User(userId, room: Room(client: client)).startDirectChat(); + if (roomId == null) { + throw 'Unable to start new room'; + } + final room = client.getRoomById(roomId) ?? Room(id: roomId, client: client); + final request = KeyVerification(client: client, room: room, userId: userId); + await request.start(); + // no need to add to the request client object. As we are doing a room + // verification request that'll happen automatically once we know the transaction id + return request; + } + DeviceKeysList.fromDb( DbUserDeviceKey dbEntry, List childEntries, diff --git a/lib/src/utils/key_verification.dart b/lib/src/utils/key_verification.dart index 96b56cc..1858a8d 100644 --- a/lib/src/utils/key_verification.dart +++ b/lib/src/utils/key_verification.dart @@ -83,11 +83,11 @@ List _bytesToInt(Uint8List bytes, int totalBits) { return ret; } -final VERIFICATION_METHODS = [_KeyVerificationMethodSas.type]; +final VERIFICATION_METHODS = ['m.sas.v1']; _KeyVerificationMethod _makeVerificationMethod( String type, KeyVerification request) { - if (type == _KeyVerificationMethodSas.type) { + if (type == 'm.sas.v1') { return _KeyVerificationMethodSas(request: request); } throw 'Unkown method type'; @@ -138,7 +138,7 @@ class KeyVerification { Future sendStart() async { await send('m.key.verification.request', { 'methods': VERIFICATION_METHODS, - 'timestamp': DateTime.now().millisecondsSinceEpoch, + if (room == null) 'timestamp': DateTime.now().millisecondsSinceEpoch, }); startedVerification = true; setState(KeyVerificationState.waitingAccept); @@ -189,6 +189,7 @@ class KeyVerification { setState(KeyVerificationState.askAccept); break; case 'm.key.verification.ready': + _deviceId ??= payload['from_device']; possibleMethods = _intersect(VERIFICATION_METHODS, payload['methods']); if (possibleMethods.isEmpty) { @@ -205,6 +206,32 @@ class KeyVerification { _deviceId ??= payload['from_device']; print('Setting device id start: ' + _deviceId.toString()); transactionId ??= eventId ?? payload['transaction_id']; + if (method != null) { + print('DUPLICATE START'); + // the other side sent us a start, even though we already sent one + if (payload['method'] == method.type) { + // same method. Determine priority + final ourEntry = '${client.userID}|${client.deviceID}'; + final entries = [ourEntry, '${userId}|${deviceId}']; + entries.sort(); + if (entries.first == ourEntry) { + // our start won, nothing to do + print('we won, nothing to do'); + return; + } else { + print('They won, handing off'); + // the other start won, let's hand off + startedVerification = false; // it is now as if they started + lastStep = + 'm.key.verification.request'; // we fake the last step + method.dispose(); // in case anything got created already + } + } else { + // methods don't match up, let's cancel this + await cancel('m.unexpected_message'); + return; + } + } if (!(await verifyLastStep(['m.key.verification.request', null]))) { return; // abort } @@ -221,6 +248,7 @@ class KeyVerification { startPaylaod = payload; setState(KeyVerificationState.askAccept); } else { + print('handling start in method.....'); await method.handlePayload(type, payload); } break; @@ -247,6 +275,13 @@ class KeyVerification { } } + void otherDeviceAccepted() { + canceled = true; + canceledCode = 'm.accepted'; + canceledReason = 'm.accepted'; + setState(KeyVerificationState.error); + } + Future openSSSS( {String password, String recoveryKey, bool skip = false}) async { final next = () { @@ -482,6 +517,9 @@ abstract class _KeyVerificationMethod { return false; } + String _type; + String get type => _type; + Future sendStart(); void dispose() {} } @@ -495,7 +533,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { _KeyVerificationMethodSas({KeyVerification request}) : super(request: request); - static String type = 'm.sas.v1'; + @override + String _type = 'm.sas.v1'; String keyAgreementProtocol; String hash; diff --git a/pubspec.yaml b/pubspec.yaml index 51d3e62..8fd70fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: olm: git: url: https://gitlab.com/famedly/libraries/dart-olm.git - ref: 8749474d611f02a89893e067b6e479ebfd40c51d + ref: a6cde466e707b54e39df8d035a0a79714621bc0b matrix_file_e2ee: path: /home/sorunome/repos/famedly/matrix_file_e2ee