From 1c838e3be8bdd7447b50e104af668d3ea900bab2 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 27 Sep 2021 12:17:35 +0200 Subject: [PATCH] fix: Cleanup nullsafe encryption a bit --- lib/encryption/encryption.dart | 17 ++--- lib/encryption/key_manager.dart | 83 ++++++++++------------ lib/encryption/olm_manager.dart | 19 ++--- lib/encryption/utils/bootstrap.dart | 33 ++++----- lib/encryption/utils/key_verification.dart | 24 +++---- lib/encryption/utils/session_key.dart | 2 +- lib/src/event.dart | 80 ++++++++++----------- lib/src/utils/device_keys_list.dart | 34 ++++----- pubspec.yaml | 2 +- 9 files changed, 132 insertions(+), 162 deletions(-) diff --git a/lib/encryption/encryption.dart b/lib/encryption/encryption.dart index bffb0fc2..95b1242e 100644 --- a/lib/encryption/encryption.dart +++ b/lib/encryption/encryption.dart @@ -149,8 +149,7 @@ class Encryption { runInRoot(() => keyVerificationManager.handleEventUpdate(update)); } if (update.content['sender'] == client.userID && - (!update.content.containsKey('unsigned') || - !update.content['unsigned'].containsKey('transaction_id'))) { + update.content['unsigned']?['transaction_id'] == null) { // maybe we need to re-try SSSS secrets // ignore: unawaited_futures runInRoot(() => ssss.periodicallyRequestMissingCache()); @@ -375,15 +374,13 @@ class Encryption { Future autovalidateMasterOwnKey() async { // check if we can set our own master key as verified, if it isn't yet + final masterKey = client.userDeviceKeys[client.userID]?.masterKey; if (client.database != null && - client.userDeviceKeys.containsKey(client.userID)) { - final masterKey = client.userDeviceKeys[client.userID]!.masterKey; - if (masterKey != null && - !masterKey.directVerified && - masterKey - .hasValidSignatureChain(onlyValidateUserIds: {client.userID})) { - await masterKey.setVerified(true); - } + masterKey != null && + !masterKey.directVerified && + masterKey + .hasValidSignatureChain(onlyValidateUserIds: {client.userID})) { + await masterKey.setVerified(true); } } diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index 1023d080..ec34662f 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -146,10 +146,10 @@ class KeyManager { newSession.dispose(); return; } - if (!_inboundGroupSessions.containsKey(roomId)) { - _inboundGroupSessions[roomId] = {}; - } - _inboundGroupSessions[roomId]![sessionId] = newSession; + + final roomInboundGroupSessions = + _inboundGroupSessions[roomId] ??= {}; + roomInboundGroupSessions[sessionId] = newSession; if (!client.isLogged() || client.encryption == null) { return; } @@ -189,9 +189,8 @@ class KeyManager { SessionKey? getInboundGroupSession( String roomId, String sessionId, String senderKey, {bool otherRooms = true}) { - if (_inboundGroupSessions.containsKey(roomId) && - _inboundGroupSessions[roomId]!.containsKey(sessionId)) { - final sess = _inboundGroupSessions[roomId]![sessionId]!; + final sess = _inboundGroupSessions[roomId]?[sessionId]; + if (sess != null) { if (sess.senderKey != senderKey && sess.senderKey.isNotEmpty) { return null; } @@ -202,8 +201,8 @@ class KeyManager { } // search if this session id is *somehow* found in another room for (final val in _inboundGroupSessions.values) { - if (val.containsKey(sessionId)) { - final sess = val[sessionId]!; + final sess = val[sessionId]; + if (sess != null) { if (sess.senderKey != senderKey && sess.senderKey.isNotEmpty) { return null; } @@ -231,9 +230,8 @@ class KeyManager { /// Loads an inbound group session Future loadInboundGroupSession( String roomId, String sessionId, String senderKey) async { - if (_inboundGroupSessions.containsKey(roomId) && - _inboundGroupSessions[roomId]!.containsKey(sessionId)) { - final sess = _inboundGroupSessions[roomId]![sessionId]!; + final sess = _inboundGroupSessions[roomId]?[sessionId]; + if (sess != null) { if (sess.senderKey != senderKey && sess.senderKey.isNotEmpty) { return null; // sender keys do not match....better not do anything } @@ -244,16 +242,15 @@ class KeyManager { if (session == null) { return null; } - final sess = SessionKey.fromDb(session, client.userID); - if (!_inboundGroupSessions.containsKey(roomId)) { - _inboundGroupSessions[roomId] = {}; - } - if (!sess.isValid || - sess.senderKey.isEmpty || - sess.senderKey != senderKey) { + final dbSess = SessionKey.fromDb(session, client.userID); + final roomInboundGroupSessions = + _inboundGroupSessions[roomId] ??= {}; + if (!dbSess.isValid || + dbSess.senderKey.isEmpty || + dbSess.senderKey != senderKey) { return null; } - _inboundGroupSessions[roomId]![sessionId] = sess; + roomInboundGroupSessions[sessionId] = dbSess; return sess; } @@ -261,14 +258,13 @@ class KeyManager { List deviceKeys) { final deviceKeyIds = >{}; for (final device in deviceKeys) { - if (device.deviceId == null) { + final deviceId = device.deviceId; + if (deviceId == null) { Logs().w('[KeyManager] ignoring device without deviceid'); continue; } - if (!deviceKeyIds.containsKey(device.userId)) { - deviceKeyIds[device.userId] = {}; - } - deviceKeyIds[device.userId]![device.deviceId!] = !device.encryptToDevice; + final userDeviceKeyIds = deviceKeyIds[device.userId] ??= {}; + userDeviceKeyIds[deviceId] = !device.encryptToDevice; } return deviceKeyIds; } @@ -440,8 +436,9 @@ class KeyManager { /// Creates an outbound group session for a given room id Future createOutboundGroupSession(String roomId) async { - if (_pendingNewOutboundGroupSessions.containsKey(roomId)) { - return _pendingNewOutboundGroupSessions[roomId]!; + final sess = _pendingNewOutboundGroupSessions[roomId]; + if (sess != null) { + return sess; } _pendingNewOutboundGroupSessions[roomId] = _createOutboundGroupSession(roomId); @@ -791,14 +788,12 @@ class KeyManager { Logs().i('[KeyManager] No body, doing nothing'); return; // no body } - if (!client.userDeviceKeys.containsKey(event.sender) || - !client.userDeviceKeys[event.sender]!.deviceKeys - .containsKey(event.content['requesting_device_id'])) { + final device = client.userDeviceKeys[event.sender] + ?.deviceKeys[event.content['requesting_device_id']]; + if (device == null) { Logs().i('[KeyManager] Device not found, doing nothing'); return; // device not found } - final device = client.userDeviceKeys[event.sender]! - .deviceKeys[event.content['requesting_device_id']]!; if (device.userId == client.userID && device.deviceId == client.deviceID) { Logs().i('[KeyManager] Request is by ourself, ignoring'); @@ -914,10 +909,8 @@ class KeyManager { }; final data = >>{}; for (final device in request.devices) { - if (!data.containsKey(device.userId)) { - data[device.userId] = {}; - } - data[device.userId]![device.deviceId!] = sendToDeviceMessage; + final userData = data[device.userId] ??= {}; + userData[device.deviceId!] = sendToDeviceMessage; } await client.sendToDevice( EventTypes.RoomKeyRequest, @@ -933,13 +926,10 @@ class KeyManager { } final String roomId = event.content['room_id']; final String sessionId = event.content['session_id']; - if (client.userDeviceKeys.containsKey(event.sender) && - client.userDeviceKeys[event.sender]!.deviceKeys - .containsKey(event.content['requesting_device_id'])) { - event.content['sender_claimed_ed25519_key'] = client - .userDeviceKeys[event.sender]! - .deviceKeys[event.content['requesting_device_id']]! - .ed25519Key; + final sender_ed25519 = client.userDeviceKeys[event.sender] + ?.deviceKeys[event.content['requesting_device_id']]?.ed25519Key; + if (sender_ed25519 != null) { + event.content['sender_claimed_ed25519_key'] = sender_ed25519; } Logs().v('[KeyManager] Keeping room key'); setInboundGroupSession(roomId, sessionId, @@ -1044,9 +1034,8 @@ RoomKeys _generateUploadKeys(_GenerateUploadKeysArgs args) { continue; } // create the room if it doesn't exist - if (!roomKeys.rooms.containsKey(sess.roomId)) { - roomKeys.rooms[sess.roomId] = RoomKeyBackup(sessions: {}); - } + final roomKeyBackup = + roomKeys.rooms[sess.roomId] ??= RoomKeyBackup(sessions: {}); // generate the encrypted content final payload = { 'algorithm': AlgorithmTypes.megolmV1AesSha2, @@ -1061,7 +1050,7 @@ RoomKeys _generateUploadKeys(_GenerateUploadKeysArgs args) { // fetch the device, if available... //final device = args.client.getUserDeviceKeysByCurve25519Key(sess.senderKey); // aaaand finally add the session key to our payload - roomKeys.rooms[sess.roomId]!.sessions[sess.sessionId] = KeyBackupData( + roomKeyBackup.sessions[sess.sessionId] = KeyBackupData( firstMessageIndex: sess.inboundGroupSession!.first_known_index(), forwardedCount: sess.forwardingCurve25519KeyChain.length, isVerified: dbSession.verified, //device?.verified ?? false, diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index a417b51c..36a479ba 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -411,12 +411,10 @@ class OlmManager { } } final Map plainContent = json.decode(plaintext); - if (plainContent.containsKey('sender') && - plainContent['sender'] != event.sender) { + if (plainContent['sender'] != event.sender) { throw DecryptException(DecryptException.senderDoesntMatch); } - if (plainContent.containsKey('recipient') && - plainContent['recipient'] != client.userID) { + if (plainContent['recipient'] != client.userID) { throw DecryptException(DecryptException.recipientDoesntMatch); } if (plainContent['recipient_keys'] is Map && @@ -637,19 +635,16 @@ class OlmManager { } final deviceKeysWithoutSession = List.from(deviceKeys); deviceKeysWithoutSession.removeWhere((DeviceKeys deviceKeys) => - olmSessions.containsKey(deviceKeys.curve25519Key) && - olmSessions[deviceKeys.curve25519Key]!.isNotEmpty); + olmSessions[deviceKeys.curve25519Key]?.isNotEmpty ?? false); if (deviceKeysWithoutSession.isNotEmpty) { await startOutgoingOlmSessions(deviceKeysWithoutSession); } for (final device in deviceKeys) { - if (!data.containsKey(device.userId)) { - data[device.userId] = {}; - } + final userData = data[device.userId] ??= {}; try { - data[device.userId]![device.deviceId!] = - await encryptToDeviceMessagePayload(device, type, payload, - getFromDb: false); + userData[device.deviceId!] = await encryptToDeviceMessagePayload( + device, type, payload, + getFromDb: false); } catch (e, s) { Logs().w('[LibOlm] Error encrypting to-device event', e, s); continue; diff --git a/lib/encryption/utils/bootstrap.dart b/lib/encryption/utils/bootstrap.dart index 3b3f6da9..9566ad62 100644 --- a/lib/encryption/utils/bootstrap.dart +++ b/lib/encryption/utils/bootstrap.dart @@ -90,10 +90,11 @@ class Bootstrap { // cache the secret analyzing so that we don't drop stuff a different client sets during bootstrapping Map>? _secretsCache; Map> analyzeSecrets() { - if (_secretsCache != null) { + final secretsCache = _secretsCache; + if (secretsCache != null) { // deep-copy so that we can do modifications final newSecrets = >{}; - for (final s in _secretsCache!.entries) { + for (final s in secretsCache.entries) { newSecrets[s.key] = Set.from(s.value); } return newSecrets; @@ -147,11 +148,7 @@ class Bootstrap { final usage = {}; for (final keys in secrets.values) { for (final key in keys) { - if (!usage.containsKey(key)) { - usage[key] = 1; - } else { - usage[key] = usage[key]! + 1; - } + usage.update(key, (i) => i++, ifAbsent: () => 1); } } final entriesList = usage.entries.toList(); @@ -315,14 +312,15 @@ class Bootstrap { } Future openExistingSsss() async { - if (state != BootstrapState.openExistingSsss || newSsssKey == null) { + final newSsssKey_ = newSsssKey; + if (state != BootstrapState.openExistingSsss || newSsssKey_ == null) { throw BootstrapBadStateException(); } - if (!newSsssKey!.isUnlocked) { + if (!newSsssKey_.isUnlocked) { throw BootstrapBadStateException('Key not unlocked'); } Logs().v('Maybe cache all...'); - await newSsssKey!.maybeCacheAll(); + await newSsssKey_.maybeCacheAll(); checkCrossSigning(); } @@ -477,12 +475,9 @@ class Bootstrap { futures.add( client.onSync.stream .firstWhere((syncUpdate) => - client.userDeviceKeys.containsKey(client.userID) && - client.userDeviceKeys[client.userID]!.masterKey != null && - client.userDeviceKeys[client.userID]!.masterKey!.ed25519Key != - null && - client.userDeviceKeys[client.userID]!.masterKey!.ed25519Key == - masterKey!.publicKey) + masterKey?.publicKey != null && + client.userDeviceKeys[client.userID]?.masterKey?.ed25519Key == + masterKey?.publicKey) .then((_) => Logs().v('New Master Key was created')), ); } @@ -595,8 +590,10 @@ class Bootstrap { if (state != BootstrapState.error) { _state = newState; } - if (onUpdate != null) { - onUpdate!(); + + final onUpdate_ = onUpdate; + if (onUpdate_ != null) { + onUpdate_(); } } } diff --git a/lib/encryption/utils/key_verification.dart b/lib/encryption/utils/key_verification.dart index 91317a08..57f5f1f8 100644 --- a/lib/encryption/utils/key_verification.dart +++ b/lib/encryption/utils/key_verification.dart @@ -227,10 +227,9 @@ class KeyVerification { if (deviceId == '*') { _deviceId = payload['from_device']; // gotta set the real device id // and broadcast the cancel to the other devices - final devices = client.userDeviceKeys.containsKey(userId) - ? List.from( - client.userDeviceKeys[userId]!.deviceKeys.values) - : List.from([]); + final devices = List.from( + client.userDeviceKeys[userId]?.deviceKeys.values ?? + Iterable.empty()); devices.removeWhere( (d) => {deviceId, client.deviceID}.contains(d.deviceId)); final cancelPayload = { @@ -472,7 +471,8 @@ class KeyVerification { Future Function(String, SignableKey) verifier) async { _verifiedDevices = []; - if (!client.userDeviceKeys.containsKey(userId)) { + final userDeviceKey = client.userDeviceKeys[userId]; + if (userDeviceKey == null) { await cancel('m.key_mismatch'); return; } @@ -480,7 +480,7 @@ class KeyVerification { final keyId = entry.key; final verifyDeviceId = keyId.substring('ed25519:'.length); final keyInfo = entry.value; - final key = client.userDeviceKeys[userId]!.getKey(verifyDeviceId); + final key = userDeviceKey.getKey(verifyDeviceId); if (key != null) { if (!(await verifier(keyInfo, key))) { await cancel('m.key_mismatch'); @@ -619,8 +619,10 @@ class KeyVerification { if (state != KeyVerificationState.error) { state = newState; } + + final onUpdate = this.onUpdate; if (onUpdate != null) { - onUpdate!(); + onUpdate(); } } } @@ -808,8 +810,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } Future _sendAccept() async { - sas = olm.SAS(); - commitment = _makeCommitment(sas!.get_pubkey(), startCanonicalJson); + final sas = this.sas ??= olm.SAS(); + commitment = _makeCommitment(sas.get_pubkey(), startCanonicalJson); await request.send(EventTypes.KeyVerificationAccept, { 'method': type, 'key_agreement_protocol': keyAgreementProtocol, @@ -906,9 +908,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { _calculateMac(encryption.fingerprintKey!, baseInfo + deviceKeyId); keyList.add(deviceKeyId); - final masterKey = client.userDeviceKeys.containsKey(client.userID) - ? client.userDeviceKeys[client.userID]!.masterKey - : null; + final masterKey = client.userDeviceKeys[client.userID]?.masterKey; if (masterKey != null && masterKey.verified) { // we have our own master key verified, let's send it! final masterKeyId = 'ed25519:${masterKey.publicKey}'; diff --git a/lib/encryption/utils/session_key.dart b/lib/encryption/utils/session_key.dart index 752d0e51..7babdade 100644 --- a/lib/encryption/utils/session_key.dart +++ b/lib/encryption/utils/session_key.dart @@ -49,7 +49,7 @@ class SessionKey { late Map senderClaimedKeys; /// Sender curve25519 key - late String senderKey; + String senderKey; /// Is this session valid? bool get isValid => inboundGroupSession != null; diff --git a/lib/src/event.dart b/lib/src/event.dart index f964e570..ee0a030b 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -98,10 +98,10 @@ class Event extends MatrixEvent { this.roomId = roomId ?? room?.id; this.senderId = senderId; this.unsigned = unsigned; - // synapse unfortunatley isn't following the spec and tosses the prev_content - // into the unsigned block. - // Currently we are facing a very strange bug in web which is impossible to debug. - // It may be because of this line so we put this in try-catch until we can fix it. +// synapse unfortunatley isn't following the spec and tosses the prev_content +// into the unsigned block. +// Currently we are facing a very strange bug in web which is impossible to debug. +// It may be because of this line so we put this in try-catch until we can fix it. try { this.prevContent = (prevContent != null && prevContent.isNotEmpty) ? prevContent @@ -111,21 +111,21 @@ class Event extends MatrixEvent { ? unsigned['prev_content'] : null; } catch (_) { - // A strange bug in dart web makes this crash +// A strange bug in dart web makes this crash } this.stateKey = stateKey; this.originServerTs = originServerTs; - // Mark event as failed to send if status is `sending` and event is older - // than the timeout. This should not happen with the deprecated Moor - // database! +// Mark event as failed to send if status is `sending` and event is older +// than the timeout. This should not happen with the deprecated Moor +// database! if (status == 0 && room?.client?.database != null) { - // Age of this event in milliseconds +// Age of this event in milliseconds final age = DateTime.now().millisecondsSinceEpoch - originServerTs.millisecondsSinceEpoch; if (age > room.client.sendMessageTimeoutSeconds * 1000) { - // Update this event in database and open timelines +// Update this event in database and open timelines final json = toJson(); json['unsigned'] ??= {}; json['unsigned'][messageSendingStatusKey] = -1; @@ -324,8 +324,8 @@ class Event extends MatrixEvent { /// Try to send this event again. Only works with events of status -1. Future sendAgain({String txid}) async { if (status != -1) return null; - // we do not remove the event here. It will automatically be updated - // in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2 +// we do not remove the event here. It will automatically be updated +// in the `sendEvent` method to transition -1 -> 0 -> 1 -> 2 final newEventId = await room.sendEvent( content, txid: txid ?? unsigned['transaction_id'] ?? eventId, @@ -420,7 +420,7 @@ class Event extends MatrixEvent { return getThumbnail ? thumbnailMxcUrl : attachmentMxcUrl; } - // size determined from an approximate 800x800 jpeg thumbnail with method=scale +// size determined from an approximate 800x800 jpeg thumbnail with method=scale static const _minNoThumbSize = 80 * 1024; /// Gets the attachment https URL to display in the timeline, taking into account if the original image is tiny. @@ -449,14 +449,14 @@ class Event extends MatrixEvent { final thisInfoMap = useThumbnailMxcUrl ? thumbnailInfoMap : infoMap; final thisMxcUrl = useThumbnailMxcUrl ? infoMap['thumbnail_url'] : content['url']; - // if we have as method scale, we can return safely the original image, should it be small enough +// if we have as method scale, we can return safely the original image, should it be small enough if (getThumbnail && method == ThumbnailMethod.scale && thisInfoMap['size'] is int && thisInfoMap['size'] < minNoThumbSize) { getThumbnail = false; } - // now generate the actual URLs +// now generate the actual URLs if (getThumbnail) { return Uri.parse(thisMxcUrl).getThumbnail( room.client, @@ -480,7 +480,7 @@ class Event extends MatrixEvent { throw "This event hasn't any attachment or thumbnail."; } getThumbnail = mxcUrl != attachmentMxcUrl; - // Is this file storeable? +// Is this file storeable? final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap; final storeable = room.client.database != null && thisInfoMap['size'] is int && @@ -517,7 +517,7 @@ class Event extends MatrixEvent { Uint8List uint8list; - // Is this file storeable? +// Is this file storeable? final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap; var storeable = room.client.database != null && thisInfoMap['size'] is int && @@ -527,7 +527,7 @@ class Event extends MatrixEvent { uint8list = await room.client.database.getFile(mxcUrl); } - // Download the file +// Download the file if (uint8list == null) { downloadCallback ??= (Uri url) async { return (await http.get(url)).bodyBytes; @@ -541,7 +541,7 @@ class Event extends MatrixEvent { } } - // Decrypt the file +// Decrypt the file if (isEncrypted) { final fileMap = getThumbnail ? infoMap['thumbnail_file'] : content['file']; @@ -579,10 +579,10 @@ class Event extends MatrixEvent { } var body = plaintextBody ? this.plaintextBody : this.body; - // we need to know if the message is an html message to be able to determine - // if we need to strip the reply fallback. +// we need to know if the message is an html message to be able to determine +// if we need to strip the reply fallback. var htmlMessage = content['format'] != 'org.matrix.custom.html'; - // If we have an edit, we want to operate on the new content +// If we have an edit, we want to operate on the new content if (hideEdit && relationshipType == RelationshipTypes.edit && content.tryGet>('m.new_content') != null) { @@ -600,9 +600,9 @@ class Event extends MatrixEvent { body; } } - // Hide reply fallback - // Be sure that the plaintextBody already stripped teh reply fallback, - // if the message is formatted +// Hide reply fallback +// Be sure that the plaintextBody already stripped teh reply fallback, +// if the message is formatted if (hideReply && (!plaintextBody || htmlMessage)) { body = body.replaceFirst( RegExp(r'^>( \*)? <[^>]+>[^\n\r]+\r?\n(> [^\n]*\r?\n)*\r?\n'), ''); @@ -613,7 +613,7 @@ class Event extends MatrixEvent { localizedBody = callback(this, i18n, body); } - // Add the sender name prefix +// Add the sender name prefix if (withSenderNamePrefix && type == EventTypes.Message && textOnlyMessageTypes.contains(messageType)) { @@ -691,13 +691,13 @@ class Event extends MatrixEvent { return this; } if (hasAggregatedEvents(timeline, RelationshipTypes.edit)) { - // alright, we have an edit +// alright, we have an edit final allEditEvents = aggregatedEvents(timeline, RelationshipTypes.edit) // we only allow edits made by the original author themself .where((e) => e.senderId == senderId && e.type == EventTypes.Message) .toList(); - // we need to check again if it isn't empty, as we potentially removed all - // aggregated edits +// we need to check again if it isn't empty, as we potentially removed all +// aggregated edits if (allEditEvents.isNotEmpty) { allEditEvents.sort((a, b) => a.originServerTs.millisecondsSinceEpoch - b.originServerTs.millisecondsSinceEpoch > @@ -705,7 +705,7 @@ class Event extends MatrixEvent { ? 1 : -1); final rawEvent = allEditEvents.last.toJson(); - // update the content of the new event to render +// update the content of the new event to render if (rawEvent['content']['m.new_content'] is Map) { rawEvent['content'] = rawEvent['content']['m.new_content']; } @@ -720,16 +720,16 @@ class Event extends MatrixEvent { content['format'] == 'org.matrix.custom.html' && content['formatted_body'] is String; - // regexes to fetch the number of emotes, including emoji, and if the message consists of only those - // to match an emoji we can use the following regex: - // (?:\x{00a9}|\x{00ae}|[\x{2600}-\x{27bf}]|[\x{2b00}-\x{2bff}]|\x{d83c}[\x{d000}-\x{dfff}]|\x{d83d}[\x{d000}-\x{dfff}]|\x{d83e}[\x{d000}-\x{dfff}])[\x{fe00}-\x{fe0f}]? - // we need to replace \x{0000} with \u0000, the comment is left in the other format to be able to paste into regex101.com - // to see if there is a custom emote, we use the following regex: ]+data-mx-(?:emote|emoticon)(?==|>|\s)[^>]*> - // now we combind the two to have four regexes: - // 1. are there only emoji, or whitespace - // 2. are there only emoji, emotes, or whitespace - // 3. count number of emoji - // 4- count number of emoji or emotes +// regexes to fetch the number of emotes, including emoji, and if the message consists of only those +// to match an emoji we can use the following regex: +// (?:\x{00a9}|\x{00ae}|[\x{2600}-\x{27bf}]|[\x{2b00}-\x{2bff}]|\x{d83c}[\x{d000}-\x{dfff}]|\x{d83d}[\x{d000}-\x{dfff}]|\x{d83e}[\x{d000}-\x{dfff}])[\x{fe00}-\x{fe0f}]? +// we need to replace \x{0000} with \u0000, the comment is left in the other format to be able to paste into regex101.com +// to see if there is a custom emote, we use the following regex: ]+data-mx-(?:emote|emoticon)(?==|>|\s)[^>]*> +// now we combind the two to have four regexes: +// 1. are there only emoji, or whitespace +// 2. are there only emoji, emotes, or whitespace +// 3. count number of emoji +// 4- count number of emoji or emotes static final RegExp _onlyEmojiRegex = RegExp( r'^((?:\u00a9|\u00ae|[\u2600-\u27bf]|[\u2b00-\u2bff]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])[\ufe00-\ufe0f]?|\s)*$', caseSensitive: false, diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index b0212ca1..495936d1 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -249,14 +249,9 @@ abstract class SignableKey extends MatrixSignableKey { if (otherUserId == userId && keyId == identifier) { continue; } - SignableKey? key; - if (client.userDeviceKeys[otherUserId]!.deviceKeys.containsKey(keyId)) { - key = client.userDeviceKeys[otherUserId]!.deviceKeys[keyId]; - } else if (client.userDeviceKeys[otherUserId]!.crossSigningKeys - .containsKey(keyId)) { - key = client.userDeviceKeys[otherUserId]!.crossSigningKeys[keyId]; - } + final key = client.userDeviceKeys[otherUserId]?.deviceKeys[keyId] ?? + client.userDeviceKeys[otherUserId]?.crossSigningKeys[keyId]; if (key == null) { continue; } @@ -272,25 +267,22 @@ abstract class SignableKey extends MatrixSignableKey { } var haveValidSignature = false; var gotSignatureFromCache = false; - if (validSignatures != null && - validSignatures!.containsKey(otherUserId) && - validSignatures![otherUserId].containsKey(fullKeyId)) { - if (validSignatures![otherUserId][fullKeyId] == true) { - haveValidSignature = true; - gotSignatureFromCache = true; - } else if (validSignatures![otherUserId][fullKeyId] == false) { - haveValidSignature = false; - gotSignatureFromCache = true; - } + if (validSignatures?[otherUserId][fullKeyId] == true) { + haveValidSignature = true; + gotSignatureFromCache = true; + } else if (validSignatures?[otherUserId][fullKeyId] == false) { + haveValidSignature = false; + gotSignatureFromCache = true; } + if (!gotSignatureFromCache && key.ed25519Key != null) { // validate the signature manually haveValidSignature = _verifySignature(key.ed25519Key!, signature); - validSignatures ??= {}; - if (!validSignatures!.containsKey(otherUserId)) { - validSignatures![otherUserId] = {}; + final validSignatures = this.validSignatures ??= {}; + if (!validSignatures.containsKey(otherUserId)) { + validSignatures[otherUserId] = {}; } - validSignatures![otherUserId][fullKeyId] = haveValidSignature; + validSignatures[otherUserId][fullKeyId] = haveValidSignature; } if (!haveValidSignature) { // no valid signature, this key is useless diff --git a/pubspec.yaml b/pubspec.yaml index 998892f8..a1c83749 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: js: ^0.6.3 slugify: ^2.0.0 html: ^0.15.0 - collection: ^1.15.0-nullsafety.4 + collection: ^1.15.0 dev_dependencies: pedantic: ^1.11.0