1

Im trying to test some signature verification in Solidity. I have generated the signature with nodejs using the ethers.js library. The signature successfully verifies in nodejs.

I have tried to verify the same signature in solidity but it recovers a different signer address.

NodeJS Code

"use strict";
const ethers = require("ethers");

// remix account #0. Address: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
var wallet = new ethers.Wallet("PK HERE");   

console.log("Wallet Address: " + wallet.address);    

async function SignData() {        

    var packedAddress = ethers.utils.solidityPack(["address"], [wallet.address]);
    var sig = await wallet.signMessage(packedAddress);

    console.log("Sig1 " + sig);

    console.log("Sig1 Valid? " + (ethers.utils.verifyMessage(packedAddress, sig) == wallet.address));

}

SignData();

Console output:

Sig1    0xbc223797d57c0cac4405c7751a54cae569c8a96c7a2296af28c18b45291a54f5774b1606277dfa1b19e83b24ab415e63b4dcd60be194f73c0ebe5cb3cd3e40e61c
Sig 1 Valid? true

Remix Code to verify the same signature

function testVerify(address signer, bytes memory signature_rsv) external pure returns(bool, address) {
    bytes32 hashedMessage = keccak256(abi.encodePacked(signer));

    (uint8 v, bytes32 r, bytes32 s) = splitSignature(signature_rsv);

    address recov_signer = ecrecover(hashedMessage, v, r, s);

    return (recov_signer == signer, recov_signer);
}

function splitSignature(bytes memory sig)
    internal
    pure
    returns (uint8 v, bytes32 r, bytes32 s)
{
    require(sig.length == 65, "Signature length is wrong !");

    assembly {
        // first 32 bytes, after the length prefix.
        r := mload(add(sig, 32))
        // second 32 bytes.
        s := mload(add(sig, 64))
        // final byte (first byte of the next 32 bytes).
        v := byte(0, mload(add(sig, 96)))
    }
    return (v, r, s);
}

Remix returns:

0: bool: false
1: address: 0x0E929401ba22b6b2035DdDc1A3D3F078E7779560

2 Answers 2

0

Yes, so ethers.utils is checking the two signatures successfully, whereas your mock up of the same functionality in splitSignature is not.

I believe it actually comes down to the way you are trying to create the assembly and might have declared v in the wrong way. Try

v := and(mload(add(sig, 65)), 255)

...also, having read the docs, is also super important to add the following line, due to the function of web3 vs the EVM:

if (v < 27) v += 27;

Hope that helps.

EDIT

problem solved by @CathalMF

I found the problem. The wallet.signMessage function of ethers.js automatically adds a "Ethereum Signed Message" prefix. So instead I used the wallet._signingKey().signDigest() function

3
  • No that didnt make any difference. Both your suggestion and the original post are valid ways to get the V value.
    – CathalMF
    Jan 10 at 9:49
  • just to check, did you add if (v < 27) v += 27; ?
    – immaxkent
    Jan 10 at 16:39
  • I found the problem. The wallet.signMessage function of ethers.js automatically adds a "Ethereum Signed Message" prefix. So instead I used the wallet._signingKey().signDigest() function
    – CathalMF
    Jan 10 at 17:54
0

I assume it's because node.js signMessage involves a default prefix: "\x19Ethereum Signed Message:", which ecrecover in solidity excludes. In order to sign a message ecrecover can recognize, I suggest 2 solutions:

  1. sign gthe message without "\x19Ethereum Signed Message:".
  2. while using ecrecover, include the aboved mentioned prefix in solidity code.

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.