I have ECDSA signatures created using Golang without using web3 or anything related to Ethereum, and I want to verify these in Solidity.
Step 1: Signing the message in Golang
My Go code to create signatures:
import (
"crypto/ecdsa"
"crypto/elliptic"
"fmt"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"crypto/rand"
"github.com/miguelmota/go-solidity-sha3"
"encoding/hex"
"strconv"
)
func KeyGen() (pubkey []byte, privkey []byte) {
key, err := ecdsa.GenerateKey(S256(), rand.Reader)
if err != nil {
panic(err)
}
pubkey = elliptic.Marshal(S256(), key.X, key.Y)
return pubkey, math.PaddedBigBytes(key.D, 32)
}
func Sign(message string, sk []byte) ([]byte) {
hash := solsha3.SoliditySHA3(solsha3.String("\x19Ethereum Signed Message:\n"+strconv.FormatInt(int64(len(message)),10)+message))
signature, _ := secp256k1.Sign(hash, sk)
return signature
}
Basically, in the Sign method, I take the hash of
"\x19Ethereum Signed Message:\n" + message.length + message
and sign the hash.
My main method is:
//Create keypair and signature
pk, sk := KeyGen()
message := "TEST"
sig := Sign(message, sk)
//Print public key and sig in hex notation
fmt.Println("PK", hex.EncodeToString(pk))
fmt.Println("Sig", hex.EncodeToString(sig))
//Print hash
hash := solsha3.SoliditySHA3(solsha3.String("\x19Ethereum Signed Message:\n"+strconv.FormatInt(int64(len(message)),10)+message))
fmt.Println("hash", hex.EncodeToString(hash[:]))
Step 2: Verification in Solidity
In Solidity, I use a library by openzeppelin found at https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol to recover the public key. I put the three values printed in Go (hash, publickey, signature) as attributes of the contract and attempt to verify the signature:
contract ECDSA {
bytes32 hash = hex"fb96181ff706848b10a93f4028537caf17026e28ce5c0cce90af46b4d3ad04c6";
bytes sig = hex"cd044278098c2e5e36cc716423638509bbc818b0daadd7b7bba6322a99de67373255201bac6634b6adeb29d4c3bd9b3a6a14363c4e5b5eb3087c7882c7ebca6800";
bytes publickey = hex"0485d169380f762e82da692c4b50c8873b1ef9820f15ae6cd96d0d4741056a1b9c828f4f027d0374f22311cba8ceb5845232549e6e4df7ae490b6903a5ee1447be";
function verifysignature() returns (bool)
{
return recover(hash, sig) == address(keccak256(publickey));
}
}
Question
The above does not return 'true' when given a valid signature. What am I doing wrong?
A few things I think could be wrong:
- Is it even possible to use ecrecover to validate signatures that were not created by web3.eth.sign?
- Do I use the right prefix for the message before hashing it?
- Is address(keccak256(publickey)) the right way to convert public key to the corresponding address?
UPDATE
I created a gist of the full working solution based on the answer by smarx. The gist is found here.