{"action":"created","comment":{"url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/comments/233088448","pull_request_review_id":174426567,"id":233088448,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMzA4ODQ0OA==","diff_hunk":"@@ -0,0 +1,723 @@\n+/*\n+ Copyright 2018 New Vector Ltd\n+\n+ Licensed under the Apache License, Version 2.0 (the \"License\");\n+ you may not use this file except in compliance with the License.\n+ You may obtain a copy of the License at\n+\n+ http://www.apache.org/licenses/LICENSE-2.0\n+\n+ Unless required by applicable law or agreed to in writing, software\n+ distributed under the License is distributed on an \"AS IS\" BASIS,\n+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ See the License for the specific language governing permissions and\n+ limitations under the License.\n+ */\n+\n+#import \"MXKeyBackup.h\"\n+#import \"MXKeyBackup_Private.h\"\n+\n+#import \"MXCrypto_Private.h\"\n+\n+#import <OLMKit/OLMKit.h>\n+#import \"MXRecoveryKey.h\"\n+#import \"MXSession.h\"   // TODO: To remove\n+#import \"MXTools.h\"\n+\n+\n+#pragma mark - Constants definitions\n+\n+NSString *const kMXKeyBackupDidStateChangeNotification = @\"kMXKeyBackupDidStateChangeNotification\";\n+\n+/**\n+ Maximum delay in ms in `[MXKeyBackup maybeSendKeyBackup]`.\n+ */\n+NSUInteger const kMXKeyBackupWaitingTimeToSendKeyBackup = 10000;\n+\n+/**\n+ Maximum number of keys to send at a time to the homeserver.\n+ */\n+NSUInteger const kMXKeyBackupSendKeysMaxCount = 100;\n+\n+\n+@interface MXKeyBackup ()\n+{\n+    MXSession *mxSession;\n+\n+    // Observer to kMXKeyBackupDidStateChangeNotification when backupAllGroupSessions is progressing\n+    id backupAllGroupSessionsObserver;\n+\n+    // Failure block when backupAllGroupSessions is progressing\n+    void (^backupAllGroupSessionsFailure)(NSError *error);\n+    \n+    // track whether this device's megolm keys are being backed up incrementally\n+    // to the server or not.\n+    // XXX: this should probably have a single source of truth from OlmAccount\n+// +    this.backupInfo = null; // The info dict from /room_keys/version\n+// +   this.backupKey = null; // The encryption key object\n+//    this._checkedForBackup = false; // Have we checked the server for a backup we can use?\n+// X  this._sendingBackups = false; // Are we currently sending backups?\n+}\n+\n+@end\n+\n+@implementation MXKeyBackup\n+\n+#pragma mark - SDK-Private methods -\n+\n+- (instancetype)initWithMatrixSession:(MXSession *)matrixSession\n+{\n+    self = [self init];\n+    {\n+        _state = MXKeyBackupStateDisabled;\n+        mxSession = matrixSession;\n+    }\n+    return self;\n+}\n+\n+- (NSError*)enableKeyBackup:(MXKeyBackupVersion*)version\n+{\n+    MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData modelFromJSON:version.authData];\n+    if (authData)\n+    {\n+        _keyBackupVersion = version;\n+        _backupKey = [OLMPkEncryption new];\n+        [_backupKey setRecipientKey:authData.publicKey];\n+\n+        self.state = MXKeyBackupStateReadyToBackUp;\n+        \n+        [self maybeSendKeyBackup];\n+\n+        return nil;\n+    }\n+\n+    return [NSError errorWithDomain:MXKeyBackupErrorDomain\n+                               code:MXKeyBackupErrorInvalidParametersCode\n+                           userInfo:@{\n+                                      NSLocalizedDescriptionKey: @\"Invalid authentication data\",\n+                                      }];\n+}\n+\n+- (void)disableKeyBackup\n+{\n+    [self resetBackupAllGroupSessionsObjects];\n+    \n+    _keyBackupVersion = nil;\n+    _backupKey = nil;\n+    self.state = MXKeyBackupStateDisabled;\n+\n+    // Reset backup markers\n+    [self->mxSession.crypto.store resetBackupMarkers];\n+}\n+\n+- (void)maybeSendKeyBackup\n+{\n+    if (_state == MXKeyBackupStateReadyToBackUp)\n+    {\n+        self.state = MXKeyBackupStateWillBackUp;\n+\n+        // Wait between 0 and 10 seconds, to avoid backup requests from\n+        // different clients hitting the server all at the same time when a\n+        // new key is sent\n+        NSUInteger delayInMs = arc4random_uniform(kMXKeyBackupWaitingTimeToSendKeyBackup);\n+\n+        MXWeakify(self);\n+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInMs * NSEC_PER_MSEC)), mxSession.crypto.cryptoQueue, ^{\n+            MXStrongifyAndReturnIfNil(self);\n+\n+            [self sendKeyBackup];\n+        });\n+    }\n+    else\n+    {\n+        NSLog(@\"[MXKeyBackup] maybeSendKeyBackup: Skip it because state: %@\", @(_state));\n+    }\n+}\n+\n+- (void)sendKeyBackup\n+{\n+    // Get a chunk of keys to backup\n+    NSArray<MXOlmInboundGroupSession*> *sessions = [mxSession.crypto.store inboundGroupSessionsToBackup:kMXKeyBackupSendKeysMaxCount];\n+\n+    NSLog(@\"[MXKeyBackup] sendKeyBackup: %@ sessions to back up\", @(sessions.count));\n+\n+    if (!sessions.count)\n+    {\n+        // Backup is up to date\n+        return;\n+    }\n+\n+    if (_state == MXKeyBackupStateBackingUp || _state == MXKeyBackupStateDisabled)\n+    {\n+        // Do nothing if we are already backing up or if the backup has been disabled\n+        return;\n+    }\n+\n+    // Sanity check\n+    if (!_backupKey || !_keyBackupVersion)\n+    {\n+        NSLog(@\"[MXKeyBackup] sendKeyBackup: Invalide state: %@\", @(_state));\n+        return;\n+    }\n+\n+    self.state = MXKeyBackupStateBackingUp;\n+\n+    // Gather data to send to the homeserver\n+    // roomId -> sessionId -> MXKeyBackupData\n+    NSMutableDictionary<NSString *,\n+        NSMutableDictionary<NSString *, MXKeyBackupData*> *> *roomsKeyBackup = [NSMutableDictionary dictionary];\n+\n+    for (MXOlmInboundGroupSession *session in sessions)\n+    {\n+        MXKeyBackupData *keyBackupData = [self encryptGroupSession:session withPkEncryption:_backupKey];\n+\n+        if (!roomsKeyBackup[session.roomId])\n+        {\n+            roomsKeyBackup[session.roomId] = [NSMutableDictionary dictionary];\n+        }\n+        roomsKeyBackup[session.roomId][session.session.sessionIdentifier] = keyBackupData;\n+    }\n+\n+    // Finalise data to send\n+    NSMutableDictionary<NSString*, MXRoomKeysBackupData*> *rooms = [NSMutableDictionary dictionary];\n+    for (NSString *roomId in roomsKeyBackup)\n+    {\n+        NSMutableDictionary<NSString*, MXKeyBackupData*> *roomSessions = [NSMutableDictionary dictionary];\n+        for (NSString *sessionId in roomsKeyBackup[roomId])\n+        {\n+            roomSessions[sessionId] = roomsKeyBackup[roomId][sessionId];\n+        }\n+        MXRoomKeysBackupData *roomKeysBackupData = [MXRoomKeysBackupData new];\n+        roomKeysBackupData.sessions = roomSessions;\n+\n+        rooms[roomId] = roomKeysBackupData;\n+    }\n+\n+    MXKeysBackupData *keysBackupData = [MXKeysBackupData new];\n+    keysBackupData.rooms = rooms;\n+\n+    // Make the request\n+    MXWeakify(self);\n+    [mxSession.crypto.matrixRestClient sendKeysBackup:keysBackupData version:_keyBackupVersion.version success:^{\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        // Mark keys as backed up\n+        for (MXOlmInboundGroupSession *session in sessions)\n+        {\n+            [self->mxSession.crypto.store markBackupDoneForInboundGroupSessionWithId:session.session.sessionIdentifier andSenderKey:session.senderKey];\n+        }\n+\n+        if (sessions.count < kMXKeyBackupSendKeysMaxCount)\n+        {\n+            NSLog(@\"[MXKeyBackup] sendKeyBackup: All keys have been backed up\");\n+            self.state = MXKeyBackupStateReadyToBackUp;\n+        }\n+        else\n+        {\n+            NSLog(@\"[MXKeyBackup] sendKeyBackup: Continue to back up keys\");\n+            self.state = MXKeyBackupStateWillBackUp;\n+\n+            [self sendKeyBackup];\n+        }\n+\n+    } failure:^(NSError *error) {\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        if (self->backupAllGroupSessionsFailure)\n+        {\n+            self->backupAllGroupSessionsFailure(error);\n+        }\n+\n+        // TODO: Manage retries\n+        NSLog(@\"[MXKeyBackup] sendKeyBackup: sendKeysBackup failed. Error: %@\", error);\n+    }];\n+}\n+\n+\n+#pragma mark - Public methods -\n+\n+#pragma mark - Backup management\n+\n+- (MXHTTPOperation *)version:(void (^)(MXKeyBackupVersion * _Nonnull))success failure:(void (^)(NSError * _Nonnull))failure\n+{\n+    return [mxSession.matrixRestClient keyBackupVersion:success failure:failure];\n+}\n+\n+- (void)prepareKeyBackupVersion:(void (^)(MXMegolmBackupCreationInfo *keyBackupCreationInfo))success\n+                        failure:(nullable void (^)(NSError *error))failure;\n+{\n+    MXWeakify(self);\n+    dispatch_async(mxSession.crypto.cryptoQueue, ^{\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        OLMPkDecryption *decryption = [OLMPkDecryption new];\n+\n+        NSError *error;\n+        MXMegolmBackupAuthData *authData = [MXMegolmBackupAuthData new];\n+        authData.publicKey = [decryption generateKey:&error];\n+        if (error)\n+        {\n+            if (failure)\n+            {\n+                dispatch_async(dispatch_get_main_queue(), ^{\n+                    failure(error);\n+                });\n+            }\n+            return;\n+        }\n+        authData.signatures = [self->mxSession.crypto signObject:authData.JSONDictionary];\n+\n+        MXMegolmBackupCreationInfo *keyBackupCreationInfo = [MXMegolmBackupCreationInfo new];\n+        keyBackupCreationInfo.algorithm = kMXCryptoMegolmBackupAlgorithm;\n+        keyBackupCreationInfo.authData = authData;\n+        keyBackupCreationInfo.recoveryKey = [MXRecoveryKey encode:decryption.privateKey];\n+\n+        dispatch_async(dispatch_get_main_queue(), ^{\n+            success(keyBackupCreationInfo);\n+        });\n+    });\n+}\n+\n+- (MXHTTPOperation*)createKeyBackupVersion:(MXMegolmBackupCreationInfo*)keyBackupCreationInfo\n+                                   success:(void (^)(MXKeyBackupVersion *keyBackupVersion))success\n+                                   failure:(nullable void (^)(NSError *error))failure\n+{\n+    MXHTTPOperation *operation = [MXHTTPOperation new];\n+\n+    [self setState:MXKeyBackupStateEnabling];\n+\n+    MXWeakify(self);\n+    dispatch_async(mxSession.crypto.cryptoQueue, ^{\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        // Reset backup markers\n+        [self->mxSession.crypto.store resetBackupMarkers];\n+\n+        MXKeyBackupVersion *keyBackupVersion = [MXKeyBackupVersion new];\n+        keyBackupVersion.algorithm = keyBackupCreationInfo.algorithm;\n+        keyBackupVersion.authData = keyBackupCreationInfo.authData.JSONDictionary;\n+\n+        MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient createKeyBackupVersion:keyBackupVersion success:^(NSString *version) {\n+\n+            keyBackupVersion.version = version;\n+\n+            NSError *error = [self enableKeyBackup:keyBackupVersion];\n+\n+            dispatch_async(dispatch_get_main_queue(), ^{\n+                if (!error)\n+                {\n+                    success(keyBackupVersion);\n+                }\n+                else if (failure)\n+                {\n+                    failure(error);\n+                }\n+            });\n+\n+        } failure:^(NSError *error) {\n+            if (failure) {\n+                dispatch_async(dispatch_get_main_queue(), ^{\n+                    failure(error);\n+                });\n+            }\n+        }];\n+\n+        if (operation2)\n+        {\n+            [operation mutateTo:operation2];\n+        }\n+    });\n+\n+    return operation;\n+}\n+\n+- (MXHTTPOperation*)deleteKeyBackupVersion:(NSString*)version\n+                                   success:(void (^)(void))success\n+                                   failure:(nullable void (^)(NSError *error))failure\n+{\n+    MXHTTPOperation *operation = [MXHTTPOperation new];\n+\n+    MXWeakify(self);\n+    dispatch_async(mxSession.crypto.cryptoQueue, ^{\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        // If we're currently backing up to this backup... stop.\n+        // (We start using it automatically in createKeyBackupVersion\n+        // so this is symmetrical).\n+        if ([self.keyBackupVersion.version isEqualToString:version])\n+        {\n+            [self disableKeyBackup];\n+        }\n+\n+        MXHTTPOperation *operation2 = [self->mxSession.crypto.matrixRestClient deleteKeysFromBackup:version success:^{\n+\n+            dispatch_async(dispatch_get_main_queue(), ^{\n+                success();\n+            });\n+\n+        } failure:^(NSError *error) {\n+            if (failure) {\n+                dispatch_async(dispatch_get_main_queue(), ^{\n+                    failure(error);\n+                });\n+            }\n+        }];\n+\n+        if (operation2)\n+        {\n+            [operation mutateTo:operation2];\n+        }\n+    });\n+\n+    return operation;\n+}\n+\n+\n+#pragma mark - Backup storing\n+\n+- (void)backupAllGroupSessions:(nullable void (^)(void))success\n+                      progress:(nullable void (^)(NSProgress *backupProgress))progress\n+                       failure:(nullable void (^)(NSError *error))failure;\n+{\n+    // Get a status right now\n+    MXWeakify(self);\n+    [self backupProgress:^(NSProgress * _Nonnull backupProgress) {\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        // Reset previous state if any\n+        [self resetBackupAllGroupSessionsObjects];\n+\n+        NSLog(@\"[MXKeyBackup] backupAllGroupSessions: backupProgress: %@\", backupProgress);\n+\n+        if (progress)\n+        {\n+            progress(backupProgress);\n+        }\n+\n+        if (backupProgress.finished)\n+        {\n+            NSLog(@\"[MXKeyBackup] backupAllGroupSessions: complete\");\n+            if (success)\n+            {\n+                success();\n+            }\n+            return;\n+        }\n+\n+        // Listen to `self.state` change to determine when to call onBackupProgress and onComplete\n+        MXWeakify(self);\n+        self->backupAllGroupSessionsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKeyBackupDidStateChangeNotification object:self queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {\n+            MXStrongifyAndReturnIfNil(self);\n+\n+            [self backupProgress:^(NSProgress * _Nonnull backupProgress) {\n+\n+                if (progress)\n+                {\n+                    progress(backupProgress);\n+                }\n+\n+                if (self.state == MXKeyBackupStateReadyToBackUp)\n+                {\n+                    [self resetBackupAllGroupSessionsObjects];\n+\n+                    if (success)\n+                    {\n+                        success();\n+                    }\n+                }\n+            }];\n+        }];\n+\n+        dispatch_async(self->mxSession.crypto.cryptoQueue, ^{\n+            MXStrongifyAndReturnIfNil(self);\n+\n+            // Listen to error\n+            if (failure)\n+            {\n+                MXWeakify(self);\n+                self->backupAllGroupSessionsFailure = ^(NSError *error) {\n+                    MXStrongifyAndReturnIfNil(self);\n+\n+                    failure(error);\n+                    [self resetBackupAllGroupSessionsObjects];\n+                };\n+            }\n+\n+            [self sendKeyBackup];\n+        });\n+    }];\n+}\n+\n+- (void)resetBackupAllGroupSessionsObjects\n+{\n+    if (backupAllGroupSessionsObserver)\n+    {\n+        [[NSNotificationCenter defaultCenter] removeObserver:backupAllGroupSessionsObserver];\n+        backupAllGroupSessionsObserver = nil;\n+    }\n+    backupAllGroupSessionsFailure = nil;\n+}\n+\n+- (void)backupProgress:(void (^)(NSProgress *backupProgress))backupProgress\n+{\n+    MXWeakify(self);\n+    dispatch_async(mxSession.crypto.cryptoQueue, ^{\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        NSUInteger keys = [self->mxSession.crypto.store inboundGroupSessionsCount:NO];\n+        NSUInteger backedUpkeys = [self->mxSession.crypto.store inboundGroupSessionsCount:YES];\n+\n+        NSProgress *progress = [NSProgress progressWithTotalUnitCount:keys];\n+        progress.completedUnitCount = backedUpkeys;\n+\n+        dispatch_async(dispatch_get_main_queue(), ^{\n+            backupProgress(progress);\n+        });\n+     });\n+}\n+\n+\n+#pragma mark - Backup restoring\n+\n++ (BOOL)isValidRecoveryKey:(NSString*)recoveryKey\n+{\n+    NSError *error;\n+    NSData *privateKeyOut = [MXRecoveryKey decode:recoveryKey error:&error];\n+\n+    return !error && privateKeyOut;\n+}\n+\n+- (MXHTTPOperation*)restoreKeyBackup:(NSString*)version\n+                         recoveryKey:(NSString*)recoveryKey\n+                                room:(nullable NSString*)roomId\n+                             session:(nullable NSString*)sessionId\n+                             success:(nullable void (^)(NSUInteger total, NSUInteger imported))success\n+                             failure:(nullable void (^)(NSError *error))failure\n+{\n+    MXHTTPOperation *operation = [MXHTTPOperation new];\n+\n+    NSLog(@\"[MXKeyBackup] restoreKeyBackup: From backup version: %@\", version);\n+\n+    MXWeakify(self);\n+    dispatch_async(mxSession.crypto.cryptoQueue, ^{\n+        MXStrongifyAndReturnIfNil(self);\n+\n+        // Get a PK decryption instance\n+        NSError *error;\n+        OLMPkDecryption *decryption = [self pkDecryptionFromRecoveryKey:recoveryKey error:&error];\n+        if (error)\n+        {\n+            NSLog(@\"[MXKeyBackup] restoreKeyBackup: Invalid recovery key. Error: %@\", error);\n+            if (failure)\n+            {\n+                dispatch_async(dispatch_get_main_queue(), ^{\n+                    failure(error);\n+                });\n+            }\n+            return;\n+        }\n+\n+        // Get backup from the homeserver\n+        MXWeakify(self);\n+        MXHTTPOperation *operation2 = [self keyBackupForSession:sessionId inRoom:roomId version:version success:^(MXKeysBackupData *keysBackupData) {\n+            MXStrongifyAndReturnIfNil(self);\n+\n+            NSMutableArray<MXMegolmSessionData*> *sessionDatas = [NSMutableArray array];\n+\n+            // Restore that data\n+            for (NSString *roomId in keysBackupData.rooms)\n+            {\n+                for (NSString *sessionId in keysBackupData.rooms[roomId].sessions)\n+                {\n+                    MXKeyBackupData *keyBackupData = keysBackupData.rooms[roomId].sessions[sessionId];\n+\n+                    MXMegolmSessionData *sessionData = [self decryptKeyBackupData:keyBackupData forSession:sessionId inRoom:roomId withPkDecryption:decryption];\n+\n+                    [sessionDatas addObject:sessionData];\n+                }\n+            }\n+\n+            NSLog(@\"[MXKeyBackup] restoreKeyBackup: Got %@ keys from the backup store on the homeserver\", @(sessionDatas.count));\n+\n+            // Do not trigger a backup for them if they come from the backup version we are using\n+            BOOL backUp = ![version isEqualToString:self.keyBackupVersion.version];\n+            if (backUp)\n+            {\n+                NSLog(@\"[MXKeyBackup] restoreKeyBackup: Those keys will be backed up to backup version: %@\", self.keyBackupVersion.version);\n+            }\n+\n+            // Import them into the crypto store\n+            [self->mxSession.crypto importMegolmSessionDatas:sessionDatas backUp:backUp success:success failure:^(NSError *error) {\n+                if (failure)\n+                {\n+                    dispatch_async(dispatch_get_main_queue(), ^{\n+                        failure(error);\n+                    });\n+                }\n+            }];\n+\n+        } failure:^(NSError *error) {\n+            if (failure)\n+            {\n+                dispatch_async(dispatch_get_main_queue(), ^{\n+                    failure(error);\n+                });\n+            }\n+        }];\n+\n+        if (operation2)\n+        {\n+            [operation mutateTo:operation2];\n+        }\n+    });\n+\n+    return operation;\n+}\n+\n+\n+#pragma mark - Backup state\n+\n+- (BOOL)enabled\n+{\n+    return _state != MXKeyBackupStateDisabled;\n+}\n+\n+\n+#pragma mark - Private methods -\n+\n+- (void)setState:(MXKeyBackupState)state\n+{\n+    _state = state;\n+\n+    dispatch_async(dispatch_get_main_queue(), ^{\n+        [[NSNotificationCenter defaultCenter] postNotificationName:kMXKeyBackupDidStateChangeNotification object:self];\n+    });\n+}\n+\n+// Same method as [MXRestClient keysBackupInRoom] except that it accepts nullable\n+// parameters and always returns a MXKeysBackupData object\n+- (MXHTTPOperation*)keyBackupForSession:(nullable NSString*)sessionId\n+                                 inRoom:(nullable NSString*)roomId\n+                                version:(NSString*)version\n+                                success:(void (^)(MXKeysBackupData *keysBackupData))success\n+                                failure:(void (^)(NSError *error))failure;\n+{\n+    MXHTTPOperation *operation;\n+\n+    if (!sessionId && !roomId)\n+    {\n+        operation = [mxSession.crypto.matrixRestClient keysBackup:version success:success failure:failure];\n+    }\n+    else if (!sessionId)\n+    {\n+        operation = [mxSession.crypto.matrixRestClient keysBackupInRoom:roomId version:version success:^(MXRoomKeysBackupData *roomKeysBackupData) {\n+\n+            MXKeysBackupData *keysBackupData = [MXKeysBackupData new];\n+            keysBackupData.rooms = @{\n+                                     roomId: roomKeysBackupData\n+                                     };\n+\n+            success(keysBackupData);\n+\n+        } failure:failure];\n+    }\n+    else\n+    {\n+        operation =  [mxSession.crypto.matrixRestClient keyBackupForSession:sessionId inRoom:roomId version:version success:^(MXKeyBackupData *keyBackupData) {\n+\n+            MXRoomKeysBackupData *roomKeysBackupData = [MXRoomKeysBackupData new];\n+            roomKeysBackupData.sessions = @{\n+                                            sessionId: keyBackupData\n+                                            };\n+\n+            MXKeysBackupData *keysBackupData = [MXKeysBackupData new];\n+            keysBackupData.rooms = @{\n+                                     roomId: roomKeysBackupData\n+                                     };\n+\n+            success(keysBackupData);\n+\n+        } failure:failure];\n+    }\n+\n+    return operation;\n+}\n+\n+- (OLMPkDecryption*)pkDecryptionFromRecoveryKey:(NSString*)recoveryKey error:(NSError **)error\n+{\n+    // Extract the primary key\n+    NSData *privateKey = [MXRecoveryKey decode:recoveryKey error:error];\n+\n+    // Built the PK decryption with it\n+    OLMPkDecryption *decryption;\n+    if (privateKey)\n+    {\n+        decryption = [OLMPkDecryption new];\n+        [decryption setPrivateKey:privateKey error:error];\n+    }\n+\n+    return decryption;\n+}\n+\n+- (MXKeyBackupData*)encryptGroupSession:(MXOlmInboundGroupSession*)session withPkEncryption:(OLMPkEncryption*)encryption","path":"MatrixSDK/Crypto/KeyBackup/MXKeyBackup.m","position":871,"original_position":662,"commit_id":"516919ed39ca676bc24f225b65b5d8c096dbdb26","original_commit_id":"9f693e6dc841dbb3ef0c422a09e8e87eedf01440","user":{"login":"manuroe","id":8418515,"node_id":"MDQ6VXNlcjg0MTg1MTU=","avatar_url":"https://avatars0.githubusercontent.com/u/8418515?v=4","gravatar_id":"","url":"https://api.github.com/users/manuroe","html_url":"https://github.com/manuroe","followers_url":"https://api.github.com/users/manuroe/followers","following_url":"https://api.github.com/users/manuroe/following{/other_user}","gists_url":"https://api.github.com/users/manuroe/gists{/gist_id}","starred_url":"https://api.github.com/users/manuroe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/manuroe/subscriptions","organizations_url":"https://api.github.com/users/manuroe/orgs","repos_url":"https://api.github.com/users/manuroe/repos","events_url":"https://api.github.com/users/manuroe/events{/privacy}","received_events_url":"https://api.github.com/users/manuroe/received_events","type":"User","site_admin":false},"body":"Fixed within the method body.","created_at":"2018-11-13T15:35:01Z","updated_at":"2018-11-13T15:35:02Z","html_url":"https://github.com/matrix-org/matrix-ios-sdk/pull/591#discussion_r233088448","pull_request_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/comments/233088448"},"html":{"href":"https://github.com/matrix-org/matrix-ios-sdk/pull/591#discussion_r233088448"},"pull_request":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591"}},"in_reply_to_id":232261175},"pull_request":{"url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591","id":227342415,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI3MzQyNDE1","html_url":"https://github.com/matrix-org/matrix-ios-sdk/pull/591","diff_url":"https://github.com/matrix-org/matrix-ios-sdk/pull/591.diff","patch_url":"https://github.com/matrix-org/matrix-ios-sdk/pull/591.patch","issue_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/591","number":591,"state":"open","locked":false,"title":"E2e keys backup","user":{"login":"manuroe","id":8418515,"node_id":"MDQ6VXNlcjg0MTg1MTU=","avatar_url":"https://avatars0.githubusercontent.com/u/8418515?v=4","gravatar_id":"","url":"https://api.github.com/users/manuroe","html_url":"https://github.com/manuroe","followers_url":"https://api.github.com/users/manuroe/followers","following_url":"https://api.github.com/users/manuroe/following{/other_user}","gists_url":"https://api.github.com/users/manuroe/gists{/gist_id}","starred_url":"https://api.github.com/users/manuroe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/manuroe/subscriptions","organizations_url":"https://api.github.com/users/manuroe/orgs","repos_url":"https://api.github.com/users/manuroe/repos","events_url":"https://api.github.com/users/manuroe/events{/privacy}","received_events_url":"https://api.github.com/users/manuroe/received_events","type":"User","site_admin":false},"body":"https://github.com/vector-im/riot-ios/issues/2070","created_at":"2018-10-31T15:53:18Z","updated_at":"2018-11-13T15:35:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"82c0b88bf3ef5d64138145ae464e9f5273f74f88","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591/commits","review_comments_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591/comments","review_comment_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/comments{/number}","comments_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/591/comments","statuses_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/statuses/516919ed39ca676bc24f225b65b5d8c096dbdb26","head":{"label":"matrix-org:e2e_keys_backup","ref":"e2e_keys_backup","sha":"516919ed39ca676bc24f225b65b5d8c096dbdb26","user":{"login":"matrix-org","id":8418310,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg0MTgzMTA=","avatar_url":"https://avatars2.githubusercontent.com/u/8418310?v=4","gravatar_id":"","url":"https://api.github.com/users/matrix-org","html_url":"https://github.com/matrix-org","followers_url":"https://api.github.com/users/matrix-org/followers","following_url":"https://api.github.com/users/matrix-org/following{/other_user}","gists_url":"https://api.github.com/users/matrix-org/gists{/gist_id}","starred_url":"https://api.github.com/users/matrix-org/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matrix-org/subscriptions","organizations_url":"https://api.github.com/users/matrix-org/orgs","repos_url":"https://api.github.com/users/matrix-org/repos","events_url":"https://api.github.com/users/matrix-org/events{/privacy}","received_events_url":"https://api.github.com/users/matrix-org/received_events","type":"Organization","site_admin":false},"repo":{"id":24633503,"node_id":"MDEwOlJlcG9zaXRvcnkyNDYzMzUwMw==","name":"matrix-ios-sdk","full_name":"matrix-org/matrix-ios-sdk","private":false,"owner":{"login":"matrix-org","id":8418310,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg0MTgzMTA=","avatar_url":"https://avatars2.githubusercontent.com/u/8418310?v=4","gravatar_id":"","url":"https://api.github.com/users/matrix-org","html_url":"https://github.com/matrix-org","followers_url":"https://api.github.com/users/matrix-org/followers","following_url":"https://api.github.com/users/matrix-org/following{/other_user}","gists_url":"https://api.github.com/users/matrix-org/gists{/gist_id}","starred_url":"https://api.github.com/users/matrix-org/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matrix-org/subscriptions","organizations_url":"https://api.github.com/users/matrix-org/orgs","repos_url":"https://api.github.com/users/matrix-org/repos","events_url":"https://api.github.com/users/matrix-org/events{/privacy}","received_events_url":"https://api.github.com/users/matrix-org/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/matrix-org/matrix-ios-sdk","description":"The Matrix SDK for iOS","fork":false,"url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk","forks_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/forks","keys_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/teams","hooks_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/hooks","issue_events_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/events{/number}","events_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/events","assignees_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/assignees{/user}","branches_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/branches{/branch}","tags_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/tags","blobs_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/refs{/sha}","trees_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/statuses/{sha}","languages_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/languages","stargazers_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/stargazers","contributors_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/contributors","subscribers_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/subscribers","subscription_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/subscription","commits_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/commits{/sha}","git_commits_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/commits{/sha}","comments_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/comments{/number}","issue_comment_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/comments{/number}","contents_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/contents/{+path}","compare_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/merges","archive_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/downloads","issues_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues{/number}","pulls_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls{/number}","milestones_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/milestones{/number}","notifications_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/labels{/name}","releases_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/releases{/id}","deployments_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/deployments","created_at":"2014-09-30T10:06:39Z","updated_at":"2018-11-12T08:58:18Z","pushed_at":"2018-11-13T15:33:49Z","git_url":"git://github.com/matrix-org/matrix-ios-sdk.git","ssh_url":"git@github.com:matrix-org/matrix-ios-sdk.git","clone_url":"https://github.com/matrix-org/matrix-ios-sdk.git","svn_url":"https://github.com/matrix-org/matrix-ios-sdk","homepage":null,"size":9776,"stargazers_count":90,"watchers_count":90,"language":"Objective-C","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":41,"mirror_url":null,"archived":false,"open_issues_count":36,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":41,"open_issues":36,"watchers":90,"default_branch":"develop"}},"base":{"label":"matrix-org:develop","ref":"develop","sha":"f2806f7176dfefc22470c47763f6c48b4440dd87","user":{"login":"matrix-org","id":8418310,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg0MTgzMTA=","avatar_url":"https://avatars2.githubusercontent.com/u/8418310?v=4","gravatar_id":"","url":"https://api.github.com/users/matrix-org","html_url":"https://github.com/matrix-org","followers_url":"https://api.github.com/users/matrix-org/followers","following_url":"https://api.github.com/users/matrix-org/following{/other_user}","gists_url":"https://api.github.com/users/matrix-org/gists{/gist_id}","starred_url":"https://api.github.com/users/matrix-org/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matrix-org/subscriptions","organizations_url":"https://api.github.com/users/matrix-org/orgs","repos_url":"https://api.github.com/users/matrix-org/repos","events_url":"https://api.github.com/users/matrix-org/events{/privacy}","received_events_url":"https://api.github.com/users/matrix-org/received_events","type":"Organization","site_admin":false},"repo":{"id":24633503,"node_id":"MDEwOlJlcG9zaXRvcnkyNDYzMzUwMw==","name":"matrix-ios-sdk","full_name":"matrix-org/matrix-ios-sdk","private":false,"owner":{"login":"matrix-org","id":8418310,"node_id":"MDEyOk9yZ2FuaXphdGlvbjg0MTgzMTA=","avatar_url":"https://avatars2.githubusercontent.com/u/8418310?v=4","gravatar_id":"","url":"https://api.github.com/users/matrix-org","html_url":"https://github.com/matrix-org","followers_url":"https://api.github.com/users/matrix-org/followers","following_url":"https://api.github.com/users/matrix-org/following{/other_user}","gists_url":"https://api.github.com/users/matrix-org/gists{/gist_id}","starred_url":"https://api.github.com/users/matrix-org/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/matrix-org/subscriptions","organizations_url":"https://api.github.com/users/matrix-org/orgs","repos_url":"https://api.github.com/users/matrix-org/repos","events_url":"https://api.github.com/users/matrix-org/events{/privacy}","received_events_url":"https://api.github.com/users/matrix-org/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/matrix-org/matrix-ios-sdk","description":"The Matrix SDK for iOS","fork":false,"url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk","forks_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/forks","keys_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/teams","hooks_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/hooks","issue_events_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/events{/number}","events_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/events","assignees_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/assignees{/user}","branches_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/branches{/branch}","tags_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/tags","blobs_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/refs{/sha}","trees_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/statuses/{sha}","languages_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/languages","stargazers_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/stargazers","contributors_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/contributors","subscribers_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/subscribers","subscription_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/subscription","commits_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/commits{/sha}","git_commits_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/git/commits{/sha}","comments_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/comments{/number}","issue_comment_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/comments{/number}","contents_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/contents/{+path}","compare_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/merges","archive_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/downloads","issues_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues{/number}","pulls_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls{/number}","milestones_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/milestones{/number}","notifications_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/labels{/name}","releases_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/releases{/id}","deployments_url":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/deployments","created_at":"2014-09-30T10:06:39Z","updated_at":"2018-11-12T08:58:18Z","pushed_at":"2018-11-13T15:33:49Z","git_url":"git://github.com/matrix-org/matrix-ios-sdk.git","ssh_url":"git@github.com:matrix-org/matrix-ios-sdk.git","clone_url":"https://github.com/matrix-org/matrix-ios-sdk.git","svn_url":"https://github.com/matrix-org/matrix-ios-sdk","homepage":null,"size":9776,"stargazers_count":90,"watchers_count":90,"language":"Objective-C","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":41,"mirror_url":null,"archived":false,"open_issues_count":36,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":41,"open_issues":36,"watchers":90,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591"},"html":{"href":"https://github.com/matrix-org/matrix-ios-sdk/pull/591"},"issue":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/591"},"comments":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/issues/591/comments"},"review_comments":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591/comments"},"review_comment":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/pulls/591/commits"},"statuses":{"href":"https://api.github.com/repos/matrix-org/matrix-ios-sdk/statuses/516919ed39ca676bc24f225b65b5d8c096dbdb26"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"manuroe"}}