diff --git a/Package.swift b/Package.swift index 2262457..c5e4183 100644 --- a/Package.swift +++ b/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "tkey_pkg", platforms: [ - .iOS(SupportedPlatform.IOSVersion.v14), + .iOS(.v13), .macOS(.v10_15), ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. @@ -18,6 +18,7 @@ let package = Package( // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), .package(name: "TorusUtils", url: "https://github.com/torusresearch/torus-utils-swift" , from: "6.0.1"), + .package(name: "secp256k1", url: "https://github.com/GigaBitcoin/secp256k1.swift", .exact("0.12.2")), // dev dependencies only .package(name:"CryptoSwift", url: "https://github.com/krzyzanowskim/CryptoSwift.git",from: "1.5.1"), .package(name:"jwt-kit", url: "https://github.com/vapor/jwt-kit.git", from: "4.0.0"), diff --git a/Sources/ThresholdKey/Common/helper.swift b/Sources/ThresholdKey/Common/helper.swift new file mode 100644 index 0000000..9288599 --- /dev/null +++ b/Sources/ThresholdKey/Common/helper.swift @@ -0,0 +1,44 @@ +// +// File.swift +// +// +// Created by CW Lee on 26/09/2023. +// + +import Foundation + + +func selectedServerToPointer (selectedServers: [UInt32]?) throws -> UnsafeMutablePointer? { + guard let selectedServers = selectedServers else { + return nil + } + var serversPointer: UnsafeMutablePointer? + + let selected_servers_json = try JSONSerialization.data(withJSONObject: selectedServers as Any) + let selected_servers_str = String(data: selected_servers_json, encoding: .utf8)! + print(selected_servers_str) + serversPointer = UnsafeMutablePointer(mutating: (selected_servers_str as NSString).utf8String) + + guard let serversPointer = serversPointer else { + throw RuntimeError("convert error") + } + return serversPointer +} + +func authSignaturesToPointer ( authSignatures : [String]?) throws -> UnsafeMutablePointer? { + guard let authSignatures = authSignatures else { + return nil + } + let auth_signatures_json = try JSONSerialization.data(withJSONObject: authSignatures) + guard let auth_signatures_str = String(data: auth_signatures_json, encoding: .utf8) else { + throw RuntimeError("auth signatures error") + } + print(authSignatures) + print(auth_signatures_str.count) + + let authSignaturesPointer = UnsafeMutablePointer(mutating: (auth_signatures_str as NSString).utf8String) + + + return authSignaturesPointer +} + diff --git a/Sources/ThresholdKey/Modules/TssModule.swift b/Sources/ThresholdKey/Modules/TssModule.swift index f0977e5..91cce50 100644 --- a/Sources/ThresholdKey/Modules/TssModule.swift +++ b/Sources/ThresholdKey/Modules/TssModule.swift @@ -169,7 +169,7 @@ public final class TssModule { /// - Returns: `Int32` /// /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key. - public static func get_tss_nonce(threshold_key: ThresholdKey, tss_tag: String, prefetch: Bool = false) throws -> Int32 { + public static func get_tss_nonce(threshold_key: ThresholdKey, tss_tag: String, prefetch: Bool = false, incNonce: Int32 = 1) throws -> Int32 { var errorCode: Int32 = -1 let tss_tag_pointer: UnsafeMutablePointer? = UnsafeMutablePointer(mutating: NSString(string: tss_tag).utf8String) var nonce = withUnsafeMutablePointer(to: &errorCode, { error in @@ -179,7 +179,7 @@ public final class TssModule { } if prefetch { - nonce += 1 + nonce += incNonce } return nonce @@ -208,7 +208,7 @@ public final class TssModule { threshold_key_get_tss_share(threshold_key.pointer, factorKeyPointer, threshold, curvePointer, error) }) guard errorCode == 0 else { - throw RuntimeError("Error in ThresholdKey get_tss_share") + throw RuntimeError("Error in ThresholdKey get_tss_share \(errorCode)") } let string = String(cString: result!) string_free(result) @@ -252,9 +252,9 @@ public final class TssModule { /// - torusUtils: torusUtils used to retrieve dkg tss pub key /// /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key. - public static func create_tagged_tss_share(threshold_key: ThresholdKey, tss_tag: String, deviceTssShare: String?, factorPub: String, deviceTssIndex: Int32, nodeDetails: AllNodeDetailsModel, torusUtils: TorusUtils) async throws { + public static func create_tagged_tss_share(threshold_key: ThresholdKey, tss_tag: String, deviceTssShare: String? = nil, factorPub: String, deviceTssIndex: Int32) async throws { try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag) - try await TssModule.update_tss_pub_key(threshold_key: threshold_key, tss_tag: tss_tag, nodeDetails: nodeDetails, torusUtils: torusUtils) + try await TssModule.update_tss_pub_key(threshold_key: threshold_key, tss_tag: tss_tag) return try await withCheckedThrowingContinuation { continuation in create_tagged_tss_share(threshold_key: threshold_key, deviceTssShare: deviceTssShare, factorPub: factorPub, deviceTssIndex: deviceTssIndex) { @@ -289,12 +289,12 @@ public final class TssModule { /// - prefetch: Fetch the next nonce's pub key /// /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key. - public static func update_tss_pub_key(threshold_key: ThresholdKey, tss_tag: String, nodeDetails: AllNodeDetailsModel, torusUtils: TorusUtils, prefetch: Bool = false) async throws { + public static func update_tss_pub_key(threshold_key: ThresholdKey, tss_tag: String, prefetch: Bool = false, incNonce: Int32 = 1) async throws { try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag) - let nonce = String(try get_tss_nonce(threshold_key: threshold_key, tss_tag: tss_tag, prefetch: prefetch)) + let nonce = String(try get_tss_nonce(threshold_key: threshold_key, tss_tag: tss_tag, prefetch: prefetch, incNonce: incNonce)) - let public_address = try await get_dkg_pub_key(threshold_key: threshold_key, tssTag: tss_tag, nonce: nonce, nodeDetails: nodeDetails, torusUtils: torusUtils) + let public_address = try await get_dkg_pub_key(threshold_key: threshold_key, tssTag: tss_tag, nonce: nonce) let pk_encoded = try JSONEncoder().encode(public_address) guard let public_key = String(data: pk_encoded, encoding: .utf8) else { throw RuntimeError("update_tss_pub_key - Conversion Error - ResultString") @@ -364,12 +364,12 @@ public final class TssModule { } } - private static func generate_tss_share(threshold_key: ThresholdKey, input_tss_share: String, tss_input_index: Int32, auth_signatures: [String], new_factor_pub: String, new_tss_index: Int32, selected_servers: [Int32]? = nil, completion: @escaping (Result) -> Void) { + private static func generate_tss_share(threshold_key: ThresholdKey, input_tss_share: String, tss_input_index: Int32, new_factor_pub: String, new_tss_index: Int32, selected_servers: [Int32]? = nil, completion: @escaping (Result) -> Void) { threshold_key.tkeyQueue.async { do { var errorCode: Int32 = -1 let curvePointer = UnsafeMutablePointer(mutating: (threshold_key.curveN as NSString).utf8String) - + let auth_signatures = try threshold_key.getAuthSignatures() let auth_signatures_json = try JSONSerialization.data(withJSONObject: auth_signatures) guard let auth_signatures_str = String(data: auth_signatures_json, encoding: .utf8) else { throw RuntimeError("auth signatures error") @@ -390,7 +390,7 @@ public final class TssModule { threshold_key_generate_tss_share(threshold_key.pointer, inputSharePointer, tss_input_index, new_tss_index, newFactorPubPointer, serversPointer, authSignaturesPointer, curvePointer, error) }) guard errorCode == 0 else { - throw RuntimeError("Error in ThresholdKey generate_tss_share") + throw RuntimeError("Error in ThresholdKey generate_tss_share : \(errorCode)") } completion(.success(())) } catch { @@ -399,14 +399,14 @@ public final class TssModule { } } - public static func generate_tss_share(threshold_key: ThresholdKey, tss_tag: String, input_tss_share: String, tss_input_index: Int32, auth_signatures: [String], new_factor_pub: String, new_tss_index: Int32, nodeDetails: AllNodeDetailsModel, torusUtils: TorusUtils, selected_servers: [Int32]? = nil) async throws { + public static func generate_tss_share(threshold_key: ThresholdKey, tss_tag: String, input_tss_share: String, tss_input_index: Int32, new_factor_pub: String, new_tss_index: Int32, selected_servers: [Int32]? = nil) async throws { try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag) - try await update_tss_pub_key(threshold_key: threshold_key, tss_tag: tss_tag, nodeDetails: nodeDetails, torusUtils: torusUtils, prefetch: true) + try await update_tss_pub_key(threshold_key: threshold_key, tss_tag: tss_tag, prefetch: true) return try await withCheckedThrowingContinuation { continuation in - generate_tss_share(threshold_key: threshold_key, input_tss_share: input_tss_share, tss_input_index: tss_input_index, auth_signatures: auth_signatures, new_factor_pub: new_factor_pub, new_tss_index: new_tss_index) { + generate_tss_share(threshold_key: threshold_key, input_tss_share: input_tss_share, tss_input_index: tss_input_index, new_factor_pub: new_factor_pub, new_tss_index: new_tss_index) { result in switch result { case let .success(result): @@ -418,12 +418,13 @@ public final class TssModule { } } - private static func delete_tss_share(threshold_key: ThresholdKey, input_tss_share: String, tss_input_index: Int32, auth_signatures: [String], delete_factor_pub: String, selected_servers: [Int32]? = nil, completion: @escaping (Result) -> Void) { + private static func delete_tss_share(threshold_key: ThresholdKey, input_tss_share: String, tss_input_index: Int32, delete_factor_pub: String, selected_servers: [Int32]? = nil, completion: @escaping (Result) -> Void) { threshold_key.tkeyQueue.async { do { var errorCode: Int32 = -1 let curvePointer = UnsafeMutablePointer(mutating: (threshold_key.curveN as NSString).utf8String) - + + let auth_signatures = try threshold_key.getAuthSignatures() let auth_signatures_json = try JSONSerialization.data(withJSONObject: auth_signatures) guard let auth_signatures_str = String(data: auth_signatures_json, encoding: .utf8) else { throw RuntimeError("auth signatures error") @@ -444,7 +445,7 @@ public final class TssModule { threshold_key_delete_tss_share(threshold_key.pointer, inputSharePointer, tss_input_index, factorPubPointer, serversPointer, authSignaturesPointer, curvePointer, error) }) guard errorCode == 0 else { - throw RuntimeError("Error in ThresholdKey delete tss share") + throw RuntimeError("Error in ThresholdKey delete tss share : error code \(String(errorCode))") } completion(.success(())) } catch { @@ -453,13 +454,13 @@ public final class TssModule { } } - public static func delete_tss_share(threshold_key: ThresholdKey, tss_tag: String, input_tss_share: String, tss_input_index: Int32, auth_signatures: [String], delete_factor_pub: String, nodeDetails: AllNodeDetailsModel, torusUtils: TorusUtils, selected_servers: [Int32]? = nil) async throws { - try await update_tss_pub_key(threshold_key: threshold_key, tss_tag: tss_tag, nodeDetails: nodeDetails, torusUtils: torusUtils, prefetch: true) + public static func delete_tss_share(threshold_key: ThresholdKey, tss_tag: String, input_tss_share: String, tss_input_index: Int32, delete_factor_pub: String, selected_servers: [Int32]? = nil) async throws { + try await update_tss_pub_key(threshold_key: threshold_key, tss_tag: tss_tag, prefetch: true) try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag) return try await withCheckedThrowingContinuation { continuation in - delete_tss_share(threshold_key: threshold_key, input_tss_share: input_tss_share, tss_input_index: tss_input_index, auth_signatures: auth_signatures, delete_factor_pub: delete_factor_pub) { + delete_tss_share(threshold_key: threshold_key, input_tss_share: input_tss_share, tss_input_index: tss_input_index, delete_factor_pub: delete_factor_pub) { result in switch result { case let .success(result): @@ -471,6 +472,18 @@ public final class TssModule { } } + public static func create_factor (threshold_key: ThresholdKey, tss_tag: String, factor_key: String, new_factor_pub: String, new_tss_index: Int32, selected_servers: [Int32]? = nil ) async throws { + if factor_key.count > 66 { throw RuntimeError("Invalid factor Key") } + try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag) + + let (tss_index, tss_share ) = try await get_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, factorKey: factor_key) + if tss_index == String(new_tss_index) { + try await copy_factor_pub(threshold_key: threshold_key, tss_tag: tss_tag, factorKey: factor_key, newFactorPub: new_factor_pub, tss_index: new_tss_index) + } else { + try await TssModule.generate_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, input_tss_share: tss_share, tss_input_index: Int32(tss_index)!, new_factor_pub: new_factor_pub, new_tss_index: new_tss_index, selected_servers: selected_servers) + } + } + /// Generate tss_index's share and register to new factor key /// - Parameters: /// - threshold_key: The threshold key to act on. @@ -484,12 +497,12 @@ public final class TssModule { /// - torusUtils: torus utils /// /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key. - public static func add_factor_pub(threshold_key: ThresholdKey, tss_tag: String, factor_key: String, auth_signatures: [String], new_factor_pub: String, new_tss_index: Int32, selected_servers: [Int32]? = nil, nodeDetails: AllNodeDetailsModel, torusUtils: TorusUtils) async throws { + public static func add_factor_pub(threshold_key: ThresholdKey, tss_tag: String, factor_key: String, new_factor_pub: String, new_tss_index: Int32, selected_servers: [Int32]? = nil) async throws { if factor_key.count > 66 { throw RuntimeError("Invalid factor Key") } try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag) let (tss_index, tss_share) = try await get_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, factorKey: factor_key) - try await TssModule.generate_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, input_tss_share: tss_share, tss_input_index: Int32(tss_index)!, auth_signatures: auth_signatures, new_factor_pub: new_factor_pub, new_tss_index: new_tss_index, nodeDetails: nodeDetails, torusUtils: torusUtils, selected_servers: selected_servers) + try await TssModule.generate_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, input_tss_share: tss_share, tss_input_index: Int32(tss_index)!, new_factor_pub: new_factor_pub, new_tss_index: new_tss_index, selected_servers: selected_servers) } /// Delete factor pub from tss metadata @@ -504,12 +517,12 @@ public final class TssModule { /// - selected_servers: node indexes of the server that will be communicated to /// /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key. - public static func delete_factor_pub(threshold_key: ThresholdKey, tss_tag: String, factor_key: String, auth_signatures: [String], delete_factor_pub: String, nodeDetails: AllNodeDetailsModel, torusUtils: TorusUtils, selected_servers: [Int32]? = nil) async throws { + public static func delete_factor_pub(threshold_key: ThresholdKey, tss_tag: String, factor_key: String, delete_factor_pub: String, selected_servers: [Int32]? = nil) async throws { if factor_key.count > 66 { throw RuntimeError("Invalid factor Key") } try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tss_tag) let (tss_index, tss_share) = try await get_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, factorKey: factor_key) - try await TssModule.delete_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, input_tss_share: tss_share, tss_input_index: Int32(tss_index)!, auth_signatures: auth_signatures, delete_factor_pub: delete_factor_pub, nodeDetails: nodeDetails, torusUtils: torusUtils, selected_servers: selected_servers) + try await TssModule.delete_tss_share(threshold_key: threshold_key, tss_tag: tss_tag, input_tss_share: tss_share, tss_input_index: Int32(tss_index)!, delete_factor_pub: delete_factor_pub, selected_servers: selected_servers) } /// Backup device share with factor key @@ -540,22 +553,39 @@ public final class TssModule { /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key. public static func find_device_share_index(threshold_key: ThresholdKey, factor_key: String) async throws -> String { let result = try await threshold_key.storage_layer_get_metadata(private_key: factor_key) + guard let resultData = result.data(using: .utf8) else { throw "Invalid factor key" } guard let resultJson = try JSONSerialization.jsonObject(with: resultData) as? [String: Any] else { throw "Invalid factor key" } - guard let deviceShareJson = resultJson["deviceShare"] as? [String: Any] else { - throw "Invalid factor key" - } - guard let shareJson = deviceShareJson["share"] as? [String: Any] else { - throw "Invalid factor key" - } - guard let shareIndex = shareJson["shareIndex"] as? String else { - throw "Invalid factor key" - } - return shareIndex + + let shareStore = try ShareStore(json: result) + return try shareStore.share_index() +// +// // TODO : Fix the deserialization of the Factor cloud metadata +// // ts implemetation break this +// let deviceShareJson : [String: Any] +// if resultJson["deviceShare"] != nil { +// guard var deviceShare = resultJson["deviceShare"] as? [String: Any] else { +// throw "invalid factor json without deviceShare or share indexkey" +// } +// deviceShareJson = deviceShare +// } else { +// guard let deviceShare = resultJson["share"] as? [String:Any] else { +// throw "invalid factor json without deviceShare or share indexkey" +// } +// deviceShareJson = deviceShare +// } +// +// guard let shareJson = deviceShareJson["share"] as? [String: Any] else { +// throw "Invalid factor key" +// } +// guard let shareIndex = shareJson["shareIndex"] as? String else { +// throw "Invalid factor key" +// } +// return shareIndex } /// Function to get dkg public key @@ -568,9 +598,11 @@ public final class TssModule { /// - Returns: `TSSPubKeyResult` /// /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key. - public static func get_dkg_pub_key(threshold_key: ThresholdKey, tssTag: String, nonce: String, nodeDetails: AllNodeDetailsModel, torusUtils: TorusUtils) async throws -> TSSPubKeyResult { + public static func get_dkg_pub_key(threshold_key: ThresholdKey, tssTag: String, nonce: String) async throws -> TSSPubKeyResult { let extendedVerifierId = try threshold_key.get_extended_verifier_id() let split = extendedVerifierId.components(separatedBy: "\u{001c}") + let torusUtils = try threshold_key.getTorusUtils() + let nodeDetails = try threshold_key.getnodeDetails() let result = try await torusUtils.getPublicAddress(endpoints: nodeDetails.torusNodeEndpoints, torusNodePubs: nodeDetails.torusNodePub, verifier: split[0], verifierId: split[1], extendedVerifierId: "\(split[1])\u{0015}\(tssTag)\u{0016}\(nonce)") diff --git a/Sources/ThresholdKey/Modules/TssSecurityQuestionModule.swift b/Sources/ThresholdKey/Modules/TssSecurityQuestionModule.swift new file mode 100644 index 0000000..9c896f0 --- /dev/null +++ b/Sources/ThresholdKey/Modules/TssSecurityQuestionModule.swift @@ -0,0 +1,261 @@ +import Foundation +#if canImport(lib) + import lib +#endif +import BigInt + +let TssSecurityQuestion = "tssSecurityQuestion" + + +public struct EncryptedMessage : Codable { + public let ciphertext: String; + public let ephemPublicKey: String; + public let iv: String; + public let mac: String; + + public func toString() throws -> String { + let data = try JSONEncoder().encode(self) +// let data = try JSONSerialization.data(withJSONObject: self) + guard let result = String(data: data, encoding: .utf8) else { + throw "invalid toString" + } + return result + } +} + +public struct TssSecurityQuestionData : Codable{ + public var shareIndex: String + public var factorPublicKey: String + public var question: String + + public func toJsonString() throws -> String { + let jsonData = try JSONEncoder().encode(self) + guard let jsonStr = String(data: jsonData, encoding: .utf8) else { + throw "Invalid security question data" + } + return jsonStr + } + + public static func fromJsonString(jsonStr: String ) throws -> Self { + guard let data = jsonStr.data(using: .utf8) else { + throw "invalid security question data" + } + let store = try JSONDecoder().decode( TssSecurityQuestionData.self, from: data) + return store + } +} + +// Security question has low entrophy, hence it is not recommended way to secure the factor key or share +public final class TssSecurityQuestionModule { + public static func compute_hash( threshold : ThresholdKey, answer: String, tag: String ) throws -> String { + let suffix = try threshold.get_key_details().pub_key.getPublicKey(format: .EllipticCompress) + tag + let prehash = answer + suffix + guard let hash = prehash.data(using: .utf8)?.sha3(.keccak256) else { + throw "Invalid answer format for answer : \(answer)" + } + return hash.toHexString() + } + + /// set security question + /// - Parameters: + /// - threshold_key: The threshold key to act on. + /// - question: The security question. + /// - answer: The answer for the security question. + /// - factorKey: Factor key that registred to security question + /// - tag: tss tag + /// + /// - Returns: `` + /// + /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key of failed to set security question + public static func set_security_question( threshold : ThresholdKey, question: String, answer: String, factorKey :String, selectedServer:String, tag: String ) async throws -> String { + + try await TssModule.set_tss_tag(threshold_key: threshold, tss_tag: tag) + try await TssModule.update_tss_pub_key(threshold_key: threshold, tss_tag: tag, prefetch: true) + var errorCode: Int32 = -1 + let hash = try compute_hash(threshold: threshold, answer: answer, tag: tag) + let factorKeyPtr = UnsafeMutablePointer(mutating: (factorKey as NSString).utf8String) + let questionPtr = UnsafeMutablePointer(mutating: (question as NSString).utf8String) + let hashPtr = UnsafeMutablePointer(mutating: (hash as NSString).utf8String) + let curvePointer = UnsafeMutablePointer(mutating: (threshold.curveN as NSString).utf8String) + + + + let auth_signatures_json = try JSONSerialization.data(withJSONObject: threshold.authSignatures) + guard let auth_signatures_str = String(data: auth_signatures_json, encoding: .utf8) else { + throw RuntimeError("auth signatures error") + } + let authSignaturesPointer = UnsafeMutablePointer(mutating: (auth_signatures_str as NSString).utf8String) + + let selectedServers:[UInt32] = [1,2,3] + let selected_servers_json = try JSONSerialization.data(withJSONObject: selectedServers as Any) + guard let selected_servers_str = String(data: selected_servers_json, encoding: .utf8) else { + throw RuntimeError("selectedServers error") + } + let serversPointer = UnsafeMutablePointer(mutating: (selected_servers_str as NSString).utf8String) + +// let tssIndex: UInt32 = 2 + + withUnsafeMutablePointer(to: &errorCode, { error in + tss_security_question_set_security_question(threshold.pointer, factorKeyPtr, questionPtr, hashPtr, 2, serversPointer, authSignaturesPointer, + curvePointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey set_security_question \(errorCode)") + } + + let shareIndex = try await TssModule.find_device_share_index(threshold_key: threshold, factor_key: factorKey); + try TssModule.backup_share_with_factor_key(threshold_key: threshold, shareIndex: shareIndex, factorKey: hash) + return hash + } + + + /// change security question to new question and answer + /// - Parameters: + /// - threshold_key: The threshold key to act on. + /// - newQuestion: The new security question . + /// - newAnswer: The new answer for the security question. + /// - answer: current answer + /// - tag: tss tag + /// + /// - Returns: `` + /// + /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key or fail to change security question + public static func change_security_question( threshold : ThresholdKey, newQuestion: String, newAnswer: String, answer: String, tag: String) async throws -> (String, String){ + + try await TssModule.set_tss_tag(threshold_key: threshold, tss_tag: tag) + try await TssModule.update_tss_pub_key(threshold_key: threshold, tss_tag: tag, prefetch: true, incNonce: 2) + var errorCode: Int32 = -1 + + let hash = try compute_hash(threshold: threshold, answer: answer, tag: tag) + let newHash = try compute_hash(threshold: threshold, answer: newAnswer, tag: tag) + let newQuestionPtr = UnsafeMutablePointer(mutating: (newQuestion as NSString).utf8String) + let hashPtr = UnsafeMutablePointer(mutating: (hash as NSString).utf8String) + let newHashPtr = UnsafeMutablePointer(mutating: (newHash as NSString).utf8String) + let curvePointer = UnsafeMutablePointer(mutating: (threshold.curveN as NSString).utf8String) + + let auth_signatures_json = try JSONSerialization.data(withJSONObject: threshold.authSignatures) + guard let auth_signatures_str = String(data: auth_signatures_json, encoding: .utf8) else { + throw RuntimeError("auth signatures error") + } + let authSignaturesPointer = UnsafeMutablePointer(mutating: (auth_signatures_str as NSString).utf8String) + + let selectedServers:[UInt32] = [1,2,3] + let selected_servers_json = try JSONSerialization.data(withJSONObject: selectedServers as Any) + guard let selected_servers_str = String(data: selected_servers_json, encoding: .utf8) else { + throw RuntimeError("selectedServers error") + } + let serversPointer = UnsafeMutablePointer(mutating: (selected_servers_str as NSString).utf8String) + + withUnsafeMutablePointer(to: &errorCode, { error in + tss_security_question_change_question(threshold.pointer, newHashPtr, newQuestionPtr, hashPtr, serversPointer, authSignaturesPointer, curvePointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey change_security_question \(errorCode)") + } + return (hash, newHash) + } + + /// delete security question + /// - Parameters: + /// - threshold_key: The threshold key to act on. + /// - tag: tss tag + /// + /// - Returns: `String` public key of the factor + /// + /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key or fail to delete security question + public static func delete_security_question( threshold : ThresholdKey, tag: String, factorKey: String + , answer: String? = nil) async throws -> String { + + try await TssModule.set_tss_tag(threshold_key: threshold, tss_tag: tag) + try await TssModule.update_tss_pub_key(threshold_key: threshold, tss_tag: tag, prefetch: true) + + var errorCode: Int32 = -1 + let factorKeyPtr = UnsafeMutablePointer(mutating: (factorKey as NSString).utf8String) + var hashPtr : UnsafeMutablePointer?; + if let answer = answer { + let hash = try self.compute_hash(threshold: threshold, answer: answer, tag: tag) + hashPtr = UnsafeMutablePointer(mutating: (hash as NSString).utf8String) + } + let curvePointer = UnsafeMutablePointer(mutating: (threshold.curveN as NSString).utf8String) + + let auth_signatures_json = try JSONSerialization.data(withJSONObject: threshold.authSignatures) + guard let auth_signatures_str = String(data: auth_signatures_json, encoding: .utf8) else { + throw RuntimeError("auth signatures error") + } + let authSignaturesPointer = UnsafeMutablePointer(mutating: (auth_signatures_str as NSString).utf8String) + + let selectedServers:[UInt32] = [1,2,3] + let selected_servers_json = try JSONSerialization.data(withJSONObject: selectedServers as Any) + guard let selected_servers_str = String(data: selected_servers_json, encoding: .utf8) else { + throw RuntimeError("selectedServers error") + } + let serversPointer = UnsafeMutablePointer(mutating: (selected_servers_str as NSString).utf8String) + + let result = withUnsafeMutablePointer(to: &errorCode, { error in + tss_security_question_delete_security_question(threshold.pointer, factorKeyPtr, hashPtr, serversPointer, authSignaturesPointer, curvePointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey delete_security_question \(errorCode)") + } + let hash = String(cString: result!) + string_free(result) + return hash + } + + + /// get security question + /// - Parameters: + /// - threshold_key: The threshold key to act on. + /// - tag: tss tag + /// + /// - Returns: `String` question + /// + /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key or fail to get security question + public static func get_question( threshold: ThresholdKey, tag: String ) async throws -> String { + + try await TssModule.set_tss_tag(threshold_key: threshold, tss_tag: tag) + var errorCode: Int32 = -1 + + let result = withUnsafeMutablePointer(to: &errorCode, { error in + tss_security_question_get_question(threshold.pointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey get_question \(errorCode)") + } + let question = String(cString: result!) + string_free(result) + return question + + } + + + /// recover security question's factor given correct answer + /// - Parameters: + /// - threshold_key: The threshold key to act on. + /// - answer: answer to security question + /// - tag: tss tag + /// + /// - Returns: `String` factor key + /// + /// - Throws: `RuntimeError`, indicates invalid parameters was used or invalid threshold key or fail to delete security question + public static func recover_factor ( threshold: ThresholdKey, answer: String , tag: String ) async throws -> String { + + try await TssModule.set_tss_tag(threshold_key: threshold, tss_tag: tag) + var errorCode: Int32 = -1 + + let hash = try compute_hash(threshold: threshold, answer: answer, tag: tag) + + let answerPtr = UnsafeMutablePointer(mutating: (hash as NSString).utf8String) + + let result = withUnsafeMutablePointer(to: &errorCode, { error in + tss_security_question_recover_factor(threshold.pointer, answerPtr, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey recover_factor \(errorCode)") + } + let hashOut = String(cString: result!) + string_free(result) + + return hash + } +} diff --git a/Sources/ThresholdKey/StorageLayer.swift b/Sources/ThresholdKey/StorageLayer.swift index 92ce5ee..1d87e45 100644 --- a/Sources/ThresholdKey/StorageLayer.swift +++ b/Sources/ThresholdKey/StorageLayer.swift @@ -4,6 +4,21 @@ import Foundation #endif +func multipartData (finalMetadataParams: [[String: Any]] , boundary: String) -> Data { + // Create a FormData-like structure in Swift + var formData = Data() + for (key, value) in finalMetadataParams.enumerated() { + if let jsonValue = try? JSONSerialization.data(withJSONObject: value) { + formData.append("--\(boundary)\r\n".data(using: .utf8)!) + formData.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!) + formData.append(jsonValue) + formData.append("\r\n".data(using: .utf8)!) + } + } + formData.append("--\(boundary)--".data(using: .utf8)!) + return formData +} + public final class StorageLayer { private(set) var pointer: OpaquePointer? @@ -51,29 +66,12 @@ public final class StorageLayer { request.addValue("Content-Type", forHTTPHeaderField: "Access-Control-Allow-Headers") if urlString.split(separator: "/").last == "bulk_set_stream" { - // let boundary = UUID().uuidString; - // request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") - request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") - + let boundary = UUID().uuidString; + request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") + let json = try! JSONSerialization.jsonObject(with: dataString.data(using: String.Encoding.utf8)!, options: .allowFragments) as! [[String: Any]] - - // for item in json { - // let dataItem = try! JSONSerialization.data(withJSONObject: item, options: .prettyPrinted) - // requestData.append(StorageLayer.createMultipartBody(data: dataItem, boundary: boundary, file: "multipartData")) - // } - - var form_data: [String] = [] - - // urlencoded item format: "(key)=(self.percentEscapeString(value))" - for (index, element) in json.enumerated() { - let json_elem = try! JSONSerialization.data(withJSONObject: element, options: .withoutEscapingSlashes) - let json_escaped_string = StorageLayer.percentEscapeString(string: String(data: json_elem, encoding: .utf8)!) - let final_string = String(index) + "=" + json_escaped_string - form_data.append(final_string) - } - let body_data = form_data.joined(separator: "&") - - request.httpBody = body_data.data(using: String.Encoding.utf8) + let formData = multipartData(finalMetadataParams: json, boundary: boundary) + request.httpBody = formData } else { request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = dataString.data(using: String.Encoding.utf8) diff --git a/Sources/ThresholdKey/ThresholdKey.swift b/Sources/ThresholdKey/ThresholdKey.swift index aaf3721..ce9e8c0 100644 --- a/Sources/ThresholdKey/ThresholdKey.swift +++ b/Sources/ThresholdKey/ThresholdKey.swift @@ -10,7 +10,40 @@ public class ThresholdKey { private(set) var use_tss: Bool = false internal let curveN = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" internal let tkeyQueue = DispatchQueue(label: "thresholdkey.queue") + internal var authSignatures: [String]? + internal var nodeDetails: AllNodeDetailsModel? + internal var torusUtils: TorusUtils? + + public func getAuthSignatures () throws -> [String] { + guard let result = self.authSignatures else { + throw "authSignatures is undefined" + } + return result + } + public func getnodeDetails () throws -> AllNodeDetailsModel { + guard let result = self.nodeDetails else { + throw "authSignatures is undefined" + } + return result + } + public func getTorusUtils () throws -> TorusUtils { + guard let result = self.torusUtils else { + throw "authSignatures is undefined" + } + return result + } + + public func setAuthSignatures ( authSignatures: [String]) { + self.authSignatures = authSignatures + } + public func setnodeDetails (nodeDetails : AllNodeDetailsModel) { + self.nodeDetails = nodeDetails + } + public func setTorusUtils (torusUtils : TorusUtils) { + self.torusUtils = torusUtils + } + /// Instantiate a `ThresholdKey` object, /// /// - Parameters: @@ -108,7 +141,7 @@ public class ThresholdKey { let ptr = withUnsafeMutablePointer(to: &device_index, { tssDeviceIndexPointer in withUnsafeMutablePointer(to: &errorCode, { error in threshold_key_initialize(self.pointer, keyPointer, storePtr, neverInitializeNewKey, includeLocalMetadataTransitions, false, curvePointer, useTss, nil, tssDeviceIndexPointer, nil, error) }) }) guard errorCode == 0 else { - throw RuntimeError("Error in ThresholdKey Initialize") + throw RuntimeError("Error in ThresholdKey Initialize \(errorCode)") } let result = try! KeyDetails(pointer: ptr!) completion(.success(result)) @@ -524,7 +557,7 @@ public class ThresholdKey { threshold_key_input_factor_key(self.pointer, cFactorKey, error) }) guard errorCode == 0 else { - throw RuntimeError("Error in ThresholdKey input_factor_key") + throw RuntimeError("Error in ThresholdKey input_factor_key \(errorCode)") } completion(.success(())) } catch { @@ -554,6 +587,48 @@ public class ThresholdKey { } } + +// private func patch_input_factor_key(factorKey: String, completion: @escaping (Result) -> Void) { +// tkeyQueue.async { +// do { +// var errorCode: Int32 = -1 +// let cFactorKey = UnsafeMutablePointer(mutating: (factorKey as NSString).utf8String) +// let curvePointer = UnsafeMutablePointer(mutating: (self.curveN as NSString).utf8String) +// +// withUnsafeMutablePointer(to: &errorCode, { error in +// threshold_key_patch_input_factor_key(self.pointer, cFactorKey, curvePointer, error) +// }) +// guard errorCode == 0 else { +// throw RuntimeError("Error in ThresholdKey input_factor_key \(errorCode)") +// } +// completion(.success(())) +// } catch { +// completion(.failure(error)) +// } +// } +// } + + /// Patch and Inserts a `ShareStore` into `ThresholdKey` using `FactorKey`, useful for insertion before reconstruction to ensure the number of shares meet the minimum threshold. + /// + /// - Parameters: + /// - factorKey : The `factorKey` to be inserted + /// + /// - Throws: `RuntimeError`, indicates invalid parameters or invalid `ThresholdKey`. +// public func patch_input_factor_key(factorKey: String) async throws { +// return try await withCheckedThrowingContinuation { +// continuation in +// self.patch_input_factor_key(factorKey: factorKey) { +// result in +// switch result { +// case let .success(result): +// continuation.resume(returning: result) +// case let .failure(error): +// continuation.resume(throwing: error) +// } +// } +// } +// } + /// Retrieves all share indexes for a `ThresholdKey`. /// /// - Returns: Array of String @@ -716,6 +791,78 @@ public class ThresholdKey { let json = try! JSONSerialization.jsonObject(with: string.data(using: .utf8)!, options: .allowFragments) as! [String: Any] return json } + + + /// set the general store domain. + /// + /// - Parameters: + /// - key: key domain to be stored + /// - data: json string data t be stored + /// + /// - Throws: `RuntimeError`, indicates invalid parameters or invalid `ThresholdKey`. + public func set_general_store_domain( key: String, data: String) throws { + var errorCode: Int32 = -1 + let keyPointer = UnsafeMutablePointer(mutating: (key as NSString).utf8String) + + let dataPointer = UnsafeMutablePointer(mutating: (data as NSString).utf8String) + + withUnsafeMutablePointer(to: &errorCode, { error in + threshold_key_set_general_store_domain(pointer, keyPointer, dataPointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey set_domain_store_item : error Code : \(errorCode)") + } + } + + + + /// Returns the general store domain. + /// + /// - Parameters: + /// - key: key domain stored + /// + /// - Returns: `String` json_string + /// + /// - Throws: `RuntimeError`, indicates invalid parameters or invalid `ThresholdKey`. + public func get_general_store_domain(key: String) throws -> String { + var errorCode: Int32 = -1 + let keyPointer = UnsafeMutablePointer(mutating: (key as NSString).utf8String) + + let result = withUnsafeMutablePointer(to: &errorCode, { error in + threshold_key_get_general_store_domain(pointer, keyPointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey get_domain_store_item \(errorCode)") + } + let string = String(cString: result!) + string_free(result) + + return string + } + + + /// delete the general store domain. + /// + /// - Parameters: + /// - key: key domain to be deleted + /// + /// - Returns: `String` json_string + /// + /// - Throws: `RuntimeError`, indicates invalid parameters or invalid `ThresholdKey`. + public func delete_general_store_domain(key: String) throws { + var errorCode: Int32 = -1 + let keyPointer = UnsafeMutablePointer(mutating: (key as NSString).utf8String) + + let result = withUnsafeMutablePointer(to: &errorCode, { error in + threshold_key_get_general_store_domain(pointer, keyPointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey get_domain_store_item") + } + let string = String(cString: result!) + string_free(result) + } + /// Returns all shares according to their mapping. /// @@ -753,6 +900,26 @@ public class ThresholdKey { } } } + + private func sync_metadata(completion: @escaping (Result) -> Void) { + tkeyQueue.async { + do { + var errorCode: Int32 = -1 + + let curvePointer = UnsafeMutablePointer(mutating: NSString(string: self.curveN).utf8String) + + withUnsafeMutablePointer(to: &errorCode, { error in + threshold_key_sync_metadata(self.pointer, curvePointer, error) + }) + guard errorCode == 0 else { + throw RuntimeError("Error in ThresholdKey sync_local_metadata_transistions") + } + completion(.success(())) + } catch { + completion(.failure(error)) + } + } + } /// Syncronises metadata transitions, only used if manual sync is enabled. /// @@ -771,6 +938,24 @@ public class ThresholdKey { } } } + + /// create metadata transitions (to all shares), sync to server if manual sync is false. + /// + /// - Throws: `RuntimeError`, indicates invalid parameters or invalid `ThresholdKey`. + public func sync_metadata() async throws { + return try await withCheckedThrowingContinuation { + continuation in + self.sync_metadata { + result in + switch result { + case let .success(result): + continuation.resume(returning: result) + case let .failure(error): + continuation.resume(throwing: error) + } + } + } + } /// Returns all shares descriptions. /// diff --git a/Sources/libtkey/include/tkey.h b/Sources/libtkey/include/tkey.h index 809cda3..50e79ed 100644 --- a/Sources/libtkey/include/tkey.h +++ b/Sources/libtkey/include/tkey.h @@ -30,7 +30,6 @@ struct TssOptions; struct NodeDetails; struct FFIRssComm; - //Methods char* get_version(int* error_code); void string_free(char *ptr); @@ -80,11 +79,16 @@ void generate_share_store_result_free(struct GenerateShareStoreResult* ptr); void share_store_poly_id_index_map_free(struct ShareStorePolyIDShareIndexMap* ptr); struct GenerateShareStoreResult* threshold_key_generate_share(struct FFIThresholdKey* threshold_key, char* curve_n, bool use_tss, struct TssOptions* tss_options, int* error_code); + void threshold_key_import_tss_key(struct FFIThresholdKey* threshold_key, bool update_metadata, char* tss_tag, char* import_key, int new_tss_index, struct KeyPoint* factor_pub, char* selected_servers, char* auth_signatures, char* curve_n, int* error_code); void threshold_key_delete_share(struct FFIThresholdKey* threshold_key, char* share_index, char* curve_n, bool use_tss, struct TssOptions* tss_options, int* error_code); void threshold_key_delete_tkey(struct FFIThresholdKey* threshold_key, char* curve_n, int* error_code); char* threshold_key_output_share(struct FFIThresholdKey* threshold_key, char* share_index, char* share_type, char* curve_n, int* error_code); char* threshold_key_get_tkey_store(struct FFIThresholdKey* threshold_key, char* module_name, int* error_code); char* threshold_key_get_tkey_store_item(struct FFIThresholdKey* threshold_key, char* module_name, char* identifier, int* error_code); + void threshold_key_set_general_store_domain(struct FFIThresholdKey* threshold_key, char* key, char* json_data, int* error_code); + char* threshold_key_get_general_store_domain(struct FFIThresholdKey* threshold_key, char* key, int* error_code); + + void threshold_key_input_share(struct FFIThresholdKey* threshold_key, char* share, char* share_type, char* curve_n, int* error_code); struct ShareStore* threshold_key_output_share_store(struct FFIThresholdKey* threshold_key, char* share_index, char* poly_id, char* curve_n, int* error_code); void threshold_key_input_share_store(struct FFIThresholdKey* threshold_key, struct ShareStore* share_store, int* error_code); @@ -97,6 +101,7 @@ struct Polynomial* threshold_key_reconstruct_latest_poly(struct FFIThresholdKey *threshold_key, char* curve_n, int* error_code); struct Metadata* threshold_key_get_last_fetched_cloud_metadata(struct FFIThresholdKey* threshold_key, int* error_code); void threshold_key_sync_local_metadata_transitions(struct FFIThresholdKey *threshold_key, char* curve_n, int* error_code); + void threshold_key_sync_metadata(struct FFIThresholdKey *threshold_key, char* curve_n, int* error_code); struct ShareStoreArray* threshold_key_get_all_share_stores_for_latest_polynomial(struct FFIThresholdKey* threshold_key, char* curve_n, int* error_code); struct ShareStorePolyIDShareIndexMap* threshold_key_get_shares(struct FFIThresholdKey* threshold_key, int* error_code); @@ -199,10 +204,16 @@ // TssOptions struct TssOptions* tss_options(char* input_tss_share, int input_tss_index, struct KeyPoint* factor_pub, char* auth_signatures, char* selected_servers, int* new_tss_index, struct KeyPoint* new_factor_pub, int* error_code); void tss_options_free(struct TssOptions* ptr); - //NodeDetails + //Module: tss security-question + void tss_security_question_set_security_question(struct FFIThresholdKey* threshold_key, char* factor_key, char* questions, char* answer, unsigned int tss_index, char* selected_servers, char* auth_signatures, char* curve_n, int* error_code); + char* tss_security_question_recover_factor(struct FFIThresholdKey* threshold_key, char* answer, int* error_code); + void tss_security_question_change_question(struct FFIThresholdKey* threshold_key, char* new_answer, char* new_questions, char* answer, char* selected_servers, char* auth_signatures, char* curve_n, int* error_code); + char* tss_security_question_delete_security_question(struct FFIThresholdKey* threshold_key, char* factor_key, char* answer, char* selected_servers, char* auth_signatures, char* curve_n, int* error_code); + char* tss_security_question_get_question(struct FFIThresholdKey* threshold_key, int* error_code); + // NodeDetails struct NodeDetails* node_details(char* server_endpoints, char* server_public_keys, int server_threshold, int* error_code); void node_details_free(struct NodeDetails* ptr); - //RssComm + // RssComm struct FFIRSSComm* rss_comm(char* (*network_callback)(char*, char*, void*, int*), void* parent_instance_ref, int* error_code); void* rss_comm_free(struct FFIRssComm* ptr); #ifdef __cplusplus diff --git a/Sources/libtkey/libtkey.xcframework/Info.plist b/Sources/libtkey/libtkey.xcframework/Info.plist index 51ab6cd..4957386 100644 --- a/Sources/libtkey/libtkey.xcframework/Info.plist +++ b/Sources/libtkey/libtkey.xcframework/Info.plist @@ -5,6 +5,8 @@ AvailableLibraries + BinaryPath + libtkey.a LibraryIdentifier ios-arm64_x86_64-simulator LibraryPath @@ -20,16 +22,19 @@ simulator + BinaryPath + libtkey.a LibraryIdentifier - ios-arm64 + macos-arm64_x86_64 LibraryPath libtkey.a SupportedArchitectures arm64 + x86_64 SupportedPlatform - ios + macos CFBundlePackageType diff --git a/Sources/libtkey/libtkey.xcframework/ios-arm64_x86_64-simulator/libtkey.a b/Sources/libtkey/libtkey.xcframework/ios-arm64_x86_64-simulator/libtkey.a index ab241f2..3483f56 100644 Binary files a/Sources/libtkey/libtkey.xcframework/ios-arm64_x86_64-simulator/libtkey.a and b/Sources/libtkey/libtkey.xcframework/ios-arm64_x86_64-simulator/libtkey.a differ diff --git a/Sources/libtkey/libtkey.xcframework/ios-arm64/libtkey.a b/Sources/libtkey/libtkey.xcframework/macos-arm64_x86_64/libtkey.a similarity index 50% rename from Sources/libtkey/libtkey.xcframework/ios-arm64/libtkey.a rename to Sources/libtkey/libtkey.xcframework/macos-arm64_x86_64/libtkey.a index 4d89a2b..17050be 100644 Binary files a/Sources/libtkey/libtkey.xcframework/ios-arm64/libtkey.a and b/Sources/libtkey/libtkey.xcframework/macos-arm64_x86_64/libtkey.a differ diff --git a/Tests/tkeypkgTests/IntegrationTests.swift b/Tests/tkeypkgTests/IntegrationTests.swift index c7e8ca6..d13f896 100644 --- a/Tests/tkeypkgTests/IntegrationTests.swift +++ b/Tests/tkeypkgTests/IntegrationTests.swift @@ -37,6 +37,11 @@ final class integrationTests: XCTestCase { manual_sync: false, rss_comm: rss_comm ) + + // setting variables needed for tss operations + threshold.setAuthSignatures(authSignatures: signatures) + threshold.setnodeDetails(nodeDetails: nodeDetail) + threshold.setTorusUtils(torusUtils: torusUtils) _ = try! await threshold.initialize() _ = try! await threshold.reconstruct() @@ -48,7 +53,7 @@ final class integrationTests: XCTestCase { let factorPub = try factorKey.toPublic() try TssModule.backup_share_with_factor_key(threshold_key: threshold, shareIndex: shareIndex.hex, factorKey: factorKey.hex) - try await TssModule.create_tagged_tss_share(threshold_key: threshold, tss_tag: tssTag, deviceTssShare: nil, factorPub: factorPub, deviceTssIndex: 2, nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.create_tagged_tss_share(threshold_key: threshold, tss_tag: tssTag, deviceTssShare: nil, factorPub: factorPub, deviceTssIndex: 2 ) let (tss_index, tss_share) = try await TssModule.get_tss_share(threshold_key: threshold, tss_tag: tssTag, factorKey: factorKey.hex) @@ -57,7 +62,7 @@ final class integrationTests: XCTestCase { let newFactorKey = try PrivateKey.generate() let newFactorPub = try newFactorKey.toPublic() // 2/2 -> 2/3 tss - try await TssModule.generate_tss_share(threshold_key: threshold, tss_tag: tssTag, input_tss_share: tss_share, tss_input_index: Int32(tss_index)!, auth_signatures: signatures, new_factor_pub: newFactorPub, new_tss_index: 3, nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.generate_tss_share(threshold_key: threshold, tss_tag: tssTag, input_tss_share: tss_share, tss_input_index: Int32(tss_index)!, new_factor_pub: newFactorPub, new_tss_index: 3) let (tss_index3, tss_share3) = try await TssModule.get_tss_share(threshold_key: threshold, tss_tag: tssTag, factorKey: newFactorKey.hex) let (_, tss_share_updated) = try await TssModule.get_tss_share(threshold_key: threshold, tss_tag: tssTag, factorKey: factorKey.hex) @@ -73,6 +78,12 @@ final class integrationTests: XCTestCase { enable_logging: true, manual_sync: false ) + + // setting variables needed for tss operations + threshold2.setAuthSignatures(authSignatures: signatures) + threshold2.setnodeDetails(nodeDetails: nodeDetail) + threshold2.setTorusUtils(torusUtils: torusUtils) + _ = try! await threshold2.initialize() try await threshold2.input_factor_key(factorKey: factorKey.hex) @@ -91,7 +102,7 @@ final class integrationTests: XCTestCase { XCTAssertEqual(tss_index3, tss_index2_3) // 2/3 -> 2/2 tss - try await TssModule.delete_tss_share(threshold_key: threshold, tss_tag: tssTag, input_tss_share: tss_share3, tss_input_index: Int32(tss_index3)!, auth_signatures: signatures, delete_factor_pub: newFactorPub, nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.delete_tss_share(threshold_key: threshold, tss_tag: tssTag, input_tss_share: tss_share3, tss_input_index: Int32(tss_index3)!, delete_factor_pub: newFactorPub) // XCTAssertThrowsError( try await TssModule.get_tss_share(threshold_key: threshold, tss_tag: tssTag, factorKey: newFactorKey.hex) ) let (tss_index_updated2, tss_share_updated2) = try await TssModule.get_tss_share(threshold_key: threshold, tss_tag: tssTag, factorKey: factorKey.hex) @@ -101,10 +112,10 @@ final class integrationTests: XCTestCase { XCTAssertNotEqual(tss_share_updated, tss_share_updated2) // 2/2 -> 2/3 tss - try await TssModule.add_factor_pub(threshold_key: threshold, tss_tag: tssTag, factor_key: factorKey.hex, auth_signatures: signatures, new_factor_pub: newFactorPub, new_tss_index: 3, nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.add_factor_pub(threshold_key: threshold, tss_tag: tssTag, factor_key: factorKey.hex, new_factor_pub: newFactorPub, new_tss_index: 3) // 2/3 -> 2/2 tss - try await TssModule.delete_factor_pub(threshold_key: threshold, tss_tag: tssTag, factor_key: factorKey.hex, auth_signatures: signatures, delete_factor_pub: newFactorPub, nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.delete_factor_pub(threshold_key: threshold, tss_tag: tssTag, factor_key: factorKey.hex, delete_factor_pub: newFactorPub ) } func test_TssModule_multi_tag() async throws { @@ -142,6 +153,11 @@ final class integrationTests: XCTestCase { manual_sync: true, rss_comm: rss_comm ) + + // setting variables needed for tss operations + threshold.setAuthSignatures(authSignatures: signatures) + threshold.setnodeDetails(nodeDetails: nodeDetail) + threshold.setTorusUtils(torusUtils: torusUtils) _ = try! await threshold.initialize() _ = try! await threshold.reconstruct() @@ -149,7 +165,7 @@ final class integrationTests: XCTestCase { let share = try threshold.output_share(shareIndex: shareIndex.hex) print(share) - let testTags = ["tag1", "tag2", "tag3", "tag4", "tag5"] + let testTags = ["tag1", "tag2", "tag3", "tag4"] var tssMods: [(ThresholdKey, String)] = [] @@ -171,7 +187,7 @@ final class integrationTests: XCTestCase { factorPubs.append(factorPub) try await TssModule.set_tss_tag(threshold_key: threshold, tss_tag: tag) - try await TssModule.create_tagged_tss_share(threshold_key: threshold, tss_tag: tag, deviceTssShare: nil, factorPub: factorPub, deviceTssIndex: 2, nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.create_tagged_tss_share(threshold_key: threshold, tss_tag: tag, deviceTssShare: nil, factorPub: factorPub, deviceTssIndex: 2) let (tssIndex, tssShare) = try! await TssModule.get_tss_share(threshold_key: threshold, tss_tag: tag, factorKey: factorKey.hex) tssIndexes.append(tssIndex) @@ -188,7 +204,7 @@ final class integrationTests: XCTestCase { newFactorKeys.append(newFactorKey) newFactorPubs.append(newFactorPub) - try await TssModule.add_factor_pub(threshold_key: threshold, tss_tag: tag, factor_key: factorKeys[index].hex, auth_signatures: signatures, new_factor_pub: newFactorPub, new_tss_index: 3, nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.add_factor_pub(threshold_key: threshold, tss_tag: tag, factor_key: factorKeys[index].hex, new_factor_pub: newFactorPub, new_tss_index: 3) try await threshold.sync_local_metadata_transistions() @@ -235,6 +251,11 @@ final class integrationTests: XCTestCase { enable_logging: true, manual_sync: false ) + // setting variables needed for tss operations + threshold2.setAuthSignatures(authSignatures: signatures) + threshold2.setnodeDetails(nodeDetails: nodeDetail) + threshold2.setTorusUtils(torusUtils: torusUtils) + _ = try! await threshold2.initialize() try! await threshold2.input_share(share: share, shareType: nil) _ = try! await threshold2.reconstruct() @@ -255,7 +276,7 @@ final class integrationTests: XCTestCase { newFactorKeys2.append(newFactorKey2) newFactorPubs2.append(newFactorPub2) - try await TssModule.delete_factor_pub(threshold_key: threshold, tss_tag: tag, factor_key: newFactorKeys[index].hex, auth_signatures: signatures, delete_factor_pub: newFactorPubs[index], nodeDetails: nodeDetail, torusUtils: torusUtils) + try await TssModule.delete_factor_pub(threshold_key: threshold, tss_tag: tag, factor_key: newFactorKeys[index].hex, delete_factor_pub: newFactorPubs[index]) } try await threshold.sync_local_metadata_transistions() print(try threshold.get_all_tss_tags()) diff --git a/Tests/tkeypkgTests/modules/tkey_pkgTssSecurityQuestionModuleTests.swift b/Tests/tkeypkgTests/modules/tkey_pkgTssSecurityQuestionModuleTests.swift new file mode 100644 index 0000000..3b94ca3 --- /dev/null +++ b/Tests/tkeypkgTests/modules/tkey_pkgTssSecurityQuestionModuleTests.swift @@ -0,0 +1,227 @@ +import XCTest +import Foundation +@testable import tkey_pkg +import Foundation +import TorusUtils +import CommonSources +import FetchNodeDetails + +final class tkey_pkgTssSecurityQuestionModuleTests: XCTestCase { + private var threshold_key: ThresholdKey! + private var storage_layer: StorageLayer! + private var service_provider: ServiceProvider! + + override func setUp() async throws { + let TORUS_TEST_EMAIL = "saasa2123@tr.us" + let TORUS_TEST_VERIFIER = "torus-test-health" + + let nodeManager = NodeDetailManager(network: .sapphire(.SAPPHIRE_DEVNET)) + let nodeDetail = try await nodeManager.getNodeDetails(verifier: TORUS_TEST_VERIFIER, verifierID: TORUS_TEST_EMAIL) + let torusUtils = TorusUtils(serverTimeOffset: 1000, network: .sapphire(.SAPPHIRE_DEVNET)) + + let idToken = try generateIdToken(email: TORUS_TEST_EMAIL) + let verifierParams = VerifierParams(verifier_id: TORUS_TEST_EMAIL) + let retrievedShare = try await torusUtils.retrieveShares(endpoints: nodeDetail.torusNodeEndpoints, torusNodePubs: nodeDetail.torusNodePub, indexes: nodeDetail.torusIndexes, verifier: TORUS_TEST_VERIFIER, verifierParams: verifierParams, idToken: idToken) + let signature = retrievedShare.sessionData?.sessionTokenData + let signatures = signature!.compactMap { item in + item?.signature + } + + + let postbox_key = try! PrivateKey.generate() + let tssEndpoint0 = nodeDetail.torusNodeTSSEndpoints[0] + let metadataEndpoint = tssEndpoint0.replacingOccurrences(of: "/tss", with: "") + "/metadata" + let storage_layer_local = try! StorageLayer(enable_logging: true, host_url: metadataEndpoint, server_time_offset: 2) + let service_provider_local = try! ServiceProvider(enable_logging: true, postbox_key: postbox_key.hex, verifier: TORUS_TEST_VERIFIER, verifierId: TORUS_TEST_EMAIL, nodeDetails: nodeDetail) + let rss_comm = try! RssComm() + let threshold = try! ThresholdKey( + storage_layer: storage_layer_local, + service_provider: service_provider_local, + enable_logging: true, + manual_sync: false, + rss_comm: rss_comm + ) + + _ = try! await threshold.initialize() + + // setting variables needed for tss operations + threshold.setAuthSignatures(authSignatures: signatures) + threshold.setnodeDetails(nodeDetails: nodeDetail) + threshold.setTorusUtils(torusUtils: torusUtils) + + threshold_key = threshold + service_provider = service_provider_local + storage_layer = storage_layer_local + } + + override func tearDown() { + threshold_key = nil + + } + + func test() async throws { + let _ = try! await threshold_key.reconstruct() + let question = "favorite marvel character" + let question2 = "favorite villian character" + let answer = "iron man" + let answer_2 = "captain america" + let factor_key = try! PrivateKey.generate() + let factor_pub = try factor_key.toPublic(format: .EllipticCompress) + var allIndex = try! threshold_key.get_shares_indexes() + let tag = "special" + allIndex.removeAll(where: {$0 == "1"}) + + try await TssModule.create_tagged_tss_share(threshold_key: threshold_key, tss_tag: "special", deviceTssShare: nil, factorPub: factor_pub, deviceTssIndex: 2) + + try! TssModule.backup_share_with_factor_key(threshold_key: threshold_key, shareIndex: allIndex[0], factorKey: factor_key.hex) + + try await TssModule.set_tss_tag(threshold_key: threshold_key, tss_tag: tag) + let sq_factor = try await TssSecurityQuestionModule.set_security_question(threshold: threshold_key, question: question, answer: answer,factorKey: factor_key.hex, selectedServer: "[1,2,3]", tag: "special") + + + do { + let _ = try await TssSecurityQuestionModule.set_security_question(threshold: threshold_key, question: question, answer: answer_2, factorKey: factor_key.hex, selectedServer: "[1,2,3]", tag: "special") + XCTFail("Should not able to set quesetion twice") + } catch {} + + let questionReturn = try? await TssSecurityQuestionModule.get_question(threshold: threshold_key, tag: "special") + XCTAssertEqual(questionReturn, question) + + let factor = try await TssSecurityQuestionModule.recover_factor(threshold: threshold_key, answer: answer, tag: "special") + do { + let _ = try await TssSecurityQuestionModule.recover_factor(threshold: threshold_key, answer: answer_2, tag: "special") + XCTFail("Should be able to get factor using incorrect answer") + } catch {} + try await threshold_key.input_factor_key(factorKey: factor) + // check for valid tss share + let _ = try await TssModule.get_tss_share(threshold_key: threshold_key, tss_tag: "special", factorKey: factor) + + XCTAssertEqual(String(factor.suffix(64)), sq_factor) + + // delete security question and add new security question + let factorPubDeleted = try await TssSecurityQuestionModule.delete_security_question(threshold: threshold_key, tag: "special", factorKey: factor_key.hex) + XCTAssertEqual(factorPubDeleted, try PrivateKey(hex: sq_factor).toPublic(format: .EllipticCompress)) + + + do { + let _ = try await TssSecurityQuestionModule.get_question(threshold: threshold_key, tag: "special") + XCTFail("Should not able get question after delete") + }catch{} + do { + let _ = try await TssSecurityQuestionModule.recover_factor(threshold: threshold_key, answer: answer, tag: "special") + XCTFail("Should not able get question after delete") + }catch{} + + // able to set new question and answer + let sq_factor2 = try await TssSecurityQuestionModule.set_security_question(threshold: threshold_key, question: question2, answer: answer_2, factorKey: factor_key.hex, selectedServer: "[]", tag: "special") + + let questionReturn2 = try await TssSecurityQuestionModule.get_question(threshold: threshold_key, tag: "special") + XCTAssertEqual(questionReturn2, question2) + + let factor2 = try await TssSecurityQuestionModule.recover_factor(threshold: threshold_key, answer: answer_2, tag: "special") + + do { + let _ = try await TssSecurityQuestionModule.recover_factor(threshold: threshold_key, answer: answer, tag: "special") + XCTFail("Should be able to get factor using incorrect answer") + } catch {} + + XCTAssertEqual(String(factor2.suffix(64)), sq_factor2) + + // change answer and security question + do { + let _ = try await TssSecurityQuestionModule.change_security_question(threshold: threshold_key, newQuestion: question, newAnswer: answer, answer: answer, tag: "special") + XCTFail("Should be not able to change sq using incorrect answer") + } catch {} + + let (old_sq_factor, new_sq_factor) = try await TssSecurityQuestionModule.change_security_question(threshold: threshold_key, newQuestion: question, newAnswer: answer, answer: answer_2, tag: "special") + + XCTAssertEqual(old_sq_factor, sq_factor2) + + do { + let _ = try await TssSecurityQuestionModule.recover_factor(threshold: threshold_key, answer: answer_2, tag: "special") + XCTFail("Should be not able to get factor using incorrect answer") + } catch {} + + let questionChanged = try await TssSecurityQuestionModule.get_question(threshold: threshold_key, tag: "special") + let factorChanged = try await TssSecurityQuestionModule.recover_factor(threshold: threshold_key, answer: answer, tag: "special") + + XCTAssertEqual(String(factorChanged.suffix(64)), new_sq_factor) + XCTAssertEqual(questionChanged, question) + + + + let newThreshold = try! ThresholdKey( + storage_layer: storage_layer, + service_provider: service_provider, + enable_logging: true, + manual_sync: false + ) + + let _ = try await newThreshold.initialize(); + + let newInstanceQuestion = try await TssSecurityQuestionModule.get_question(threshold: newThreshold, tag: "special") + let newInstanceFactor = try await TssSecurityQuestionModule.recover_factor(threshold: newThreshold, answer: answer, tag: "special") + + try await newThreshold.input_factor_key(factorKey: newInstanceFactor) + try await newThreshold.input_factor_key(factorKey: newInstanceFactor) + let _ = try await newThreshold.reconstruct() + + XCTAssertEqual(String(newInstanceFactor.suffix(64)), new_sq_factor) + XCTAssertEqual(newInstanceQuestion, question) + } + + func test_js_compatible () async throws { + + let TORUS_TEST_EMAIL = "testing2001@example.com" + let TORUS_TEST_VERIFIER = "torus-test-health" + + let nodeManager = NodeDetailManager(network: .sapphire(.SAPPHIRE_DEVNET)) + let nodeDetail = try await nodeManager.getNodeDetails(verifier: TORUS_TEST_VERIFIER, verifierID: TORUS_TEST_EMAIL) + let torusUtils = TorusUtils(serverTimeOffset: 2, network: .sapphire(.SAPPHIRE_DEVNET)) + + let idToken = try generateIdToken(email: TORUS_TEST_EMAIL) + let verifierParams = VerifierParams(verifier_id: TORUS_TEST_EMAIL) + let retrievedShare = try await torusUtils.retrieveShares(endpoints: nodeDetail.torusNodeEndpoints, torusNodePubs: nodeDetail.torusNodePub, indexes: nodeDetail.torusIndexes, verifier: TORUS_TEST_VERIFIER, verifierParams: verifierParams, idToken: idToken) + let signature = retrievedShare.sessionData?.sessionTokenData + let signatures = signature!.compactMap { item in + item?.signature + } + + guard let postbox = retrievedShare.oAuthKeyData?.privKey else { + throw "invalid postbox key" + } + print(postbox) + let service_provider_local = try! ServiceProvider(enable_logging: true, postbox_key: postbox , verifier: TORUS_TEST_VERIFIER, verifierId: TORUS_TEST_EMAIL, nodeDetails: nodeDetail) + let rss_comm = try RssComm() + let threshold = try! ThresholdKey( + storage_layer: storage_layer, + service_provider: service_provider_local, + enable_logging: true, + manual_sync: false, + rss_comm: rss_comm + ) + + _ = try! await threshold.initialize() + + // setting variables needed for tss operations + threshold.setAuthSignatures(authSignatures: signatures) + threshold.setnodeDetails(nodeDetails: nodeDetail) + threshold.setTorusUtils(torusUtils: torusUtils) +// threshold.init + print( try threshold.get_key_details().pub_key.getPublicKey(format: .EllipticCompress)) + let factorKey = "36c1728c47c84dfe855949fa76daf82f8bda801af9374f30aa4c91b7fd7a8e3b" + let answer = "jsanswer" + let question = "js question" + + let questionResult = try await TssSecurityQuestionModule.get_question(threshold: threshold, tag: "default") + print(questionResult) + let factor = try await TssSecurityQuestionModule.recover_factor(threshold: threshold, answer: answer, tag: "default") + print(factor) + + try await threshold.input_factor_key(factorKey: factor) + try await threshold.reconstruct() + let (tssIndex, tssShare) = try await TssModule.get_tss_share(threshold_key: threshold, tss_tag: "default", factorKey: factor) + print (tssIndex) + print (tssShare) + } +}