Signing and Verifying Messages in Ethereum (2024)

Cryptographic signatures are a powerful primitive in Ethereum. Signatures are used to authorize transactions, but they’re a more general tool that’s available to smart contracts. Signatures can be used to prove to a smart contract that a certain account approved a certain message.

In this post, I’ll build a simple smart contract that lets me transmit ether in an unusual way. Instead of calling a function myself to initiate a payment, I’ll let the receiver of the payment do that—and therefore pay the transaction fee! Only authorized payments will be allowed, thanks to cryptographic signatures that I will make and send off-chain (e.g. via email).

Paying someone with the ReceiverPays contract works a lot like writing a check. The person receiving the payment gets to take money directly out of my bank account, but only if they have a valid check bearing my signature.

Overview

The ReceiverPays contract holds ether on behalf of its owner and allows that ether to be withdrawn when authorized by a cryptographic signature:

  1. The owner deploys ReceiverPays, attaching enough ether to cover the payments that will be made.
  2. The owner authorizes a payment by cryptographically signing a message with their private key.
  3. The owner sends the signed message to the designated recipient. The message does not need to be kept secret, and the mechanism for sending it doesn’t matter. It can be sent via email, instant message, or even a public forum.
  4. The recipient claims their payment by presenting the signed message to the smart contract. The smart contract verifies its authenticity and then releases the funds.

Creating the Signature

Signing a message with a private key does not require interacting with the Ethereum network. It can be done completely offline, and every major programming language has the necessary cryptographic libraries to do it. The signature algorithm Ethereum has built-in support for is the Elliptic Curve Digital Signature Algorithm (EDCSA)

For this post, I’ll focus on signing messages in the web browser using web3.js and MetaMask.

There are still a number of ways to sign messages using web3.js, and, unfortunately, different methods sign data in incompatible ways. Members of the community are actively working to standardize on a new way to sign messages (EIP-712) that has a number of other security benefits. For now, though, my advice is to stick with the most standard format of signing, as specified by the eth_sign JSON-RPC method:

The sign method calculates an Ethereum specific signature with:sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).

By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim.

At least in MetaMask, this algorithm is followed best by the web3.js function web3.personal.sign, so I recommend using that: 1

// Hashing first makes a few things easier.var hash = web3.sha3("message to sign");web3.personal.sign(hash, web3.eth.defaultAccount, function () { ... });

Recall that the prefix includes the length of the message. Hashing first means the message will always be 32 bytes long, which means the prefix is always the same. This makes life easier, particularly on the Solidity side, as I’ll demonstrate later in this post.

What to Sign

For a signature to represent authorization to take some action, it’s important that the message specify exactly what’s being authorized. For a contract that fulfills payments, the signed message must include the address of the recipient and the amount that is to be transferred. In addition, the message should include data that protects against replay attacks.

Avoiding Replay Attacks

A replay attack is when a signature is used again (“replayed”) to claim authorization for a second action. For our example, it would be a serious security flaw if the recipient of a payment could submit the same signature again to receive a second payment. Just as in Ethereum transactions themselves, messages typically include a nonce to protect against replay attacks. The smart contract checks that no nonce is reused:

mapping(uint256 => bool) usedNonces;function claimPayment(uint256 amount, uint256 nonce, bytes sig) public { require(!usedNonces[nonce]); usedNonces[nonce] = true; // ...}

There’s another kind of replay attack that we need to protect against. Suppose I deploy the ReceiverPays smart contract, make some payments, and then destroy the contract. Later, I would like to make more payments, so I decide to deploy the RecipientPays smart contract again. The new deployed instance of the contract doesn’t know about the used nonces from the previous deployment, so it will happily pay in response to any of the old signatures again.

A simple way to protect against this type of replay attack is to make sure the message includes the contract’s address. The new contract will reject all signatures that reference the wrong (old) address.

Packing Arguments

Now that I’ve identified what information to include in the signed message, I’m ready to put the message together, hash it, and sign it. Solidity’s keccak256/sha3 function hashes multiple arguments by first concatenating them in a tightly packed form. For the hash generated on the client to match the one generated in the smart contract, the arguments must be concatenated in the same way.

The ethereumjs-abi library 2 provides a function called soliditySHA3 that mimics the behavior of Solidity’s keccak256 function. It accepts an array of values as well as an array of their Solidity types so it can serialize the values accordingly.

Putting it all together, here’s a JavaScript function that creates the proper signature for the ReceiverPays example: 3

// recipient is the address that should be paid.// amount, in wei, specifies how much ether should be sent.// nonce can be any unique number, used to prevent replay attacks.// contractAddress is used to prevent cross-contract replay attacks.function signPayment(recipient, amount, nonce, contractAddress, callback) { var hash = "0x" + ethereumjs.ABI.soliditySHA3( ["address", "uint256", "uint256", "address"], [recipient, amount, nonce, contractAddress] ).toString("hex"); web3.personal.sign(hash, web3.eth.defaultAccount, callback);}

Recovering the Message Signer in Solidity

In general, ECDSA signatures consist of two parameters, r and s. Signatures in Ethereum include a third parameter, v, which provides additional information that can be used to recover which account’s private key was used to sign the message. This same mechanism is how Ethereum determines which account sent a given transaction.

Solidity provides a built-in function ecrecover that accepts a message along with the r, s, and v parameters and returns the address that was used to sign the message.

Extracting the Signature Parameters

Signatures produced by web3.js are the concatenation of r, s, and v, so a necessary first step is splitting those parameters back out. This process can be done on the client, but I find it more convenient to do it inside the smart contract. This means only one signature parameter needs to be sent rather than three.

Unfortunately, splitting apart a bytes array into component parts is a little messy. I’m making use of inline assembly to do the job:

function splitSignature(bytes sig) internal pure returns (uint8, bytes32, bytes32){ require(sig.length == 65); bytes32 r; bytes32 s; uint8 v; 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);}

Here’s a brief explanation of that code:

  • Dynamically-sized arrays (including bytes) are represented in memory by a 32-byte length followed by the actual data. The code here starts reading data at byte 32 to skip past that length. The require at the top of the function ensures the signature is the correct length.
  • r and s are 32 bytes each and together make up the first 64 bytes of the signature.
  • v is the 65th byte, which can be found at byte offset 96 (32 bytes for the length, 64 bytes for r and s). The mload opcode loads 32 bytes at a time, so the function then needs to extract just the first byte of the word that was read. This is what byte(0, ...) does.

Computing the Message Hash

In addition to the r, s, and v parameters from the signature, recovering the message signer requires knowledge of the message that was signed. The message hash needs to be recomputed from the sent parameters along with the known prefix.

It may seem tempting at first to just have the caller pass in the message that was signed, but that would only prove that some message was signed by the owner. The smart contract needs to know exactly what parameters were signed, and so it must recreate the message from the parameters and use that for signature verification:

// Builds a prefixed hash to mimic the behavior of eth_sign.function prefixed(bytes32 hash) internal pure returns (bytes32) { return keccak256("\x19Ethereum Signed Message:\n32", hash);}function claimPayment(uint256 amount, uint256 nonce, bytes sig) public { require(!usedNonces[nonce]); usedNonces[nonce] = true; // This recreates the message that was signed on the client. bytes32 message = prefixed(keccak256(msg.sender, amount, nonce, this)); require(recoverSigner(message, sig) == owner); msg.sender.transfer(amount);}

Finally, the implementation of recoverSigner is straightforward:

function recoverSigner(bytes32 message, bytes sig) internal pure returns (address){ uint8 v; bytes32 r; bytes32 s; (v, r, s) = splitSignature(sig); return ecrecover(message, v, r, s);}

Summary

  • Signed messages provide a way to authenticate a message to a smart contract.
  • Signed messages may need a nonce to protect against replay attacks.
  • Signed messages may need to include the contracts’s address to protect against replay attacks.
  • Until a better signature standard is adopted, I recommend following the behavior of the eth_sign JSON-RPC method.

Full Source Code

receiverPays.sol

pragma solidity ^0.4.20;contract ReceiverPays { address owner = msg.sender; mapping(uint256 => bool) usedNonces; // Funds are sent at deployment time. function ReceiverPays() public payable { } function claimPayment(uint256 amount, uint256 nonce, bytes sig) public { require(!usedNonces[nonce]); usedNonces[nonce] = true; // This recreates the message that was signed on the client. bytes32 message = prefixed(keccak256(msg.sender, amount, nonce, this)); require(recoverSigner(message, sig) == owner); msg.sender.transfer(amount); } // Destroy contract and reclaim leftover funds. function kill() public { require(msg.sender == owner); selfdestruct(msg.sender); } // Signature methods function splitSignature(bytes sig) internal pure returns (uint8, bytes32, bytes32) { require(sig.length == 65); bytes32 r; bytes32 s; uint8 v; 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); } function recoverSigner(bytes32 message, bytes sig) internal pure returns (address) { uint8 v; bytes32 r; bytes32 s; (v, r, s) = splitSignature(sig); return ecrecover(message, v, r, s); } // Builds a prefixed hash to mimic the behavior of eth_sign. function prefixed(bytes32 hash) internal pure returns (bytes32) { return keccak256("\x19Ethereum Signed Message:\n32", hash); }}
  1. MetaMask’s UI shows the user the message they’re signing, but since here the message is a hash, it will never be meaningful to the user. This is one of the issues that EIP-712 is intended to solve.
  2. You can find browser builds of ethereumjs-abi in the ethereumjs/browser-builds repository.
  3. Web3.js 1.0.0-beta includes a similar function called soliditySha3, but at the time of this writing, that version is still unreleased and therefore lacking in compatibility with other tools, including MetaMask.
Signing and Verifying Messages in Ethereum (2024)

FAQs

How do you verify message signature in Ethereum? ›

To verify a signature:
  1. You need the message, the signature (r, s, v), and the public key or Ethereum address of the signer.
  2. Extract the public key from the signature using the recovery id (v).
  3. Verify that the signature corresponds to the given message hash and extracted public key.
Dec 11, 2023

Is signing a message safe on Ethereum? ›

Kind of, you can sign a message off-chain for zero gas that could be used maliciously. One of the oldest, and most dangerous ways to sign data, is eth_sign . See this quote from Metamask: it allows the website you're on to request that you sign an arbitrary hash.

How does signing an Ethereum transaction work? ›

Transactions in Ethereum are cryptographically signed data messages that contain a set of instructions. These instructions can interpret to sending Ether from one Ethereum account to another or interacting with a smart contract deployed on the blockchain.

How to verify a signature on Etherscan? ›

To verify this signed message, click on the Verify Signature button on our Verified Signatures page and enter the message details into their respective fields.
  1. Address = The signer's address.
  2. Message = The entire message that was signed by the signer's address. ...
  3. Signature Hash = Usually stylized as "sig".
Aug 8, 2023

How do I verify my signature? ›

How do I verify a digital signature? Open the PDF file in PDF Converter Professional. Left-click on the Digital Signature field. Click "Verify Signature".

How do you verify the digital signature of a signed message? ›

Locate the digital signature object within the document. Right click or command-click on the signature object. Select "Verify Signature" from the context menu.

What are the biggest risks to Ethereum? ›

You have a major risk of losing your entire capital. Ethereum is a cryptocurrency running on the blockchain, an unregulated market not backed by anyone or anything. It's like investing in thin air.

Are digitally signed messages encrypted? ›

Your digital signature, which includes your certificate and public key, originates from your digital ID. And that digital ID serves as your unique digital mark and signals the recipient that the content hasn't been altered in transit. For additional privacy, you also can encrypt email messages.

How long is an Ethereum signed message? ›

ECDSA signatures in Ethereum consist of three parameters: v , r and s . The signature is always 65-bytes in length. and returns the address used to sign the message.

How do signed messages work? ›

Message signing helps ensure data integrity, message authentication, and non-repudiation. For example, if John wants to digitally sign a message to Michelle, he uses his private key to sign the message, and sends it (along with his public key if it hasn't already been sent) to Michelle.

What does an Ethereum signature look like? ›

An Ethereum signature contains three values: v, R, S. A Bitcoin signature contains only two numbers: R and S. The extra value that Ethereum uses makes it possible to recover the public key from the signature which means that an Ethereum transaction does not include the public key.

How to trace Ethereum transactions? ›

There are various blockchain explorers, such as Etherscan, that can be used to track and view all Ethereum transactions. These explorers will provide a detailed transaction history, including relevant information such as gas used, timestamp, transaction fees, and more.

Can anyone verify a contract on Etherscan? ›

Verifying a contract on Etherscan means that you upload the source code and metadata of your smart contract to the Etherscan website, and it matches the bytecode deployed on the blockchain. This allows anyone to view, analyze, and interact with your contract, as well as to verify its authenticity and integrity.

How to find out who owns an Ethereum address? ›

In summary, checking the owner of an Ethereum address can be done using an Ethereum blockchain explorer, searching for public information, or using a third-party service.

How does Etherscan verification work? ›

With etherscan, users can view and verify smart contracts, check the current balance and transaction history of a particular Ethereum address and witness real-time transactions taking place on the Ethereum network. Contract verification is important because it gives room for transparency and trust.

What is the message signature hash in Ethereum? ›

Signature hashes point to a specific signed message on the blockchain, and message verification tools input the signature hash while verifying a message to retrieve the particular message and decode its details using the verifying wallet's public key.

How do I verify my Bitcoin signature? ›

When a Bitcoin transaction occurs, the sender uses their private key to sign the transaction, providing proof of ownership. The network verifies the signature using the sender's public key, ensuring authenticity and preventing double-spending.

Top Articles
Buying Bitcoin Made Easy | Crypto Dispensers
From Blockchain Architects To Skilled Marketers… These Are the Most Sought After Jobs in Web3
Duralast Gold Cv Axle
Riverrun Rv Park Middletown Photos
Palm Coast Permits Online
Practical Magic 123Movies
Melfme
ds. J.C. van Trigt - Lukas 23:42-43 - Preekaantekeningen
The Many Faces of the Craigslist Killer
Enterprise Car Sales Jacksonville Used Cars
Parent Resources - Padua Franciscan High School
Carson Municipal Code
Keurig Refillable Pods Walmart
Gia_Divine
X-Chromosom: Aufbau und Funktion
[Cheryll Glotfelty, Harold Fromm] The Ecocriticism(z-lib.org)
Marine Forecast Sandy Hook To Manasquan Inlet
Drug Test 35765N
Craigslist Alo
Rogue Lineage Uber Titles
What Individuals Need to Know When Raising Money for a Charitable Cause
Watson 853 White Oval
What we lost when Craigslist shut down its personals section
91 Octane Gas Prices Near Me
Vlocity Clm
Kltv Com Big Red Box
Supermarkt Amsterdam - Openingstijden, Folder met alle Aanbiedingen
Panchitos Harlingen Tx
Kvoa Tv Schedule
Atlanta Musicians Craigslist
Indiana Jones 5 Showtimes Near Cinemark Stroud Mall And Xd
Yogu Cheshire
Infinite Campus Farmingdale
Acts 16 Nkjv
VPN Free - Betternet Unlimited VPN Proxy - Chrome Web Store
Bekkenpijn: oorzaken en symptomen van pijn in het bekken
Jammiah Broomfield Ig
Ehc Workspace Login
Gary Vandenheuvel Net Worth
Noga Funeral Home Obituaries
Sky Dental Cartersville
Learn4Good Job Posting
Graduation Requirements
Terrell Buckley Net Worth
Wzzm Weather Forecast
Sapphire Pine Grove
Strange World Showtimes Near Marcus La Crosse Cinema
San Diego Padres Box Scores
Zadruga Elita 7 Live - Zadruga Elita 8 Uživo HD Emitirani Sat Putem Interneta
How To Win The Race In Sneaky Sasquatch
Latest Posts
Article information

Author: Twana Towne Ret

Last Updated:

Views: 6653

Rating: 4.3 / 5 (64 voted)

Reviews: 95% of readers found this page helpful

Author information

Name: Twana Towne Ret

Birthday: 1994-03-19

Address: Apt. 990 97439 Corwin Motorway, Port Eliseoburgh, NM 99144-2618

Phone: +5958753152963

Job: National Specialist

Hobby: Kayaking, Photography, Skydiving, Embroidery, Leather crafting, Orienteering, Cooking

Introduction: My name is Twana Towne Ret, I am a famous, talented, joyous, perfect, powerful, inquisitive, lovely person who loves writing and wants to share my knowledge and understanding with you.