Skip to content

Commit

Permalink
MXRecoveryService: Add options to create and delete key backup automa…
Browse files Browse the repository at this point in the history
  • Loading branch information
manuroe committed Jun 30, 2020
1 parent d059cba commit d665dce
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Improvements:
* MXCrossSigning: Add the bootstrapWithAuthParams method.
* MXRecoveryService: Create this service to manage keys we want to store in SSSS.
* MXRecoveryService: Add deleteRecovery.
* MXRecoveryService: Add options to create and delete key backup automatically (vector-im/riot-ios/issues/3361).
* MXSecretStorage: Add options to remove secrets and SSSS.
* MXWellKnown: Add JSONDictionary implementation to return original and extended data.
* MXCrossSigning: Gossip the master key (vector-im/riot-ios/issues/3346).
Expand Down
10 changes: 8 additions & 2 deletions MatrixSDK/Crypto/Recovery/MXRecoveryService.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ FOUNDATION_EXPORT NSString *const MXRecoveryServiceErrorDomain;
typedef NS_ENUM(NSInteger, MXRecoveryServiceErrorCode)
{
MXRecoveryServiceSSSSAlreadyExistsErrorCode,
MXRecoveryServiceKeyBackupExistsButNoPrivateKeyErrorCode,
MXRecoveryServiceNoSSSSErrorCode,
MXRecoveryServiceNotProtectedByPassphraseErrorCode,
MXRecoveryServiceBadRecoveryKeyErrorCode,
Expand Down Expand Up @@ -76,11 +77,14 @@ typedef NS_ENUM(NSInteger, MXRecoveryServiceErrorCode)
/**
Delete the current recovery.
@param deleteServicesBackups YES to delete backups for associated services. Only keyBackup is supported.
@param success A block object called when the operation succeeds.
@param failure A block object called when the operation fails.
*/
- (void)deleteRecoveryWithSuccess:(void (^)(void))success
failure:(void (^)(NSError *error))failure;
- (void)deleteRecoveryWithDeleteServicesBackups:(BOOL)deleteServicesBackups
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure;


#pragma mark - Secrets in the recovery
Expand All @@ -106,12 +110,14 @@ typedef NS_ENUM(NSInteger, MXRecoveryServiceErrorCode)
@param secrets secrets ids to store in the recovery. Nil for all self.supportedSecrets.
@param passphrase a passphrase used to generate the recovery key to encrypt keys. Nil will generate it.
@param createServicesBackups YES to create backups for associated services. Only keyBackup is supported.
@param success A block object called when the operation succeeds.
@param failure A block object called when the operation fails.
*/
- (void)createRecoveryForSecrets:(nullable NSArray<NSString*>*)secrets
withPassphrase:(nullable NSString*)passphrase
createServicesBackups:(BOOL)createServicesBackups
success:(void (^)(MXSecretStorageKeyCreationInfo *keyCreationInfo))success
failure:(void (^)(NSError *error))failure;

Expand Down
115 changes: 108 additions & 7 deletions MatrixSDK/Crypto/Recovery/MXRecoveryService.m
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,26 @@ - (BOOL)usePassphrase
return (keyContent.passphrase != nil);
}

- (void)deleteRecoveryWithSuccess:(void (^)(void))success failure:(void (^)(NSError * _Nonnull))failure
- (void)deleteRecoveryWithDeleteServicesBackups:(BOOL)deleteServicesBackups
success:(void (^)(void))success
failure:(void (^)(NSError *error))failure
{
NSLog(@"[MXRecoveryService] deleteRecovery");
NSLog(@"[MXRecoveryService] deleteRecovery: deleteServicesBackups: %@", @(deleteServicesBackups));

if (deleteServicesBackups)
{
[self deleteKeyBackupWithSuccess:^{
[self deleteRecoveryWithSuccess:success failure:failure];
} failure:failure];
}
else
{
[self deleteRecoveryWithSuccess:success failure:failure];
}
}

- (void)deleteRecoveryWithSuccess:(void (^)(void))success failure:(void (^)(NSError * _Nonnull))failure
{
dispatch_group_t dispatchGroup = dispatch_group_create();
__block NSError *error;

Expand Down Expand Up @@ -143,6 +159,27 @@ - (void)deleteRecoveryWithSuccess:(void (^)(void))success failure:(void (^)(NSEr
});
}

- (void)deleteKeyBackupWithSuccess:(void (^)(void))success
failure:(void (^)(NSError *error))failure
{
NSLog(@"[MXRecoveryService] deleteKeyBackup");

MXKeyBackup *keyBackup = self.crypto.backup;

[keyBackup forceRefresh:^(BOOL usingLastVersion) {

if (keyBackup.keyBackupVersion)
{
[keyBackup deleteKeyBackupVersion:keyBackup.keyBackupVersion.version success:success failure:failure];
}
else
{
success();
}

} failure:failure];
}


#pragma mark - Secrets in the recovery

Expand Down Expand Up @@ -192,15 +229,16 @@ - (NSArray*)secretsStoredLocally

- (void)createRecoveryForSecrets:(nullable NSArray<NSString*>*)secrets
withPassphrase:(nullable NSString*)passphrase
createServicesBackups:(BOOL)createServicesBackups
success:(void (^)(MXSecretStorageKeyCreationInfo *keyCreationInfo))success
failure:(void (^)(NSError *error))failure
{
NSLog(@"[MXRecoveryService] createRecovery: secrets: %@", secrets);
NSLog(@"[MXRecoveryService] createRecovery: secrets: %@. createServicesBackups: %@", secrets, @(createServicesBackups));

if (self.hasRecovery)
{
NSLog(@"[MXRecoveryService] createRecovery: Error: A recovery already exists.");
NSError *error = [NSError errorWithDomain:MXCrossSigningErrorDomain
NSError *error = [NSError errorWithDomain:MXRecoveryServiceErrorDomain
code:MXRecoveryServiceSSSSAlreadyExistsErrorCode
userInfo:@{
NSLocalizedDescriptionKey: @"MXRecoveryService: A secret storage already exists",
Expand All @@ -209,6 +247,24 @@ - (void)createRecoveryForSecrets:(nullable NSArray<NSString*>*)secrets
return;
}

if (createServicesBackups
&& (!secrets || [secrets containsObject:MXSecretId.keyBackup]))
{
[self createKeyBackupWithSuccess:^{
[self createRecoveryForSecrets:secrets withPassphrase:passphrase success:success failure:failure];
} failure:failure];
}
else
{
[self createRecoveryForSecrets:secrets withPassphrase:passphrase success:success failure:failure];
}
}

- (void)createRecoveryForSecrets:(nullable NSArray<NSString*>*)secrets
withPassphrase:(nullable NSString*)passphrase
success:(void (^)(MXSecretStorageKeyCreationInfo *keyCreationInfo))success
failure:(void (^)(NSError *error))failure
{
MXWeakify(self);
[_secretStorage createKeyWithKeyId:nil keyName:nil passphrase:passphrase success:^(MXSecretStorageKeyCreationInfo * _Nonnull keyCreationInfo) {
MXStrongifyAndReturnIfNil(self);
Expand All @@ -228,6 +284,51 @@ - (void)createRecoveryForSecrets:(nullable NSArray<NSString*>*)secrets
}];
}

- (void)createKeyBackupWithSuccess:(void (^)(void))success
failure:(void (^)(NSError *error))failure
{
NSLog(@"[MXRecoveryService] createKeyBackup");

MXKeyBackup *keyBackup = self.crypto.backup;

[keyBackup forceRefresh:^(BOOL usingLastVersion) {

// If there is a
if (keyBackup.enabled)
{
if ([self.cryptoStore secretWithSecretId:MXSecretId.keyBackup])
{
NSLog(@"[MXRecoveryService] createKeyBackup: Reuse private key of existing one");
success();
}
else
{
NSLog(@"[MXRecoveryService] createKeyBackup: Error: A key backup already exists");
NSError *error = [NSError errorWithDomain:MXRecoveryServiceErrorDomain
code:MXRecoveryServiceKeyBackupExistsButNoPrivateKeyErrorCode
userInfo:@{
NSLocalizedDescriptionKey: @"MXRecoveryService: A key backup already exists but the private key is unknown",
}];
failure(error);
}
return;
}

// Setup the key backup
[keyBackup prepareKeyBackupVersionWithPassword:nil success:^(MXMegolmBackupCreationInfo * _Nonnull keyBackupCreationInfo) {
[keyBackup createKeyBackupVersion:keyBackupCreationInfo success:^(MXKeyBackupVersion * _Nonnull keyBackupVersion) {
[keyBackup backupAllGroupSessions:^{

// The private key is stored as MXSecretId.keyBackup
success();

} progress:nil failure:failure];
} failure:failure];
} failure:failure];

} failure:failure];
}

- (void)updateRecoveryForSecrets:(nullable NSArray<NSString*>*)secrets
withPrivateKey:(NSData*)privateKey
success:(void (^)(void))success
Expand All @@ -240,7 +341,7 @@ - (void)updateRecoveryForSecrets:(nullable NSArray<NSString*>*)secrets
{
// No recovery
NSLog(@"[MXRecoveryService] updateRecovery: Error: No existing SSSS");
NSError *error = [NSError errorWithDomain:MXCrossSigningErrorDomain
NSError *error = [NSError errorWithDomain:MXRecoveryServiceErrorDomain
code:MXRecoveryServiceNoSSSSErrorCode
userInfo:@{
NSLocalizedDescriptionKey: @"MXRecoveryService: The account has no secret storage",
Expand Down Expand Up @@ -593,7 +694,7 @@ - (void)privateKeyFromPassphrase:(NSString*)passphrase
if (!recoveryId)
{
// No SSSS
NSError *error = [NSError errorWithDomain:MXCrossSigningErrorDomain
NSError *error = [NSError errorWithDomain:MXRecoveryServiceErrorDomain
code:MXRecoveryServiceNoSSSSErrorCode
userInfo:@{
NSLocalizedDescriptionKey: @"MXRecoveryService: The account has no secret storage",
Expand All @@ -606,7 +707,7 @@ - (void)privateKeyFromPassphrase:(NSString*)passphrase
if (!keyContent.passphrase)
{
// No passphrase
NSError *error = [NSError errorWithDomain:MXCrossSigningErrorDomain
NSError *error = [NSError errorWithDomain:MXRecoveryServiceErrorDomain
code:MXRecoveryServiceNotProtectedByPassphraseErrorCode
userInfo:@{
NSLocalizedDescriptionKey: @"MXRecoveryService: Secret storage not protected by a passphrase",
Expand Down
Loading

0 comments on commit d665dce

Please sign in to comment.