7

I'm trying to get a little example working with ECDSA.sol here: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol

This contract:

  1. Generate a random(ish) bytes32 (stub for a future message digest).
  2. Morph it into an EthSignedHash with keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
  3. Use ecrecover to work out who signed a message.
pragma solidity 0.5.8;

import "../../node_modules/openzeppelin-solidity/contracts/cryptography/ECDSA.sol";

contract Sigs {

    using ECDSA for bytes32;

    function rndHash() public view returns(bytes32) {
        return keccak256(abi.encodePacked(block.number));
    }

    function ethSignedHash(bytes32 messageHash) public pure returns(bytes32) {
        return messageHash.toEthSignedMessageHash();
    }

    function recover(bytes32 hash, bytes memory signature) public pure returns(address) {
        return hash.recover(signature);
    }
}

On the client side:

  1. Conjure up a random(ish) message.
  2. Make an ethHash of the message.
  3. Sign it.
  4. Send ethHash and the signature to ecRecover and get the signer address.

I'm not sure what I'm missing. I think the original message hash should be signed and EIP-712 does the "Ethereum Signing", so I should send the "Ethereum Hash" to ecrecover (as implemented in ECDSA.sol). In any case, no combination brings any joy.

var Sigs = artifacts.require("./Junk/Sigs");

contract("Sigs", accounts => {

  var signer;

  beforeEach(async () => {
    signer = accounts[0];
    sigs = await Sigs.new({from: signer}); 
  });

  it("should recover signer address.", async () =>  {

    console.log("SIGNER: ", signer);

    var random  = await sigs.rndHash({from: signer});
    var ethHash = await sigs.ethSignedHash(random);

    // four possible combinations tried for the next step :/
    var signature = await web3.eth.sign(random, signer);    // sign the random hash or the ethHash
    var recovered = await sigs.recover(ethHash, signature); // recover from the random hash or the ethHash

    console.log("random1: ", random);
    console.log("signature: ", signature);
    console.log("recovered: ", recovered);

    assert.strictEqual(recovered, signer, "The recovered signature does not match the signer.");

  });    
});

I'd be very grateful if someone can sort me out.

Truffle v5.0.41 (core: 5.0.41)
Solidity - 0.5.8 (solc-js)
Node v8.10.0
Web3.js v1.2.1

Thanks!

Update

For anyone else who happens across this, this is the revised, passing test.

const Sigs = artifacts.require("./Junk/Sigs");
const EthCrypto = require("eth-crypto");

contract("Sigs", accounts => {

  var signer;

  beforeEach(async () => {
    signer = accounts[0];
    sigs = await Sigs.new({from: signer}); 
  });

  it("should recover signer address.", async () =>  {

    console.log("SIGNER: ", signer);

    var message = "0x1234";
    console.log("message: ", message);
    var msgHash    = await sigs.messageHash(message);
    var ethHash = await sigs.ethSignedHash(msgHash);

    var signature = await web3.eth.sign(msgHash, signer);    // sign the mesage hash
    signature = signature.substr(0, 130) + (signature.substr(130) == "00" ? "1b" : "1c"); // v: 0,1 => 27,28
    var recovered = await sigs.recover(ethHash, signature); // recover from the ethHash

    console.log("msgHash: ", msgHash);
    console.log("ethHash: ", ethHash);
    console.log("signature: ", signature);
    console.log("recovered: ", recovered);

    assert.strictEqual(recovered, signer, "The recovered signature does not match the signer.");

  });    
});
3
  • 1
    Have you tried to post a question (an issue) on their GitHub or on OpenZeppelin Community? They are usually very responsive (a lot more than this community) and very helpful. Oct 19, 2019 at 6:25
  • Rob, openzeppelin-solidity is now @openzeppelin/contracts. Also in environments like truffle, you can write your import as: import "@openzeppelin/contracts/cryptography/ECDSA.sol"; You don't need to specify node_modules
    – abcoathup
    Oct 21, 2019 at 0:41
  • 1
    Thanks @goodvibration, I am the Community Manager at OpenZeppelin. I do check in here, but spend most of my time in the OpenZeppelin Community Forum
    – abcoathup
    Oct 21, 2019 at 0:45

2 Answers 2

6

The problem is that eth.sign returns a signature where v is 0 or 1 and ecrecover expect it to be 27 or 28.

A note in the documentation for web3 v0.20 is clear:

Note that if you are using ecrecover, v will be either "00" or "01". As a result, in order to use this value, you will have to parse it to an integer and then add 27. This will result in either a 27 or a 28.

You have to do something like this

var signature = await web3.eth.sign(random, signer);    // sign the random hash or the ethHash
signature = signature.substr(0, 130) + (signature.substr(130) == "00" ? "1b" : "1c");
var recovered = await sigs.recover(ethHash, signature); // recover from the random hash or the ethHash
4
7

Rob, I see that you have already resolved (which is great).

For future readers:

For OpenZeppelin Contracts you can check the documentation for more information, in this case the Cryptography section: https://docs.openzeppelin.com/contracts/2.x/utilities#cryptography

The tests for the contracts can be helpful to look at, in this case ECDSA.test.js: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/cryptography/ECDSA.test.js

There is a tutorial on signing in the forum: https://forum.openzeppelin.com/t/sign-it-like-you-mean-it-creating-and-verifying-ethereum-signatures/697

If you have questions on using OpenZeppelin you can also ask in the Community Forum: https://forum.openzeppelin.com/

Disclosure: I am the Community Manager at OpenZeppelin

0

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.