Skip to content

Commit

Permalink
Merge pull request #1091 from matrix-org/manu/async_decryption
Browse files Browse the repository at this point in the history
MXCrypto: Move decryptions out of the main thread
  • Loading branch information
manuroe committed May 20, 2021
2 parents e68e309 + d8ed7e2 commit 026cb56
Show file tree
Hide file tree
Showing 24 changed files with 1,208 additions and 969 deletions.
12 changes: 10 additions & 2 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@ Changes to be released in next version
🙌 Improvements
* MXSession: Cache initial sync response until it is fully handled (vector-im/element-ios/issues/4317).
* MXStore: New commit method accepting a completion block.
* MXCrypto: Decrypt events asynchronously and no more on the main thread )(vector-im/element-ios/issues/4306).
* MXSession: Add the decryptEvents method to decypt a bunch of events asynchronously.
* MXSession: Make the eventWithEventId method decrypt the event if needed.
* MXEventTimeline: Add NSCopying implementation so that another pagination can be done on the same set of data.
* MXCrypto: eventDeviceInfo: Do not synchronise anymore the operation with the decryption queue.

🐛 Bugfix
*
* MXRoomSummary: Fix decryption of the last message when it is edited (vector-im/element-ios/issues/4322).

⚠️ API Changes
*
* MXRoom: MXRoom.outgoingMessages does not decrypt messages anymore. Use MXSession.decryptEvents to get decrypted events.
* MXSession: [MXSession decryptEvent:inTimeline:] is deprecated, use [MXSession decryptEvents:inTimeline:onComplete:] instead.
* MXCrypto: [MXCrypto decryptEvent:inTimeline:] is deprecated, use [MXCrypto decryptEvents:inTimeline:onComplete:] instead.
* MXCrypto: [MXCrypto hasKeysToDecryptEvent:] is now asynchronous.

🗣 Translations
*
Expand Down
18 changes: 0 additions & 18 deletions MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,6 @@ - (MXHTTPOperation*)replaceTextMessageEvent:(MXEvent*)event
failure(nil);
return nil;
}

// If it is not already done, decrypt the event to build the new content
if (event.isEncrypted && !event.clearEvent)
{
if (![self.mxSession decryptEvent:event inTimeline:nil])
{
NSLog(@"[MXAggregations] replaceTextMessageEvent: Fail to decrypt original event: %@", event.eventId);
failure(nil);
return nil;
}
}

NSString *messageType = event.content[@"msgtype"];

Expand Down Expand Up @@ -243,13 +232,6 @@ - (void)handleReplace:(MXEvent *)replaceEvent
if (editedEvent)
{
[self.matrixStore replaceEvent:editedEvent inRoom:roomId];

if (editedEvent.isEncrypted && !editedEvent.clearEvent)
{
[self.mxSession decryptEvent:editedEvent inTimeline:nil];
}


[self notifyEventEditsListenersOfRoom:roomId replaceEvent:replaceEvent];
}
}
Expand Down
5 changes: 0 additions & 5 deletions MatrixSDK/Aggregations/MXAggregatedReferencesUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,6 @@ - (void)handleReference:(MXEvent *)referenceEvent
{
[self.matrixStore replaceEvent:newEvent inRoom:roomId];

if (newEvent.isEncrypted && !newEvent.clearEvent)
{
[self.mxSession decryptEvent:newEvent inTimeline:nil];
}

// TODO or not?
//[self notifyEventEditsListenersOfRoom:roomId replaceEvent:replaceEvent];
}
Expand Down
14 changes: 12 additions & 2 deletions MatrixSDK/Aggregations/MXAggregations.m
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,25 @@ - (MXHTTPOperation*)referenceEventsForEvent:(NSString*)eventId
}
}

NSMutableArray *eventsToDecrypt = [NSMutableArray array];
for (MXEvent *event in allEvents)
{
if (event.isEncrypted && !event.clearEvent)
{
[self.mxSession decryptEvent:event inTimeline:nil];
[eventsToDecrypt addObject:event];
}
}

success(paginatedResponse);
if (eventsToDecrypt.count)
{
[self.mxSession decryptEvents:eventsToDecrypt inTimeline:nil onComplete:^(NSArray<MXEvent *> *failedEvents) {
success(paginatedResponse);
}];
}
else
{
success(paginatedResponse);
}
};

MXEvent *event = [self.mxSession.store eventWithEventId:eventId inRoom:roomId];
Expand Down
10 changes: 3 additions & 7 deletions MatrixSDK/Crypto/Algorithms/MXDecrypting.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,13 @@
/**
Decrypt a message.
In case of success, the event is updated with clear data.
In case of failure, event.decryptionError contains the error.
@param event the raw event.
@param timeline the id of the timeline where the event is decrypted. It is used
to prevent replay attack.
@param error the result error if there is a problem decrypting the event.
to prevent replay attack. Can be nil.
@return The decryption result. Nil if it failed.
@return The decryption result.
*/
- (MXEventDecryptionResult *)decryptEvent:(MXEvent*)event inTimeline:(NSString*)timeline error:(NSError** )error;
- (MXEventDecryptionResult *)decryptEvent:(MXEvent*)event inTimeline:(NSString*)timeline;

/**
* Handle a key event.
Expand Down
6 changes: 6 additions & 0 deletions MatrixSDK/Crypto/Algorithms/MXEventDecryptionResult.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2021 The Matrix.org Foundation C.I.C
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -44,4 +45,9 @@
*/
@property NSArray<NSString *> *forwardingCurve25519KeyChain;

/**
If any, the error that occured during decryption.
*/
@property (nonatomic) NSError *error;

@end
56 changes: 23 additions & 33 deletions MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ - (BOOL)hasKeysToDecryptEvent:(MXEvent *)event

NSString *senderKey, *sessionId;

MXJSONModelSetString(senderKey, event.content[@"sender_key"]);
MXJSONModelSetString(sessionId, event.content[@"session_id"]);
MXJSONModelSetString(senderKey, event.wireContent[@"sender_key"]);
MXJSONModelSetString(sessionId, event.wireContent[@"session_id"]);
if (senderKey && sessionId)
{
hasKeys = ([crypto.store inboundGroupSessionWithId:sessionId andSenderKey:senderKey] != nil);
Expand All @@ -78,35 +78,32 @@ - (BOOL)hasKeysToDecryptEvent:(MXEvent *)event
return hasKeys;
}

- (MXEventDecryptionResult *)decryptEvent:(MXEvent*)event inTimeline:(NSString*)timeline error:(NSError** )error;
- (MXEventDecryptionResult *)decryptEvent:(MXEvent*)event inTimeline:(NSString*)timeline
{
MXEventDecryptionResult *result;
NSString *senderKey, *ciphertext, *sessionId;

MXJSONModelSetString(senderKey, event.content[@"sender_key"]);
MXJSONModelSetString(ciphertext, event.content[@"ciphertext"]);
MXJSONModelSetString(sessionId, event.content[@"session_id"]);
MXJSONModelSetString(senderKey, event.wireContent[@"sender_key"]);
MXJSONModelSetString(ciphertext, event.wireContent[@"ciphertext"]);
MXJSONModelSetString(sessionId, event.wireContent[@"session_id"]);

if (!senderKey || !sessionId || !ciphertext)
{
if (error)
{
*error = [NSError errorWithDomain:MXDecryptingErrorDomain
result = [MXEventDecryptionResult new];
result.error = [NSError errorWithDomain:MXDecryptingErrorDomain
code:MXDecryptingErrorMissingFieldsCode
userInfo:@{
NSLocalizedDescriptionKey: MXDecryptingErrorMissingFieldsReason
}];
}
return nil;
return result;
}

NSError *olmError;
MXDecryptionResult *olmResult = [olmDevice decryptGroupMessage:ciphertext roomId:event.roomId inTimeline:timeline sessionId:sessionId senderKey:senderKey error:&olmError];

result = [MXEventDecryptionResult new];
if (olmResult)
{
result = [[MXEventDecryptionResult alloc] init];

result.clearEvent = olmResult.payload;
result.senderCurve25519Key = olmResult.senderKey;
result.claimedEd25519Key = olmResult.keysClaimed[@"ed25519"];
Expand Down Expand Up @@ -141,10 +138,7 @@ - (MXEventDecryptionResult *)decryptEvent:(MXEvent*)event inTimeline:(NSString*)
});
}

if (error)
{
*error = olmError;
}
result.error = olmError;
}

return result;
Expand Down Expand Up @@ -400,29 +394,25 @@ - (void)retryDecryption:(NSString*)senderKey sessionId:(NSString*)sessionId comp
else
{
// Decrypt on the current thread (Must be MXCrypto.cryptoQueue)
NSError *error;
MXEventDecryptionResult *result = [self decryptEvent:event inTimeline:(timelineId.length ? timelineId : nil) error:&error];
MXEventDecryptionResult *result = [self decryptEvent:event inTimeline:(timelineId.length ? timelineId : nil)];

// And set the result on the main thread to be compatible with other modules
dispatch_group_enter(group);
dispatch_async(dispatch_get_main_queue(), ^{
if (result)
if (result.error)
{
if (event.clearEvent)
{
// This can happen when the event is in several timelines
NSLog(@"[MXMegolmDecryption] retryDecryption: %@ already decrypted on main thread", event.eventId);
}
else
{
[event setClearData:result];
}
NSLog(@"[MXMegolmDecryption] retryDecryption: Still can't decrypt %@. Error: %@", event.eventId, result.error);
allDecrypted = NO;
}
else if (error)

if (event.clearEvent)
{
NSLog(@"[MXMegolmDecryption] retryDecryption: Still can't decrypt %@. Error: %@", event.eventId, event.decryptionError);
event.decryptionError = error;
allDecrypted = NO;
// This can happen when the event is in several timelines
NSLog(@"[MXMegolmDecryption] retryDecryption: %@ already decrypted on main thread", event.eventId);
}
else
{
[event setClearData:result];
}

dispatch_group_leave(group);
Expand Down

0 comments on commit 026cb56

Please sign in to comment.