Ethereumjs-utils – Getting an Address Using ethereumjs-utils ecrecover

cryptographyecrecoverethereumjssignature

I am trying to sign a string using web3.eth.sign() and then get the public key I signed with from the signature. To do this I am using ecrecover() from ethereumjs-utils, which returns a buffer. When I use bufferToHex() on the buffer it gives a hex string that is much too long to be an address – 130 characters including '0x'

web3.personal.unlockAccount(myAccount,pass)
msg = web3.sha3(aString)
sig = web3.eth.sign(myAccount,msg)
r = sig.slice(0, 66)
s = '0x' + sig.slice(66, 130)
v = '0x' + sig.slice(130, 132)
v = web3.toDecimal(v)
msg = ethJsUtil.toBuffer(msg)
addr = ethJsUtil.ecrecover(msg,v,r,s)
addr = ethJsUtil.bufferToHex(addr)

I took most of the code from the answer to workflow on signing a string with private key, followed by signature verification with public key but had to convert 'msg' to a buffer as ecrecover threw a type error otherwise.

What do I need to do to convert the buffer from ecrecover to an address?

Best Answer

ecrecover returns public key, you need to convert it to address with pubToAddress.

pub     = ethJsUtil.ecrecover(msg, v, r, s);
addrBuf = ethJsUtil.pubToAddress(pub);
addr    = ethJsUtil.bufferToHex(addrBuf);

Also, you can use fromRpcSig to get v, r, s

sig = web3.eth.sign(myAccount,msg)
res = ethJsUtil.fromRpcSig(sig)
pub = ethJsUtil.ecrecover(msg, res.v, res.r, res.s);

Please note that web3.eth.sign adds prefix to the message before signing it (see JSON-RPC spec).

Here is how to add it manually:

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);

On the other hand, testrpc (at least version 3.0.5) does not add such prefix.

Example node.js + testrpc session:

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);

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