Asymmetric cryptography is one of the most important computer science inventions of the previous century. It also lies at the heart of all blockchain technology. In this post we’ll take a deeper look at how Ethereum uses the Elliptic Curve Digital Signature Algorithm (ECDSA) to validate the origin and integrity of messages.
Encryption techniques like ECDSA are also essential in securely extending existing blockchains. We’ve seen this in my past post analyzing decentralized exchanges where a DEX uses signatures in it’s offchain communication. As the blockchain ecosystem matures I expect we will see more Layer-2 and Layer-3 extensions of the existing low level blockchain infrastructure.
Private Keys and Public Keys
In Ethereum like any other blockchain system there is a private and a public key. These keys are generated when you create a new blockchain “account”. Keeping the private key secure is essential because any copy of it allows access to the ledger. Hardware wallets to securely store the private key have become an essential best practice.
The notion of an account is a bit of a misnomer, because in strict technical terms there are only keys and a ledger of funds that correspond with those keys. An Ethereum or Bitcoin address is essentially a hashed version of the public key.
Elliptic Curve Cryptography (ECC) and ECDSA are a specific flavor of asymmetric cryptography. They are widely used in blockchain technology because of three reasons:
- Their computational performance is economical compared to a lot of other algorithms
- The keys that are generated are relatively short
- Bitcoin started it, so most new blockchain projects have copied it
ECDSA uses the algebraic structure of elliptic curves over finite fields. Without getting into the mathematics of it, they require a set of constants to define this curve. The constants used by most blockchains are set in the secp256k1 standard.
Before blockchain, this elliptic curve standard was not common at all. In fact, most mainstream hardware vendors don’t support hardware encryption for this curve. It is rumored that secp256k1 was picked because it has the least likelihood of having kleptographic backdoors implanted by the NSA.
How Signing Works
In a blockchain system, any key holder can use their private key to sign a piece of data. This results in a signature. Whoever obtains the signature can use this to:
- Recover the public key (account address) of the Author
- Verify whether the message is the same as the one signed by Author
Let’s take a look at the exact functions used in Ethereum to do the signing:
The first line creates a SHA3 hash of the message we want to sign. This results in the following 32 bytes (256 bits) hash:
Line 2 then uses Ethereum’s JSON RPC to tell the Ethereum wallet (which controls the private key) to sign the message on a given account, resulting in a signature. The final line is decoding the JSON RPC output so that we obtain the signature values V, R and S. (V is something that was added to address a certain kind of attack).
Both Smart Contracts and Ethereum clients have the ability to verify ECDSA signatures. ECDSA verification in Smart Contracts allows tamper proof communications outside of the blockchain. There are many projects out there like for example $ZRX that rely on this for offchain communication.
In Solidity, a signed message can be verified with the following code:
This code will return the Ethereum address (public key) that was used to sign the message. Any change of the message hash or signature will result in a different address than the origin address.
Sometimes it’s good to take a look under the hood of these functions, just so we can get a deeper understanding. The following two sections follow the code that’s used in the popular Meta Mask wallet and the inner guts of the Go-Ethereum Smart Contract code.
Appendix A: Signing Code inside Meta Mask
Meta Mask is a Chrome extension that creates a user friendly transaction experience. Users can send/receive Ether, sign messages and interact with Smart Contracts. It also comes with an awesome 3D polygon fox that follows your cursor as you type.
Private keys in Meta Mask are stored in the browser’s local storage. Meta Mask uses an external library to encrypt the private key information with a password. (Using AES-GCM).
When we sign messages using Meta Mask we get a popup that shows the message and account to sign from:
When we examine the underlying code we pass through the following libraries — most of which are controlled by the Meta Mask developers.
The most relevant Ethereum JS code for the signing is as follows:
There is a lot of data structure conversion going on, but in the final function we can see how the R, S, V signature is obtained from calling the secp256k1.sign() function. Which leads us to the following piece of fundamental code inside the elliptic library:
Appendix B: Signing Code inside Smart Contracts / Go-Ethereum
Let’s take a closer look at the aforementioned contract code that recovers the public key from the signature:
The ecrecover() function provided by Solidity is actually a bit special. It’s one of the few “native contracts” available in smart contracts. These are internal in-code smart contracts that can be called. Here’s an alternative way of calling ecrecover() in Solidity EVM assembler:
The call() function executes the contract at fixed address 3000. It then executes the following code (in case of the go-ethereum implementation):
This eventually calls out to the following C code to recover the public key (using the pubkey pointer):