5

Resolved

The basic process flow of what I'm writing goes as follows.

  • Sign a message with a private key and create an Ethereum address from the private key (AKA the vettingAddress)
  • Pass the signature constituents to the contract along with a special hashed version of the hashed message (See #Cheking If Correct)

I don't have any problem doing with natively with Go, see below:

Signing With Golang :

func sign(message string, ecdsaKey *ecdsa.PrivateKey) []byte {
    hash := crypto.Keccak256([]byte(message))
    signature, _ := crypto.Sign(hash, ecdsaKey)
}

The resulting signature is validated easily with the Golang Ethereum lib using the following golang code:

Validating / Verifying with Golang :

func validate(ecdsaPubBytes []byte, signature []byte, messageHash []byte) bool {
    return crypto.VerifySignature(ecdsaPubBytes, messageHash , signature[:64])
}

So far so good this work perfectly.


My problem is with validating / verifying with an Ethereum smart contract, I have the following code:

My Golang Code:

type Signature struct {
    Hash [32]byte
    R    [32]byte
    S    [32]byte
    V    uint8
}

func Sign(message string) Signature {
    hashRaw := crypto.Keccak256([]byte(message))
    signature, err := crypto.Sign(hashRaw, ecdsaKey)
    p.errorHandler.Handle(err, "Signature error")

    validationMsg := "\x19Ethereum Signed Message:\n" + strconv.Itoa(len(message)) + message
    println(validationMsg)
    validationHash := crypto.Keccak256([]byte(validationMsg))

    return Signature{
        p.bytes32(validationHash),
        p.bytes32(signature[:32]),
        p.bytes32(signature[32:64]),
        uint8(binary.LittleEndian.Uint32(signature[:65]))+27, // Yes add 27, weird Ethereum quirk
    }
}

Passing to the network

contract, _ := Signature(common.HexToAddress("..."), etherClient)
println(contract.VerifySig(callOpts, sig.Hash, sig.R, sig.S, sig.V, vettingAddress))

My Solidity contract:

pragma solidity ^0.4.20;

library Signature {

    function verify(bytes32 hash, bytes32 r, bytes32 s,  uint8 v, address vettingAddress) internal pure returns(bool)
    {
        return ecrecover(hash, v, r, s) == vettingAddress;
    }
}

The contract is always returning false. There are very few resources for handling ecrecover, I've read this article), but I don't see what I'm doing incorrectly.


EDIT

I've solved this issue. The problem was related to how I parse the 65th byte of the signature. Below was incorrect for a number of reasons:

Parse the Int correctly

uint8(binary.LittleEndian.Uint32(signature[:65]))+27, // Yes add 27, weird Ethereum quirk

Should look like:

uint8(int(signature[65])) + 27, // Yes add 27, weird Ethereum quirk

Don't need a special hash

Also the validation hash was not needed.

p.bytes32(validationHash),

Should be:

p.bytes32(hashRaw),

So all in all the new signature method looked like this:

type Signature struct {
    Raw   []byte
    Hash  [32]byte
    R     [32]byte
    S     [32]byte
    V     uint8
}

func Sign(message string) Signature {
    hashRaw := crypto.Keccak256([]byte(message))
    signature, err := crypto.Sign(hashRaw, p.ecdsa)
    p.errorHandler.Handle(err, "Signature error")

    return Signature{
        signature,
        p.bytes32(hashRaw),
        p.bytes32(signature[:32]),
        p.bytes32(signature[32:64]),
        uint8(int(signature[65])) + 27, // Yes add 27, weird Ethereum quirk
    }
}
2
  • NOTE: I post this as a question because I do not have enough reputation to comment. Hi Samuel Hawksby-Robinson, Thanks for your question and answer. You left out certain details of your example which makes it more difficult to reproduce: - How do you generate the keys for the right curve? - What is the variable 'p' that you are using? (For instance: p.bytes32 and p.ecdsa methods) - How do you create vettingAddress exactly? Would you be able to include this information? Thank you in advance.
    – Saffie
    Commented Jul 26, 2018 at 14:12
  • Please, correct for 64 index instead of 65 Commented Aug 6, 2022 at 7:22

3 Answers 3

2

I've solved this issue. The problem was related to how I parse the 65th byte of the signature. Below was incorrect for a number of reasons:

Parse the Int correctly

uint8(binary.LittleEndian.Uint32(signature[:65]))+27, // Yes add 27, weird Ethereum quirk

Should look like:

uint8(int(signature[65])) + 27, // Yes add 27, weird Ethereum quirk

Don't need a special hash

Also the validation hash was not needed.

p.bytes32(validationHash),

Should be:

p.bytes32(hashRaw),

So all in all the new signature method looked like this:

type Signature struct {
    Raw   []byte
    Hash  [32]byte
    R     [32]byte
    S     [32]byte
    V     uint8
}

func Sign(message string) Signature {
    hashRaw := crypto.Keccak256([]byte(message))
    signature, err := crypto.Sign(hashRaw, p.ecdsa)
    p.errorHandler.Handle(err, "Signature error")

    return Signature{
        signature,
        p.bytes32(hashRaw),
        p.bytes32(signature[:32]),
        p.bytes32(signature[32:64]),
        uint8(int(signature[65])) + 27, // Yes add 27, weird Ethereum quirk
    }
}
1
  • it should be 64 index, not 65 uint8(int(signature[64])) + 27 Commented Aug 6, 2022 at 7:20
1

I know that there is a library https://github.com/storyicon/sigverify that encapsulates the function of verifying Ethereum signatures in golang, including EIP712 and ERC1271.

0
privatekeyPointer, err := crypto.HexToECDSA(privatekey)
message := "tokenXYZ,amount=10,price=1.23"
prefix := "\x19Ethereum Signed Message\n"
preSigned := prefix + strconv.Itoa(len(message)) + message
hashedMesg := crypto.Keccak256Hash([]byte(preSigned))
signatureBytes, err := crypto.Sign(hashedMesg.Bytes(), privatekeyPointer)

r := signatureBytes[:32]
s := signatureBytes[32:64]
v := uint8(int(signatureBytes[64])) + 27

reference: https://godoc.org/github.com/ethereum/go-ethereum/crypto#Sign

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.