Approaching Secure Signature Validation in Smart Contracts | Opal Graham - Coinbase
Ethereum Denver·Mon, Mar 9, 2026, 12:00 AM
🚀 Get Ready for ETHDenver 2026! 🚀 We're already hard at work preparing for next year's biggest Web3 event! Keep your eyes peeled for more info on ETHDenver 2026—it’s going to be epic! 🌟
Transcript
[music] Good morning everyone. Uh so this talk is kind of funny following the second or the first talk that we just had. Um sounds like what my topic is going on over today is going to be you know useful for about the next 10 years and then you have an expiry. Um but uh yeah, we're going to talk about approaching secure signature validation in smart contracts. And security for signature validation in smart contracts is largely contextbased and we're going to um see that through these four topics that we focus on for today.
Um so let's start with just what is signature validation? What does it look like in smart contracts? And um to be to give us context, we are mainly focusing on EVM based signature validation. And signature validation is the process of uh allowing having a method for saying I sign off on a I sign off on an action without having to ever interact with the smart contract and then a somebody else can use that signature. Um so as an example we have Alice here and she is signing off saying yes I approve of this message offchain and then supplying that to Bob.
Bob goes to the smart contract onchain and performs an action that was okayed by Alice. Now uh where is this useful for us? This is useful in the context of onboarding first-time users or bridge validation or token movement or just generalized central action centralized actions within smart contracts. Um, and the way that this I feel like this is like getting into the context a little bit more of like what the last guy uh sort of like he glossed over at a higher level. We're we're going into the lower level part of it at this point.
Um so the public signature uh recovery depends upon the fact that um externally owned accounts are like both public and private key pairs here. Um we have the public address part that everybody sees on chain and then we have the private key part that obviously we keep to ourselves and then we keep it to ourselves because this is what allows us to determine um whether or not we have okayed an action in on the blockchain. Um and this um the underlying mechanics of this is ECDSA. Um and um we when we are looking to sign something we can do so offchain with tools like ethersjs, web3js or foundry. Um and the cryptographic process that's underlying that in the implementation is that a message gets hashed.
We'll call that h. Um and then we generate a random X value that we call R on the elliptic curve. Um and we use that along with your private key to calculate a signature proof that we'll call S. And this signature is this RS pair along with a V that we'll talk about in a little bit. Um and the important underlying part that again is the part that is going away with quantum computing is that um knowing the generated signature doesn't reveal the private key that um that it's easy to go forward but not easy right now to go backwards.
Um, and if you are on the other side, if you are the person that is then taking that signature and um, using it within a smart contract, what that's going to look like for you is that you're going to hash the same message on your side and then you're going to put this through the pre-ompile EEC recover um, along with the provided signature. The output will be an address or address zero if it's an invalid uh, message signature combo. Um, and then we have things like open Zeppelin and Solady that are libraries that are helpful for doing this. But we have this uh screenshot here of like what it will generally look like within your smart contract, right? And um again like from our side within the purpose of this talk, we're not going to worry so much about exposing the private key from our side.
We're not going to worry about whether EC recover is implemented correctly within the EVM. What we worry about from this side of things is how we're using it within the smart contract. And I want to break that down into what we see what we see on um within this line of signer equals EC recover. Um we have this part where we have this output of a serer address. Um we want to make sure that this is validated.
We have a message hash that we want to make sure can't be manipulated. and we have a signature that we want to make sure can't be adjusted. And then beyond that, context plays a super important role. You want to make sure you are qualifying who can use the signature, where it can be used, and how many times it can be used. For instance, so let's get into this.
Um, first off, with recovered address comparison, when you use your EC recover, it doesn't automatically revert if you give it an invalid signature. it returns address zero. So if you don't have any uh checks within your smart contract um that check to see that it's not address zero, then you're now allowing a invalid signature um to be passed through your smart contract, which can allow basically any message to be passed. Um even further than that though, you don't want to just check that it's not address zero. You want to check that it's the intended address.
And so the solution for this when it um is relevant is to check that the returned address is a specific address. So here in this example that I have I have highlighted that um let's say within an initialize we have no uh address zero check and we accidentally set the serer to be address zero. Then further down when we have this requirement that the recoverer is the signer, we've actually now made it so that you have to have an invalid signature in order to pass this check. Um, so this isn't what we're intending at all. And we can fix this um by adding in a requirement that the signer isn't address zero at the top.
Um, and again, so just to note, some ECDSA libraries do have the zero address check, but like a lot of the times we want to um specify the signer. So it would oftentimes go beyond just what's inside the library. Um, let's get into replay here. And so when we're considering replay, we need to be asking ourselves what what is our signature meant to be used for? What smart contract?
What function? What chain? How many times? Who benefits from it? These are all relevant uh questions.
So if we first consider what we have here, we have here that a message is being composed of a recipient amount in an announce. And this non gets increased later on. This guarantees that we can never use the same signature again for this particular nons. That is an example of function replay protection that we have it protected from being replayed again within that exact function, right? But we have other things we need to consider.
Um it's not sometimes we'll have signatures that are used through multiple functions, right? It's not just one particular function. Maybe we have a deposit and a withdrawal that both require signatures. In that case, we want to h include type hashes that um specify what it which function it is we're actually interested in providing the signature for. So that's what we have highlighted here.
Um beyond that, we also have within the same chain, what if we are uh looking at two contracts that do the same thing and have the same signer? That's when we need this inclusion of address this within what's called a domain separator. This is a lar larger context of looking at what contract you're looking at and then for crosschain replay what block you're on. So say you have two contracts that are the same address on different chains with same signer. You want to make sure that the signature includes the blockchain ID to um prevent reuse across chains unless that's what you mean for it to happen.
Um, one other thing to consider as far as like sort of like a replay thing is um to include who the signature is intended for. Uh, for instance, in this example here, we have that we have a message composed of a voucher ID and a discount amount. Um, and let's say that in a scenario, we have a user that goes to apply this discount uh voucher that is legitimately supposed to be using this, but then an MEV bot sees this in the memole and front runs. And then because of that, the original user, the original intended user is no longer allowed to use that signature that was meant for them because there was no protection saying it was meant specifically for them. We can fix this by including within the message who the authorized user is and requiring that that be who calls this function.
So that's some replay consideration. Going on to hash collision, we're going into the input part. Um, oftentimes you'll see AI encodep used and this becomes diff a difficulty and a challenge when dynamic variable types like strings or bytes are used because if we use encode packed on string AB with C and we also use encodep on string A with string BC there's no difference in that message and this is because there's no padding in the encoding of those values. Um, as an example here, let's say we have two strings. We have a username and then a token symbol.
And I'm going to put Alice and token symbol USDT. This comes to this hash here. But so does this hash where we have Alice U as the username and SDT as the token symbol. And both of those are two legitimate token symbols within the contract. This becomes a problem um because there's ambiguity there.
Oh, sorry. We fixed this by changing it to using AI. Which does include packing. Um or there's also some other considerations like um like making sure that uh you do you really need the data types that you have used? For instance, do you need two strings to uh to specify the type of data that you're looking at?
And if you do, make sure that these two things are not next to each other. So we can see in the example now that once we have used abi.enccode that we now get two different hashes with th those two different uh uses there. Um the last thing we'll talk about is signature malleability. So this is not uh me uh a manipulation of the message and instead is a manipulation of what's going on with the signature itself.
And so anytime you have a VRS combo that represents a signature to use an EC recover, um then you also have this corresponding V prime, R and S prime that uses the same R that will also work as a signature for the same message. And that's important to know. Um because of this, this makes it an ineffective check to use a signature as a way to determine whether or not um something a message has already been signed before. What's going on behind the scenes for this? What's really happening is that on an elliptic curve um you really have two points that have that same x value r that is randomly generated when you are making a signature.
Um, and so when you have this VRS combo, the way to determine one of those is to pick a V. And that V is described by a 27 or a 28. And this determines which of those points you're talking about. But then there's this other point that's going to correspond to V prime being the opposite value. So if V was 27, then V prime would be 28, for instance, or vice versa.
And then S prime would be this value subtracting S from it. So this V prime and S-prime along with the original R will work the same as that that VRS original combo. The way that we prevent uh that from being a problem within smart contracts is to take to require that S is no more than half of that value that we talked about. Um S prime that will determine that S prime has to be larger than half of that value. And so it will no longer pass that check.
So you'll only have one of those that will pass the check and meaning that only one of those will be the predetermined signature that's allowed. Um a lot of ECDSA libraries make this check but um you should always check to make sure that that's there and if not you need to add it in. Um and another lesson learned from this is to not log signatures as a a prevention for replay. So with all that up um we've looked at how context is important. you have to describe who's meant to use that signature, when and where.
Um, we are looking at how validating the return address is important. And then we want to make sure we limit the ways in which our input is ambiguous both with the signature and with the message. Thank you.
Automatic transcript — names and jargon may be misspelled.