[Ethereum] workflow on signing a string with private key, followed by signature verification with public key


I know this question has been asked and answered, but while trying to get everything to work, I've come across some problems. This is what I'm trying to do:

1. take a target string 'Schoolbus'
2. use JSON with geth to eth_sign it
3. obtain v,r,s of signature
4. attempt to verify with a solidity contract, need the hash of 'Schoolbus'

So here's what I got. First of all, we can't all use the same private key, so if someone can verify my work and get a gist of my problem, that would be great.

Pretending that my priv key is '0xd1ade25ccd3d550a7eb532ac759cac7be09c2719', to sign 'Schoolbus', I use

curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sign","params":["0xd1ade25ccd3d550a7eb532ac759cac7be09c2719", "Schoolbus"],"id":1}'

Where I get the result


I'm using (I might be wrong here)


Then I whipped up my contract, which is a variation of an answer in a related thread:

contract Auth {      
    function verify( bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address retAddr) {
        retAddr= ecrecover(hash, v, r, s);

Because I never got the hash of 'Schoolbus', I tried a couple of things in web3.js (the UTF8 one confused me, I was expecting that {encoding:'hex'} version to be the correct one):

console.log('1 '+ web3.sha3(web3.toHex('Schoolbus'))); //05ab39621b81764697fcfb6ae4fcf6b023cd644721c67c13a49fbd769c75671c
console.log('2 '+ web3.sha3(web3.toHex('Schoolbus'),{encoding:'hex'}));//d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c
console.log('3 '+ web3.sha3('Schoolbus'));//d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c
console.log('4 '+  web3.sha3(  unescape(encodeURIComponent('Schoolbus'))  ) ); //to UTF8 //d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c
console.log('5 '+  web3.sha3(  unescape(encodeURIComponent('Schoolbus')), {encoding:'hex'} ) ); //to UTF8 //8f1cbe7efcf383ffeb1aeaf1e826c778a087153344cbeba144fbe967ad3ab11a

I ended up using this, but don't know why:


Then I called the contract:

var contDep=web3.eth.contract( [abi def] ).at( contractAddress);

    contDep.verify('d030d9a04df643f62a1502b017f51c41a659268091abbd20e2de97b935724d7c', 2a,'c19db245478a06032e69cdbd2b54e648b78431d0a47bd1fbab18f79f820ba407', '466e37adbe9e84541cab97ab7d290f4a64a5825c876d22109f3bf813254e8601')

Here's my problem. I keep getting this weird address back. It starts with 0x, it's 20 bytes, but it doesn't have [a-f] in it:


If I swapped r and s, I get almost the same result back.
I was wondering if someone can verify my experience, or point out what I was doing wrong. I feel like a crazy person here.

Thanks for all your help.

Best Answer

I was having the same problem earlier, so I am going to give an extensive answer to how this works. I assume you are using geth as a client. There is an open issue where the geth client returns v in the wrong format, so let's keep in mind that if we get a v that is 0 or 1 we should add 27 to it. If you are running node and have connected web3 to your favorite client:

var msg = web3.sha3('Schoolbus')
var signature = web3.eth.sign(web3.eth.accounts[0], msg)

In my case, the signature is:


The string represents r, s, and v respectively in that order. To feed it to your Auth contract however, you need to convert v to an uint8 and add make sure to have the hex prefix 0x everywhere:

var r = signature.slice(0, 66)
var s = '0x' + signature.slice(66, 130)
var v = '0x' + signature.slice(130, 132)
v = web3.toDecimal(v)
msg = '0x' + msg

Remember that v should be 27 or 28! If it isn't, set v = v + 27. You can now call your verify function like:

var addr = Auth_instance.verify.call(msg, v, r, s)

and you can check that addr has the same value as web3.eth.accounts[0].

Related Topic