LCOV - code coverage report
Current view: top level - lib/encryption - key_verification_manager.dart (source / functions) Hit Total Coverage
Test: merged.info Lines: 63 65 96.9 %
Date: 2024-09-30 15:57:20 Functions: 0 0 -

          Line data    Source code
       1             : /*
       2             :  *   Famedly Matrix SDK
       3             :  *   Copyright (C) 2020, 2021 Famedly GmbH
       4             :  *
       5             :  *   This program is free software: you can redistribute it and/or modify
       6             :  *   it under the terms of the GNU Affero General Public License as
       7             :  *   published by the Free Software Foundation, either version 3 of the
       8             :  *   License, or (at your option) any later version.
       9             :  *
      10             :  *   This program is distributed in the hope that it will be useful,
      11             :  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      13             :  *   GNU Affero General Public License for more details.
      14             :  *
      15             :  *   You should have received a copy of the GNU Affero General Public License
      16             :  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : import 'package:matrix/encryption/encryption.dart';
      20             : import 'package:matrix/encryption/utils/key_verification.dart';
      21             : import 'package:matrix/matrix.dart';
      22             : 
      23             : class KeyVerificationManager {
      24             :   final Encryption encryption;
      25           9 :   Client get client => encryption.client;
      26             : 
      27          24 :   KeyVerificationManager(this.encryption);
      28             : 
      29             :   final Map<String, KeyVerification> _requests = {};
      30             : 
      31          24 :   Future<void> cleanup() async {
      32             :     final Set entriesToDispose = <String>{};
      33          50 :     for (final entry in _requests.entries) {
      34           4 :       var dispose = entry.value.canceled ||
      35           6 :           entry.value.state == KeyVerificationState.done ||
      36           6 :           entry.value.state == KeyVerificationState.error;
      37             :       if (!dispose) {
      38           4 :         dispose = !(await entry.value.verifyActivity());
      39             :       }
      40             :       if (dispose) {
      41           4 :         entry.value.dispose();
      42           4 :         entriesToDispose.add(entry.key);
      43             :       }
      44             :     }
      45          72 :     entriesToDispose.forEach(_requests.remove);
      46             :   }
      47             : 
      48           3 :   void addRequest(KeyVerification request) {
      49           3 :     if (request.transactionId == null) {
      50             :       return;
      51             :     }
      52           9 :     _requests[request.transactionId!] = request;
      53             :   }
      54             : 
      55           6 :   KeyVerification? getRequest(String requestId) => _requests[requestId];
      56             : 
      57           1 :   Future<void> handleToDeviceEvent(ToDeviceEvent event) async {
      58           2 :     if (!event.type.startsWith('m.key.verification.') ||
      59           3 :         client.verificationMethods.isEmpty) {
      60             :       return;
      61             :     }
      62             :     // we have key verification going on!
      63           2 :     final transactionId = KeyVerification.getTransactionId(event.content);
      64             :     if (transactionId == null) {
      65             :       return; // TODO: send cancel with unknown transaction id
      66             :     }
      67           2 :     final request = _requests[transactionId];
      68             :     if (request != null) {
      69             :       // make sure that new requests can't come from ourself
      70           3 :       if (!{EventTypes.KeyVerificationRequest}.contains(event.type)) {
      71           3 :         await request.handlePayload(event.type, event.content);
      72             :       }
      73             :     } else {
      74           2 :       if (!{EventTypes.KeyVerificationRequest, EventTypes.KeyVerificationStart}
      75           2 :           .contains(event.type)) {
      76             :         return; // we can only start on these
      77             :       }
      78             :       final newKeyRequest =
      79           3 :           KeyVerification(encryption: encryption, userId: event.sender);
      80           3 :       await newKeyRequest.handlePayload(event.type, event.content);
      81           2 :       if (newKeyRequest.state != KeyVerificationState.askAccept) {
      82             :         // okay, something went wrong (unknown transaction id?), just dispose it
      83           0 :         newKeyRequest.dispose();
      84             :       } else {
      85           2 :         _requests[transactionId] = newKeyRequest;
      86           3 :         client.onKeyVerificationRequest.add(newKeyRequest);
      87             :       }
      88             :     }
      89             :   }
      90             : 
      91           2 :   Future<void> handleEventUpdate(EventUpdate update) async {
      92           2 :     final event = update.content;
      93           4 :     final type = event['type'].startsWith('m.key.verification.')
      94           1 :         ? event['type']
      95           4 :         : event['content']['msgtype'];
      96             :     if (type == null ||
      97           2 :         !type.startsWith('m.key.verification.') ||
      98           6 :         client.verificationMethods.isEmpty) {
      99             :       return;
     100             :     }
     101           1 :     if (type == EventTypes.KeyVerificationRequest) {
     102           3 :       event['content']['timestamp'] = event['origin_server_ts'];
     103             :     }
     104             : 
     105             :     final transactionId =
     106           3 :         KeyVerification.getTransactionId(event['content']) ?? event['event_id'];
     107             : 
     108           2 :     final req = _requests[transactionId];
     109             :     if (req != null) {
     110           2 :       final otherDeviceId = event['content']['from_device'];
     111           4 :       if (event['sender'] != client.userID) {
     112           3 :         await req.handlePayload(type, event['content'], event['event_id']);
     113           4 :       } else if (event['sender'] == client.userID &&
     114             :           otherDeviceId != null &&
     115           3 :           otherDeviceId != client.deviceID) {
     116             :         // okay, another of our devices answered
     117           1 :         req.otherDeviceAccepted();
     118           1 :         req.dispose();
     119           2 :         _requests.remove(transactionId);
     120             :       }
     121           4 :     } else if (event['sender'] != client.userID) {
     122           2 :       if (!{EventTypes.KeyVerificationRequest, EventTypes.KeyVerificationStart}
     123           1 :           .contains(type)) {
     124             :         return; // we can only start on these
     125             :       }
     126           3 :       final room = client.getRoomById(update.roomID) ??
     127           3 :           Room(id: update.roomID, client: client);
     128           1 :       final newKeyRequest = KeyVerification(
     129           2 :           encryption: encryption, userId: event['sender'], room: room);
     130           1 :       await newKeyRequest.handlePayload(
     131           2 :           type, event['content'], event['event_id']);
     132           2 :       if (newKeyRequest.state != KeyVerificationState.askAccept) {
     133             :         // something went wrong, let's just dispose the request
     134           0 :         newKeyRequest.dispose();
     135             :       } else {
     136             :         // new request! Let's notify it and stuff
     137           2 :         _requests[transactionId] = newKeyRequest;
     138           3 :         client.onKeyVerificationRequest.add(newKeyRequest);
     139             :       }
     140             :     }
     141             :   }
     142             : 
     143          21 :   void dispose() {
     144          45 :     for (final req in _requests.values) {
     145           3 :       req.dispose();
     146             :     }
     147             :   }
     148             : }

Generated by: LCOV version 1.14