diff --git a/lib/encryption/encryption.dart b/lib/encryption/encryption.dart index cd2e05fd..7bf18b13 100644 --- a/lib/encryption/encryption.dart +++ b/lib/encryption/encryption.dart @@ -155,7 +155,7 @@ class Encryption { var canRequestSession = false; try { if (event.content['algorithm'] != AlgorithmTypes.megolmV1AesSha2) { - throw (DecryptError.UNKNOWN_ALGORITHM); + throw DecryptException(DecryptException.unknownAlgorithm); } final String sessionId = event.content['session_id']; final String senderKey = event.content['sender_key']; @@ -163,10 +163,11 @@ class Encryption { keyManager.getInboundGroupSession(roomId, sessionId, senderKey); if (inboundGroupSession == null) { canRequestSession = true; - throw (DecryptError.UNKNOWN_SESSION); + throw DecryptException(DecryptException.unknownSession); } // decrypt errors here may mean we have a bad session key - others might have a better one canRequestSession = true; + final decryptResult = inboundGroupSession.inboundGroupSession .decrypt(event.content['ciphertext']); canRequestSession = false; @@ -179,7 +180,7 @@ class Encryption { if (haveIndex && inboundGroupSession.indexes[messageIndexKey] != messageIndexValue) { Logs().e('[Decrypt] Could not decrypt due to a corrupted session.'); - throw (DecryptError.CHANNEL_CORRUPTED); + throw DecryptException(DecryptException.channelCorrupted); } inboundGroupSession.indexes[messageIndexKey] = messageIndexValue; if (!haveIndex) { @@ -197,7 +198,7 @@ class Encryption { } catch (exception) { // alright, if this was actually by our own outbound group session, we might as well clear it if (client.enableE2eeRecovery && - exception != DecryptError.UNKNOWN_SESSION && + exception.toString() != DecryptException.unknownSession && (keyManager .getOutboundGroupSession(roomId) ?.outboundGroupSession @@ -383,11 +384,30 @@ class Encryption { } } -abstract class DecryptError { - static const String NOT_ENABLED = 'Encryption is not enabled in your client.'; - static const String UNKNOWN_ALGORITHM = 'Unknown encryption algorithm.'; - static const String UNKNOWN_SESSION = +class DecryptException implements Exception { + String cause; + String libolmMessage; + DecryptException(this.cause, [this.libolmMessage]); + + @override + String toString() => cause; + + static const String notEnabled = 'Encryption is not enabled in your client.'; + static const String unknownAlgorithm = 'Unknown encryption algorithm.'; + static const String unknownSession = 'The sender has not sent us the session key.'; - static const String CHANNEL_CORRUPTED = + static const String channelCorrupted = 'The secure channel with the sender was corrupted.'; + static const String unableToDecryptWithAnyOlmSession = + 'Unable to decrypt with any existing OLM session'; + static const String senderDoesntMatch = + "Message was decrypted but sender doesn't match"; + static const String recipientDoesntMatch = + "Message was decrypted but recipient doesn't match"; + static const String ownFingerprintDoesntMatch = + "Message was decrypted but own fingerprint Key doesn't match"; + static const String isntSentForThisDevice = + "The message isn't sent for this device"; + static const String unknownMessageType = 'Unknown message type'; + static const String decryptionFailed = 'Decryption failed'; } diff --git a/lib/encryption/olm_manager.dart b/lib/encryption/olm_manager.dart index fefd8e7b..1c961ca9 100644 --- a/lib/encryption/olm_manager.dart +++ b/lib/encryption/olm_manager.dart @@ -248,17 +248,17 @@ class OlmManager { return event; } if (event.content['algorithm'] != AlgorithmTypes.olmV1Curve25519AesSha2) { - throw ('Unknown algorithm: ${event.content['algorithm']}'); + throw DecryptException(DecryptException.unknownAlgorithm); } if (!event.content['ciphertext'].containsKey(identityKey)) { - throw ("The message isn't sent for this device"); + throw DecryptException(DecryptException.isntSentForThisDevice); } String plaintext; final String senderKey = event.content['sender_key']; final String body = event.content['ciphertext'][identityKey]['body']; final int type = event.content['ciphertext'][identityKey]['type']; if (type != 0 && type != 1) { - throw ('Unknown message type'); + throw DecryptException(DecryptException.unknownMessageType); } final device = client.userDeviceKeys[event.sender]?.deviceKeys?.values ?.firstWhere((d) => d.curve25519Key == senderKey, orElse: () => null); @@ -280,7 +280,12 @@ class OlmManager { if (existingSessions != null) { for (var session in existingSessions) { if (type == 0 && session.session.matches_inbound(body) == true) { - plaintext = session.session.decrypt(type, body); + try { + plaintext = session.session.decrypt(type, body); + } catch (e) { + throw DecryptException( + DecryptException.decryptionFailed, e.toString()); + } updateSessionUsage(session); break; } else if (type == 1) { @@ -295,7 +300,7 @@ class OlmManager { } } if (plaintext == null && type != 0) { - throw ('Unable to decrypt with any existing OLM session'); + throw DecryptException(DecryptException.unableToDecryptWithAnyOlmSession); } if (plaintext == null) { @@ -313,24 +318,24 @@ class OlmManager { lastReceived: DateTime.now(), ))); updateSessionUsage(); - } catch (_) { + } catch (e) { newSession?.free(); - rethrow; + throw DecryptException(DecryptException.decryptionFailed, e.toString()); } } final Map plainContent = json.decode(plaintext); if (plainContent.containsKey('sender') && plainContent['sender'] != event.sender) { - throw ("Message was decrypted but sender doesn't match"); + throw DecryptException(DecryptException.senderDoesntMatch); } if (plainContent.containsKey('recipient') && plainContent['recipient'] != client.userID) { - throw ("Message was decrypted but recipient doesn't match"); + throw DecryptException(DecryptException.recipientDoesntMatch); } if (plainContent['recipient_keys'] is Map && plainContent['recipient_keys']['ed25519'] is String && plainContent['recipient_keys']['ed25519'] != fingerprintKey) { - throw ("Message was decrypted but own fingerprint Key doesn't match"); + throw DecryptException(DecryptException.ownFingerprintDoesntMatch); } return ToDeviceEvent( content: plainContent['content'], diff --git a/lib/src/utils/event_localizations.dart b/lib/src/utils/event_localizations.dart index 86551f32..0008bdd2 100644 --- a/lib/src/utils/event_localizations.dart +++ b/lib/src/utils/event_localizations.dart @@ -43,16 +43,16 @@ abstract class EventLocalizations { case MessageTypes.BadEncrypted: String errorText; switch (event.body) { - case DecryptError.CHANNEL_CORRUPTED: + case DecryptException.channelCorrupted: errorText = i18n.channelCorruptedDecryptError + '.'; break; - case DecryptError.NOT_ENABLED: + case DecryptException.notEnabled: errorText = i18n.encryptionNotEnabled + '.'; break; - case DecryptError.UNKNOWN_ALGORITHM: + case DecryptException.unknownAlgorithm: errorText = i18n.unknownEncryptionAlgorithm + '.'; break; - case DecryptError.UNKNOWN_SESSION: + case DecryptException.unknownSession: errorText = i18n.noPermission + '.'; break; default: diff --git a/test/event_test.dart b/test/event_test.dart index ccd98e41..2b133a24 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -302,7 +302,7 @@ void main() { 'status': 2, 'content': json.encode({ 'msgtype': 'm.bad.encrypted', - 'body': DecryptError.UNKNOWN_SESSION, + 'body': DecryptException.unknownSession, 'can_request_session': true, 'algorithm': AlgorithmTypes.megolmV1AesSha2, 'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...',