diff --git a/TangemSdk/TangemSdk.xcodeproj/project.pbxproj b/TangemSdk/TangemSdk.xcodeproj/project.pbxproj index d8932ff70..4dca28658 100644 --- a/TangemSdk/TangemSdk.xcodeproj/project.pbxproj +++ b/TangemSdk/TangemSdk.xcodeproj/project.pbxproj @@ -318,6 +318,7 @@ DC59CB0A29AF6F9C00EC14E1 /* EntropyLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC59CB0929AF6F9C00EC14E1 /* EntropyLength.swift */; }; DC59CB0C29AF706100EC14E1 /* MnemonicError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC59CB0B29AF706100EC14E1 /* MnemonicError.swift */; }; DC59CB0E29AF70C700EC14E1 /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC59CB0D29AF70C700EC14E1 /* Mnemonic.swift */; }; + DC7254902A03E20A0003FE1B /* DerivedKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC72548F2A03E20A0003FE1B /* DerivedKeys.swift */; }; DC8B0E3F286F221D009D64F7 /* BiometricsUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC8B0E3E286F221D009D64F7 /* BiometricsUtil.swift */; }; DCA9706628E35EAD0046E62E /* GenerateOTPCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA9706528E35EAD0046E62E /* GenerateOTPCommand.swift */; }; DCC0A21129D3146100C45B13 /* SetUserSettingsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC0A21029D3146100C45B13 /* SetUserSettingsCommand.swift */; }; @@ -661,6 +662,7 @@ DC59CB0929AF6F9C00EC14E1 /* EntropyLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntropyLength.swift; sourceTree = ""; }; DC59CB0B29AF706100EC14E1 /* MnemonicError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MnemonicError.swift; sourceTree = ""; }; DC59CB0D29AF70C700EC14E1 /* Mnemonic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mnemonic.swift; sourceTree = ""; }; + DC72548F2A03E20A0003FE1B /* DerivedKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DerivedKeys.swift; sourceTree = ""; }; DC8B0E3E286F221D009D64F7 /* BiometricsUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BiometricsUtil.swift; sourceTree = ""; }; DCA9706528E35EAD0046E62E /* GenerateOTPCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateOTPCommand.swift; sourceTree = ""; }; DCC0A21029D3146100C45B13 /* SetUserSettingsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetUserSettingsCommand.swift; sourceTree = ""; }; @@ -1427,6 +1429,7 @@ 5D379C26268FA47600C7F473 /* EncryptionMode.swift */, 5D270F2426A0199100D2EDC1 /* WalletData.swift */, DCC0A21529D3216100C45B13 /* UserSettings.swift */, + DC72548F2A03E20A0003FE1B /* DerivedKeys.swift */, ); path = Card; sourceTree = ""; @@ -1838,6 +1841,7 @@ 5D2F3EE526CBDAA100779CAC /* KeyboardAdaptive.swift in Sources */, 5D7D5FB223449D4000058D69 /* SessionEnvironment.swift in Sources */, 5D2FE06324DD82750086B5E8 /* AttestCardKeyCommand.swift in Sources */, + DC7254902A03E20A0003FE1B /* DerivedKeys.swift in Sources */, DC1244E229BB7B390037BC05 /* WIF.swift in Sources */, 5D539ECB276CDD8600AB8B53 /* DeriveMultipleWalletPublicKeysTask.swift in Sources */, DA6C752A292682650070EEFD /* LAContext+.swift in Sources */, diff --git a/TangemSdk/TangemSdk/Common/Card/DerivedKeys.swift b/TangemSdk/TangemSdk/Common/Card/DerivedKeys.swift new file mode 100644 index 000000000..c68f93869 --- /dev/null +++ b/TangemSdk/TangemSdk/Common/Card/DerivedKeys.swift @@ -0,0 +1,64 @@ +// +// DerivedKeys.swift +// TangemSdk +// +// Created by Alexander Osokin on 04.05.2023. +// Copyright © 2023 Tangem AG. All rights reserved. +// + +import Foundation + +// We can't use CodingKeyRepresentable because of iOS 15 version +@available(iOS 13.0, *) +public struct DerivedKeys: JSONStringConvertible { + public private(set) var keys: [DerivationPath:ExtendedPublicKey] + + public init(keys: [DerivationPath : ExtendedPublicKey]) { + self.keys = keys + } + + public subscript(_ path: DerivationPath) -> ExtendedPublicKey? { + get { + return keys[path] + } + set(newValue) { + keys[path] = newValue + } + } +} + +@available(iOS 13.0, *) +extension DerivedKeys: Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let stringDictionary = try container.decode([String: ExtendedPublicKey].self) + + let keysDictionary: [DerivationPath: ExtendedPublicKey] = try stringDictionary.reduce(into: [:]) { partialResult, item in + let path = try DerivationPath(rawPath: item.key) + partialResult[path] = item.value + } + + self.init(keys: keysDictionary) + } + + public func encode(to encoder: Encoder) throws { + let stringDictionary = keys.reduce(into: [:]) { partialResult, item in + partialResult[item.key.rawPath] = item.value + } + + var container = encoder.singleValueContainer() + try container.encode(stringDictionary) + } +} + + +@available(iOS 13.0, *) +extension DerivedKeys: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (DerivationPath, ExtendedPublicKey)...) { + let dictionary = elements.reduce(into: [:]) { partialResult, item in + partialResult[item.0] = item.1 + } + + self.init(keys: dictionary) + } +} diff --git a/TangemSdk/TangemSdk/Common/Card/Wallet.swift b/TangemSdk/TangemSdk/Common/Card/Wallet.swift index 6cb83e9fe..221f32e66 100644 --- a/TangemSdk/TangemSdk/Common/Card/Wallet.swift +++ b/TangemSdk/TangemSdk/Common/Card/Wallet.swift @@ -35,7 +35,7 @@ public extension Card { /// Does this wallet has a backup public var hasBackup: Bool /// Derived keys according to `Config.defaultDerivationPaths` - public var derivedKeys: [DerivationPath:ExtendedPublicKey] = [:] + public var derivedKeys: DerivedKeys = [:] } } diff --git a/TangemSdk/TangemSdk/Operations/Derivation/DeriveMultipleWalletPublicKeysTask.swift b/TangemSdk/TangemSdk/Operations/Derivation/DeriveMultipleWalletPublicKeysTask.swift index 6bec1df8d..52387064f 100644 --- a/TangemSdk/TangemSdk/Operations/Derivation/DeriveMultipleWalletPublicKeysTask.swift +++ b/TangemSdk/TangemSdk/Operations/Derivation/DeriveMultipleWalletPublicKeysTask.swift @@ -10,7 +10,7 @@ import Foundation @available(iOS 13.0, *) public class DeriveMultipleWalletPublicKeysTask: CardSessionRunnable { - public typealias Response = [Data: [DerivationPath:ExtendedPublicKey]] + public typealias Response = [Data: DerivedKeys] private let derivations: Array<(Data,[DerivationPath])> private var response: Response = .init() diff --git a/TangemSdk/TangemSdk/Operations/Derivation/DeriveWalletPublicKeysTask.swift b/TangemSdk/TangemSdk/Operations/Derivation/DeriveWalletPublicKeysTask.swift index 538d0a386..7168b1cac 100644 --- a/TangemSdk/TangemSdk/Operations/Derivation/DeriveWalletPublicKeysTask.swift +++ b/TangemSdk/TangemSdk/Operations/Derivation/DeriveWalletPublicKeysTask.swift @@ -10,7 +10,6 @@ import Foundation @available(iOS 13.0, *) public class DeriveWalletPublicKeysTask: CardSessionRunnable { - public typealias Response = [DerivationPath:ExtendedPublicKey] private let walletPublicKey: Data private let derivationPaths: [DerivationPath] @@ -28,11 +27,11 @@ public class DeriveWalletPublicKeysTask: CardSessionRunnable { Log.debug("DeriveWalletPublicKeysTask deinit") } - public func run(in session: CardSession, completion: @escaping CompletionResult) { + public func run(in session: CardSession, completion: @escaping CompletionResult) { runDerivation(at: 0, keys: [:], in: session, completion: completion) } - private func runDerivation(at index: Int, keys: [DerivationPath:ExtendedPublicKey], in session: CardSession, completion: @escaping CompletionResult) { + private func runDerivation(at index: Int, keys: DerivedKeys, in session: CardSession, completion: @escaping CompletionResult) { guard index < derivationPaths.count else { completion(.success(keys)) return @@ -51,6 +50,3 @@ public class DeriveWalletPublicKeysTask: CardSessionRunnable { } } } - -@available(iOS 13.0, *) -extension DeriveWalletPublicKeysTask.Response: JSONStringConvertible {} diff --git a/TangemSdk/TangemSdk/TangemSdk.swift b/TangemSdk/TangemSdk/TangemSdk.swift index 6ac944b1f..84af16cc5 100644 --- a/TangemSdk/TangemSdk/TangemSdk.swift +++ b/TangemSdk/TangemSdk/TangemSdk.swift @@ -320,12 +320,12 @@ public extension TangemSdk { /// - walletPublicKey: Seed public key. /// - derivationPaths: Derivation paths. Repeated items will be ignored. /// - initialMessage: A custom description that shows at the beginning of the NFC session. If nil, default message will be used - /// - completion: Returns `Swift.Result<[ExtendedPublicKey],TangemSdkError>`. All derived keys are unique and will be returned in arbitrary order. + /// - completion: Returns `Swift.Result`. All derived keys are unique and will be returned in arbitrary order. func deriveWalletPublicKeys(cardId: String, walletPublicKey: Data, derivationPaths: [DerivationPath], initialMessage: Message? = nil, - completion: @escaping CompletionResult<[DerivationPath:ExtendedPublicKey]>) { + completion: @escaping CompletionResult) { let command = DeriveWalletPublicKeysTask(walletPublicKey: walletPublicKey, derivationPaths: derivationPaths) startSession(with: command, cardId: cardId, initialMessage: initialMessage, completion: completion) } diff --git a/TangemSdk/TangemSdkTests/JSONRPCTests.swift b/TangemSdk/TangemSdkTests/JSONRPCTests.swift index aeef9d798..2d59915a4 100644 --- a/TangemSdk/TangemSdkTests/JSONRPCTests.swift +++ b/TangemSdk/TangemSdkTests/JSONRPCTests.swift @@ -222,11 +222,11 @@ class JSONRPCTests: XCTestCase { } func testDerivePublicKeys() { - let result = ["m/44'/0'" : ExtendedPublicKey(publicKey: Data(hexString: "0200300397571D99D41BB2A577E2CBE495C04AC5B9A97B7A4ECF999F23CE45E962"), + let keys = [try! DerivationPath(rawPath: "m/44'/0'") : ExtendedPublicKey(publicKey: Data(hexString: "0200300397571D99D41BB2A577E2CBE495C04AC5B9A97B7A4ECF999F23CE45E962"), chainCode: Data(hexString: "537F7361175B150732E17508066982B42D9FB1F8239C4D7BFC490088C83A8BBB")), - "m/44'/1'" : ExtendedPublicKey(publicKey: Data(hexString: "0200300397571D99D41BB2A577E2CBE495C04AC5B9A97B7A4ECF999F23CE45E962"), + try! DerivationPath(rawPath: "m/44'/1'") : ExtendedPublicKey(publicKey: Data(hexString: "0200300397571D99D41BB2A577E2CBE495C04AC5B9A97B7A4ECF999F23CE45E962"), chainCode: Data(hexString: "537F7361175B150732E17508066982B42D9FB1F8239C4D7BFC490088C83A8BBB"))] - + let result = DerivedKeys(keys: keys) testMethod(name: "DeriveWalletPublicKeys", result: result) } diff --git a/TangemSdk/TangemSdkTests/Jsons/Card.json b/TangemSdk/TangemSdkTests/Jsons/Card.json index 8376da125..0bcaa46bd 100644 --- a/TangemSdk/TangemSdkTests/Jsons/Card.json +++ b/TangemSdk/TangemSdkTests/Jsons/Card.json @@ -65,7 +65,7 @@ "index" : 0, "hasBackup" : false, "isImported": false, - "derivedKeys" : [] + "derivedKeys" : {} }, { "publicKey" : "0440C533E007D029C1F345CA70A9F6016EC7A95C775B6320AE84248F20B647FBBD90FF56A2D9C3A1984279ED2367274A49079789E130444541C2F15907D5570B49", @@ -77,7 +77,7 @@ "index" : 1, "hasBackup" : false, "isImported": false, - "derivedKeys" : [] + "derivedKeys" : {} }, { "publicKey" : "04DDFACEF55A95EAB2CDCC8E86CE779342D2E2A53CF8F0F20BF2B248336AE3EEA6DD62D1F4C5420A71D6212073B136034CDC878DAD3AE3FDFA3360E6FE6184F470", @@ -89,7 +89,7 @@ "index" : 2, "hasBackup" : false, "isImported": false, - "derivedKeys" : [] + "derivedKeys" : {} } ], "isPasscodeSet" : true, diff --git a/TangemSdk/TangemSdkTests/Jsons/CreateWallet.json b/TangemSdk/TangemSdkTests/Jsons/CreateWallet.json index 4fb7c37fd..4e7b0ef9e 100644 --- a/TangemSdk/TangemSdkTests/Jsons/CreateWallet.json +++ b/TangemSdk/TangemSdkTests/Jsons/CreateWallet.json @@ -22,7 +22,7 @@ "index": 1, "hasBackup" : false, "isImported": false, - "derivedKeys" : [] + "derivedKeys" : {} } }, "id" : 1 diff --git a/TangemSdk/TangemSdkTests/Jsons/ImportWalletMnemonic.json b/TangemSdk/TangemSdkTests/Jsons/ImportWalletMnemonic.json index d93946966..790b80367 100644 --- a/TangemSdk/TangemSdkTests/Jsons/ImportWalletMnemonic.json +++ b/TangemSdk/TangemSdkTests/Jsons/ImportWalletMnemonic.json @@ -25,7 +25,7 @@ "index": 1, "hasBackup" : false, "isImported": false, - "derivedKeys" : [] + "derivedKeys" : {} } }, "id" : 1 diff --git a/TangemSdk/TangemSdkTests/Jsons/ImportWalletSeed.json b/TangemSdk/TangemSdkTests/Jsons/ImportWalletSeed.json index cb9a90a3b..15caca58b 100644 --- a/TangemSdk/TangemSdkTests/Jsons/ImportWalletSeed.json +++ b/TangemSdk/TangemSdkTests/Jsons/ImportWalletSeed.json @@ -23,7 +23,7 @@ "index": 1, "hasBackup" : false, "isImported": false, - "derivedKeys" : [] + "derivedKeys" : {} } }, "id" : 1