Cryptographic signatures have many different use cases in the Ethereum ecosystem. Signatures are used for verification of ownership, payments, authorization, etc. The combination of smart contracts and cryptographic signatures allow payments to be made off chain for a future date and time. This is similar to writing checks to pay for a product or service. In this tutorial we will focus on how to sign and verify signatures in Ethereum off chain. This will be the beginning of a two part introduction on how to setup a payment channel.
How to encrypt and sign a message in Ethereum
The process below will demonstrate how one can use a smart contract and web3 to achieve signing a message off chain. Transactions are not written to the blockchain.
To keep it simple we will:
- First use Remix to encrypt / hash a message in keccak256
- Make sure you have MetaMask installed and you are logged in to the test network
- Then use the web browser to sign the message in MetaMask
Deploy a smart contract in Remix to hash a string and verify signatures
First create a simple contract in Remix that contains the three functions below. These functions are:
- getHash – Used to hash a string
- getEthSignedHash – Takes the getHash results and prefixes with “\x19Ethereum Signed Message” and hashes the message again
- verify – allows you to input the getEthSignedHash results and the users signature hash results to determine what Ethereum account signed the transaction
pragma solidity ^0.7;
contract VerifySignature {
// use this function to get the hash of any string
function getHash(string memory str) public pure returns (bytes32) {
return keccak256(abi.encodePacked(str));
}
// take the keccak256 hashed message from the getHash function above and input into this function
// this function prefixes the hash above with \x19Ethereum signed message:\n32 + hash
// and produces a new hash signature
function getEthSignedHash(bytes32 _messageHash) public pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash));
}
// input the getEthSignedHash results and the signature hash results
// the output of this function will be the account number that signed the original message
function verify(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) {
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
return ecrecover(_ethSignedMessageHash, v, r, s);
}
}
Try it in Remix
After the contract is locally deployed hash the word “hello”. The Keccak256 algorithm will generate an alpha numeric hash string.
Since we do not have any Web3 code written we will type several commands manually in the bowser to illustrate how this could work. Naturally this process would be automated in a decentralized application.
Use the web browser to sign an Ethereum transaction
Next, in your browser press F12 to launch your developer console. This console will allow you to write simple commands and inspect code. Clear the console to remove any text that is present on the screen. We will type in a few commands that will interact with MetaMask and Ethereum.
Type the word hash = “hash generated in Remix” and paste in the hash that you generated in Remix and press enter. This will create a variable called hash which we will use in a future function.
Then type ethereum.enable().then(console.log). A promise message will display to indicate the default account in MetaMask. The account displayed will be used to sign the transaction.
Copy your account number and assign it to a variable named account which we will use in a future function. Type account = “your MetaMask account”.
Type ethereum.request({method: “personal_sign”, parms: [account, hash]}).then(console.log) which will initiate signing the hash. The method “personal_sign” takes the parameters account and hash (which you set above). Your MetaMask wallet will popup asking if you approve or reject signing the message. Confirm that the hash you entered in the console is the same hash you are signing with your wallet.
After pressing “Sign” in MetaMask your signed hash will display in the console. This hash is used to confirm the message was signed by your private key.
How to determine the account that signed a message
Return to your contract in Remix and copy the “hello” hash from the getHash function and paste it in the getSignedHash function. This function prefixes the hash above with \x19Ethereum signed message:\n32 + hash and produces a new hash signature. This is preparation for the final verification step.
Finally take the hash that getEthSignedHash generated and the signed hash from your browser above and input both hash’s into the verify function in Remix. The verify function will display the account that signed the transaction. If the account is different in Remix that means a different account signed the message or something went wrong in the process.
This was a simple example of how to create a hash, sign it with your wallet and verify the account that signed the message. This can be implemented using a combination of a smart contract in solidity and Web3 in Python or JavaScript.
This code is for learning and entertainment purposes only. The code has not been audited and use at your own risk. Remember smart contracts are experimental and could contain bugs.
Click here for more information about how to use the Ethereum test network and how to obtain test ETH.