Skip to content

Commit

Permalink
Merge pull request #159 from Clee681/recover-pub-key
Browse files Browse the repository at this point in the history
add KeyUtil.recoverPublicKey
  • Loading branch information
DarthMike authored Nov 29, 2021
2 parents 8205d3f + d4130d5 commit d9494a9
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 1 deletion.
8 changes: 8 additions & 0 deletions web3sTests/Utils/KeyUtilTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,12 @@ class KeyUtilTests: XCTestCase {
XCTAssertEqual(address.value, "0x751e735a83a8142c1b9dc722ef559b898f1d77fa")
}

func testRecoverPublicKey() {
let account = try! EthereumAccount(keyStorage: TestEthereumKeyStorage(privateKey: "0x2639f727ded571d584643895d43d02a7a190f8249748a2c32200cfc12dde7173"))
let signature = try! account.sign(message: "Hello message!")

let address = try! KeyUtil.recoverPublicKey(message: "Hello message!".web3.keccak256, signature: signature)

XCTAssertEqual(address, account.address.value.lowercased())
}
}
51 changes: 50 additions & 1 deletion web3swift/src/Utils/KeyUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ enum KeyUtilError: Error {
case privateKeyInvalid
case unknownError
case signatureFailure
case signatureParseFailure
case badArguments
}

class KeyUtil {

static func generatePrivateKeyData() -> Data? {
return Data.randomOfLength(32)
}
Expand Down Expand Up @@ -105,4 +106,52 @@ class KeyUtil {

return signature
}

static func recoverPublicKey(message: Data, signature: Data) throws -> String {
if signature.count != 65 || message.count != 32 {
throw KeyUtilError.badArguments
}

guard let ctx = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY)) else {
print("Failed to sign message: invalid context.")
throw KeyUtilError.invalidContext
}
defer { secp256k1_context_destroy(ctx) }

// get recoverable signature
let signaturePtr = UnsafeMutablePointer<secp256k1_ecdsa_recoverable_signature>.allocate(capacity: 1)
defer { signaturePtr.deallocate() }

let serializedSignature = Data(signature[0..<64])
var v = Int32(signature[64])
if v >= 27 && v <= 30 {
v -= 27
} else if v >= 31 && v <= 34 {
v -= 31
} else if v >= 35 && v <= 38 {
v -= 35
}

try serializedSignature.withUnsafeBytes {
guard secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, signaturePtr, $0.bindMemory(to: UInt8.self).baseAddress!, v) == 1 else {
print("Failed to parse signature: recoverable ECDSA signature parse failed.")
throw KeyUtilError.signatureParseFailure
}
}
let pubkey = UnsafeMutablePointer<secp256k1_pubkey>.allocate(capacity: 1)
defer { pubkey.deallocate() }

try message.withUnsafeBytes {
guard secp256k1_ecdsa_recover(ctx, pubkey, signaturePtr, $0.bindMemory(to: UInt8.self).baseAddress!) == 1 else {
throw KeyUtilError.signatureFailure
}
}
var size: Int = 65
var rv = Data(count: size)
rv.withUnsafeMutableBytes {
secp256k1_ec_pubkey_serialize(ctx, $0.bindMemory(to: UInt8.self).baseAddress!, &size, pubkey, UInt32(SECP256K1_EC_UNCOMPRESSED))
return
}
return "0x\(rv[1...].web3.keccak256.web3.hexString.suffix(40))"
}
}

0 comments on commit d9494a9

Please sign in to comment.