addresses signature cryptography – Getting the wrong address back from ecrecover

addressescryptographyecrecoverethereumjssignature

When I sign a string using the following code I get back a different address to the one I signed with. The address returned is different for different strings.

const util = require('ethereumjs-util')
const msg = web3.sha3('hello!');
const sig = web3.eth.sign(web3.eth.accounts[0], msg);
const {v, r, s} = util.fromRpcSig(sig);

const pubKey  = util.ecrecover(util.toBuffer(msg), v, r, s);
const addrBuf = util.pubToAddress(pubKey);
const addr    = util.bufferToHex(addrBuf);

I took the code from the answer to a question I asked previously here Getting an address from ethereumjs-utils erecover.

I asked a question here: Secp256k1 bindings are not compiled. Pure JS implementation will be used about a possibly relevant error. The comments suggested that it should be harmless though.

Best Answer

Geth adds prefix to the message before siginig it in web3.eth.sign (see JSON-PRC spec). Without this it can be possible to trick user to sign transaction (more here).

So, the correct code to sign message with web3.eth.sign and recover address with ethereumjs-util.ecrecover or (Solidity's ecrecover) should add the prefix explicitly.

const util = require('ethereumjs-util')

const msg = new Buffer('hello');
const sig = web3.eth.sign(web3.eth.accounts[0], '0x' + msg.toString('hex'));
const res = util.fromRpcSig(sig);

const prefix = new Buffer("\x19Ethereum Signed Message:\n");
const prefixedMsg = util.sha3(
  Buffer.concat([prefix, new Buffer(String(msg.length)), msg])
);

const pubKey  = util.ecrecover(prefixedMsg, res.v, res.r, res.s);
const addrBuf = util.pubToAddress(pubKey);
const addr    = util.bufferToHex(addrBuf);

console.log(web3.eth.accounts[0],  addr);

There is misleading inconsistency with ethereumjs-testrpc as it does not prefix message before signing it in web3.eth.sign.