As we have already gathered the vast majority of theoretical knowledge that we needed to get started with our web3 journey, the ratio of new things per week started to go down. Don't get me wrong, we haven't stopped learning, we are just learning more complex stuff which requires more time to digest, and we also started working on a small project of our choice to practice as much as possible (I will probably do an extra post at the end of the course).
On top of that, we also had several crypto talks with amazing guests where we learned about Zero-Knowledge Proofs, Foundry, or the Graph protocol. Yes, I know what you are thinking... I have a lot of interesting topics in my backlog and I should definitely produce more content!
So, now that you know what we have been up to, let's focus on digital signature verification and ecrecover
.
First things first. Digital Signatures
Before figuring out what the hell ecrecover
is, we need to master the basics. You have probably signed messages with your wallet, but have you ever stopped think about how does it actually work?
Digital signatures work similar to asymmetric cryptography encryption but in the opposite way.
In digital signatures, some data is signed by hashing the message with the sender’s private key (and obviously a hashing algorithm too). This produces a hash digest, that can only be recreated by using one of the keys in the key pair used by the sender. The recipient will then use the sender's public key to hash the message and assert that it matches the received hash digest. If that happens, then the receiver can be sure that the sender was the one who signed the message, and that the message wasn't altered.
Elliptic Curve Digital Signature Algorithm
If you ever hear about Elliptic Curve Digital Signature Algorithm (ECDSA), you should know that it is one of the more complex and efficient public key encryption algorithms. Keys are generated via elliptic curve cryptography, which are smaller than the average keys generated by digital signing algorithms. Yet they are able to offer the same level of security as any other digital signature algorithms.
ecrecover
Smart contracts on Ethereum have access to the built-in ECDSA signature verification algorithm via ecrecover
. This system method verifies the integrity of any arbitrary signed data and recovers the address corresponding to the signer by using v, r, s
and the signature hash.
Signatures are interesting because users can approve stuff without having to submit a transaction. This feature is useful because it can improve the UX and reduce user gas costs by using meta-transactions or ERC20-permit (more on this later).
Nevertheless, as usual, better UX and convenience generally comes at the expense of security. Because of that, it is important to follow the community-developed standards and best practices when dealing with digital signatures.
Digital Signature Standards
There are three main standards that can be used to sign data with an Ethereum key:
Eth_sign: The simplest, most powerful, and most dangerous way to sign a message. This method is used to sign arbitrary data, so it can lead to users signing data that enables a future transaction without further confirmation. This feature is extremely dangerous because users may sign those types of "uncovered" transactions without knowing it. Because of that, the usage of
eth_sign
is discouraged.Personal_sign: EIP-191 introduced a format to sign data and ensure that it wasn't a valid transaction. To implement this method in geth, they prefixed any signed data with
"\x19Ethereum Signed Message:\n" + len(message)
. Thus, making it impossible to use these signatures as valid transactions.
"0x19 [1 byte version] [versidon specific data] [data to sign]".
signTypedData_v4: To help standardize signature usage in Ethereum, EIP-712 was introduced and is currently widely used and considered a standard that helps developers avoid most common security issues when dealing with signatures.
How to implement EIP-712
The main goal of this EIP was to ensure that users understood what they were signing. Providing explicit context of the contract address and network. Finally, this EIP also aimed to ensure that each signature could only be used by the intended contract.
These requirements are achieved by combining a Domain Separator hash and a Typed Structed Data hash, and using them inside ecrecover
.
Domain Separator Hash: The domain separator is a hash that is computed from the domain data and is used to prevent reusability of one signature in another dApp.
Typed Structured Data: The data that needs to be signed is defined using structs and Solidity primary types.
Meta-transactions
A meta-transaction is a method for separating who pays for the gas of a transaction from who benefits from the transaction's execution.
A user signs the meta-transaction and then sends it to an operator (note that since there is no interaction with the blockchain, no gas is required to sign). The operator takes this signed meta-transaction and submits it to the blockchain, paying for the fees of the outer, regular transaction himself.
ERC20-Permit
The ERC20 standard requires two different transactions to transfer ERC20 tokens from an externally owned account (EOA) by using a third-party smartcontract:
First, the EOA needs to perform an
ERC20.approve()
transaction to whitelist the third-party smartcontract.Then, the EOA needs to call the function from the third-party smartcontract that triggers the
ERC20.transferFrom()
to transfer the tokens.
With the implementation of EIP-2612, also known as ERC20-Permit, it is possible to simplify the process by using signature verification. With the use of ERC20-Permit, users can simply sign the allowance amount with their wallet, and the operator can take care of the rest.
Vulnerabilities and Best Practices
Signature Reply Attack: Unless some precautions are taken, the same signature can be used multiple times to execute a function. To prevent these types of exploits, the use of nonces is extremely encouraged (note that in the EIP-712 implementation above I have already used a nonce to prevent this vulnerability).
Signature Malleability: Due to the symmetric structure of elliptic curves, for every set of v, r, s
there is another different set of v, r, s
that also has the same relationship. This results in two valid signatures and allows malicious actors to compute a valid signature without the target user's private key. The easiest way to solve this vulnerability is by using ECDS OpenZeppelin library.
Resources
If you are not familiar with basic encryption, you should definitely check Artemis' public slides. Shoutout to the team for progressively sharing the content of the course and making it accessible to anyone interested!
If you want to see implementations of meta-transactions and erc20-permit, you can look into these articles by soliditydeveloper.com.
Finally, if you want to get a really in-depth understanding of digital signatures and, especially, their potential vulnerabilities, I highly encourage you to check this great article by Immunefi, one of the leading bug bounty platforms.