From 7c3536b995438bcb0c46596afd1c159e0d17d737 Mon Sep 17 00:00:00 2001 From: Simon McLoughlin Date: Thu, 1 Jun 2023 14:14:04 +0100 Subject: [PATCH] add secp256k1 uncompress function + tests --- Sources/KukaiCryptoSwift/KeyPair.swift | 21 +++++++++++++++++- .../KukaiCryptoSwiftTests/KeyPairTests.swift | 22 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Sources/KukaiCryptoSwift/KeyPair.swift b/Sources/KukaiCryptoSwift/KeyPair.swift index cbb0873..1488373 100644 --- a/Sources/KukaiCryptoSwift/KeyPair.swift +++ b/Sources/KukaiCryptoSwift/KeyPair.swift @@ -135,7 +135,7 @@ public struct KeyPair { - // MARK: - Private Helpers + // MARK: - Helpers /// Helper method to take a secp256k1 private key (for a regualr keypair) and use it to create a public key for the same curve public static func secp256k1PublicKey(fromPrivateKeyBytes pkBytes: [UInt8]) -> PublicKey? { @@ -156,4 +156,23 @@ public struct KeyPair { return PublicKey(publicKeyBytes, signingCurve: .secp256k1) } + + /// Helper method to uncompress a secp256k1 public key + public static func secp256k1PublicKey_uncompressed(fromBytes: [UInt8]) -> [UInt8] { + var publicKey = secp256k1_pubkey() + var outputLength = 65 + var outputBytes = [UInt8](repeating: 0, count: outputLength) + + guard let context = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN)), + secp256k1_ec_pubkey_parse(context, &publicKey, fromBytes, fromBytes.count) != 0, + secp256k1_ec_pubkey_serialize(context, &outputBytes, &outputLength, &publicKey, UInt32(SECP256K1_EC_UNCOMPRESSED)) != 0 else { + return [] + } + + defer { + secp256k1_context_destroy(context) + } + + return outputBytes + } } diff --git a/Tests/KukaiCryptoSwiftTests/KeyPairTests.swift b/Tests/KukaiCryptoSwiftTests/KeyPairTests.swift index 936d3dd..4b31b39 100644 --- a/Tests/KukaiCryptoSwiftTests/KeyPairTests.swift +++ b/Tests/KukaiCryptoSwiftTests/KeyPairTests.swift @@ -102,4 +102,26 @@ final class KeyPairTests: XCTestCase { XCTAssert(EllipticalCurve.fromAddress("tZ1abc123") == .ed25519) XCTAssert(EllipticalCurve.fromAddress("tZ2abc123") == .secp256k1) } + + func testUncompress() throws { + let mnemonic = try Mnemonic(seedPhrase: "gym exact clown can answer hope sample mirror knife twenty powder super imitate lion churn almost shed chalk dust civil gadget pyramid helmet trade") + let keyPair1 = KeyPair.regular(fromMnemonic: mnemonic, passphrase: "", andSigningCurve: .secp256k1) + + let uncompressed = KeyPair.secp256k1PublicKey_uncompressed(fromBytes: keyPair1?.publicKey.bytes ?? []) + let data = Data(bytes: uncompressed, count: uncompressed.count) + let dataString = data.hexString + + XCTAssert(dataString.count == 130, dataString.count.description) + XCTAssert(dataString == "047b6d7bf2cbb376149211eacab517359cb035b0f0c36f57f7fe923a2453c7a8f1a594260c94501bc7ad63324b42638f768840948d1f1cadf9cb6d1ce456b7c8dc", dataString) + + + let keyPair2 = KeyPair.regular(fromMnemonic: mnemonic, passphrase: "", andSigningCurve: .ed25519) + + let uncompressed2 = KeyPair.secp256k1PublicKey_uncompressed(fromBytes: keyPair2?.publicKey.bytes ?? []) + let data2 = Data(bytes: uncompressed2, count: uncompressed2.count) + let dataString2 = data2.hexString + + XCTAssert(dataString2.count == 0, dataString2.count.description) + XCTAssert(dataString2 == "", dataString2) + } }