From 262a0943505c40f6f995f30be6dafc1480637220 Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Fri, 19 Oct 2018 10:51:45 +0200 Subject: [PATCH 01/58] Add MXEncryptedContentInfo class and MXEncryptedContentKey --- .../Crypto/Data/MXEncryptedContentInfo.h | 76 +++++++++++++++++++ .../Crypto/Data/MXEncryptedContentInfo.m | 53 +++++++++++++ MatrixSDK/Crypto/Data/MXEncryptedContentKey.h | 59 ++++++++++++++ MatrixSDK/Crypto/Data/MXEncryptedContentKey.m | 50 ++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h create mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m create mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentKey.h create mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentKey.m diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h b/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h new file mode 100644 index 0000000000..4d8bf3c9c0 --- /dev/null +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h @@ -0,0 +1,76 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXJSONModel.h" + +/** + `MXEncryptedContentInfo` contains the encryption data required to decrypt an encrypted content. + These data are available in an encrypted event content under the `file` or `thumbnail_file` keys. + When the client creates an encrypted attachment, these data are returned on success (see MXEncryptedAttachment class). + + See below an example of these data: + { + "url": "mxc://…", + "mimetype": "video/mp4", + "key": { + "alg": "A256CTR", + "ext": true, + "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0", + "key_ops": ["encrypt","decrypt"], + "kty": "oct" + }, + "iv": "+pNiVx4SS9wXOV69UZqutg", + "hashes": { + "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA", + } + } +*/ + +@class MXEncryptedContentKey; + +@interface MXEncryptedContentInfo : MXJSONModel + +/** + Version of the encrypted attachments protocol. + */ +@property (nonatomic) NSString *v; + +/** + The URL to the encrypted file. + */ +@property (nonatomic) NSString *url; + +/** + The mimetype of the content. + */ +@property (nonatomic) NSString *mimetype; + +/** + The Key object. + */ +@property (nonatomic) MXEncryptedContentKey *key; + +/** + The Initialisation Vector used by AES-CTR, encoded as unpadded base64. + */ +@property (nonatomic) NSString *iv; + +/** + A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64. Clients should support the SHA-256 hash, which uses the key sha256. + */ +@property (nonatomic) NSDictionary *hashes; + +@end diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m b/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m new file mode 100644 index 0000000000..caad758349 --- /dev/null +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m @@ -0,0 +1,53 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXEncryptedContentInfo.h" +#import "MXEncryptedContentKey.h" + +@implementation MXEncryptedContentInfo + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXEncryptedContentInfo *encryptedContentInfo = [[MXEncryptedContentInfo alloc] init]; + if (encryptedContentInfo) + { + MXJSONModelSetString(encryptedContentInfo.v, JSONDictionary[@"v"]); + MXJSONModelSetString(encryptedContentInfo.url, JSONDictionary[@"url"]); + MXJSONModelSetString(encryptedContentInfo.mimetype, JSONDictionary[@"mimetype"]); + MXJSONModelSetMXJSONModel(encryptedContentInfo.key, MXEncryptedContentKey, JSONDictionary[@"key"]); + MXJSONModelSetString(encryptedContentInfo.iv, JSONDictionary[@"iv"]); + MXJSONModelSetDictionary(encryptedContentInfo.hashes, JSONDictionary[@"hashes"]); + } + return encryptedContentInfo; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + if (JSONDictionary) + { + JSONDictionary[@"v"] = _v; + JSONDictionary[@"url"] = _url; + JSONDictionary[@"mimetype"] = _mimetype; + JSONDictionary[@"key"] = _key.JSONDictionary; + JSONDictionary[@"iv"] = _iv; + JSONDictionary[@"hashes"] = _hashes; + } + + return JSONDictionary; +} + +@end diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentKey.h b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.h new file mode 100644 index 0000000000..cb21aa9110 --- /dev/null +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.h @@ -0,0 +1,59 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXJSONModel.h" + +/** + `MXEncryptedContentKey` stores the key information for an encrypted content. + It is used in `MXEncryptedContentInfo`. + + "key": { + "alg": "A256CTR", + "ext": true, + "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0", + "key_ops": ["encrypt","decrypt"], + "kty": "oct" + } +*/ + +@interface MXEncryptedContentKey : MXJSONModel + +/** + The algorithm. + */ +@property (nonatomic) NSString *alg; + +/** + Tell whether it is extractable. + */ +@property (nonatomic) BOOL ext; + +/** + The key, encoded as urlsafe unpadded base64. + */ +@property (nonatomic) NSString *k; + +/** + The key operations. Must at least contain encrypt and decrypt. + */ +@property (nonatomic) NSArray *key_ops; + +/** + The key type. + */ +@property (nonatomic) NSString *kty; + +@end diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentKey.m b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.m new file mode 100644 index 0000000000..505f9bede5 --- /dev/null +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.m @@ -0,0 +1,50 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXEncryptedContentKey.h" + +@implementation MXEncryptedContentKey + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXEncryptedContentKey *encryptedContentKey = [[MXEncryptedContentKey alloc] init]; + if (encryptedContentKey) + { + MXJSONModelSetString(encryptedContentKey.alg, JSONDictionary[@"alg"]); + MXJSONModelSetBoolean(encryptedContentKey.ext, JSONDictionary[@"ext"]); + MXJSONModelSetString(encryptedContentKey.k, JSONDictionary[@"k"]); + MXJSONModelSetArray(encryptedContentKey.key_ops, JSONDictionary[@"key_ops"]); + MXJSONModelSetString(encryptedContentKey.kty, JSONDictionary[@"kty"]); + } + return encryptedContentKey; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + if (JSONDictionary) + { + JSONDictionary[@"alg"] = _alg; + JSONDictionary[@"ext"] = @(_ext); + JSONDictionary[@"k"] = _k; + JSONDictionary[@"key_ops"] = _key_ops; + JSONDictionary[@"kty"] = _kty; + } + + return JSONDictionary; +} + +@end From 8e2f54f967bbea073cd5b2d590c26a176a99aa6d Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Fri, 19 Oct 2018 23:44:50 +0200 Subject: [PATCH 02/58] Add MXEncryptedContentFile and MXEncryptedContentKey classes --- CHANGES.rst | 1 + MatrixSDK.xcodeproj/project.pbxproj | 16 ++++ .../Crypto/Data/MXEncryptedAttachments.h | 35 +++------ .../Crypto/Data/MXEncryptedAttachments.m | 64 ++++++++-------- .../Crypto/Data/MXEncryptedContentFile.h | 76 +++++++++++++++++++ .../Crypto/Data/MXEncryptedContentFile.m | 53 +++++++++++++ MatrixSDK/Crypto/Data/MXEncryptedContentKey.h | 4 +- MatrixSDK/Crypto/Data/MXEncryptedContentKey.m | 4 +- MatrixSDK/Data/MXRoom.m | 21 ++--- 9 files changed, 205 insertions(+), 69 deletions(-) create mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentFile.h create mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentFile.m diff --git a/CHANGES.rst b/CHANGES.rst index 2436a286c6..18be00db5d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Improvements: * MXCrypto: Encrypt the messages for invited members according to the history visibility (#559) * MXSession: When create a room as direct wait for room being tagged as direct chat before calling success block. * CallKit is now disabled in China (PR #578). +* Add MXEncryptedContentFile and MXEncryptedContentKey classes. Bug fix: * MXEvent: Move `invite_room_state` to the correct place in the client-server API (vector-im/riot-ios/issues/2010). diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 1d4231d245..8ce4c3a1b9 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 021AFBA42179E91900742B2C /* MXEncryptedContentFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 021AFBA02179E91800742B2C /* MXEncryptedContentFile.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 021AFBA52179E91900742B2C /* MXEncryptedContentKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */; }; + 021AFBA62179E91900742B2C /* MXEncryptedContentKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 021AFBA22179E91800742B2C /* MXEncryptedContentKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 021AFBA72179E91900742B2C /* MXEncryptedContentFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */; }; 320BBF3C1D6C7D9D0079890E /* MXEventsEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 320BBF411D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 320BBF3D1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m */; }; 320BBF421D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3E1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -307,6 +311,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 021AFBA02179E91800742B2C /* MXEncryptedContentFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEncryptedContentFile.h; sourceTree = ""; }; + 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEncryptedContentKey.m; sourceTree = ""; }; + 021AFBA22179E91800742B2C /* MXEncryptedContentKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEncryptedContentKey.h; sourceTree = ""; }; + 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEncryptedContentFile.m; sourceTree = ""; }; 0BCFBADF157F3C8C43112BD6 /* Pods-MatrixSDKTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDKTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixSDKTests/Pods-MatrixSDKTests.release.xcconfig"; sourceTree = ""; }; 2BF02FACC417CA3368671024 /* Pods-MatrixSDK.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixSDK/Pods-MatrixSDK.debug.xcconfig"; sourceTree = ""; }; 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEventsEnumerator.h; sourceTree = ""; }; @@ -923,6 +931,10 @@ isa = PBXGroup; children = ( 3284A59C1DB7C00600A09972 /* Store */, + 021AFBA02179E91800742B2C /* MXEncryptedContentFile.h */, + 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */, + 021AFBA22179E91800742B2C /* MXEncryptedContentKey.h */, + 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */, 3256E37F1DCB91EB003C9718 /* MXCryptoConstants.h */, 3256E3801DCB91EB003C9718 /* MXCryptoConstants.m */, 32A151401DAF7C0C00400192 /* MXDeviceInfo.h */, @@ -1269,6 +1281,7 @@ 32B76EA320FDE2BE00B095F6 /* MXRoomMembersCount.h in Headers */, 32C6F93319DD814400EA4E9C /* MatrixSDK.h in Headers */, 324BE46C1E422766008D99D4 /* MXMegolmSessionData.h in Headers */, + 021AFBA62179E91900742B2C /* MXEncryptedContentKey.h in Headers */, 327187891DA7DCE50071C818 /* MXOlmEncryption.h in Headers */, 32A1514E1DAF897600400192 /* MXOlmSessionResult.h in Headers */, 320BBF3C1D6C7D9D0079890E /* MXEventsEnumerator.h in Headers */, @@ -1285,6 +1298,7 @@ 322691321E5EF77D00966A6E /* MXDeviceListOperation.h in Headers */, 32481A841C03572900782AD3 /* MXRoomAccountData.h in Headers */, 32A9770421626E5C00919CC0 /* MXServerNotices.h in Headers */, + 021AFBA42179E91900742B2C /* MXEncryptedContentFile.h in Headers */, F08B8D5C1E014711006171A8 /* NSData+MatrixSDK.h in Headers */, C60165381E3AA57900B92CFA /* MXSDKOptions.h in Headers */, 3281E8B919E42DFE00976E1A /* MXJSONModels.h in Headers */, @@ -1537,6 +1551,7 @@ 32A151271DABB0CB00400192 /* MXMegolmDecryption.m in Sources */, C6D5D60E1E4FBD2900706C0F /* MXSession.swift in Sources */, 320DFDE319DD99B60068622A /* MXError.m in Sources */, + 021AFBA52179E91900742B2C /* MXEncryptedContentKey.m in Sources */, 32F634AC1FC5E3480054EF49 /* MXEventDecryptionResult.m in Sources */, 327137281A24D50A00DB6757 /* MXMyUser.m in Sources */, C6F935881E5B3BE600FC34BF /* MX3PID.swift in Sources */, @@ -1557,6 +1572,7 @@ 3281E8BA19E42DFE00976E1A /* MXJSONModels.m in Sources */, 3245A7531AF7B2930001D8A7 /* MXCallManager.m in Sources */, 32E226A71D06AC9F00E6CA54 /* MXPeekingRoom.m in Sources */, + 021AFBA72179E91900742B2C /* MXEncryptedContentFile.m in Sources */, 3220094619EFBF30008DE41D /* MXSessionEventListener.m in Sources */, 32A31BC920D401FC005916C7 /* MXRoomFilter.m in Sources */, 32A151471DAF7C0C00400192 /* MXDeviceInfo.m in Sources */, diff --git a/MatrixSDK/Crypto/Data/MXEncryptedAttachments.h b/MatrixSDK/Crypto/Data/MXEncryptedAttachments.h index d8b371951c..d40caa3b77 100644 --- a/MatrixSDK/Crypto/Data/MXEncryptedAttachments.h +++ b/MatrixSDK/Crypto/Data/MXEncryptedAttachments.h @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd + Copyright 2018 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,43 +20,27 @@ extern NSString *const MXEncryptedAttachmentsErrorDomain; @class MXMediaLoader; +@class MXEncryptedContentFile; @interface MXEncryptedAttachments : NSObject + (void)encryptAttachment:(MXMediaLoader *)uploader mimeType:(NSString *)mimeType localUrl:(NSURL *)url - success:(void(^)(NSDictionary *result))success + success:(void(^)(MXEncryptedContentFile *result))success failure:(void(^)(NSError *error))failure; + (void)encryptAttachment:(MXMediaLoader *)uploader mimeType:(NSString *)mimeType data:(NSData *)data - success:(void(^)(NSDictionary *result))success + success:(void(^)(MXEncryptedContentFile *result))success failure:(void(^)(NSError *error))failure; /** Create an encrypted attachment object by encrypting the given data - and uploading it to the media repository. On success, a dictionary - representing a matrix attachment 'file' is provided to the success - callback, eg: - - { - "url": "mxc://…", - "mimetype": "video/mp4", - "key": { - "alg": "A256CTR", - "ext": true, - "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0", - "key_ops": ["encrypt","decrypt"], - "kty": "oct" - }, - "iv": "+pNiVx4SS9wXOV69UZqutg", - "hashes": { - "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA", - } - } - + and uploading it to the media repository. On success, a MXEncryptedContentFile + instance representing a matrix attachment 'file' is provided to the success + callback @param uploader A valid, ready to use media loader @param mimeType The mime type of the file @@ -69,11 +54,11 @@ extern NSString *const MXEncryptedAttachmentsErrorDomain; + (void)encryptAttachment:(MXMediaLoader *)uploader mimeType:(NSString *)mimeType dataCallback:(NSData *(^)(void))dataCallback - success:(void(^)(NSDictionary *result))success + success:(void(^)(MXEncryptedContentFile *result))success failure:(void(^)(NSError *error))failure; /** - Given the dictionary of information about an encrypted + Given the information about an encrypted attachment, performs the decryption on the data provided by the input stream and writes it to the output stream. The 'url' in the information is ignored, with the @@ -85,7 +70,7 @@ extern NSString *const MXEncryptedAttachmentsErrorDomain; @param outputStream Stream to write the plaintext to @returns NSError nil on success, otherwise an error describing what went wrong */ -+ (NSError *)decryptAttachment:(NSDictionary *)fileInfo ++ (NSError *)decryptAttachment:(MXEncryptedContentFile *)fileInfo inputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream; diff --git a/MatrixSDK/Crypto/Data/MXEncryptedAttachments.m b/MatrixSDK/Crypto/Data/MXEncryptedAttachments.m index c884f1a0c5..225ca566c3 100644 --- a/MatrixSDK/Crypto/Data/MXEncryptedAttachments.m +++ b/MatrixSDK/Crypto/Data/MXEncryptedAttachments.m @@ -16,6 +16,8 @@ #import "MXEncryptedAttachments.h" #import "MXMediaLoader.h" +#import "MXEncryptedContentFile.h" +#import "MXEncryptedContentKey.h" #import #import @@ -30,7 +32,7 @@ @implementation MXEncryptedAttachments + (void)encryptAttachment:(MXMediaLoader *)uploader mimeType:(NSString *)mimeType localUrl:(NSURL *)url - success:(void(^)(NSDictionary *result))success + success:(void(^)(MXEncryptedContentFile *result))success failure:(void(^)(NSError *error))failure { NSError *err; @@ -54,7 +56,7 @@ + (void)encryptAttachment:(MXMediaLoader *)uploader + (void)encryptAttachment:(MXMediaLoader *)uploader mimeType:(NSString *)mimeType data:(NSData *)data - success:(void(^)(NSDictionary *result))success + success:(void(^)(MXEncryptedContentFile *result))success failure:(void(^)(NSError *error))failure { __block bool dataGiven = false; @@ -72,7 +74,7 @@ + (void)encryptAttachment:(MXMediaLoader *)uploader + (void)encryptAttachment:(MXMediaLoader *)uploader mimeType:(NSString *)mimeType dataCallback:(NSData *(^)(void))dataCallback - success:(void(^)(NSDictionary *result))success + success:(void(^)(MXEncryptedContentFile *result))success failure:(void(^)(NSError *error))failure { NSError *err; @@ -154,22 +156,24 @@ + (void)encryptAttachment:(MXMediaLoader *)uploader [uploader uploadData:ciphertext filename:nil mimeType:@"application/octet-stream" success:^(NSString *url) { - success(@{ - @"v": @"v2", - @"url": url, - @"mimetype": mimeType, - @"key": @{ - @"alg": @"A256CTR", - @"ext": @YES, - @"key_ops": @[@"encrypt", @"decrypt"], - @"kty": @"oct", - @"k": [MXEncryptedAttachments base64ToBase64Url:[key base64EncodedStringWithOptions:0]], - }, - @"iv": [iv base64EncodedStringWithOptions:0], - @"hashes": @{ - @"sha256": [MXEncryptedAttachments base64ToUnpaddedBase64:[computedSha256 base64EncodedStringWithOptions:0]], - } - }); + MXEncryptedContentKey *encryptedContentKey = [[MXEncryptedContentKey alloc] init]; + encryptedContentKey.alg = @"A256CTR"; + encryptedContentKey.ext = @YES; + encryptedContentKey.keyOps = @[@"encrypt", @"decrypt"]; + encryptedContentKey.kty = @"oct"; + encryptedContentKey.k = [MXEncryptedAttachments base64ToBase64Url:[key base64EncodedStringWithOptions:0]]; + + MXEncryptedContentFile *encryptedContentFile = [[MXEncryptedContentFile alloc] init]; + encryptedContentFile.v = @"v2"; + encryptedContentFile.url = url; + encryptedContentFile.mimetype = mimeType; + encryptedContentFile.key = encryptedContentKey; + encryptedContentFile.iv = [iv base64EncodedStringWithOptions:0]; + encryptedContentFile.hashes = @{ + @"sha256": [MXEncryptedAttachments base64ToUnpaddedBase64:[computedSha256 base64EncodedStringWithOptions:0]], + }; + + success(encryptedContentFile); } failure:^(NSError *error) { failure(error); }]; @@ -177,45 +181,45 @@ + (void)encryptAttachment:(MXMediaLoader *)uploader #pragma mark decrypt -+ (NSError *)decryptAttachment:(NSDictionary *)fileInfo ++ (NSError *)decryptAttachment:(MXEncryptedContentFile *)fileInfo inputStream:(NSInputStream *)inputStream outputStream:(NSOutputStream *)outputStream { // NB. We don;t check the 'v' field here: future versions should be backwards compatible so we try to decode // whatever the version is. We can only really decode v1, but the difference is the IV wraparound so we can try // decoding v0 attachments and the worst that will happen is that it won't work. - if (!fileInfo[@"key"] || ![fileInfo[@"key"] isKindOfClass:[NSDictionary class]]) + if (!fileInfo.key) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"missing_key"}]; } - if (![fileInfo[@"key"][@"alg"] isKindOfClass:[NSString class]] || ![fileInfo[@"key"][@"alg"] isEqualToString:@"A256CTR"]) + if (![fileInfo.key.alg isEqualToString:@"A256CTR"]) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"missing_or_incorrect_key_alg"}]; } - if (!fileInfo[@"key"][@"k"] || ![fileInfo[@"key"][@"k"] isKindOfClass:[NSString class]]) + if (!fileInfo.key.k) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"missing_key_data"}]; } - if (!fileInfo[@"iv"] || ![fileInfo[@"iv"] isKindOfClass:[NSString class]]) + if (!fileInfo.iv) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"missing_iv"}]; } - if (!fileInfo[@"hashes"] || ![fileInfo[@"hashes"] isKindOfClass:[NSDictionary class]]) + if (!fileInfo.hashes) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"missing_hashes"}]; } - if (![fileInfo[@"hashes"][@"sha256"] isKindOfClass:[NSString class]]) + if (!fileInfo.hashes[@"sha256"]) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"missing_sha256_hash"}]; } - NSData *keyData = [[NSData alloc] initWithBase64EncodedString:[MXEncryptedAttachments base64UrlToBase64:fileInfo[@"key"][@"k"]] + NSData *keyData = [[NSData alloc] initWithBase64EncodedString:[MXEncryptedAttachments base64UrlToBase64:fileInfo.key.k] options:0]; if (!keyData || keyData.length != kCCKeySizeAES256) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"bad_key_data"}]; } - NSData *ivData = [[NSData alloc] initWithBase64EncodedString:[MXEncryptedAttachments padBase64:fileInfo[@"iv"]] options:0]; + NSData *ivData = [[NSData alloc] initWithBase64EncodedString:[MXEncryptedAttachments padBase64:fileInfo.iv] options:0]; if (!ivData || ivData.length != kCCBlockSizeAES128) { return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"bad_iv_data"}]; @@ -268,11 +272,11 @@ + (NSError *)decryptAttachment:(NSDictionary *)fileInfo NSMutableData *computedSha256 = [[NSMutableData alloc] initWithLength:CC_SHA256_DIGEST_LENGTH]; CC_SHA256_Final(computedSha256.mutableBytes, &sha256ctx); - NSData *expectedSha256 = [[NSData alloc] initWithBase64EncodedString:[MXEncryptedAttachments padBase64:fileInfo[@"hashes"][@"sha256"]] options:0]; + NSData *expectedSha256 = [[NSData alloc] initWithBase64EncodedString:[MXEncryptedAttachments padBase64:fileInfo.hashes[@"sha256"]] options:0]; if (![computedSha256 isEqualToData:expectedSha256]) { - NSLog(@"Hash mismatch when decrypting attachment! Expected: %@, got %@", fileInfo[@"hashes"][@"sha256"], [computedSha256 base64EncodedStringWithOptions:0]); + NSLog(@"Hash mismatch when decrypting attachment! Expected: %@, got %@", fileInfo.hashes[@"sha256"], [computedSha256 base64EncodedStringWithOptions:0]); return [NSError errorWithDomain:MXEncryptedAttachmentsErrorDomain code:0 userInfo:@{@"err": @"hash_mismatch"}]; } return nil; diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentFile.h b/MatrixSDK/Crypto/Data/MXEncryptedContentFile.h new file mode 100644 index 0000000000..2158c2ea25 --- /dev/null +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentFile.h @@ -0,0 +1,76 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXJSONModel.h" + +/** + `MXEncryptedContentFile` contains the encryption data required to decrypt an encrypted content. + These data are available in an encrypted event content under the `file` or `thumbnail_file` keys. + When the client creates an encrypted attachment, these data are returned on success (see MXEncryptedAttachment class). + + See below an example of these data: + { + "url": "mxc://…", + "mimetype": "video/mp4", + "key": { + "alg": "A256CTR", + "ext": true, + "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0", + "key_ops": ["encrypt","decrypt"], + "kty": "oct" + }, + "iv": "+pNiVx4SS9wXOV69UZqutg", + "hashes": { + "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA", + } + } +*/ + +@class MXEncryptedContentKey; + +@interface MXEncryptedContentFile : MXJSONModel + +/** + Version of the encrypted attachments protocol. + */ +@property (nonatomic) NSString *v; + +/** + The URL to the encrypted file. + */ +@property (nonatomic) NSString *url; + +/** + The mimetype of the content. + */ +@property (nonatomic) NSString *mimetype; + +/** + The Key object. + */ +@property (nonatomic) MXEncryptedContentKey *key; + +/** + The Initialisation Vector used by AES-CTR, encoded as unpadded base64. + */ +@property (nonatomic) NSString *iv; + +/** + A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64. Clients should support the SHA-256 hash, which uses the key sha256. + */ +@property (nonatomic) NSDictionary *hashes; + +@end diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentFile.m b/MatrixSDK/Crypto/Data/MXEncryptedContentFile.m new file mode 100644 index 0000000000..98d5f43b51 --- /dev/null +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentFile.m @@ -0,0 +1,53 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXEncryptedContentFile.h" +#import "MXEncryptedContentKey.h" + +@implementation MXEncryptedContentFile + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXEncryptedContentFile *encryptedContentFile = [[MXEncryptedContentFile alloc] init]; + if (encryptedContentFile) + { + MXJSONModelSetString(encryptedContentFile.v, JSONDictionary[@"v"]); + MXJSONModelSetString(encryptedContentFile.url, JSONDictionary[@"url"]); + MXJSONModelSetString(encryptedContentFile.mimetype, JSONDictionary[@"mimetype"]); + MXJSONModelSetMXJSONModel(encryptedContentFile.key, MXEncryptedContentKey, JSONDictionary[@"key"]); + MXJSONModelSetString(encryptedContentFile.iv, JSONDictionary[@"iv"]); + MXJSONModelSetDictionary(encryptedContentFile.hashes, JSONDictionary[@"hashes"]); + } + return encryptedContentFile; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + if (JSONDictionary) + { + JSONDictionary[@"v"] = _v; + JSONDictionary[@"url"] = _url; + JSONDictionary[@"mimetype"] = _mimetype; + JSONDictionary[@"key"] = _key.JSONDictionary; + JSONDictionary[@"iv"] = _iv; + JSONDictionary[@"hashes"] = _hashes; + } + + return JSONDictionary; +} + +@end diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentKey.h b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.h index cb21aa9110..7cf5b74c2d 100644 --- a/MatrixSDK/Crypto/Data/MXEncryptedContentKey.h +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.h @@ -18,7 +18,7 @@ /** `MXEncryptedContentKey` stores the key information for an encrypted content. - It is used in `MXEncryptedContentInfo`. + It is used in `MXEncryptedContentFile`. "key": { "alg": "A256CTR", @@ -49,7 +49,7 @@ /** The key operations. Must at least contain encrypt and decrypt. */ -@property (nonatomic) NSArray *key_ops; +@property (nonatomic) NSArray *keyOps; /** The key type. diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentKey.m b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.m index 505f9bede5..e0284bee08 100644 --- a/MatrixSDK/Crypto/Data/MXEncryptedContentKey.m +++ b/MatrixSDK/Crypto/Data/MXEncryptedContentKey.m @@ -26,7 +26,7 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary MXJSONModelSetString(encryptedContentKey.alg, JSONDictionary[@"alg"]); MXJSONModelSetBoolean(encryptedContentKey.ext, JSONDictionary[@"ext"]); MXJSONModelSetString(encryptedContentKey.k, JSONDictionary[@"k"]); - MXJSONModelSetArray(encryptedContentKey.key_ops, JSONDictionary[@"key_ops"]); + MXJSONModelSetArray(encryptedContentKey.keyOps, JSONDictionary[@"key_ops"]); MXJSONModelSetString(encryptedContentKey.kty, JSONDictionary[@"kty"]); } return encryptedContentKey; @@ -40,7 +40,7 @@ - (NSDictionary *)JSONDictionary JSONDictionary[@"alg"] = _alg; JSONDictionary[@"ext"] = @(_ext); JSONDictionary[@"k"] = _k; - JSONDictionary[@"key_ops"] = _key_ops; + JSONDictionary[@"key_ops"] = _keyOps; JSONDictionary[@"kty"] = _kty; } diff --git a/MatrixSDK/Data/MXRoom.m b/MatrixSDK/Data/MXRoom.m index ae8678566f..e51a1ce12b 100644 --- a/MatrixSDK/Data/MXRoom.m +++ b/MatrixSDK/Data/MXRoom.m @@ -24,6 +24,7 @@ #import "MXDecryptionResult.h" #import "MXEncryptedAttachments.h" +#import "MXEncryptedContentFile.h" #import "MXMediaManager.h" #import "MXRoomOperation.h" @@ -913,10 +914,10 @@ - (MXHTTPOperation*)sendImage:(NSData*)imageData }]; NSURL *localURL = [NSURL URLWithString:cacheFilePath]; - [MXEncryptedAttachments encryptAttachment:uploader mimeType:mimetype localUrl:localURL success:^(NSDictionary *result) { + [MXEncryptedAttachments encryptAttachment:uploader mimeType:mimetype localUrl:localURL success:^(MXEncryptedContentFile *result) { [msgContent removeObjectForKey:@"url"]; - msgContent[@"file"] = result; + msgContent[@"file"] = result.JSONDictionary; void(^onDidUpload)(void) = ^{ @@ -956,9 +957,9 @@ - (MXHTTPOperation*)sendImage:(NSData*)imageData NSData *pngImageData = [newRep representationUsingType:NSPNGFileType properties:@{}]; #endif - [MXEncryptedAttachments encryptAttachment:thumbUploader mimeType:@"image/png" data:pngImageData success:^(NSDictionary *result) { + [MXEncryptedAttachments encryptAttachment:thumbUploader mimeType:@"image/png" data:pngImageData success:^(MXEncryptedContentFile *result) { - msgContent[@"info"][@"thumbnail_file"] = result; + msgContent[@"info"][@"thumbnail_file"] = result.JSONDictionary; onDidUpload(); @@ -1121,10 +1122,10 @@ - (MXHTTPOperation*)sendVideo:(NSURL*)videoLocalURL if (self.mxSession.crypto && self.summary.isEncrypted) { - [MXEncryptedAttachments encryptAttachment:thumbUploader mimeType:@"image/jpeg" data:videoThumbnailData success:^(NSDictionary *result) { + [MXEncryptedAttachments encryptAttachment:thumbUploader mimeType:@"image/jpeg" data:videoThumbnailData success:^(MXEncryptedContentFile *result) { // Update thumbnail URL with the actual mxc: URL - msgContent[@"info"][@"thumbnail_file"] = result; + msgContent[@"info"][@"thumbnail_file"] = result.JSONDictionary; [msgContent[@"info"] removeObjectForKey:@"thumbnail_url"]; MXMediaLoader *videoUploader = [MXMediaManager prepareUploaderWithMatrixSession:self.mxSession initialRange:0.1 andRange:1]; @@ -1158,7 +1159,7 @@ - (MXHTTPOperation*)sendVideo:(NSURL*)videoLocalURL }]; - [MXEncryptedAttachments encryptAttachment:videoUploader mimeType:mimetype localUrl:convertedLocalURL success:^(NSDictionary *result) { + [MXEncryptedAttachments encryptAttachment:videoUploader mimeType:mimetype localUrl:convertedLocalURL success:^(MXEncryptedContentFile *result) { // Do not go further if the orignal request has been cancelled if (roomOperation.isCancelled) @@ -1168,7 +1169,7 @@ - (MXHTTPOperation*)sendVideo:(NSURL*)videoLocalURL } [msgContent removeObjectForKey:@"url"]; - msgContent[@"file"] = result; + msgContent[@"file"] = result.JSONDictionary; // Send this content (the sent state of the local echo will be updated, its local storage too). MXHTTPOperation *operation2 = [self sendMessageWithContent:msgContent localEcho:&event success:onSuccess failure:onFailure]; @@ -1402,7 +1403,7 @@ - (MXHTTPOperation*)sendFile:(NSURL*)fileLocalURL }]; - [MXEncryptedAttachments encryptAttachment:uploader mimeType:mimeType localUrl:fileLocalURL success:^(NSDictionary *result) { + [MXEncryptedAttachments encryptAttachment:uploader mimeType:mimeType localUrl:fileLocalURL success:^(MXEncryptedContentFile *result) { // Do not go further if the orignal request has been cancelled if (roomOperation.isCancelled) @@ -1412,7 +1413,7 @@ - (MXHTTPOperation*)sendFile:(NSURL*)fileLocalURL } [msgContent removeObjectForKey:@"url"]; - msgContent[@"file"] = result; + msgContent[@"file"] = result.JSONDictionary; MXHTTPOperation *operation2 = [self sendMessageWithContent:msgContent localEcho:&event success:onSuccess failure:onFailure]; From 6c3c23d25d799a81743d0322a21008519198edc4 Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Fri, 19 Oct 2018 23:45:43 +0200 Subject: [PATCH 03/58] remove wrong files --- .../Crypto/Data/MXEncryptedContentInfo.h | 76 ------------------- .../Crypto/Data/MXEncryptedContentInfo.m | 53 ------------- 2 files changed, 129 deletions(-) delete mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h delete mode 100644 MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h b/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h deleted file mode 100644 index 4d8bf3c9c0..0000000000 --- a/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - Copyright 2018 New Vector Ltd - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXJSONModel.h" - -/** - `MXEncryptedContentInfo` contains the encryption data required to decrypt an encrypted content. - These data are available in an encrypted event content under the `file` or `thumbnail_file` keys. - When the client creates an encrypted attachment, these data are returned on success (see MXEncryptedAttachment class). - - See below an example of these data: - { - "url": "mxc://…", - "mimetype": "video/mp4", - "key": { - "alg": "A256CTR", - "ext": true, - "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0", - "key_ops": ["encrypt","decrypt"], - "kty": "oct" - }, - "iv": "+pNiVx4SS9wXOV69UZqutg", - "hashes": { - "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA", - } - } -*/ - -@class MXEncryptedContentKey; - -@interface MXEncryptedContentInfo : MXJSONModel - -/** - Version of the encrypted attachments protocol. - */ -@property (nonatomic) NSString *v; - -/** - The URL to the encrypted file. - */ -@property (nonatomic) NSString *url; - -/** - The mimetype of the content. - */ -@property (nonatomic) NSString *mimetype; - -/** - The Key object. - */ -@property (nonatomic) MXEncryptedContentKey *key; - -/** - The Initialisation Vector used by AES-CTR, encoded as unpadded base64. - */ -@property (nonatomic) NSString *iv; - -/** - A map from an algorithm name to a hash of the ciphertext, encoded as unpadded base64. Clients should support the SHA-256 hash, which uses the key sha256. - */ -@property (nonatomic) NSDictionary *hashes; - -@end diff --git a/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m b/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m deleted file mode 100644 index caad758349..0000000000 --- a/MatrixSDK/Crypto/Data/MXEncryptedContentInfo.m +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2018 New Vector Ltd - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MXEncryptedContentInfo.h" -#import "MXEncryptedContentKey.h" - -@implementation MXEncryptedContentInfo - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary -{ - MXEncryptedContentInfo *encryptedContentInfo = [[MXEncryptedContentInfo alloc] init]; - if (encryptedContentInfo) - { - MXJSONModelSetString(encryptedContentInfo.v, JSONDictionary[@"v"]); - MXJSONModelSetString(encryptedContentInfo.url, JSONDictionary[@"url"]); - MXJSONModelSetString(encryptedContentInfo.mimetype, JSONDictionary[@"mimetype"]); - MXJSONModelSetMXJSONModel(encryptedContentInfo.key, MXEncryptedContentKey, JSONDictionary[@"key"]); - MXJSONModelSetString(encryptedContentInfo.iv, JSONDictionary[@"iv"]); - MXJSONModelSetDictionary(encryptedContentInfo.hashes, JSONDictionary[@"hashes"]); - } - return encryptedContentInfo; -} - -- (NSDictionary *)JSONDictionary -{ - NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; - if (JSONDictionary) - { - JSONDictionary[@"v"] = _v; - JSONDictionary[@"url"] = _url; - JSONDictionary[@"mimetype"] = _mimetype; - JSONDictionary[@"key"] = _key.JSONDictionary; - JSONDictionary[@"iv"] = _iv; - JSONDictionary[@"hashes"] = _hashes; - } - - return JSONDictionary; -} - -@end From 51c8f84c0c956ae6ef3a14c9217643166024c0ef Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Sat, 20 Oct 2018 00:29:15 +0200 Subject: [PATCH 04/58] Update MatrixSDK header --- MatrixSDK/MatrixSDK.h | 1 + 1 file changed, 1 insertion(+) diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index debc3e4212..25134067b3 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -51,6 +51,7 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXCrypto.h" #import "MXMegolmExportEncryption.h" +#import "MXEncryptedContentFile.h" #import "MXBugReportRestClient.h" From 408d843e0838dbff586351e7448827eaa1865dad Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Mon, 22 Oct 2018 13:37:06 +0200 Subject: [PATCH 05/58] Add antivirus server support - MXRestClient: - add optional antivirus server url - implement Matrix Content Scanner API --- MatrixSDK.xcodeproj/project.pbxproj | 40 +++++ .../Data/MXContentScanEncryptedBody.h | 39 +++++ .../Data/MXContentScanEncryptedBody.m | 46 +++++ .../JSONModels/MXContentScanResult.h | 34 ++++ .../JSONModels/MXContentScanResult.m | 32 ++++ MatrixSDK/MXRestClient.h | 80 ++++++++- MatrixSDK/MXRestClient.m | 163 +++++++++++++++++- 7 files changed, 430 insertions(+), 4 deletions(-) create mode 100644 MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.h create mode 100644 MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.m create mode 100644 MatrixSDK/ContentScan/JSONModels/MXContentScanResult.h create mode 100644 MatrixSDK/ContentScan/JSONModels/MXContentScanResult.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 8ce4c3a1b9..cb4f3a82c5 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -11,6 +11,10 @@ 021AFBA52179E91900742B2C /* MXEncryptedContentKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */; }; 021AFBA62179E91900742B2C /* MXEncryptedContentKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 021AFBA22179E91800742B2C /* MXEncryptedContentKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; 021AFBA72179E91900742B2C /* MXEncryptedContentFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */; }; + 02CAD438217DD12F0074700B /* MXContentScanResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 02CAD433217DD12F0074700B /* MXContentScanResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 02CAD439217DD12F0074700B /* MXContentScanResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 02CAD434217DD12F0074700B /* MXContentScanResult.m */; }; + 02CAD43A217DD12F0074700B /* MXContentScanEncryptedBody.m in Sources */ = {isa = PBXBuildFile; fileRef = 02CAD436217DD12F0074700B /* MXContentScanEncryptedBody.m */; }; + 02CAD43B217DD12F0074700B /* MXContentScanEncryptedBody.h in Headers */ = {isa = PBXBuildFile; fileRef = 02CAD437217DD12F0074700B /* MXContentScanEncryptedBody.h */; settings = {ATTRIBUTES = (Public, ); }; }; 320BBF3C1D6C7D9D0079890E /* MXEventsEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 320BBF411D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 320BBF3D1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m */; }; 320BBF421D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3E1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -315,6 +319,10 @@ 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEncryptedContentKey.m; sourceTree = ""; }; 021AFBA22179E91800742B2C /* MXEncryptedContentKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEncryptedContentKey.h; sourceTree = ""; }; 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEncryptedContentFile.m; sourceTree = ""; }; + 02CAD433217DD12F0074700B /* MXContentScanResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXContentScanResult.h; sourceTree = ""; }; + 02CAD434217DD12F0074700B /* MXContentScanResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXContentScanResult.m; sourceTree = ""; }; + 02CAD436217DD12F0074700B /* MXContentScanEncryptedBody.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXContentScanEncryptedBody.m; sourceTree = ""; }; + 02CAD437217DD12F0074700B /* MXContentScanEncryptedBody.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXContentScanEncryptedBody.h; sourceTree = ""; }; 0BCFBADF157F3C8C43112BD6 /* Pods-MatrixSDKTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDKTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixSDKTests/Pods-MatrixSDKTests.release.xcconfig"; sourceTree = ""; }; 2BF02FACC417CA3368671024 /* Pods-MatrixSDK.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixSDK/Pods-MatrixSDK.debug.xcconfig"; sourceTree = ""; }; 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEventsEnumerator.h; sourceTree = ""; }; @@ -627,6 +635,33 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 02CAD431217DD12F0074700B /* ContentScan */ = { + isa = PBXGroup; + children = ( + 02CAD432217DD12F0074700B /* JSONModels */, + 02CAD435217DD12F0074700B /* Data */, + ); + path = ContentScan; + sourceTree = ""; + }; + 02CAD432217DD12F0074700B /* JSONModels */ = { + isa = PBXGroup; + children = ( + 02CAD433217DD12F0074700B /* MXContentScanResult.h */, + 02CAD434217DD12F0074700B /* MXContentScanResult.m */, + ); + path = JSONModels; + sourceTree = ""; + }; + 02CAD435217DD12F0074700B /* Data */ = { + isa = PBXGroup; + children = ( + 02CAD436217DD12F0074700B /* MXContentScanEncryptedBody.m */, + 02CAD437217DD12F0074700B /* MXContentScanEncryptedBody.h */, + ); + path = Data; + sourceTree = ""; + }; 238F35931D935D88B55C4FBC /* Pods */ = { isa = PBXGroup; children = ( @@ -999,6 +1034,7 @@ 32C6F92F19DD814400EA4E9C /* MatrixSDK */ = { isa = PBXGroup; children = ( + 02CAD431217DD12F0074700B /* ContentScan */, 322A51B31D9AB13E00C8536D /* Crypto */, 320DFDC719DD99B60068622A /* Data */, 3281E8B219E42DFE00976E1A /* JSONModels */, @@ -1288,6 +1324,7 @@ 32637ED41E5B00400011E20D /* MXDeviceList.h in Headers */, 32F9FA7D1DBA0CF0009D98A6 /* MXDecryptionResult.h in Headers */, 3245A7501AF7B2930001D8A7 /* MXCall.h in Headers */, + 02CAD43B217DD12F0074700B /* MXContentScanEncryptedBody.h in Headers */, 3271877D1DA7CB2F0071C818 /* MXDecrypting.h in Headers */, 32114A851A262CE000FF2EC4 /* MXStore.h in Headers */, 32BA86AF2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h in Headers */, @@ -1319,6 +1356,7 @@ 324BE4681E3FADB1008D99D4 /* MXMegolmExportEncryption.h in Headers */, 32CAB10B1A925B41008C5BB9 /* MXHTTPOperation.h in Headers */, 32F945F81FAB83D900622468 /* MXIncomingRoomKeyRequestCancellation.h in Headers */, + 02CAD438217DD12F0074700B /* MXContentScanResult.h in Headers */, 32618E7B20EFA45B00E1D2EA /* MXRoomMembers.h in Headers */, 3240969D1F9F751600DBA607 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */, 32F634AB1FC5E3480054EF49 /* MXEventDecryptionResult.h in Headers */, @@ -1578,6 +1616,7 @@ 32A151471DAF7C0C00400192 /* MXDeviceInfo.m in Sources */, 32A1515C1DB525DA00400192 /* NSObject+sortedKeys.m in Sources */, 32618E7C20EFA45B00E1D2EA /* MXRoomMembers.m in Sources */, + 02CAD43A217DD12F0074700B /* MXContentScanEncryptedBody.m in Sources */, F03EF5091DF071D5009DF592 /* MXEncryptedAttachments.m in Sources */, B172857D2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m in Sources */, 32FA10CF1FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.m in Sources */, @@ -1596,6 +1635,7 @@ B17982F62119E4A2001FD722 /* MXRoomTombStoneContent.m in Sources */, C602B58E1F22A8D700B67D87 /* MXImage.swift in Sources */, 3295401A216385F100E300FC /* MXServerNoticeContent.m in Sources */, + 02CAD439217DD12F0074700B /* MXContentScanResult.m in Sources */, 32A151491DAF7C0C00400192 /* MXKey.m in Sources */, F0C34CBB1C18C93700C36F09 /* MXSDKOptions.m in Sources */, 320BBF441D6C81550079890E /* MXEventsEnumeratorOnArray.m in Sources */, diff --git a/MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.h b/MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.h new file mode 100644 index 0000000000..a87410e211 --- /dev/null +++ b/MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.h @@ -0,0 +1,39 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXJSONModel.h" + +/** + `MXContentScanEncryptedBody` contains the encrypted body use to scan an encrypted content. + */ +@interface MXContentScanEncryptedBody : MXJSONModel + +/** + The base64-encoded string representing encrypted JSON of the original request body. + */ +@property (nonatomic) NSString *ciphertext; + +/** + The base64-encoded string representing the MAC. + */ +@property (nonatomic) NSString *mac; + +/** + The base64-encoded string representing the ephemeral public key. + */ +@property (nonatomic) NSString *ephemeral; + +@end diff --git a/MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.m b/MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.m new file mode 100644 index 0000000000..d4605835e9 --- /dev/null +++ b/MatrixSDK/ContentScan/Data/MXContentScanEncryptedBody.m @@ -0,0 +1,46 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXContentScanEncryptedBody.h" + +@implementation MXContentScanEncryptedBody + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXContentScanEncryptedBody *contentScanEncryptedBody = [[MXContentScanEncryptedBody alloc] init]; + if (contentScanEncryptedBody) + { + MXJSONModelSetString(contentScanEncryptedBody.ciphertext, JSONDictionary[@"ciphertext"]); + MXJSONModelSetString(contentScanEncryptedBody.mac, JSONDictionary[@"mac"]); + MXJSONModelSetString(contentScanEncryptedBody.ephemeral, JSONDictionary[@"ephemeral"]); + } + return contentScanEncryptedBody; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + if (JSONDictionary) + { + JSONDictionary[@"ciphertext"] = _ciphertext; + JSONDictionary[@"mac"] = _mac; + JSONDictionary[@"ephemeral"] = _ephemeral; + } + + return JSONDictionary; +} + +@end diff --git a/MatrixSDK/ContentScan/JSONModels/MXContentScanResult.h b/MatrixSDK/ContentScan/JSONModels/MXContentScanResult.h new file mode 100644 index 0000000000..d14f364cc1 --- /dev/null +++ b/MatrixSDK/ContentScan/JSONModels/MXContentScanResult.h @@ -0,0 +1,34 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXJSONModel.h" + +/** + `MXContentScanResult` contains the antivirus scan result of a matrix content. + */ +@interface MXContentScanResult : MXJSONModel + +/** + If true, the script ran with an exit code of 0. Otherwise it ran with a non-zero exit code. + */ +@property (nonatomic) BOOL clean; + +/** + Human-readable information about the result. + */ +@property (nonatomic) NSString *info; + +@end diff --git a/MatrixSDK/ContentScan/JSONModels/MXContentScanResult.m b/MatrixSDK/ContentScan/JSONModels/MXContentScanResult.m new file mode 100644 index 0000000000..9bd4b15859 --- /dev/null +++ b/MatrixSDK/ContentScan/JSONModels/MXContentScanResult.m @@ -0,0 +1,32 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXContentScanResult.h" + +@implementation MXContentScanResult + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXContentScanResult *contentScanResult = [[MXContentScanResult alloc] init]; + if (contentScanResult) + { + MXJSONModelSetBoolean(contentScanResult.clean, JSONDictionary[@"clean"]); + MXJSONModelSetString(contentScanResult.info, JSONDictionary[@"info"]); + } + return contentScanResult; +} + +@end diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index 3ca216c2d6..58a92a3bdd 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -33,6 +33,9 @@ #import "MXJSONModels.h" #import "MXFilterJSONModel.h" #import "MXMatrixVersions.h" +#import "MXContentScanResult.h" +#import "MXEncryptedContentFile.h" +#import "MXContentScanEncryptedBody.h" #pragma mark - Constants definitions /** @@ -41,7 +44,7 @@ FOUNDATION_EXPORT NSString *const kMXAPIPrefixPathR0; /** - A constant representing tthe URI path for as-yet unspecified of the Client-Server HTTP API. + A constant representing the URI path for as-yet unspecified of the Client-Server HTTP API. */ FOUNDATION_EXPORT NSString *const kMXAPIPrefixPathUnstable; @@ -50,6 +53,11 @@ FOUNDATION_EXPORT NSString *const kMXAPIPrefixPathUnstable; */ FOUNDATION_EXPORT NSString *const kMXIdentityAPIPrefixPath; +/** + A constant representing the URI path for as-yet unspecified of the AntiVirus Client-Server HTTP API. + */ +FOUNDATION_EXPORT NSString *const kMXAntivirusAPIPrefixPathUnstable; + /** Scheme used in Matrix content URIs. */ @@ -148,6 +156,19 @@ typedef enum : NSUInteger */ @property (nonatomic) NSString *identityServer; +/** + The antivirus server. + By default, it points to the defined home server. If needed, change it by setting + this property. + */ +@property (nonatomic) NSString *antivirusServer; + +/** + The Client-Server API prefix to use for the antivirus server + By default, it is defined by the constant kMXAntivirusAPIPrefixPathUnstable. + */ +@property (nonatomic) NSString *antivirusServerPathPrefix; + /** The current trusted certificate (if any). */ @@ -1860,6 +1881,62 @@ typedef enum : NSUInteger success:(void (^)(NSDictionary *thirdPartySigned))success failure:(void (^)(NSError *error))failure NS_REFINED_FOR_SWIFT; + +#pragma mark - Antivirus server API + +/** + Get the current public curve25519 key that the antivirus server is advertising. + + @param success A block object called when the operation succeeds. It provides the antivirus public key. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)getAntivirusServerPublicKey:(void (^)(NSString *publicKey))success + failure:(void (^)(NSError *error))failure; + +/** + Scan an unencrypted content. + + @param mxcContentURI the Matrix content URI to scan (in the form of "mxc://..."). + @param success A block object called when the operation succeeds. It provides the scan result. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)scanUnencryptedContent:(NSString*)mxcContentURI + success:(void (^)(MXContentScanResult *scanResult))success + failure:(void (^)(NSError *error))failure; + +/** + Scan an encrypted content. + + @param encryptedContentFile the information of the encrypted content + @param success A block object called when the operation succeeds. It provides the scan result. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)scanEncryptedContent:(MXEncryptedContentFile*)encryptedContentFile + success:(void (^)(MXContentScanResult *scanResult))success + failure:(void (^)(NSError *error))failure; + +/** + Scan an encrypted content by sending an encrypted body (produced by considering the antivirus + server public key). + + @param encryptedbody the encrypted data used to + @param success A block object called when the operation succeeds. It provides the scan result. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)scanEncryptedContentWithSecureExchange:(MXContentScanEncryptedBody *)encryptedbody + success:(void (^)(MXContentScanResult *scanResult))success + failure:(void (^)(NSError *error))failure; + + +#pragma mark - Certificates /** Set the certificates used to evaluate server trust according to the SSL pinning mode. @@ -1867,6 +1944,7 @@ typedef enum : NSUInteger */ -(void)setPinnedCertificates:(NSSet *)pinnedCertificates; + #pragma mark - VoIP API /** Get the TURN server configuration advised by the homeserver. diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index 90c95bfca5..4237e7d825 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -36,6 +36,11 @@ */ NSString *const kMXIdentityAPIPrefixPath = @"_matrix/identity/api/v1"; +/** + Prefix used in path of antivirus server API requests. + */ +NSString *const kMXAntivirusAPIPrefixPathUnstable = @"_matrix/media_proxy/unstable/"; + /** Matrix content respository path */ @@ -98,6 +103,11 @@ @interface MXRestClient () */ MXHTTPClient *identityHttpClient; + /** + HTTP client to the antivirus server. + */ + MXHTTPClient *antivirusHttpClient; + /** The queue to process server response. This queue is used to create models from JSON dictionary without blocking the main thread. @@ -107,7 +117,7 @@ @interface MXRestClient () @end @implementation MXRestClient -@synthesize homeserver, homeserverSuffix, credentials, apiPathPrefix, contentPathPrefix, completionQueue; +@synthesize homeserver, homeserverSuffix, credentials, apiPathPrefix, contentPathPrefix, completionQueue, antivirusServerPathPrefix; -(id)initWithHomeServer:(NSString *)inHomeserver andOnUnrecognizedCertificateBlock:(MXHTTPClientOnUnrecognizedCertificate)onUnrecognizedCertBlock { @@ -116,6 +126,7 @@ -(id)initWithHomeServer:(NSString *)inHomeserver andOnUnrecognizedCertificateBlo { homeserver = inHomeserver; apiPathPrefix = kMXAPIPrefixPathR0; + antivirusServerPathPrefix = kMXAntivirusAPIPrefixPathUnstable; contentPathPrefix = kMXContentPrefixPath; httpClient = [[MXHTTPClient alloc] initWithBaseURL:homeserver @@ -146,8 +157,9 @@ -(id)initWithHomeServer:(NSString *)inHomeserver andOnUnrecognizedCertificateBlo } }]; - // By default, use the same address for the identity server + // By default, use the same address for the identity server, and the antivirus server self.identityServer = homeserver; + self.antivirusServer = homeserver; completionQueue = dispatch_get_main_queue(); @@ -163,6 +175,7 @@ -(id)initWithCredentials:(MXCredentials*)inCredentials andOnUnrecognizedCertific { homeserver = inCredentials.homeServer; apiPathPrefix = kMXAPIPrefixPathR0; + antivirusServerPathPrefix = kMXAntivirusAPIPrefixPathUnstable; contentPathPrefix = kMXContentPrefixPath; self.credentials = inCredentials; @@ -210,8 +223,9 @@ -(id)initWithCredentials:(MXCredentials*)inCredentials andOnUnrecognizedCertific } }]; - // By default, use the same address for the identity server + // By default, use the same address for the identity server, and the antivirus server self.identityServer = homeserver; + self.antivirusServer = homeserver; completionQueue = dispatch_get_main_queue(); @@ -227,6 +241,7 @@ - (void)close homeserverSuffix = nil; httpClient = nil; identityHttpClient = nil; + antivirusHttpClient = nil; processingQueue = nil; completionQueue = nil; @@ -3487,6 +3502,148 @@ - (MXHTTPOperation*)signUrl:(NSString*)signUrl }]; } +#pragma mark - Antivirus server API +- (void)setAntivirusServer:(NSString *)antivirusServer +{ + _antivirusServer = [antivirusServer copy]; + antivirusHttpClient = [[MXHTTPClient alloc] initWithBaseURL:[NSString stringWithFormat:@"%@/%@", antivirusServer, antivirusServerPathPrefix] + andOnUnrecognizedCertificateBlock:nil]; +} + +- (MXHTTPOperation*)getAntivirusServerPublicKey:(void (^)(NSString *publicKey))success + failure:(void (^)(NSError *error))failure +{ + MXWeakify(self); + return [antivirusHttpClient requestWithMethod:@"GET" + path:@"public_key" + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + if (success) + { + __block NSString *publicKey; + [self dispatchProcessing:^{ + MXJSONModelSetString(publicKey, JSONResponse[@"public_key"]); + } andCompletion:^{ + success(publicKey); + }]; + } + } + failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)scanUnencryptedContent:(NSString*)mxcContentURI + success:(void (^)(MXContentScanResult *scanResult))success + failure:(void (^)(NSError *error))failure +{ + // Sanity check + if (![mxcContentURI hasPrefix:kMXContentUriScheme]) + { + // do not scan non-mxc content URLs + return nil; + } + + // Build request path by replacing the "mxc://" scheme + NSString *path = [mxcContentURI stringByReplacingOccurrencesOfString:kMXContentUriScheme withString:@"scan/"]; + MXWeakify(self); + return [antivirusHttpClient requestWithMethod:@"GET" + path:path + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + if (success) + { + __block MXContentScanResult *scanResult; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(scanResult, MXContentScanResult, JSONResponse); + } andCompletion:^{ + success(scanResult); + }]; + } + } + failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)scanEncryptedContent:(MXEncryptedContentFile*)encryptedContentFile + success:(void (^)(MXContentScanResult *scanResult))success + failure:(void (^)(NSError *error))failure +{ + NSData *payloadData = nil; + if (encryptedContentFile) + { + payloadData = [NSJSONSerialization dataWithJSONObject:@{@"file": encryptedContentFile.JSONDictionary} options:0 error:nil]; + } + + MXWeakify(self); + return [antivirusHttpClient requestWithMethod:@"POST" + path:@"scan_encrypted" + parameters:nil + data:payloadData + headers:@{@"Content-Type": @"application/json"} + timeout:-1 + uploadProgress:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + if (success) + { + __block MXContentScanResult *scanResult; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(scanResult, MXContentScanResult, JSONResponse); + } andCompletion:^{ + success(scanResult); + }]; + } + } + failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)scanEncryptedContentWithSecureExchange:(MXContentScanEncryptedBody *)encryptedbody + success:(void (^)(MXContentScanResult *scanResult))success + failure:(void (^)(NSError *error))failure +{ + NSData *payloadData = nil; + if (encryptedbody) + { + payloadData = [NSJSONSerialization dataWithJSONObject:@{@"encrypted_body": encryptedbody.JSONDictionary} options:0 error:nil]; + } + + MXWeakify(self); + return [antivirusHttpClient requestWithMethod:@"POST" + path:@"scan_encrypted" + parameters:nil + data:payloadData + headers:@{@"Content-Type": @"application/json"} + timeout:-1 + uploadProgress:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + if (success) + { + __block MXContentScanResult *scanResult; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(scanResult, MXContentScanResult, JSONResponse); + } andCompletion:^{ + success(scanResult); + }]; + } + } + failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +#pragma mark - Certificates + -(void)setPinnedCertificates:(NSSet *)pinnedCertificates { httpClient.pinnedCertificates = pinnedCertificates; } From de4f03f89386368e3eb4f67526f8d2b9ca0270ad Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 17 Oct 2018 14:35:04 +0200 Subject: [PATCH 06/58] Tests: Make MXRealmCryptoStore work the first time tests are launched on simulators for iOS 11 and higher --- CHANGES.rst | 7 ++++++ .../MXRealmCryptoStore/MXRealmCryptoStore.m | 22 ++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c21d227c39..89d899e1e5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +Changes in Matrix iOS SDK in 0.11.7 (2018-11-) +=============================================== + +Improvements: +* Tests: Make MXRealmCryptoStore work the first time tests are launched on simulators for iOS 11 and higher. + + Changes in Matrix iOS SDK in 0.11.6 (2018-10-31) =============================================== diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index 9893a0fab9..69b6cac4bd 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -860,10 +860,26 @@ + (RLMRealm*)realmForUser:(NSString*)userId // will be called twice for the same room id which breaks the uniqueness of the // primary key (roomId) for this table. RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; - + + NSURL *defaultRealmPathURL = config.fileURL.URLByDeletingLastPathComponent; + +#if TARGET_OS_SIMULATOR + // On simulator from iOS 11, the Documents folder used by Realm by default + // can be missing. Create it if required + // https://stackoverflow.com/a/50817364 + if (![NSFileManager.defaultManager fileExistsAtPath:defaultRealmPathURL.path]) + { + NSError *error; + [[NSFileManager defaultManager] createDirectoryAtPath:defaultRealmPathURL.path + withIntermediateDirectories:YES + attributes:nil + error:&error]; + NSLog(@"[MXRealmCryptoStore] On simulator, create the file tree used by Realm. Error: %@", error); + } +#endif + // Default db file URL: use the default directory, but replace the filename with the userId. - NSURL *defaultRealmFileURL = [[[config.fileURL URLByDeletingLastPathComponent] - URLByAppendingPathComponent:userId] + NSURL *defaultRealmFileURL = [[defaultRealmPathURL URLByAppendingPathComponent:userId] URLByAppendingPathExtension:@"realm"]; // Check for a potential application group id. From c78c3a7c95e07ff4fc88f6e1dd722346407c8c51 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 17 Oct 2018 14:52:36 +0200 Subject: [PATCH 07/58] Keys backup: Added data models --- .../Crypto/Data/Backup/MXKeyBackupVersion.h | 46 ++++++++++++++++++ .../Crypto/Data/Backup/MXKeyBackupVersion.m | 47 +++++++++++++++++++ .../Data/Backup/MXMegolmBackupAuthData.h | 40 ++++++++++++++++ .../Data/Backup/MXMegolmBackupAuthData.m | 21 +++++++++ MatrixSDK/Crypto/Data/MXCryptoConstants.h | 5 ++ MatrixSDK/Crypto/Data/MXCryptoConstants.m | 5 +- MatrixSDK/JSONModels/MXJSONModels.h | 2 + 7 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h create mode 100644 MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m create mode 100644 MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.h create mode 100644 MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.m diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h new file mode 100644 index 0000000000..c71e92d5d6 --- /dev/null +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h @@ -0,0 +1,46 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MXJSONModel.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Information on a backup version. + */ +@interface MXKeyBackupVersion : MXJSONModel + +/** + The algorithm used for storing backups. + Currently, only kMXCryptoMegolmBackupAlgorithm (m.megolm_backup.v1.curve25519-aes-sha2) is defined. + */ +@property (nonatomic) NSString *algorithm; + +/** + Algorithm-dependent data. + */ +@property (nonatomic) NSDictionary *authData; + +/** + The backup version. + */ +@property (nonatomic) NSInteger version; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m new file mode 100644 index 0000000000..f8e6c838c8 --- /dev/null +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m @@ -0,0 +1,47 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXKeyBackupVersion.h" + +@implementation MXKeyBackupVersion + +#pragma mark - MXJSONModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion new]; + if (keyBackupVersion) + { + MXJSONModelSetString(keyBackupVersion.algorithm, JSONDictionary[@"algorithm"]); + MXJSONModelSetDictionary(keyBackupVersion.authData, JSONDictionary[@"authData"]); + MXJSONModelSetInteger(keyBackupVersion.version, JSONDictionary[@"deviceInfo"]); + } + + return keyBackupVersion; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + + JSONDictionary[@"algorithm"] = _algorithm; + JSONDictionary[@"authData"] = _authData; + JSONDictionary[@"version"] = @(_version); + + return JSONDictionary; +} + +@end diff --git a/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.h b/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.h new file mode 100644 index 0000000000..be55704bfc --- /dev/null +++ b/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.h @@ -0,0 +1,40 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MXJSONModel.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Data model for MXKeyBackupVersion.authData in case of kMXCryptoMegolmBackupAlgorithm. + */ +@interface MXMegolmBackupAuthData : MXJSONModel + +/** + The curve25519 public key used to encrypt the backups. + */ +@property (nonatomic) NSString *publicKey; + +/** + Signatures of the public key. + */ +@property (nonatomic) NSDictionary *signatures; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.m b/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.m new file mode 100644 index 0000000000..871ec07a76 --- /dev/null +++ b/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.m @@ -0,0 +1,21 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXMegolmBackupAuthData.h" + +@implementation MXMegolmBackupAuthData + +@end diff --git a/MatrixSDK/Crypto/Data/MXCryptoConstants.h b/MatrixSDK/Crypto/Data/MXCryptoConstants.h index 51cdc54d65..b370abc99b 100644 --- a/MatrixSDK/Crypto/Data/MXCryptoConstants.h +++ b/MatrixSDK/Crypto/Data/MXCryptoConstants.h @@ -27,6 +27,11 @@ FOUNDATION_EXPORT NSString *const kMXCryptoOlmAlgorithm; */ FOUNDATION_EXPORT NSString *const kMXCryptoMegolmAlgorithm; +/** + Matrix algorithm tag for megolm keys backup. + */ +FOUNDATION_EXPORT NSString *const kMXCryptoMegolmBackupAlgorithm; + #pragma mark - Encrypting error diff --git a/MatrixSDK/Crypto/Data/MXCryptoConstants.m b/MatrixSDK/Crypto/Data/MXCryptoConstants.m index 7fa6d1f2eb..e0ce2f8bf7 100644 --- a/MatrixSDK/Crypto/Data/MXCryptoConstants.m +++ b/MatrixSDK/Crypto/Data/MXCryptoConstants.m @@ -17,8 +17,9 @@ #import "MXCryptoConstants.h" -NSString *const kMXCryptoOlmAlgorithm = @"m.olm.v1.curve25519-aes-sha2"; -NSString *const kMXCryptoMegolmAlgorithm = @"m.megolm.v1.aes-sha2"; +NSString *const kMXCryptoOlmAlgorithm = @"m.olm.v1.curve25519-aes-sha2"; +NSString *const kMXCryptoMegolmAlgorithm = @"m.megolm.v1.aes-sha2"; +NSString *const kMXCryptoMegolmBackupAlgorithm = @"m.megolm_backup.v1.curve25519-aes-sha2"; #pragma mark - Encrypting error diff --git a/MatrixSDK/JSONModels/MXJSONModels.h b/MatrixSDK/JSONModels/MXJSONModels.h index 23b0b5f050..9a84a43062 100644 --- a/MatrixSDK/JSONModels/MXJSONModels.h +++ b/MatrixSDK/JSONModels/MXJSONModels.h @@ -19,6 +19,8 @@ #import "MXJSONModel.h" #import "MXUsersDevicesMap.h" +#import "MXKeyBackupVersion.h" +#import "MXMegolmBackupAuthData.h" @class MXEvent, MXDeviceInfo, MXKey, MXUser; From f0aaed437f3486a9ba45563fdbe43a74684fe949 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 17 Oct 2018 18:13:17 +0200 Subject: [PATCH 08/58] Keys backup: MXKeyBackupVersion.version is now a string in the spec --- MatrixSDK.xcodeproj/project.pbxproj | 28 ++++++ .../Crypto/Data/Backup/MXKeyBackupVersion.h | 2 +- .../Crypto/Data/Backup/MXKeyBackupVersion.m | 11 ++- MatrixSDK/MXRestClient.h | 28 ++++++ MatrixSDK/MXRestClient.m | 53 +++++++++++ MatrixSDKTests/MXCryptoBackupTests.m | 90 +++++++++++++++++++ 6 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 MatrixSDKTests/MXCryptoBackupTests.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 8ce4c3a1b9..1aa9ceab8b 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -133,6 +133,11 @@ 328DDEC11A07E57E008C7DC8 /* MXJSONModelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */; }; 3291D4D41A68FFEB00C3BA41 /* MXFileRoomStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3291D4D21A68FFEB00C3BA41 /* MXFileRoomStore.h */; }; 3291D4D51A68FFEB00C3BA41 /* MXFileRoomStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3291D4D31A68FFEB00C3BA41 /* MXFileRoomStore.m */; }; + 32935F5A216FA0BA00A1BC24 /* MXKeyBackupVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 32935F58216FA0BA00A1BC24 /* MXKeyBackupVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32935F5B216FA0BA00A1BC24 /* MXKeyBackupVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F59216FA0BA00A1BC24 /* MXKeyBackupVersion.m */; }; + 32935F5E216FA19B00A1BC24 /* MXMegolmBackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32935F5C216FA19B00A1BC24 /* MXMegolmBackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32935F5F216FA19B00A1BC24 /* MXMegolmBackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F5D216FA19B00A1BC24 /* MXMegolmBackupAuthData.m */; }; + 32935F61216FA49D00A1BC24 /* MXCryptoBackupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F60216FA49D00A1BC24 /* MXCryptoBackupTests.m */; }; 3293C700214BBA4F009B3DDB /* MXPeekingRoomSummary.h in Headers */ = {isa = PBXBuildFile; fileRef = 3293C6FE214BBA4F009B3DDB /* MXPeekingRoomSummary.h */; }; 3293C701214BBA4F009B3DDB /* MXPeekingRoomSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 3293C6FF214BBA4F009B3DDB /* MXPeekingRoomSummary.m */; }; 32954019216385F100E300FC /* MXServerNoticeContent.h in Headers */ = {isa = PBXBuildFile; fileRef = 32954017216385F100E300FC /* MXServerNoticeContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -441,6 +446,11 @@ 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXJSONModelTests.m; sourceTree = ""; }; 3291D4D21A68FFEB00C3BA41 /* MXFileRoomStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXFileRoomStore.h; sourceTree = ""; }; 3291D4D31A68FFEB00C3BA41 /* MXFileRoomStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXFileRoomStore.m; sourceTree = ""; }; + 32935F58216FA0BA00A1BC24 /* MXKeyBackupVersion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupVersion.h; sourceTree = ""; }; + 32935F59216FA0BA00A1BC24 /* MXKeyBackupVersion.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupVersion.m; sourceTree = ""; }; + 32935F5C216FA19B00A1BC24 /* MXMegolmBackupAuthData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXMegolmBackupAuthData.h; sourceTree = ""; }; + 32935F5D216FA19B00A1BC24 /* MXMegolmBackupAuthData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXMegolmBackupAuthData.m; sourceTree = ""; }; + 32935F60216FA49D00A1BC24 /* MXCryptoBackupTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCryptoBackupTests.m; sourceTree = ""; }; 3293C6FE214BBA4F009B3DDB /* MXPeekingRoomSummary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXPeekingRoomSummary.h; sourceTree = ""; }; 3293C6FF214BBA4F009B3DDB /* MXPeekingRoomSummary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXPeekingRoomSummary.m; sourceTree = ""; }; 32954017216385F100E300FC /* MXServerNoticeContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXServerNoticeContent.h; sourceTree = ""; }; @@ -905,6 +915,17 @@ path = Store; sourceTree = ""; }; + 32935F57216FA09B00A1BC24 /* Backup */ = { + isa = PBXGroup; + children = ( + 32935F58216FA0BA00A1BC24 /* MXKeyBackupVersion.h */, + 32935F59216FA0BA00A1BC24 /* MXKeyBackupVersion.m */, + 32935F5C216FA19B00A1BC24 /* MXMegolmBackupAuthData.h */, + 32935F5D216FA19B00A1BC24 /* MXMegolmBackupAuthData.m */, + ); + path = Backup; + sourceTree = ""; + }; 329571941B024D2B00ABB3BA /* Mocks */ = { isa = PBXGroup; children = ( @@ -930,6 +951,7 @@ 32A1513B1DAF768D00400192 /* Data */ = { isa = PBXGroup; children = ( + 32935F57216FA09B00A1BC24 /* Backup */, 3284A59C1DB7C00600A09972 /* Store */, 021AFBA02179E91800742B2C /* MXEncryptedContentFile.h */, 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */, @@ -1035,6 +1057,7 @@ 32C6F93919DD814400EA4E9C /* MatrixSDKTests */ = { isa = PBXGroup; children = ( + 32935F60216FA49D00A1BC24 /* MXCryptoBackupTests.m */, 32C03CB52123076F00D92712 /* DirectRoomTests.m */, 320E1BC01E0AD674009635F5 /* MXRoomSummaryTests.m */, 32322A471E57264E005DD155 /* MXSelfSignedHomeserverTests.m */, @@ -1256,6 +1279,7 @@ B17982F52119E4A2001FD722 /* MXRoomCreateContent.h in Headers */, 32DC15CF1A8CF7AE006F9AD3 /* MXPushRuleConditionChecker.h in Headers */, 32954019216385F100E300FC /* MXServerNoticeContent.h in Headers */, + 32935F5A216FA0BA00A1BC24 /* MXKeyBackupVersion.h in Headers */, 327187851DA7D0220071C818 /* MXOlmDecryption.h in Headers */, 32D7767D1A27860600FC4AA2 /* MXMemoryStore.h in Headers */, 32CE6FB81A409B1F00317F1E /* MXFileStoreMetaData.h in Headers */, @@ -1358,6 +1382,7 @@ 32DC15D01A8CF7AE006F9AD3 /* MXNotificationCenter.h in Headers */, F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */, 329FB17F1A0B665800A5E88E /* MXUser.h in Headers */, + 32935F5E216FA19B00A1BC24 /* MXMegolmBackupAuthData.h in Headers */, 320DFDE219DD99B60068622A /* MXError.h in Headers */, 327E37B61A974F75007F026F /* MXLogger.h in Headers */, 3256E3811DCB91EB003C9718 /* MXCryptoConstants.h in Headers */, @@ -1523,6 +1548,7 @@ 321B41401E09937E009EEEC7 /* MXRoomSummary.m in Sources */, 32CAB1081A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */, 3245A7511AF7B2930001D8A7 /* MXCall.m in Sources */, + 32935F5B216FA0BA00A1BC24 /* MXKeyBackupVersion.m in Sources */, 327F8DB31C6112BA00581CA3 /* MXRoomThirdPartyInvite.m in Sources */, B17982FC2119E4A2001FD722 /* MXRoomPowerLevels.m in Sources */, 32C235731F827F3800E38FC5 /* MXRoomOperation.m in Sources */, @@ -1612,6 +1638,7 @@ 3220093919EFA4C9008DE41D /* MXEventListener.m in Sources */, C6481AF21F1678A9000DB8A0 /* MXSessionEventListener.swift in Sources */, 32A1514F1DAF897600400192 /* MXOlmSessionResult.m in Sources */, + 32935F5F216FA19B00A1BC24 /* MXMegolmBackupAuthData.m in Sources */, C6F9357C1E5B39CA00FC34BF /* MXRestClient.swift in Sources */, 322A51B71D9AB15900C8536D /* MXCrypto.m in Sources */, B17982FB2119E4A2001FD722 /* MXRoomPredecessorInfo.m in Sources */, @@ -1655,6 +1682,7 @@ 321809B919EEBF3000377451 /* MXEventTests.m in Sources */, 32A31BC120D3F4C4005916C7 /* MXFilterTests.m in Sources */, C61A4AF41E5DD88400442158 /* Dummy.swift in Sources */, + 32935F61216FA49D00A1BC24 /* MXCryptoBackupTests.m in Sources */, 328DDEC11A07E57E008C7DC8 /* MXJSONModelTests.m in Sources */, 32832B5C1BCC048300241108 /* MXStoreFileStoreTests.m in Sources */, 320E1BC11E0AD674009635F5 /* MXRoomSummaryTests.m in Sources */, diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h index c71e92d5d6..354075d51d 100644 --- a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h @@ -39,7 +39,7 @@ NS_ASSUME_NONNULL_BEGIN /** The backup version. */ -@property (nonatomic) NSInteger version; +@property (nonatomic, nullable) NSString *version; @end diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m index f8e6c838c8..d933360bf0 100644 --- a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m @@ -26,8 +26,8 @@ + (id)modelFromJSON:(NSDictionary *)JSONDictionary if (keyBackupVersion) { MXJSONModelSetString(keyBackupVersion.algorithm, JSONDictionary[@"algorithm"]); - MXJSONModelSetDictionary(keyBackupVersion.authData, JSONDictionary[@"authData"]); - MXJSONModelSetInteger(keyBackupVersion.version, JSONDictionary[@"deviceInfo"]); + MXJSONModelSetDictionary(keyBackupVersion.authData, JSONDictionary[@"auth_data"]); + MXJSONModelSetString(keyBackupVersion.version, JSONDictionary[@"version"]); } return keyBackupVersion; @@ -38,8 +38,11 @@ - (NSDictionary *)JSONDictionary NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; JSONDictionary[@"algorithm"] = _algorithm; - JSONDictionary[@"authData"] = _authData; - JSONDictionary[@"version"] = @(_version); + JSONDictionary[@"auth_data"] = _authData; + if (_version) + { + JSONDictionary[@"version"] = _version; + } return JSONDictionary; } diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index a7aaab136e..a133fa6911 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -2054,6 +2054,33 @@ typedef enum : NSUInteger failure:(void (^)(NSError *error))failure; +#pragma mark - Crypto: e2e keys backup + +/** + Create a new backup version. + + @param keyBackupVersion backup information. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)createKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion + success:(void (^)(NSString *version))success + failure:(void (^)(NSError *error))failure; + +/** + Get information about the current version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)keyBackupVersion:(void (^)(MXKeyBackupVersion *keyBackupVersion))success + failure:(void (^)(NSError *error))failure; + #pragma mark - Direct-to-device messaging /** Send an event to a specific list of devices @@ -2072,6 +2099,7 @@ typedef enum : NSUInteger success:(void (^)(void))success failure:(void (^)(NSError *error))failure NS_REFINED_FOR_SWIFT; + #pragma mark - Device Management /** Get information about all devices for the current user. diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index 533d790a46..511b2d0394 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -3819,6 +3819,59 @@ - (MXHTTPOperation *)keyChangesFrom:(NSString *)fromToken to:(NSString *)toToken } +#pragma mark - Crypto: e2e keys backup +- (MXHTTPOperation*)createKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion + success:(void (^)(NSString *version))success + failure:(void (^)(NSError *error))failure +{ + MXWeakify(self); + return [httpClient requestWithMethod:@"POST" + path:[NSString stringWithFormat:@"%@/room_keys/version", kMXAPIPrefixPathUnstable] + parameters:keyBackupVersion.JSONDictionary + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + __block NSString *version; + [self dispatchProcessing:^{ + MXJSONModelSetString(version, JSONResponse[@"version"]); + } andCompletion:^{ + success(version); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)keyBackupVersion:(void (^)(MXKeyBackupVersion *keyBackupVersion))success + failure:(void (^)(NSError *error))failure +{ + MXWeakify(self); + return [httpClient requestWithMethod:@"GET" + path:[NSString stringWithFormat:@"%@/room_keys/version", kMXAPIPrefixPathUnstable] + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + __block MXKeyBackupVersion *keyBackupVersion; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(keyBackupVersion, MXKeyBackupVersion, JSONResponse); + } andCompletion:^{ + success(keyBackupVersion); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + + #pragma mark - Direct-to-device messaging - (MXHTTPOperation*)sendToDevice:(NSString*)eventType contentMap:(MXUsersDevicesMap*)contentMap txnId:(NSString*)txnId diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m new file mode 100644 index 0000000000..c766b875dd --- /dev/null +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -0,0 +1,90 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MatrixSDKTestsData.h" +#import "MatrixSDKTestsE2EData.h" + +// Do not bother with retain cycles warnings in tests +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" + +@interface MXCryptoBackupTests : XCTestCase +{ + MatrixSDKTestsData *matrixSDKTestsData; + MatrixSDKTestsE2EData *matrixSDKTestsE2EData; +} +@end + +@implementation MXCryptoBackupTests + +- (void)setUp +{ + [super setUp]; + + matrixSDKTestsData = [[MatrixSDKTestsData alloc] init]; + matrixSDKTestsE2EData = [[MatrixSDKTestsE2EData alloc] initWithMatrixSDKTestsData:matrixSDKTestsData]; +} + +- (void)tearDown +{ + matrixSDKTestsData = nil; + matrixSDKTestsE2EData = nil; + + [super tearDown]; +} + +- (void)testRESTCreateKeyBackupVersion +{ + [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { + + MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion new]; + keyBackupVersion.algorithm = kMXCryptoMegolmBackupAlgorithm; + keyBackupVersion.authData = @{ + @"public_key": @"abcdefg", + @"signatures": @{ + @"something": @{ + @"ed25519:something": @"hijklmnop" + } + } + }; + + [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + + [aliceRestClient keyBackupVersion:^(MXKeyBackupVersion *keyBackupVersion2) { + + XCTAssertNotNil(keyBackupVersion2); + XCTAssertEqualObjects(keyBackupVersion2.version, version); + XCTAssertEqualObjects(keyBackupVersion2.algorithm, keyBackupVersion.algorithm); + XCTAssertEqualObjects(keyBackupVersion2.authData, keyBackupVersion.authData); + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + +@end + +#pragma clang diagnostic pop From 89c9a0c01d2ac8e213662296045f542fc2fa20ca Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 18 Oct 2018 11:32:23 +0200 Subject: [PATCH 09/58] Keys backup: MXKeyBackupVersion: Guarantee parameters non-nullabililty This is what could be done for #582. --- .../Crypto/Data/Backup/MXKeyBackupVersion.h | 13 +++++++-- .../Crypto/Data/Backup/MXKeyBackupVersion.m | 29 +++++++++++++------ MatrixSDKTests/MXCryptoBackupTests.m | 21 +++++++------- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h index 354075d51d..dda1ca485c 100644 --- a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h @@ -29,17 +29,24 @@ NS_ASSUME_NONNULL_BEGIN The algorithm used for storing backups. Currently, only kMXCryptoMegolmBackupAlgorithm (m.megolm_backup.v1.curve25519-aes-sha2) is defined. */ -@property (nonatomic) NSString *algorithm; +@property (nonatomic, readonly) NSString *algorithm; /** Algorithm-dependent data. */ -@property (nonatomic) NSDictionary *authData; +@property (nonatomic, readonly) NSDictionary *authData; /** The backup version. */ -@property (nonatomic, nullable) NSString *version; +@property (nonatomic, readonly, nullable) NSString *version; + +/** + Enforce usage of factory method to guarantee non-nullabity. + TODO: Should be done at `MXJSONModel` model. + */ +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; @end diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m index d933360bf0..6dd1ee7cf1 100644 --- a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m @@ -18,19 +18,30 @@ @implementation MXKeyBackupVersion -#pragma mark - MXJSONModel - -+ (id)modelFromJSON:(NSDictionary *)JSONDictionary +- (nullable instancetype)initWithJSON:(NSDictionary *)JSONDictionary { - MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion new]; - if (keyBackupVersion) + self = [super init]; + if (self) + { + MXJSONModelSetString(_algorithm, JSONDictionary[@"algorithm"]); + MXJSONModelSetDictionary(_authData, JSONDictionary[@"auth_data"]); + MXJSONModelSetString(_version, JSONDictionary[@"version"]); + } + + // nonnull checks + if (!_algorithm || !_authData) { - MXJSONModelSetString(keyBackupVersion.algorithm, JSONDictionary[@"algorithm"]); - MXJSONModelSetDictionary(keyBackupVersion.authData, JSONDictionary[@"auth_data"]); - MXJSONModelSetString(keyBackupVersion.version, JSONDictionary[@"version"]); + return nil; } - return keyBackupVersion; + return self; +} + +#pragma mark - MXJSONModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + return [[MXKeyBackupVersion alloc] initWithJSON:JSONDictionary]; } - (NSDictionary *)JSONDictionary diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index c766b875dd..6c095ac0ba 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -52,16 +52,17 @@ - (void)testRESTCreateKeyBackupVersion { [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { - MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion new]; - keyBackupVersion.algorithm = kMXCryptoMegolmBackupAlgorithm; - keyBackupVersion.authData = @{ - @"public_key": @"abcdefg", - @"signatures": @{ - @"something": @{ - @"ed25519:something": @"hijklmnop" - } - } - }; + MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion modelFromJSON:@{ + @"algorithm": kMXCryptoMegolmBackupAlgorithm, + @"auth_data": @{ + @"public_key": @"abcdefg", + @"signatures": @{ + @"something": @{ + @"ed25519:something": @"hijklmnop" + } + } + } + }]; [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { From 103588e55b63de50de3b1c0e1d0832440649f005 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 18 Oct 2018 17:09:30 +0200 Subject: [PATCH 10/58] Keys backup: Add models and APIs to backup and retrieve keys --- MatrixSDK.xcodeproj/project.pbxproj | 8 + .../Crypto/Data/Backup/MXKeyBackupData.h | 68 ++++++ .../Crypto/Data/Backup/MXKeyBackupData.m | 142 ++++++++++++ MatrixSDK/JSONModels/MXJSONModels.h | 1 + MatrixSDK/MXRestClient.h | 105 +++++++++ MatrixSDK/MXRestClient.m | 214 ++++++++++++++++++ MatrixSDKTests/MXCryptoBackupTests.m | 97 +++++++- 7 files changed, 624 insertions(+), 11 deletions(-) create mode 100644 MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.h create mode 100644 MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 1aa9ceab8b..d5925342c9 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -192,6 +192,8 @@ 32BA86AC21529E29008F277E /* MXRoomNameStringsLocalizable.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BA86AB21529AE3008F277E /* MXRoomNameStringsLocalizable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32BA86AF2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BA86AD2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h */; }; 32BA86B02152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BA86AE2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m */; }; + 32BBAE622178957600D85F46 /* MXKeyBackupData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE602178957600D85F46 /* MXKeyBackupData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32BBAE632178957600D85F46 /* MXKeyBackupData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE612178957600D85F46 /* MXKeyBackupData.m */; }; 32BD34BE1E84134A006EDC0D /* MatrixSDKTestsE2EData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BD34BD1E84134A006EDC0D /* MatrixSDKTestsE2EData.m */; }; 32BED28F1B00A23F00E668FE /* MXCallStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BED28E1B00A23F00E668FE /* MXCallStack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32C03CB62123076F00D92712 /* DirectRoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C03CB52123076F00D92712 /* DirectRoomTests.m */; }; @@ -507,6 +509,8 @@ 32BA86AB21529AE3008F277E /* MXRoomNameStringsLocalizable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomNameStringsLocalizable.h; sourceTree = ""; }; 32BA86AD2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomNameDefaultStringLocalizations.h; sourceTree = ""; }; 32BA86AE2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRoomNameDefaultStringLocalizations.m; sourceTree = ""; }; + 32BBAE602178957600D85F46 /* MXKeyBackupData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupData.h; sourceTree = ""; }; + 32BBAE612178957600D85F46 /* MXKeyBackupData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupData.m; sourceTree = ""; }; 32BD34BC1E84134A006EDC0D /* MatrixSDKTestsE2EData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatrixSDKTestsE2EData.h; sourceTree = ""; }; 32BD34BD1E84134A006EDC0D /* MatrixSDKTestsE2EData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MatrixSDKTestsE2EData.m; sourceTree = ""; }; 32BED28E1B00A23F00E668FE /* MXCallStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallStack.h; sourceTree = ""; }; @@ -920,6 +924,8 @@ children = ( 32935F58216FA0BA00A1BC24 /* MXKeyBackupVersion.h */, 32935F59216FA0BA00A1BC24 /* MXKeyBackupVersion.m */, + 32BBAE602178957600D85F46 /* MXKeyBackupData.h */, + 32BBAE612178957600D85F46 /* MXKeyBackupData.m */, 32935F5C216FA19B00A1BC24 /* MXMegolmBackupAuthData.h */, 32935F5D216FA19B00A1BC24 /* MXMegolmBackupAuthData.m */, ); @@ -1295,6 +1301,7 @@ 92634B821EF2E3C400DB9F60 /* MXCallKitConfiguration.h in Headers */, 32618E7120ED2DF500E1D2EA /* MXFilterJSONModel.h in Headers */, 322360521A8E610500A3CA81 /* MXPushRuleDisplayNameCondtionChecker.h in Headers */, + 32BBAE622178957600D85F46 /* MXKeyBackupData.h in Headers */, F03EF5001DF014D9009DF592 /* MXMediaManager.h in Headers */, 3245A7521AF7B2930001D8A7 /* MXCallManager.h in Headers */, C6FE1EF01E65C4F7008587E4 /* MXAnalyticsDelegate.h in Headers */, @@ -1641,6 +1648,7 @@ 32935F5F216FA19B00A1BC24 /* MXMegolmBackupAuthData.m in Sources */, C6F9357C1E5B39CA00FC34BF /* MXRestClient.swift in Sources */, 322A51B71D9AB15900C8536D /* MXCrypto.m in Sources */, + 32BBAE632178957600D85F46 /* MXKeyBackupData.m in Sources */, B17982FB2119E4A2001FD722 /* MXRoomPredecessorInfo.m in Sources */, 32DC15D11A8CF7AE006F9AD3 /* MXNotificationCenter.m in Sources */, 32637ED51E5B00400011E20D /* MXDeviceList.m in Sources */, diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.h b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.h new file mode 100644 index 0000000000..4f8820204f --- /dev/null +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.h @@ -0,0 +1,68 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MXJSONModel.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Backup data for one key. + */ +@interface MXKeyBackupData : MXJSONModel + +/** + The index of the first message in the session that the key can decrypt. + */ +@property (nonatomic) NSInteger firstMessageIndex; + +/** + The number of times this key has been forwarded. + */ +@property (nonatomic) NSInteger forwardedCount; + +/** + Whether the device backing up the key has verified the device that the key is from. + */ +@property (nonatomic) BOOL verified; + +/** + Algorithm-dependent data. + */ +@property (nonatomic) NSDictionary *sessionData; + +@end + +/** + Backup data for several keys within a room. + */ +@interface MXRoomKeysBackupData : MXJSONModel + +@property (nonatomic) NSDictionary *sessions; + +@end + +/** + Backup data for several keys in several rooms. + */ +@interface MXKeysBackupData : MXJSONModel + +@property (nonatomic) NSDictionary *rooms; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.m b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.m new file mode 100644 index 0000000000..09ab02418e --- /dev/null +++ b/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.m @@ -0,0 +1,142 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXKeyBackupData.h" + +@implementation MXKeyBackupData + +#pragma mark - MXJSONModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXKeyBackupData *keyBackupData = [[MXKeyBackupData alloc] init]; + if (keyBackupData) + { + MXJSONModelSetInteger(keyBackupData.firstMessageIndex, JSONDictionary[@"first_message_index"]); + MXJSONModelSetInteger(keyBackupData.forwardedCount, JSONDictionary[@"forwarded_count"]); + MXJSONModelSetBoolean(keyBackupData.verified, JSONDictionary[@"is_verified"]); + MXJSONModelSetDictionary(keyBackupData.sessionData, JSONDictionary[@"session_data"]); + } + return keyBackupData; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + + JSONDictionary[@"first_message_index"] = @(_firstMessageIndex); + JSONDictionary[@"forwarded_count"] = @(_forwardedCount); + JSONDictionary[@"is_verified"] = @(_verified); + JSONDictionary[@"session_data"] = _sessionData; + + return JSONDictionary; +} + +@end + + +@implementation MXRoomKeysBackupData + +#pragma mark - MXJSONModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXRoomKeysBackupData *roomKeysBackupData = [[MXRoomKeysBackupData alloc] init]; + if (roomKeysBackupData) + { + NSDictionary *sessions; + MXJSONModelSetDictionary(sessions, JSONDictionary[@"sessions"]); + + if (sessions) + { + NSMutableDictionary *mutableSessions = [[NSMutableDictionary alloc] initWithCapacity:sessions.count]; + for (NSString *sessionId in sessions) + { + MXKeyBackupData *keyBackupData; + MXJSONModelSetMXJSONModel(keyBackupData, MXKeyBackupData, sessions[sessionId]); + if (keyBackupData) + { + mutableSessions[sessionId] = keyBackupData; + } + } + roomKeysBackupData.sessions = mutableSessions; + } + } + return roomKeysBackupData; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *sessions = [NSMutableDictionary dictionary]; + + for (NSString *sessionId in _sessions) + { + sessions[sessionId] = _sessions[sessionId].JSONDictionary; + } + + return @{ + @"sessions" : sessions + }; +} + +@end + + +@implementation MXKeysBackupData + +#pragma mark - MXJSONModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXKeysBackupData *keysBackupData = [[MXKeysBackupData alloc] init]; + if (keysBackupData) + { + NSDictionary *rooms; + MXJSONModelSetDictionary(rooms, JSONDictionary[@"rooms"]); + + if (rooms) + { + NSMutableDictionary *mutableRooms = [[NSMutableDictionary alloc] initWithCapacity:rooms.count]; + for (NSString *roomId in rooms) + { + MXRoomKeysBackupData *roomKeysBackupData; + MXJSONModelSetMXJSONModel(roomKeysBackupData, MXRoomKeysBackupData, rooms[roomId]); + if (roomKeysBackupData) + { + mutableRooms[roomId] = roomKeysBackupData; + } + } + keysBackupData.rooms = mutableRooms; + } + } + return keysBackupData; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *rooms = [NSMutableDictionary dictionary]; + + for (NSString *roomId in _rooms) + { + rooms[roomId] = _rooms[roomId].JSONDictionary; + } + + return @{ + @"rooms" : rooms + }; +} + +@end diff --git a/MatrixSDK/JSONModels/MXJSONModels.h b/MatrixSDK/JSONModels/MXJSONModels.h index 9a84a43062..7eb184143c 100644 --- a/MatrixSDK/JSONModels/MXJSONModels.h +++ b/MatrixSDK/JSONModels/MXJSONModels.h @@ -20,6 +20,7 @@ #import "MXJSONModel.h" #import "MXUsersDevicesMap.h" #import "MXKeyBackupVersion.h" +#import "MXKeyBackupData.h" #import "MXMegolmBackupAuthData.h" @class MXEvent, MXDeviceInfo, MXKey, MXUser; diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index a133fa6911..a078ae0fc4 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -2081,6 +2081,111 @@ typedef enum : NSUInteger - (MXHTTPOperation*)keyBackupVersion:(void (^)(MXKeyBackupVersion *keyBackupVersion))success failure:(void (^)(NSError *error))failure; +/** + Back up a session key to the homeserver. + + @param keyBackupData the key to backup. + @param roomId the id of the room that the keys are for. + @param sessionId the id of the session that the keys are. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)sendKeyBackup:(MXKeyBackupData*)keyBackupData + room:(NSString*)roomId + session:(NSString*)sessionId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure; + +/** + Back up keys in a room to the homeserver. + + @param roomKeysBackupData keys to backup. + @param roomId the id of the room that the keys are for. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)sendRoomKeysBackup:(MXRoomKeysBackupData*)roomKeysBackupData + room:(NSString*)roomId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure; + +/** + Back up keys to the homeserver. + + @param keysBackupData keys to backup. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)sendKeysBackup:(MXKeysBackupData*)keysBackupData + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure; + +/** + Retrieve the backup for a session key from the homeserver. + + @param roomId the id of the room that the keys are for. + @param sessionId the id of the session that the keys are. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)keyBackup:(NSString*)roomId + session:(NSString*)sessionId + version:(NSString*)version + success:(void (^)(MXKeyBackupData *keyBackupData))success + failure:(void (^)(NSError *error))failure; + +/** + Retrieve the backup for all keys in a room from the homeserver. + + @param roomKeysBackupData keys to backup. + @param roomId the id of the room that the keys are for. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)roomKeysBackup:(NSString*)roomId + version:(NSString*)version + success:(void (^)(MXRoomKeysBackupData *roomKeysBackupData))success + failure:(void (^)(NSError *error))failure; + +/** + Retrive all keys backup from the homeserver. + + @param keysBackupData keys to backup. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)keysBackup:(NSString*)version + success:(void (^)(MXKeysBackupData *keysBackupData))success + failure:(void (^)(NSError *error))failure; + + #pragma mark - Direct-to-device messaging /** Send an event to a specific list of devices diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index 511b2d0394..a312f197bd 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -3871,6 +3871,220 @@ - (MXHTTPOperation*)keyBackupVersion:(void (^)(MXKeyBackupVersion *keyBackupVers }]; } +- (MXHTTPOperation*)sendKeyBackup:(MXKeyBackupData*)keyBackupData + room:(NSString*)roomId + session:(NSString*)sessionId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:roomId session:sessionId version:version]; + if (!path || !keyBackupData || !roomId || !sessionId) + { + NSLog(@"[MXRestClient] sendKeyBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + return [self sendBackup:keyBackupData.JSONDictionary path:path success:success failure:failure]; +} + +- (MXHTTPOperation*)sendRoomKeysBackup:(MXRoomKeysBackupData*)roomKeysBackupData + room:(NSString*)roomId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:roomId session:nil version:version]; + if (!path || !roomKeysBackupData || !roomId) + { + NSLog(@"[MXRestClient] sendRoomKeysBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + return [self sendBackup:roomKeysBackupData.JSONDictionary path:path success:success failure:failure]; +} + +- (MXHTTPOperation*)sendKeysBackup:(MXKeysBackupData*)keysBackupData + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:nil session:nil version:nil]; + if (!path || !keysBackupData) + { + NSLog(@"[MXRestClient] sendKeysBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + return [self sendBackup:keysBackupData.JSONDictionary path:path success:success failure:failure]; +} + +- (MXHTTPOperation*)sendBackup:(NSDictionary*)backupData + path:(NSString*)path + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure +{ + MXWeakify(self); + return [httpClient requestWithMethod:@"PUT" + path:path + parameters:backupData + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + [self dispatchProcessing:nil + andCompletion:^{ + success(); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)keyBackup:(NSString*)roomId + session:(NSString*)sessionId + version:(NSString*)version + success:(void (^)(MXKeyBackupData *keyBackupData))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:roomId session:sessionId version:version]; + if (!path || !roomId || !sessionId) + { + NSLog(@"[MXRestClient] keyBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + MXWeakify(self); + return [httpClient requestWithMethod:@"GET" + path:path + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + __block MXKeyBackupData *keyBackupData; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(keyBackupData, MXKeyBackupData, JSONResponse); + } andCompletion:^{ + success(keyBackupData); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)roomKeysBackup:(NSString*)roomId + version:(NSString*)version + success:(void (^)(MXRoomKeysBackupData *roomKeysBackupData))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:roomId session:nil version:version]; + if (!path || !roomId) + { + NSLog(@"[MXRestClient] roomKeysBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + MXWeakify(self); + return [httpClient requestWithMethod:@"GET" + path:path + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + __block MXRoomKeysBackupData *roomKeysBackupData; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(roomKeysBackupData, MXRoomKeysBackupData, JSONResponse); + } andCompletion:^{ + success(roomKeysBackupData); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)keysBackup:(NSString*)version + success:(void (^)(MXKeysBackupData *keysBackupData))success + failure:(void (^)(NSError *error))failure; +{ + NSString *path = [self keyBackupPath:nil session:nil version:version]; + if (!path) + { + NSLog(@"[MXRestClient] keysBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + MXWeakify(self); + return [httpClient requestWithMethod:@"GET" + path:path + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + __block MXKeysBackupData *keysBackupData; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(keysBackupData, MXKeysBackupData, JSONResponse); + } andCompletion:^{ + success(keysBackupData); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (NSString*)keyBackupPath:(NSString*)roomId session:(NSString*)sessionId version:(NSString*)version +{ + if (!version) + { + return nil; + } + + NSMutableString *path = [NSMutableString stringWithFormat:@"%@/room_keys/keys", kMXAPIPrefixPathUnstable]; + + if (sessionId) + { + if (!roomId) + { + NSLog(@"[MXRestClient] keyBackupPath: ERROR: Null version"); + return nil; + } + [path appendString:@"/"]; + [path appendString:[roomId stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + [path appendString:@"/"]; + [path appendString:[sessionId stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + } + else if (roomId) + { + [path appendString:@"/"]; + [path appendString:[roomId stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + } + + [path appendString:@"?version="]; + [path appendString:version]; + + return path; +} + #pragma mark - Direct-to-device messaging - (MXHTTPOperation*)sendToDevice:(NSString*)eventType contentMap:(MXUsersDevicesMap*)contentMap diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 6c095ac0ba..ee8b53c7f6 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -48,26 +48,35 @@ - (void)tearDown [super tearDown]; } +/** + - Create a backup version on the server + - Get the current version from the server + - Check they match + */ - (void)testRESTCreateKeyBackupVersion { [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { - MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion modelFromJSON:@{ - @"algorithm": kMXCryptoMegolmBackupAlgorithm, - @"auth_data": @{ - @"public_key": @"abcdefg", - @"signatures": @{ - @"something": @{ - @"ed25519:something": @"hijklmnop" - } - } - } - }]; + // - Create a backup version on the server + MXKeyBackupVersion *keyBackupVersion = + [MXKeyBackupVersion modelFromJSON:@{ + @"algorithm": kMXCryptoMegolmBackupAlgorithm, + @"auth_data": @{ + @"public_key": @"abcdefg", + @"signatures": @{ + @"something": @{ + @"ed25519:something": @"hijklmnop" + } + } + } + }]; [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + // - Get the current version from the server [aliceRestClient keyBackupVersion:^(MXKeyBackupVersion *keyBackupVersion2) { + // - Check they match XCTAssertNotNil(keyBackupVersion2); XCTAssertEqualObjects(keyBackupVersion2.version, version); XCTAssertEqualObjects(keyBackupVersion2.algorithm, keyBackupVersion.algorithm); @@ -86,6 +95,72 @@ - (void)testRESTCreateKeyBackupVersion }]; } +/** + - Create a backup version on the server + - Make a backup + - Get the backup back + -> Check they match + */ +- (void)testRESTBackupKeys +{ + [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { + + // - Create a backup version on the server + MXKeyBackupVersion *keyBackupVersion = + [MXKeyBackupVersion modelFromJSON:@{ + @"algorithm": kMXCryptoMegolmBackupAlgorithm, + @"auth_data": @{ + @"public_key": @"abcdefg", + @"signatures": @{ + @"something": @{ + @"ed25519:something": @"hijklmnop" + } + } + } + }]; + + [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + + //- Make a backup + MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; + keyBackupData.firstMessageIndex = 1; + keyBackupData.forwardedCount = 2; + keyBackupData.verified = YES; + keyBackupData.sessionData = @{ + @"key": @"value" + }; + + NSString *roomId = @"!aRoomId:matrix.org"; + NSString *sessionId = @"ASession"; + + [aliceRestClient sendKeyBackup:keyBackupData room:roomId session:sessionId version:version success:^{ + + // - Get the backup back + [aliceRestClient keysBackup:version success:^(MXKeysBackupData *keysBackupData) { + + // -> Check they match + MXKeyBackupData *keyBackupData2 = keysBackupData.rooms[roomId].sessions[sessionId]; + XCTAssertNotNil(keyBackupData2); + XCTAssertEqualObjects(keyBackupData2.JSONDictionary, keyBackupData.JSONDictionary); + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} @end #pragma clang diagnostic pop From 1d013c58d601c282da7000f50041da245140f074 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 18 Oct 2018 17:22:13 +0200 Subject: [PATCH 11/58] Keys backup: Add delete APIs --- MatrixSDK/MXRestClient.h | 52 ++++++++++++++- MatrixSDK/MXRestClient.m | 99 ++++++++++++++++++++++++++++ MatrixSDKTests/MXCryptoBackupTests.m | 74 ++++++++++++++++++++- 3 files changed, 223 insertions(+), 2 deletions(-) diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index a078ae0fc4..5b54eaac60 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -2171,7 +2171,7 @@ typedef enum : NSUInteger failure:(void (^)(NSError *error))failure; /** - Retrive all keys backup from the homeserver. + Retrieve all keys backup from the homeserver. @param keysBackupData keys to backup. @param version the backup version. @@ -2185,6 +2185,56 @@ typedef enum : NSUInteger success:(void (^)(MXKeysBackupData *keysBackupData))success failure:(void (^)(NSError *error))failure; +/** + Delete the backup for a session key from the homeserver. + + @param roomId the id of the room that the keys are for. + @param sessionId the id of the session that the keys are. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)deleteKeyFromBackup:(NSString*)roomId + session:(NSString*)sessionId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure; + +/** + Delete the backup for all keys in a room from the homeserver. + + @param roomKeysBackupData keys to backup. + @param roomId the id of the room that the keys are for. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)deleteKeysInRoomFromBackup:(NSString*)roomId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure; + +/** + Delete all keys backup from the homeserver. + + @param keysBackupData keys to backup. + @param version the backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)deleteKeysFromBackup:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure; + #pragma mark - Direct-to-device messaging /** diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index a312f197bd..cb29a3ba1b 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -4052,6 +4052,105 @@ - (MXHTTPOperation*)keysBackup:(NSString*)version }]; } +- (MXHTTPOperation*)deleteKeyFromBackup:(NSString*)roomId + session:(NSString*)sessionId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:roomId session:sessionId version:version]; + if (!path || !roomId || !sessionId) + { + NSLog(@"[MXRestClient] deleteKeyFromBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + MXWeakify(self); + return [httpClient requestWithMethod:@"DELETE" + path:path + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + [self dispatchProcessing:nil + andCompletion:^{ + success(); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)deleteKeysInRoomFromBackup:(NSString*)roomId + version:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:roomId session:nil version:version]; + if (!path || !roomId) + { + NSLog(@"[MXRestClient] deleteKeysInRoomFromBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + MXWeakify(self); + return [httpClient requestWithMethod:@"DELETE" + path:path + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + [self dispatchProcessing:nil + andCompletion:^{ + success(); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + +- (MXHTTPOperation*)deleteKeysFromBackup:(NSString*)version + success:(void (^)(void))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [self keyBackupPath:nil session:nil version:version]; + if (!path) + { + NSLog(@"[MXRestClient] keysBackup: ERROR: Bad parameters"); + [self dispatchFailure:nil inBlock:failure]; + return nil; + } + + MXWeakify(self); + return [httpClient requestWithMethod:@"DELETE" + path:path + parameters:nil + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + [self dispatchProcessing:nil + andCompletion:^{ + success(); + }]; + } + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + - (NSString*)keyBackupPath:(NSString*)roomId session:(NSString*)sessionId version:(NSString*)version { if (!version) diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index ee8b53c7f6..3294c0f5e4 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -149,18 +149,90 @@ - (void)testRESTBackupKeys XCTFail(@"The request should not fail - NSError: %@", error); [expectation fulfill]; }]; - } failure:^(NSError *error) { XCTFail(@"The request should not fail - NSError: %@", error); [expectation fulfill]; }]; + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + +/** + - Create a backup version on the server + - Make a backup + - Delete it + - Get the backup back + -> Check it is now empty + */ +- (void)testRESTDeleteBackupKeys +{ + [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { + + // - Create a backup version on the server + MXKeyBackupVersion *keyBackupVersion = + [MXKeyBackupVersion modelFromJSON:@{ + @"algorithm": kMXCryptoMegolmBackupAlgorithm, + @"auth_data": @{ + @"public_key": @"abcdefg", + @"signatures": @{ + @"something": @{ + @"ed25519:something": @"hijklmnop" + } + } + } + }]; + [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + + //- Make a backup + MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; + keyBackupData.firstMessageIndex = 1; + keyBackupData.forwardedCount = 2; + keyBackupData.verified = YES; + keyBackupData.sessionData = @{ + @"key": @"value" + }; + + NSString *roomId = @"!aRoomId:matrix.org"; + NSString *sessionId = @"ASession"; + + [aliceRestClient sendKeyBackup:keyBackupData room:roomId session:sessionId version:version success:^{ + + // - Delete it + [aliceRestClient deleteKeyFromBackup:roomId session:sessionId version:version success:^{ + + // - Get the backup back + // TODO: The test currently fails because of https://github.com/matrix-org/synapse/issues/4056 + [aliceRestClient keysBackup:version success:^(MXKeysBackupData *keysBackupData) { + + // -> Check it is now empty + XCTAssertNotNil(keysBackupData); + XCTAssertEqual(keysBackupData.rooms.count, 0); + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; } failure:^(NSError *error) { XCTFail(@"The request should not fail - NSError: %@", error); [expectation fulfill]; }]; }]; } + @end #pragma clang diagnostic pop From d1893142d3c796d655db6fe9af8a0092ae24b93a Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 18 Oct 2018 18:16:27 +0200 Subject: [PATCH 12/58] Keys backup: Move everything to a dedicated /Crypto/Backup folder --- MatrixSDK.xcodeproj/project.pbxproj | 72 ++++++++++--------- .../Data}/MXKeyBackupData.h | 0 .../Data}/MXKeyBackupData.m | 0 .../Data}/MXKeyBackupVersion.h | 0 .../Data}/MXKeyBackupVersion.m | 0 .../Data}/MXMegolmBackupAuthData.h | 0 .../Data}/MXMegolmBackupAuthData.m | 0 7 files changed, 40 insertions(+), 32 deletions(-) rename MatrixSDK/Crypto/{Data/Backup => KeyBackup/Data}/MXKeyBackupData.h (100%) rename MatrixSDK/Crypto/{Data/Backup => KeyBackup/Data}/MXKeyBackupData.m (100%) rename MatrixSDK/Crypto/{Data/Backup => KeyBackup/Data}/MXKeyBackupVersion.h (100%) rename MatrixSDK/Crypto/{Data/Backup => KeyBackup/Data}/MXKeyBackupVersion.m (100%) rename MatrixSDK/Crypto/{Data/Backup => KeyBackup/Data}/MXMegolmBackupAuthData.h (100%) rename MatrixSDK/Crypto/{Data/Backup => KeyBackup/Data}/MXMegolmBackupAuthData.m (100%) diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index d5925342c9..648d5229ee 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -133,10 +133,6 @@ 328DDEC11A07E57E008C7DC8 /* MXJSONModelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */; }; 3291D4D41A68FFEB00C3BA41 /* MXFileRoomStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3291D4D21A68FFEB00C3BA41 /* MXFileRoomStore.h */; }; 3291D4D51A68FFEB00C3BA41 /* MXFileRoomStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3291D4D31A68FFEB00C3BA41 /* MXFileRoomStore.m */; }; - 32935F5A216FA0BA00A1BC24 /* MXKeyBackupVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 32935F58216FA0BA00A1BC24 /* MXKeyBackupVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32935F5B216FA0BA00A1BC24 /* MXKeyBackupVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F59216FA0BA00A1BC24 /* MXKeyBackupVersion.m */; }; - 32935F5E216FA19B00A1BC24 /* MXMegolmBackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32935F5C216FA19B00A1BC24 /* MXMegolmBackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32935F5F216FA19B00A1BC24 /* MXMegolmBackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F5D216FA19B00A1BC24 /* MXMegolmBackupAuthData.m */; }; 32935F61216FA49D00A1BC24 /* MXCryptoBackupTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32935F60216FA49D00A1BC24 /* MXCryptoBackupTests.m */; }; 3293C700214BBA4F009B3DDB /* MXPeekingRoomSummary.h in Headers */ = {isa = PBXBuildFile; fileRef = 3293C6FE214BBA4F009B3DDB /* MXPeekingRoomSummary.h */; }; 3293C701214BBA4F009B3DDB /* MXPeekingRoomSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 3293C6FF214BBA4F009B3DDB /* MXPeekingRoomSummary.m */; }; @@ -192,8 +188,12 @@ 32BA86AC21529E29008F277E /* MXRoomNameStringsLocalizable.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BA86AB21529AE3008F277E /* MXRoomNameStringsLocalizable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32BA86AF2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BA86AD2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h */; }; 32BA86B02152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BA86AE2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m */; }; - 32BBAE622178957600D85F46 /* MXKeyBackupData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE602178957600D85F46 /* MXKeyBackupData.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32BBAE632178957600D85F46 /* MXKeyBackupData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE612178957600D85F46 /* MXKeyBackupData.m */; }; + 32BBAE6C2178E99100D85F46 /* MXKeyBackupData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE662178E99100D85F46 /* MXKeyBackupData.m */; }; + 32BBAE6D2178E99100D85F46 /* MXKeyBackupVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE672178E99100D85F46 /* MXKeyBackupVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32BBAE6E2178E99100D85F46 /* MXMegolmBackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE682178E99100D85F46 /* MXMegolmBackupAuthData.m */; }; + 32BBAE6F2178E99100D85F46 /* MXKeyBackupVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */; }; + 32BBAE702178E99100D85F46 /* MXKeyBackupData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE6A2178E99100D85F46 /* MXKeyBackupData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32BBAE712178E99100D85F46 /* MXMegolmBackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE6B2178E99100D85F46 /* MXMegolmBackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32BD34BE1E84134A006EDC0D /* MatrixSDKTestsE2EData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BD34BD1E84134A006EDC0D /* MatrixSDKTestsE2EData.m */; }; 32BED28F1B00A23F00E668FE /* MXCallStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BED28E1B00A23F00E668FE /* MXCallStack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32C03CB62123076F00D92712 /* DirectRoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C03CB52123076F00D92712 /* DirectRoomTests.m */; }; @@ -448,10 +448,6 @@ 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXJSONModelTests.m; sourceTree = ""; }; 3291D4D21A68FFEB00C3BA41 /* MXFileRoomStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXFileRoomStore.h; sourceTree = ""; }; 3291D4D31A68FFEB00C3BA41 /* MXFileRoomStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXFileRoomStore.m; sourceTree = ""; }; - 32935F58216FA0BA00A1BC24 /* MXKeyBackupVersion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupVersion.h; sourceTree = ""; }; - 32935F59216FA0BA00A1BC24 /* MXKeyBackupVersion.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupVersion.m; sourceTree = ""; }; - 32935F5C216FA19B00A1BC24 /* MXMegolmBackupAuthData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXMegolmBackupAuthData.h; sourceTree = ""; }; - 32935F5D216FA19B00A1BC24 /* MXMegolmBackupAuthData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXMegolmBackupAuthData.m; sourceTree = ""; }; 32935F60216FA49D00A1BC24 /* MXCryptoBackupTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXCryptoBackupTests.m; sourceTree = ""; }; 3293C6FE214BBA4F009B3DDB /* MXPeekingRoomSummary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXPeekingRoomSummary.h; sourceTree = ""; }; 3293C6FF214BBA4F009B3DDB /* MXPeekingRoomSummary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXPeekingRoomSummary.m; sourceTree = ""; }; @@ -509,8 +505,12 @@ 32BA86AB21529AE3008F277E /* MXRoomNameStringsLocalizable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomNameStringsLocalizable.h; sourceTree = ""; }; 32BA86AD2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomNameDefaultStringLocalizations.h; sourceTree = ""; }; 32BA86AE2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRoomNameDefaultStringLocalizations.m; sourceTree = ""; }; - 32BBAE602178957600D85F46 /* MXKeyBackupData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupData.h; sourceTree = ""; }; - 32BBAE612178957600D85F46 /* MXKeyBackupData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupData.m; sourceTree = ""; }; + 32BBAE662178E99100D85F46 /* MXKeyBackupData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupData.m; sourceTree = ""; }; + 32BBAE672178E99100D85F46 /* MXKeyBackupVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupVersion.h; sourceTree = ""; }; + 32BBAE682178E99100D85F46 /* MXMegolmBackupAuthData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXMegolmBackupAuthData.m; sourceTree = ""; }; + 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupVersion.m; sourceTree = ""; }; + 32BBAE6A2178E99100D85F46 /* MXKeyBackupData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupData.h; sourceTree = ""; }; + 32BBAE6B2178E99100D85F46 /* MXMegolmBackupAuthData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXMegolmBackupAuthData.h; sourceTree = ""; }; 32BD34BC1E84134A006EDC0D /* MatrixSDKTestsE2EData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatrixSDKTestsE2EData.h; sourceTree = ""; }; 32BD34BD1E84134A006EDC0D /* MatrixSDKTestsE2EData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MatrixSDKTestsE2EData.m; sourceTree = ""; }; 32BED28E1B00A23F00E668FE /* MXCallStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallStack.h; sourceTree = ""; }; @@ -776,6 +776,7 @@ 324BE4651E3FADB1008D99D4 /* Utils */, 322A51C01D9AC8FE00C8536D /* Algorithms */, 32A1513B1DAF768D00400192 /* Data */, + 32BBAE642178E99100D85F46 /* KeyBackup */, 32FA10B21FA1C28100E54233 /* KeySharing */, 322A51B41D9AB15900C8536D /* MXCrypto.h */, 322A51B51D9AB15900C8536D /* MXCrypto.m */, @@ -919,19 +920,6 @@ path = Store; sourceTree = ""; }; - 32935F57216FA09B00A1BC24 /* Backup */ = { - isa = PBXGroup; - children = ( - 32935F58216FA0BA00A1BC24 /* MXKeyBackupVersion.h */, - 32935F59216FA0BA00A1BC24 /* MXKeyBackupVersion.m */, - 32BBAE602178957600D85F46 /* MXKeyBackupData.h */, - 32BBAE612178957600D85F46 /* MXKeyBackupData.m */, - 32935F5C216FA19B00A1BC24 /* MXMegolmBackupAuthData.h */, - 32935F5D216FA19B00A1BC24 /* MXMegolmBackupAuthData.m */, - ); - path = Backup; - sourceTree = ""; - }; 329571941B024D2B00ABB3BA /* Mocks */ = { isa = PBXGroup; children = ( @@ -957,7 +945,6 @@ 32A1513B1DAF768D00400192 /* Data */ = { isa = PBXGroup; children = ( - 32935F57216FA09B00A1BC24 /* Backup */, 3284A59C1DB7C00600A09972 /* Store */, 021AFBA02179E91800742B2C /* MXEncryptedContentFile.h */, 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */, @@ -1000,6 +987,27 @@ name = Lib; sourceTree = ""; }; + 32BBAE642178E99100D85F46 /* KeyBackup */ = { + isa = PBXGroup; + children = ( + 32BBAE652178E99100D85F46 /* Data */, + ); + path = KeyBackup; + sourceTree = ""; + }; + 32BBAE652178E99100D85F46 /* Data */ = { + isa = PBXGroup; + children = ( + 32BBAE6A2178E99100D85F46 /* MXKeyBackupData.h */, + 32BBAE662178E99100D85F46 /* MXKeyBackupData.m */, + 32BBAE672178E99100D85F46 /* MXKeyBackupVersion.h */, + 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */, + 32BBAE6B2178E99100D85F46 /* MXMegolmBackupAuthData.h */, + 32BBAE682178E99100D85F46 /* MXMegolmBackupAuthData.m */, + ); + path = Data; + sourceTree = ""; + }; 32C6F92319DD814400EA4E9C = { isa = PBXGroup; children = ( @@ -1285,7 +1293,6 @@ B17982F52119E4A2001FD722 /* MXRoomCreateContent.h in Headers */, 32DC15CF1A8CF7AE006F9AD3 /* MXPushRuleConditionChecker.h in Headers */, 32954019216385F100E300FC /* MXServerNoticeContent.h in Headers */, - 32935F5A216FA0BA00A1BC24 /* MXKeyBackupVersion.h in Headers */, 327187851DA7D0220071C818 /* MXOlmDecryption.h in Headers */, 32D7767D1A27860600FC4AA2 /* MXMemoryStore.h in Headers */, 32CE6FB81A409B1F00317F1E /* MXFileStoreMetaData.h in Headers */, @@ -1301,7 +1308,6 @@ 92634B821EF2E3C400DB9F60 /* MXCallKitConfiguration.h in Headers */, 32618E7120ED2DF500E1D2EA /* MXFilterJSONModel.h in Headers */, 322360521A8E610500A3CA81 /* MXPushRuleDisplayNameCondtionChecker.h in Headers */, - 32BBAE622178957600D85F46 /* MXKeyBackupData.h in Headers */, F03EF5001DF014D9009DF592 /* MXMediaManager.h in Headers */, 3245A7521AF7B2930001D8A7 /* MXCallManager.h in Headers */, C6FE1EF01E65C4F7008587E4 /* MXAnalyticsDelegate.h in Headers */, @@ -1344,6 +1350,7 @@ 3271877B1DA7CAA60071C818 /* MXEncrypting.h in Headers */, 32A31BC420D3FFB0005916C7 /* MXFilter.h in Headers */, 32A151481DAF7C0C00400192 /* MXKey.h in Headers */, + 32BBAE6D2178E99100D85F46 /* MXKeyBackupVersion.h in Headers */, 32A151461DAF7C0C00400192 /* MXDeviceInfo.h in Headers */, 32CAB1071A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.h in Headers */, C61A4CC41E5F38CB00442158 /* SwiftMatrixSDK.h in Headers */, @@ -1389,11 +1396,12 @@ 32DC15D01A8CF7AE006F9AD3 /* MXNotificationCenter.h in Headers */, F0173EAC1FCF0E8900B5F6A3 /* MXGroup.h in Headers */, 329FB17F1A0B665800A5E88E /* MXUser.h in Headers */, - 32935F5E216FA19B00A1BC24 /* MXMegolmBackupAuthData.h in Headers */, 320DFDE219DD99B60068622A /* MXError.h in Headers */, + 32BBAE702178E99100D85F46 /* MXKeyBackupData.h in Headers */, 327E37B61A974F75007F026F /* MXLogger.h in Headers */, 3256E3811DCB91EB003C9718 /* MXCryptoConstants.h in Headers */, 320DFDE619DD99B60068622A /* MXHTTPClient.h in Headers */, + 32BBAE712178E99100D85F46 /* MXMegolmBackupAuthData.h in Headers */, 320DFDDB19DD99B60068622A /* MXRoom.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1553,9 +1561,9 @@ 320BBF411D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m in Sources */, 3259CD541DF860C300186944 /* MXRealmCryptoStore.m in Sources */, 321B41401E09937E009EEEC7 /* MXRoomSummary.m in Sources */, + 32BBAE6E2178E99100D85F46 /* MXMegolmBackupAuthData.m in Sources */, 32CAB1081A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */, 3245A7511AF7B2930001D8A7 /* MXCall.m in Sources */, - 32935F5B216FA0BA00A1BC24 /* MXKeyBackupVersion.m in Sources */, 327F8DB31C6112BA00581CA3 /* MXRoomThirdPartyInvite.m in Sources */, B17982FC2119E4A2001FD722 /* MXRoomPowerLevels.m in Sources */, 32C235731F827F3800E38FC5 /* MXRoomOperation.m in Sources */, @@ -1568,6 +1576,7 @@ C6D5D60A1E4FA74000706C0F /* MXEnumConstants.swift in Sources */, 3256E3821DCB91EB003C9718 /* MXCryptoConstants.m in Sources */, B18D18C22152B8E4003677D1 /* MXRoomMember.swift in Sources */, + 32BBAE6F2178E99100D85F46 /* MXKeyBackupVersion.m in Sources */, 326056861C76FDF2009D44AD /* MXEventTimeline.m in Sources */, 32A1513A1DAD292400400192 /* MXMegolmEncryption.m in Sources */, 32F945F71FAB83D900622468 /* MXIncomingRoomKeyRequest.m in Sources */, @@ -1602,6 +1611,7 @@ 323E0C5C1A306D7A00A31D73 /* MXEvent.m in Sources */, F03EF5011DF014D9009DF592 /* MXMediaManager.m in Sources */, 32CAB10C1A925B41008C5BB9 /* MXHTTPOperation.m in Sources */, + 32BBAE6C2178E99100D85F46 /* MXKeyBackupData.m in Sources */, 3281E8BA19E42DFE00976E1A /* MXJSONModels.m in Sources */, 3245A7531AF7B2930001D8A7 /* MXCallManager.m in Sources */, 32E226A71D06AC9F00E6CA54 /* MXPeekingRoom.m in Sources */, @@ -1645,10 +1655,8 @@ 3220093919EFA4C9008DE41D /* MXEventListener.m in Sources */, C6481AF21F1678A9000DB8A0 /* MXSessionEventListener.swift in Sources */, 32A1514F1DAF897600400192 /* MXOlmSessionResult.m in Sources */, - 32935F5F216FA19B00A1BC24 /* MXMegolmBackupAuthData.m in Sources */, C6F9357C1E5B39CA00FC34BF /* MXRestClient.swift in Sources */, 322A51B71D9AB15900C8536D /* MXCrypto.m in Sources */, - 32BBAE632178957600D85F46 /* MXKeyBackupData.m in Sources */, B17982FB2119E4A2001FD722 /* MXRoomPredecessorInfo.m in Sources */, 32DC15D11A8CF7AE006F9AD3 /* MXNotificationCenter.m in Sources */, 32637ED51E5B00400011E20D /* MXDeviceList.m in Sources */, diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.h b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.h similarity index 100% rename from MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.h rename to MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.h diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.m b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.m similarity index 100% rename from MatrixSDK/Crypto/Data/Backup/MXKeyBackupData.m rename to MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.m diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.h similarity index 100% rename from MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.h rename to MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.h diff --git a/MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.m similarity index 100% rename from MatrixSDK/Crypto/Data/Backup/MXKeyBackupVersion.m rename to MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.m diff --git a/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.h b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.h similarity index 100% rename from MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.h rename to MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.h diff --git a/MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.m b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m similarity index 100% rename from MatrixSDK/Crypto/Data/Backup/MXMegolmBackupAuthData.m rename to MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m From 68d5f7ab6b103dd950c5814ca9330600a8586764 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 22 Oct 2018 09:20:46 +0200 Subject: [PATCH 13/58] Keys backup: Add MXCryptoStore APIs. To implement them, it required to add a primary key to MXOlmInboundGroupSession to MXRealmCryptoStore. Hopefully, this will make decrypting a bit quicker too. --- MatrixSDK/Crypto/Data/Store/MXCryptoStore.h | 23 ++++ .../MXRealmCryptoStore/MXRealmCryptoStore.m | 108 ++++++++++++++++-- MatrixSDKTests/MXCryptoBackupTests.m | 32 ++++++ 3 files changed, 153 insertions(+), 10 deletions(-) diff --git a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h index d66a25a26a..19d8a531de 100644 --- a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h +++ b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h @@ -215,6 +215,29 @@ - (NSArray *)inboundGroupSessions; +#pragma mark - Key backup +/** + Mark all inbound group sessions as not backed up. + */ +- (void)resetBackupMarkers; + +/** + Mark an inbound group session as backed up on the user homeserver. + + @param sessionId the session identifier. + @param senderKey the base64-encoded curve25519 key of the sender. + */ +- (void)markBackupDoneForInboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey; + +/** + Retrieve inbound group sessions that are not yet backed up. + + @param limit the maximum number of sessions to return. + @return an array of non backed up inbound group sessions. + */ +- (NSArray*)inboundGroupSessionsToBackup:(NSUInteger)limit; + + #pragma mark - Key sharing - Outgoing key requests /** diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index 69b6cac4bd..f0b8db9cab 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -23,7 +23,7 @@ #import "MXSession.h" #import "MXTools.h" -NSUInteger const kMXRealmCryptoStoreVersion = 6; +NSUInteger const kMXRealmCryptoStoreVersion = 7; static NSString *const kMXRealmCryptoStoreFolder = @"MXRealmCryptoStore"; @@ -95,9 +95,25 @@ @interface MXRealmOlmInboundGroupSession : RLMObject @property NSString *sessionId; @property NSString *senderKey; @property NSData *olmInboundGroupSessionData; + +// A primary key is required to update `backedUp`. +// Do our combined primary key ourselves as it is not supported by Realm. +@property NSString *sessionIdSenderKer; + +// Indicate if the key has been backed up to the homeserver +@property BOOL backedUp; @end @implementation MXRealmOlmInboundGroupSession ++ (NSString *)primaryKey +{ + return @"sessionIdSenderKer"; +} + ++ (NSString *)primaryKeyWithSessionId:(NSString*)sessionId senderKey:(NSString*)senderKey +{ + return [NSString stringWithFormat:@"%@|%@", sessionId, senderKey]; +} @end RLM_ARRAY_TYPE(MXRealmOlmInboundGroupSession) @@ -627,7 +643,9 @@ - (void)storeInboundGroupSession:(MXOlmInboundGroupSession*)session RLMRealm *realm = self.realm; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionId = %@ AND senderKey = %@", session.session.sessionIdentifier, session.senderKey].firstObject; + NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier + senderKey:session.senderKey]; + MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionIdSenderKer = %@", sessionIdSenderKer].firstObject; if (realmSession) { // Update the existing one @@ -639,9 +657,12 @@ - (void)storeInboundGroupSession:(MXOlmInboundGroupSession*)session { // Create it isNew = YES; + NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier + senderKey:session.senderKey]; realmSession = [[MXRealmOlmInboundGroupSession alloc] initWithValue:@{ @"sessionId": session.session.sessionIdentifier, @"senderKey": session.senderKey, + @"sessionIdSenderKer": sessionIdSenderKer, @"olmInboundGroupSessionData": [NSKeyedArchiver archivedDataWithRootObject:session] }]; @@ -656,7 +677,9 @@ - (void)storeInboundGroupSession:(MXOlmInboundGroupSession*)session - (MXOlmInboundGroupSession*)inboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey { MXOlmInboundGroupSession *session; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:self.realm where:@"sessionId = %@ AND senderKey = %@", sessionId, senderKey].firstObject; + NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId + senderKey:senderKey]; + MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:self.realm where:@"sessionIdSenderKer = %@", sessionIdSenderKer].firstObject; NSLog(@"[MXRealmCryptoStore] inboundGroupSessionWithId: %@ -> %@", sessionId, realmSession ? @"found" : @"not found"); @@ -696,6 +719,63 @@ - (void)removeInboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSStr }]; } + +#pragma mark - Key backup + +- (void)resetBackupMarkers +{ + RLMRealm *realm = self.realm; + + RLMResults *realmSessions = [MXRealmOlmInboundGroupSession allObjectsInRealm:realm]; + + [realm transactionWithBlock:^{ + for (MXRealmOlmInboundGroupSession *realmSession in realmSessions) + { + realmSession.backedUp = NO; + } + + [realm addOrUpdateObjects:realmSessions]; + }]; +} + +- (void)markBackupDoneForInboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey +{ + RLMRealm *realm = self.realm; + + NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId + senderKey:senderKey]; + MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionIdSenderKer = %@", sessionIdSenderKer].firstObject; + + [realm transactionWithBlock:^{ + realmSession.backedUp = @(YES); + + [realm addOrUpdateObject:realmSession]; + }]; +} + +- (NSArray*)inboundGroupSessionsToBackup:(NSUInteger)limit +{ + NSMutableArray *sessions = [NSMutableArray new]; + + RLMRealm *realm = self.realm; + + RLMResults *realmSessions = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"backedUp = NO"]; + + for (MXRealmOlmInboundGroupSession *realmSession in realmSessions) + { + MXOlmInboundGroupSession *session = [NSKeyedUnarchiver unarchiveObjectWithData:realmSession.olmInboundGroupSessionData]; + [sessions addObject:session]; + + if (sessions.count >= limit) + { + break; + } + } + + return sessions; +} + + #pragma mark - Key sharing - Outgoing key requests - (MXOutgoingRoomKeyRequest*)outgoingRoomKeyRequestWithRequestBody:(NSDictionary *)requestBody @@ -1020,23 +1100,31 @@ + (RLMRealm*)realmForUser:(NSString*)userId } case 2: - { NSLog(@"[MXRealmCryptoStore] Migration from schema #2 -> #3: Nothing to do (add MXRealmOlmAccount.deviceSyncToken)"); - } case 3: - { NSLog(@"[MXRealmCryptoStore] Migration from schema #3 -> #4: Nothing to do (add MXRealmOlmAccount.globalBlacklistUnverifiedDevices & MXRealmRoomAlgortithm.blacklistUnverifiedDevices)"); - } case 4: - { NSLog(@"[MXRealmCryptoStore] Migration from schema #4 -> #5: Nothing to do (add deviceTrackingStatusData)"); - } case 5: - { NSLog(@"[MXRealmCryptoStore] Migration from schema #5 -> #6: Nothing to do (remove MXRealmOlmAccount.deviceAnnounced)"); + + case 6: + { + NSLog(@"[MXRealmCryptoStore] Migration from schema #6 -> #7"); + + // We need to update the db because a sessionId property has been added MXRealmOlmSession + // to ensure uniqueness + NSLog(@" Add sessionIdSenderKer, a combined primary key, to all MXRealmOlmInboundGroupSession objects"); + [migration enumerateObjects:MXRealmOlmInboundGroupSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { + + newObject[@"sessionIdSenderKer"] = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:oldObject[@"sessionId"] + senderKey:oldObject[@"senderKey"]]; + }]; + + NSLog(@"[MXRealmCryptoStore] Migration from schema #6 -> #7 completed"); } } } diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 3294c0f5e4..16612a2005 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -19,6 +19,8 @@ #import "MatrixSDKTestsData.h" #import "MatrixSDKTestsE2EData.h" +#import "MXCrypto_Private.h" + // Do not bother with retain cycles warnings in tests #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" @@ -233,6 +235,36 @@ - (void)testRESTDeleteBackupKeys }]; } + +/** +- From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have non backed up keys +- Check backup keys after having marked one as backuped +- Reset keys backup markers +*/ +- (void)testBackupStore +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + // - From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have non backed up keys + NSArray *sessions = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; + NSUInteger sessionsCount = sessions.count; + XCTAssertGreaterThan(sessionsCount, 0); + + // - Check backup keys after having marked one as backuped + MXOlmInboundGroupSession *session = sessions.firstObject; + [aliceSession.crypto.store markBackupDoneForInboundGroupSessionWithId:session.session.sessionIdentifier andSenderKey:session.senderKey]; + sessions = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; + XCTAssertEqual(sessions.count, sessionsCount - 1); + + // - Reset keys backup markers + [aliceSession.crypto.store resetBackupMarkers]; + sessions = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; + XCTAssertEqual(sessions.count, sessionsCount); + + [expectation fulfill]; + }]; +} + @end #pragma clang diagnostic pop From 881d5197eb633a6b4ab0d5aa973eff3a31b5c6d7 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 23 Oct 2018 12:04:35 +0200 Subject: [PATCH 14/58] MXTools: Add addWhiteSpacesToString method --- MatrixSDK/Utils/MXTools.h | 9 +++++++++ MatrixSDK/Utils/MXTools.m | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/MatrixSDK/Utils/MXTools.h b/MatrixSDK/Utils/MXTools.h index 35ac769d9e..d646381704 100644 --- a/MatrixSDK/Utils/MXTools.h +++ b/MatrixSDK/Utils/MXTools.h @@ -60,6 +60,15 @@ */ + (NSString*)stripNewlineCharacters:(NSString *)inputString; +/** + Add a white space every given number of characters. + + @param inputString the original string. + @param characters number of characters between each white space. + @return a string with white spaces. + */ ++ (NSString*)addWhiteSpacesToString:(NSString *)inputString every:(NSUInteger)characters; + #pragma mark - Strings kinds check diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index d3890c7292..de3f66cba2 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -271,6 +271,29 @@ + (NSString*)stripNewlineCharacters:(NSString *)inputString return string; } ++ (NSString*)addWhiteSpacesToString:(NSString *)inputString every:(NSUInteger)characters +{ + NSMutableString *whiteSpacedString = [NSMutableString new]; + for (int i = 0; i < inputString.length / characters + 1; i++) + { + NSUInteger fromIndex = i * characters; + NSUInteger len = inputString.length - fromIndex; + if (len > characters) + { + len = characters; + } + + NSString *whiteFormat = @"%@ "; + if (fromIndex + characters >= inputString.length) + { + whiteFormat = @"%@"; + } + [whiteSpacedString appendFormat:whiteFormat, [inputString substringWithRange:NSMakeRange(fromIndex, len)]]; + } + + return whiteSpacedString; +} + #pragma mark - String kinds check From c3efcb24a09ebe5a668d447074fc1022ccfd7451 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 23 Oct 2018 12:09:04 +0200 Subject: [PATCH 15/58] Keys backup: Add MXRecoryKey to manage Base58 representation of the private backup key. We use https://github.com/caobo56/CBBase58, a pod that exposes only the Base58 part of https://github.com/oleganza/CoreBitcoin. --- MatrixSDK.podspec | 1 + MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h | 62 +++++++++++ MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m | 119 +++++++++++++++++++++ MatrixSDKTests/MXCryptoBackupTests.m | 36 +++++++ Podfile | 1 + SwiftMatrixSDK.podspec | 1 + 6 files changed, 220 insertions(+) create mode 100644 MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h create mode 100644 MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m diff --git a/MatrixSDK.podspec b/MatrixSDK.podspec index 7c8f226ed5..5fa86f5394 100644 --- a/MatrixSDK.podspec +++ b/MatrixSDK.podspec @@ -37,6 +37,7 @@ Pod::Spec.new do |s| # Requirements for e2e encryption ss.dependency 'OLMKit', '~> 3.0.0' ss.dependency 'Realm', '~> 3.11.1' + ss.dependency 'CBBase58', '~> 0.9.1' end s.subspec 'JingleCallStack' do |ss| diff --git a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h new file mode 100644 index 0000000000..6590b6ac88 --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h @@ -0,0 +1,62 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + + +/** + The error domain for this class. + */ +FOUNDATION_EXPORT NSString *const MXRecoveryKeyErrorDomain; + +/** + All associated error code. + */ +typedef enum : NSUInteger +{ + MXRecoveryKeyErrorParityCode = 0, + MXRecoveryKeyErrorHeaderCode, + MXRecoveryKeyErrorLengthCode +} MXRecoveryKeyErrorCode; + + +/** + The `MXRecoveryKey` class encodes and decodes a recovery key used for backup. + */ +@interface MXRecoveryKey : NSObject + +/** + Encode a key into a base58 string. + + @param key the private key. + @return the base58 recovery key. + */ ++ (NSString *)encode:(NSData*)key; + +/** + Decode a base58 recovery key. + + @param key the recovery key. + @param error the output error. + @return the private key. + */ ++ (nullable NSData *)decode:(NSString*)recoveryKey error:(NSError * _Nullable *)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m new file mode 100644 index 0000000000..b8241e6b53 --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m @@ -0,0 +1,119 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXRecoveryKey.h" + +#import "MXTools.h" + +#import +#import "NS+BTCBase58.h" + + +NSString *const MXRecoveryKeyErrorDomain = @"org.matrix.sdk.recoverykey"; + +// Picked arbitrarily but to try & avoid clashing with any bitcoin ones +// (also base58 encoded, albeit with a lot of hashing) +const UInt8 kOlmRecoveryKeyPrefix[] = {0x8B, 0x01}; + +@implementation MXRecoveryKey + ++ (NSString *)encode:(NSData *)key +{ + // Prepend the recovery key 2-bytes header + NSMutableData *buffer = [NSMutableData dataWithBytes:kOlmRecoveryKeyPrefix length:sizeof(kOlmRecoveryKeyPrefix)]; + [buffer appendData:key]; + + // Add a parity checksum + UInt8 parity = 0; + UInt8 *bytes = (UInt8 *)buffer.bytes; + for (NSUInteger i = 0; i < buffer.length; i++) + { + parity ^= bytes[i]; + } + [buffer appendBytes:&parity length:sizeof(parity)]; + + // Encode it in Base58 + NSString *recoveryKey = [buffer base58String]; + + // Add white spaces + return [MXTools addWhiteSpacesToString:recoveryKey every:4]; +} + ++ (NSData *)decode:(NSString *)recoveryKey error:(NSError **)error +{ + NSString *recoveryKeyWithNoSpaces = [recoveryKey stringByReplacingOccurrencesOfString:@" " withString:@""]; + NSMutableData *result = [recoveryKeyWithNoSpaces dataFromBase58]; + + // Check the checksum + UInt8 parity = 0; + UInt8 *bytes = (UInt8 *)result.bytes; + for (NSUInteger i = 0; i < result.length; i++) + { + parity ^= bytes[i]; + } + if (parity != 0) + { + if (error) + { + *error = [NSError errorWithDomain:MXRecoveryKeyErrorDomain + code:MXRecoveryKeyErrorParityCode + userInfo:@{ + NSLocalizedDescriptionKey: @"Incorrect parity", + }]; + } + return nil; + } + + // Check recovery key header + for (NSUInteger i = 0; i < sizeof(kOlmRecoveryKeyPrefix); i++) + { + if (bytes[i] != kOlmRecoveryKeyPrefix[i]) + { + if (error) + { + *error = [NSError errorWithDomain:MXRecoveryKeyErrorDomain + code:MXRecoveryKeyErrorHeaderCode + userInfo:@{ + NSLocalizedDescriptionKey: @"Invalid header", + }]; + } + return nil; + } + } + + // Check length + if (result.length != + sizeof(kOlmRecoveryKeyPrefix) + [OLMPkDecryption privateKeyLength] + 1) + { + if (error) + { + *error = [NSError errorWithDomain:MXRecoveryKeyErrorDomain + code:MXRecoveryKeyErrorLengthCode + userInfo:@{ + NSLocalizedDescriptionKey: @"Incorrect length", + }]; + } + return nil; + } + + // Remove header and checksum bytes + [result replaceBytesInRange:NSMakeRange(0, 2) withBytes:NULL length:0]; + result.length -= 1; + + return result; +} + +@end diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 16612a2005..fc08160926 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -20,6 +20,7 @@ #import "MatrixSDKTestsE2EData.h" #import "MXCrypto_Private.h" +#import "MXRecoveryKey.h" // Do not bother with retain cycles warnings in tests #pragma clang diagnostic push @@ -265,6 +266,41 @@ - (void)testBackupStore }]; } +/** + - Check [MXRecoveryKey encode:] + - Check [MXRecoveryKey decode:error:] with a valid recovery key + - Check [MXRecoveryKey decode:error:] with an invalid recovery key + */ +- (void)testRecoveryKey +{ + UInt8 privateKeyBytes[] = { + 0x77, 0x07, 0x6D, 0x0A, 0x73, 0x18, 0xA5, 0x7D, + 0x3C, 0x16, 0xC1, 0x72, 0x51, 0xB2, 0x66, 0x45, + 0xDF, 0x4C, 0x2F, 0x87, 0xEB, 0xC0, 0x99, 0x2A, + 0xB1, 0x77, 0xFB, 0xA5, 0x1D, 0xB9, 0x2C, 0x2A + }; + NSData *privateKey = [NSData dataWithBytes:privateKeyBytes length:sizeof(privateKeyBytes)]; + + // Got this value from js console with recoveryKey.js:encodeRecoveryKey + NSString *recoveryKey = @"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d"; + + // - Check [MXRecoveryKey encode:] + NSString *recoveryKeyOut = [MXRecoveryKey encode:privateKey]; + XCTAssertEqualObjects(recoveryKeyOut, recoveryKey); + + // - Check [MXRecoveryKey decode:error:] with a valid recovery key + NSError *error; + NSData *privateKeyOut = [MXRecoveryKey decode:recoveryKey error:&error]; + XCTAssertNil(error); + XCTAssertEqualObjects(privateKeyOut, privateKey); + + // - Check [MXRecoveryKey decode:error:] with an invalid recovery key + NSString *badRecoveryKey = [recoveryKey stringByReplacingOccurrencesOfString:@"UE4d" withString:@"UE4e"]; + privateKeyOut = [MXRecoveryKey decode:badRecoveryKey error:&error]; + XCTAssertNil(privateKeyOut); + XCTAssertEqualObjects(error.domain, MXRecoveryKeyErrorDomain); +} + @end #pragma clang diagnostic pop diff --git a/Podfile b/Podfile index dd2c80fef0..d2c0e7de5a 100644 --- a/Podfile +++ b/Podfile @@ -11,6 +11,7 @@ pod 'OLMKit', '~> 3.0.0', :inhibit_warnings => true #pod 'OLMKit', :path => '../olm/OLMKit.podspec' pod 'Realm', '~> 3.11.1' +pod 'CBBase58', '~> 0.9.1' end diff --git a/SwiftMatrixSDK.podspec b/SwiftMatrixSDK.podspec index 899a590f65..6b4635c5c1 100644 --- a/SwiftMatrixSDK.podspec +++ b/SwiftMatrixSDK.podspec @@ -31,5 +31,6 @@ Pod::Spec.new do |s| # Requirements for e2e encryption s.dependency 'OLMKit', '~> 3.0.0' s.dependency 'Realm', '~> 3.11.1' + s.dependency 'CBBase58', '~> 0.9.1' end From d3bec5ad8acd591af8d93ac98270263578fbb735 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 24 Oct 2018 12:28:31 +0200 Subject: [PATCH 16/58] Keys backup: MXKeyBackupVersion: We need to be to create such object --- MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.h b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.h index dda1ca485c..e03f2380ef 100644 --- a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.h +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.h @@ -29,24 +29,24 @@ NS_ASSUME_NONNULL_BEGIN The algorithm used for storing backups. Currently, only kMXCryptoMegolmBackupAlgorithm (m.megolm_backup.v1.curve25519-aes-sha2) is defined. */ -@property (nonatomic, readonly) NSString *algorithm; +@property (nonatomic) NSString *algorithm; /** Algorithm-dependent data. */ -@property (nonatomic, readonly) NSDictionary *authData; +@property (nonatomic) NSDictionary *authData; /** The backup version. */ -@property (nonatomic, readonly, nullable) NSString *version; +@property (nonatomic, nullable) NSString *version; /** Enforce usage of factory method to guarantee non-nullabity. TODO: Should be done at `MXJSONModel` model. */ -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)new NS_UNAVAILABLE; +//- (instancetype)init NS_UNAVAILABLE; +//+ (instancetype)new NS_UNAVAILABLE; @end From 7ca520ffae8cad6ebb0438d8670f2aeb745f97b0 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 24 Oct 2018 12:30:56 +0200 Subject: [PATCH 17/58] Keys backup: Add MXKeyBackup to manage keys backup WIP --- .../Algorithms/Megolm/MXMegolmDecryption.m | 4 + .../Algorithms/Megolm/MXMegolmEncryption.m | 2 + .../Crypto/KeyBackup/Data/MXKeyBackupData.h | 7 + .../Data/MXMegolmBackupCreationInfo.h | 46 +++ .../Data/MXMegolmBackupCreationInfo.m | 21 + MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 122 ++++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 372 ++++++++++++++++++ .../Crypto/KeyBackup/MXKeyBackup_Private.h | 47 +++ MatrixSDK/Crypto/MXCrypto.h | 7 + MatrixSDK/Crypto/MXCrypto.m | 17 +- MatrixSDK/Crypto/MXCrypto_Private.h | 9 + 11 files changed, 648 insertions(+), 6 deletions(-) create mode 100644 MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.h create mode 100644 MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.m create mode 100644 MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h create mode 100644 MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m create mode 100644 MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m index d5331fb1c5..ada10f949b 100644 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m +++ b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m @@ -227,6 +227,8 @@ - (void)onRoomKeyEvent:(MXEvent *)event [olmDevice addInboundGroupSession:sessionId sessionKey:sessionKey roomId:roomId senderKey:senderKey forwardingCurve25519KeyChain:forwardingKeyChain keysClaimed:keysClaimed exportFormat:exportFormat]; + [crypto.backup maybeSendKeyBackup]; + // cancel any outstanding room key requests for this session [crypto cancelRoomKeyRequest:@{ @"algorithm": content[@"algorithm"], @@ -242,6 +244,8 @@ - (void)importRoomKey:(MXMegolmSessionData *)session { [olmDevice importInboundGroupSession:session]; + [crypto.backup maybeSendKeyBackup]; + // Have another go at decrypting events sent with this session [self retryDecryption:session.senderKey sessionId:session.sessionId]; } diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m index a7804f9223..aa86bc4c76 100644 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m +++ b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmEncryption.m @@ -344,6 +344,8 @@ - (MXOutboundSessionInfo*)prepareNewSession exportFormat:NO ]; + [crypto.backup maybeSendKeyBackup]; + return [[MXOutboundSessionInfo alloc] initWithSessionID:sessionId]; } diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.h b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.h index 4f8820204f..f5663e6109 100644 --- a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.h +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupData.h @@ -52,6 +52,10 @@ NS_ASSUME_NONNULL_BEGIN */ @interface MXRoomKeysBackupData : MXJSONModel +/** + + sessionId -> MXKeyBackupData + */ @property (nonatomic) NSDictionary *sessions; @end @@ -61,6 +65,9 @@ NS_ASSUME_NONNULL_BEGIN */ @interface MXKeysBackupData : MXJSONModel +/** + roomId -> MXRoomKeysBackupData + */ @property (nonatomic) NSDictionary *rooms; @end diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.h b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.h new file mode 100644 index 0000000000..334c337005 --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.h @@ -0,0 +1,46 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MXMegolmBackupAuthData.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + `MXMegolmBackupCreationInfo` represents data to create a megolm keys backup on + the homeserver. + */ +@interface MXMegolmBackupCreationInfo : NSObject + +/** + The algorithm used for storing backups (kMXCryptoMegolmBackupAlgorithm). + */ +@property (nonatomic) NSString *algorithm; + +/** + Authentication data. + */ +@property (nonatomic) MXMegolmBackupAuthData *authData; + +/** + The Base58 recovery key. + */ +@property (nonatomic) NSString *recoveryKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.m b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.m new file mode 100644 index 0000000000..6210f95ea7 --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupCreationInfo.m @@ -0,0 +1,21 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXMegolmBackupCreationInfo.h" + +@implementation MXMegolmBackupCreationInfo + +@end diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h new file mode 100644 index 0000000000..03670189fb --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -0,0 +1,122 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MXRestClient.h" +#import "MXMegolmBackupCreationInfo.h" + +@class MXSession; +@class OLMPkEncryption; + +// WDYT? +typedef enum : NSUInteger +{ + MXKeyBackupStateDisabled = 0, + MXKeyBackupStateEnabling, + MXKeyBackupStateReadyToBackup, + MXKeyBackupStateWillBackup, + MXKeyBackupStateBackingUp +} MXKeyBackupState; + +NS_ASSUME_NONNULL_BEGIN + +/** + A `MXKeyBackup` class instance manage incremental backup of e2e keys (megolm keys) + to the user's homeserver. + */ +@interface MXKeyBackup : NSObject + +/** + Get information about the current backup version. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +// TODO: hide it? +- (MXHTTPOperation*)version:(void (^)(MXKeyBackupVersion *keyBackupVersion))success + failure:(void (^)(NSError *error))failure; + +/** + Set up the data required to create a new backup version. + + The backup version will not be created and enabled until `createKeyBackupVersion` + is called. + The returned `MXMegolmBackupCreationInfo` object has a `recoveryKey` member with + the user-facing recovery key string. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails + */ +- (void)prepareKeyBackupVersion:(void (^)(MXMegolmBackupCreationInfo *keyBackupCreationInfo))success + failure:(nullable void (^)(NSError *error))failure; + +/** + Create a new key backup version and enable it, using the information return from +`prepareKeyBackupVersion`. + + @param keyBackupCreationInfo the info object from `prepareKeyBackupVersion`. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBackupCreationInfo + success:(void (^)(void))success + failure:(nullable void (^)(NSError *error))failure; + +/** + Delete a key backup version. + + If we are backing up to this version. Backup will be stopped. + + @param keyBackupVersion the backup version to delete. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)deleteKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion + success:(void (^)(void))success + failure:(nullable void (^)(NSError *error))failure; + +/** + The backup state. + */ +@property (nonatomic, readonly) MXKeyBackupState state; + +/** + Indicate if the backup is enabled. + */ +@property (nonatomic, readonly) BOOL enabled; + +/** + The backup version being used. + */ +@property (nonatomic, readonly, nullable) MXKeyBackupVersion *keyBackupVersion; + +/** + The backup key being used. + */ +@property (nonatomic, readonly, nullable) OLMPkEncryption *backupKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m new file mode 100644 index 0000000000..5a55a0392b --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -0,0 +1,372 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXKeyBackup.h" +#import "MXKeyBackup_Private.h" + +#import "MXCrypto_Private.h" + +#import +#import "MXRecoveryKey.h" +#import "MXSession.h" // TODO: To remove +#import "MXTools.h" + +/** + Maximum delay in ms in `[MXKeyBackup maybeSendKeyBackup]`. + */ +NSUInteger const kMXKeyBackupWaitingTimeToSendKeyBackup = 10000; + +/** + Maximum number of keys to send at a time to the homeserver. + */ +NSUInteger const kMXKeyBackupSendKeysMaxCount = 100; + + +@interface MXKeyBackup () +{ + MXSession *mxSession; + + // track whether this device's megolm keys are being backed up incrementally + // to the server or not. + // XXX: this should probably have a single source of truth from OlmAccount +// + this.backupInfo = null; // The info dict from /room_keys/version +// + this.backupKey = null; // The encryption key object +// this._checkedForBackup = false; // Have we checked the server for a backup we can use? +// X this._sendingBackups = false; // Are we currently sending backups? +} + +@end + +@implementation MXKeyBackup + +#pragma mark - SDK-Private methods + +- (instancetype)initWithMatrixSession:(MXSession *)matrixSession +{ + self = [self init]; + { + _state = MXKeyBackupStateDisabled; + mxSession = matrixSession; + } + return self; +} + +- (BOOL)enableKeyBackup:(MXKeyBackupVersion*)version +{ + MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData modelFromJSON:version.authData]; + if (authData) + { + _keyBackupVersion = version; + _backupKey = [OLMPkEncryption new]; + [_backupKey setRecipientKey:authData.publicKey]; + + self.state = MXKeyBackupStateReadyToBackup; + + [self maybeSendKeyBackup]; + } + + // wdty? + return _backupKey; +} + +- (void)disableKeyBackup +{ + _keyBackupVersion = nil; + _backupKey = nil; + self.state = MXKeyBackupStateDisabled; + + // Reset backup markers + [self->mxSession.crypto.store resetBackupMarkers]; +} + +- (void)maybeSendKeyBackup +{ + if (_state == MXKeyBackupStateReadyToBackup) + { + self.state = MXKeyBackupStateWillBackup; + + // Wait between 0 and 10 seconds, to avoid backup requests from + // different clients hitting the server all at the same time when a + // new key is sent + NSUInteger delayInMs = arc4random_uniform(kMXKeyBackupWaitingTimeToSendKeyBackup); + + MXWeakify(self); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInMs * NSEC_PER_MSEC)), mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + [self sendKeyBackup]; + }); + } + else + { + NSLog(@"[MXKeyBackup] maybeSendKeyBackup: Skip it because state: %@", @(_state)); + } +} + +- (void)sendKeyBackup +{ + NSArray *sessions = [mxSession.crypto.store inboundGroupSessionsToBackup:kMXKeyBackupSendKeysMaxCount]; + + NSLog(@"[MXKeyBackup] sendKeyBackup: %@ sessions to back up", @(sessions.count)); + + if (!sessions.count) + { + // Backup is up to date + return; + } + + if (_state == MXKeyBackupStateBackingUp || _state == MXKeyBackupStateDisabled) + { + // Do nothing if we are already backing up or if the backup has been disabled + return; + } + + // Sanity check + if (!_backupKey || !_keyBackupVersion) + { + NSLog(@"[MXKeyBackup] sendKeyBackup: Invalide state: %@", @(_state)); + } + + // Gather data to send to the homeserver + // roomId -> sessionId -> MXKeyBackupData + NSMutableDictionary *> *roomsKeyBackup = [NSMutableDictionary dictionary]; + + for (MXOlmInboundGroupSession *session in sessions) + { + // Gather information for each key + // TODO: userId? + MXDeviceInfo *device = [mxSession.crypto.deviceList deviceWithIdentityKey:session.senderKey forUser:nil andAlgorithm:kMXCryptoMegolmAlgorithm]; + + // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at + // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format + MXMegolmSessionData *sessionData = session.exportSessionData; + NSDictionary *sessionBackupData = @{ + @"algorithm": sessionData.algorithm, + @"sender_key": sessionData.senderKey, + @"sender_claimed_keys": sessionData.senderClaimedKeys, + @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ? sessionData.forwardingCurve25519KeyChain : @[], + @"session_key": sessionData.sessionKey + }; + OLMPkMessage *encryptedSessionBackupData = [_backupKey encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; + + // Build backup data for that key + MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; + keyBackupData.firstMessageIndex = session.session.firstKnownIndex; + keyBackupData.forwardedCount = session.forwardingCurve25519KeyChain.count; + keyBackupData.verified = device.verified; + keyBackupData.sessionData = @{ + @"ciphertext": encryptedSessionBackupData.ciphertext, + @"mac": encryptedSessionBackupData.mac, + @"ephemeral": encryptedSessionBackupData.ephemeralKey, + }; + + if (!roomsKeyBackup[session.roomId]) + { + roomsKeyBackup[session.roomId] = [NSMutableDictionary dictionary]; + } + roomsKeyBackup[session.roomId][sessionData.sessionId] = keyBackupData; + } + + // Finalise data to send + NSMutableDictionary *rooms = [NSMutableDictionary dictionary]; + for (NSString *roomId in roomsKeyBackup) + { + NSMutableDictionary *roomSessions = [NSMutableDictionary dictionary]; + for (NSString *sessionId in roomsKeyBackup[roomId]) + { + roomSessions[sessionId] = roomsKeyBackup[roomId][sessionId]; + } + MXRoomKeysBackupData *roomKeysBackupData = [MXRoomKeysBackupData new]; + roomKeysBackupData.sessions = roomSessions; + + rooms[roomId] = roomKeysBackupData; + } + + MXKeysBackupData *keysBackupData = [MXKeysBackupData new]; + keysBackupData.rooms = rooms; + + // Make the request + MXWeakify(self); + [mxSession.crypto.matrixRestClient sendKeysBackup:keysBackupData version:_keyBackupVersion.version success:^{ + MXStrongifyAndReturnIfNil(self); + + self.state = MXKeyBackupStateReadyToBackup; + + if (sessions.count < kMXKeyBackupSendKeysMaxCount) + { + NSLog(@"[MXKeyBackup] sendKeyBackup: All keys have been backed up"); + } + else + { + NSLog(@"[MXKeyBackup] sendKeyBackup: Continue to back up keys"); + [self sendKeyBackup]; + } + + } failure:^(NSError *error) { + // TODO: Manage failure + NSLog(@"[MXKeyBackup] sendKeyBackup: sendKeysBackup failed. Error: %@", error); + }]; +} + + +#pragma mark - Public methods + +- (MXHTTPOperation *)version:(void (^)(MXKeyBackupVersion * _Nonnull))success failure:(void (^)(NSError * _Nonnull))failure +{ + return [mxSession.matrixRestClient keyBackupVersion:success failure:failure]; +} + +- (void)prepareKeyBackupVersion:(void (^)(MXMegolmBackupCreationInfo *keyBackupCreationInfo))success + failure:(nullable void (^)(NSError *error))failure; +{ + MXWeakify(self); + dispatch_async(mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + OLMPkDecryption *decryption = [OLMPkDecryption new]; + + NSError *error; + MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData new]; + authData.publicKey = [decryption generateKey:&error]; + if (error) + { + if (failure) + { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + } + return; + } + authData.signatures = [self->mxSession.crypto signObject:authData.JSONDictionary]; + + MXMegolmBackupCreationInfo *keyBackupCreationInfo = [MXMegolmBackupCreationInfo new]; + keyBackupCreationInfo.algorithm = kMXCryptoMegolmBackupAlgorithm; + keyBackupCreationInfo.authData = authData; + keyBackupCreationInfo.recoveryKey = [MXRecoveryKey encode:decryption.privateKey]; + + dispatch_async(dispatch_get_main_queue(), ^{ + success(keyBackupCreationInfo); + }); + }); +} + +- (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBackupCreationInfo + success:(void (^)(void))success + failure:(nullable void (^)(NSError *error))failure +{ + MXHTTPOperation *operation = [MXHTTPOperation new]; + + [self setState:MXKeyBackupStateEnabling]; + + MXWeakify(self); + dispatch_async(mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + // Reset backup markers + [self->mxSession.crypto.store resetBackupMarkers]; + + MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion new]; + keyBackupVersion.algorithm = keyBackupCreationInfo.algorithm; + keyBackupVersion.authData = keyBackupCreationInfo.authData.JSONDictionary; + + MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + + keyBackupVersion.version = version; + + [self enableKeyBackup:keyBackupVersion]; + + dispatch_async(dispatch_get_main_queue(), ^{ + success(); + }); + + } failure:^(NSError *error) { + if (failure) { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + } + }]; + + if (operation2) + { + [operation mutateTo:operation2]; + } + }); + + return operation; +} + +- (MXHTTPOperation*)deleteKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion + success:(void (^)(void))success + failure:(nullable void (^)(NSError *error))failure +{ + MXHTTPOperation *operation = [MXHTTPOperation new]; + + MXWeakify(self); + dispatch_async(mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + // If we're currently backing up to this backup... stop. + // (We start using it automatically in createKeyBackupVersion + // so this is symmetrical). + if ([self.keyBackupVersion.JSONDictionary isEqualToDictionary:keyBackupVersion.JSONDictionary]) + { + //[self disableKeyBackup]; + } + + MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient deleteKeysFromBackup:keyBackupVersion.version success:^{ + + dispatch_async(dispatch_get_main_queue(), ^{ + success(); + }); + + } failure:^(NSError *error) { + if (failure) { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + } + }]; + + if (operation2) + { + [operation mutateTo:operation2]; + } + }); + + return operation; +} + +- (BOOL)enabled +{ + return _state != MXKeyBackupStateDisabled; +} + + +#pragma mark - Private methods + +- (void)setState:(MXKeyBackupState)state +{ + _state = state; + + dispatch_async(dispatch_get_main_queue(), ^{ + // TODO + }); +} + +@end diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h new file mode 100644 index 0000000000..aefe73e6da --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h @@ -0,0 +1,47 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXKeyBackup.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + The `MXKeyBackup_Private` extension exposes internal operations. + */ +@interface MXKeyBackup () + +/** + + */ +- (instancetype)initWithMatrixSession:(MXSession*)mxSession; + +/** + Enable backing up of keys. + + @param keyBackupVersion backup information object as returned by `[MXKeyBackup version]`. + */ +- (BOOL)enableKeyBackup:(MXKeyBackupVersion*)keyBackupVersion; + +/** + * Disable backing up of keys. + */ +- (void)disableKeyBackup; + +- (void)maybeSendKeyBackup; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/MXCrypto.h b/MatrixSDK/Crypto/MXCrypto.h index 716282c9c0..b5d10690ad 100644 --- a/MatrixSDK/Crypto/MXCrypto.h +++ b/MatrixSDK/Crypto/MXCrypto.h @@ -27,6 +27,8 @@ #import "MXIncomingRoomKeyRequest.h" #import "MXIncomingRoomKeyRequestCancellation.h" +#import "MXKeyBackup.h" + @class MXSession; /** @@ -75,6 +77,11 @@ FOUNDATION_EXPORT NSString *const kMXCryptoRoomKeyRequestCancellationNotificatio */ @property (nonatomic, readonly) NSString *olmVersion; +/** + The key backup manager. + */ +@property (nonatomic, readonly) MXKeyBackup *backup; + /** Create a new crypto instance and data for the given user. diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index 71744d807a..b6ba2fca60 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -1732,6 +1732,16 @@ - (NSDictionary*)encryptMessage:(NSDictionary*)payloadFields forDevices:(NSArray return alg; } +- (NSDictionary*)signObject:(NSDictionary*)object +{ + return @{ + myDevice.userId: @{ + [NSString stringWithFormat:@"ed25519:%@", myDevice.deviceId]: [_olmDevice signJSON:object] + } + }; +} + + #pragma mark - Key sharing - (void)requestRoomKey:(NSDictionary*)requestBody recipients:(NSArray*>*)recipients { @@ -2228,12 +2238,7 @@ - (MXHTTPOperation *)uploadOneTimeKeys:(void (^)(MXKeysUploadResponse *keysUploa // Sign each one-time key NSMutableDictionary *k = [NSMutableDictionary dictionary]; k[@"key"] = oneTimeKeys[@"curve25519"][keyId]; - - k[@"signatures"] = @{ - myDevice.userId: @{ - [NSString stringWithFormat:@"ed25519:%@", myDevice.deviceId]: [_olmDevice signJSON:k] - } - }; + k[@"signatures"] = [self signObject:k]; oneTimeJson[[NSString stringWithFormat:@"signed_curve25519:%@", keyId]] = k; } diff --git a/MatrixSDK/Crypto/MXCrypto_Private.h b/MatrixSDK/Crypto/MXCrypto_Private.h index c1f4118d2b..6d3dc1c57e 100644 --- a/MatrixSDK/Crypto/MXCrypto_Private.h +++ b/MatrixSDK/Crypto/MXCrypto_Private.h @@ -28,6 +28,7 @@ #import "MXCryptoAlgorithms.h" #import "MXUsersDevicesMap.h" #import "MXOlmSessionResult.h" +#import "MXKeyBackup_Private.h" #import "MXCrypto.h" @@ -150,6 +151,14 @@ */ - (id)getRoomDecryptor:(NSString*)roomId algorithm:(NSString*)algorithm; +/** + Sign the given object with our ed25519 key. + + @param Object the dictionary to sign. + @return signatures. + */ +- (NSDictionary*)signObject:(NSDictionary*)object; + #pragma mark - Key sharing From e1709610c0f0e87ce31b9313eac355cf44631f20 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 11:13:39 +0200 Subject: [PATCH 18/58] Keys backup: Refine MXKeyBackupState --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 27 ++++++++++++++++++++---- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 19 +++++++++++------ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index 03670189fb..b8a89454d1 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -22,17 +22,36 @@ @class MXSession; @class OLMPkEncryption; -// WDYT? +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - Constants definitions + +/** + E2e keys backup states. + */ typedef enum : NSUInteger { + // Backup is not enabled MXKeyBackupStateDisabled = 0, + + // Backup is being enabled MXKeyBackupStateEnabling, - MXKeyBackupStateReadyToBackup, - MXKeyBackupStateWillBackup, + + // Backup is enabled and ready to send backup to the homeserver + MXKeyBackupStateReadyToBackUp, + + // Backup is going to be send to the homeserver + MXKeyBackupStateWillBackUp, + + // Backup is being sent to the homeserver MXKeyBackupStateBackingUp } MXKeyBackupState; -NS_ASSUME_NONNULL_BEGIN +/** + Posted when the state of the MXKeyBackup instance changes. + */ +FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; + /** A `MXKeyBackup` class instance manage incremental backup of e2e keys (megolm keys) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 5a55a0392b..02968e4682 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -24,6 +24,11 @@ #import "MXSession.h" // TODO: To remove #import "MXTools.h" + +#pragma mark - Constants definitions + +NSString *const kMXKeyBackupDidStateChangeNotification = @"kMXKeyBackupDidStateChangeNotification"; + /** Maximum delay in ms in `[MXKeyBackup maybeSendKeyBackup]`. */ @@ -73,7 +78,7 @@ - (BOOL)enableKeyBackup:(MXKeyBackupVersion*)version _backupKey = [OLMPkEncryption new]; [_backupKey setRecipientKey:authData.publicKey]; - self.state = MXKeyBackupStateReadyToBackup; + self.state = MXKeyBackupStateReadyToBackUp; [self maybeSendKeyBackup]; } @@ -94,9 +99,9 @@ - (void)disableKeyBackup - (void)maybeSendKeyBackup { - if (_state == MXKeyBackupStateReadyToBackup) + if (_state == MXKeyBackupStateReadyToBackUp) { - self.state = MXKeyBackupStateWillBackup; + self.state = MXKeyBackupStateWillBackUp; // Wait between 0 and 10 seconds, to avoid backup requests from // different clients hitting the server all at the same time when a @@ -118,6 +123,7 @@ - (void)maybeSendKeyBackup - (void)sendKeyBackup { + // Get a chunk of keys to backup NSArray *sessions = [mxSession.crypto.store inboundGroupSessionsToBackup:kMXKeyBackupSendKeysMaxCount]; NSLog(@"[MXKeyBackup] sendKeyBackup: %@ sessions to back up", @(sessions.count)); @@ -204,15 +210,16 @@ - (void)sendKeyBackup [mxSession.crypto.matrixRestClient sendKeysBackup:keysBackupData version:_keyBackupVersion.version success:^{ MXStrongifyAndReturnIfNil(self); - self.state = MXKeyBackupStateReadyToBackup; - if (sessions.count < kMXKeyBackupSendKeysMaxCount) { NSLog(@"[MXKeyBackup] sendKeyBackup: All keys have been backed up"); + self.state = MXKeyBackupStateReadyToBackUp; } else { NSLog(@"[MXKeyBackup] sendKeyBackup: Continue to back up keys"); + self.state = MXKeyBackupStateWillBackUp; + [self sendKeyBackup]; } @@ -365,7 +372,7 @@ - (void)setState:(MXKeyBackupState)state _state = state; dispatch_async(dispatch_get_main_queue(), ^{ - // TODO + [[NSNotificationCenter defaultCenter] postNotificationName:kMXKeyBackupDidStateChangeNotification object:self]; }); } From 2b719c01d8afeb071735800797d1bf6bee84521c Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 11:22:08 +0200 Subject: [PATCH 19/58] Keys backup: MXKeyBackup: Use version as string in interfaces --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 6 +++--- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index b8a89454d1..375b7fcb49 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -97,7 +97,7 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; @return a MXHTTPOperation instance. */ - (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBackupCreationInfo - success:(void (^)(void))success + success:(void (^)(MXKeyBackupVersion *keyBackupVersion))success failure:(nullable void (^)(NSError *error))failure; /** @@ -105,14 +105,14 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; If we are backing up to this version. Backup will be stopped. - @param keyBackupVersion the backup version to delete. + @param version the backup version to delete. @param success A block object called when the operation succeeds. @param failure A block object called when the operation fails. @return a MXHTTPOperation instance. */ -- (MXHTTPOperation*)deleteKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion +- (MXHTTPOperation*)deleteKeyBackupVersion:(NSString*)version success:(void (^)(void))success failure:(nullable void (^)(NSError *error))failure; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 02968e4682..25487a82a2 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -273,7 +273,7 @@ - (void)prepareKeyBackupVersion:(void (^)(MXMegolmBackupCreationInfo *keyBackupC } - (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBackupCreationInfo - success:(void (^)(void))success + success:(void (^)(MXKeyBackupVersion *keyBackupVersion))success failure:(nullable void (^)(NSError *error))failure { MXHTTPOperation *operation = [MXHTTPOperation new]; @@ -298,7 +298,7 @@ - (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBacku [self enableKeyBackup:keyBackupVersion]; dispatch_async(dispatch_get_main_queue(), ^{ - success(); + success(keyBackupVersion); }); } failure:^(NSError *error) { @@ -318,7 +318,7 @@ - (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBacku return operation; } -- (MXHTTPOperation*)deleteKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion +- (MXHTTPOperation*)deleteKeyBackupVersion:(NSString*)version success:(void (^)(void))success failure:(nullable void (^)(NSError *error))failure { @@ -331,12 +331,12 @@ - (MXHTTPOperation*)deleteKeyBackupVersion:(MXKeyBackupVersion*)keyBackupVersion // If we're currently backing up to this backup... stop. // (We start using it automatically in createKeyBackupVersion // so this is symmetrical). - if ([self.keyBackupVersion.JSONDictionary isEqualToDictionary:keyBackupVersion.JSONDictionary]) + if ([self.keyBackupVersion.version isEqualToString:version]) { - //[self disableKeyBackup]; + [self disableKeyBackup]; } - MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient deleteKeysFromBackup:keyBackupVersion.version success:^{ + MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient deleteKeysFromBackup:version success:^{ dispatch_async(dispatch_get_main_queue(), ^{ success(); From 62755a5fd86592d581fbb525035b1fb925473b74 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 13:10:25 +0200 Subject: [PATCH 20/58] Keys backup: MXKeyBackup: Add backupAllGroupSessions and backupProgress methods --- MatrixSDK.xcodeproj/project.pbxproj | 42 +++++++++-- MatrixSDK/Crypto/Data/Store/MXCryptoStore.h | 8 ++ .../MXRealmCryptoStore/MXRealmCryptoStore.m | 16 ++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 24 ++++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 73 +++++++++++++++++++ MatrixSDKTests/MXCryptoBackupTests.m | 14 +++- 6 files changed, 165 insertions(+), 12 deletions(-) diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 648d5229ee..a9e8693896 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -11,6 +11,10 @@ 021AFBA52179E91900742B2C /* MXEncryptedContentKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 021AFBA12179E91800742B2C /* MXEncryptedContentKey.m */; }; 021AFBA62179E91900742B2C /* MXEncryptedContentKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 021AFBA22179E91800742B2C /* MXEncryptedContentKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; 021AFBA72179E91900742B2C /* MXEncryptedContentFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */; }; + 320A883C217F4E35002EA952 /* MXMegolmBackupCreationInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 320A883A217F4E35002EA952 /* MXMegolmBackupCreationInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 320A883D217F4E35002EA952 /* MXMegolmBackupCreationInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 320A883B217F4E35002EA952 /* MXMegolmBackupCreationInfo.m */; }; + 320A8840217F4E3F002EA952 /* MXMegolmBackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = 320A883E217F4E3E002EA952 /* MXMegolmBackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 320A8841217F4E3F002EA952 /* MXMegolmBackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = 320A883F217F4E3F002EA952 /* MXMegolmBackupAuthData.m */; }; 320BBF3C1D6C7D9D0079890E /* MXEventsEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 320BBF411D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 320BBF3D1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m */; }; 320BBF421D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 320BBF3E1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -190,10 +194,10 @@ 32BA86B02152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BA86AE2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m */; }; 32BBAE6C2178E99100D85F46 /* MXKeyBackupData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE662178E99100D85F46 /* MXKeyBackupData.m */; }; 32BBAE6D2178E99100D85F46 /* MXKeyBackupVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE672178E99100D85F46 /* MXKeyBackupVersion.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32BBAE6E2178E99100D85F46 /* MXMegolmBackupAuthData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE682178E99100D85F46 /* MXMegolmBackupAuthData.m */; }; 32BBAE6F2178E99100D85F46 /* MXKeyBackupVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */; }; 32BBAE702178E99100D85F46 /* MXKeyBackupData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE6A2178E99100D85F46 /* MXKeyBackupData.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 32BBAE712178E99100D85F46 /* MXMegolmBackupAuthData.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE6B2178E99100D85F46 /* MXMegolmBackupAuthData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32BBAE742179CF4000D85F46 /* MXKeyBackup.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BBAE722179CF4000D85F46 /* MXKeyBackup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 32BBAE752179CF4000D85F46 /* MXKeyBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BBAE732179CF4000D85F46 /* MXKeyBackup.m */; }; 32BD34BE1E84134A006EDC0D /* MatrixSDKTestsE2EData.m in Sources */ = {isa = PBXBuildFile; fileRef = 32BD34BD1E84134A006EDC0D /* MatrixSDKTestsE2EData.m */; }; 32BED28F1B00A23F00E668FE /* MXCallStack.h in Headers */ = {isa = PBXBuildFile; fileRef = 32BED28E1B00A23F00E668FE /* MXCallStack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32C03CB62123076F00D92712 /* DirectRoomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C03CB52123076F00D92712 /* DirectRoomTests.m */; }; @@ -239,6 +243,8 @@ 32FCAB4D19E578860049C555 /* MXRestClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FCAB4C19E578860049C555 /* MXRestClientTests.m */; }; 32FE41361D0AB7070060835E /* MXEnumConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 32FE41341D0AB7070060835E /* MXEnumConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32FE41371D0AB7070060835E /* MXEnumConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FE41351D0AB7070060835E /* MXEnumConstants.m */; }; + 32FFB4F0217E146A00C96002 /* MXRecoveryKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 32FFB4EE217E146A00C96002 /* MXRecoveryKey.h */; }; + 32FFB4F1217E146A00C96002 /* MXRecoveryKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FFB4EF217E146A00C96002 /* MXRecoveryKey.m */; }; 71DE22E01BC7C51200284153 /* MXReceiptData.m in Sources */ = {isa = PBXBuildFile; fileRef = 71DE22DC1BC7C51200284153 /* MXReceiptData.m */; }; 71DE22E11BC7C51200284153 /* MXReceiptData.h in Headers */ = {isa = PBXBuildFile; fileRef = 71DE22DD1BC7C51200284153 /* MXReceiptData.h */; settings = {ATTRIBUTES = (Public, ); }; }; 92634B7F1EF2A37A00DB9F60 /* MXCallAudioSessionConfigurator.h in Headers */ = {isa = PBXBuildFile; fileRef = 92634B7E1EF2A37A00DB9F60 /* MXCallAudioSessionConfigurator.h */; }; @@ -324,6 +330,10 @@ 021AFBA32179E91800742B2C /* MXEncryptedContentFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEncryptedContentFile.m; sourceTree = ""; }; 0BCFBADF157F3C8C43112BD6 /* Pods-MatrixSDKTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDKTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixSDKTests/Pods-MatrixSDKTests.release.xcconfig"; sourceTree = ""; }; 2BF02FACC417CA3368671024 /* Pods-MatrixSDK.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDK.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixSDK/Pods-MatrixSDK.debug.xcconfig"; sourceTree = ""; }; + 320A883A217F4E35002EA952 /* MXMegolmBackupCreationInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXMegolmBackupCreationInfo.h; sourceTree = ""; }; + 320A883B217F4E35002EA952 /* MXMegolmBackupCreationInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXMegolmBackupCreationInfo.m; sourceTree = ""; }; + 320A883E217F4E3E002EA952 /* MXMegolmBackupAuthData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXMegolmBackupAuthData.h; sourceTree = ""; }; + 320A883F217F4E3F002EA952 /* MXMegolmBackupAuthData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXMegolmBackupAuthData.m; sourceTree = ""; }; 320BBF3B1D6C7D9D0079890E /* MXEventsEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEventsEnumerator.h; sourceTree = ""; }; 320BBF3D1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEventsByTypesEnumeratorOnArray.m; sourceTree = ""; }; 320BBF3E1D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEventsByTypesEnumeratorOnArray.h; sourceTree = ""; }; @@ -507,10 +517,10 @@ 32BA86AE2152A79E008F277E /* MXRoomNameDefaultStringLocalizations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRoomNameDefaultStringLocalizations.m; sourceTree = ""; }; 32BBAE662178E99100D85F46 /* MXKeyBackupData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupData.m; sourceTree = ""; }; 32BBAE672178E99100D85F46 /* MXKeyBackupVersion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupVersion.h; sourceTree = ""; }; - 32BBAE682178E99100D85F46 /* MXMegolmBackupAuthData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXMegolmBackupAuthData.m; sourceTree = ""; }; 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupVersion.m; sourceTree = ""; }; 32BBAE6A2178E99100D85F46 /* MXKeyBackupData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupData.h; sourceTree = ""; }; - 32BBAE6B2178E99100D85F46 /* MXMegolmBackupAuthData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXMegolmBackupAuthData.h; sourceTree = ""; }; + 32BBAE722179CF4000D85F46 /* MXKeyBackup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackup.h; sourceTree = ""; }; + 32BBAE732179CF4000D85F46 /* MXKeyBackup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackup.m; sourceTree = ""; }; 32BD34BC1E84134A006EDC0D /* MatrixSDKTestsE2EData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatrixSDKTestsE2EData.h; sourceTree = ""; }; 32BD34BD1E84134A006EDC0D /* MatrixSDKTestsE2EData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MatrixSDKTestsE2EData.m; sourceTree = ""; }; 32BED28E1B00A23F00E668FE /* MXCallStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallStack.h; sourceTree = ""; }; @@ -562,6 +572,9 @@ 32FCAB4C19E578860049C555 /* MXRestClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MXRestClientTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 32FE41341D0AB7070060835E /* MXEnumConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXEnumConstants.h; sourceTree = ""; }; 32FE41351D0AB7070060835E /* MXEnumConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXEnumConstants.m; sourceTree = ""; }; + 32FFB4ED217DC0E900C96002 /* MXKeyBackup_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackup_Private.h; sourceTree = ""; }; + 32FFB4EE217E146A00C96002 /* MXRecoveryKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRecoveryKey.h; sourceTree = ""; }; + 32FFB4EF217E146A00C96002 /* MXRecoveryKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRecoveryKey.m; sourceTree = ""; }; 3ABDD5D65684E6B7F52FB94E /* libPods-MatrixSDKTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MatrixSDKTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 71DE22DC1BC7C51200284153 /* MXReceiptData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXReceiptData.m; sourceTree = ""; }; 71DE22DD1BC7C51200284153 /* MXReceiptData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXReceiptData.h; sourceTree = ""; }; @@ -991,6 +1004,11 @@ isa = PBXGroup; children = ( 32BBAE652178E99100D85F46 /* Data */, + 32BBAE722179CF4000D85F46 /* MXKeyBackup.h */, + 32BBAE732179CF4000D85F46 /* MXKeyBackup.m */, + 32FFB4ED217DC0E900C96002 /* MXKeyBackup_Private.h */, + 32FFB4EE217E146A00C96002 /* MXRecoveryKey.h */, + 32FFB4EF217E146A00C96002 /* MXRecoveryKey.m */, ); path = KeyBackup; sourceTree = ""; @@ -1002,8 +1020,10 @@ 32BBAE662178E99100D85F46 /* MXKeyBackupData.m */, 32BBAE672178E99100D85F46 /* MXKeyBackupVersion.h */, 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */, - 32BBAE6B2178E99100D85F46 /* MXMegolmBackupAuthData.h */, - 32BBAE682178E99100D85F46 /* MXMegolmBackupAuthData.m */, + 320A883E217F4E3E002EA952 /* MXMegolmBackupAuthData.h */, + 320A883F217F4E3F002EA952 /* MXMegolmBackupAuthData.m */, + 320A883A217F4E35002EA952 /* MXMegolmBackupCreationInfo.h */, + 320A883B217F4E35002EA952 /* MXMegolmBackupCreationInfo.m */, ); path = Data; sourceTree = ""; @@ -1295,6 +1315,7 @@ 32954019216385F100E300FC /* MXServerNoticeContent.h in Headers */, 327187851DA7D0220071C818 /* MXOlmDecryption.h in Headers */, 32D7767D1A27860600FC4AA2 /* MXMemoryStore.h in Headers */, + 32FFB4F0217E146A00C96002 /* MXRecoveryKey.h in Headers */, 32CE6FB81A409B1F00317F1E /* MXFileStoreMetaData.h in Headers */, 32A31BC820D401FC005916C7 /* MXRoomFilter.h in Headers */, 322691361E5EFF8700966A6E /* MXDeviceListOperationsPool.h in Headers */, @@ -1305,6 +1326,7 @@ 323E0C5B1A306D7A00A31D73 /* MXEvent.h in Headers */, 327F8DB21C6112BA00581CA3 /* MXRoomThirdPartyInvite.h in Headers */, 32BA86AC21529E29008F277E /* MXRoomNameStringsLocalizable.h in Headers */, + 32BBAE742179CF4000D85F46 /* MXKeyBackup.h in Headers */, 92634B821EF2E3C400DB9F60 /* MXCallKitConfiguration.h in Headers */, 32618E7120ED2DF500E1D2EA /* MXFilterJSONModel.h in Headers */, 322360521A8E610500A3CA81 /* MXPushRuleDisplayNameCondtionChecker.h in Headers */, @@ -1399,9 +1421,10 @@ 320DFDE219DD99B60068622A /* MXError.h in Headers */, 32BBAE702178E99100D85F46 /* MXKeyBackupData.h in Headers */, 327E37B61A974F75007F026F /* MXLogger.h in Headers */, + 320A883C217F4E35002EA952 /* MXMegolmBackupCreationInfo.h in Headers */, 3256E3811DCB91EB003C9718 /* MXCryptoConstants.h in Headers */, 320DFDE619DD99B60068622A /* MXHTTPClient.h in Headers */, - 32BBAE712178E99100D85F46 /* MXMegolmBackupAuthData.h in Headers */, + 320A8840217F4E3F002EA952 /* MXMegolmBackupAuthData.h in Headers */, 320DFDDB19DD99B60068622A /* MXRoom.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1557,11 +1580,12 @@ 32A31BC520D3FFB0005916C7 /* MXFilter.m in Sources */, 32B76EA520FDE85100B095F6 /* MXRoomMembersCount.m in Sources */, F03EF4FF1DF014D9009DF592 /* MXMediaLoader.m in Sources */, + 320A8841217F4E3F002EA952 /* MXMegolmBackupAuthData.m in Sources */, + 32FFB4F1217E146A00C96002 /* MXRecoveryKey.m in Sources */, 32DC15D51A8CF874006F9AD3 /* MXPushRuleEventMatchConditionChecker.m in Sources */, 320BBF411D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m in Sources */, 3259CD541DF860C300186944 /* MXRealmCryptoStore.m in Sources */, 321B41401E09937E009EEEC7 /* MXRoomSummary.m in Sources */, - 32BBAE6E2178E99100D85F46 /* MXMegolmBackupAuthData.m in Sources */, 32CAB1081A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */, 3245A7511AF7B2930001D8A7 /* MXCall.m in Sources */, 327F8DB31C6112BA00581CA3 /* MXRoomThirdPartyInvite.m in Sources */, @@ -1644,6 +1668,7 @@ 320BBF441D6C81550079890E /* MXEventsEnumeratorOnArray.m in Sources */, 32618E7220ED2DF500E1D2EA /* MXFilterJSONModel.m in Sources */, 323F8865212D4E480001C73C /* MXMatrixVersions.m in Sources */, + 320A883D217F4E35002EA952 /* MXMegolmBackupCreationInfo.m in Sources */, 320DFDDC19DD99B60068622A /* MXRoom.m in Sources */, F08B8D5D1E014711006171A8 /* NSData+MatrixSDK.m in Sources */, 3291D4D51A68FFEB00C3BA41 /* MXFileRoomStore.m in Sources */, @@ -1656,6 +1681,7 @@ C6481AF21F1678A9000DB8A0 /* MXSessionEventListener.swift in Sources */, 32A1514F1DAF897600400192 /* MXOlmSessionResult.m in Sources */, C6F9357C1E5B39CA00FC34BF /* MXRestClient.swift in Sources */, + 32BBAE752179CF4000D85F46 /* MXKeyBackup.m in Sources */, 322A51B71D9AB15900C8536D /* MXCrypto.m in Sources */, B17982FB2119E4A2001FD722 /* MXRoomPredecessorInfo.m in Sources */, 32DC15D11A8CF7AE006F9AD3 /* MXNotificationCenter.m in Sources */, diff --git a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h index 19d8a531de..a508733b14 100644 --- a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h +++ b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h @@ -237,6 +237,14 @@ */ - (NSArray*)inboundGroupSessionsToBackup:(NSUInteger)limit; +/** + Number of stored inbound group sessions. + + @param onlyBackedUp if YES, count only session marked as backed up. + @return a count. + */ +- (NSUInteger)inboundGroupSessionsCount:(BOOL)onlyBackedUp; + #pragma mark - Key sharing - Outgoing key requests diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index f0b8db9cab..1471f8b555 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -775,6 +775,22 @@ - (void)markBackupDoneForInboundGroupSessionWithId:(NSString*)sessionId andSende return sessions; } +- (NSUInteger)inboundGroupSessionsCount:(BOOL)onlyBackedUp +{ + RLMRealm *realm = self.realm; + RLMResults *realmSessions; + + if (onlyBackedUp) + { + realmSessions = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"backedUp = YES"]; + } + else + { + realmSessions = [MXRealmOlmInboundGroupSession allObjectsInRealm:realm]; + } + + return realmSessions.count; +} #pragma mark - Key sharing - Outgoing key requests diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index 375b7fcb49..599bdb9928 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -116,6 +116,30 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; success:(void (^)(void))success failure:(nullable void (^)(NSError *error))failure; +/** + Start to back up keys immediately. + + @param version the backup version to delete. + + @param success A block object called when the operation succeeds. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (void)backupAllGroupSessions:(nullable void (^)(NSProgress *backupProgress))onBackupProgress + onComplete:(nullable void (^)(void))onComplete; + +/** + Get the current backup progress. + + Can be called at any `MXKeyBackup` state. + `backupProgress.totalUnitCount` represents the total number of (group sessions) keys. + `backupProgress.completedUnitCount` is the number of keys already backed up. + + @param backupProgress the current backup progress + */ +- (void)backupProgress:(void (^)(NSProgress *backupProgress))backupProgress; + /** The backup state. */ diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 25487a82a2..74250c4c28 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -144,8 +144,11 @@ - (void)sendKeyBackup if (!_backupKey || !_keyBackupVersion) { NSLog(@"[MXKeyBackup] sendKeyBackup: Invalide state: %@", @(_state)); + return; } + self.state = MXKeyBackupStateBackingUp; + // Gather data to send to the homeserver // roomId -> sessionId -> MXKeyBackupData NSMutableDictionarymxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + [self sendKeyBackup]; + }); + }]; +} + +- (void)backupProgress:(void (^)(NSProgress *backupProgress))backupProgress +{ + MXWeakify(self); + dispatch_async(mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + NSUInteger keys = [self->mxSession.crypto.store inboundGroupSessionsCount:NO]; + NSUInteger backedUpkeys = [self->mxSession.crypto.store inboundGroupSessionsCount:YES]; + + NSProgress *progress = [NSProgress progressWithTotalUnitCount:keys]; + progress.completedUnitCount = backedUpkeys; + + dispatch_async(dispatch_get_main_queue(), ^{ + backupProgress(progress); + }); + }); +} + - (BOOL)enabled { return _state != MXKeyBackupStateDisabled; diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index fc08160926..939c7c18c5 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -238,29 +238,35 @@ - (void)testRESTDeleteBackupKeys /** -- From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have non backed up keys -- Check backup keys after having marked one as backuped +- From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have no backed up keys +- Check backup keys after having marked one as backed up - Reset keys backup markers */ - (void)testBackupStore { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - // - From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have non backed up keys + // - From doE2ETestWithAliceAndBobInARoomWithCryptedMessages, we should have no backed up keys NSArray *sessions = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; NSUInteger sessionsCount = sessions.count; XCTAssertGreaterThan(sessionsCount, 0); + XCTAssertEqual([aliceSession.crypto.store inboundGroupSessionsCount:NO], sessionsCount); + XCTAssertEqual([aliceSession.crypto.store inboundGroupSessionsCount:YES], 0); - // - Check backup keys after having marked one as backuped + // - Check backup keys after having marked one as backed up MXOlmInboundGroupSession *session = sessions.firstObject; [aliceSession.crypto.store markBackupDoneForInboundGroupSessionWithId:session.session.sessionIdentifier andSenderKey:session.senderKey]; sessions = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; XCTAssertEqual(sessions.count, sessionsCount - 1); + XCTAssertEqual([aliceSession.crypto.store inboundGroupSessionsCount:NO], sessionsCount); + XCTAssertEqual([aliceSession.crypto.store inboundGroupSessionsCount:YES], 1); // - Reset keys backup markers [aliceSession.crypto.store resetBackupMarkers]; sessions = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; XCTAssertEqual(sessions.count, sessionsCount); + XCTAssertEqual([aliceSession.crypto.store inboundGroupSessionsCount:NO], sessionsCount); + XCTAssertEqual([aliceSession.crypto.store inboundGroupSessionsCount:YES], 0); [expectation fulfill]; }]; From ba0b66d2f7fbb55c89ec2e55a0054710efd8c859 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 13:36:02 +0200 Subject: [PATCH 21/58] Keys backup: Start to plug in all together and tests WIP --- .../KeyBackup/Data/MXMegolmBackupAuthData.m | 16 +++++++++++++ MatrixSDK/Crypto/MXCrypto.m | 2 ++ MatrixSDKTests/MXCryptoBackupTests.m | 24 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m index 871ec07a76..7b1f14d489 100644 --- a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m @@ -16,6 +16,22 @@ #import "MXMegolmBackupAuthData.h" +#import "MXTools.h" + @implementation MXMegolmBackupAuthData +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + + JSONDictionary[@"public_key"] = _publicKey; + + if (_signatures) + { + JSONDictionary[@"signatures"] = [MXTools serialiseJSONObject:_signatures]; + } + + return JSONDictionary; +} + @end diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index b6ba2fca60..37772fb8b6 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -1363,6 +1363,8 @@ - (instancetype)initWithMatrixSession:(MXSession*)matrixSession cryptoQueue:(dis oneTimeKeyCount = -1; + _backup = [[MXKeyBackup alloc] initWithMatrixSession:_mxSession]; + outgoingRoomKeyRequestManager = [[MXOutgoingRoomKeyRequestManager alloc] initWithMatrixRestClient:_matrixRestClient deviceId:myDevice.deviceId diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 939c7c18c5..6a651dcc8b 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -307,6 +307,30 @@ - (void)testRecoveryKey XCTAssertEqualObjects(error.domain, MXRecoveryKeyErrorDomain); } +- (void)testPrepareKeyBackupVersion +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + XCTAssertNotNil(aliceSession.crypto.backup); + XCTAssertFalse(aliceSession.crypto.backup.enabled); + + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + + XCTAssertNotNil(keyBackupCreationInfo); + XCTAssertEqual(keyBackupCreationInfo.algorithm, kMXCryptoMegolmBackupAlgorithm); + XCTAssertNotNil(keyBackupCreationInfo.authData.publicKey); + XCTAssertNotNil(keyBackupCreationInfo.authData.signatures); + XCTAssertNotNil(keyBackupCreationInfo.recoveryKey); + + [expectation fulfill]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop From b5a73e97dd509472a0d78904cd07a7aa94459f7d Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 15:30:18 +0200 Subject: [PATCH 22/58] Keys backup: MXKeyBackup: Manage error in enableKeyBackup --- MatrixSDK/Crypto/Data/MXCryptoConstants.h | 9 ++++++++ MatrixSDK/Crypto/Data/MXCryptoConstants.m | 5 +++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 22 ++++++++++++++----- .../Crypto/KeyBackup/MXKeyBackup_Private.h | 3 ++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/MatrixSDK/Crypto/Data/MXCryptoConstants.h b/MatrixSDK/Crypto/Data/MXCryptoConstants.h index b370abc99b..9271bc1073 100644 --- a/MatrixSDK/Crypto/Data/MXCryptoConstants.h +++ b/MatrixSDK/Crypto/Data/MXCryptoConstants.h @@ -52,3 +52,12 @@ FOUNDATION_EXPORT NSString* const MXEncryptingErrorUnknownDeviceReason; */ FOUNDATION_EXPORT NSString *const MXEncryptingErrorUnknownDeviceDevicesKey; + +#pragma mark - Backup error + +FOUNDATION_EXPORT NSString *const MXKeyBackupErrorDomain; + +typedef enum : NSUInteger +{ + MXKeyBackupErrorInvalidParametersCode +} MXKeyBackupErrorCode; diff --git a/MatrixSDK/Crypto/Data/MXCryptoConstants.m b/MatrixSDK/Crypto/Data/MXCryptoConstants.m index e0ce2f8bf7..c23ea8dd45 100644 --- a/MatrixSDK/Crypto/Data/MXCryptoConstants.m +++ b/MatrixSDK/Crypto/Data/MXCryptoConstants.m @@ -29,3 +29,8 @@ NSString* const MXEncryptingErrorUnknownDeviceReason = @"This room contains unknown devices which have not been verified. We strongly recommend you verify them before continuing."; NSString* const MXEncryptingErrorUnknownDeviceDevicesKey = @"MXEncryptingErrorUnknownDeviceDevicesKey"; + + +#pragma mark - Backup error + +NSString *const MXKeyBackupErrorDomain = @"MXKeyBackupErrorDomain"; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 74250c4c28..58f8dac4ed 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -69,7 +69,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)matrixSession return self; } -- (BOOL)enableKeyBackup:(MXKeyBackupVersion*)version +- (NSError*)enableKeyBackup:(MXKeyBackupVersion*)version { MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData modelFromJSON:version.authData]; if (authData) @@ -81,10 +81,15 @@ - (BOOL)enableKeyBackup:(MXKeyBackupVersion*)version self.state = MXKeyBackupStateReadyToBackUp; [self maybeSendKeyBackup]; + + return nil; } - // wdty? - return _backupKey; + return [NSError errorWithDomain:MXKeyBackupErrorDomain + code:MXKeyBackupErrorInvalidParametersCode + userInfo:@{ + NSLocalizedDescriptionKey: @"Invalid authentication data", + }]; } - (void)disableKeyBackup @@ -298,10 +303,17 @@ - (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBacku keyBackupVersion.version = version; - [self enableKeyBackup:keyBackupVersion]; + NSError *error = [self enableKeyBackup:keyBackupVersion]; dispatch_async(dispatch_get_main_queue(), ^{ - success(keyBackupVersion); + if (!error) + { + success(keyBackupVersion); + } + else if (failure) + { + failure(error); + } }); } failure:^(NSError *error) { diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h index aefe73e6da..8a82c54e82 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h @@ -32,8 +32,9 @@ NS_ASSUME_NONNULL_BEGIN Enable backing up of keys. @param keyBackupVersion backup information object as returned by `[MXKeyBackup version]`. + @return an error if the operation fails. */ -- (BOOL)enableKeyBackup:(MXKeyBackupVersion*)keyBackupVersion; +- (NSError*)enableKeyBackup:(MXKeyBackupVersion*)keyBackupVersion; /** * Disable backing up of keys. From 901727103302b9b1977d987e010fb2bb75b1c7b2 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 17:09:56 +0200 Subject: [PATCH 23/58] Keys backup: MXKeyBackup: Manage error in backupAllGroupSessions --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 8 +-- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 69 +++++++++++++++++++----- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index 599bdb9928..66959f2501 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -121,13 +121,15 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; @param version the backup version to delete. - @param success A block object called when the operation succeeds. + @param success A block object called when the operation complets. + @param progress A block object called to indicate operation progress based on number of backed up keys. @param failure A block object called when the operation fails. @return a MXHTTPOperation instance. */ -- (void)backupAllGroupSessions:(nullable void (^)(NSProgress *backupProgress))onBackupProgress - onComplete:(nullable void (^)(void))onComplete; +- (void)backupAllGroupSessions:(nullable void (^)(void))success + progress:(nullable void (^)(NSProgress *backupProgress))progress + failure:(nullable void (^)(NSError *error))failure; /** Get the current backup progress. diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 58f8dac4ed..abc9962078 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -43,6 +43,12 @@ @interface MXKeyBackup () { MXSession *mxSession; + + // Observer to kMXKeyBackupDidStateChangeNotification when backupAllGroupSessions is progressing + id backupAllGroupSessionsObserver; + + // Failure block when backupAllGroupSessions is progressing + void (^backupAllGroupSessionsFailure)(NSError *error); // track whether this device's megolm keys are being backed up incrementally // to the server or not. @@ -94,6 +100,8 @@ - (NSError*)enableKeyBackup:(MXKeyBackupVersion*)version - (void)disableKeyBackup { + [self resetBackupAllGroupSessionsObjects]; + _keyBackupVersion = nil; _backupKey = nil; self.state = MXKeyBackupStateDisabled; @@ -232,7 +240,14 @@ - (void)sendKeyBackup } } failure:^(NSError *error) { - // TODO: Manage failure + MXStrongifyAndReturnIfNil(self); + + if (self->backupAllGroupSessionsFailure) + { + self->backupAllGroupSessionsFailure(error); + } + + // TODO: Manage retries NSLog(@"[MXKeyBackup] sendKeyBackup: sendKeysBackup failed. Error: %@", error); }]; } @@ -374,46 +389,52 @@ - (MXHTTPOperation*)deleteKeyBackupVersion:(NSString*)version return operation; } -- (void)backupAllGroupSessions:(nullable void (^)(NSProgress * _Nonnull))onBackupProgress - onComplete:(nullable void (^)(void))onComplete +- (void)backupAllGroupSessions:(nullable void (^)(void))success + progress:(nullable void (^)(NSProgress *backupProgress))progress + failure:(nullable void (^)(NSError *error))failure; { // Get a status right now MXWeakify(self); [self backupProgress:^(NSProgress * _Nonnull backupProgress) { MXStrongifyAndReturnIfNil(self); + // Reset previous state if any + [self resetBackupAllGroupSessionsObjects]; + NSLog(@"[MXKeyBackup] backupAllGroupSessions: backupProgress: %@", backupProgress); - if (onBackupProgress) + if (progress) { - onBackupProgress(backupProgress); + progress(backupProgress); } if (backupProgress.finished) { NSLog(@"[MXKeyBackup] backupAllGroupSessions: complete"); - onComplete(); + if (success) + { + success(); + } return; } // Listen to `self.state` change to determine when to call onBackupProgress and onComplete MXWeakify(self); - id observer; - observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + self->backupAllGroupSessionsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { MXStrongifyAndReturnIfNil(self); - if (onBackupProgress) + if (progress) { - [self backupProgress:onBackupProgress]; + [self backupProgress:progress]; } if (self.state == MXKeyBackupStateReadyToBackUp) { - [[NSNotificationCenter defaultCenter] removeObserver:observer]; + [self resetBackupAllGroupSessionsObjects]; - if (onComplete) + if (success) { - onComplete(); + success(); } } }]; @@ -421,11 +442,33 @@ - (void)backupAllGroupSessions:(nullable void (^)(NSProgress * _Nonnull))onBacku dispatch_async(self->mxSession.crypto.cryptoQueue, ^{ MXStrongifyAndReturnIfNil(self); + // Listen to error + if (failure) + { + MXWeakify(self); + self->backupAllGroupSessionsFailure = ^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + + failure(error); + [self resetBackupAllGroupSessionsObjects]; + }; + } + [self sendKeyBackup]; }); }]; } +- (void)resetBackupAllGroupSessionsObjects +{ + if (backupAllGroupSessionsObserver) + { + [[NSNotificationCenter defaultCenter] removeObserver:backupAllGroupSessionsObserver]; + backupAllGroupSessionsObserver = nil; + } + backupAllGroupSessionsFailure = nil; +} + - (void)backupProgress:(void (^)(NSProgress *backupProgress))backupProgress { MXWeakify(self); From 4982f4a1907d3f1824a650aba28a3a8adfd5e359 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 17:10:43 +0200 Subject: [PATCH 24/58] Keys backup: Fix [MXRestClient sendKeysBackup:] --- MatrixSDK/MXRestClient.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index cb29a3ba1b..e8888e3d4d 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -3911,7 +3911,7 @@ - (MXHTTPOperation*)sendKeysBackup:(MXKeysBackupData*)keysBackupData success:(void (^)(void))success failure:(void (^)(NSError *error))failure { - NSString *path = [self keyBackupPath:nil session:nil version:nil]; + NSString *path = [self keyBackupPath:nil session:nil version:version]; if (!path || !keysBackupData) { NSLog(@"[MXRestClient] sendKeysBackup: ERROR: Bad parameters"); From 58718d3e6765c2074a2617a5778c52d215a1c061 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 17:24:30 +0200 Subject: [PATCH 25/58] Keys backup: Mark keys as backed up when they are --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index abc9962078..d539991542 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -226,6 +226,12 @@ - (void)sendKeyBackup [mxSession.crypto.matrixRestClient sendKeysBackup:keysBackupData version:_keyBackupVersion.version success:^{ MXStrongifyAndReturnIfNil(self); + // Mark keys as backed up + for (MXOlmInboundGroupSession *session in sessions) + { + [self->mxSession.crypto.store markBackupDoneForInboundGroupSessionWithId:session.session.sessionIdentifier andSenderKey:session.senderKey]; + } + if (sessions.count < kMXKeyBackupSendKeysMaxCount) { NSLog(@"[MXKeyBackup] sendKeyBackup: All keys have been backed up"); From 8fd1859dba99fec4aa7f8c1a4b1b7ef59f0e551f Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 17:28:06 +0200 Subject: [PATCH 26/58] Keys backup: [MXKeyBackup backupAllGroupSessions]: make sure progress is called with 100% before success --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index d539991542..4239d67a3b 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -429,9 +429,11 @@ - (void)backupAllGroupSessions:(nullable void (^)(void))success self->backupAllGroupSessionsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { MXStrongifyAndReturnIfNil(self); + [self backupProgress:^(NSProgress * _Nonnull backupProgress) { + if (progress) { - [self backupProgress:progress]; + progress(backupProgress); } if (self.state == MXKeyBackupStateReadyToBackUp) @@ -443,6 +445,7 @@ - (void)backupAllGroupSessions:(nullable void (^)(void))success success(); } } + }]; }]; dispatch_async(self->mxSession.crypto.cryptoQueue, ^{ From 1b60df3ffed0f64af1bd56a6907752c0780eefb8 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 17:28:34 +0200 Subject: [PATCH 27/58] Keys backup: Fix and finish MXMegolmBackupAuthData implementation --- .../KeyBackup/Data/MXMegolmBackupAuthData.m | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m index 7b1f14d489..c6cbca00de 100644 --- a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m @@ -16,10 +16,22 @@ #import "MXMegolmBackupAuthData.h" -#import "MXTools.h" - @implementation MXMegolmBackupAuthData +#pragma mark - MXJSONModel + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXMegolmBackupAuthData *megolmBackupAuthData = [MXMegolmBackupAuthData new]; + if (megolmBackupAuthData) + { + MXJSONModelSetString(megolmBackupAuthData.publicKey, JSONDictionary[@"public_key"]); + MXJSONModelSetDictionary(megolmBackupAuthData.signatures, JSONDictionary[@"signatures"]); + } + + return megolmBackupAuthData; +} + - (NSDictionary *)JSONDictionary { NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; @@ -28,7 +40,7 @@ - (NSDictionary *)JSONDictionary if (_signatures) { - JSONDictionary[@"signatures"] = [MXTools serialiseJSONObject:_signatures]; + JSONDictionary[@"signatures"] = _signatures; } return JSONDictionary; From ea1d1ef390bf2c25cba5c72cc3c873c4ce15b327 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 25 Oct 2018 17:53:53 +0200 Subject: [PATCH 28/58] Keys backup: Add tests --- MatrixSDKTests/MXCryptoBackupTests.m | 124 ++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 6a651dcc8b..2c6e0adea2 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -307,6 +307,9 @@ - (void)testRecoveryKey XCTAssertEqualObjects(error.domain, MXRecoveryKeyErrorDomain); } +/** + Check that `[MXKeyBackup prepareKeyBackupVersion` returns valid data + */ - (void)testPrepareKeyBackupVersion { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -314,10 +317,11 @@ - (void)testPrepareKeyBackupVersion XCTAssertNotNil(aliceSession.crypto.backup); XCTAssertFalse(aliceSession.crypto.backup.enabled); + // Check that `[MXKeyBackup prepareKeyBackupVersion` returns valid data [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { XCTAssertNotNil(keyBackupCreationInfo); - XCTAssertEqual(keyBackupCreationInfo.algorithm, kMXCryptoMegolmBackupAlgorithm); + XCTAssertEqualObjects(keyBackupCreationInfo.algorithm, kMXCryptoMegolmBackupAlgorithm); XCTAssertNotNil(keyBackupCreationInfo.authData.publicKey); XCTAssertNotNil(keyBackupCreationInfo.authData.signatures); XCTAssertNotNil(keyBackupCreationInfo.recoveryKey); @@ -331,6 +335,124 @@ - (void)testPrepareKeyBackupVersion }]; } +/** + Check that `[MXKeyBackup createKeyBackupVersion` returns valid data + */ +- (void)testCreateKeyBackupVersion +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + XCTAssertFalse(aliceSession.crypto.backup.enabled); + + // Check that `[MXKeyBackup createKeyBackupVersion` returns valid data + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + + XCTAssertEqualObjects(keyBackupVersion.algorithm, kMXCryptoMegolmBackupAlgorithm); + XCTAssertEqualObjects(keyBackupVersion.authData, keyBackupCreationInfo.authData.JSONDictionary); + XCTAssertNotNil(keyBackupVersion.version); + + // Backup must be enable now + XCTAssertTrue(aliceSession.crypto.backup.enabled); + + [expectation fulfill]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + +/** + - Check that `[MXKeyBackup createKeyBackupVersion` launches the backup + - Check the backup completes + */ +- (void)testBackupCreateKeyBackupVersion +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + + // Check that `[MXKeyBackup createKeyBackupVersion` launches the backup + XCTAssert(aliceSession.crypto.backup.state == MXKeyBackupStateEnabling + || aliceSession.crypto.backup.state == MXKeyBackupStateWillBackUp); + + NSUInteger keys = [aliceSession.crypto.store inboundGroupSessionsCount:NO]; + + [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:aliceSession.crypto.backup queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + + // Check the backup completes + if (aliceSession.crypto.backup.state == MXKeyBackupStateReadyToBackUp) + { + NSUInteger backedUpkeys = [aliceSession.crypto.store inboundGroupSessionsCount:YES]; + XCTAssertEqual(backedUpkeys, keys, @"All keys must have been marked as backed up"); + + [expectation fulfill]; + } + }]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + +/** + Check that `[MXKeyBackup backupAllGroupSessions` returns valid data + */ +- (void)testBackupAllGroupSessions +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + // Check that `[MXKeyBackup backupAllGroupSessions` returns valid data + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + + NSUInteger keys = [aliceSession.crypto.store inboundGroupSessionsCount:NO]; + __block NSUInteger lastbackedUpkeysProgress = 0; + + [aliceSession.crypto.backup backupAllGroupSessions:^{ + + NSUInteger backedUpkeys = [aliceSession.crypto.store inboundGroupSessionsCount:YES]; + XCTAssertEqual(backedUpkeys, keys, @"All keys must have been marked as backed up"); + + XCTAssertEqual(lastbackedUpkeysProgress, keys); + + [expectation fulfill]; + + } progress:^(NSProgress * _Nonnull backupProgress) { + + NSLog(@"---- %@", backupProgress); + + XCTAssertEqual(backupProgress.totalUnitCount, keys); + lastbackedUpkeysProgress = backupProgress.completedUnitCount; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop From fedadea3624f65c8d367f815730327b4904b81bc Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 26 Oct 2018 11:47:06 +0200 Subject: [PATCH 29/58] Keys backup: MXRestClient: Improve naming of the new methods --- MatrixSDK/MXRestClient.h | 22 ++++++++++------------ MatrixSDK/MXRestClient.m | 18 +++++++++--------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index 5b54eaac60..a3f0b935fc 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -2138,8 +2138,8 @@ typedef enum : NSUInteger /** Retrieve the backup for a session key from the homeserver. - @param roomId the id of the room that the keys are for. @param sessionId the id of the session that the keys are. + @param roomId the id of the room that the keys are for. @param version the backup version. @param success A block object called when the operation succeeds. @@ -2147,16 +2147,15 @@ typedef enum : NSUInteger @return a MXHTTPOperation instance. */ -- (MXHTTPOperation*)keyBackup:(NSString*)roomId - session:(NSString*)sessionId - version:(NSString*)version - success:(void (^)(MXKeyBackupData *keyBackupData))success - failure:(void (^)(NSError *error))failure; +- (MXHTTPOperation*)keyBackupForSession:(NSString*)sessionId + inRoom:(NSString*)roomId + version:(NSString*)version + success:(void (^)(MXKeyBackupData *keyBackupData))success + failure:(void (^)(NSError *error))failure; /** Retrieve the backup for all keys in a room from the homeserver. - @param roomKeysBackupData keys to backup. @param roomId the id of the room that the keys are for. @param version the backup version. @@ -2165,15 +2164,14 @@ typedef enum : NSUInteger @return a MXHTTPOperation instance. */ -- (MXHTTPOperation*)roomKeysBackup:(NSString*)roomId - version:(NSString*)version - success:(void (^)(MXRoomKeysBackupData *roomKeysBackupData))success - failure:(void (^)(NSError *error))failure; +- (MXHTTPOperation*)keysBackupInRoom:(NSString*)roomId + version:(NSString*)version + success:(void (^)(MXRoomKeysBackupData *roomKeysBackupData))success + failure:(void (^)(NSError *error))failure; /** Retrieve all keys backup from the homeserver. - @param keysBackupData keys to backup. @param version the backup version. @param success A block object called when the operation succeeds. diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index e8888e3d4d..5581debf4d 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -3947,11 +3947,11 @@ - (MXHTTPOperation*)sendBackup:(NSDictionary*)backupData }]; } -- (MXHTTPOperation*)keyBackup:(NSString*)roomId - session:(NSString*)sessionId - version:(NSString*)version - success:(void (^)(MXKeyBackupData *keyBackupData))success - failure:(void (^)(NSError *error))failure +- (MXHTTPOperation*)keyBackupForSession:(NSString*)sessionId + inRoom:(NSString*)roomId + version:(NSString*)version + success:(void (^)(MXKeyBackupData *keyBackupData))success + failure:(void (^)(NSError *error))failure { NSString *path = [self keyBackupPath:roomId session:sessionId version:version]; if (!path || !roomId || !sessionId) @@ -3983,10 +3983,10 @@ - (MXHTTPOperation*)keyBackup:(NSString*)roomId }]; } -- (MXHTTPOperation*)roomKeysBackup:(NSString*)roomId - version:(NSString*)version - success:(void (^)(MXRoomKeysBackupData *roomKeysBackupData))success - failure:(void (^)(NSError *error))failure +- (MXHTTPOperation*)keysBackupInRoom:(NSString*)roomId + version:(NSString*)version + success:(void (^)(MXRoomKeysBackupData *roomKeysBackupData))success + failure:(void (^)(NSError *error))failure { NSString *path = [self keyBackupPath:roomId session:nil version:version]; if (!path || !roomId) From 5ecdefe20606bddd2b7c76f0302cd83e41a3b69a Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 26 Oct 2018 16:54:26 +0200 Subject: [PATCH 30/58] MXCrypto: importRoomKeys methods now return number of imported keys. --- CHANGES.rst | 3 ++ MatrixSDK/Crypto/Algorithms/MXDecrypting.h | 4 +- .../Algorithms/Megolm/MXMegolmDecryption.m | 18 ++++++--- .../Crypto/Algorithms/Olm/MXOlmDecryption.m | 3 +- MatrixSDK/Crypto/MXCrypto.h | 6 ++- MatrixSDK/Crypto/MXCrypto.m | 39 +++++++++++++------ MatrixSDK/Crypto/MXCrypto_Private.h | 17 ++++++++ MatrixSDK/Crypto/MXOlmDevice.h | 5 ++- MatrixSDK/Crypto/MXOlmDevice.m | 6 ++- MatrixSDKTests/MXCryptoTests.m | 12 ++++-- 10 files changed, 85 insertions(+), 28 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 89d899e1e5..e01acb0260 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,6 +23,9 @@ Bug fix: * Left room is still displayed as "Empty room" in rooms list (vector-im/riot-ios/issues/2082). * Reply of reply with unexpected newlines renders badly (vector-im/riot-ios/issues/2086). +API break: +* MXCrypto: importRoomKeys methods now return number of imported keys. + Changes in Matrix iOS SDK in 0.11.5 (2018-10-05) =============================================== diff --git a/MatrixSDK/Crypto/Algorithms/MXDecrypting.h b/MatrixSDK/Crypto/Algorithms/MXDecrypting.h index e6d843bc7e..ab5e34fe06 100644 --- a/MatrixSDK/Crypto/Algorithms/MXDecrypting.h +++ b/MatrixSDK/Crypto/Algorithms/MXDecrypting.h @@ -59,9 +59,11 @@ /** Import a room key. + @param backUp YES to back up them to the homeserver. @param session the session data to import. + @return YES if the key has been imported. */ -- (void)importRoomKey:(MXMegolmSessionData*)session; +- (BOOL)importRoomKey:(MXMegolmSessionData*)session backUp:(BOOL)backUp; /** Determine if we have the keys necessary to respond to a room key request. diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m index ada10f949b..4b25f13c4f 100644 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m +++ b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m @@ -240,14 +240,22 @@ - (void)onRoomKeyEvent:(MXEvent *)event [self retryDecryption:senderKey sessionId:content[@"session_id"]]; } -- (void)importRoomKey:(MXMegolmSessionData *)session +- (BOOL)importRoomKey:(MXMegolmSessionData *)session backUp:(BOOL)backUp { - [olmDevice importInboundGroupSession:session]; + BOOL imported = [olmDevice importInboundGroupSession:session]; + if (imported) + { + // Do not back up the key if it comes from a backup recovery + if (backUp) + { + [crypto.backup maybeSendKeyBackup]; + } - [crypto.backup maybeSendKeyBackup]; + // Have another go at decrypting events sent with this session + [self retryDecryption:session.senderKey sessionId:session.sessionId]; + } - // Have another go at decrypting events sent with this session - [self retryDecryption:session.senderKey sessionId:session.sessionId]; + return imported; } - (BOOL)hasKeysForKeyRequest:(MXIncomingRoomKeyRequest*)keyRequest diff --git a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.m b/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.m index 8f2889ecc1..fcf652d498 100644 --- a/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.m +++ b/MatrixSDK/Crypto/Algorithms/Olm/MXOlmDecryption.m @@ -236,9 +236,10 @@ - (void)onRoomKeyEvent:(MXEvent *)event // No impact for olm } -- (void)importRoomKey:(MXMegolmSessionData *)session +- (BOOL)importRoomKey:(MXMegolmSessionData *)session backUp:(BOOL)backUp { // No impact for olm + return NO; } - (BOOL)hasKeysForKeyRequest:(MXIncomingRoomKeyRequest*)keyRequest diff --git a/MatrixSDK/Crypto/MXCrypto.h b/MatrixSDK/Crypto/MXCrypto.h index b5d10690ad..9d01aa9755 100644 --- a/MatrixSDK/Crypto/MXCrypto.h +++ b/MatrixSDK/Crypto/MXCrypto.h @@ -297,10 +297,11 @@ FOUNDATION_EXPORT NSString *const kMXCryptoRoomKeyRequestCancellationNotificatio Import a list of room keys previously exported by exportRoomKeys. @param success A block object called when the operation succeeds. + It provides the number of found keys and the number of successfully imported keys. @param failure A block object called when the operation fails. */ - (void)importRoomKeys:(NSArray*)keys - success:(void (^)(void))success + success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *error))failure; /** @@ -309,10 +310,11 @@ FOUNDATION_EXPORT NSString *const kMXCryptoRoomKeyRequestCancellationNotificatio @param keyFile the encrypted keys file data. @password the passphrase used to decrypts keys. @param success A block object called when the operation succeeds. + It provides the number of found keys and the number of successfully imported keys. @param failure A block object called when the operation fails. */ - (void)importRoomKeys:(NSData *)keyFile withPassword:(NSString*)password - success:(void (^)(void))success + success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *error))failure; diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index 37772fb8b6..cdee6a6b49 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -1001,18 +1001,32 @@ - (void)exportRoomKeysWithPassword:(NSString *)password success:(void (^)(NSData #endif } -- (void)importRoomKeys:(NSArray *)keys success:(void (^)(void))success failure:(void (^)(NSError *))failure +- (void)importRoomKeys:(NSArray *)keys success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *))failure { #ifdef MX_CRYPTO dispatch_async(_decryptionQueue, ^{ NSLog(@"[MXCrypto] importRoomKeys:"); - NSDate *startDate = [NSDate date]; - // Convert JSON to MXMegolmSessionData NSArray *sessionDatas = [MXMegolmSessionData modelsFromJSON:keys]; + [self importMegolmSessionDatas:sessionDatas backUp:YES success:success failure:failure]; + }); +#endif +} + +- (void)importMegolmSessionDatas:(NSArray*)sessionDatas backUp:(BOOL)backUp success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *error))failure +{ +#ifdef MX_CRYPTO + dispatch_async(_decryptionQueue, ^{ + + NSLog(@"[MXCrypto] importMegolmSessionDatas: backUp: %@", @(backUp)); + + NSUInteger imported = 0; + NSUInteger totalKeyCount = sessionDatas.count; + NSDate *startDate = [NSDate date]; + for (MXMegolmSessionData *sessionData in sessionDatas) { NSLog(@" - importing %@|%@", sessionData.senderKey, sessionData.sessionId); @@ -1025,16 +1039,19 @@ - (void)importRoomKeys:(NSArray *)keys success:(void (^)(void))s // Import the session id alg = [self getRoomDecryptor:sessionData.roomId algorithm:sessionData.algorithm]; - [alg importRoomKey:sessionData]; + if ([alg importRoomKey:sessionData backUp:backUp]) + { + imported++; + } } - NSLog(@"[MXCrypto] importRoomKeys: Imported %tu keys in %.0fms", keys.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); + NSLog(@"[MXCrypto] importMegolmSessionDatas: Imported %tu keys from %tu provided keys in %.0fms", imported, totalKeyCount, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); dispatch_async(dispatch_get_main_queue(), ^{ if (success) { - success(); + success(totalKeyCount, imported); } }); @@ -1042,7 +1059,7 @@ - (void)importRoomKeys:(NSArray *)keys success:(void (^)(void))s #endif } -- (void)importRoomKeys:(NSData *)keyFile withPassword:(NSString *)password success:(void (^)(void))success failure:(void (^)(NSError *))failure +- (void)importRoomKeys:(NSData *)keyFile withPassword:(NSString *)password success:(void (^)(NSUInteger total, NSUInteger imported))success failure:(void (^)(NSError *))failure { #ifdef MX_CRYPTO dispatch_async(_decryptionQueue, ^{ @@ -1058,13 +1075,13 @@ - (void)importRoomKeys:(NSData *)keyFile withPassword:(NSString *)password succe NSArray *keys = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; if (keys) { - [self importRoomKeys:keys success:^{ + [self importRoomKeys:keys success:^(NSUInteger total, NSUInteger imported) { - NSLog(@"[MXCrypto] importRoomKeys:withPassord: Imported %tu keys in %.0fms", keys.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); + NSLog(@"[MXCrypto] importRoomKeys:withPassord: Imported %tu keys from %tu provided keys in %.0fms", imported, total, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); if (success) { - success(); + success(total, imported); } } failure:failure]; @@ -1080,9 +1097,7 @@ - (void)importRoomKeys:(NSData *)keyFile withPassword:(NSString *)password succe { failure(error); } - }); - }); #endif } diff --git a/MatrixSDK/Crypto/MXCrypto_Private.h b/MatrixSDK/Crypto/MXCrypto_Private.h index 6d3dc1c57e..63effd1059 100644 --- a/MatrixSDK/Crypto/MXCrypto_Private.h +++ b/MatrixSDK/Crypto/MXCrypto_Private.h @@ -160,6 +160,23 @@ - (NSDictionary*)signObject:(NSDictionary*)object; +#pragma mark - import/export + +/** + Import a list of megolm session keys. + + @param sessionDatas megolm sessions. + @param backUp YES to back up them to the homeserver. + @param success A block object called when the operation succeeds. + It provides the number of found keys and the number of successfully imported keys. + @param failure A block object called when the operation fails. + */ +- (void)importMegolmSessionDatas:(NSArray*)sessionDatas + backUp:(BOOL)backUp + success:(void (^)(NSUInteger total, NSUInteger imported))success + failure:(void (^)(NSError *error))failure; + + #pragma mark - Key sharing /** diff --git a/MatrixSDK/Crypto/MXOlmDevice.h b/MatrixSDK/Crypto/MXOlmDevice.h index be74c39a65..f8203ad895 100644 --- a/MatrixSDK/Crypto/MXOlmDevice.h +++ b/MatrixSDK/Crypto/MXOlmDevice.h @@ -232,9 +232,10 @@ Determine if an incoming messages is a prekey message matching an existing sessi /** Add a previously-exported inbound group session to the session store. - @param data the session data + @param data the session data. + @return YES if the key has been imported. */ -- (void)importInboundGroupSession:(MXMegolmSessionData*)data; +- (BOOL)importInboundGroupSession:(MXMegolmSessionData*)data; /** Decrypt a received message with an inbound group session. diff --git a/MatrixSDK/Crypto/MXOlmDevice.m b/MatrixSDK/Crypto/MXOlmDevice.m index d0135c7d5e..d956182dc1 100644 --- a/MatrixSDK/Crypto/MXOlmDevice.m +++ b/MatrixSDK/Crypto/MXOlmDevice.m @@ -353,7 +353,7 @@ - (BOOL)addInboundGroupSession:(NSString*)sessionId sessionKey:(NSString*)sessio return YES; } -- (void)importInboundGroupSession:(MXMegolmSessionData *)data +- (BOOL)importInboundGroupSession:(MXMegolmSessionData *)data { NSError *error; MXOlmInboundGroupSession *session = [self inboundGroupSessionWithId:data.sessionId senderKey:data.senderKey roomId:data.roomId error:&error]; @@ -364,12 +364,14 @@ - (void)importInboundGroupSession:(MXMegolmSessionData *)data NSLog(@"[MXOlmDevice] importInboundGroupSession: Update for megolm session %@|%@", data.senderKey, data.sessionId); // For now we just ignore updates. TODO: implement something here - return; + return NO; } session = [[MXOlmInboundGroupSession alloc] initWithImportedSessionData:data]; [store storeInboundGroupSession:session]; + + return YES; } - (MXDecryptionResult *)decryptGroupMessage:(NSString *)body roomId:(NSString *)roomId diff --git a/MatrixSDKTests/MXCryptoTests.m b/MatrixSDKTests/MXCryptoTests.m index ef3f37788c..3ee5fef082 100644 --- a/MatrixSDKTests/MXCryptoTests.m +++ b/MatrixSDKTests/MXCryptoTests.m @@ -2341,7 +2341,10 @@ - (void)testImportRoomKeys }]; // Import the exported keys - [bobSession.crypto importRoomKeys:keys success:^{ + [bobSession.crypto importRoomKeys:keys success:^(NSUInteger total, NSUInteger imported) { + + XCTAssertGreaterThan(total, 0); + XCTAssertEqual(total, imported); XCTAssertEqual(encryptedEvents.count, 0, @"All events should have been decrypted after the keys import"); @@ -2422,8 +2425,11 @@ - (void)testExportImportRoomKeysWithPassword }]; // Import the exported keys - [bobSession.crypto importRoomKeys:keyFile withPassword:password success:^{ + [bobSession.crypto importRoomKeys:keyFile withPassword:password success:^(NSUInteger total, NSUInteger imported) { + XCTAssertGreaterThan(total, 0); + XCTAssertEqual(total, imported); + XCTAssertEqual(encryptedEvents.count, 0, @"All events should have been decrypted after the keys import"); [expectation fulfill]; @@ -2468,7 +2474,7 @@ - (void)testImportRoomKeysWithWrongPassword [bobSession.crypto exportRoomKeysWithPassword:@"APassword" success:^(NSData *keyFile) { - [bobSession.crypto importRoomKeys:keyFile withPassword:@"AnotherPassword" success:^{ + [bobSession.crypto importRoomKeys:keyFile withPassword:@"AnotherPassword" success:^(NSUInteger total, NSUInteger imported) { XCTFail(@"The import must fail when using a wrong password"); [expectation fulfill]; From 25acbead7f1dcd6955f59d6e4a0f29ab7359f362 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 26 Oct 2018 17:26:33 +0200 Subject: [PATCH 31/58] Keys backup: Make the full flow of encrypting/decrypting megolm keys and add restoreKeyBackup --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 39 ++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 259 ++++++++++++++++++++--- MatrixSDKTests/MXCryptoBackupTests.m | 72 ++++++- 3 files changed, 338 insertions(+), 32 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index 66959f2501..cfd6b85c98 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -59,6 +59,8 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; */ @interface MXKeyBackup : NSObject +#pragma mark - Backup management + /** Get information about the current backup version. @@ -116,6 +118,9 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; success:(void (^)(void))success failure:(nullable void (^)(NSError *error))failure; + +#pragma mark - Backup storing + /** Start to back up keys immediately. @@ -142,6 +147,40 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; */ - (void)backupProgress:(void (^)(NSProgress *backupProgress))backupProgress; + +#pragma mark - Backup restoring + +/** + Check if a key is a valid recovery key. + + @param recoveryKey the string to valid. + @return YES if valid + */ ++ (BOOL)isValidRecoveryKey:(NSString*)recoveryKey; + +/** + Restore a backup from a given backup version stored on the homeserver. + + @param version the backup version to restore from. + @param recoveryKey the recovery key to decrypt the retrieved backup. + @param roomId the id of the room to get backup data from. + @param sessionId the id of the session to restore. + + @param success A block object called when the operation succeeds. + It provides the number of found keys and the number of successfully imported keys. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)restoreKeyBackup:(NSString*)version + recoveryKey:(NSString*)recoveryKey + room:(nullable NSString*)roomId + session:(nullable NSString*)sessionId + success:(nullable void (^)(NSUInteger total, NSUInteger imported))success + failure:(nullable void (^)(NSError *error))failure; + +#pragma mark - Backup state + /** The backup state. */ diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 4239d67a3b..75cd0fe7fd 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -63,7 +63,7 @@ @interface MXKeyBackup () @implementation MXKeyBackup -#pragma mark - SDK-Private methods +#pragma mark - SDK-Private methods - - (instancetype)initWithMatrixSession:(MXSession *)matrixSession { @@ -169,38 +169,13 @@ - (void)sendKeyBackup for (MXOlmInboundGroupSession *session in sessions) { - // Gather information for each key - // TODO: userId? - MXDeviceInfo *device = [mxSession.crypto.deviceList deviceWithIdentityKey:session.senderKey forUser:nil andAlgorithm:kMXCryptoMegolmAlgorithm]; - - // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at - // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format - MXMegolmSessionData *sessionData = session.exportSessionData; - NSDictionary *sessionBackupData = @{ - @"algorithm": sessionData.algorithm, - @"sender_key": sessionData.senderKey, - @"sender_claimed_keys": sessionData.senderClaimedKeys, - @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ? sessionData.forwardingCurve25519KeyChain : @[], - @"session_key": sessionData.sessionKey - }; - OLMPkMessage *encryptedSessionBackupData = [_backupKey encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; - - // Build backup data for that key - MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; - keyBackupData.firstMessageIndex = session.session.firstKnownIndex; - keyBackupData.forwardedCount = session.forwardingCurve25519KeyChain.count; - keyBackupData.verified = device.verified; - keyBackupData.sessionData = @{ - @"ciphertext": encryptedSessionBackupData.ciphertext, - @"mac": encryptedSessionBackupData.mac, - @"ephemeral": encryptedSessionBackupData.ephemeralKey, - }; + MXKeyBackupData *keyBackupData = [self encryptGroupSession:session withPkEncryption:_backupKey]; if (!roomsKeyBackup[session.roomId]) { roomsKeyBackup[session.roomId] = [NSMutableDictionary dictionary]; } - roomsKeyBackup[session.roomId][sessionData.sessionId] = keyBackupData; + roomsKeyBackup[session.roomId][session.session.sessionIdentifier] = keyBackupData; } // Finalise data to send @@ -259,7 +234,9 @@ - (void)sendKeyBackup } -#pragma mark - Public methods +#pragma mark - Public methods - + +#pragma mark - Backup management - (MXHTTPOperation *)version:(void (^)(MXKeyBackupVersion * _Nonnull))success failure:(void (^)(NSError * _Nonnull))failure { @@ -395,6 +372,9 @@ - (MXHTTPOperation*)deleteKeyBackupVersion:(NSString*)version return operation; } + +#pragma mark - Backup storing + - (void)backupAllGroupSessions:(nullable void (^)(void))success progress:(nullable void (^)(NSProgress *backupProgress))progress failure:(nullable void (^)(NSError *error))failure; @@ -496,13 +476,106 @@ - (void)backupProgress:(void (^)(NSProgress *backupProgress))backupProgress }); } + +#pragma mark - Backup restoring + ++ (BOOL)isValidRecoveryKey:(NSString*)recoveryKey +{ + NSError *error; + NSData *privateKeyOut = [MXRecoveryKey decode:recoveryKey error:&error]; + + return !error && privateKeyOut; +} + +- (MXHTTPOperation*)restoreKeyBackup:(NSString*)version + recoveryKey:(NSString*)recoveryKey + room:(nullable NSString*)roomId + session:(nullable NSString*)sessionId + success:(nullable void (^)(NSUInteger total, NSUInteger imported))success + failure:(nullable void (^)(NSError *error))failure +{ + MXHTTPOperation *operation = [MXHTTPOperation new]; + + MXWeakify(self); + dispatch_async(mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + // Get a PK decryption instance + NSError *error; + OLMPkDecryption *decryption = [self pkDecryptionFromRecoveryKey:recoveryKey error:&error]; + if (error) + { + NSLog(@"[MXKeyBackup] restoreKeyBackup: Invalid recovery key. Error: %@", error); + if (failure) + { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + } + return; + } + + // Get backup from the homeserver + MXWeakify(self); + MXHTTPOperation *operation2 = [self keyBackupForSession:sessionId inRoom:roomId version:version success:^(MXKeysBackupData *keysBackupData) { + MXStrongifyAndReturnIfNil(self); + + NSMutableArray *sessionDatas = [NSMutableArray array]; + + // Restore that data + for (NSString *roomId in keysBackupData.rooms) + { + for (NSString *sessionId in keysBackupData.rooms[roomId].sessions) + { + MXKeyBackupData *keyBackupData = keysBackupData.rooms[roomId].sessions[sessionId]; + + MXMegolmSessionData *sessionData = [self decryptKeyBackupData:keyBackupData forSession:sessionId inRoom:roomId withPkDecryption:decryption]; + + [sessionDatas addObject:sessionData]; + } + } + + // Do not trigger a backup for them if they come from the backup version we are using + BOOL backUp = ![version isEqualToString:self.keyBackupVersion.version]; + + // Import them into the crypto store + [self->mxSession.crypto importMegolmSessionDatas:sessionDatas backUp:backUp success:success failure:^(NSError *error) { + if (failure) + { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + } + }]; + + } failure:^(NSError *error) { + if (failure) + { + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + } + }]; + + if (operation2) + { + [operation mutateTo:operation2]; + } + }); + + return operation; +} + + +#pragma mark - Backup state + - (BOOL)enabled { return _state != MXKeyBackupStateDisabled; } -#pragma mark - Private methods +#pragma mark - Private methods - - (void)setState:(MXKeyBackupState)state { @@ -513,4 +586,130 @@ - (void)setState:(MXKeyBackupState)state }); } +// Same method as [MXRestClient keysBackupInRoom] except that it accepts nullable +// parameters and always returns a MXKeysBackupData object +- (MXHTTPOperation*)keyBackupForSession:(nullable NSString*)sessionId + inRoom:(nullable NSString*)roomId + version:(NSString*)version + success:(void (^)(MXKeysBackupData *keysBackupData))success + failure:(void (^)(NSError *error))failure; +{ + MXHTTPOperation *operation; + + if (!sessionId && !roomId) + { + operation = [mxSession.crypto.matrixRestClient keysBackup:version success:success failure:failure]; + } + else if (!sessionId) + { + operation = [mxSession.crypto.matrixRestClient keysBackupInRoom:roomId version:version success:^(MXRoomKeysBackupData *roomKeysBackupData) { + + MXKeysBackupData *keysBackupData = [MXKeysBackupData new]; + keysBackupData.rooms = @{ + roomId: roomKeysBackupData + }; + + success(keysBackupData); + + } failure:failure]; + } + else + { + operation = [mxSession.crypto.matrixRestClient keyBackupForSession:sessionId inRoom:roomId version:version success:^(MXKeyBackupData *keyBackupData) { + + MXRoomKeysBackupData *roomKeysBackupData = [MXRoomKeysBackupData new]; + roomKeysBackupData.sessions = @{ + sessionId: keyBackupData + }; + + MXKeysBackupData *keysBackupData = [MXKeysBackupData new]; + keysBackupData.rooms = @{ + roomId: roomKeysBackupData + }; + + success(keysBackupData); + + } failure:failure]; + } + + return operation; +} + +- (OLMPkDecryption*)pkDecryptionFromRecoveryKey:(NSString*)recoveryKey error:(NSError **)error +{ + // Extract the primary key + NSData *privateKey = [MXRecoveryKey decode:recoveryKey error:error]; + + // Built the PK decryption with it + OLMPkDecryption *decryption; + if (privateKey) + { + decryption = [OLMPkDecryption new]; + [decryption setPrivateKey:privateKey error:error]; + } + + return decryption; +} + +- (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withPkEncryption:(OLMPkEncryption*)encryption +{ + // Gather information for each key + // TODO: userId? + MXDeviceInfo *device = [mxSession.crypto.deviceList deviceWithIdentityKey:session.senderKey forUser:nil andAlgorithm:kMXCryptoMegolmAlgorithm]; + + // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at + // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format + MXMegolmSessionData *sessionData = session.exportSessionData; + NSDictionary *sessionBackupData = @{ + @"algorithm": sessionData.algorithm, + @"sender_key": sessionData.senderKey, + @"sender_claimed_keys": sessionData.senderClaimedKeys, + @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ? sessionData.forwardingCurve25519KeyChain : @[], + @"session_key": sessionData.sessionKey + }; + OLMPkMessage *encryptedSessionBackupData = [_backupKey encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; + + // Build backup data for that key + MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; + keyBackupData.firstMessageIndex = session.session.firstKnownIndex; + keyBackupData.forwardedCount = session.forwardingCurve25519KeyChain.count; + keyBackupData.verified = device.verified; + keyBackupData.sessionData = @{ + @"ciphertext": encryptedSessionBackupData.ciphertext, + @"mac": encryptedSessionBackupData.mac, + @"ephemeral": encryptedSessionBackupData.ephemeralKey, + }; + + return keyBackupData; +} + +- (MXMegolmSessionData*)decryptKeyBackupData:(MXKeyBackupData*)keyBackupData forSession:(NSString*)sessionId inRoom:(NSString*)roomId withPkDecryption:(OLMPkDecryption*)decryption +{ + MXMegolmSessionData *sessionData; + + NSString *ciphertext, *mac, *ephemeralKey; + + MXJSONModelSetString(ciphertext, keyBackupData.sessionData[@"ciphertext"]); + MXJSONModelSetString(mac, keyBackupData.sessionData[@"mac"]); + MXJSONModelSetString(ephemeralKey, keyBackupData.sessionData[@"ephemeral"]); + + if (ciphertext && mac && ephemeralKey) + { + OLMPkMessage *encrypted = [[OLMPkMessage alloc] initWithCiphertext:ciphertext mac:mac ephemeralKey:ephemeralKey]; + + NSError *error; + NSDictionary *sessionBackupData = [MXTools deserialiseJSONString:[decryption decryptMessage:encrypted error:&error]]; + + if (sessionBackupData) + { + MXJSONModelSetMXJSONModel(sessionData, MXMegolmSessionData, sessionBackupData); + + sessionData.sessionId = sessionId; + sessionData.roomId = roomId; + } + } + + return sessionData; +} + @end diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 2c6e0adea2..908e9cab45 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -22,6 +22,15 @@ #import "MXCrypto_Private.h" #import "MXRecoveryKey.h" +@interface MXKeyBackup (Testing) + +- (OLMPkDecryption*)pkDecryptionFromRecoveryKey:(NSString*)recoveryKey error:(NSError **)error; +- (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withPkEncryption:(OLMPkEncryption*)encryption; +- (MXMegolmSessionData*)decryptKeyBackupData:(MXKeyBackupData*)keyBackupData forSession:(NSString*)sessionId inRoom:(NSString*)roomId withPkDecryption:(OLMPkDecryption*)decryption; + +@end + + // Do not bother with retain cycles warnings in tests #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" @@ -307,6 +316,19 @@ - (void)testRecoveryKey XCTAssertEqualObjects(error.domain, MXRecoveryKeyErrorDomain); } +- (void)testIsValidRecoveryKey +{ + NSString *recoveryKey = @"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d"; + NSString *invalidRecoveryKey1 = @"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4e"; + NSString *invalidRecoveryKey2 = @"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4f"; + NSString *invalidRecoveryKey3 = @"EqTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d"; + + XCTAssertTrue([MXKeyBackup isValidRecoveryKey:recoveryKey]); + XCTAssertFalse([MXKeyBackup isValidRecoveryKey:invalidRecoveryKey1]); + XCTAssertFalse([MXKeyBackup isValidRecoveryKey:invalidRecoveryKey2]); + XCTAssertFalse([MXKeyBackup isValidRecoveryKey:invalidRecoveryKey3]); +} + /** Check that `[MXKeyBackup prepareKeyBackupVersion` returns valid data */ @@ -433,8 +455,6 @@ - (void)testBackupAllGroupSessions } progress:^(NSProgress * _Nonnull backupProgress) { - NSLog(@"---- %@", backupProgress); - XCTAssertEqual(backupProgress.totalUnitCount, keys); lastbackedUpkeysProgress = backupProgress.completedUnitCount; @@ -453,6 +473,54 @@ - (void)testBackupAllGroupSessions }]; } +/** + Check that encryption and decryption of megolm keys + - Pick a megolm key + - Check [MXKeyBackup encryptGroupSession] returns stg + - Check [MXKeyBackup pkDecryptionFromRecoveryKey] is able to create a OLMPkDecryption + - Check [MXKeyBackup decryptKeyBackupData] returns stg + - Compare the decrypted megolm key with the original one + */ +- (void)testEncryptAndDecryptKeyBackupData +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + // Pick a megolm key + MXOlmInboundGroupSession *session = [aliceSession.crypto.store inboundGroupSessionsToBackup:1].firstObject; + + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + + // Check [MXKeyBackup encryptGroupSession] returns stg + MXKeyBackupData *keyBackupData = [aliceSession.crypto.backup encryptGroupSession:session withPkEncryption:aliceSession.crypto.backup.backupKey]; + XCTAssertNotNil(keyBackupData); + XCTAssertNotNil(keyBackupData.sessionData); + + // Check [MXKeyBackup pkDecryptionFromRecoveryKey] is able to create a OLMPkDecryption + OLMPkDecryption *decryption = [aliceSession.crypto.backup pkDecryptionFromRecoveryKey:keyBackupCreationInfo.recoveryKey error:nil]; + XCTAssertNotNil(decryption); + + // Check [MXKeyBackup decryptKeyBackupData] returns stg + MXMegolmSessionData *sessionData = [aliceSession.crypto.backup decryptKeyBackupData:keyBackupData forSession:session.session.sessionIdentifier inRoom:roomId withPkDecryption:decryption]; + XCTAssertNotNil(sessionData); + + // Compare the decrypted megolm key with the original one + XCTAssertEqualObjects(session.exportSessionData.JSONDictionary, sessionData.JSONDictionary); + + [expectation fulfill]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + }]; +} + @end #pragma clang diagnostic pop From e920cbd5b7b16bef00c629a73c0f4663c5bd298d Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 26 Oct 2018 18:54:30 +0200 Subject: [PATCH 32/58] Keys backup: Add testRestoreKeyBackup WIP. It does not pass --- MatrixSDKTests/MXCryptoBackupTests.m | 91 +++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 908e9cab45..2db8eed7a4 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -431,13 +431,13 @@ - (void)testBackupCreateKeyBackupVersion } /** - Check that `[MXKeyBackup backupAllGroupSessions` returns valid data + Check that `[MXKeyBackup backupAllGroupSessions]` returns valid data */ - (void)testBackupAllGroupSessions { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - // Check that `[MXKeyBackup backupAllGroupSessions` returns valid data + // Check that `[MXKeyBackup backupAllGroupSessions]` returns valid data [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { @@ -485,26 +485,26 @@ - (void)testEncryptAndDecryptKeyBackupData { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - // Pick a megolm key + // - Pick a megolm key MXOlmInboundGroupSession *session = [aliceSession.crypto.store inboundGroupSessionsToBackup:1].firstObject; [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - // Check [MXKeyBackup encryptGroupSession] returns stg + // - Check [MXKeyBackup encryptGroupSession] returns stg MXKeyBackupData *keyBackupData = [aliceSession.crypto.backup encryptGroupSession:session withPkEncryption:aliceSession.crypto.backup.backupKey]; XCTAssertNotNil(keyBackupData); XCTAssertNotNil(keyBackupData.sessionData); - // Check [MXKeyBackup pkDecryptionFromRecoveryKey] is able to create a OLMPkDecryption + // - Check [MXKeyBackup pkDecryptionFromRecoveryKey] is able to create a OLMPkDecryption OLMPkDecryption *decryption = [aliceSession.crypto.backup pkDecryptionFromRecoveryKey:keyBackupCreationInfo.recoveryKey error:nil]; XCTAssertNotNil(decryption); - // Check [MXKeyBackup decryptKeyBackupData] returns stg + // - Check [MXKeyBackup decryptKeyBackupData] returns stg MXMegolmSessionData *sessionData = [aliceSession.crypto.backup decryptKeyBackupData:keyBackupData forSession:session.session.sessionIdentifier inRoom:roomId withPkDecryption:decryption]; XCTAssertNotNil(sessionData); - // Compare the decrypted megolm key with the original one + // - Compare the decrypted megolm key with the original one XCTAssertEqualObjects(session.exportSessionData.JSONDictionary, sessionData.JSONDictionary); [expectation fulfill]; @@ -521,6 +521,83 @@ - (void)testEncryptAndDecryptKeyBackupData }]; } +/** + - Do an e2e backup to the homeserver + - Log Alice on a new device + - Restore the e2e backup from the homeserver + - Imported keys number must be correct + - The new device must have the same count of megolm keys + - Alice must have the same keys on both devices + */ +- (void)testRestoreKeyBackup +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + [aliceSession.crypto exportRoomKeys:^(NSArray *aliceKeys1) { + + NSArray *aliceKeys = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; + + // - Do an e2e backup to the homeserver + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + [aliceSession.crypto.backup backupAllGroupSessions:^{ + + // - Log Alice on a new device + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + [matrixSDKTestsData relogUserSessionWithNewDevice:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + + // Test check: aliceSession2 has no keys at login + XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:NO], 0); + + // - Restore the e2e backup from the homeserver + [aliceSession2.crypto.backup restoreKeyBackup:keyBackupVersion.version + recoveryKey:keyBackupCreationInfo.recoveryKey + room:nil session:nil + success:^(NSUInteger total, NSUInteger imported) + { + // - Imported keys number must be correct + XCTAssertEqual(total, aliceKeys.count); + XCTAssertEqual(total, imported); + + // - The new device must have the same count of megolm keys + XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:NO], aliceKeys.count); + XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:YES], aliceKeys.count); + + // - Alice must have the same keys on both devices + [aliceSession2.crypto exportRoomKeys:^(NSArray *aliceKeys2) { + + XCTAssertEqualObjects(aliceKeys1, aliceKeys2); + [expectation fulfill]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; + } progress:nil failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop From fb46439afa39d8acfefe69558b7a052896706353 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 29 Oct 2018 10:33:57 +0100 Subject: [PATCH 33/58] Keys backup: Do not backup keys we got from a backup recovery --- MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m | 4 ++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m index 4b25f13c4f..2dcb9bb731 100644 --- a/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m +++ b/MatrixSDK/Crypto/Algorithms/Megolm/MXMegolmDecryption.m @@ -250,6 +250,10 @@ - (BOOL)importRoomKey:(MXMegolmSessionData *)session backUp:(BOOL)backUp { [crypto.backup maybeSendKeyBackup]; } + else + { + [crypto.store markBackupDoneForInboundGroupSessionWithId:session.sessionId andSenderKey:session.senderKey]; + } // Have another go at decrypting events sent with this session [self retryDecryption:session.senderKey sessionId:session.sessionId]; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 75cd0fe7fd..4baf9f2729 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -496,6 +496,8 @@ - (MXHTTPOperation*)restoreKeyBackup:(NSString*)version { MXHTTPOperation *operation = [MXHTTPOperation new]; + NSLog(@"[MXKeyBackup] restoreKeyBackup: From backup version: %@", version); + MXWeakify(self); dispatch_async(mxSession.crypto.cryptoQueue, ^{ MXStrongifyAndReturnIfNil(self); @@ -535,8 +537,14 @@ - (MXHTTPOperation*)restoreKeyBackup:(NSString*)version } } + NSLog(@"[MXKeyBackup] restoreKeyBackup: Got %@ keys from the backup store on the homeserver", @(sessionDatas.count)); + // Do not trigger a backup for them if they come from the backup version we are using BOOL backUp = ![version isEqualToString:self.keyBackupVersion.version]; + if (backUp) + { + NSLog(@"[MXKeyBackup] restoreKeyBackup: Those keys will be backed up to backup version: %@", self.keyBackupVersion.version); + } // Import them into the crypto store [self->mxSession.crypto importMegolmSessionDatas:sessionDatas backUp:backUp success:success failure:^(NSError *error) { From 40016ad0420fded2790865531403d5c49d997cf4 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 29 Oct 2018 10:34:38 +0100 Subject: [PATCH 34/58] Keys backup: Fix testRestoreKeyBackup --- MatrixSDKTests/MXCryptoBackupTests.m | 89 ++++++++++++++-------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 2db8eed7a4..93870c6b45 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -533,57 +533,54 @@ - (void)testRestoreKeyBackup { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { - [aliceSession.crypto exportRoomKeys:^(NSArray *aliceKeys1) { + NSArray *aliceKeys1 = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; - NSArray *aliceKeys = [aliceSession.crypto.store inboundGroupSessionsToBackup:100]; - - // - Do an e2e backup to the homeserver - [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { - [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { - [aliceSession.crypto.backup backupAllGroupSessions:^{ + // - Do an e2e backup to the homeserver + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + [aliceSession.crypto.backup backupAllGroupSessions:^{ - // - Log Alice on a new device - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; - [matrixSDKTestsData relogUserSessionWithNewDevice:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { - [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + // - Log Alice on a new device + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + [matrixSDKTestsData relogUserSessionWithNewDevice:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + + // Test check: aliceSession2 has no keys at login + XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:NO], 0); + + // - Restore the e2e backup from the homeserver + [aliceSession2.crypto.backup restoreKeyBackup:keyBackupVersion.version + recoveryKey:keyBackupCreationInfo.recoveryKey + room:nil session:nil + success:^(NSUInteger total, NSUInteger imported) + { + // - Imported keys number must be correct + XCTAssertEqual(total, aliceKeys1.count); + XCTAssertEqual(total, imported); + + // - The new device must have the same count of megolm keys + XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:NO], aliceKeys1.count); + + // TODO: This test will pass once the backup will be started automatically when a backup version + // is detected + XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:YES], aliceKeys1.count); + + // - Alice must have the same keys on both devices + for (MXOlmInboundGroupSession *aliceKey1 in aliceKeys1) + { + MXOlmInboundGroupSession *aliceKey2 = [aliceSession2.crypto.store inboundGroupSessionWithId:aliceKey1.session.sessionIdentifier andSenderKey:aliceKey1.senderKey]; + XCTAssertNotNil(aliceKey2); + XCTAssertEqualObjects(aliceKey2.exportSessionData.JSONDictionary, aliceKey1.exportSessionData.JSONDictionary); + } - // Test check: aliceSession2 has no keys at login - XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:NO], 0); + [expectation fulfill]; - // - Restore the e2e backup from the homeserver - [aliceSession2.crypto.backup restoreKeyBackup:keyBackupVersion.version - recoveryKey:keyBackupCreationInfo.recoveryKey - room:nil session:nil - success:^(NSUInteger total, NSUInteger imported) - { - // - Imported keys number must be correct - XCTAssertEqual(total, aliceKeys.count); - XCTAssertEqual(total, imported); - - // - The new device must have the same count of megolm keys - XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:NO], aliceKeys.count); - XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:YES], aliceKeys.count); - - // - Alice must have the same keys on both devices - [aliceSession2.crypto exportRoomKeys:^(NSArray *aliceKeys2) { - - XCTAssertEqualObjects(aliceKeys1, aliceKeys2); - [expectation fulfill]; - - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - } failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; - }]; - }]; - } progress:nil failure:^(NSError * _Nonnull error) { - XCTFail(@"The request should not fail - NSError: %@", error); - [expectation fulfill]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; }]; - } failure:^(NSError * _Nonnull error) { + } progress:nil failure:^(NSError * _Nonnull error) { XCTFail(@"The request should not fail - NSError: %@", error); [expectation fulfill]; }]; From 84a54c5d3f85927bd1c9a929e163852c96ec5434 Mon Sep 17 00:00:00 2001 From: giomfo Date: Tue, 6 Nov 2018 10:06:47 +0100 Subject: [PATCH 35/58] MXSession: configure antivirus scanner use. (#592) The antivirus url is null by default. Set a non null url to configure the antivirus scanner. This url is applied to the current rest client to enable the antivirus server API use. --- MatrixSDK/MXRestClient.h | 10 +++++----- MatrixSDK/MXRestClient.m | 21 ++++++++++++++------- MatrixSDK/MXSession.h | 5 +++++ MatrixSDK/MXSession.m | 8 ++++++++ 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index dd74e90524..9e55fef047 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -123,7 +123,7 @@ typedef enum : NSUInteger @interface MXRestClient : NSObject /** - The homeserver. + The homeserver URL. */ @property (nonatomic, readonly) NSString *homeserver; @@ -150,22 +150,22 @@ typedef enum : NSUInteger @property (nonatomic) NSString *contentPathPrefix; /** - The identity server. + The identity server URL. By default, it points to the defined home server. If needed, change it by setting this property. */ @property (nonatomic) NSString *identityServer; /** - The antivirus server. - By default, it points to the defined home server. If needed, change it by setting - this property. + The antivirus server URL (nil by default). + Set a non-null url to enable the antivirus scanner use. */ @property (nonatomic) NSString *antivirusServer; /** The Client-Server API prefix to use for the antivirus server By default, it is defined by the constant kMXAntivirusAPIPrefixPathUnstable. + In case of a custom path prefix use, set it before settings the antivirus server url. */ @property (nonatomic) NSString *antivirusServerPathPrefix; diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index a93b56d7cd..bbecd6b712 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -157,9 +157,8 @@ -(id)initWithHomeServer:(NSString *)inHomeserver andOnUnrecognizedCertificateBlo } }]; - // By default, use the same address for the identity server, and the antivirus server + // By default, use the same address for the identity server self.identityServer = homeserver; - self.antivirusServer = homeserver; completionQueue = dispatch_get_main_queue(); @@ -223,9 +222,8 @@ -(id)initWithCredentials:(MXCredentials*)inCredentials andOnUnrecognizedCertific } }]; - // By default, use the same address for the identity server, and the antivirus server + // By default, use the same address for the identity server self.identityServer = homeserver; - self.antivirusServer = homeserver; completionQueue = dispatch_get_main_queue(); @@ -3543,9 +3541,18 @@ - (MXHTTPOperation*)signUrl:(NSString*)signUrl #pragma mark - Antivirus server API - (void)setAntivirusServer:(NSString *)antivirusServer { - _antivirusServer = [antivirusServer copy]; - antivirusHttpClient = [[MXHTTPClient alloc] initWithBaseURL:[NSString stringWithFormat:@"%@/%@", antivirusServer, antivirusServerPathPrefix] - andOnUnrecognizedCertificateBlock:nil]; + if (antivirusServer.length) + { + _antivirusServer = [antivirusServer copy]; + antivirusHttpClient = [[MXHTTPClient alloc] initWithBaseURL:[NSString stringWithFormat:@"%@/%@", antivirusServer, antivirusServerPathPrefix] + andOnUnrecognizedCertificateBlock:nil]; + } + else + { + // Disable antivirus requests + _antivirusServer = nil; + antivirusHttpClient = nil; + } } - (MXHTTPOperation*)getAntivirusServerPublicKey:(void (^)(NSString *publicKey))success diff --git a/MatrixSDK/MXSession.h b/MatrixSDK/MXSession.h index 0d8dc78da9..ef712bffec 100644 --- a/MatrixSDK/MXSession.h +++ b/MatrixSDK/MXSession.h @@ -632,6 +632,11 @@ typedef void (^MXOnBackgroundSyncFail)(NSError *error); */ - (MXHTTPOperation*)supportedMatrixVersions:(void (^)(MXMatrixVersions *matrixVersions))success failure:(void (^)(NSError *error))failure; +/** + The antivirus server URL (nil by default). + Set a non-null url to configure the antivirus scanner use. + */ +@property (nonatomic) NSString *antivirusServerURL; #pragma mark - Rooms operations /** diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index 5c7aa9d43f..7e98903b34 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -1566,6 +1566,14 @@ - (MXHTTPOperation*)supportedMatrixVersions:(void (^)(MXMatrixVersions *))succes return [matrixRestClient supportedMatrixVersions:success failure:failure]; } +- (void)setAntivirusServerURL:(NSString *)antivirusServerURL +{ + _antivirusServerURL = antivirusServerURL; + // Update the current restClient + [matrixRestClient setAntivirusServer:antivirusServerURL]; + + // TODO: configure here a scan manager, and update the media manager. +} #pragma mark - Rooms operations From 9f693e6dc841dbb3ef0c422a09e8e87eedf01440 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 6 Nov 2018 11:47:43 +0100 Subject: [PATCH 36/58] Keys backup: Use another lib to manage base58 libbase58 is self contained whereas CBBase58 depends on a static lib and static libs do not work well with Cocoapods --- MatrixSDK.podspec | 2 +- MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h | 3 +- MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m | 66 +++++++++++++++++++++- Podfile | 2 +- SwiftMatrixSDK.podspec | 2 +- 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/MatrixSDK.podspec b/MatrixSDK.podspec index 5fa86f5394..f6c3e74f4b 100644 --- a/MatrixSDK.podspec +++ b/MatrixSDK.podspec @@ -37,7 +37,7 @@ Pod::Spec.new do |s| # Requirements for e2e encryption ss.dependency 'OLMKit', '~> 3.0.0' ss.dependency 'Realm', '~> 3.11.1' - ss.dependency 'CBBase58', '~> 0.9.1' + ss.dependency 'libbase58', '~> 0.1.4' end s.subspec 'JingleCallStack' do |ss| diff --git a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h index 6590b6ac88..91c75cb53f 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h +++ b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.h @@ -29,7 +29,8 @@ FOUNDATION_EXPORT NSString *const MXRecoveryKeyErrorDomain; */ typedef enum : NSUInteger { - MXRecoveryKeyErrorParityCode = 0, + MXRecoveryKeyErrorBase58Code, + MXRecoveryKeyErrorParityCode, MXRecoveryKeyErrorHeaderCode, MXRecoveryKeyErrorLengthCode } MXRecoveryKeyErrorCode; diff --git a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m index b8241e6b53..d2aadd95ea 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m +++ b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m @@ -19,7 +19,8 @@ #import "MXTools.h" #import -#import "NS+BTCBase58.h" + +#import NSString *const MXRecoveryKeyErrorDomain = @"org.matrix.sdk.recoverykey"; @@ -46,7 +47,7 @@ + (NSString *)encode:(NSData *)key [buffer appendBytes:&parity length:sizeof(parity)]; // Encode it in Base58 - NSString *recoveryKey = [buffer base58String]; + NSString *recoveryKey = [self encodeBase58:buffer]; // Add white spaces return [MXTools addWhiteSpacesToString:recoveryKey every:4]; @@ -55,7 +56,18 @@ + (NSString *)encode:(NSData *)key + (NSData *)decode:(NSString *)recoveryKey error:(NSError **)error { NSString *recoveryKeyWithNoSpaces = [recoveryKey stringByReplacingOccurrencesOfString:@" " withString:@""]; - NSMutableData *result = [recoveryKeyWithNoSpaces dataFromBase58]; + NSMutableData *result = [[self decodeBase58:recoveryKeyWithNoSpaces] mutableCopy]; + + if (!result) + { + *error = [NSError errorWithDomain:MXRecoveryKeyErrorDomain + code:MXRecoveryKeyErrorBase58Code + userInfo:@{ + NSLocalizedDescriptionKey: @"Cannot decode Base58 string", + }]; + return nil; + } + // Check the checksum UInt8 parity = 0; @@ -116,4 +128,52 @@ + (NSData *)decode:(NSString *)recoveryKey error:(NSError **)error return result; } + +#pragma mark - Private methods + ++ (NSString *)encodeBase58:(NSData *)data +{ + NSString *base58; + + // Get the required buffer size + size_t base58Length = 0; + b58enc(nil, &base58Length, data.bytes, data.length); + + // Encode + NSMutableData *base58Data = [NSMutableData dataWithLength:base58Length]; + BOOL result = b58enc(base58Data.mutableBytes, &base58Length, data.bytes, data.length); + + if (result) + { + base58 = [[NSString alloc] initWithData:base58Data encoding:NSUTF8StringEncoding]; + base58 = [base58 substringToIndex:base58Length - 1]; + } + + return base58; +} + ++ (NSData *)decodeBase58:(NSString *)base58 +{ + NSMutableData *data; + + NSData *base58Data = [base58 dataUsingEncoding:NSUTF8StringEncoding]; + + // Get the required buffer size + // We need to pass a non null buffer, so allocate one using the base64 string length + // The decoded buffer can only be smaller + size_t dataLength = base58.length; + data = [NSMutableData dataWithLength:dataLength]; + b58tobin(data.mutableBytes, &dataLength, base58Data.bytes, base58Data.length); + + // Decode with the actual result size + data = [NSMutableData dataWithLength:dataLength]; + BOOL result = b58tobin(data.mutableBytes, &dataLength, base58Data.bytes, base58Data.length); + if (!result) + { + data = nil; + } + + return data; +} + @end diff --git a/Podfile b/Podfile index d2c0e7de5a..5fda15054a 100644 --- a/Podfile +++ b/Podfile @@ -11,7 +11,7 @@ pod 'OLMKit', '~> 3.0.0', :inhibit_warnings => true #pod 'OLMKit', :path => '../olm/OLMKit.podspec' pod 'Realm', '~> 3.11.1' -pod 'CBBase58', '~> 0.9.1' +pod 'libbase58', '~> 0.1.4' end diff --git a/SwiftMatrixSDK.podspec b/SwiftMatrixSDK.podspec index 6b4635c5c1..adff26e1cb 100644 --- a/SwiftMatrixSDK.podspec +++ b/SwiftMatrixSDK.podspec @@ -31,6 +31,6 @@ Pod::Spec.new do |s| # Requirements for e2e encryption s.dependency 'OLMKit', '~> 3.0.0' s.dependency 'Realm', '~> 3.11.1' - s.dependency 'CBBase58', '~> 0.9.1' + s.dependency 'libbase58', '~> 0.1.4' end From 0e6594897352e47200989a83b0db09155c82e2bf Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 7 Nov 2018 10:33:48 +0100 Subject: [PATCH 37/58] Keys backup: testRESTDeleteBackupKeys appreciates the server fix --- MatrixSDKTests/MXCryptoBackupTests.m | 1 - 1 file changed, 1 deletion(-) diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 93870c6b45..bacdfebfef 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -217,7 +217,6 @@ - (void)testRESTDeleteBackupKeys [aliceRestClient deleteKeyFromBackup:roomId session:sessionId version:version success:^{ // - Get the backup back - // TODO: The test currently fails because of https://github.com/matrix-org/synapse/issues/4056 [aliceRestClient keysBackup:version success:^(MXKeysBackupData *keysBackupData) { // -> Check it is now empty From 8fb66e6f962fb37e23ee916754ef62a7b3d8e56e Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 8 Nov 2018 17:03:18 +0100 Subject: [PATCH 38/58] Keys backup: Add [MXKeyBackup isKeyBackupTrusted:] --- MatrixSDK.xcodeproj/project.pbxproj | 8 ++ .../KeyBackup/Data/MXKeyBackupVersionTrust.h | 62 ++++++++++++++ .../KeyBackup/Data/MXKeyBackupVersionTrust.m | 36 ++++++++ .../KeyBackup/Data/MXMegolmBackupAuthData.h | 8 +- .../KeyBackup/Data/MXMegolmBackupAuthData.m | 7 ++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 9 ++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 82 ++++++++++++++++++- MatrixSDKTests/MXCryptoBackupTests.m | 40 ++++++++- 8 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.h create mode 100644 MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index a9e8693896..fbd7744291 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -134,6 +134,8 @@ 3283F7781EAF30F700C1688C /* MXBugReportRestClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 3283F7761EAF30F700C1688C /* MXBugReportRestClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3283F7791EAF30F700C1688C /* MXBugReportRestClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 3283F7771EAF30F700C1688C /* MXBugReportRestClient.m */; }; 3284A5A01DB7C00600A09972 /* MXCryptoStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3284A59D1DB7C00600A09972 /* MXCryptoStore.h */; }; + 328BCB3321947BE200A976D3 /* MXKeyBackupVersionTrust.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BCB3121947BE200A976D3 /* MXKeyBackupVersionTrust.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 328BCB3421947BE200A976D3 /* MXKeyBackupVersionTrust.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BCB3221947BE200A976D3 /* MXKeyBackupVersionTrust.m */; }; 328DDEC11A07E57E008C7DC8 /* MXJSONModelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */; }; 3291D4D41A68FFEB00C3BA41 /* MXFileRoomStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3291D4D21A68FFEB00C3BA41 /* MXFileRoomStore.h */; }; 3291D4D51A68FFEB00C3BA41 /* MXFileRoomStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3291D4D31A68FFEB00C3BA41 /* MXFileRoomStore.m */; }; @@ -455,6 +457,8 @@ 3283F7761EAF30F700C1688C /* MXBugReportRestClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXBugReportRestClient.h; sourceTree = ""; }; 3283F7771EAF30F700C1688C /* MXBugReportRestClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXBugReportRestClient.m; sourceTree = ""; }; 3284A59D1DB7C00600A09972 /* MXCryptoStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCryptoStore.h; sourceTree = ""; }; + 328BCB3121947BE200A976D3 /* MXKeyBackupVersionTrust.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKeyBackupVersionTrust.h; sourceTree = ""; }; + 328BCB3221947BE200A976D3 /* MXKeyBackupVersionTrust.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKeyBackupVersionTrust.m; sourceTree = ""; }; 328DDEC01A07E57E008C7DC8 /* MXJSONModelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXJSONModelTests.m; sourceTree = ""; }; 3291D4D21A68FFEB00C3BA41 /* MXFileRoomStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXFileRoomStore.h; sourceTree = ""; }; 3291D4D31A68FFEB00C3BA41 /* MXFileRoomStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXFileRoomStore.m; sourceTree = ""; }; @@ -1020,6 +1024,8 @@ 32BBAE662178E99100D85F46 /* MXKeyBackupData.m */, 32BBAE672178E99100D85F46 /* MXKeyBackupVersion.h */, 32BBAE692178E99100D85F46 /* MXKeyBackupVersion.m */, + 328BCB3121947BE200A976D3 /* MXKeyBackupVersionTrust.h */, + 328BCB3221947BE200A976D3 /* MXKeyBackupVersionTrust.m */, 320A883E217F4E3E002EA952 /* MXMegolmBackupAuthData.h */, 320A883F217F4E3F002EA952 /* MXMegolmBackupAuthData.m */, 320A883A217F4E35002EA952 /* MXMegolmBackupCreationInfo.h */, @@ -1405,6 +1411,7 @@ F082946D1DB66C3D00CEAB63 /* MXInvite3PID.h in Headers */, 3233606F1A403A0D0071A488 /* MXFileStore.h in Headers */, 32A1513E1DAF768D00400192 /* MXOlmInboundGroupSession.h in Headers */, + 328BCB3321947BE200A976D3 /* MXKeyBackupVersionTrust.h in Headers */, 32FA10C11FA1C9EE00E54233 /* MXOutgoingRoomKeyRequestManager.h in Headers */, 3293C700214BBA4F009B3DDB /* MXPeekingRoomSummary.h in Headers */, 326056851C76FDF2009D44AD /* MXEventTimeline.h in Headers */, @@ -1609,6 +1616,7 @@ 3265CB391A14C43E00E24B2F /* MXRoomState.m in Sources */, 3281E8B819E42DFE00976E1A /* MXJSONModel.m in Sources */, 32A1514B1DAF7C0C00400192 /* MXUsersDevicesMap.m in Sources */, + 328BCB3421947BE200A976D3 /* MXKeyBackupVersionTrust.m in Sources */, C6B40F521F2A35BE00C42C72 /* MXRoomState.swift in Sources */, 32F945F51FAB83D900622468 /* MXIncomingRoomKeyRequestCancellation.m in Sources */, 32D776821A27877300FC4AA2 /* MXMemoryRoomStore.m in Sources */, diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.h b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.h new file mode 100644 index 0000000000..bb0fb5e4cb --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.h @@ -0,0 +1,62 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MXDeviceInfo.h" + +NS_ASSUME_NONNULL_BEGIN + +@class MXKeyBackupVersionTrustSignature; + + +/** + Data model for response to [MXKeyBackup isKeyBackupTrusted:]. + */ +@interface MXKeyBackupVersionTrust : NSObject + +/** + Flag to indicate if the backup is trusted. + YES if there is a signature that is valid & from a trusted device. + */ +@property (nonatomic) BOOL usable; + +/** + Signatures found in the backup version. + */ +@property (nonatomic) NSArray *signatures; + +@end + + +/** + A signature in a the `MXKeyBackupVersionTrust` object. + */ +@interface MXKeyBackupVersionTrustSignature : NSObject + +/** + The device that signed the backup version. + */ +@property (nonatomic) MXDeviceInfo *device; + +/** + Flag to indicate the signature from this device is valid. + */ +@property (nonatomic) BOOL valid; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.m b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.m new file mode 100644 index 0000000000..44981fe2d8 --- /dev/null +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersionTrust.m @@ -0,0 +1,36 @@ +/* + Copyright 2018 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MXKeyBackupVersionTrust.h" + +@implementation MXKeyBackupVersionTrust + +- (instancetype)init +{ + self = [super init]; + if (self) + { + _usable = NO; + _signatures = [NSArray new]; + } + return self; +} + +@end + +@implementation MXKeyBackupVersionTrustSignature + +@end diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.h b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.h index be55704bfc..8c706a72d9 100644 --- a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.h +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.h @@ -33,7 +33,13 @@ NS_ASSUME_NONNULL_BEGIN /** Signatures of the public key. */ -@property (nonatomic) NSDictionary *signatures; +@property (nonatomic) NSDictionary *signatures; + +/** + Same as the parent [MXJSONModel JSONDictionary] but return only + data that must be signed. + */ +@property (nonatomic, readonly) NSDictionary *signalableJSONDictionary; @end diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m index c6cbca00de..33fcb21ba2 100644 --- a/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXMegolmBackupAuthData.m @@ -46,4 +46,11 @@ - (NSDictionary *)JSONDictionary return JSONDictionary; } +- (NSDictionary *)signalableJSONDictionary +{ + return @{ + @"public_key": _publicKey + }; +} + @end diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index cfd6b85c98..a4d7b2faa6 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -18,6 +18,7 @@ #import "MXRestClient.h" #import "MXMegolmBackupCreationInfo.h" +#import "MXKeyBackupVersionTrust.h" @class MXSession; @class OLMPkEncryption; @@ -73,6 +74,14 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; - (MXHTTPOperation*)version:(void (^)(MXKeyBackupVersion *keyBackupVersion))success failure:(void (^)(NSError *error))failure; +/** + Check trust on a key backup version. + + @param keyBackupVersion the backup version to check. + @param onComplete block called when the operations completes. + */ +- (void)isKeyBackupTrusted:(MXKeyBackupVersion*)keyBackupVersion onComplete:(void (^)(MXKeyBackupVersionTrust *keyBackupVersionTrust))onComplete; + /** Set up the data required to create a new backup version. diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 4baf9f2729..0ed61badee 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -243,6 +243,86 @@ - (MXHTTPOperation *)version:(void (^)(MXKeyBackupVersion * _Nonnull))success fa return [mxSession.matrixRestClient keyBackupVersion:success failure:failure]; } +- (void)isKeyBackupTrusted:(MXKeyBackupVersion *)keyBackupVersion onComplete:(void (^)(MXKeyBackupVersionTrust * _Nonnull))onComplete +{ + MXWeakify(self); + dispatch_async(mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + NSString *myUserId = self->mxSession.myUser.userId; + + MXKeyBackupVersionTrust *keyBackupVersionTrust = [MXKeyBackupVersionTrust new]; + + MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData modelFromJSON:keyBackupVersion.authData]; + if (!keyBackupVersion.algorithm || !authData + || !authData.publicKey || !authData.signatures) + { + NSLog(@"[MXKeyBackup] isKeyBackupTrusted: Key backup is absent or missing required data"); + dispatch_async(dispatch_get_main_queue(), ^{ + onComplete(keyBackupVersionTrust); + }); + return; + } + + NSDictionary *mySigs = authData.signatures[myUserId]; + if (mySigs.count == 0) + { + NSLog(@"[MXKeyBackup] isKeyBackupTrusted: Ignoring key backup because it lacks any signatures from this user"); + dispatch_async(dispatch_get_main_queue(), ^{ + onComplete(keyBackupVersionTrust); + }); + return; + } + + NSMutableArray *signatures = [NSMutableArray array]; + for (NSString *keyId in mySigs) + { + // XXX: is this how we're supposed to get the device id? + NSString *deviceId; + NSArray *components = [keyId componentsSeparatedByString:@":"]; + if (components.count == 2) + { + deviceId = components[1]; + } + + MXDeviceInfo *device; + if (deviceId) + { + device = [self->mxSession.crypto.deviceList storedDevice:myUserId deviceId:deviceId]; + } + if (!device) + { + NSLog(@"[MXKeyBackup] isKeyBackupTrusted: Ignoring signature from unknown key %@", deviceId); + continue; + } + + NSError *error; + BOOL valid = [self->mxSession.crypto.olmDevice verifySignature:device.fingerprint JSON:authData.signalableJSONDictionary signature:mySigs[keyId] error:&error]; + + if (!valid) + { + NSLog(@"[MXKeyBackup] isKeyBackupTrusted: Bad signature from device %@: %@", device.deviceId, error); + } + else if (device.verified) + { + keyBackupVersionTrust.usable = YES; + } + + MXKeyBackupVersionTrustSignature *signature = [MXKeyBackupVersionTrustSignature new]; + signature.device = device; + signature.valid = valid; + + [signatures addObject:signature]; + } + + keyBackupVersionTrust.signatures = signatures; + + dispatch_async(dispatch_get_main_queue(), ^{ + onComplete(keyBackupVersionTrust); + }); + }); +} + - (void)prepareKeyBackupVersion:(void (^)(MXMegolmBackupCreationInfo *keyBackupCreationInfo))success failure:(nullable void (^)(NSError *error))failure; { @@ -265,7 +345,7 @@ - (void)prepareKeyBackupVersion:(void (^)(MXMegolmBackupCreationInfo *keyBackupC } return; } - authData.signatures = [self->mxSession.crypto signObject:authData.JSONDictionary]; + authData.signatures = [self->mxSession.crypto signObject:authData.signalableJSONDictionary]; MXMegolmBackupCreationInfo *keyBackupCreationInfo = [MXMegolmBackupCreationInfo new]; keyBackupCreationInfo.algorithm = kMXCryptoMegolmBackupAlgorithm; diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index bacdfebfef..6e5dbb6e7b 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -393,7 +393,7 @@ - (void)testCreateKeyBackupVersion - Check that `[MXKeyBackup createKeyBackupVersion` launches the backup - Check the backup completes */ -- (void)testBackupCreateKeyBackupVersion +- (void)testBackupAfterCreateKeyBackupVersion { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { @@ -429,6 +429,44 @@ - (void)testBackupCreateKeyBackupVersion }]; } +/** + - Create a backup version + - Check the returned MXKeyBackupVersion is trusted + */ +- (void)testIsKeyBackupTrusted +{ + // - Create a backup version + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + + // - Check the returned MXKeyBackupVersion is trusted + [aliceSession.crypto.backup isKeyBackupTrusted:keyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull keyBackupVersionTrust) { + + XCTAssertNotNil(keyBackupVersionTrust); + XCTAssertTrue(keyBackupVersionTrust.usable); + + XCTAssertEqual(keyBackupVersionTrust.signatures.count, 1); + + MXKeyBackupVersionTrustSignature *signature = keyBackupVersionTrust.signatures.firstObject; + XCTAssertTrue(signature.valid); + XCTAssertEqualObjects(signature.device.deviceId, aliceSession.matrixRestClient.credentials.deviceId); + + [expectation fulfill]; + }]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + /** Check that `[MXKeyBackup backupAllGroupSessions]` returns valid data */ From 331eac196800ab38ac25bae861466a6153772378 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 9 Nov 2018 11:19:35 +0100 Subject: [PATCH 39/58] Keys backup: Add [MXKeyBackup checkAndStartKeyBackup:] to start backup --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 40 +++++++++- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 73 ++++++++++++++++++- .../Crypto/KeyBackup/MXKeyBackup_Private.h | 8 ++ MatrixSDK/Crypto/MXCrypto.m | 2 + MatrixSDKTests/MXCryptoBackupTests.m | 68 +++++++++++++++-- 5 files changed, 177 insertions(+), 14 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index a4d7b2faa6..1c66a19f9d 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -28,14 +28,49 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Constants definitions /** - E2e keys backup states. + * E2e keys backup states. + * + * | + * V deleteKeyBackupVersion (on current backup) + * +----------------------> UNKNOWN <------------- + * | | + * | | checkAndStartKeyBackup (at startup or on new verified device) + * | V + * | CHECKING BACKUP + * | | + * | Network error | + * +<---------------------------+---------> DISABLED <----------------------+ + * | | | | + * | | | createKeyBackupVersion | + * | | V | + * | | ENABLING | + * | | | | + * | V | error | + * | +---> READY <--------+----------------------------+ + * | | | + * | | | on new key + * | | V + * | | WILL BACK UP + * | | | + * | | V + * | | BACKING UP + * | Error | | + * +<-----------------+---------+ + * */ typedef enum : NSUInteger { // Backup is not enabled - MXKeyBackupStateDisabled = 0, + MXKeyBackupStateUnknown = 0, + + // Backup is not enabled + MXKeyBackupStateCheckingBackUpOnHomeserver, + + // Backup from this device is not enabled + MXKeyBackupStateDisabled, // Backup is being enabled + // The backup version is being created on the homeserver MXKeyBackupStateEnabling, // Backup is enabled and ready to send backup to the homeserver @@ -70,7 +105,6 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; @return a MXHTTPOperation instance. */ -// TODO: hide it? - (MXHTTPOperation*)version:(void (^)(MXKeyBackupVersion *keyBackupVersion))success failure:(void (^)(NSError *error))failure; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 0ed61badee..ec1c35b5cb 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -69,12 +69,71 @@ - (instancetype)initWithMatrixSession:(MXSession *)matrixSession { self = [self init]; { - _state = MXKeyBackupStateDisabled; + _state = MXKeyBackupStateUnknown; mxSession = matrixSession; } return self; } +- (void)checkAndStartKeyBackup +{ + if (_state != MXKeyBackupStateUnknown) + { + return; + } + + self.state = MXKeyBackupStateCheckingBackUpOnHomeserver; + + MXWeakify(self); + [self version:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + MXStrongifyAndReturnIfNil(self); + + MXWeakify(self); + [self isKeyBackupTrusted:keyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull trustInfo) { + MXStrongifyAndReturnIfNil(self); + + self.state = MXKeyBackupStateDisabled; + + if (trustInfo.usable) + { + NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Found usable key backup. version: %@", keyBackupVersion.version); + if (!self.keyBackupVersion) + { + NSLog(@"[MXKeyBackup] -> enabling key backups"); + [self enableKeyBackup:keyBackupVersion]; + } + else if ([self.keyBackupVersion.version isEqualToString:self.keyBackupVersion.version]) + { + NSLog(@"[MXKeyBackup] -> same backup version(%@). Keep usint it", self.keyBackupVersion.version); + } + else + { + NSLog(@"[MXKeyBackup] -> disable the current version(%@) and enabling the new one", self.keyBackupVersion.version); + [self disableKeyBackup]; + [self enableKeyBackup:keyBackupVersion]; + } + } + else + { + NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: No usable key backup. version: %@", keyBackupVersion.version); + if (!self.keyBackupVersion) + { + NSLog(@"[MXKeyBackup] -> not enabling key backup"); + } + else + { + NSLog(@"[MXKeyBackup] -> disabling key backup"); + [self disableKeyBackup]; + } + } + }]; + + } failure:^(NSError * _Nonnull error) { + NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Failed to get current version: %@", error); + self.state = MXKeyBackupStateUnknown; + }]; +} + - (NSError*)enableKeyBackup:(MXKeyBackupVersion*)version { MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData modelFromJSON:version.authData]; @@ -131,6 +190,13 @@ - (void)maybeSendKeyBackup else { NSLog(@"[MXKeyBackup] maybeSendKeyBackup: Skip it because state: %@", @(_state)); + + if (self.state == MXKeyBackupStateUnknown) + { + // If not already done, check for a valid backup version on the homeserver. + // If one, maybeSendKeyBackup will be called again. + [self checkAndStartKeyBackup]; + } } } @@ -147,7 +213,7 @@ - (void)sendKeyBackup return; } - if (_state == MXKeyBackupStateBackingUp || _state == MXKeyBackupStateDisabled) + if (_state == MXKeyBackupStateBackingUp || !self.enabled) { // Do nothing if we are already backing up or if the backup has been disabled return; @@ -427,6 +493,7 @@ - (MXHTTPOperation*)deleteKeyBackupVersion:(NSString*)version if ([self.keyBackupVersion.version isEqualToString:version]) { [self disableKeyBackup]; + self.state = MXKeyBackupStateUnknown; } MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient deleteKeysFromBackup:version success:^{ @@ -659,7 +726,7 @@ - (MXHTTPOperation*)restoreKeyBackup:(NSString*)version - (BOOL)enabled { - return _state != MXKeyBackupStateDisabled; + return _state >= MXKeyBackupStateReadyToBackUp; } diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h index 8a82c54e82..4794a7e7b2 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h @@ -28,6 +28,14 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithMatrixSession:(MXSession*)mxSession; +/** + Check the server for an active key backup. + + If one is present and has a valid signature from one of the user's verified + devices, start backing up to it. + */ +- (void)checkAndStartKeyBackup; + /** Enable backing up of keys. diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index cdee6a6b49..5c844d643c 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -246,6 +246,8 @@ - (void)start:(void (^)(void))success [self->outgoingRoomKeyRequestManager start]; + [self->_backup checkAndStartKeyBackup]; + dispatch_async(dispatch_get_main_queue(), ^{ self->startOperation = nil; success(); diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 6e5dbb6e7b..e56439497b 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -406,11 +406,15 @@ - (void)testBackupAfterCreateKeyBackupVersion NSUInteger keys = [aliceSession.crypto.store inboundGroupSessionsCount:NO]; - [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:aliceSession.crypto.backup queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + __block id observer; + observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:aliceSession.crypto.backup queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { // Check the backup completes - if (aliceSession.crypto.backup.state == MXKeyBackupStateReadyToBackUp) + if (observer && aliceSession.crypto.backup.state == MXKeyBackupStateReadyToBackUp) { + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + observer = nil; + NSUInteger backedUpkeys = [aliceSession.crypto.store inboundGroupSessionsCount:YES]; XCTAssertEqual(backedUpkeys, keys, @"All keys must have been marked as backed up"); @@ -511,7 +515,7 @@ - (void)testBackupAllGroupSessions } /** - Check that encryption and decryption of megolm keys + Check encryption and decryption of megolm keys in the backup. - Pick a megolm key - Check [MXKeyBackup encryptGroupSession] returns stg - Check [MXKeyBackup pkDecryptionFromRecoveryKey] is able to create a OLMPkDecryption @@ -554,7 +558,6 @@ - (void)testEncryptAndDecryptKeyBackupData XCTFail(@"The request should not fail - NSError: %@", error); [expectation fulfill]; }]; - }]; } @@ -598,10 +601,6 @@ - (void)testRestoreKeyBackup // - The new device must have the same count of megolm keys XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:NO], aliceKeys1.count); - // TODO: This test will pass once the backup will be started automatically when a backup version - // is detected - XCTAssertEqual([aliceSession2.crypto.store inboundGroupSessionsCount:YES], aliceKeys1.count); - // - Alice must have the same keys on both devices for (MXOlmInboundGroupSession *aliceKey1 in aliceKeys1) { @@ -632,6 +631,59 @@ - (void)testRestoreKeyBackup }]; } +/** + Check backup starts automatically if there is an existing and compatible backup + version on the homeserver. + - Create a backup version + - Restart alice session + -> The new alice session must back up to the same version + */ +- (void)testCheckAndStartKeyBackupWhenRestartingAMatrixSession +{ + // - Create a backup version + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + XCTAssertFalse(aliceSession.crypto.backup.enabled); + + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + + XCTAssertTrue(aliceSession.crypto.backup.enabled); + + // - Restart alice session + MXSession *aliceSession2 = [[MXSession alloc] initWithMatrixRestClient:aliceSession.matrixRestClient]; + [aliceSession close]; + [aliceSession2 start:nil failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + // -> The new alice session must back up to the same version + __block id observer; + observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:aliceSession2.crypto.backup queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + + if (observer && aliceSession2.crypto.backup.state == MXKeyBackupStateReadyToBackUp) + { + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + observer = nil; + + XCTAssertEqualObjects(aliceSession2.crypto.backup.keyBackupVersion.version, keyBackupVersion.version); + + [expectation fulfill]; + } + }]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop From 3e1b963617e8188420d6485f66633157f23c69ee Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 12 Nov 2018 10:36:58 +0100 Subject: [PATCH 40/58] Keys backup: Detect M_WRONG_ROOM_KEYS_VERSION and create the corresponding state MXKeyBackupStateWrongBackUpVersion --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 46 +++++----- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 24 ++++- MatrixSDK/MXError.h | 1 + MatrixSDK/MXError.m | 1 + MatrixSDKTests/MXCryptoBackupTests.m | 111 ++++++++++++++--------- 5 files changed, 117 insertions(+), 66 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index 1c66a19f9d..a87210a5b5 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -34,38 +34,42 @@ NS_ASSUME_NONNULL_BEGIN * V deleteKeyBackupVersion (on current backup) * +----------------------> UNKNOWN <------------- * | | - * | | checkAndStartKeyBackup (at startup or on new verified device) + * | | checkAndStartKeyBackup (at startup or on new verified device or a new detected backup) * | V * | CHECKING BACKUP * | | - * | Network error | - * +<---------------------------+---------> DISABLED <----------------------+ - * | | | | - * | | | createKeyBackupVersion | - * | | V | - * | | ENABLING | - * | | | | - * | V | error | - * | +---> READY <--------+----------------------------+ - * | | | - * | | | on new key - * | | V - * | | WILL BACK UP - * | | | - * | | V - * | | BACKING UP - * | Error | | - * +<-----------------+---------+ + * | Network error | + * +<----------+----------------+---------> DISABLED <----------------------+ + * | | | | | + * | | | | createKeyBackupVersion | + * | V | V | + * +<--- WRONG VERSION | ENABLING | + * ^ | | | + * | V | error | + * | READY <--------+----------------------------+ + * | | | + * | | on new key | + * | V | + * | WILL BACK UP | + * | | | + * | V | + * | BACKING UP | + * | Error | | + * +<---------------+------------->+ * */ typedef enum : NSUInteger { - // Backup is not enabled + // Need to check the current backup version on the homeserver MXKeyBackupStateUnknown = 0, - // Backup is not enabled + // Making the check request on the homeserver MXKeyBackupStateCheckingBackUpOnHomeserver, + // Backup has been stopped because a new backup version has been detected on + // the homeserver + MXKeyBackupStateWrongBackUpVersion, + // Backup from this device is not enabled MXKeyBackupStateDisabled, diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index ec1c35b5cb..e41f82126e 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -23,6 +23,7 @@ #import "MXRecoveryKey.h" #import "MXSession.h" // TODO: To remove #import "MXTools.h" +#import "MXError.h" #pragma mark - Constants definitions @@ -289,13 +290,26 @@ - (void)sendKeyBackup } failure:^(NSError *error) { MXStrongifyAndReturnIfNil(self); - if (self->backupAllGroupSessionsFailure) + NSLog(@"[MXKeyBackup] sendKeyBackup: sendKeysBackup failed. Error: %@", error); + + void (^backupAllGroupSessionsFailure)(NSError *error) = self->backupAllGroupSessionsFailure; + + MXError *mxError = [[MXError alloc] initWithNSError:error]; + if ([mxError.errcode isEqualToString:kMXErrCodeStringBackupWrongKeysVersion]) + { + [self disableKeyBackup]; + self.state = MXKeyBackupStateWrongBackUpVersion; + } + else { - self->backupAllGroupSessionsFailure(error); + // Come back to the ready state so that we will retry on the next received key + self.state = MXKeyBackupStateReadyToBackUp; } - // TODO: Manage retries - NSLog(@"[MXKeyBackup] sendKeyBackup: sendKeysBackup failed. Error: %@", error); + if (backupAllGroupSessionsFailure) + { + backupAllGroupSessionsFailure(error); + } }]; } @@ -734,6 +748,8 @@ - (BOOL)enabled - (void)setState:(MXKeyBackupState)state { + NSLog(@"[MXKeyBackup] setState: %@ -> %@", @(_state), @(state)); + _state = state; dispatch_async(dispatch_get_main_queue(), ^{ diff --git a/MatrixSDK/MXError.h b/MatrixSDK/MXError.h index 2f2fb8563d..21ef7cabe7 100644 --- a/MatrixSDK/MXError.h +++ b/MatrixSDK/MXError.h @@ -44,6 +44,7 @@ FOUNDATION_EXPORT NSString *const kMXErrCodeStringServerNotTrusted; FOUNDATION_EXPORT NSString *const kMXErrCodeStringGuestAccessForbidden; FOUNDATION_EXPORT NSString *const kMXErrCodeStringConsentNotGiven; FOUNDATION_EXPORT NSString *const kMXErrCodeStringResourceLimitExceeded; +FOUNDATION_EXPORT NSString *const kMXErrCodeStringBackupWrongKeysVersion; FOUNDATION_EXPORT NSString *const kMXErrorStringInvalidToken; diff --git a/MatrixSDK/MXError.m b/MatrixSDK/MXError.m index aaafd05f61..1aa29e65e2 100644 --- a/MatrixSDK/MXError.m +++ b/MatrixSDK/MXError.m @@ -41,6 +41,7 @@ NSString *const kMXErrCodeStringGuestAccessForbidden = @"M_GUEST_ACCESS_FORBIDDEN"; NSString *const kMXErrCodeStringConsentNotGiven = @"M_CONSENT_NOT_GIVEN"; NSString *const kMXErrCodeStringResourceLimitExceeded = @"M_RESOURCE_LIMIT_EXCEEDED"; +NSString *const kMXErrCodeStringBackupWrongKeysVersion = @"M_WRONG_ROOM_KEYS_VERSION"; NSString *const kMXErrorStringInvalidToken = @"Invalid token"; diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index e56439497b..91b844cc8d 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -60,6 +60,22 @@ - (void)tearDown [super tearDown]; } +- (MXKeyBackupVersion*)fakeKeyBackupVersion +{ + return [MXKeyBackupVersion modelFromJSON:@{ + @"algorithm": kMXCryptoMegolmBackupAlgorithm, + @"auth_data": @{ + @"public_key": @"abcdefg", + @"signatures": @{ + @"something": @{ + @"ed25519:something": @"hijklmnop" + } + } + } + }]; +} + + /** - Create a backup version on the server - Get the current version from the server @@ -70,19 +86,7 @@ - (void)testRESTCreateKeyBackupVersion [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { // - Create a backup version on the server - MXKeyBackupVersion *keyBackupVersion = - [MXKeyBackupVersion modelFromJSON:@{ - @"algorithm": kMXCryptoMegolmBackupAlgorithm, - @"auth_data": @{ - @"public_key": @"abcdefg", - @"signatures": @{ - @"something": @{ - @"ed25519:something": @"hijklmnop" - } - } - } - }]; - + MXKeyBackupVersion *keyBackupVersion = self.fakeKeyBackupVersion; [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { // - Get the current version from the server @@ -118,20 +122,7 @@ - (void)testRESTBackupKeys [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { // - Create a backup version on the server - MXKeyBackupVersion *keyBackupVersion = - [MXKeyBackupVersion modelFromJSON:@{ - @"algorithm": kMXCryptoMegolmBackupAlgorithm, - @"auth_data": @{ - @"public_key": @"abcdefg", - @"signatures": @{ - @"something": @{ - @"ed25519:something": @"hijklmnop" - } - } - } - }]; - - [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + [aliceRestClient createKeyBackupVersion:self.fakeKeyBackupVersion success:^(NSString *version) { //- Make a backup MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; @@ -184,20 +175,7 @@ - (void)testRESTDeleteBackupKeys [matrixSDKTestsData doMXRestClientTestWithAlice:self readyToTest:^(MXRestClient *aliceRestClient, XCTestExpectation *expectation) { // - Create a backup version on the server - MXKeyBackupVersion *keyBackupVersion = - [MXKeyBackupVersion modelFromJSON:@{ - @"algorithm": kMXCryptoMegolmBackupAlgorithm, - @"auth_data": @{ - @"public_key": @"abcdefg", - @"signatures": @{ - @"something": @{ - @"ed25519:something": @"hijklmnop" - } - } - } - }]; - - [aliceRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + [aliceRestClient createKeyBackupVersion:self.fakeKeyBackupVersion success:^(NSString *version) { //- Make a backup MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; @@ -684,6 +662,57 @@ - (void)testCheckAndStartKeyBackupWhenRestartingAMatrixSession }]; } +/** + Check backup starts automatically if there is an existing and compatible backup + version on the homeserver. + - Make alice back up her keys to her homeserver + - Create a new backup with fake data on the homeserver + - Make alice back up all her keys again + -> That must fail and her backup state must be disabled + */ +- (void)testBackupWhenAnotherBackupWasCreated +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + // - Make alice back up her keys to her homeserver + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + + XCTAssertTrue(aliceSession.crypto.backup.enabled); + + // - Create a new backup with fake data on the homeserver + [aliceSession.matrixRestClient createKeyBackupVersion:self.fakeKeyBackupVersion success:^(NSString *version) { + + // - Make alice back up all her keys again + [aliceSession.crypto.backup backupAllGroupSessions:^{ + + XCTFail(@"The backup must fail"); + [expectation fulfill]; + + } progress:nil failure:^(NSError * _Nonnull error) { + + // -> That must fail and her backup state must be disabled + XCTAssertEqual(aliceSession.crypto.backup.state, MXKeyBackupStateWrongBackUpVersion); + XCTAssertFalse(aliceSession.crypto.backup.enabled); + + [expectation fulfill]; + }]; + + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop From 481623f0d6eded0642b80bcf2d45fddc95a95502 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 12 Nov 2018 18:12:48 +0100 Subject: [PATCH 41/58] MXCryptoStore: add deviceWithIdentityKey method --- MatrixSDK/Crypto/Data/MXDeviceList.h | 3 +- MatrixSDK/Crypto/Data/MXDeviceList.m | 20 ++--------- MatrixSDK/Crypto/Data/Store/MXCryptoStore.h | 16 ++++++--- .../MXRealmCryptoStore/MXRealmCryptoStore.m | 36 +++++++++++++++++-- MatrixSDK/Crypto/MXCrypto.m | 4 +-- 5 files changed, 51 insertions(+), 28 deletions(-) diff --git a/MatrixSDK/Crypto/Data/MXDeviceList.h b/MatrixSDK/Crypto/Data/MXDeviceList.h index b364e5c802..d733dcb755 100644 --- a/MatrixSDK/Crypto/Data/MXDeviceList.h +++ b/MatrixSDK/Crypto/Data/MXDeviceList.h @@ -124,12 +124,11 @@ typedef enum : NSUInteger /** Find a device by curve25519 identity key - @param userId the owner of the device. @param algorithm the encryption algorithm. @param senderKey the curve25519 key to match. @return the device info. */ -- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)senderKey forUser:(NSString*)userId andAlgorithm:(NSString*)algorithm; +- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)senderKey andAlgorithm:(NSString*)algorithm; /** Flag the given user for device-list tracking, if they are not already. diff --git a/MatrixSDK/Crypto/Data/MXDeviceList.m b/MatrixSDK/Crypto/Data/MXDeviceList.m index 1b84ca352f..f5be8af436 100644 --- a/MatrixSDK/Crypto/Data/MXDeviceList.m +++ b/MatrixSDK/Crypto/Data/MXDeviceList.m @@ -226,7 +226,7 @@ - (MXDeviceInfo*)storedDevice:(NSString*)userId deviceId:(NSString*)deviceId return [crypto.store devicesForUser:userId][deviceId]; } -- (MXDeviceInfo *)deviceWithIdentityKey:(NSString *)senderKey forUser:(NSString *)userId andAlgorithm:(NSString *)algorithm +- (MXDeviceInfo *)deviceWithIdentityKey:(NSString *)senderKey andAlgorithm:(NSString *)algorithm { if (![algorithm isEqualToString:kMXCryptoOlmAlgorithm] && ![algorithm isEqualToString:kMXCryptoMegolmAlgorithm]) @@ -235,23 +235,7 @@ - (MXDeviceInfo *)deviceWithIdentityKey:(NSString *)senderKey forUser:(NSString return nil; } - for (MXDeviceInfo *device in [self storedDevicesForUser:userId]) - { - for (NSString *keyId in device.keys) - { - if ([keyId hasPrefix:@"curve25519:"]) - { - NSString *deviceKey = device.keys[keyId]; - if ([senderKey isEqualToString:deviceKey]) - { - return device; - } - } - } - } - - // Doesn't match a known device - return nil; + return [crypto.store deviceWithIdentityKey:senderKey]; } - (void)startTrackingDeviceList:(NSString*)userId diff --git a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h index a508733b14..a8d4384588 100644 --- a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h +++ b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h @@ -115,7 +115,7 @@ /** Store a device for a user. - @param userId The user's id. + @param userId the user's id. @param device the device to store. */ - (void)storeDeviceForUser:(NSString*)userId device:(MXDeviceInfo*)device; @@ -123,12 +123,20 @@ /** Retrieve a device for a user. - @param deviceId The device id. - @param userId The user's id. - @return A map from device id to 'MXDevice' object for the device. + @param deviceId the device id. + @param userId the user's id. + @return The device. */ - (MXDeviceInfo*)deviceWithDeviceId:(NSString*)deviceId forUser:(NSString*)userId; +/** + Retrieve a device by its identity key. + + @param identityKey the device identity key (`MXDeviceInfo.identityKey`)/ + @return The device. + */ +- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)identityKey; + /** Store the known devices for a user. diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index 1471f8b555..a295d29090 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -23,7 +23,7 @@ #import "MXSession.h" #import "MXTools.h" -NSUInteger const kMXRealmCryptoStoreVersion = 7; +NSUInteger const kMXRealmCryptoStoreVersion = 8; static NSString *const kMXRealmCryptoStoreFolder = @"MXRealmCryptoStore"; @@ -33,6 +33,7 @@ @interface MXRealmDeviceInfo : RLMObject @property NSData *deviceInfoData; @property (nonatomic) NSString *deviceId; +@property (nonatomic) NSString *identityKey; @end @implementation MXRealmDeviceInfo @@ -413,6 +414,7 @@ - (void)storeDeviceForUser:(NSString*)userID device:(MXDeviceInfo*)device @"deviceId": device.deviceId, @"deviceInfoData": [NSKeyedArchiver archivedDataWithRootObject:device] }]; + realmDevice.identityKey = device.identityKey; [realmUser.devices addObject:realmDevice]; } else @@ -438,6 +440,17 @@ - (MXDeviceInfo*)deviceWithDeviceId:(NSString*)deviceId forUser:(NSString*)userI return nil; } +- (MXDeviceInfo*)deviceWithIdentityKey:(NSString*)identityKey +{ + MXRealmDeviceInfo *realmDevice = [MXRealmDeviceInfo objectsInRealm:self.realm where:@"identityKey = %@", identityKey].firstObject; + if (realmDevice) + { + return [NSKeyedUnarchiver unarchiveObjectWithData:realmDevice.deviceInfoData]; + } + + return nil; +} + - (void)storeDevicesForUser:(NSString*)userID devices:(NSDictionary*)devices { NSDate *startDate = [NSDate date]; @@ -467,6 +480,7 @@ - (void)storeDevicesForUser:(NSString*)userID devices:(NSDictionary #7"); - // We need to update the db because a sessionId property has been added MXRealmOlmSession + // We need to update the db because a sessionId property has been added to MXRealmOlmSession // to ensure uniqueness NSLog(@" Add sessionIdSenderKer, a combined primary key, to all MXRealmOlmInboundGroupSession objects"); [migration enumerateObjects:MXRealmOlmInboundGroupSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { @@ -1142,6 +1156,24 @@ + (RLMRealm*)realmForUser:(NSString*)userId NSLog(@"[MXRealmCryptoStore] Migration from schema #6 -> #7 completed"); } + case 7: + { + NSLog(@"[MXRealmCryptoStore] Migration from schema #7 -> #8"); + + // We need to update the db because a identityKey property has been added to MXRealmDeviceInfo + NSLog(@" Add identityKey to all MXRealmOlmInboundGroupSession objects"); + [migration enumerateObjects:MXRealmDeviceInfo.className block:^(RLMObject *oldObject, RLMObject *newObject) { + + MXDeviceInfo *device = [NSKeyedUnarchiver unarchiveObjectWithData:oldObject[@"deviceInfoData"]]; + NSString *identityKey = device.identityKey; + if (identityKey) + { + newObject[@"identityKey"] = identityKey; + } + }]; + + NSLog(@"[MXRealmCryptoStore] Migration from schema #7 -> #8 completed"); + } } } }; diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index 5c844d643c..139940b36d 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -715,7 +715,7 @@ - (MXDeviceInfo *)eventDeviceInfo:(MXEvent *)event MXStrongifyAndReturnIfNil(self); NSString *algorithm = event.wireContent[@"algorithm"]; - device = [self.deviceList deviceWithIdentityKey:event.senderKey forUser:event.sender andAlgorithm:algorithm]; + device = [self.deviceList deviceWithIdentityKey:event.senderKey andAlgorithm:algorithm]; }); } @@ -1417,7 +1417,7 @@ - (MXDeviceInfo *)eventSenderDeviceOfEvent:(MXEvent *)event // senderKey is the Curve25519 identity key of the device which the event // was sent from. In the case of Megolm, it's actually the Curve25519 // identity key of the device which set up the Megolm session. - MXDeviceInfo *device = [_deviceList deviceWithIdentityKey:senderKey forUser:event.sender andAlgorithm:algorithm]; + MXDeviceInfo *device = [_deviceList deviceWithIdentityKey:senderKey andAlgorithm:algorithm]; if (!device) { // we haven't downloaded the details of this device yet. From 4214f6189af30c916e16e3330e1df702900af2b8 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 12 Nov 2018 18:13:35 +0100 Subject: [PATCH 42/58] Keys backup: Code cleaning --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index e41f82126e..6ddf49d950 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -21,7 +21,7 @@ #import #import "MXRecoveryKey.h" -#import "MXSession.h" // TODO: To remove +#import "MXSession.h" #import "MXTools.h" #import "MXError.h" @@ -50,14 +50,6 @@ @interface MXKeyBackup () // Failure block when backupAllGroupSessions is progressing void (^backupAllGroupSessionsFailure)(NSError *error); - - // track whether this device's megolm keys are being backed up incrementally - // to the server or not. - // XXX: this should probably have a single source of truth from OlmAccount -// + this.backupInfo = null; // The info dict from /room_keys/version -// + this.backupKey = null; // The encryption key object -// this._checkedForBackup = false; // Have we checked the server for a backup we can use? -// X this._sendingBackups = false; // Are we currently sending backups? } @end @@ -599,7 +591,10 @@ - (void)backupAllGroupSessions:(nullable void (^)(void))success self->backupAllGroupSessionsFailure = ^(NSError *error) { MXStrongifyAndReturnIfNil(self); - failure(error); + dispatch_async(dispatch_get_main_queue(), ^{ + failure(error); + }); + [self resetBackupAllGroupSessionsObjects]; }; } @@ -825,8 +820,7 @@ - (OLMPkDecryption*)pkDecryptionFromRecoveryKey:(NSString*)recoveryKey error:(NS - (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withPkEncryption:(OLMPkEncryption*)encryption { // Gather information for each key - // TODO: userId? - MXDeviceInfo *device = [mxSession.crypto.deviceList deviceWithIdentityKey:session.senderKey forUser:nil andAlgorithm:kMXCryptoMegolmAlgorithm]; + MXDeviceInfo *device = [mxSession.crypto.deviceList deviceWithIdentityKey:session.senderKey andAlgorithm:kMXCryptoMegolmAlgorithm]; // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format From c3e871b19df5bb5d77f6bd63a2fdb2e23a831341 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 10:48:23 +0100 Subject: [PATCH 43/58] Keys backup: Store backup version so that at startup we can reset backup markers if the backup version has changed --- MatrixSDK/Crypto/Data/Store/MXCryptoStore.h | 7 +++++++ .../MXRealmCryptoStore/MXRealmCryptoStore.m | 20 +++++++++++++++++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 10 ++++++++++ 3 files changed, 37 insertions(+) diff --git a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h index a8d4384588..387214bec1 100644 --- a/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h +++ b/MatrixSDK/Crypto/Data/Store/MXCryptoStore.h @@ -224,6 +224,13 @@ #pragma mark - Key backup + +/** + The backup version currently used. + Nil means no backup. + */ +@property (nonatomic) NSString *backupVersion; + /** Mark all inbound group sessions as not backed up. */ diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index a295d29090..339162ec59 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -151,6 +151,12 @@ @interface MXRealmOlmAccount : RLMObject Settings for blacklisting unverified devices. */ @property (nonatomic) BOOL globalBlacklistUnverifiedDevices; + +/** + The backup version currently used. + */ +@property (nonatomic) NSString *backupVersion; + @end @implementation MXRealmOlmAccount @@ -736,6 +742,20 @@ - (void)removeInboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSStr #pragma mark - Key backup +- (void)setBackupVersion:(NSString *)backupVersion +{ + MXRealmOlmAccount *account = self.accountInCurrentThread; + [account.realm transactionWithBlock:^{ + account.backupVersion = backupVersion; + }]; +} + +- (NSString *)backupVersion +{ + MXRealmOlmAccount *account = self.accountInCurrentThread; + return account.backupVersion; +} + - (void)resetBackupMarkers { RLMRealm *realm = self.realm; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 6ddf49d950..27bbc4d834 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -92,6 +92,14 @@ - (void)checkAndStartKeyBackup NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Found usable key backup. version: %@", keyBackupVersion.version); if (!self.keyBackupVersion) { + // Check the version we used at the previous app run + NSString *versionInStore = self->mxSession.crypto.store.backupVersion; + if (versionInStore && ![versionInStore isEqualToString:keyBackupVersion.version]) + { + NSLog(@"[MXKeyBackup] -> clean the previously used version(%@)", versionInStore); + [self disableKeyBackup]; + } + NSLog(@"[MXKeyBackup] -> enabling key backups"); [self enableKeyBackup:keyBackupVersion]; } @@ -133,6 +141,7 @@ - (NSError*)enableKeyBackup:(MXKeyBackupVersion*)version if (authData) { _keyBackupVersion = version; + self->mxSession.crypto.store.backupVersion = version.version; _backupKey = [OLMPkEncryption new]; [_backupKey setRecipientKey:authData.publicKey]; @@ -155,6 +164,7 @@ - (void)disableKeyBackup [self resetBackupAllGroupSessionsObjects]; _keyBackupVersion = nil; + self->mxSession.crypto.store.backupVersion = nil; _backupKey = nil; self.state = MXKeyBackupStateDisabled; From 978680830866763141207734f22d3f1eaaeccca8 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 10:54:49 +0100 Subject: [PATCH 44/58] Keys backup: Improve [MXKeyBackup version:] comments and usage --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 4 +- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 76 +++++++++++++----------- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index a87210a5b5..cca3cac913 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -102,7 +102,9 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; #pragma mark - Backup management /** - Get information about the current backup version. + Get information about the current backup version defined on the homeserver. + + It can be different than `self.keyBackupVersion`. @param success A block object called when the operation succeeds. @param failure A block object called when the operation fails. diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 27bbc4d834..56230dbc1a 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -82,52 +82,57 @@ - (void)checkAndStartKeyBackup MXStrongifyAndReturnIfNil(self); MXWeakify(self); - [self isKeyBackupTrusted:keyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull trustInfo) { + dispatch_async(self->mxSession.crypto.cryptoQueue, ^{ MXStrongifyAndReturnIfNil(self); - self.state = MXKeyBackupStateDisabled; + MXWeakify(self); + [self isKeyBackupTrusted:keyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull trustInfo) { + MXStrongifyAndReturnIfNil(self); - if (trustInfo.usable) - { - NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Found usable key backup. version: %@", keyBackupVersion.version); - if (!self.keyBackupVersion) + self.state = MXKeyBackupStateDisabled; + + if (trustInfo.usable) { - // Check the version we used at the previous app run - NSString *versionInStore = self->mxSession.crypto.store.backupVersion; - if (versionInStore && ![versionInStore isEqualToString:keyBackupVersion.version]) + NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Found usable key backup. version: %@", keyBackupVersion.version); + if (!self.keyBackupVersion) + { + // Check the version we used at the previous app run + NSString *versionInStore = self->mxSession.crypto.store.backupVersion; + if (versionInStore && ![versionInStore isEqualToString:keyBackupVersion.version]) + { + NSLog(@"[MXKeyBackup] -> clean the previously used version(%@)", versionInStore); + [self disableKeyBackup]; + } + + NSLog(@"[MXKeyBackup] -> enabling key backups"); + [self enableKeyBackup:keyBackupVersion]; + } + else if ([self.keyBackupVersion.version isEqualToString:self.keyBackupVersion.version]) + { + NSLog(@"[MXKeyBackup] -> same backup version(%@). Keep usint it", self.keyBackupVersion.version); + } + else { - NSLog(@"[MXKeyBackup] -> clean the previously used version(%@)", versionInStore); + NSLog(@"[MXKeyBackup] -> disable the current version(%@) and enabling the new one", self.keyBackupVersion.version); [self disableKeyBackup]; + [self enableKeyBackup:keyBackupVersion]; } - - NSLog(@"[MXKeyBackup] -> enabling key backups"); - [self enableKeyBackup:keyBackupVersion]; - } - else if ([self.keyBackupVersion.version isEqualToString:self.keyBackupVersion.version]) - { - NSLog(@"[MXKeyBackup] -> same backup version(%@). Keep usint it", self.keyBackupVersion.version); - } - else - { - NSLog(@"[MXKeyBackup] -> disable the current version(%@) and enabling the new one", self.keyBackupVersion.version); - [self disableKeyBackup]; - [self enableKeyBackup:keyBackupVersion]; - } - } - else - { - NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: No usable key backup. version: %@", keyBackupVersion.version); - if (!self.keyBackupVersion) - { - NSLog(@"[MXKeyBackup] -> not enabling key backup"); } else { - NSLog(@"[MXKeyBackup] -> disabling key backup"); - [self disableKeyBackup]; + NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: No usable key backup. version: %@", keyBackupVersion.version); + if (!self.keyBackupVersion) + { + NSLog(@"[MXKeyBackup] -> not enabling key backup"); + } + else + { + NSLog(@"[MXKeyBackup] -> disabling key backup"); + [self disableKeyBackup]; + } } - } - }]; + }]; + }); } failure:^(NSError * _Nonnull error) { NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Failed to get current version: %@", error); @@ -322,6 +327,7 @@ - (void)sendKeyBackup - (MXHTTPOperation *)version:(void (^)(MXKeyBackupVersion * _Nonnull))success failure:(void (^)(NSError * _Nonnull))failure { + // Use mxSession.matrixRestClient to respond to the main thread as this method is public return [mxSession.matrixRestClient keyBackupVersion:success failure:failure]; } From 4eb56f91d1665d77e1acbbb0517de4949d7e62a9 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 12:09:44 +0100 Subject: [PATCH 45/58] Keys backup: Make sure [MXKeyBackup backupAllGroupSessions:] correctly fails on a bad backup state --- MatrixSDK/Crypto/Data/MXCryptoConstants.h | 1 + MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/MatrixSDK/Crypto/Data/MXCryptoConstants.h b/MatrixSDK/Crypto/Data/MXCryptoConstants.h index 9271bc1073..b9d60ffeed 100644 --- a/MatrixSDK/Crypto/Data/MXCryptoConstants.h +++ b/MatrixSDK/Crypto/Data/MXCryptoConstants.h @@ -59,5 +59,6 @@ FOUNDATION_EXPORT NSString *const MXKeyBackupErrorDomain; typedef enum : NSUInteger { + MXKeyBackupErrorInvalidStateCode, MXKeyBackupErrorInvalidParametersCode } MXKeyBackupErrorCode; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 56230dbc1a..0848afd14b 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -221,16 +221,25 @@ - (void)sendKeyBackup return; } - if (_state == MXKeyBackupStateBackingUp || !self.enabled) + if (_state == MXKeyBackupStateBackingUp) { - // Do nothing if we are already backing up or if the backup has been disabled + // Do nothing if we are already backing up return; } // Sanity check - if (!_backupKey || !_keyBackupVersion) + if (!self.enabled || !_backupKey || !_keyBackupVersion) { NSLog(@"[MXKeyBackup] sendKeyBackup: Invalide state: %@", @(_state)); + if (backupAllGroupSessionsFailure) + { + NSError *error = [NSError errorWithDomain:MXKeyBackupErrorDomain + code:MXKeyBackupErrorInvalidStateCode + userInfo:@{ + NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Invalid state (%@) for making a backup", @(_state)] + }]; + backupAllGroupSessionsFailure(error); + } return; } From 1078df9b2b644669a3cc60681ddc3f1cec17e7a2 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 15:19:11 +0100 Subject: [PATCH 46/58] Keys backup: Start backup once a device has been verified --- .../KeyBackup/Data/MXKeyBackupVersion.m | 5 + MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 2 +- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 45 ++++++-- MatrixSDK/Crypto/MXCrypto.m | 19 +++- MatrixSDKTests/MXCryptoBackupTests.m | 103 +++++++++++++++++- 5 files changed, 157 insertions(+), 17 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.m b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.m index 6dd1ee7cf1..604e82cd10 100644 --- a/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.m +++ b/MatrixSDK/Crypto/KeyBackup/Data/MXKeyBackupVersion.m @@ -37,6 +37,11 @@ - (nullable instancetype)initWithJSON:(NSDictionary *)JSONDictionary return self; } +- (NSString *)description +{ + return [NSString stringWithFormat:@" version: %@ - algorithm: %@", self, _version, _algorithm]; +} + #pragma mark - MXJSONModel + (id)modelFromJSON:(NSDictionary *)JSONDictionary diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index cca3cac913..9454d29cf2 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -111,7 +111,7 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; @return a MXHTTPOperation instance. */ -- (MXHTTPOperation*)version:(void (^)(MXKeyBackupVersion *keyBackupVersion))success +- (MXHTTPOperation*)version:(void (^)(MXKeyBackupVersion * _Nullable keyBackupVersion))success failure:(void (^)(NSError *error))failure; /** diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 0848afd14b..fa20c6b523 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -70,21 +70,23 @@ - (instancetype)initWithMatrixSession:(MXSession *)matrixSession - (void)checkAndStartKeyBackup { - if (_state != MXKeyBackupStateUnknown) - { - return; - } - self.state = MXKeyBackupStateCheckingBackUpOnHomeserver; MXWeakify(self); - [self version:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + [self version:^(MXKeyBackupVersion * _Nullable keyBackupVersion) { MXStrongifyAndReturnIfNil(self); MXWeakify(self); dispatch_async(self->mxSession.crypto.cryptoQueue, ^{ MXStrongifyAndReturnIfNil(self); + if (!keyBackupVersion) + { + NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Found no key backup version on the homeserver"); + [self disableKeyBackup]; + return; + } + MXWeakify(self); [self isKeyBackupTrusted:keyBackupVersion onComplete:^(MXKeyBackupVersionTrust * _Nonnull trustInfo) { MXStrongifyAndReturnIfNil(self); @@ -135,8 +137,15 @@ - (void)checkAndStartKeyBackup }); } failure:^(NSError * _Nonnull error) { - NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Failed to get current version: %@", error); - self.state = MXKeyBackupStateUnknown; + MXStrongifyAndReturnIfNil(self); + + MXWeakify(self); + dispatch_async(self->mxSession.crypto.cryptoQueue, ^{ + MXStrongifyAndReturnIfNil(self); + + NSLog(@"[MXKeyBackup] checkAndStartKeyBackup: Failed to get current version: %@", error); + self.state = MXKeyBackupStateUnknown; + }); }]; } @@ -334,10 +343,26 @@ - (void)sendKeyBackup #pragma mark - Backup management -- (MXHTTPOperation *)version:(void (^)(MXKeyBackupVersion * _Nonnull))success failure:(void (^)(NSError * _Nonnull))failure +- (MXHTTPOperation *)version:(void (^)(MXKeyBackupVersion * _Nullable))success failure:(void (^)(NSError * _Nonnull))failure { // Use mxSession.matrixRestClient to respond to the main thread as this method is public - return [mxSession.matrixRestClient keyBackupVersion:success failure:failure]; + return [mxSession.matrixRestClient keyBackupVersion:success failure:^(NSError *error) { + + // Workaround because the homeserver currently returns M_NOT_FOUND when there is + // no key backup + MXError *mxError = [[MXError alloc] initWithNSError:error]; + if ([mxError.errcode isEqualToString:kMXErrCodeStringNotFound]) + { + if (success) + { + success(nil); + } + } + else if (failure) + { + failure(error); + } + }]; } - (void)isKeyBackupTrusted:(MXKeyBackupVersion *)keyBackupVersion onComplete:(void (^)(MXKeyBackupVersionTrust * _Nonnull))onComplete diff --git a/MatrixSDK/Crypto/MXCrypto.m b/MatrixSDK/Crypto/MXCrypto.m index 139940b36d..cf38c5779b 100644 --- a/MatrixSDK/Crypto/MXCrypto.m +++ b/MatrixSDK/Crypto/MXCrypto.m @@ -743,10 +743,13 @@ - (void)setDeviceVerification:(MXDeviceVerification)verificationStatus forDevice if (!device) { NSLog(@"[MXCrypto] setDeviceVerificationForDevice: Unknown device %@:%@", userId, deviceId); - - dispatch_async(dispatch_get_main_queue(), ^{ - success(); - }); + + if (success) + { + dispatch_async(dispatch_get_main_queue(), ^{ + success(); + }); + } return; } @@ -754,6 +757,14 @@ - (void)setDeviceVerification:(MXDeviceVerification)verificationStatus forDevice { device.verified = verificationStatus; [self.store storeDeviceForUser:userId device:device]; + + if ([userId isEqualToString:self.mxSession.myUser.userId]) + { + // If one of the user's own devices is being marked as verified / unverified, + // check the key backup status, since whether or not we use this depends on + // whether it has a signature from a verified device + [self.backup checkAndStartKeyBackup]; + } } if (success) diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 91b844cc8d..50d0719969 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -663,8 +663,7 @@ - (void)testCheckAndStartKeyBackupWhenRestartingAMatrixSession } /** - Check backup starts automatically if there is an existing and compatible backup - version on the homeserver. + Check MXKeyBackupStateWrongBackUpVersion state - Make alice back up her keys to her homeserver - Create a new backup with fake data on the homeserver - Make alice back up all her keys again @@ -713,6 +712,106 @@ - (void)testBackupWhenAnotherBackupWasCreated }]; } +/** + - Do an e2e backup to the homeserver + - Log Alice on a new device + - Post a message to have a new megolm session + - Try to backup all + -> It must fail + - Validate the old device from the new one + -> Backup should automatically enable on the new device + -> It must use the same backup version + - Try to backup all again + -> It must success + */ +- (void)testBackupAfterVerifingADevice +{ + [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { + + // - Do an e2e backup to the homeserver + [aliceSession.crypto.backup prepareKeyBackupVersion:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) { + [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { + [aliceSession.crypto.backup backupAllGroupSessions:^{ + + NSString *oldDeviceId = aliceSession.matrixRestClient.credentials.deviceId; + MXKeyBackupVersion *oldKeyBackupVersion = aliceSession.crypto.backup.keyBackupVersion; + + // - Log Alice on a new device + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = YES; + [matrixSDKTestsData relogUserSessionWithNewDevice:aliceSession withPassword:MXTESTS_ALICE_PWD onComplete:^(MXSession *aliceSession2) { + [MXSDKOptions sharedInstance].enableCryptoWhenStartingMXSession = NO; + + // - Post a message to have a new megolm session + aliceSession2.crypto.warnOnUnknowDevices = NO; + MXRoom *room2 = [aliceSession2 roomWithRoomId:roomId]; + [room2 sendTextMessage:@"New keys" success:^(NSString *eventId) { + + // - Try to backup all + [aliceSession2.crypto.backup backupAllGroupSessions:^{ + + XCTFail(@"The backup must fail"); + [expectation fulfill]; + + } progress:nil failure:^(NSError * _Nonnull error) { + + // -> It must fail + XCTAssertEqualObjects(error.domain, MXKeyBackupErrorDomain); + XCTAssertEqual(error.code, MXKeyBackupErrorInvalidStateCode); + XCTAssertFalse(aliceSession2.crypto.backup.enabled); + + // - Validate the old device from the new one + [aliceSession2.crypto setDeviceVerification:MXDeviceVerified forDevice:oldDeviceId ofUser:aliceSession2.myUser.userId success:nil failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + // -> Backup should automatically enable on the new device + __block id observer; + observer = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:aliceSession2.crypto.backup queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { + + if (observer && aliceSession2.crypto.backup.state == MXKeyBackupStateReadyToBackUp) + { + [[NSNotificationCenter defaultCenter] removeObserver:observer]; + observer = nil; + + // -> It must use the same backup version + XCTAssertEqualObjects(oldKeyBackupVersion.version, aliceSession2.crypto.backup.keyBackupVersion.version); + + // - Try to backup all again + [aliceSession2.crypto.backup backupAllGroupSessions:^{ + + // -> It must success + XCTAssertTrue(aliceSession2.crypto.backup.enabled); + + [expectation fulfill]; + + } progress:nil failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } + }]; + }]; + } failure:^(NSError *error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; + } progress:nil failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError * _Nonnull error) { + XCTFail(@"The request should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + @end #pragma clang diagnostic pop From ba4480dcb324cb159f07a05d964190c3a2022c63 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 15:53:45 +0100 Subject: [PATCH 47/58] Keys backup: Fix typo sessionIdSenderKer -> sessionIdSenderKey --- .../MXRealmCryptoStore/MXRealmCryptoStore.m | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index 339162ec59..5a041ff6c2 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -99,7 +99,7 @@ @interface MXRealmOlmInboundGroupSession : RLMObject // A primary key is required to update `backedUp`. // Do our combined primary key ourselves as it is not supported by Realm. -@property NSString *sessionIdSenderKer; +@property NSString *sessionIdSenderKey; // Indicate if the key has been backed up to the homeserver @property BOOL backedUp; @@ -108,7 +108,7 @@ @interface MXRealmOlmInboundGroupSession : RLMObject @implementation MXRealmOlmInboundGroupSession + (NSString *)primaryKey { - return @"sessionIdSenderKer"; + return @"sessionIdSenderKey"; } + (NSString *)primaryKeyWithSessionId:(NSString*)sessionId senderKey:(NSString*)senderKey @@ -663,9 +663,9 @@ - (void)storeInboundGroupSession:(MXOlmInboundGroupSession*)session RLMRealm *realm = self.realm; - NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier + NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier senderKey:session.senderKey]; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionIdSenderKer = %@", sessionIdSenderKer].firstObject; + MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionIdSenderKey = %@", sessionIdSenderKey].firstObject; if (realmSession) { // Update the existing one @@ -677,12 +677,12 @@ - (void)storeInboundGroupSession:(MXOlmInboundGroupSession*)session { // Create it isNew = YES; - NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier + NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:session.session.sessionIdentifier senderKey:session.senderKey]; realmSession = [[MXRealmOlmInboundGroupSession alloc] initWithValue:@{ @"sessionId": session.session.sessionIdentifier, @"senderKey": session.senderKey, - @"sessionIdSenderKer": sessionIdSenderKer, + @"sessionIdSenderKey": sessionIdSenderKey, @"olmInboundGroupSessionData": [NSKeyedArchiver archivedDataWithRootObject:session] }]; @@ -697,9 +697,9 @@ - (void)storeInboundGroupSession:(MXOlmInboundGroupSession*)session - (MXOlmInboundGroupSession*)inboundGroupSessionWithId:(NSString*)sessionId andSenderKey:(NSString*)senderKey { MXOlmInboundGroupSession *session; - NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId + NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId senderKey:senderKey]; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:self.realm where:@"sessionIdSenderKer = %@", sessionIdSenderKer].firstObject; + MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:self.realm where:@"sessionIdSenderKey = %@", sessionIdSenderKey].firstObject; NSLog(@"[MXRealmCryptoStore] inboundGroupSessionWithId: %@ -> %@", sessionId, realmSession ? @"found" : @"not found"); @@ -776,9 +776,9 @@ - (void)markBackupDoneForInboundGroupSessionWithId:(NSString*)sessionId andSende { RLMRealm *realm = self.realm; - NSString *sessionIdSenderKer = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId + NSString *sessionIdSenderKey = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:sessionId senderKey:senderKey]; - MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionIdSenderKer = %@", sessionIdSenderKer].firstObject; + MXRealmOlmInboundGroupSession *realmSession = [MXRealmOlmInboundGroupSession objectsInRealm:realm where:@"sessionIdSenderKey = %@", sessionIdSenderKey].firstObject; [realm transactionWithBlock:^{ realmSession.backedUp = @(YES); @@ -1167,10 +1167,10 @@ + (RLMRealm*)realmForUser:(NSString*)userId // We need to update the db because a sessionId property has been added to MXRealmOlmSession // to ensure uniqueness - NSLog(@" Add sessionIdSenderKer, a combined primary key, to all MXRealmOlmInboundGroupSession objects"); + NSLog(@" Add sessionIdSenderKey, a combined primary key, to all MXRealmOlmInboundGroupSession objects"); [migration enumerateObjects:MXRealmOlmInboundGroupSession.className block:^(RLMObject *oldObject, RLMObject *newObject) { - newObject[@"sessionIdSenderKer"] = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:oldObject[@"sessionId"] + newObject[@"sessionIdSenderKey"] = [MXRealmOlmInboundGroupSession primaryKeyWithSessionId:oldObject[@"sessionId"] senderKey:oldObject[@"senderKey"]]; }]; From 86d55488cde06eee74ddbe7ae59bb3cb4bdc8e8b Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 16:10:58 +0100 Subject: [PATCH 48/58] Keys backup: createKeyBackupVersion: reset backup markers only on success --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index fa20c6b523..9d71a62a2d 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -492,15 +492,15 @@ - (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBacku dispatch_async(mxSession.crypto.cryptoQueue, ^{ MXStrongifyAndReturnIfNil(self); - // Reset backup markers - [self->mxSession.crypto.store resetBackupMarkers]; - MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion new]; keyBackupVersion.algorithm = keyBackupCreationInfo.algorithm; keyBackupVersion.authData = keyBackupCreationInfo.authData.JSONDictionary; MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) { + // Reset backup markers + [self->mxSession.crypto.store resetBackupMarkers]; + keyBackupVersion.version = version; NSError *error = [self enableKeyBackup:keyBackupVersion]; From 516919ed39ca676bc24f225b65b5d8c096dbdb26 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 16:33:43 +0100 Subject: [PATCH 49/58] Keys backup: Thanks into account Benoit's comments --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h | 2 -- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 3 ++- MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m | 31 +++++++++++----------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h index 9454d29cf2..0c6caa434a 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.h @@ -173,8 +173,6 @@ FOUNDATION_EXPORT NSString *const kMXKeyBackupDidStateChangeNotification; /** Start to back up keys immediately. - @param version the backup version to delete. - @param success A block object called when the operation complets. @param progress A block object called to indicate operation progress based on number of backed up keys. @param failure A block object called when the operation fails. diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 9d71a62a2d..0854a13617 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -227,6 +227,7 @@ - (void)sendKeyBackup if (!sessions.count) { // Backup is up to date + self.state = MXKeyBackupStateReadyToBackUp; return; } @@ -882,7 +883,7 @@ - (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withP @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ? sessionData.forwardingCurve25519KeyChain : @[], @"session_key": sessionData.sessionKey }; - OLMPkMessage *encryptedSessionBackupData = [_backupKey encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; + OLMPkMessage *encryptedSessionBackupData = [encryption encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; // Build backup data for that key MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; diff --git a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m index d2aadd95ea..36f4fe20b5 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m +++ b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m @@ -68,6 +68,20 @@ + (NSData *)decode:(NSString *)recoveryKey error:(NSError **)error return nil; } + // Check length + if (result.length != + sizeof(kOlmRecoveryKeyPrefix) + [OLMPkDecryption privateKeyLength] + 1) + { + if (error) + { + *error = [NSError errorWithDomain:MXRecoveryKeyErrorDomain + code:MXRecoveryKeyErrorLengthCode + userInfo:@{ + NSLocalizedDescriptionKey: @"Incorrect length", + }]; + } + return nil; + } // Check the checksum UInt8 parity = 0; @@ -106,23 +120,8 @@ + (NSData *)decode:(NSString *)recoveryKey error:(NSError **)error } } - // Check length - if (result.length != - sizeof(kOlmRecoveryKeyPrefix) + [OLMPkDecryption privateKeyLength] + 1) - { - if (error) - { - *error = [NSError errorWithDomain:MXRecoveryKeyErrorDomain - code:MXRecoveryKeyErrorLengthCode - userInfo:@{ - NSLocalizedDescriptionKey: @"Incorrect length", - }]; - } - return nil; - } - // Remove header and checksum bytes - [result replaceBytesInRange:NSMakeRange(0, 2) withBytes:NULL length:0]; + [result replaceBytesInRange:NSMakeRange(0, sizeof(kOlmRecoveryKeyPrefix)) withBytes:NULL length:0]; result.length -= 1; return result; From fe52770cd9b7f24e048e767ed00fa39faa09d7e4 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 16:39:31 +0100 Subject: [PATCH 50/58] Keys backup: Thanks into account Benoit's comments --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 0854a13617..4bd90b5e2a 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -240,7 +240,7 @@ - (void)sendKeyBackup // Sanity check if (!self.enabled || !_backupKey || !_keyBackupVersion) { - NSLog(@"[MXKeyBackup] sendKeyBackup: Invalide state: %@", @(_state)); + NSLog(@"[MXKeyBackup] sendKeyBackup: Invalid state: %@", @(_state)); if (backupAllGroupSessionsFailure) { NSError *error = [NSError errorWithDomain:MXKeyBackupErrorDomain From 40ba3a2bb316b67923c7e75654b9cad64e602754 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 16:42:05 +0100 Subject: [PATCH 51/58] Keys backup: Thanks into account Benoit's comments --- MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m index 36f4fe20b5..468d3ad1e6 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m +++ b/MatrixSDK/Crypto/KeyBackup/MXRecoveryKey.m @@ -25,7 +25,7 @@ NSString *const MXRecoveryKeyErrorDomain = @"org.matrix.sdk.recoverykey"; -// Picked arbitrarily but to try & avoid clashing with any bitcoin ones +// Picked arbitrarily bits to try & avoid clashing with any bitcoin ones // (also base58 encoded, albeit with a lot of hashing) const UInt8 kOlmRecoveryKeyPrefix[] = {0x8B, 0x01}; From 18f7ee848f15fddd28e2e1ebbd6418125c501593 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 16:52:49 +0100 Subject: [PATCH 52/58] Keys backup: MXRealmCryptoStore: Do not increment 2 db schema versions into the same feature dev branch --- .../Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m index 5a041ff6c2..240fee2628 100644 --- a/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m +++ b/MatrixSDK/Crypto/Data/Store/MXRealmCryptoStore/MXRealmCryptoStore.m @@ -23,7 +23,7 @@ #import "MXSession.h" #import "MXTools.h" -NSUInteger const kMXRealmCryptoStoreVersion = 8; +NSUInteger const kMXRealmCryptoStoreVersion = 7; static NSString *const kMXRealmCryptoStoreFolder = @"MXRealmCryptoStore"; @@ -1174,12 +1174,6 @@ + (RLMRealm*)realmForUser:(NSString*)userId senderKey:oldObject[@"senderKey"]]; }]; - NSLog(@"[MXRealmCryptoStore] Migration from schema #6 -> #7 completed"); - } - case 7: - { - NSLog(@"[MXRealmCryptoStore] Migration from schema #7 -> #8"); - // We need to update the db because a identityKey property has been added to MXRealmDeviceInfo NSLog(@" Add identityKey to all MXRealmOlmInboundGroupSession objects"); [migration enumerateObjects:MXRealmDeviceInfo.className block:^(RLMObject *oldObject, RLMObject *newObject) { @@ -1192,7 +1186,7 @@ + (RLMRealm*)realmForUser:(NSString*)userId } }]; - NSLog(@"[MXRealmCryptoStore] Migration from schema #7 -> #8 completed"); + NSLog(@"[MXRealmCryptoStore] Migration from schema #6 -> #7 completed"); } } } From 67380316d55466896342bae1021f764f7850a0c3 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 13 Nov 2018 18:02:25 +0100 Subject: [PATCH 53/58] Keys backup: Update CHANGES --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 49ee01a076..19800ce8df 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,7 @@ Changes in Matrix iOS SDK in 0.12.0 (2018-11-) =============================================== Improvements: + * MXCrypto: Add the MXKeyBackup module to manage e2e keys backup (vector-im/riot-ios#2070). * MXMediaManager/MXMediaLoader: Do not allow non-mxc content URLs. * MXMediaManager: Add a constructor based on a homeserver URL, to handle directly the Matrix Content URI (mxc://...). * MXSession: Add a MediaManager instance to handle the media stored on the Matrix Content repository. From 6e90dc251abcf84f05eca27e06c6e995639ae51e Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 14 Nov 2018 09:49:15 +0100 Subject: [PATCH 54/58] Keys backup: MXKeyBackUp: Make sure we retry on failure on sendKeyBackup Add some dev logs to check timing in this method --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 4bd90b5e2a..473563d790 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -219,10 +219,12 @@ - (void)maybeSendKeyBackup - (void)sendKeyBackup { + NSLog(@"[MXKeyBackup] sendKeyBackup"); + // Get a chunk of keys to backup NSArray *sessions = [mxSession.crypto.store inboundGroupSessionsToBackup:kMXKeyBackupSendKeysMaxCount]; - NSLog(@"[MXKeyBackup] sendKeyBackup: %@ sessions to back up", @(sessions.count)); + NSLog(@"[MXKeyBackup] sendKeyBackup: 1 - %@ sessions to back up", @(sessions.count)); if (!sessions.count) { @@ -255,6 +257,8 @@ - (void)sendKeyBackup self.state = MXKeyBackupStateBackingUp; + NSLog(@"[MXKeyBackup] sendKeyBackup: 2 - Encrypting keys"); + // Gather data to send to the homeserver // roomId -> sessionId -> MXKeyBackupData NSMutableDictionary *rooms = [NSMutableDictionary dictionary]; for (NSString *roomId in roomsKeyBackup) @@ -289,11 +295,15 @@ - (void)sendKeyBackup MXKeysBackupData *keysBackupData = [MXKeysBackupData new]; keysBackupData.rooms = rooms; + NSLog(@"[MXKeyBackup] sendKeyBackup: 4 - Sending request"); + // Make the request MXWeakify(self); [mxSession.crypto.matrixRestClient sendKeysBackup:keysBackupData version:_keyBackupVersion.version success:^{ MXStrongifyAndReturnIfNil(self); + NSLog(@"[MXKeyBackup] sendKeyBackup: 5a - Request complete"); + // Mark keys as backed up for (MXOlmInboundGroupSession *session in sessions) { @@ -316,7 +326,7 @@ - (void)sendKeyBackup } failure:^(NSError *error) { MXStrongifyAndReturnIfNil(self); - NSLog(@"[MXKeyBackup] sendKeyBackup: sendKeysBackup failed. Error: %@", error); + NSLog(@"[MXKeyBackup] sendKeyBackup: 5b - sendKeysBackup failed. Error: %@", error); void (^backupAllGroupSessionsFailure)(NSError *error) = self->backupAllGroupSessionsFailure; @@ -328,8 +338,9 @@ - (void)sendKeyBackup } else { - // Come back to the ready state so that we will retry on the next received key + // Retry a bit later self.state = MXKeyBackupStateReadyToBackUp; + [self maybeSendKeyBackup]; } if (backupAllGroupSessionsFailure) From 9d9edbd6a2d43c34200b06f1ce8ec5abcadabb13 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 15 Nov 2018 09:17:13 +0100 Subject: [PATCH 55/58] Keys backup: Podfile.lock with Base58 --- Podfile.lock | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index bf6c5d72c7..c5b180451b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,6 +15,7 @@ PODS: - AFNetworking/UIKit (3.2.1): - AFNetworking/NSURLSession - GZIP (1.2.2) + - libbase58 (0.1.4) - OLMKit (3.0.0): - OLMKit/olmc (= 3.0.0) - OLMKit/olmcpp (= 3.0.0) @@ -27,6 +28,7 @@ PODS: DEPENDENCIES: - AFNetworking (~> 3.2.0) - GZIP (~> 1.2.2) + - libbase58 (~> 0.1.4) - OLMKit (~> 3.0.0) - Realm (~> 3.11.1) @@ -34,15 +36,17 @@ SPEC REPOS: https://github.com/cocoapods/specs.git: - AFNetworking - GZIP + - libbase58 - OLMKit - Realm SPEC CHECKSUMS: AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 GZIP: 12374d285e3b5d46cfcd480700fcfc7e16caf4f1 + libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd OLMKit: 88eda69110489f817d59bcb4353b7c247570aa4f Realm: 037c5919b9ceb59d6beed5d3b031096856b119b3 -PODFILE CHECKSUM: bde5adf0c918613fa0f49b23ec06562ddba86eef +PODFILE CHECKSUM: 61c5af3ab100e2e410c011b64edf50bea12e675f COCOAPODS: 1.6.0.beta.2 From 397757f1bdb829c9137e359a2d8e88853746456a Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 15 Nov 2018 09:34:45 +0100 Subject: [PATCH 56/58] Keys backup: Manage Benoit's comments --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 8 ++++---- MatrixSDKTests/MXCryptoBackupTests.m | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 473563d790..641dde4247 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -109,7 +109,7 @@ - (void)checkAndStartKeyBackup NSLog(@"[MXKeyBackup] -> enabling key backups"); [self enableKeyBackup:keyBackupVersion]; } - else if ([self.keyBackupVersion.version isEqualToString:self.keyBackupVersion.version]) + else if ([self.keyBackupVersion.version isEqualToString:keyBackupVersion.version]) { NSLog(@"[MXKeyBackup] -> same backup version(%@). Keep usint it", self.keyBackupVersion.version); } @@ -266,7 +266,7 @@ - (void)sendKeyBackup for (MXOlmInboundGroupSession *session in sessions) { - MXKeyBackupData *keyBackupData = [self encryptGroupSession:session withPkEncryption:_backupKey]; + MXKeyBackupData *keyBackupData = [self encryptGroupSession:session]; if (!roomsKeyBackup[session.roomId]) { @@ -879,7 +879,7 @@ - (OLMPkDecryption*)pkDecryptionFromRecoveryKey:(NSString*)recoveryKey error:(NS return decryption; } -- (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withPkEncryption:(OLMPkEncryption*)encryption +- (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session { // Gather information for each key MXDeviceInfo *device = [mxSession.crypto.deviceList deviceWithIdentityKey:session.senderKey andAlgorithm:kMXCryptoMegolmAlgorithm]; @@ -894,7 +894,7 @@ - (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withP @"forwarding_curve25519_key_chain": sessionData.forwardingCurve25519KeyChain ? sessionData.forwardingCurve25519KeyChain : @[], @"session_key": sessionData.sessionKey }; - OLMPkMessage *encryptedSessionBackupData = [encryption encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; + OLMPkMessage *encryptedSessionBackupData = [_backupKey encryptMessage:[MXTools serialiseJSONObject:sessionBackupData] error:nil]; // Build backup data for that key MXKeyBackupData *keyBackupData = [MXKeyBackupData new]; diff --git a/MatrixSDKTests/MXCryptoBackupTests.m b/MatrixSDKTests/MXCryptoBackupTests.m index 50d0719969..765eaf63c2 100644 --- a/MatrixSDKTests/MXCryptoBackupTests.m +++ b/MatrixSDKTests/MXCryptoBackupTests.m @@ -25,7 +25,7 @@ @interface MXKeyBackup (Testing) - (OLMPkDecryption*)pkDecryptionFromRecoveryKey:(NSString*)recoveryKey error:(NSError **)error; -- (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withPkEncryption:(OLMPkEncryption*)encryption; +- (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session; - (MXMegolmSessionData*)decryptKeyBackupData:(MXKeyBackupData*)keyBackupData forSession:(NSString*)sessionId inRoom:(NSString*)roomId withPkDecryption:(OLMPkDecryption*)decryption; @end @@ -511,7 +511,7 @@ - (void)testEncryptAndDecryptKeyBackupData [aliceSession.crypto.backup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) { // - Check [MXKeyBackup encryptGroupSession] returns stg - MXKeyBackupData *keyBackupData = [aliceSession.crypto.backup encryptGroupSession:session withPkEncryption:aliceSession.crypto.backup.backupKey]; + MXKeyBackupData *keyBackupData = [aliceSession.crypto.backup encryptGroupSession:session]; XCTAssertNotNil(keyBackupData); XCTAssertNotNil(keyBackupData.sessionData); @@ -724,7 +724,7 @@ - (void)testBackupWhenAnotherBackupWasCreated - Try to backup all again -> It must success */ -- (void)testBackupAfterVerifingADevice +- (void)testBackupAfterVerifyingADevice { [matrixSDKTestsE2EData doE2ETestWithAliceAndBobInARoomWithCryptedMessages:self cryptedBob:YES readyToTest:^(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation) { From c6ddcd5dc1995197af3818884882cc4a98ae1f24 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 15 Nov 2018 09:48:41 +0100 Subject: [PATCH 57/58] Keys backup: Manage Steve's comments --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 6 ++++++ MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h | 11 +++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 641dde4247..27cb992d6a 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -149,6 +149,12 @@ - (void)checkAndStartKeyBackup }]; } +/** + Enable backing up of keys. + + @param keyBackupVersion backup information object as returned by `[MXKeyBackup version]`. + @return an error if the operation fails. + */ - (NSError*)enableKeyBackup:(MXKeyBackupVersion*)version { MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData modelFromJSON:version.authData]; diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h index 4794a7e7b2..1bba553e3d 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup_Private.h @@ -36,19 +36,14 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)checkAndStartKeyBackup; -/** - Enable backing up of keys. - - @param keyBackupVersion backup information object as returned by `[MXKeyBackup version]`. - @return an error if the operation fails. - */ -- (NSError*)enableKeyBackup:(MXKeyBackupVersion*)keyBackupVersion; - /** * Disable backing up of keys. */ - (void)disableKeyBackup; +/** + Do a backup if there are new keys. + */ - (void)maybeSendKeyBackup; @end From 4ad1641eec7c984f9b948e7551bd5ba9fc30d99a Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 15 Nov 2018 15:42:46 +0100 Subject: [PATCH 58/58] Keys backup: Fix typo --- MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m index 27cb992d6a..6794c5557c 100644 --- a/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m +++ b/MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m @@ -111,7 +111,7 @@ - (void)checkAndStartKeyBackup } else if ([self.keyBackupVersion.version isEqualToString:keyBackupVersion.version]) { - NSLog(@"[MXKeyBackup] -> same backup version(%@). Keep usint it", self.keyBackupVersion.version); + NSLog(@"[MXKeyBackup] -> same backup version(%@). Keep using it", self.keyBackupVersion.version); } else {