Reputation: 3773
I need to be able to retrieve a document from the contract, have it signed by a users wallet and then send back to the contract and the signature validated.
Here's how I have address0 sign it on the client side:
let message : string = "check this message signed by account0";
let messageHash = keccak256(utils.toUtf8Bytes(message));
let signature = await address0.signMessage(messageHash);
await hm.connect(address0).verifyMessage(message, signature);
Here's the validator in my contract:
function verifyMessage(string memory message,
bytes memory signature)
public view returns(bool) {
//hash the plain text message
bytes32 messagehash = keccak256(bytes(message));
//hash the prefix and messagehash together
bytes32 messagehash2 = keccak256(abi.encodePacked("\x19Ethereum Signed Messsage:\n32", messagehash));
//extract the signing contract address
address signeraddress = ECDSA.recover( messagehash2, signature);
if (msg.sender==signeraddress) {
//The message is authentic
return true;
} else {
//msg.sender didnt sign this message.
return false;
}
}
Regrettably, the value returned by ECDSA.recover for signeraddress is not account0's address, and despite a lot of experimentation I've not been able to derive the correct address of the message sender from the signature.
Would appreciate any pointers.
Upvotes: 1
Views: 1624
Reputation: 3773
I was able to get an answer from folks over on Openzeppelin.
In case any body else runs into the situation, one issue is the way that the hash is calculated and the signature is computed on the client side
Instead of
let messageHash = keccak256(utils.toUtf8Bytes(message));
use
let messageHash = ethers.utils.solidityKeccak256(['string'], [message]);
and instead of
let signature = await address0.signMessage(messageHash);
use
let signature = await address0.signMessage(ethers.utils.arrayify(messageHash));
On the server side, the addition of the prefix can be done more simply with `ECDSA.toEthSignedMessageHash() as in the solution below:
using ECDSA for bytes32;
function verifyMessage(string memory message, bytes memory signature) public view returns(address, bool) {
//hash the plain text message
bytes32 messagehash = keccak256(bytes(message));
address signeraddress = messagehash.toEthSignedMessageHash().recover(signature);
if (msg.sender==signeraddress) {
//The message is authentic
return (signeraddress, true);
} else {
//msg.sender didnt sign this message.
return (signeraddress, false);
}
}
Upvotes: 5