Digital Signatures

Cryptography

#The problem hashing doesn’t solve

I’ve already gone through hash functions, and how they can be used to prove that a piece of data hasn’t been tampered with. However, on their own, hash functions tell you nothing about who sent the data, or whether they can be trusted.

Say you want to spend some bitcoin. You need to produce a message that says “move these coins that I own” in a way that anyone can check is legitimate, but that only you could have produced. Hashing the message alone doesn’t do this, since anyone can hash a message. What you need is a way to tie the message to a private key that only you hold, i.e., a digital signature.

#Digital signatures

We are already familiar with Asymmetric Encryption: two keys, one public and one private, where anything encrypted with the public key can only be decrypted with the private key. Digital signatures use the same public/private key setup, so it’s tempting to assume they are just encryption flipped around: decrypt with the private key to sign, encrypt with the public key to verify. This is a common misconception for people first learning about digital signatures [1]. It doesn’t hold in general, and most signature schemes (including the ones Bitcoin uses) don’t have a decryption step to flip in the first place. In other words, signing needs its own definition, separate from encryption. Signing has to guarantee that anyone can verify the signature, but only you could have produced it.

Following Katz and Lindell again [1], let’s define a digital signature formally.

Digital signature: A digital signature scheme is a 3-tuple of probabilistic polynomial-time algorithms comprising

  1. A key-generation algorithm Gen(1κ)→(pk,sk)\texttt{Gen}(1^\kappa) \to (pk, sk) generating a public (pkpk) and private (sksk) key pair from a security parameter κ\kappa.
  2. A signing algorithm Signsk(m)→σ\texttt{Sign}_{sk}(m) \to \sigma that outputs a signature σ\sigma from a private key sksk and message mm.
  3. A deterministic verification algorithm Vrfypk(m,σ)→{0,1}\texttt{Vrfy}_{pk}(m, \sigma) \to \{0, 1\} that verifies if σ\sigma is a valid signature of mm under the public key pkpk, with an output of 11 meaning valid and 00 meaning invalid.

Except with negligible probability (a chance so small it can be safely ignored), it must hold that

Vrfypk(m,Signsk(m))=1\texttt{Vrfy}_{pk}\left(m, \texttt{Sign}_{sk}(m)\right) = 1(1)

for every (legal) message mm.

To use a signature scheme, the sender runs Gen(1κ)\texttt{Gen}(1^\kappa) to obtain the public and private keys (pk,sk)(pk, sk). The sender is then free to publish pkpk anywhere they wish. When the sender wants to authenticate a message mm, they compute

σ←Signsk(m)\sigma \gets \texttt{Sign}_{sk}(m)(2)

and then send (m,σ)(m, \sigma) to the recipient.

Notice mm is publicly exposed, along with σ\sigma. This is different from encryption, where mm was a secret. This is on purpose, since a signature only has to prove who mm came from. The point is that anyone with pkpk, σ\sigma, and mm can verify the signature themselves. In this context, mm can be anything: a contract, a software update, a message, a vote, etc. For Bitcoin specifically, mm is built from the transaction data, which we will learn about in a later post.

The recipient who knows pkpk can then check whether

Vrfypk(m,σ)=?1.\texttt{Vrfy}_{pk}(m, \sigma) \stackrel{?}{=} 1.(3)

This way, the receiver can verify that the sender sent mm, and that mm has not been modified in transit.

#What does “secure” mean for signatures?

Up to this point we have (hopefully) gotten a good idea of what it means for something to be secure in the cryptographic sense. The Integer Factorization Problem, the Discrete Logarithm Problem (DLP), and the Elliptic Curve Discrete Logarithm Problem (ECDLP) underlie asymmetric encryption, and preimage and collision resistance (Cryptographic hash function) underlie hash functions. However, signatures are a little different. A secure signature scheme needs more than an attacker being unable to compute sksk from pkpk.

Consider an adversary who has seen you sign a bunch of other messages, and now tries to forge your signature on a message you have never signed. The formal name for security against this attack is existential unforgeability under an adaptive chosen-message attack (EUF-CMA) [1].

“Adaptive chosen-message” here means the attacker gets to pick which messages you sign, and can base each choice on the signatures they have seen so far.

Put simply, a digital signature scheme is EUF-CMA secure if no efficient attacker can produce a valid (m,σ)(m, \sigma) pair on a message mm it was never given a signature for, even if it first gets to request signatures on any other messages of its choosing. Without this property, a digital signature doesn’t prove authenticity, since someone could forge your signature on a message you never touched.

#Textbook RSA signatures

So what does it look like when a scheme isn’t EUF-CMA secure? RSA is a good example, and it actually got attacked recently. Remember the misconception from earlier, that signing is just encryption flipped around? For RSA in its most bare-bones form, usually called textbook RSA, that’s actually how it works. This is probably where the misconception came from [1]. It’s also insecure, which we’ll see in a second. You sign a message by raising it to your private exponent,

σ=msk mod N,\sigma = m^{sk} \bmod N,(4)

and anyone with pk=(N,e)pk = (N, e) can verify it by checking whether

σe≡m(modN).\sigma^e \equiv m \pmod{N}.(5)

The problem is that this plays way too nicely with multiplication. Say you get the owner to sign two messages, m1m_1 and m2m_2. Multiplying the two signatures together gives

σ1σ2≡m1skm2sk≡(m1m2)sk(modN),\sigma_1 \sigma_2 \equiv m_1^{sk} m_2^{sk} \equiv (m_1 m_2)^{sk} \pmod{N},(6)

which is a valid signature on m1m2m_1 m_2, a message they never signed. That alone already breaks EUF-CMA [1]. It’s a pretty limited forgery though, since you only get signatures on products of things you’ve already had signed. What if you want a signature on one specific message?

That’s what Joux, Naccache, and Thomé worked out back in 2007 [2]. The idea is to get signatures on a huge pile of numbers you pick (all of a specific form), then find a way to build your target message out of them by multiplying. Once you have that, you multiply the matching signatures together just like above. Finding that combination is the hard part, and they did it with the same kind of math the best factoring algorithms use. It still takes a long time, but it’s faster than the best known way to factor NN, so forging a signature ends up easier than breaking the key outright.

Nobody actually pulled this off for almost 20 years, until September 2026, when a team at UC San Diego and INRIA (including Thomé from the original paper) did it [3]. They took a device that signs with a 1024-bit RSA key, had it sign about 2322^{32} (around 4 billion) numbers of their choosing, then ran the math on a ton of computers for about five months. On a single processor core it would’ve taken 1380 years. At the end they had a forged signature on a message the device never signed, without ever getting sksk or factoring NN.

To be fair, nobody actually signs with textbook RSA. Real RSA runs the message through a hash function first and signs hash⁡(m)\operatorname{hash}(m) instead of mm. Now, to pull off the multiplying trick, you’d need to find some message mm with

hash⁡(m)≡hash⁡(m1)hash⁡(m2)(modN),\operatorname{hash}(m) \equiv \operatorname{hash}(m_1) \operatorname{hash}(m_2) \pmod{N},(7)

and with a good hash (one whose output is about as big as NN), nobody knows how to find one [1]. You no longer get to choose the numbers that actually get signed, which also kills the 2007 attack. The researchers had to switch the hashing off on the device to make it work. Still, they estimate that if an attacker can get raw RSA signatures on numbers they pick, forging one is somewhere between 30 thousand and a billion times easier than we thought from factoring alone [3].

NIST already plans to phase RSA out because of quantum computers (which we’ll get to later in the series), and under its current draft, RSA won’t be allowed at all after 2035 [4]. The same plan covers the elliptic curve signatures Bitcoin uses too. The researchers argue their result is one more reason to drop RSA entirely while we’re making that switch anyway, this time from a regular, non-quantum attack [3].

#ECDSA

Since the specific signature schemes that Bitcoin employs will be a key point in discussing how exactly quantum computers threaten Bitcoin’s security, I think it’s worth going into how these schemes work in some depth. Luckily, there are only two of interest, and the first that I will discuss is the Elliptic Curve Digital Signature Algorithm (ECDSA).

Bitcoin uses ECDSA over the secp256k1 curve. ECDSA is a general-use digital signature scheme standardized by the National Institute of Standards and Technology (NIST) [5], though the curve itself comes from a separate standard called SEC 2.

As a reminder of my earlier notation, recall that E(Zp)\mathbb{E}(\mathbb{Z}_p) is the elliptic curve group, where pp is the 256-bit prime defining secp256k1’s underlying field (with Zp={0,1,2,…,p−1}\mathbb{Z}_p = \{0, 1, 2, \dots, p - 1\}, and Zp∗\mathbb{Z}_p^* being the same set excluding 0), GG is the generator point, and nn is the order of GG (the size of the group GG generates).

Since ECDSA is a digital signature scheme, it fits into the Gen\texttt{Gen}/Sign\texttt{Sign}/Vrfy\texttt{Vrfy} template from Digital signature. Here’s what those three algorithms look like for ECDSA specifically:

ECDSA over secp256k1 is parameterized by the fixed curve E(Zp)\mathbb{E}(\mathbb{Z}_p), generator GG, order nn, hash function

(8)

and

F:E(Zp)→Zn,(x,y)↦x mod n.F: \mathbb{E}(\mathbb{Z}_p) \to \mathbb{Z}_n, \quad (x,y) \mapsto x \bmod n.(9)

These are fixed once for the whole scheme, not generated or received per key pair.

Algorithm 1: Gen\texttt{Gen}

Input: None

Output: Key pair (d,P)∈Zn∗×E(Zp)(d, P) \in \mathbb{Z}_n^* \times \mathbb{E}(\mathbb{Z}_p)

Procedure:

  1. Choose a uniformly random private key d∈Zn∗d \in \mathbb{Z}_n^*.
  2. P←dG∈E(Zp)P \gets dG \in \mathbb{E}(\mathbb{Z}_p).
  3. return (d,P)(d, P).

Algorithm 2: Sign\texttt{Sign}

Input: Private key d∈Zn∗d \in \mathbb{Z}_n^*, message m∈{0,1}∗m \in \{0, 1\}^*

Output: signature σ=(r,s)∈Zn∗×Zn∗\sigma = (r, s) \in \mathbb{Z}_n^* \times \mathbb{Z}_n^*

Procedure:

  1. Choose a uniformly random nonce k∈Zn∗k \in \mathbb{Z}_n^*.
  2. r←F(kG)r \gets F(kG).
  3. s←k−1(hash⁡(m)+dr) mod ns \gets k^{-1}(\operatorname{hash}(m) + dr) \bmod n.
  4. If r=0r = 0 or s=0s = 0, restart from step 1 with a fresh kk.
  5. return (r,s)(r, s).

Nonce is usually read as “number only used once”. Its job here is to hide dd. Look at step 3 of Sign\texttt{Sign}: if anyone ever learned kk, they could rearrange it to get d=r−1(sk−hash⁡(m)) mod nd = r^{-1}(sk - \operatorname{hash}(m)) \bmod n. So kk has to be secret, and it has to be different for every signature.

Notice that Sign\texttt{Sign} never touches mm directly, only hash⁡(m)\operatorname{hash}(m). This is where hash functions come back in. If an attacker could find two messages with hash⁡(m1)=hash⁡(m2)\operatorname{hash}(m_1) = \operatorname{hash}(m_2), a signature on m1m_1 would also be a valid signature on m2m_2. This is why the hash has to be collision resistant (Cryptographic hash function), and why MD5 signatures had to be phased out once MD5 collisions were found.

Algorithm 3: Vrfy\texttt{Vrfy}

Input: public key P∈E(Zp)P \in \mathbb{E}(\mathbb{Z}_p), message m∈{0,1}∗m \in \{0,1\}^*, signature σ=(r,s)∈Zn∗×Zn∗\sigma = (r, s) \in \mathbb{Z}_n^* \times \mathbb{Z}_n^*

Output: b∈{0,1}b \in \{0, 1\}

Procedure:

  1. Compute r′←F(hash⁡(m)s−1⋅G+rs−1⋅P)r' \gets F\left(\operatorname{hash}(m)s^{-1} \cdot G + rs^{-1} \cdot P\right).
  2. If r′=rr' = r, b←1b \gets 1, else b←0b \gets 0.
  3. return bb.

Here, ⋅\cdot is scalar multiplication on the curve, not a dot product. rs−1⋅Prs^{-1} \cdot P is the same operation as dGdG or kGkG, just written explicitly.

Why does this actually work? Let’s start with answering: why does the signature verify? Let’s substitute P=dGP = dG into the reconstruction step in Vrfy\texttt{Vrfy}.

hash⁡(m)s−1⋅G+rs−1⋅P=s−1(hash⁡(m)+rd)⋅G.\operatorname{hash}(m)s^{-1} \cdot G + rs^{-1} \cdot P = s^{-1} (\operatorname{hash}(m) + rd) \cdot G.(10)

But, from the Sign\texttt{Sign} algorithm,

s=k−1(hash⁡(m)+dr) mod n  ⟹  hash⁡(m)+rd=sk mod n.s = k^{-1}(\operatorname{hash}(m) + dr) \bmod n \implies \operatorname{hash}(m) + rd = sk \bmod n.(11)

Substituting this in, we get

s−1(sk)⋅G=kG.s^{-1}(sk) \cdot G = kG.(12)

The mod nn can be dropped here because GG has order nn, so nG=OnG = \mathcal{O} and multiplying GG by any number only depends on that number mod nn. Applying FF to both sides then gives r′=F(kG)=rr' = F(kG) = r, which is exactly the check in Vrfy\texttt{Vrfy}.

We have reconstructed kGkG, the same point rr was computed from in the first place. Why can’t this be forged? To produce a signature that passes this check without knowing dd, the obvious route is to recover dd directly from P=dGP = dG. In other words, given GG and P=dGP = dG, determine dd. Does this look familiar? This is exactly the Elliptic Curve Discrete Logarithm Problem (ECDLP) that I described earlier. This is an informal argument though, not a proof, and I’ll come back to why later.

I’ll note that this only demonstrates security against an attacker who has just your public key, but EUF-CMA is stronger, and has to hold even if an attacker has seen any number of your real signatures. The intuition still holds here, because each signature uses a fresh value of kk, so the attacker can’t extract anything useful from your past signatures unless they get lucky enough to guess kk outright.

This is also why kk can never be reused. If you sign two different messages m1m_1 and m2m_2 with the same kk, both signatures have the same rr, and subtracting the two signing equations gives

s1−s2=k−1(hash⁡(m1)−hash⁡(m2)) mod n.s_1 - s_2 = k^{-1}\left(\operatorname{hash}(m_1) - \operatorname{hash}(m_2)\right) \bmod n.(13)

Anyone can solve this for kk, and then for dd. Sony, for example, signed PlayStation 3 software using the same kk every time, and in 2010 their private key was recovered this way [6]. In 2013, a bug in Android’s random number generator caused some Bitcoin wallets to reuse kk, and coins were stolen [7].

#Schnorr Signatures

The second signature scheme of interest that Bitcoin employs is Schnorr signatures. The security of Schnorr signatures rests on the same ECDLP, but it gets there with a different construction. Once again, since Schnorr is a valid signature scheme it fits into the same Gen\texttt{Gen}/Sign\texttt{Sign}/Vrfy\texttt{Vrfy} template. However, since Gen\texttt{Gen} here is identical to the ECDSA case, I will focus only on Sign\texttt{Sign} and Vrfy\texttt{Vrfy}, since this is where Schnorr diverges.

Algorithm 4: Sign\texttt{Sign}

Input: Private key d∈Zn∗d \in \mathbb{Z}_n^*, message m∈{0,1}∗m \in \{0, 1\}^*

Output: signature σ=(r,s)∈Zn×Zn\sigma = (r, s) \in \mathbb{Z}_n \times \mathbb{Z}_n

Procedure:

  1. Choose a uniformly random nonce k∈Zn∗k \in \mathbb{Z}_n^*.
  2. I←kG∈E(Zp)I \gets kG \in \mathbb{E}(\mathbb{Z}_p).
  3. r←hash⁡(I,m)r \gets \operatorname{hash}(I, m).
  4. s←(rd+k) mod ns \gets (rd + k) \bmod n.
  5. return (r,s)(r, s).

Think of I=kGI = kG as a one-time version of your public key, the same idea as P=dGP = dG, but built from the throwaway nonce kk instead of your permanent private key dd. Hashing it with mm ties that one-time value to this specific message.

Algorithm 5: Vrfy\texttt{Vrfy}

Input: public key P∈E(Zp)P \in \mathbb{E}(\mathbb{Z}_p), message m∈{0,1}∗m \in \{0,1\}^*, signature σ=(r,s)∈Zn×Zn\sigma = (r, s) \in \mathbb{Z}_n \times \mathbb{Z}_n

Output: b∈{0,1}b \in \{0, 1\}

Procedure:

  1. I′←sG−rP∈E(Zp)I' \gets sG - rP \in \mathbb{E}(\mathbb{Z}_p).
  2. If hash⁡(I′,m)=r\operatorname{hash}(I', m) = r, b←1b \gets 1, else b←0b \gets 0.
  3. return bb.

Similar to ECDSA, Schnorr has the same idea with Vrfy\texttt{Vrfy}: reconstruct something from the signature and public key, then check it matches. For example, substituting P=dGP = dG:

sG−rP=(rd+k)G−rdG=kG=I.sG - rP = (rd + k)G - rdG = kG = I.(14)

Thus I′I^\prime reconstructs to the same point you started with, and therefore hash⁡(I′,m)=r\operatorname{hash}(I', m) = r and the verification passes. To fake this without knowing dd, you would again either have to guess kk in advance or recover dd directly from P=dGP = dG, which is the same ECDLP problem as ECDSA.

What’s the point of having two different signature schemes? Isn’t one sufficient? Both schemes reduce to the same hard problem: recovering dd from P=dGP = dG is ECDLP either way.

However, there’s one small step in the middle where they diverge, and it ends up mattering a lot. ECDSA sets r←F(kG)r \gets F(kG), which is just the x-coordinate. On the other hand, Schnorr sets r←hash⁡(I,m)r \gets \operatorname{hash}(I, m). It runs II through a hash first, and that’s why one scheme can be provably secure while the other cannot.

A proof of security basically works by imagining an attacker who could forge a signature, then showing that attacker could be turned into a machine that solves ECDLP, which is supposed to be impossible. The trick used to build that machine needs to catch the forger reusing the same kGkG but ending up with two different values of rr. If that ever happens, you can subtract the two signing equations, the nonce kk cancels out, and you’re left holding dd.

For Schnorr, a proof can actually force this to happen, because rr comes from a hash, and a hash is exactly the kind of thing a proof is allowed to just reassign (the proof treats the hash as a perfectly random function that it controls). The proof can run the forger twice with the same I=kGI = kG and hand back a different rr the second time. So an attacker who could reliably forge signatures could be forced into producing exactly this situation, two valid responses to the same kGkG, which is enough to extract dd directly. That contradicts ECDLP being hard, so no such attacker can exist. Forging a Schnorr signature is provably as hard as solving ECDLP (as long as the hash behaves like that perfectly random function) [8].

ECDSA can’t use this same argument. rr is just the xx-coordinate of kGkG, which is fixed the moment kGkG exists, so there’s no way to force a second value of rr out of it. This doesn’t mean ECDSA is unprovable in principle, just that this specific method doesn’t work, and decades later, nobody’s found a different one that works from ECDLP alone either.

So from a pure security standpoint, Schnorr wins, and it’s older (1989 vs 1992 for ECDSA [5]). Given that, you’d expect Bitcoin to have used Schnorr from day one. It didn’t, and the reason is historical. Schnorr patented his scheme [9] shortly after it was published in 1989 [10]. Around that time, NIST needed a standard digital signature algorithm but didn’t want to license someone else’s patent. So they went with DSA, which is widely seen as designed to get around Schnorr’s patent (though Schnorr argued it didn’t). ECDSA is DSA carried over to elliptic curves. The key change was swapping Schnorr’s hashed rr for just the xx-coordinate of kGkG, which also meant the proof technique above no longer works. The patent expired in 2008, shortly before Bitcoin launched, but by then ECDSA was already the standard, so that’s what Bitcoin used. Schnorr signatures were not introduced into Bitcoin until 2021 [11].

The security proof was one reason Bitcoin eventually added Schnorr, but the bigger reason was a practical one. Schnorr’s signing equation

s=(rd+k) mod ns = (rd + k) \bmod n(15)

is linear in dd and kk, meaning that different people’s signing equations can be added together. For example, three people can work together to produce a single (r,s)(r, s) pair that looks exactly like a one-person signature (this takes a special protocol, not just adding three finished signatures). The analogous step for ECDSA has a k−1k^{-1} sitting in front of it, which breaks this property.

That was all of the cryptography I had planned to cover, and I hope it was useful. Both signature schemes Bitcoin uses come down to the Elliptic Curve Discrete Logarithm Problem (ECDLP): recovering dd from P=dGP = dG. Next I’m going into Bitcoin itself and how it works.

[1]
Katz J, Lindell Y. Introduction to Modern Cryptography. 2nd ed. 2014.
[2]
Joux A, Naccache D, Thomé E. When e-th Roots Become Easier Than Factoring [Internet]. Cryptology ePrint Archive, Paper 2007/424; 2007. Available from: https://eprint.iacr.org/2007/424
[3]
Shea L, Haller M, Suhl A, Heninger N, Thomé E. Forging 1024-bit RSA Signatures in Nearly SNFS Time [Internet]. Cryptology ePrint Archive, Paper 2026/2131; 2026. Available from: https://eprint.iacr.org/2026/2131
[4]
Moody D, Perlner R, Regenscheid A, Robinson A, Cooper D. Transition to Post-Quantum Cryptography Standards [Internet]. 2024. Available from: https://doi.org/10.6028/NIST.IR.8547.ipd
[5]
Johnson D, Menezes A, Vanstone S. The Elliptic Curve Digital Signature Algorithm (ECDSA). Int J Inf Secur [Internet]. 2001;1(1):36–63. Available from: https://doi.org/dbkbpj
[6]
bushing, marcan, sven. Console Hacking 2010: PS3 Epic Fail [Internet]. Talk at the 27th Chaos Communication Congress (27C3), Berlin; 2010. Available from: https://media.ccc.de/v/27c3-4087-en-console_hacking_2010
[7]
Breitner J, Heninger N. Biased Nonce Sense: Lattice Attacks against Weak ECDSA Signatures in Cryptocurrencies. In: Financial Cryptography and Data Security – FC 2019 [Internet]. Springer; 2019. p. 3–20. Available from: https://doi.org/hckkv6
[8]
Pointcheval D, Stern J. Security Arguments for Digital Signatures and Blind Signatures. J Cryptol [Internet]. 2000;13(3):361–96. Available from: https://doi.org/b9qh92
[9]
Schnorr CP. Method for Identifying Subscribers and for Generating and Verifying Electronic Signatures in a Data Exchange System [Internet]. US Patent 4,995,082, 1991. Available from: https://patents.google.com/patent/US4995082A
[10]
Schnorr CP. Efficient Identification and Signatures for Smart Cards. In: Advances in Cryptology – CRYPTO ’89 Proceedings. Springer; 1990. p. 239–52.
[11]
Wuille P, Nick J, Ruffing T. Schnorr Signatures for secp256k1 [Internet]. Bitcoin Improvement Proposal 340; 2020. Available from: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki