question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Possible error in ECDSA.sol

See original GitHub issue

It appears that ECDSA.recover returns incorrect address for a signature created on the client side by ethers.SignerWithAddress.signMessage.

💻 Environment

Solidity 8.4 ethers.js version 5.4.7 Hardhat version 2.6.5 Running in VS Code locally with MetaMask 10.2.2

📝 Details

I am attempting to use ECDSA.recover to confirm that that a document string supplied to contract in a function call was in fact signed by the account of msg.sender. I have been unable to find any error in the code below but perhaps I misusing the libraries somehow.

It may the problem is that the signature produced by ethers.SignerWithAddress.signMessage(msg) isn’t the kind of signature ECDSA is meant to work with, but I couldn’t tell from the documentation.

I noted that the actual signature produced is a string 132 characters in length, but ECDSA.recover expects a signature of 65 bytes. I noticed that apply abi.encodePacked to the signature reduces it length from 132 to 65, so I assume that is what you’re supposed to do, but perhaps not since the function returns an address different than the address of the account singing the message.

🔢 Code to reproduce bug

Client side code (Typescript):

            let contract_address = await TokenHelper.deploy();
            let hm = await ethers.getContractAt("HiveMind", contract_address)
            let message : string = "check that this message was signed by account0";
            //hash the plain text message; 
            let messageHash = keccak256(utils.toUtf8Bytes(message));
            //account0 signs the message;
            let signature = await address0.signMessage(utils.toUtf8Bytes(messageHash));
            //verify the message
            let output = await hm.connect(address0.address).verifyMessage(message, signature);
            console.log(`Actual address:    ${address0.address}`)
            console.log(`Recovered address: ${output[0]} ${output[1]}`);
            console.log("");
            console.log("ethers.js version", ethers.version)      

Output

TokenTest

Actual address:    0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Recovered address: 0xB21D540039aB6A0F01b01c0b7bccdC887B67Bc56 false
ethers.js version ethers/5.4.7

Contract Code:

function verifyMessage(string memory message, bytes memory signature) 
        public view  returns(address, bool) {
        //hash the plain text message
        bytes32 messagehash =  keccak256(bytes(message));
        bytes memory sig = abi.encodePacked(signature);
        address signeraddress = messagehash.toEthSignedMessageHash().recover(sig);
              
        if (msg.sender==signeraddress) {
            //The message is authentic
            return (signeraddress, true);
        } else {
            //msg.sender didnt sign this message.
            return (signeraddress, false);
        }
    }

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
frangiocommented, Oct 15, 2021

Ok, @julianmrodri had the answer but it wasn’t solidityKeccak256. 😄

@grapevinegizmos The problem is that you’re signing utils.toUtf8Bytes(messageHash), i.e. the string corresponding to the hex encoded message hash, as opposed to the actual bytes of the message hash.

The fix is to use arrayify:

await address0.signMessage(utils.arrayify(messageHash))

I’ve confirmed that this recovers to the correct address.

1reaction
grapevinegizmoscommented, Oct 15, 2021

Many thanks @frangio @julianmrodri

I was able to get it working. If either of you are in a position to update the documentation on OpenZeppelin of how to actually use ECDSA I think it might save developers a lot of time to see an example like this of how to encode a message on the client side so that you can decode the signature within the contract. Its not so obvious, as maybe you can see. Thanks again.

Read more comments on GitHub >

github_iconTop Results From Across the Web

openzeppelin-contracts/ECDSA.sol at master - GitHub
OpenZeppelin Contracts is a library for secure smart contract development. - openzeppelin-contracts/ECDSA.sol at master ...
Read more >
What is the purpose of OpenZeppelin ECDSA.sol contract?
1 Answer 1 · Checking for malleable signatures · Support for 64 bytes compact signatures · Signature message manipulation, adding Ethereum ...
Read more >
how to use the ECDSA.sol module correctly? - Stack Overflow
I have this contract using the ECDSA library.
Read more >
How use ECDSA? - Contracts - OpenZeppelin Forum
The contract is recovering the signer address correctly. The issue is that the message is signed by signerIdentity , whereas the msg. sender...
Read more >
How to fix warning about ECDSA host key - Super User
I tried deleting the .ssh directory on the remote machine, and running ssh-keygen -R "myserver" locally, but this doesn't resolve the error ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found