From 28231936b1af54ebe54f8068d4d33e4ef8d0878c Mon Sep 17 00:00:00 2001 From: Christian Pauly Date: Fri, 8 Jul 2022 13:35:45 +0200 Subject: [PATCH] feat: Calc encryption health state and allow key sharing with unknown devices In order to allow key sharing with unknown devices (master key has been verified but this device is not signed by this master key) the user should at least be informed. This makes it possible to set in the client constructor whether the app should share keys with unverified devices or unknown devices. --- lib/src/client.dart | 3 +++ lib/src/room.dart | 25 +++++++++++++++++++++++++ lib/src/utils/device_keys_list.dart | 14 +++++++------- test/device_keys_list_test.dart | 7 ++++++- test/room_test.dart | 7 +++++++ 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/src/client.dart b/lib/src/client.dart index cc79c50c..cc1d8431 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -83,6 +83,8 @@ class Client extends MatrixApi { final bool mxidLocalPartFallback; + bool shareKeysWithUnverifiedDevices; + // For CommandsClientExtension final Map Function(CommandArgs)> commands = {}; final Filter syncFilter; @@ -167,6 +169,7 @@ class Client extends MatrixApi { Filter? syncFilter, this.sendTimelineEventTimeout = const Duration(minutes: 1), this.customImageResizer, + this.shareKeysWithUnverifiedDevices = true, }) : syncFilter = syncFilter ?? Filter( room: RoomFilter( diff --git a/lib/src/room.dart b/lib/src/room.dart index 45762257..6a6bfac0 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -852,6 +852,26 @@ class Room { return eventId; } + /// Calculates how secure the communication is. When all devices are blocked or + /// verified, then this returns [EncryptionHealthState.allVerified]. When at + /// least one device is not verified, then it returns + /// [EncryptionHealthState.unverifiedDevices]. Apps should display this health + /// state next to the input text field to inform the user about the current + /// encryption security level. + Future calcEncryptionHealthState() async { + final users = await requestParticipants(); + users.removeWhere((u) => + !{Membership.invite, Membership.join}.contains(u.membership) || + !client.userDeviceKeys.containsKey(u.id)); + + if (users.any((u) => + client.userDeviceKeys[u.id]!.verified != UserVerifiedStatus.verified)) { + return EncryptionHealthState.unverifiedDevices; + } + + return EncryptionHealthState.allVerified; + } + Future _sendContent( String type, Map content, { @@ -1977,3 +1997,8 @@ class Room { @override bool operator ==(dynamic other) => (other is Room && other.id == id); } + +enum EncryptionHealthState { + allVerified, + unverifiedDevices, +} diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index 9661e2cf..baaa298e 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -151,13 +151,13 @@ abstract class SignableKey extends MatrixSignableKey { bool get blocked => _blocked ?? false; set blocked(bool b) => _blocked = b; - bool get encryptToDevice => - !(blocked) && - identifier != null && - ed25519Key != null && - (client.userDeviceKeys[userId]?.masterKey?.verified ?? false - ? verified - : true); + bool get encryptToDevice { + if (blocked) return false; + + if (identifier == null || ed25519Key == null) return false; + + return client.shareKeysWithUnverifiedDevices || verified; + } void setDirectVerified(bool v) { _verified = v; diff --git a/test/device_keys_list_test.dart b/test/device_keys_list_test.dart index 80ac1638..a1bdc991 100644 --- a/test/device_keys_list_test.dart +++ b/test/device_keys_list_test.dart @@ -156,6 +156,11 @@ void main() { }, }, }, client); + expect(client.shareKeysWithUnverifiedDevices, true); + expect(key.encryptToDevice, true); + client.shareKeysWithUnverifiedDevices = false; + expect(key.encryptToDevice, false); + client.shareKeysWithUnverifiedDevices = true; final masterKey = client.userDeviceKeys[client.userID]!.masterKey!; masterKey.setDirectVerified(true); // we need to populate the ssss cache to be able to test signing easily @@ -175,7 +180,7 @@ void main() { expect( client.userDeviceKeys[client.userID]?.deviceKeys['UNSIGNEDDEVICE'] ?.encryptToDevice, - false); + true); expect(masterKey.verified, true); await masterKey.setBlocked(true); diff --git a/test/room_test.dart b/test/room_test.dart index 63d1f47e..b723a0e9 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -349,6 +349,13 @@ void main() { expect(user.room.id, '!localpart:server.abc'); }); + test('calcEncryptionHealthState', () async { + expect( + await room.calcEncryptionHealthState(), + EncryptionHealthState.unverifiedDevices, + ); + }); + test('getEventByID', () async { final event = await room.getEventById('1234'); expect(event?.eventId, '143273582443PhrSn:example.org');