10

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?

2
  • Which OS your on? also you used Meteor ?
    – DappDev
    Feb 28, 2017 at 13:44
  • I am on macOS Sierra and yes this is using meteor
    – Joe
    Feb 28, 2017 at 13:47

2 Answers 2

13

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);
6
  • Thanks for answering the pubToAddress is definitely what I was looking for but it is not giving the same address as the one I signed with. Do you have any idea why this might be or should I mark this answer as correct and make a new question?
    – Joe
    Feb 28, 2017 at 15:52
  • Everything should be ok. I updated my answer with example session. There might be a problem with v, r, s recovery, try to use fromRpcSig. Feb 28, 2017 at 16:11
  • 1
    I copy pasted your demo code and still get different addresses when I use it.
    – Joe
    Feb 28, 2017 at 16:21
  • I cant see why this would make any difference but I had to use the line var web3 = new Web3(new Web3.providers.HttpProvider("localhost:8545")) instead of your lines 2- 4 as it couldn't find the module
    – Joe
    Feb 28, 2017 at 16:24
  • 1
0

Example node.js + ethereumjs-util other method to validate signature

const ethUtil = require("ethereumjs-util");

const inSignature = "0x...."; //user signed message
const message = "hello!";
const msgHex = ethUtil.bufferToHex(Buffer.from(message));
const msgBuffer = ethUtil.toBuffer(msgHex);
const msgHash = ethUtil.hashPersonalMessage(msgBuffer);

const signature = ethUtil.toBuffer(inSignature);

const sigParams = ethUtil.fromRpcSig(signature);
const publicKey = ethUtil.ecrecover(
            msgHash,
            sigParams.v,
            sigParams.r,
            sigParams.s
);

const sender = ethUtil.publicToAddress(publicKey);
const addr = ethUtil.bufferToHex(sender);

//now compare addr with user wallet address
if("0x<userWaller>" === addr) console.log("valid");

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.