30

If I have a cryptographic signature from an Ethereum address key pair, how can I verify that signature?

eth
  • 85,679
  • 53
  • 285
  • 406
Piper Merriam
  • 3,592
  • 3
  • 22
  • 34

1 Answers1

32

Solidity and Serpent have ecrecover for this purpose.

ecrecover(bytes32 data, uint8 v, bytes32 r, bytes32 s) returns (address)

The function arguments are:

data is what was signed. Since it is 32 bytes, that usually means that the initial data is hashed first to 32 bytes, before it was signed.

v, r, s is the signature. (v is the recovery id: a 1 byte value specifying the sign and finiteness of the curve point; this value is in the range of [27, 30], however the Ethereum protocol declares the upper two possibilities, representing infinite values, invalid)

Important note with the examples below, sha3 is Keccak-256.

Here is a snippet in Solidity:

contract Auth {      
    function verify(address p, bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(bool) {
        // Note: this only verifies that signer is correct.
        // You'll also need to verify that the hash of the data
        // is also correct.
        return ecrecover(hash, v, r, s) == p;
    }
}

Here is an example in Serpent:

def test_ecrecover(h, v, r, s):
    return(ecrecover(h, v, r, s))

The corresponding test code in Python (requires bitcoin and ethereum packages):

import bitcoin as b
from ethereum import tester, utils


class TestECRecover(object):

    CONTRACT = """
def test_ecrecover(h, v, r, s):
    return(ecrecover(h, v, r, s))
"""

    def setup_class(cls):
        cls.s = tester.state()
        cls.c = cls.s.abi_contract(cls.CONTRACT)
        cls.snapshot = cls.s.snapshot()

    def setup_method(self, method):
        self.s.revert(self.snapshot)

    def test_ecrecover(self):
        priv = b.sha256('some big long brainwallet password')
        pub = b.privtopub(priv)

        msghash = b.sha256('the quick brown fox jumps over the lazy dog')
        V, R, S = b.ecdsa_raw_sign(msghash, priv)
        assert b.ecdsa_raw_verify(msghash, (V, R, S), pub)

        addr = utils.sha3(b.encode_pubkey(pub, 'bin')[1:])[12:]
        assert utils.privtoaddr(priv) == addr

        result = self.c.test_ecrecover(utils.big_endian_to_int(msghash.decode('hex')), V, R, S)
        assert result == utils.big_endian_to_int(addr)

Under the hood, ecrecover uses the ECDSARECOVER precompiled contract located at address 1.


Note: Geth and web3.eth.sign will add a prefix to the data message before signing.

The sign method calculates an Ethereum specific signature with: sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).

By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim.

For this case, the second argument to verify() must be keccak256("\x19Ethereum Signed Message:\n", len(message), message) instead of keccak256(message).

Related: ecrecover from Geth and web3.eth.sign

eth
  • 85,679
  • 53
  • 285
  • 406
  • While already a very thorough answer, can you add what ecrecover returns in case of a good or invalid signature? – J-B Jan 28 '16 at 03:32
  • 1
    As I understand it, a good signature will return the address which did the signing. A bad signature will return a different address. There's no way to tell if a signature is valid or not without also knowing the address which was supposed to do the signing. – ryepdx Mar 20 '16 at 22:17
  • 1
    From the solidity docs on ecrecover: "recover the address associated with the public key from elliptic curve signature or return zero on error" – ZMitton Feb 21 '17 at 22:00
  • Thank you to the anonymous editor who added the important code comment: "You'll also need to verify that the hash of the data is also correct" – eth Aug 10 '17 at 06:45
  • If the signature is wrong, you still get a valid address from this. So do you just have to make the signer include the address somewhere in the original message to check for correctness? – sudo Jun 03 '18 at 04:14
  • Or conversely @sudo if hash is wrong, you still get a valid but incorrect address. – Garen Vartanian Nov 15 '18 at 00:40