Signatures: Verifying Messages in Solidity
RicMoo — April 17, 2018

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:n" 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:n" 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:\n000000";
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.