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?

eth
  • 85,679
  • 53
  • 285
  • 406
Joe
  • 1,173
  • 1
  • 11
  • 31

2 Answers2

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

Ismael
  • 30,570
  • 21
  • 53
  • 96
max taldykin
  • 2,966
  • 19
  • 27
  • 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 '17 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. – max taldykin Feb 28 '17 at 16:11
  • 1
    I copy pasted your demo code and still get different addresses when I use it. – Joe Feb 28 '17 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("http://localhost:8545")) instead of your lines 2- 4 as it couldn't find the module – Joe Feb 28 '17 at 16:24
  • What versions of web3.js and ethereumjs-util you are using? Btw, I think now this deserves separate question. – max taldykin Feb 28 '17 at 16:25
  • 1
    http://ethereum.stackexchange.com/questions/12621/getting-the-wrong-address-back-from-ecrecover – Joe Mar 01 '17 at 11:57
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");