Skip to content

Commit

Permalink
Merge pull request #57 from torusresearch/alpha
Browse files Browse the repository at this point in the history
Release 6.0.1
  • Loading branch information
himanshuchawla009 authored Sep 1, 2023
2 parents c3b8fee + 02707f0 commit 61b3668
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 62 deletions.
129 changes: 82 additions & 47 deletions Sources/TorusUtils/Extensions/TorusUtils+extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ extension TorusUtils {

private func getShareOrKeyAssign(endpoints: [String], nodeSigs: [CommitmentRequestResponse], verifier: String, verifierParams: VerifierParams, idToken: String, extraParams: [String: Any] = [:]) async throws -> [URLRequest] {
let session = createURLSession()
let threshold = Int(endpoints.count / 2) + 1
let threshold = Int(endpoints.count / 2) + 1
var rpcdata: Data = Data()

let loadedStrings = extraParams
Expand Down Expand Up @@ -230,6 +230,40 @@ extension TorusUtils {



private func reconstructKey(decryptedShares: [Int: String], thresholdPublicKey: KeyAssignment.PublicKey) throws -> String? {

// run lagrange interpolation on all subsets, faster in the optimistic scenario than berlekamp-welch due to early exit
let allCombis = kCombinations(s: decryptedShares.count, k: 3)
var returnedKey: String? = nil

for j in 0..<allCombis.count {
let currentCombi = allCombis[j]
let currentCombiShares = decryptedShares.enumerated().reduce(into: [ Int : String ]()) { acc, current in
let (index, curr) = current
if (currentCombi.contains(index)) {
acc[curr.key] = curr.value
}
}
let derivedPrivateKey = try lagrangeInterpolation(shares: currentCombiShares, offset: 0)
let derivedPrivateKeyHex = derivedPrivateKey

guard Data(hexString: derivedPrivateKeyHex) != nil else {
continue
}
let decryptedPubKey = SECP256K1.privateToPublic(privateKey: Data(hex: derivedPrivateKeyHex.addLeading0sForLength64()) )?.toHexString()
print("decryptedPubKey", decryptedPubKey, derivedPrivateKey)
let decryptedPubKeyX = String(decryptedPubKey!.suffix(128).prefix(64))
let decryptedPubKeyY = String(decryptedPubKey!.suffix(64))
if decryptedPubKeyX == thresholdPublicKey.X.addLeading0sForLength64() && decryptedPubKeyY == thresholdPublicKey.Y.addLeading0sForLength64() {
returnedKey = derivedPrivateKey
break
}
}

return returnedKey

}


// MARK: - retrieveShare
// TODO: add importShare functionality later
Expand Down Expand Up @@ -278,6 +312,7 @@ extension TorusUtils {

var thresholdNonceData : GetOrSetNonceResult?
var pubkeyArr = [KeyAssignment.PublicKey]()
var isNewKeyArr: [String] = []
var completeShareRequestResponseArr = [ShareRequestResult]()
var thresholdPublicKey : KeyAssignment.PublicKey?

Expand Down Expand Up @@ -314,6 +349,7 @@ extension TorusUtils {
let decodedResult = decoded.result as? ShareRequestResult
else { throw TorusUtilError.decodingFailed("ShareReqeust error decoding error : \(decoded), can't decode into shareRequestResult") }

isNewKeyArr.append(decodedResult.isNewKey)
completeShareRequestResponseArr.append(decodedResult)
let keyObj = decodedResult.keys
if let first = keyObj.first {
Expand Down Expand Up @@ -360,19 +396,26 @@ extension TorusUtils {
throw TorusUtilError.thresholdError
})


// optimistically run lagrange interpolation once threshold number of shares have been received
// this is matched against the user public key to ensure that shares are consistent
// Note: no need of thresholdMetadataNonce for extended_verifier_id key
if completeShareRequestResponseArr.count >= threshold {

if thresholdPublicKey?.X != nil && (thresholdNonceData != nil && thresholdNonceData?.pubNonce?.x != "" || verifierParams.extended_verifier_id != nil || isLegacyNetwork()) {
// Code block to execute if all conditions are true
var sharePromises = [String]()
var sharePkcs7 = [String]()
var shareZeroPadding = [String]()
var sessionTokenSigPromises = [String?]()
var sessionTokenPromises = [String?]()
var nodeIndexes = [Int]()
var sessionTokenData = [SessionToken?]()

guard let isNewKey = thresholdSame(arr: isNewKeyArr, threshold: threshold)
else {
os_log("retrieveShare - invalid result from nodes, threshold number of is_new_key results are not matching", log: getTorusLogger(log: TorusUtilsLogger.core, type: .error), type: .error)
throw TorusUtilError.thresholdError
}

for currentShareResponse in completeShareRequestResponseArr {
let sessionTokens = currentShareResponse.sessionTokens
Expand Down Expand Up @@ -408,8 +451,17 @@ extension TorusUtils {
let data = Data(base64Encoded: latestKey.share, options: [] )!
let binaryString = String(data: data, encoding: .ascii) ?? ""
let paddedBinaryString = binaryString.padding(toLength: 64, withPad: "0", startingAt: 0)

sharePromises.append(try decryptNodeData(eciesData: latestKey.shareMetadata, ciphertextHex: paddedBinaryString, privKey: sessionAuthKey).padLeft(padChar: "0", count: 64))
var decryptedShare = try decryptNodeData(eciesData: latestKey.shareMetadata, ciphertextHex: paddedBinaryString, privKey: sessionAuthKey)
sharePkcs7.append(decryptedShare.addLeading0sForLength64())
// temporary workaround on decrypt padding issue
if ( decryptedShare.count < 64 ) {

decryptedShare = try decryptNodeData(eciesData: latestKey.shareMetadata, ciphertextHex: paddedBinaryString, privKey: sessionAuthKey, padding: .zeroPadding).addLeading0sForLength64()
shareZeroPadding.append(decryptedShare)
} else {
shareZeroPadding.append(decryptedShare)
}


} else {
os_log("retrieveShare - 0 keys returned from nodes", log: getTorusLogger(log: TorusUtilsLogger.core, type: .error), type: .error)
Expand Down Expand Up @@ -456,54 +508,38 @@ extension TorusUtils {
sessionTokenData.append(SessionToken(token: token, signature: signature!, node_pubx: nodePubX, node_puby: nodePubY))
}
}
let decryptedShares = sharePromises.enumerated().reduce(into: [ Int : String ]()) { acc, current in
let decryptedSharesPkcs7 = sharePkcs7.enumerated().reduce(into: [ Int : String ]()) { acc, current in
let (index, curr) = current
acc[nodeIndexes[index]] = curr
}

// run lagrange interpolation on all subsets, faster in the optimistic scenario than berlekamp-welch due to early exit
let allCombis = kCombinations(s: decryptedShares.count, k: threshold)
var returnedKey: String? = nil

for j in 0..<allCombis.count {
let currentCombi = allCombis[j]
let currentCombiShares = decryptedShares.enumerated().reduce(into: [ Int : String ]()) { acc, current in
let (index, curr) = current
if (currentCombi.contains(index)) {
acc[curr.key] = curr.value
}
}
let derivedPrivateKey = try lagrangeInterpolation(shares: currentCombiShares, offset: 0)
let derivedPrivateKeyHex = derivedPrivateKey


guard Data(hexString: derivedPrivateKeyHex) != nil else {
continue
}
let decryptedPubKey = SECP256K1.privateToPublic(privateKey: Data(hex: derivedPrivateKey) )?.toHexString()

let decryptedPubKeyX = String(decryptedPubKey!.prefix(64))
let decryptedPubKeyY = String(decryptedPubKey!.suffix(64))
let decryptedPubKeyXBigInt = BigUInt(decryptedPubKeyX, radix: 16)!
let decryptedPubKeyYBigInt = BigUInt(decryptedPubKeyY, radix: 16)!
let thresholdPublicKeyXBigInt = BigUInt(thresholdPublicKey?.X ?? "0", radix: 16)!
let thresholdPublicKeyYBigInt = BigUInt(thresholdPublicKey?.Y ?? "0", radix: 16)!
returnedKey = derivedPrivateKey
if decryptedPubKeyXBigInt == thresholdPublicKeyXBigInt && decryptedPubKeyYBigInt == thresholdPublicKeyYBigInt {
returnedKey = derivedPrivateKey
break

var returnedKey = try reconstructKey(decryptedShares: decryptedSharesPkcs7, thresholdPublicKey: thresholdPublicKey!)

if (returnedKey == nil) {
let decryptedSharesZeroPadding = shareZeroPadding.enumerated().reduce(into: [ Int : String ]()) { acc, current in
let (index, curr) = current
acc[nodeIndexes[index]] = curr
}
returnedKey = try reconstructKey(decryptedShares: decryptedSharesZeroPadding, thresholdPublicKey: thresholdPublicKey!)

}

guard let oAuthKey = returnedKey else {
throw TorusUtilError.privateKeyDeriveFailed
}

// let decryptedPubKey = SECP256K1.privateToPublic(privateKey: Data(hex: oAuthKey) )?.toHexString()

let oAuthKeyBigInt = BigInt(oAuthKey , radix: 16)!

guard let derivedPrivateKeyData = Data(hexString: oAuthKey) else {
throw TorusUtilError.privateKeyDeriveFailed
}

let oAuthPubKey = SECP256K1.privateToPublic(privateKey: derivedPrivateKeyData)?.toHexString()
let oAuthPubKey = SECP256K1.privateToPublic(privateKey: derivedPrivateKeyData.addLeading0sForLength64())?.toHexString()
let oauthPubKeyX = String(oAuthPubKey!.suffix(128).prefix(64))
let oauthPubKeyY = String(oAuthPubKey!.suffix(64))

Expand All @@ -520,8 +556,8 @@ extension TorusUtils {
finalPubKey = String(finalPubKey.suffix(128))
} else if case .legacy(_) = self.network {
if (self.enableOneKey) {
// get nonce
let nonceResult = try await getOrSetNonce(x: oauthPubKeyX, y: oauthPubKeyY, privateKey: oAuthKey)
// get or set nonce based on isNewKey variable
let nonceResult = try await getOrSetNonce(x: oauthPubKeyX, y: oauthPubKeyY, privateKey: oAuthKey, getOnly: isNewKey == "false")
// BigInt( Data(hex: nonceResult.nonce ?? "0"))
metadataNonce = BigInt( nonceResult.nonce ?? "0", radix: 16)!
let usertype = nonceResult.typeOfUser
Expand Down Expand Up @@ -722,7 +758,7 @@ extension TorusUtils {
return BigUInt(message, radix: 16)!
}

internal func decryptNodeData(eciesData: EciesHex, ciphertextHex: String, privKey: String) throws -> String {
internal func decryptNodeData(eciesData: EciesHex, ciphertextHex: String, privKey: String, padding: Padding = .pkcs7) throws -> String {

let eciesOpts = ECIES(
iv: eciesData.iv,
Expand All @@ -731,7 +767,7 @@ extension TorusUtils {
mac: eciesData.mac
)

let decryptedSigBuffer = try decrypt(privateKey: privKey, opts: eciesOpts).toHexString()
let decryptedSigBuffer = try decrypt(privateKey: privKey, opts: eciesOpts, padding: padding).toHexString()
return decryptedSigBuffer
}

Expand Down Expand Up @@ -834,8 +870,8 @@ extension TorusUtils {
do {
// AES-CBCblock-256
let aes = try AES(key: AesEncryptionKey.hexa, blockMode: CBC(iv: iv), padding: .pkcs7)
let decrypt = try aes.decrypt(share)
result[nodeIndex] = decrypt.hexa
let decryptData = try aes.decrypt(share)
result[nodeIndex] = decryptData.hexa
} catch let err {
result[nodeIndex] = TorusUtilError.decodingFailed(err.localizedDescription).debugDescription
}
Expand Down Expand Up @@ -884,8 +920,7 @@ extension TorusUtils {

// Convert shares to BigInt(Shares)
var shareList = [BigInt: BigInt]()
_ = shares.map { shareList[BigInt($0.key + offset)] = BigInt($0.value, radix: 16) }
os_log("lagrangeInterpolation: %@ %@", log: getTorusLogger(log: TorusUtilsLogger.core, type: .debug), type: .debug, shares, shareList)
_ = shares.map { shareList[BigInt($0.key + offset)] = BigInt($0.value.addLeading0sForLength64(), radix: 16) }

var secret = BigUInt("0") // to support BigInt 4.0 dependency on cocoapods
var sharesDecrypt = 0
Expand Down Expand Up @@ -1513,7 +1548,7 @@ extension TorusUtils {
return tupleElements
}

public func decrypt(privateKey: String, opts: ECIES) throws -> Data {
public func decrypt(privateKey: String, opts: ECIES, padding: Padding = .pkcs7) throws -> Data {
let ephermalPublicKey = opts.ephemPublicKey.strip04Prefix()
let ephermalPublicKeyBytes = ephermalPublicKey.hexa
var ephermOne = ephermalPublicKeyBytes.prefix(32)
Expand All @@ -1538,9 +1573,9 @@ extension TorusUtils {
let aesEncryptionKey = hash.prefix(64)
do {
// AES-CBCblock-256
let aes = try AES(key: aesEncryptionKey.hexa, blockMode: CBC(iv: iv), padding: .pkcs7)
let decrypt = try aes.decrypt(opts.ciphertext.hexa)
let data = Data(decrypt)
let aes = try AES(key: aesEncryptionKey.hexa, blockMode: CBC(iv: iv), padding: padding)
let decryptData = try aes.decrypt(opts.ciphertext.hexa)
let data = Data(decryptData)
return data


Expand Down
6 changes: 5 additions & 1 deletion Sources/TorusUtils/Interfaces/ShareRequestResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct ShareRequestResult : Decodable {
let sessionTokenSigMetadata: [EciesHex]
let nodePubX: String
let nodePubY: String
let isNewKey: String

enum CodingKeys: CodingKey {
case keys
Expand All @@ -17,14 +18,16 @@ struct ShareRequestResult : Decodable {
case session_token_sig_metadata
case node_pubx
case node_puby
case is_new_key
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.keys = try container.decode([KeyAssignment].self, forKey: .keys)
self.nodePubX = try container.decode(String.self, forKey: .node_pubx)
self.nodePubY = try container.decode(String.self, forKey: .node_puby)

self.isNewKey = try container.decode(String.self, forKey: .is_new_key)

if let sessionTokens = try? container.decodeIfPresent([String].self, forKey: .session_tokens) {
self.sessionTokens = sessionTokens
} else {
Expand All @@ -48,6 +51,7 @@ struct ShareRequestResult : Decodable {
} else {
self.sessionTokenSigMetadata = []
}

}

// public func encode(to encoder: Encoder) throws {
Expand Down
24 changes: 11 additions & 13 deletions Sources/TorusUtils/TorusUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,9 @@ open class TorusUtils: AbstractTorusUtils {
// MARK: - getPublicAddress

public func getPublicAddress(endpoints: [String], torusNodePubs: [TorusNodePubModel], verifier: String, verifierId: String, extendedVerifierId :String? = nil ) async throws -> TorusPublicKey {
switch network {
case .legacy(_) :
if (self.isLegacyNetwork()) {
return try await getLegacyPublicAddress(endpoints: endpoints, torusNodePubs: torusNodePubs , verifier: verifier, verifierId: verifierId, enableOneKey: self.enableOneKey)
case .sapphire(_) :
} else {
return try await getNewPublicAddress(endpoints: endpoints, verifier: verifier, verifierId: verifierId, extendedVerifierId: extendedVerifierId, enableOneKey: self.enableOneKey)
}
}
Expand All @@ -74,11 +73,10 @@ open class TorusUtils: AbstractTorusUtils {
extraParams: [String:Codable] = [:]
) async throws -> TorusKey {

switch network {
case .legacy(_) :
if (self.isLegacyNetwork()) {
let result = try await legacyRetrieveShares(torusNodePubs: torusNodePubs, indexes: indexes, endpoints: endpoints, verifier: verifier, verifierId: verifierParams.verifier_id, idToken: idToken, extraParams: extraParams)
return result
case .sapphire(_) :
} else {

let result = try await retrieveShare(
legacyMetadataHost: self.legacyMetadataHost,
Expand All @@ -101,14 +99,14 @@ open class TorusUtils: AbstractTorusUtils {


public func getUserTypeAndAddress(endpoints: [String], torusNodePubs: [TorusNodePubModel], verifier: String, verifierId: String, extendedVerifierId :String? = nil) async throws -> TorusPublicKey {
switch network {
case .legacy(_) :
if (self.isLegacyNetwork()) {
return try await getLegacyPublicAddress(endpoints: endpoints, torusNodePubs: torusNodePubs, verifier: verifier, verifierId: verifierId, enableOneKey: true)
case .sapphire(_) :
}
else {
return try await getNewPublicAddress(endpoints: endpoints, verifier: verifier, verifierId: verifierId, extendedVerifierId: extendedVerifierId, enableOneKey: true)
}

}
}


private func getNewPublicAddress(endpoints: [String], verifier: String, verifierId: String, extendedVerifierId :String? = nil, enableOneKey: Bool) async throws -> TorusPublicKey {
Expand Down Expand Up @@ -431,8 +429,8 @@ open class TorusUtils: AbstractTorusUtils {
throw TorusUtilError.decodingFailed(decoded.error?.data)
}
os_log("retrieveDecryptAndReconstuct: %@", log: getTorusLogger(log: TorusUtilsLogger.core, type: .info), type: .info, "\(decoded)")
var X = lookupPubkeyX
var Y = lookupPubkeyY
var X = lookupPubkeyX.addLeading0sForLength64()
var Y = lookupPubkeyY.addLeading0sForLength64()
if let decodedResult = decoded.result as? LegacyLookupResponse {
// case non migration
let keyObj = decodedResult.keys
Expand Down Expand Up @@ -477,7 +475,7 @@ open class TorusUtils: AbstractTorusUtils {


if filteredData.count < threshold { throw TorusUtilError.thresholdError }
let thresholdLagrangeInterpolationData = try thresholdLagrangeInterpolation(data: filteredData, endpoints: endpoints, lookupPubkeyX: X, lookupPubkeyY: Y)
let thresholdLagrangeInterpolationData = try thresholdLagrangeInterpolation(data: filteredData, endpoints: endpoints, lookupPubkeyX: X.addLeading0sForLength64(), lookupPubkeyY: Y.addLeading0sForLength64())
session.invalidateAndCancel()
return thresholdLagrangeInterpolationData
case .failure(let error):
Expand Down
Loading

0 comments on commit 61b3668

Please sign in to comment.