Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/tss sq rust #18

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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"),
Expand Down
44 changes: 44 additions & 0 deletions Sources/ThresholdKey/Common/helper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// File.swift
//
//
// Created by CW Lee on 26/09/2023.
//

import Foundation


func selectedServerToPointer (selectedServers: [UInt32]?) throws -> UnsafeMutablePointer<Int8>? {
guard let selectedServers = selectedServers else {
return nil
}
var serversPointer: UnsafeMutablePointer<Int8>?

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<Int8>(mutating: (selected_servers_str as NSString).utf8String)

guard let serversPointer = serversPointer else {
throw RuntimeError("convert error")
}
return serversPointer
}

func authSignaturesToPointer ( authSignatures : [String]?) throws -> UnsafeMutablePointer<Int8>? {
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<Int8>(mutating: (auth_signatures_str as NSString).utf8String)


return authSignaturesPointer
}
Comment on lines +11 to +43
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These pointers are probably not valid once the function returns, the memory locations they're assigned are local to this function.

Suggested change
func selectedServerToPointer (selectedServers: [UInt32]?) throws -> UnsafeMutablePointer<Int8>? {
guard let selectedServers = selectedServers else {
return nil
}
var serversPointer: UnsafeMutablePointer<Int8>?
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<Int8>(mutating: (selected_servers_str as NSString).utf8String)
guard let serversPointer = serversPointer else {
throw RuntimeError("convert error")
}
return serversPointer
}
func authSignaturesToPointer ( authSignatures : [String]?) throws -> UnsafeMutablePointer<Int8>? {
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<Int8>(mutating: (auth_signatures_str as NSString).utf8String)
return authSignaturesPointer
}


102 changes: 67 additions & 35 deletions Sources/ThresholdKey/Modules/TssModule.swift

Large diffs are not rendered by default.

261 changes: 261 additions & 0 deletions Sources/ThresholdKey/Modules/TssSecurityQuestionModule.swift
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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<Int8>(mutating: (factorKey as NSString).utf8String)
let questionPtr = UnsafeMutablePointer<Int8>(mutating: (question as NSString).utf8String)
let hashPtr = UnsafeMutablePointer<Int8>(mutating: (hash as NSString).utf8String)
let curvePointer = UnsafeMutablePointer<Int8>(mutating: (threshold.curveN as NSString).utf8String)



let auth_signatures_json = try JSONSerialization.data(withJSONObject: threshold.authSignatures)
Copy link
Collaborator

@metalurgical metalurgical Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should do a guard on threshold.authSignatures to resolve the warning here. Same for the places.

guard let auth_signatures_str = String(data: auth_signatures_json, encoding: .utf8) else {
throw RuntimeError("auth signatures error")
}
let authSignaturesPointer = UnsafeMutablePointer<Int8>(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<Int8>(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<Int8>(mutating: (newQuestion as NSString).utf8String)
let hashPtr = UnsafeMutablePointer<Int8>(mutating: (hash as NSString).utf8String)
let newHashPtr = UnsafeMutablePointer<Int8>(mutating: (newHash as NSString).utf8String)
let curvePointer = UnsafeMutablePointer<Int8>(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<Int8>(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<Int8>(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<Int8>(mutating: (factorKey as NSString).utf8String)
var hashPtr : UnsafeMutablePointer<Int8>?;
if let answer = answer {
let hash = try self.compute_hash(threshold: threshold, answer: answer, tag: tag)
hashPtr = UnsafeMutablePointer<Int8>(mutating: (hash as NSString).utf8String)
}
let curvePointer = UnsafeMutablePointer<Int8>(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<Int8>(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<Int8>(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<Int8>(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!)
Copy link
Collaborator

@metalurgical metalurgical Oct 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unused. Why not use result instead of having to call compute_hash?

Suggested change
let hashOut = String(cString: result!)

string_free(result)

return hash
}
}
42 changes: 20 additions & 22 deletions Sources/ThresholdKey/StorageLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand Down Expand Up @@ -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)
Expand Down
Loading