refactor: Olm Exceptions

This commit is contained in:
Christian Pauly 2021-01-07 12:34:17 +01:00
parent 33500012b2
commit 39b776716c
4 changed files with 49 additions and 24 deletions

View File

@ -155,7 +155,7 @@ class Encryption {
var canRequestSession = false; var canRequestSession = false;
try { try {
if (event.content['algorithm'] != AlgorithmTypes.megolmV1AesSha2) { if (event.content['algorithm'] != AlgorithmTypes.megolmV1AesSha2) {
throw (DecryptError.UNKNOWN_ALGORITHM); throw DecryptException(DecryptException.unknownAlgorithm);
} }
final String sessionId = event.content['session_id']; final String sessionId = event.content['session_id'];
final String senderKey = event.content['sender_key']; final String senderKey = event.content['sender_key'];
@ -163,10 +163,11 @@ class Encryption {
keyManager.getInboundGroupSession(roomId, sessionId, senderKey); keyManager.getInboundGroupSession(roomId, sessionId, senderKey);
if (inboundGroupSession == null) { if (inboundGroupSession == null) {
canRequestSession = true; 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 // decrypt errors here may mean we have a bad session key - others might have a better one
canRequestSession = true; canRequestSession = true;
final decryptResult = inboundGroupSession.inboundGroupSession final decryptResult = inboundGroupSession.inboundGroupSession
.decrypt(event.content['ciphertext']); .decrypt(event.content['ciphertext']);
canRequestSession = false; canRequestSession = false;
@ -179,7 +180,7 @@ class Encryption {
if (haveIndex && if (haveIndex &&
inboundGroupSession.indexes[messageIndexKey] != messageIndexValue) { inboundGroupSession.indexes[messageIndexKey] != messageIndexValue) {
Logs().e('[Decrypt] Could not decrypt due to a corrupted session.'); Logs().e('[Decrypt] Could not decrypt due to a corrupted session.');
throw (DecryptError.CHANNEL_CORRUPTED); throw DecryptException(DecryptException.channelCorrupted);
} }
inboundGroupSession.indexes[messageIndexKey] = messageIndexValue; inboundGroupSession.indexes[messageIndexKey] = messageIndexValue;
if (!haveIndex) { if (!haveIndex) {
@ -197,7 +198,7 @@ class Encryption {
} catch (exception) { } catch (exception) {
// alright, if this was actually by our own outbound group session, we might as well clear it // alright, if this was actually by our own outbound group session, we might as well clear it
if (client.enableE2eeRecovery && if (client.enableE2eeRecovery &&
exception != DecryptError.UNKNOWN_SESSION && exception.toString() != DecryptException.unknownSession &&
(keyManager (keyManager
.getOutboundGroupSession(roomId) .getOutboundGroupSession(roomId)
?.outboundGroupSession ?.outboundGroupSession
@ -383,11 +384,30 @@ class Encryption {
} }
} }
abstract class DecryptError { class DecryptException implements Exception {
static const String NOT_ENABLED = 'Encryption is not enabled in your client.'; String cause;
static const String UNKNOWN_ALGORITHM = 'Unknown encryption algorithm.'; String libolmMessage;
static const String UNKNOWN_SESSION = 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.'; '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.'; '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';
} }

View File

@ -248,17 +248,17 @@ class OlmManager {
return event; return event;
} }
if (event.content['algorithm'] != AlgorithmTypes.olmV1Curve25519AesSha2) { if (event.content['algorithm'] != AlgorithmTypes.olmV1Curve25519AesSha2) {
throw ('Unknown algorithm: ${event.content['algorithm']}'); throw DecryptException(DecryptException.unknownAlgorithm);
} }
if (!event.content['ciphertext'].containsKey(identityKey)) { if (!event.content['ciphertext'].containsKey(identityKey)) {
throw ("The message isn't sent for this device"); throw DecryptException(DecryptException.isntSentForThisDevice);
} }
String plaintext; String plaintext;
final String senderKey = event.content['sender_key']; final String senderKey = event.content['sender_key'];
final String body = event.content['ciphertext'][identityKey]['body']; final String body = event.content['ciphertext'][identityKey]['body'];
final int type = event.content['ciphertext'][identityKey]['type']; final int type = event.content['ciphertext'][identityKey]['type'];
if (type != 0 && type != 1) { if (type != 0 && type != 1) {
throw ('Unknown message type'); throw DecryptException(DecryptException.unknownMessageType);
} }
final device = client.userDeviceKeys[event.sender]?.deviceKeys?.values final device = client.userDeviceKeys[event.sender]?.deviceKeys?.values
?.firstWhere((d) => d.curve25519Key == senderKey, orElse: () => null); ?.firstWhere((d) => d.curve25519Key == senderKey, orElse: () => null);
@ -280,7 +280,12 @@ class OlmManager {
if (existingSessions != null) { if (existingSessions != null) {
for (var session in existingSessions) { for (var session in existingSessions) {
if (type == 0 && session.session.matches_inbound(body) == true) { 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); updateSessionUsage(session);
break; break;
} else if (type == 1) { } else if (type == 1) {
@ -295,7 +300,7 @@ class OlmManager {
} }
} }
if (plaintext == null && type != 0) { if (plaintext == null && type != 0) {
throw ('Unable to decrypt with any existing OLM session'); throw DecryptException(DecryptException.unableToDecryptWithAnyOlmSession);
} }
if (plaintext == null) { if (plaintext == null) {
@ -313,24 +318,24 @@ class OlmManager {
lastReceived: DateTime.now(), lastReceived: DateTime.now(),
))); )));
updateSessionUsage(); updateSessionUsage();
} catch (_) { } catch (e) {
newSession?.free(); newSession?.free();
rethrow; throw DecryptException(DecryptException.decryptionFailed, e.toString());
} }
} }
final Map<String, dynamic> plainContent = json.decode(plaintext); final Map<String, dynamic> plainContent = json.decode(plaintext);
if (plainContent.containsKey('sender') && if (plainContent.containsKey('sender') &&
plainContent['sender'] != event.sender) { plainContent['sender'] != event.sender) {
throw ("Message was decrypted but sender doesn't match"); throw DecryptException(DecryptException.senderDoesntMatch);
} }
if (plainContent.containsKey('recipient') && if (plainContent.containsKey('recipient') &&
plainContent['recipient'] != client.userID) { plainContent['recipient'] != client.userID) {
throw ("Message was decrypted but recipient doesn't match"); throw DecryptException(DecryptException.recipientDoesntMatch);
} }
if (plainContent['recipient_keys'] is Map && if (plainContent['recipient_keys'] is Map &&
plainContent['recipient_keys']['ed25519'] is String && plainContent['recipient_keys']['ed25519'] is String &&
plainContent['recipient_keys']['ed25519'] != fingerprintKey) { plainContent['recipient_keys']['ed25519'] != fingerprintKey) {
throw ("Message was decrypted but own fingerprint Key doesn't match"); throw DecryptException(DecryptException.ownFingerprintDoesntMatch);
} }
return ToDeviceEvent( return ToDeviceEvent(
content: plainContent['content'], content: plainContent['content'],

View File

@ -43,16 +43,16 @@ abstract class EventLocalizations {
case MessageTypes.BadEncrypted: case MessageTypes.BadEncrypted:
String errorText; String errorText;
switch (event.body) { switch (event.body) {
case DecryptError.CHANNEL_CORRUPTED: case DecryptException.channelCorrupted:
errorText = i18n.channelCorruptedDecryptError + '.'; errorText = i18n.channelCorruptedDecryptError + '.';
break; break;
case DecryptError.NOT_ENABLED: case DecryptException.notEnabled:
errorText = i18n.encryptionNotEnabled + '.'; errorText = i18n.encryptionNotEnabled + '.';
break; break;
case DecryptError.UNKNOWN_ALGORITHM: case DecryptException.unknownAlgorithm:
errorText = i18n.unknownEncryptionAlgorithm + '.'; errorText = i18n.unknownEncryptionAlgorithm + '.';
break; break;
case DecryptError.UNKNOWN_SESSION: case DecryptException.unknownSession:
errorText = i18n.noPermission + '.'; errorText = i18n.noPermission + '.';
break; break;
default: default:

View File

@ -302,7 +302,7 @@ void main() {
'status': 2, 'status': 2,
'content': json.encode({ 'content': json.encode({
'msgtype': 'm.bad.encrypted', 'msgtype': 'm.bad.encrypted',
'body': DecryptError.UNKNOWN_SESSION, 'body': DecryptException.unknownSession,
'can_request_session': true, 'can_request_session': true,
'algorithm': AlgorithmTypes.megolmV1AesSha2, 'algorithm': AlgorithmTypes.megolmV1AesSha2,
'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...', 'ciphertext': 'AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...',