Skip to content

Commit

Permalink
Merge pull request #356 from tangem/IOS-6784_add_interface_for_key_re…
Browse files Browse the repository at this point in the history
…covery

IOS-6784 Add public key recovery interface
  • Loading branch information
tureck1y authored May 8, 2024
2 parents bf339b4 + 40207a3 commit 659e6ee
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 5 deletions.
1 change: 1 addition & 0 deletions TangemSdk/TangemSdk/Common/Extensions/HexConvertible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ public extension HexConvertible where Self: FixedWidthInteger {

extension Int: HexConvertible {}
extension UInt64: HexConvertible {}
extension Int32: HexConvertible {}
10 changes: 9 additions & 1 deletion TangemSdk/TangemSdk/Crypto/Secp256k1Key.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ public struct Secp256k1Key {
public init(with data: Data) throws {
secp256k1PubKey = try secp256k1.parsePublicKey(data)
}


public init(with signature: Secp256k1Signature.Extended, hash: Data) throws {
secp256k1PubKey = try secp256k1.recoverPublicKey(signatureCoponents: signature.components, hash: hash)
}

public init(with signature: Secp256k1Signature.Extended, message: Data) throws {
try self.init(with: signature, hash: message.getSha256())
}

public func compress() throws -> Data {
var pubkey = secp256k1PubKey
return try secp256k1.serializePublicKey(&pubkey, compressed: true)
Expand Down
8 changes: 6 additions & 2 deletions TangemSdk/TangemSdk/Crypto/Secp256k1Signature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public struct Secp256k1Signature {

public func unmarshal(with publicKey: Data, hash: Data) throws -> Extended {
var sig = rawSig
let (r, s, v) = try secp256k1.unmarshalSignature(&sig, publicKey: publicKey, hash: hash)
return Extended(r: r, s: s, v: v)
let components = try secp256k1.unmarshalSignature(&sig, publicKey: publicKey, hash: hash)
return Extended(r: components.r, s: components.s, v: components.v)
}
}

Expand All @@ -57,6 +57,10 @@ extension Secp256k1Signature {
return r + s + v
}

var components: Secp256k1SignatureComponents {
(r,s,v)
}

public init(r: Data, s: Data, v: Data) {
self.r = r
self.s = s
Expand Down
13 changes: 12 additions & 1 deletion TangemSdk/TangemSdk/Crypto/Secp256k1Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import Foundation
import TangemSdk_secp256k1

typealias Secp256k1SignatureComponents = (r: Data, s: Data, v: Data)

@available(iOS 13.0, *)
public final class Secp256k1Utils {
private let context: OpaquePointer
Expand Down Expand Up @@ -174,7 +176,7 @@ public final class Secp256k1Utils {
return Data(der[0..<Int(length)])
}

func unmarshalSignature(_ signature: inout secp256k1_ecdsa_signature, publicKey: Data, hash: Data) throws -> (r: Data, s: Data, v: Data) {
func unmarshalSignature(_ signature: inout secp256k1_ecdsa_signature, publicKey: Data, hash: Data) throws -> Secp256k1SignatureComponents {
guard hash.count == 32 else { throw TangemSdkError.cryptoUtilsError("Hash size must be 32 bytes length") }

guard try verifySignature(&signature, publicKey: publicKey, hash: hash) else {
Expand Down Expand Up @@ -247,6 +249,15 @@ public final class Secp256k1Utils {
return pubkey
}

func recoverPublicKey(signatureCoponents: Secp256k1SignatureComponents, hash: Data) throws -> secp256k1_pubkey {
guard let intV = Int32(hexData: signatureCoponents.v) else {
throw TangemSdkError.cryptoUtilsError("Failed to parse v")
}

var recoverableSignature = try parseRecoverableSignature(signatureCoponents.r + signatureCoponents.s, v: intV)
return try recoverPublicKey(hash: hash, recoverableSignature: &recoverableSignature)
}

func parseXOnlyPublicKey(_ publicKey: Data) throws -> secp256k1_xonly_pubkey {
var pubkey = secp256k1_xonly_pubkey()

Expand Down
18 changes: 17 additions & 1 deletion TangemSdk/TangemSdkTests/CryptoUtilsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class CryptoUtilsTests: XCTestCase {
XCTAssertEqual(normalized.hexString, "5365F955FC45763383936BBC021A15D583E8D2300D1A65D21853B6A0FCAECE4E29AF6C44A13AD6E138336A4BD872F15FC517C30816E9B4C57858697AEBE25364")
}

func testRecover() {
func testSignatureUnmarshal() {
let privateKey = Data(hexString: "fd230007d4a39352f50d8c481456c1f86ddc5ff155df170af0100a62269852f0")
let publicKey = Data(hexString: "0432f507f6a3029028faa5913838c50f5ff3355b9b000b51889d03a2bdb96570cd750e8187482a27ca9d2dd0c92c632155d0384521ed406753c9883621ad0da68c")

Expand All @@ -153,6 +153,22 @@ class CryptoUtilsTests: XCTestCase {
XCTAssertEqual(unmarshalled?.v.hexString, "1C")
}

func testRecoverPublicKey() throws {
let privateKey = Data(hexString: "fd230007d4a39352f50d8c481456c1f86ddc5ff155df170af0100a62269852f0")
let publicKey = Data(hexString: "0432f507f6a3029028faa5913838c50f5ff3355b9b000b51889d03a2bdb96570cd750e8187482a27ca9d2dd0c92c632155d0384521ed406753c9883621ad0da68c")
let dummyData = Data(repeating: UInt8(1), count: 64)
let hash = dummyData.getSha256()

let signature = try Secp256k1Utils().sign(dummyData, with: privateKey)
let unmarshalled = try Secp256k1Signature(with: signature).unmarshal(with: publicKey, hash: hash)

let key = try Secp256k1Key(with: unmarshalled, hash: hash)
XCTAssertEqual(try key.decompress().hexString, publicKey.hexString)

let key1 = try Secp256k1Key(with: unmarshalled, message: dummyData)
XCTAssertEqual(try key1.decompress().hexString, publicKey.hexString)
}

func testSecp256k1PrivateKeyValidation() {
let utils = Secp256k1Utils()

Expand Down

0 comments on commit 659e6ee

Please sign in to comment.