This is a just a short article about a question that comes up quite frequently, from people using ethers.js. How do I verify a signed message in Solidity.

When a message is signed in Ethereum (e.g. using eth_signMessage ), it is first prefixed with the header \x19Ethereum Signed Message:

followed by the length of the message and then finally the message itself. First it is useful to understand why each of these things is done.

All transactions in Ethereum are encoded using Recursive Length Prefix (RLP) encoding. Without getting too deep into the details of RLP, the \x19 at the beginning is an intentionally invalid byte for a transaction to begin with. This prevents an application from tricking you into signing what appears to be a message, but is actually a transaction in disguise.

The Ethereum Signed Message:

make the contents of the message human readable. Notice that this prefix is 25 characters long, which in hex is 0x19 . This allows the string to be properly read as a length-prefixed string, which is bit of legacy from Bitcoin signed messages.

The length included in the prefix is also largely legacy from Bitcoin, but does possibly provide some additional protection in the event of the hashing algorithm is broken, and can also assist in debugging.

Once the header is prepended to the message, the message is hashed using keccak256 and that digest is signed using a secp256k1 private key.

So, now we can create a function which can take a message and a signature, construct the header, compute the hash of the full payload and recover the address. (do not worry if it looks scary, you may skip it and come back to it later)

contract Verifier { // Returns the address that signed a given string message

function verifyString(string message, uint8 v, bytes32 r,

bytes32 s) public pure returns (address signer) { // The message header; we will fill in the length next

string memory header = "\x19Ethereum Signed Message:

000000"; uint256 lengthOffset;

uint256 length;

assembly {

// The first word of a string is its length

length := mload(message) // The beginning of the base-10 message length in the prefix

lengthOffset := add(header, 57)

} // Maximum length we support

require(length <= 999999); // The length of the message's length in base-10

uint256 lengthLength = 0; // The divisor to get the next left-most message length digit

uint256 divisor = 100000; // Move one digit of the message length to the right at a time

while (divisor != 0) { // The place value at the divisor

uint256 digit = length / divisor; if (digit == 0) {

// Skip leading zeros

if (lengthLength == 0) {

divisor /= 10;

continue;

}

} // Found a non-zero digit or non-leading zero digit

lengthLength++; // Remove this digit from the message length's current value

length -= digit * divisor; // Shift our base-10 divisor over

divisor /= 10;



// Convert the digit to its ASCII representation (man ascii)

digit += 0x30; // Move to the next character and write the digit

lengthOffset++;

assembly {

mstore8(lengthOffset, digit)

}

} // The null string requires exactly 1 zero (unskip 1 leading 0)

if (lengthLength == 0) {

lengthLength = 1 + 0x19 + 1; } else {

lengthLength += 1 + 0x19;

} // Truncate the tailing zeros from the header

assembly {

mstore(header, lengthLength)

} // Perform the elliptic curve recover operation

bytes32 check = keccak256(header, message);

return ecrecover(check, v, r, s);

} }

This code may look a bit complex, and uses inline EVM assembly in a few places. This should generally be frowned upon, but (as far as I know) there is no pure-Solidity way to do this efficiently.

“Code that drops down to inline assembly without any clear reason why will look immediately suspicious.” ~Nick Johnson

Once we have our contract deployed, it is quite simple to use from JavaScript.

var ethers = require('ethers'); // The message...

var message = "Hello World"; // Sign the message (this could also come from eth_signMessage)

var wallet = new ethers.Wallet(privateKey);

var signature = wallet.signMessage(message) // Split the signature into its r, s and v (Solidity's format)

var sig = ethers.utils.splitSignature(signature); // Call the contract with the message and signature

var promise = contract.verifyString(message, sig.v, sig.r, sig.s);

promise.then(function(signer) { // Check the computed signer matches the actual signer

console.log(signer === wallet.address);

});

This is a fairly simple example of this technique, mostly as a toy. But you could use this for example to have a central authority distribute signed messages, which could be redeemed as labels against an ENS registrar for custom sub-names, or to unlock specific named tokens in an ERC-721 contract.

Thanks for reading! Any feedback or suggestions are welcome. Please feel free to follow me on Twitter and chime in on any of my other random thoughts.