Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Geth ecrecover invalid signature recovery id

I'm trying to verify signature passed in from client using the geth golang library. I'm getting the sample data (signature / address) from one of my cryptokitties accounts (I can see it in the request). If I paste the blow credentials into https://etherscan.io/verifySig, it gets verified, so I know the parameters are correct.

My code:

import (
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/crypto"
)

sig := 0x80f5bac5b6300ed64835d5e2f167a368c892ccc2d0e252bc84befbcb093f5a2d36294b95d86683cec778c8c796049933c04c71576c56c1d6e9a9fa10342beca31c

data := "Cryptokitties"
decoded = hexutil.MustDecode(sig) // j8aUTtPid0ZnNa/s4Ef5gisYYh1bCeLSmFrtJtDjNRRqxShUr+1A3BVgoAPwiZ+lKN0POB1JOdVhVHI9tcHmABs=
hash := crypto.Keccak256([]byte(data)) // "ljQQTm25oqIbD+LMl70aRUcTzXCeeDGfkRj9YJYsgKY="
pubKey, err := crypto.Ecrecover(hash, sig) // error: "invalid signature recovery id"

I'm sure I'm missing something simple, but not really sure where to look.

**UPDATE

What I tried after looking around some answers:

  • changing the message like this: fmt.Sprintf("\u0019Ethereum Signed Message:\n%d%s", len(data), data) //"\u0019Ethereum Signed Message:\n13Cryptokitties"

  • hex encoding the message before hashing it: data=hexutil.Encode(data)

  • combining the above two, so first prepending the 'Ethereum Signed Message', then hexencoding it.

Any points would be appreciated, I'm sure this is a noob question.

**UPDATE

Looking through the source code, I discovered its expecting recovery id to be greater than 4: sig[64] >= 4

In my case it turns out to be 27: sig[64] --> 27

like image 980
Levon Tamrazov Avatar asked Mar 03 '18 15:03

Levon Tamrazov


1 Answers

See: https://github.com/ethereum/go-ethereum/blob/55599ee95d4151a2502465e0afc7c47bd1acba77/internal/ethapi/api.go#L452-L459

// EcRecover returns the address for the account that was used to create the signature.
// Note, this function is compatible with eth_sign and personal_sign. As such it recovers
// the address of:
// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message})
// addr = ecrecover(hash, signature)
//
// Note, the signature must conform to the secp256k1 curve R, S and V values, where
// the V value must be be 27 or 28 for legacy reasons.
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
    if len(sig) != 65 {
        return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
    }
    if sig[64] != 27 && sig[64] != 28 {
        return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
    }
    sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1

    rpk, err := crypto.Ecrecover(signHash(data), sig)
    if err != nil {
        return common.Address{}, err
    }
    pubKey := crypto.ToECDSAPub(rpk)
    recoveredAddr := crypto.PubkeyToAddress(*pubKey)
    return recoveredAddr, nil
}

Here is my gist: https://gist.github.com/dcb9/385631846097e1f59e3cba3b1d42f3ed#file-eth_sign_verify-go

like image 127
chengbin du Avatar answered Nov 08 '22 08:11

chengbin du