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 (x,y)(x,y) 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:

r=kGs=k1(h+rp)r=?s1(hG+rP)\begin{align*} r &= kG\\ s &= k^{-1}(h + rp)\\ r &\stackrel{?}= s^{-1}(hG + rP) \end{align*}

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 QQ. The generator will be referred to as GG. The value of GG is known to all parties involved. Multiplication between a scalar and a point is written as aGaG, which means GG is added to itself aa times.

Given two points PP and QQ, we say someone knows the discrete log relationship between PP and QQ if they know xx such that xP=QxP = Q, or alternatively, yy such that P=yQP = yQ.

We say someone knows the discrete log of a point QQ if they know a scalar qq such that Q=qGQ = qG.

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 (modp)\pmod p notation for brevity.

Failed attempts at digital signatures

Naive approach: reveal the private key

We want to prove we know the private key pp for the public key PP where P=pGP = pG.

The naive approach is to reveal the private key pp to the verifier so the verifier can check that indeed P=pGP = pG.

Of course, this defeats the purpose of keeping pp private.

Proving knowledge of the discrete log relationship between PP and QQ

Proving we know pp is equivalent to proving we know the discrete log relationship between the generator GG and the public key PP. That is, GG is added to itself pp times to get PP. Revealing the discrete log relationship between GG and PP reveals the private key.

Therefore, instead of proving that we know the discrete log relationship between the generator GG and the public key PP, we can prove that we know the discrete log relationship between PP and QQ, where QQ is chosen by the prover and whose discrete log qq is unknown to the verifier. If the discrete log of QQ is unknown to the verifier, then the verifier cannot derive the private key, given the discrete log relationship between PP and QQ.

Let ss be the number of times PP needs to be added to itself to produce QQ; in other words, sP=QsP = Q. If the prover knows the discrete log of PP and QQ, as pp and qq respectively, they can easily compute ss as s=q/ps = q/p.

s=q/ps = q/p was derived as follows:

Q=sPQ=qGP=pGqG=spGq=spqp=s\begin{align*} Q &= sP\\ Q &= qG\\ P &= pG\\ qG &= spG \\ q &= sp \\ \frac{q}{p} &= s\\ \end{align*}

Then the verifier can check that indeed Q=sPQ = sP, and this shows the prover knows the discrete log relationship between PP and QQ.

However, this approach fails because a malicious prover can take someone else’s PP (of which they do not know the private key), and choose a random value ss, compute Q=sPQ = sP, and then send (P,Q,s)(P, Q, s) to the verifier.

Hence, the prover showing they know the discrete log relationship between PP and QQ is not sufficient to prove they know the discrete log of PP itself. It only shows they multiplied some PP by ss to produce QQ.

Preventing randomly chosen QQ

The above approach failed because the prover can present QQ without actually knowing the discrete log of QQ itself.

What if, to establish that the prover knows the discrete log of QQ, the prover reveals qq (the discrete log of QQ) to the verifier?

In this case, the prover must reveal both qq and ss to the verifier. Then, ss proves that the prover knows how many times PP needs to be added to itself to get QQ, and qq proves that the prover knows the discrete log of QQ. Presenting qq demonstrates that QQ wasn’t selected by choosing a value ss and adding PP to itself ss times.

In that case, the verifier checks that

qG=?Q // prover knows discrete log of QsP=?Q // prover knows how many times P needs to be added to itself to get Q\begin{align*} qG &\stackrel{?}{=} Q &&\text{ // prover knows discrete log of Q}\\ sP &\stackrel{?}{=} Q &&\text{ // prover knows how many times P needs to be added to itself to get Q} \end{align*}

With these checks, the prover can’t create or produce a valid relation without knowing the discrete log of QQ and the discrete log of PP.

However, the verifier can compute the private key based on this information.

Q=sPs1Q=Ps1(qG)=Ps1q=P/Gs1q=p\begin{align*} Q &= sP\\ s^{-1}Q&=P\\ s^{-1}(qG)&=P\\ s^{-1}q &= P/G\\ s^{-1}q &= p\\ \end{align*}

So we have a conundrum: if we reveal the discrete log of QQ, the verifier can compute the private key. If the prover can pick QQ 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 qq is out of the question.

Therefore, our solution must include the prover picking QQ – but we must prevent the prover from being able to pick QQ completely arbitrarily (such as by picking an arbitrary ss and producing Q=sPQ = sP).

Going forward, the prover must prove they know the discrete log of QQ and the discrete log of PP, but not reveal either of the discrete logs pp or qq.

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 PP and QQ, then what else must they know?

The key idea is this:

If the prover knows the discrete logs of PP and QQ, then not only must they know ss such that sP=QsP = Q, they must also know ss' such that s(hG+P)=Qs'(hG + P) = Q where hh 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 PP and QQ – and if the prover knows the discrete logs of PP and QQ, then they must be able to compute ss' such that s(hG+P)=Qs'(hG +P) = Q as long as the prover knows how much the shift hh is.

An additive shift prevents forgeries

To prevent the prover from choosing QQ such that it is a simple scalar multiplication of PP, the verifier can inject an additive shift hh.

After the prover sends PP and QQ to the verifier, the verifier responds with hh (a public scalar value). The prover must then produce ss such that Q=s(hG+P)Q = s(hG + P) (we do not need the ss' notation anymore).

Now the prover does not control the discrete log relationship between PP and QQ, they must instead show they know the discrete log relationship between (hG+P)(hG + P) and QQ.

Since the verifier is already in possession of PP and QQ, the prover cannot just pick a random ss and generate QQ such that Q=s(hG+P)Q = s(hG + P). The prover must prove they know the discrete log relationship between the new point (hG+P)(hG + P) and the original QQ.

If the prover knows the discrete logs of PP and QQ, then ss is easy to compute as:

s=qh+ps = \frac{q}{h + p}

Specifically, the prover solved

s(hG+P)=Qs(h+p)=qs=qh+p\begin{align*} s(hG + P)&=Q\\ s(h + p) &= q\\ s&=\frac{q}{h+p} \end{align*}

Let’s summarize the interactive algorithm we just created:

  1. We assume GG (and the elliptic curve group it belongs to) is agreed upon by both parties.
  2. The prover publishes their public key PP and wishes to prove they know the private key pp.
  3. The verifier responds with a scalar hh.
  4. The prover picks a random qq and computes Q=qGQ = qG.
  5. The prover computes s=q(h+p)1s = q(h + p)^{-1} and sends (s,Q)(s, Q) to the verifier
  6. The verifier checks if Q=?s(hG+P)Q \stackrel{?}= s(hG + P)

The last check works because under the hood the ss cancels out the discrete logs of hh and pp:

Q=s(hG+P)qG=qh+p(hG+P)qG=qh+p(hG+pG)qG=qh+p(h+p)GqG=qh+p(h+p)GqG=qG\begin{align*} Q &= s(hG + P)\\ qG &= \frac{q}{h + p}(hG+P)\\ qG &= \frac{q}{h + p}(hG+pG)\\ qG &= \frac{q}{h + p}(h+p)G\\ qG &= \frac{q}{\cancel{h + p}}(\cancel{h+p})G\\ qG &= qG \end{align*}

Defense against forged signatures

Let’s see how a malicious prover could try to compute ss without knowing the discrete logs of PP and QQ and see why the attempt fails.

The prover invents a value q~\tilde{q} and generates Q=q~PQ = \tilde{q}P and sends QQ to the verifier. Let q~\tilde{q} be a value invented by the malicious prover, and not the the discrete log of QQ.

The verifier responds with scalar hh.

The malicious prover must now solve the equation

s(P+hG)=Qs(P + hG) = Q

Since the prover knows Q=q~PQ = \tilde{q}P (but doesn’t know pp) they can try two substitutions:

  • Q=q~PQ = \tilde{q}P
  • P=q~1QP = \tilde{q}^{-1}Q.

However, whichever way the prover substitutes QQ or PP, they run into the issue that they cannot solve for ss because the discrete log of PP is unknown to them.

Substitute QQ

Here the prover substitutes the QQ:

s(P+hG)=Qs(P + hG) = \boxed{Q}
s(P+hG)=q~Ps(P + hG) = \boxed{\tilde{q}P}
s=q~PP+hGs = \frac{\tilde{q}P}{P + hG}

We cannot divide elliptic curve points, so to compute the fraction above, we need to know pp:

s=q~pp+hs = \frac{\tilde{q}p}{p + h}

But the prover doesn’t know pp, so they can’t compute ss.

Substitute PP

Now the malicious prover tries substituting PP instead of QQ but runs into the issue that they do not know the discrete log of QQ:

s(q~1Q+hG)=Qs(\tilde{q}^{-1}Q + hG) = Q
s=Q(q~1Q+hG)s = \frac{Q}{(\tilde{q}^{-1}Q + hG)}

Now the malicious prover needs to know the discrete log of QQ to compute the formula above. However, the prover does not know the discrete log of QQ, they only know it is q~P\tilde{q}P, but they do not know pp. Therefore, the malicious prover cannot produce ss.

Why the shift needs to be additive

How did we know to add hGhG to PP instead of multiplying hPhP and asking the prover to come up with ss such that Q=hsPQ = hsP?

The problem is that with a multiplicative shift, the prover can cancel out the factors of the discrete logs of QQ and PP.

As a recap: the a malicious prover does not know qq, the discrete log of QQ, or pp, the discrete log of PP. However, they do know that QQ is q~\tilde{q} times larger than PP, where q~\tilde{q} is a number they invented and not the true discrete log of qq.

If the verifier presents hh, and asks the prover to come up with an ss such that Q=shPQ = shP, then the prover simply computes s=q~/hs = \tilde{q}/h.

This will pass the verifier’s test:

Q=shPQ=q~hhPQ=q~hhPQ=qP\begin{align*} Q &= shP \\ Q &= \frac{\tilde{q}}{h}h P\\ Q &= \frac{\tilde{q}}{\cancel{h}}\cancel{h} P\\ Q &= qP \\ \end{align*}

Therefore, the protocol must include an additive shift so there is no simple multiplicative relationship shsh between the first point (P+hGP + hG) and the second point QQ.

Making our protocol non-interactive

Unfortunately, our solution requires the verifier to send hh after receiving PP and QQ, requiring the prover and verifier to interact with each other.

If we want to make our protocol non-interactive, then hh cannot come from the verifier and the prover must produce it.

If the prover knows hh 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 hh randomly then pick ss randomly, and compute

Q=s(hG+P)Q = s(hG + P)

and then send (P,Q,s,h)(P, Q, s, h) to the verifier. The verifier doesn’t know if QQ was generated by a random ss.

To avoid the prover choosing hh and the interaction with a verifier, we can simulate the verifier picking hh simply by hashing QQ:

h=hash(Q)h = \mathsf{hash}(Q)

Since the verifier will pick hh randomly anyway, the prover can compute hh 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:

  1. The prover picks a random scalar qq and computes Q=qGQ = qG.
  2. The prover computes h=hash(Q)h = \mathsf{hash}(Q).
  3. The prover computes s=qh+ps = \frac{q}{h + p}.
  4. The prover sends (P,Q,s,h)(P, Q, s, h) to the verifier.
  5. The verifier computes h=hash(Q)h = \mathsf{hash}(Q).
  6. The verifier checks that Q=s(hG+P)Q = s(hG + P).

The reason this is secure is because a malicious prover cannot execute step 3 without knowing the discrete log of QQ and the discrete log of PP.

Forgery is impossible

Suppose a malicious prover picks q~\tilde{q} and generates Q=q~GQ = \tilde{q}G. Here, the prover knows the discrete log of QQ, but not the discrete log of PP. After hashing QQ to get hh, the malicious prover needs to compute ss such that Q=s(hG+P)Q = s(hG + P). However, they cannot do this because they do not know the discrete log of (hG+P)(hG + P), as the discrete log of PP 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 hGhG where hh is the hash of the message and the discrete log of another point QQ.

However, this current introduces a security bug as the prover can first compute h=hash(message)h = \mathsf{hash}(\text{message}) and then pick a random ss' and compute Q=s(hG+P)Q = s'(hG + P). QQ is now fully under the control of the prover again, so we can’t trust that the prover actually knows the discrete log qq.

To secure this, we again need to introduce some additional pseudo-randomness that is beyond the prover’s control.

Adding randomness rr

Let rr be a random value derived from hashing QQ. If we multiply rr by PP 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 (rP+hG)(rP + hG) and QQ

Q=s(hG+rP)Q = s(hG + rP)
  1. The prover picks a random pp and publishes their public key PP as P=pGP = pG.
  2. The prover picks a random scalar qq and computes Q=qGQ = qG.
  3. The prover picks a message string message\text{message} and computes h=hash(message)h = \mathsf{hash}(\text{message}).
  4. The prover computes r=hash(Q)r = \mathsf{hash}(Q).
  5. The prover computes s=q(h+rp)1s = q\cdot(h + rp)^{-1}.
  6. The prover sends (message,Q,s)(\text{message}, Q, s) to the verifier
  7. The verifier computes r=hash(Q)r' = \mathsf{hash}(Q)
  8. The verifier computes h=hash(message)h = \mathsf{hash}(\text{message})
  9. The verifier checks that Q=?s(hG+rP)Q \stackrel{?}= s(hG + r'P)

Even though the prover can pick an arbitrary hh, they cannot control the discrete log of the point (hG+rP)(hG + rP) because rr is generated by hashing QQ. Therefore, to compute ss, the discrete log relationship between QQ and (hG+rP)(hG + rP), the prover must actually know the discrete log pp of PP.

Optimizing the pre-ECDSA algorithm

Optimization 1: Hashing QQ is unnecessary because elliptic curve multiplication behaves like a hash function already

Let’s think back to what r=hash(Q)r = \mathsf{hash}(Q) was trying to accomplish. The final verification formula is:

Q=?s(hG+rP)Q \stackrel{?}= s(hG + rP)

The idea is we want rr to be dependent on QQ so that QQ is not wholly determined by the value ss over which the prover has full control. In order to compute ss, the prover has to know the discrete log of PP.

We want a cheaper alternative to hashing that makes rr dependent on QQ.

Consider that if rr is dependent on QQ, then it is also dependent on the discrete log of QQ, qq, as qq entirely determines QQ.

Now consider that if we are given a scalar value aa, the elliptic curve point AA created by A=aGA = aG appears random. There is no apparent relationship between AA and aa. 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 Q=qGQ = qG the same way we would treat hash(q)\mathsf{hash}(q). That is, QQ itself behaves like the output of a hash. However, we cannot set r=Qr = Q since they are not the same type. Instead, we can simply take the xx value of QQ and make that rr (remember, QQ is an (x,y)(x, y) point).

Now rr is essentially the hash of qq, and since QQ directly depends on qq, rr behaves like a hash of QQ.

Therefore, instead of computing r=hash(Q)r = \mathsf{hash}(Q), we set r=Q.xr = Q.x (meaning the xx value of the point QQ.)

Optimization 2: We don’t need to send the entire point QQ, only the xx value of QQ

Elliptic curve points consist of two scalars: the xx and yy value of the point. Each xx value has only two possible yy values, the solutions to x3+bmodp\sqrt{x^3 + b} \mod p.

Thus, there is no need to send QQ, only rr because rr is the xx value of QQ.

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.

  1. The prover picks a random pp and publishes their public key PP as P=pGP = pG.
  2. The prover picks a message they want to sign and hashes it to get h=hash(message)h = \mathsf{hash}(\text{message}).
  3. The prover picks a random scalar kk (what we have been calling qq, but the literature calls it kk) and computes R=kGR = kG. Again, what we called QQ the literature calls RR. Only the xx value of RR is kept as r=R.xr = R.x.
  4. The prover computes ss for R=s1(hG+rP)R = s^{-1}(hG + rP) as
s=h+rpks = \frac{h + rp}{k}

Note that ss is “inverted” compared to our previous implementation. In the actual ECDSA algorithm, the prover computes s1s^{-1} and the verifier inverts ss later, as we will see.

  1. The signer sends (P,h,r,s)(P, h, r, s) to the verifier.
  2. The verifier computes R=s1(hG+rP)R' = s^{-1}(hG + rP) and checks that the xx value of RR' is equal to rr.

The formula works under the hood as follows:

=s1(hG+rP)=kh+rp(hG+rP)=kh+rp(hG+rpG)=kh+rp(h+rp)G=kh+rp(h+rp)G=kG\begin{align*} &=s^{-1}(hG + rP)\\ &=\frac{k}{h + rp}(hG + rP)\\ &=\frac{k}{h + rp}(hG + rpG)\\ &=\frac{k}{h + rp}(h + rp)G\\ &=\frac{k}{\cancel{h + rp}}(\cancel{h + rp})G\\ &=kG \end{align*}

This produces the same point RR, and hence the rr will match the xx value of the point computed by s1(hG+rP)s^{-1}(hG + rP).

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.

R=s1(hG+rP)R=s1hG+s1rPRs1hG=s1rPsr1Rr1hG=P\begin{align*} R &= s^{-1}(hG + rP)\\ R &= s^{-1}hG + s^{-1}rP\\ R - s^{-1}hG &= s^{-1}rP\\ sr^{-1}R-r^{-1}hG&=P\\ \end{align*}

However, just (message,r,s)(\text{message}, r, s) is not sufficient because rr corresponds to two possible points for the two yy values of r=R.xr = R.x. To disambiguate, the signer also needs to send a Boolean variable to indicate which value of yy is being used. Sending the entire yy would take up more space.

The ECDSA algorithm to “recover” the public key given a signature is as follows:

  1. The prover publishes their public key PP as P=pGP = pG.
  2. The prover picks a message they want to sign message\text{message} and hashes it to get h=hash(message)h = \mathsf{hash}(\text{message}).
  3. The prover picks a random scalar kk and computes R=kGR = kG.
  4. The prover computes h=hash(message)h = \mathsf{hash}(\text{message})
  5. The prover solves for ss in R=s1(hG+rP)R = s^{-1}(hG + rP) as s=h+rpks = \frac{h + rp}{k}.
  6. The signer sends (message,r,s,v)(\text{message}, r, s, v) to the verifier where vv is a boolean indicating which yy value of rr is being used.
  7. The verifier derives RR from vv and rr.
  8. The verifier derives the public key PP as
P=sr1Rr1hGP = sr^{-1}R - r^{-1}hG

and accepts the signature if the computed PP matches the public key PP that the prover published.

Vulnerabilities in ECDSA if misused

Malleability in ECDSA

Given a signature (message,r,s,v)(\text{message}, r, s, v), an attacker can compute a second signature (message,r,s,v)(\text{message}, r, s', v') such that vvv \neq v' and sss \neq s' that recovers to the same public key PP.

The attacker simply computes the additive inverse of ss as s=s1s' = s^{-1} and flips the value of vv so that RR becomes its additive inverse. Essentially, we have replaced ss with s-s and RR with R-R. Since these two values are multiplied together, the -1s cancel out and the recovered public key is the same:

P=(s)r1(R)r1hG=sr1Rr1hG\begin{align*}P &= (-s)r^{-1}(-R) - r^{-1}hG\\ &= sr^{-1}R - r^{-1}hG \end{align*}

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 RR that we do not know the discrete log of, but we know the components of. For example, the attacker picks random values aa and bb and computess R=aG+bPR = aG + bP, and r=R.xr = R.x.

Then, the attacker computes s=r/bs = r/b and h=ar/bh = ar / b.

We can see how this attack works by plugging in the false values for ss and hh into the verification formula:

R=?s1(hG+rP)R=?(rb)1((arb)G+rP)R=?(br)((arb)G+rP)R=?(aG+bP)\begin{align*} R &\stackrel{?}= s^{-1}(hG + rP) \\ R &\stackrel{?}= (\frac{r}{b})^{-1}((\frac{ar}{b})G + rP) \\ R &\stackrel{?}= (\frac{b}{r})((\frac{ar}{b})G + rP) \\ R &\stackrel{?}= (aG + bP) \end{align*}

The defense against this attack is simple: hh 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 ar/bar/b.

Summary

The ECDSA algorithm is a proof of knowledge of the discrete log relationship between an arbitrary point RR 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 RR and (hG+rP)(hG + rP).

Although the signer can pick RR arbitrarily, they cannot control the discrete log of the point (hG+rP)(hG + rP) because rr pseudorandomly depends on RR. The only way the signer can compute ss in R=s(hG+rP)R = s(hG + rP) is if they actually know the discrete log of PP, 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.

© 2025 Better-Start. All rights reserved.