Skip to content

Commit

Permalink
Merge pull request #1608 from matrix-org/andy/final_backups
Browse files Browse the repository at this point in the history
Manual key export / import with Crypto V2
  • Loading branch information
Anderas authored Oct 24, 2022
2 parents 3f8efad + a8e2a76 commit 5960e8b
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 173 deletions.
26 changes: 26 additions & 0 deletions MatrixSDK/Crypto/CryptoMachine/MXCryptoMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class MXCryptoMachine {
}

private static let storeFolder = "MXCryptoStore"
private static let kdfRounds: Int32 = 500_000

enum Error: Swift.Error {
case invalidStorage
Expand All @@ -51,6 +52,8 @@ class MXCryptoMachine {
case missingEmojis
case missingDecimals
case cannotCancelVerification
case cannotExportKeys
case cannotImportKeys
}

private let machine: OlmMachine
Expand Down Expand Up @@ -479,6 +482,14 @@ extension MXCryptoMachine: MXCryptoCrossSigning {
func exportCrossSigningKeys() -> CrossSigningKeyExport? {
machine.exportCrossSigningKeys()
}

func importCrossSigningKeys(export: CrossSigningKeyExport) {
do {
try machine.importCrossSigningKeys(export: export)
} catch {
log.error("Failed importing cross signing keys", context: error)
}
}
}

extension MXCryptoMachine: MXCryptoVerificationRequesting {
Expand Down Expand Up @@ -740,6 +751,21 @@ extension MXCryptoMachine: MXCryptoBackup {
}
return try machine.importDecryptedRoomKeys(keys: json, progressListener: progressListener)
}

func exportRoomKeys(passphrase: String) throws -> Data {
let string = try machine.exportRoomKeys(passphrase: passphrase, rounds: Self.kdfRounds)
guard let data = string.data(using: .utf8) else {
throw Error.cannotExportKeys
}
return data
}

func importRoomKeys(_ data: Data, passphrase: String, progressListener: ProgressListener) throws -> KeysImportResult {
guard let string = String(data: data, encoding: .utf8) else {
throw Error.cannotImportKeys
}
return try machine.importRoomKeys(keys: string, passphrase: passphrase, progressListener: progressListener)
}
}

extension MXCryptoMachine: Logger {
Expand Down
4 changes: 4 additions & 0 deletions MatrixSDK/Crypto/CryptoMachine/MXCryptoProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ protocol MXCryptoCrossSigning: MXCryptoUserIdentitySource {
func crossSigningStatus() -> CrossSigningStatus
func bootstrapCrossSigning(authParams: [AnyHashable: Any]) async throws
func exportCrossSigningKeys() -> CrossSigningKeyExport?
func importCrossSigningKeys(export: CrossSigningKeyExport)
}

/// Lifecycle of verification request
Expand Down Expand Up @@ -122,6 +123,9 @@ protocol MXCryptoBackup {

func backupRoomKeys() async throws
func importDecryptedKeys(roomKeys: [MXMegolmSessionData], progressListener: ProgressListener) throws -> KeysImportResult

func exportRoomKeys(passphrase: String) throws -> Data
func importRoomKeys(_ data: Data, passphrase: String, progressListener: ProgressListener) throws -> KeysImportResult
}

#endif
12 changes: 11 additions & 1 deletion MatrixSDK/Crypto/KeyBackup/Engine/MXCryptoKeyBackupEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@ class MXCryptoKeyBackupEngine: NSObject, MXKeyBackupEngine {
return data
}

// MARK: - Manual export / import

func exportRoomKeys(passphrase: String) throws -> Data {
return try backup.exportRoomKeys(passphrase: passphrase)
}

func importRoomKeys(_ data: Data, passphrase: String) throws -> KeysImportResult {
return try backup.importRoomKeys(data, passphrase: passphrase, progressListener: self)
}

// MARK: - Private

func publicKey(for keyBackupVersion: MXKeyBackupVersion) -> String? {
Expand All @@ -350,7 +360,7 @@ class MXCryptoKeyBackupEngine: NSObject, MXKeyBackupEngine {

extension MXCryptoKeyBackupEngine: ProgressListener {
func onProgress(progress: Int32, total: Int32) {
log.debug("Backup progress \(progress) of \(total) total")
log.debug("Backup / export progress \(progress) of \(total) total")
}
}

Expand Down
22 changes: 0 additions & 22 deletions MatrixSDK/Crypto/MXCrypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,17 +451,6 @@ extern NSString *const MXDeviceListDidUpdateUsersDevicesNotification;

#pragma mark - import/export

/**
Get a list containing all of the room keys.
This should be encrypted before returning it to the user.
@param success A block object called when the operation succeeds with the list of session export objects.
@param failure A block object called when the operation fails.
*/
- (void)exportRoomKeys:(void (^)(NSArray<NSDictionary*> *keys))success
failure:(void (^)(NSError *error))failure;

/**
Get all room keys under an encrypted form.
Expand All @@ -473,17 +462,6 @@ extern NSString *const MXDeviceListDidUpdateUsersDevicesNotification;
success:(void (^)(NSData *keyFile))success
failure:(void (^)(NSError *error))failure;

/**
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<NSDictionary*>*)keys
success:(void (^)(NSUInteger total, NSUInteger imported))success
failure:(void (^)(NSError *error))failure;

/**
Import an encrypted room keys file.
Expand Down
35 changes: 0 additions & 35 deletions MatrixSDK/Crypto/MXCrypto.m
Original file line number Diff line number Diff line change
Expand Up @@ -1500,41 +1500,6 @@ - (void)scheduleRequestsForAllPrivateKeys

#pragma mark - import/export

- (void)exportRoomKeys:(void (^)(NSArray<NSDictionary *> *))success failure:(void (^)(NSError *))failure
{
#ifdef MX_CRYPTO
MXWeakify(self);
dispatch_async(cargoQueue, ^{
MXStrongifyAndReturnIfNil(self);

NSDate *startDate = [NSDate date];

NSMutableArray *keys = [NSMutableArray array];

for (MXOlmInboundGroupSession *session in [self.store inboundGroupSessions])
{
MXMegolmSessionData *sessionData = [session exportSessionData];
if (sessionData)
{
[keys addObject:sessionData.JSONDictionary];
}
}

MXLogDebug(@"[MXCrypto] exportRoomKeys: Exported %tu keys in %.0fms", keys.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000);

dispatch_async(dispatch_get_main_queue(), ^{

if (success)
{
success(keys);
}

});

});
#endif
}

- (void)exportRoomKeysWithPassword:(NSString *)password success:(void (^)(NSData *))success failure:(void (^)(NSError *))failure
{
#ifdef MX_CRYPTO
Expand Down
45 changes: 33 additions & 12 deletions MatrixSDK/Crypto/MXCryptoV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -608,20 +608,41 @@ private class MXCryptoV2: MXCrypto {
log.debug("Not implemented")
}

public override func exportRoomKeys(_ success: (([[AnyHashable : Any]]?) -> Void)!, failure: ((Swift.Error?) -> Void)!) {
log.debug("Not implemented")
}

public override func exportRoomKeys(withPassword password: String!, success: ((Data?) -> Void)!, failure: ((Swift.Error?) -> Void)!) {
log.debug("Not implemented")
}

public override func importRoomKeys(_ keys: [[AnyHashable : Any]]!, success: ((UInt, UInt) -> Void)!, failure: ((Swift.Error?) -> Void)!) {
log.debug("Not implemented")
public override func exportRoomKeys(
withPassword password: String!,
success: ((Data?) -> Void)!,
failure: ((Swift.Error?) -> Void)!
) {
do {
let data = try backupEngine.exportRoomKeys(passphrase: password)
log.debug("Exported room keys")
success(data)
} catch {
log.error("Failed exporting room keys", context: error)
failure(error)
}
}

public override func importRoomKeys(_ keyFile: Data!, withPassword password: String!, success: ((UInt, UInt) -> Void)!, failure: ((Swift.Error?) -> Void)!) {
log.debug("Not implemented")
public override func importRoomKeys(
_ keyFile: Data!,
withPassword password: String!,
success: ((UInt, UInt) -> Void)!,
failure: ((Swift.Error?) -> Void)!
) {
guard let data = keyFile, let password = password else {
log.failure("Missing keys or password")
failure(nil)
return
}

do {
let result = try backupEngine.importRoomKeys(data, passphrase: password)
log.debug("Imported room keys")
success(UInt(result.total), UInt(result.imported))
} catch {
log.error("Failed importing room keys", context: error)
failure(error)
}
}

public override func pendingKeyRequests(_ onComplete: ((MXUsersDevicesMap<NSArray>?) -> Void)!) {
Expand Down
35 changes: 30 additions & 5 deletions MatrixSDK/Crypto/SecretStorage/MXCryptoSecretStoreV2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,37 @@ class MXCryptoSecretStoreV2: NSObject, MXCryptoSecretStore {
log.error("No key backup version available")
return
}

if secretId == MXSecretId.keyBackup.takeUnretainedValue() as String {

switch secretId as NSString {
case MXSecretId.crossSigningMaster.takeUnretainedValue():
crossSigning.importCrossSigningKeys(
export: .init(
masterKey: secret,
selfSigningKey: nil,
userSigningKey: nil
)
)
case MXSecretId.crossSigningSelfSigning.takeUnretainedValue():
crossSigning.importCrossSigningKeys(
export: .init(
masterKey: nil,
selfSigningKey: secret,
userSigningKey: nil
)
)
case MXSecretId.crossSigningUserSigning.takeUnretainedValue():
crossSigning.importCrossSigningKeys(
export: .init(
masterKey: nil,
selfSigningKey: nil,
userSigningKey: secret
)
)
case MXSecretId.keyBackup.takeUnretainedValue():
let privateKey = MXBase64Tools.data(fromBase64: secret)
backupEngine.savePrivateKey(privateKey, version: version)
} else {
log.error("Not implemented")
default:
log.error("Unsupported type of secret", context: secretId)
}
}

Expand All @@ -61,7 +86,7 @@ class MXCryptoSecretStoreV2: NSObject, MXCryptoSecretStore {
}
return MXBase64Tools.base64(from: privateKey)
default:
log.error("Not implemented")
log.error("Unsupported type of secret", context: secretId)
return nil
}
}
Expand Down
11 changes: 11 additions & 0 deletions MatrixSDKTests/Crypto/CryptoMachine/MXCryptoProtocolStubs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class CryptoCrossSigningStub: CryptoIdentityStub, MXCryptoCrossSigning {
return nil
}

func importCrossSigningKeys(export: CrossSigningKeyExport) {
}

var stubbedIdentities = [String: UserIdentity]()
func userIdentity(userId: String) -> UserIdentity? {
stubbedIdentities[userId]
Expand Down Expand Up @@ -228,6 +231,14 @@ class CryptoBackupStub: MXCryptoBackup {
roomKeysSpy = roomKeys
return KeysImportResult(imported: Int64(roomKeys.count), total: Int64(roomKeys.count), keys: [:])
}

func exportRoomKeys(passphrase: String) throws -> Data {
return Data()
}

func importRoomKeys(_ data: Data, passphrase: String, progressListener: ProgressListener) throws -> KeysImportResult {
return KeysImportResult(imported: 0, total: 0, keys: [:])
}
}

#endif
Loading

0 comments on commit 5960e8b

Please sign in to comment.