The intuition behind elliptic curve digital signatures (ECDSA)
This article explains how the ECDSA (Elliptic Curve Digital Signature Algorithm) works as well as why it works.
We will incrementally “rediscover” the algorithm from first principles in this tutorial.
Prerequisites
We assume prior knowledge of
Digital Signature Recap
A digital signature algorithm is a protocol which enables a user to give a cryptographic seal of approval for a string.
Such a user has a public key (an pair corresponding to an elliptic curve point). The point was generated from a private key which is a secret scalar value.
Given a public key, a message, and a signature, another user can verify that the signature was produced by someone who owns the private key for that public key, and only that person could have generated the signature.
For example, cryptocurrencies use digital signatures to verify a user really wanted to make a transaction. A user, Alice, can send a message to the blockchain saying “Send 10 of my tokens to Bob.” The blockchain network must be certain Alice really gave her seal of approval to that message and that Bob isn’t stealing Alice’s coins.
The signature produced must be specific to the message and not be accepted for other messages. Otherwise an attacker could use a signature to get the victim to approve a malicious transaction. If the signature for the message “transfer 10 coins to Bob” could also be used as a seal of approval for the message “transfer 10 coins to Eve,” then Eve could use the signature from the Bob transfer to steal coins from Alice.
We call the person generating the signature the prover or signer and the person testing if the (Public Key, message, signature) tuple is valid the verifier.
At its heart, an elliptic curve digital signature is a proof of knowledge of a private key. Specifically, it is a proof of knowledge of the discrete log of an elliptic curve point.
For readers who have already seen the ECDSA algorithm:
but don’t know where the formulas came from, you will learn the thinking behind how they were derived.
Terminology and Notation
We refer to elliptic curve points with capital letters, such as . The generator will be referred to as . The value of is known to all parties involved. Multiplication between a scalar and a point is written as , which means is added to itself times.
Given two points and , we say someone knows the discrete log relationship between and if they know such that , or alternatively, such that .
We say someone knows the discrete log of a point if they know a scalar such that .
We refer to the discrete log of a point with the lowercase version of the same letter unless otherwise noted.
All arithmetic is done in a finite field, but we omit the notation for brevity.
Failed attempts at digital signatures
Naive approach: reveal the private key
We want to prove we know the private key for the public key where .
The naive approach is to reveal the private key to the verifier so the verifier can check that indeed .
Of course, this defeats the purpose of keeping private.
Proving knowledge of the discrete log relationship between and
Proving we know is equivalent to proving we know the discrete log relationship between the generator and the public key . That is, is added to itself times to get . Revealing the discrete log relationship between and reveals the private key.
Therefore, instead of proving that we know the discrete log relationship between the generator and the public key , we can prove that we know the discrete log relationship between and , where is chosen by the prover and whose discrete log is unknown to the verifier. If the discrete log of is unknown to the verifier, then the verifier cannot derive the private key, given the discrete log relationship between and .
Let be the number of times needs to be added to itself to produce ; in other words, . If the prover knows the discrete log of and , as and respectively, they can easily compute as .
was derived as follows:
Then the verifier can check that indeed , and this shows the prover knows the discrete log relationship between and .
However, this approach fails because a malicious prover can take someone else’s (of which they do not know the private key), and choose a random value , compute , and then send to the verifier.
Hence, the prover showing they know the discrete log relationship between and is not sufficient to prove they know the discrete log of itself. It only shows they multiplied some by to produce .
Preventing randomly chosen
The above approach failed because the prover can present without actually knowing the discrete log of itself.
What if, to establish that the prover knows the discrete log of , the prover reveals (the discrete log of ) to the verifier?
In this case, the prover must reveal both and to the verifier. Then, proves that the prover knows how many times needs to be added to itself to get , and proves that the prover knows the discrete log of . Presenting demonstrates that wasn’t selected by choosing a value and adding to itself times.
In that case, the verifier checks that
With these checks, the prover can’t create or produce a valid relation without knowing the discrete log of and the discrete log of .
However, the verifier can compute the private key based on this information.
So we have a conundrum: if we reveal the discrete log of , the verifier can compute the private key. If the prover can pick at will, then the prover can create signatures for public keys they don’t know the discrete log of.
We cannot have the verifier be able to compute the private key, so revealing is out of the question.
Therefore, our solution must include the prover picking – but we must prevent the prover from being able to pick completely arbitrarily (such as by picking an arbitrary and producing ).
Going forward, the prover must prove they know the discrete log of and the discrete log of , but not reveal either of the discrete logs or .
It turns out that proving we know the discrete log of two points without revealing them is easier than proving we know the discrete log of one point without revealing the single discrete log.
If the prover knows the discrete logs of and , then what else must they know?
The key idea is this:
If the prover knows the discrete logs of and , then not only must they know such that , they must also know such that where is an arbitrary value chosen by the verifier and is a value the prover cannot predict.
In other words, we are going to start including additive and multiplicative “shifts” in the relationship between and – and if the prover knows the discrete logs of and , then they must be able to compute such that as long as the prover knows how much the shift is.
An additive shift prevents forgeries
To prevent the prover from choosing such that it is a simple scalar multiplication of , the verifier can inject an additive shift .
After the prover sends and to the verifier, the verifier responds with (a public scalar value). The prover must then produce such that (we do not need the notation anymore).
Now the prover does not control the discrete log relationship between and , they must instead show they know the discrete log relationship between and .
Since the verifier is already in possession of and , the prover cannot just pick a random and generate such that . The prover must prove they know the discrete log relationship between the new point and the original .
If the prover knows the discrete logs of and , then is easy to compute as:
Specifically, the prover solved
Let’s summarize the interactive algorithm we just created:
- We assume (and the elliptic curve group it belongs to) is agreed upon by both parties.
- The prover publishes their public key and wishes to prove they know the private key .
- The verifier responds with a scalar .
- The prover picks a random and computes .
- The prover computes and sends to the verifier
- The verifier checks if
The last check works because under the hood the cancels out the discrete logs of and :
Defense against forged signatures
Let’s see how a malicious prover could try to compute without knowing the discrete logs of and and see why the attempt fails.
The prover invents a value and generates and sends to the verifier. Let be a value invented by the malicious prover, and not the the discrete log of .
The verifier responds with scalar .
The malicious prover must now solve the equation
Since the prover knows (but doesn’t know ) they can try two substitutions:
- .
However, whichever way the prover substitutes or , they run into the issue that they cannot solve for because the discrete log of is unknown to them.
Substitute
Here the prover substitutes the :
We cannot divide elliptic curve points, so to compute the fraction above, we need to know :
But the prover doesn’t know , so they can’t compute .
Substitute
Now the malicious prover tries substituting instead of but runs into the issue that they do not know the discrete log of :
Now the malicious prover needs to know the discrete log of to compute the formula above. However, the prover does not know the discrete log of , they only know it is , but they do not know . Therefore, the malicious prover cannot produce .
Why the shift needs to be additive
How did we know to add to instead of multiplying and asking the prover to come up with such that ?
The problem is that with a multiplicative shift, the prover can cancel out the factors of the discrete logs of and .
As a recap: the a malicious prover does not know , the discrete log of , or , the discrete log of . However, they do know that is times larger than , where is a number they invented and not the true discrete log of .
If the verifier presents , and asks the prover to come up with an such that , then the prover simply computes .
This will pass the verifier’s test:
Therefore, the protocol must include an additive shift so there is no simple multiplicative relationship between the first point () and the second point .
Making our protocol non-interactive
Unfortunately, our solution requires the verifier to send after receiving and , requiring the prover and verifier to interact with each other.
If we want to make our protocol non-interactive, then cannot come from the verifier and the prover must produce it.
If the prover knows in advance (which is necessary if the proof is not interactive), then we start back at our original problem.
In this scenario, a malicious prover can pick randomly then pick randomly, and compute
and then send to the verifier. The verifier doesn’t know if was generated by a random .
To avoid the prover choosing and the interaction with a verifier, we can simulate the verifier picking simply by hashing :
Since the verifier will pick randomly anyway, the prover can compute in a pseudo-random way using a hash function. This technique is called the Fiat-Shamir Transform.
A functional proof of knowledge of a private key
The algorithm is as follows:
- The prover picks a random scalar and computes .
- The prover computes .
- The prover computes .
- The prover sends to the verifier.
- The verifier computes .
- The verifier checks that .
The reason this is secure is because a malicious prover cannot execute step 3 without knowing the discrete log of and the discrete log of .
Forgery is impossible
Suppose a malicious prover picks and generates . Here, the prover knows the discrete log of , but not the discrete log of . After hashing to get , the malicious prover needs to compute such that . However, they cannot do this because they do not know the discrete log of , as the discrete log of is unknown to them.
Thus, we have demonstrated a secure algorithm for proving knowledge of a private key without revealing the private key.
The pre-ECDSA algorithm
The algorithm we introduced above simply proves knowledge of a private key, it doesn’t allow the signer to sign a message.
In ECDSA, a signature is a proof that we know the discrete log of the point generated by additively shifting our public key by where is the hash of the message and the discrete log of another point .
However, this current introduces a security bug as the prover can first compute and then pick a random and compute . is now fully under the control of the prover again, so we can’t trust that the prover actually knows the discrete log .
To secure this, we again need to introduce some additional pseudo-randomness that is beyond the prover’s control.
Adding randomness
Let be a random value derived from hashing . If we multiply by as follows, we have a secure digital signature algorithm. At first, this appears to create a circular dependency, but it is actually core to making the algorithm secure. The algorithm for the prover showing they know the discrete log of and
- The prover picks a random and publishes their public key as .
- The prover picks a random scalar and computes .
- The prover picks a message string and computes .
- The prover computes .
- The prover computes .
- The prover sends to the verifier
- The verifier computes
- The verifier computes
- The verifier checks that
Even though the prover can pick an arbitrary , they cannot control the discrete log of the point because is generated by hashing . Therefore, to compute , the discrete log relationship between and , the prover must actually know the discrete log of .
Optimizing the pre-ECDSA algorithm
Optimization 1: Hashing is unnecessary because elliptic curve multiplication behaves like a hash function already
Let’s think back to what was trying to accomplish. The final verification formula is:
The idea is we want to be dependent on so that is not wholly determined by the value over which the prover has full control. In order to compute , the prover has to know the discrete log of .
We want a cheaper alternative to hashing that makes dependent on .
Consider that if is dependent on , then it is also dependent on the discrete log of , , as entirely determines .
Now consider that if we are given a scalar value , the elliptic curve point created by appears random. There is no apparent relationship between and . This lack of an apparent relationship is what makes computing the discrete log hard.
Hash functions have an output that appear random also: there is no apparent relationship between the input and output of a hash.
Therefore, we can treat the same way we would treat . That is, itself behaves like the output of a hash. However, we cannot set since they are not the same type. Instead, we can simply take the value of and make that (remember, is an point).
Now is essentially the hash of , and since directly depends on , behaves like a hash of .
Therefore, instead of computing , we set (meaning the value of the point .)
Optimization 2: We don’t need to send the entire point , only the value of
Elliptic curve points consist of two scalars: the and value of the point. Each value has only two possible values, the solutions to .
Thus, there is no need to send , only because is the value of .
The Complete ECDSA Algorithm: Step-by-Step
We now show the standard ECDSA algorithm along with the most commonly used notation. We note where our notation differs.
- The prover picks a random and publishes their public key as .
- The prover picks a message they want to sign and hashes it to get .
- The prover picks a random scalar (what we have been calling , but the literature calls it ) and computes . Again, what we called the literature calls . Only the value of is kept as .
- The prover computes for as
Note that is “inverted” compared to our previous implementation. In the actual ECDSA algorithm, the prover computes and the verifier inverts later, as we will see.
- The signer sends to the verifier.
- The verifier computes and checks that the value of is equal to .
The formula works under the hood as follows:
This produces the same point , and hence the will match the value of the point computed by .
Deriving the public key given a signature
The Ethereum and Bitcoin blockchains do not verify signatures, given the public key, message, and signature. Instead, given the message and signature, they solve for the public key, and check that the public key matches the expected one.
To see how this works, we can do a little algebra on the verification formula to derive the public key given the signature and the hash of the message.
However, just is not sufficient because corresponds to two possible points for the two values of . To disambiguate, the signer also needs to send a Boolean variable to indicate which value of is being used. Sending the entire would take up more space.
The ECDSA algorithm to “recover” the public key given a signature is as follows:
- The prover publishes their public key as .
- The prover picks a message they want to sign and hashes it to get .
- The prover picks a random scalar and computes .
- The prover computes
- The prover solves for in as .
- The signer sends to the verifier where is a boolean indicating which value of is being used.
- The verifier derives from and .
- The verifier derives the public key as
and accepts the signature if the computed matches the public key that the prover published.
Vulnerabilities in ECDSA if misused
Malleability in ECDSA
Given a signature , an attacker can compute a second signature such that and that recovers to the same public key .
The attacker simply computes the additive inverse of as and flips the value of so that becomes its additive inverse. Essentially, we have replaced with and with . Since these two values are multiplied together, the -1s cancel out and the recovered public key is the same:
To prevent this attack, the verifier should not accept a different signature for the same message they have seen before. The more generalized and robust solution is to use a nonce, which is an always increasing number the prover must include in their message. A nonce (which stands for “number used once”) serves as a unique identifier for each signature. By requiring the prover to incorporate a new, larger nonce with each signature, the verifier can easily detect and reject attempts to reuse or modify previous signatures.
If the verifier does not hash the message, then fake proofs can be created.
Let’s consider that an attacker can create a value that we do not know the discrete log of, but we know the components of. For example, the attacker picks random values and and computess , and .
Then, the attacker computes and .
We can see how this attack works by plugging in the false values for and into the verification formula:
The defense against this attack is simple: must be the result of the verifier hashing the message. When a message is hashed, it is extremely unlikely that the hash would be exactly .
Summary
The ECDSA algorithm is a proof of knowledge of the discrete log relationship between an arbitrary point and a point which represents the sum of the message hash multiplied by the generator and the public key, and the public key is multiplied by a pseudorandom value the signer does not control.
Specifically, it is a proof of knowledge of the discrete log relationship between and .
Although the signer can pick arbitrarily, they cannot control the discrete log of the point because pseudorandomly depends on . The only way the signer can compute in is if they actually know the discrete log of , i.e. the private key.
Credits and Acknowledgements
The following resources were consulted during the creation of this article:
Ready to Get Started?
Join Thousands of Users Today
Start your free trial now and experience the difference. No credit card required.