You can multiply on this curve, but you cannot divide

COMMIT5c5dd91HEAD → main
PUBLISHEDMar 22, 20215y ago
UPDATEDJul 30, 2026today
READING22 min4,548 words
#cryptography#bitcoin#mathematics·by santiago toscanini

There is a sentence I have used in this series and never paid for. In part two it reads: private key → public key is one multiplication on an elliptic curve: the point d·G, where d is your number and G is a fixed base point everyone shares. Easy to compute forward, believed impossible to reverse. Then the post moves on to hashes and addresses, and the curve never comes back. It reappears in the Ethereum epilogue as ecrecover, a function that reconstructs your address out of a signature, and I ask you to accept that too.

That's a lot of accepting, in a series whose whole promise is that you can read every byte. So this post is the debt. By the end you will have added two points on a curve by hand, watched a curve stop being a curve and become 78 dots, computed a real Bitcoin address from a real private key by walking 256 doublings, signed something and verified it with algebra you can follow line by line, and then stolen a private key using nothing but two signatures and a page of arithmetic.

It assumes no mathematics beyond multiplication and remainder. Everything else — fields, groups, the discrete logarithm — gets built here, in the order you need it. The one thing I'm not going to do is show you a picture of a smooth curve, say "and it's very hard to reverse," and leave. The hardness is the entire product. It deserves better than an adjective.

Nothing here is a mockup. The toy curve is real and small enough that the figures enumerate every point on it. The real curve is secp256k1, running through the same minichain library that verified those 2010 Bitcoin spends and signed the Ethereum transactions — a few hundred lines of TypeScript over native bigint, no dependencies. When the address figure hands you 1BgGZ9tc…, that address was computed in your tab from the number 1, and any wallet on Earth will agree.

01 · A street that only runs one way

Start before the curves, because curves are an optimization and the idea is older than they are.

Ordinary encryption has one key, and both sides need it. That's fine when you can meet in a car park and hand over a briefcase; it collapses the moment you want to talk to a stranger, because getting the key to them requires exactly the secure channel you were trying to build. In 1976 Diffie and Hellman published the way out: use two keys, mathematically bound, where knowing one tells you nothing useful about the other. Publish one, keep one. The one you publish can lock; only the one you keep can unlock.

Which needs a very specific kind of arithmetic — an operation that is cheap to run forwards and hopeless to run backwards, where "hopeless" isn't a matter of nobody having tried. Their answer lives in modular arithmetic, which is nothing more exotic than a clock. On a 12-hour clock, 10 + 5 = 3: you add normally and keep the remainder. Write a mod n for "the remainder of a divided by n," fix a prime p, and do all your arithmetic on a clock with p positions. Everything stays inside 0 … p−1 forever.

Now the asymmetry. Pick a number g and compute g^x mod p — multiply g by itself x times, keeping the remainder as you go. Forwards is fast: you never need x multiplications, because squaring gets you to exponents 2, 4, 8, 16, and every exponent is a sum of those, so a 256-bit x costs a few hundred multiplications rather than 2²⁵⁶ of them. Backwards — given g, p and the result, find x — is the discrete logarithm problem, and after fifty years of public attention nobody has published a method that beats trying every exponent when p is chosen well.

Watch both directions. Drag the exponent; then hand the result back to an attacker who has to find it:

Things worth watching for:

  • The orbit scatters immediately. Consecutive exponents produce results with no visible relationship — 2¹⁰⁰ and 2¹⁰¹ land nowhere near each other on the clock. That's the whole difficulty in one row of numbers: the output carries no trace of the input, so there is no gradient to follow, nothing to bisect, nothing to be clever about.
  • The attack is trivial here, and that's the honest part. 467 is a tiny prime, so "try every exponent" finishes before your finger leaves the button. The security is not in the idea; it's in the size. Everything after this is a fight about how big a number has to be.
  • The last row is the actual scale. secp256k1's group has about 1.16 × 10⁷⁷ elements. At ten billion guesses per second, walking them all takes on the order of 10⁵⁹ years, against a universe roughly 1.4 × 10¹⁰ years old. There is no engineering that closes that gap; it isn't a hardware problem.

Diffie–Hellman shipped on exactly this — plain integers modulo a prime — and it works. So why isn't this the end of the post?

Because of key sizes. The discrete log over integers mod p has better-than-brute-force attacks: the number field sieve and its relatives exploit the fact that ordinary integers factor, and that structure gives an attacker leverage. To keep 128 bits of security you need p around 3072 bits. Every handshake then drags 3072-bit numbers around, and so does every embedded device and every smart card. The question that produced elliptic curves was not "how do we make this more secure?" It was: can we find a setting where the same problem is just as hard, but the numbers are smaller?

02 · A curve, and a strange way to add its points

Here is the setting, proposed independently by Neal Koblitz and Victor Miller in 1985. Take the equation

y2=x3+ax+by^2 = x^3 + ax + b

and consider all the points (x,y)(x, y) that satisfy it. For Bitcoin, a=0a = 0 and b=7b = 7, so the equation is just y2=x3+7y^2 = x^3 + 7. It's a cubic, which matters for exactly one reason: a straight line crosses a cubic in exactly three places. That single fact is the whole construction.

Because from it you can define an addition on points. Take two points P and Q on the curve, draw the line through them, find the third place it hits the curve, and reflect that point across the x-axis. Call the result P + Q. That's it — that's the definition. It looks arbitrary and it is not: the reflection is precisely what makes the operation obey the rules ordinary addition obeys.

Drag the points and watch the construction. The three tabs are the three cases the rule has to handle:

Things worth watching for:

  • The chord case is the definition. Two distinct points, one line, one third intersection, one reflection. Nothing is approximated — the figure solves for the third point algebraically, which is why the coordinates are exact and why the same formula will survive into the next section where there is no picture at all.
  • The tangent handles P + P. When both points are the same there's no chord to draw, so the construction takes the limit: the tangent at P. It meets the curve at exactly one other place, and reflecting that gives 2P. Every "doubling" in the rest of this post is that picture, and doubling is what makes the whole scheme fast.
  • The vertical case invents a point. Draw the line through P and its mirror image −P and it's vertical: it escapes the curve without hitting a third point. Rather than accept a hole in the definition, the construction declares that the missing third intersection exists and lives infinitely far up — the point at infinity, written ∞. And that invented point turns out to behave exactly like zero: P + ∞ = P, and P + (−P) = ∞.

Now name what we just built. A group is a set with an operation that has four properties: the operation combines any two elements into a third, it's associative, there's an identity element, and every element has an inverse. Ordinary addition on integers is a group: 0 is the identity, −5 undoes 5. Points on an elliptic curve with the chord-and-tangent rule are also a group, with ∞ as the identity and the mirror image as the inverse. Associativity is the only property that isn't obvious from the pictures, and it's a genuine theorem with an ugly proof; the rest you can read straight off the figure.

Why does that matter? Because a group is all that public-key cryptography needs. Every construction in this post — key agreement, signatures, key recovery — only ever uses "combine two elements" and "do it k times." None of them cares what the elements are. Swap in a different group and the algorithms come along unchanged. Which is convenient, because we are about to break the pictures.

03 · Now delete the geometry

The curve above lives over the real numbers, and real numbers are useless to a computer: infinitely many of them, no exact representation, rounding everywhere. Cryptography needs exact arithmetic on finitely many values. So we do the obvious violent thing: run the same equation over a finite set of numbers instead.

Specifically, over the integers 0 … p−1 with arithmetic mod p, p prime. This set is a field, which is a fancy way of saying all four operations behave: you can add, subtract, multiply, and — the one that needs primality — divide by anything non-zero. Division works because when p is prime, every non-zero a has some b with a·b ≡ 1 (mod p), so "divide by a" means "multiply by that b." That inverse is what the slope formula in the last section needs, and it's the only reason p has to be prime.

Nothing else changes. The chord-and-tangent formulas were derived using addition, subtraction, multiplication and division — and a finite field has all four. So the formulas carry over verbatim. What does not carry over is the picture. Here is y2=x3+7y^2 = x^3 + 7 — the same equation as Bitcoin's, the same one from the last figure — over a field of 97 elements:

Things worth watching for:

  • The curve is gone and the algebra is fine. There is no arc, no smoothness, no left-to-right anything: 78 dots scattered in a 97×97 grid, plus ∞, making a group of exactly 79. And the addition still works. Pick any two points and the sum is another point on the curve, every time, because the formulas never needed the geometry — they needed a field.
  • The line still exists, and it wraps. On the first tab, the dim trail is every lattice point on the chord through P and Q. It runs off the top of the grid and reappears at the bottom, because the field wraps. It still meets the curve at exactly one third point. "Reflection" now means reflecting across y = p/2, the dashed line — which is what "flip the sign of y" means on a clock.
  • k·G walks the entire group. On the second tab, drag k and watch the trail fill in. Because 79 is prime, every non-identity point generates all of it; at k = 79 you land on ∞ and the walk starts over. The order in which the walk visits the points is, visually, noise — and that noise is the security.
  • The discrete log is now a curve problem. Third tab: pick any point and hit "try every k." It finds the answer in at most 79 additions, because a group of 79 elements has nowhere to hide. secp256k1 uses the same equation over a group of about 1.16 × 10⁷⁷ elements.

One theorem worth naming, because it explains why nobody has to hope: Hasse's theorem says the number of points on a curve over a field of size p is always within 2√p of p + 1. For p = 97 that's somewhere between 78 and 118 — and we got 79. So the group size is never a surprise, it's roughly p, and choosing a curve with a prime number of points (which is what real curve designers do) means the group has no small subgroups for an attacker to fall into.

04 · secp256k1, with the actual numbers in it

Now swap 97 for the prime Bitcoin actually uses. Here is the whole specification of the curve underneath every bitcoin and every ether:

p  = 2²⁵⁶ − 2³² − 977            the field: arithmetic is mod this prime
a  = 0,  b = 7                    the curve: y² = x³ + 7
G  = (79be667e…16f81798,          the base point everyone shares
      483ada77…fb10d4b8)
n  = fffffffffffffffffffffffffffffffe baaedce6af48a03bbfd25e8cd0364141
                                   the number of points, and it is prime

Six numbers. That's the entire parameter set, and the name encodes it: security, prime field, 256 bits, koblitz, curve 1. And a private key is nothing more exotic than a random number below n — that's the whole definition, which is why part two could say a private key is "a very large random number" and be complete rather than glib. Your public key is that number times G.

Type a key and watch the multiplication happen, one bit at a time, ending at a real address:

Things worth watching for:

  • 256 bits cost about 384 operations, not 2²⁵⁶. Read the ladder: each bit means "double the running point," and a 1 bit also means "add it to the accumulator." So a 256-bit scalar is at most 512 curve operations — the same square-and-multiply shortcut from section 01, one level up. Try 2²⁵⁵: one addition, 255 doublings. This is why your wallet derives an address the instant you ask.
  • The address is part two's pipeline, standing on this. The point gets serialized, hashed twice, given a version byte and a checksum, and written in Base58. That entire post sat on top of the single multiplication this figure just performed. Type 1 and you get 1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH — a real, valid mainnet address whose private key is the number one, which is to say an address anyone reading this paragraph can spend from. Its uncompressed sibling, 1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm, is one of the most-watched addresses on the chain for exactly that reason.
  • Backwards is not 256 steps the other way. The best known attacks (Pollard's rho and its parallel variants) cost about the square root of the group size — 2¹²⁸ for secp256k1. That's the honest security number, and it's the reason a 256-bit curve is quoted as offering "128-bit security."

Which finally answers section 01's question about size. Here is the trade, at 128-bit security:

primitivekey sizewhy
RSA3072 bitsfactoring has sub-exponential attacks (the number field sieve)
Diffie–Hellman mod p3072 bitssame sieve, same leverage
elliptic curve256 bitsno sub-exponential attack known; √n is the best there is

A twelvefold shrink, for the same security, because the sieve has no purchase on a curve group. That's the entire reason elliptic curves won — not extra security, the same security in smaller numbers — and it's why they run on smart cards, in TLS handshakes, and in a blockchain where every signature is stored forever by every node.

One footnote, because it's the most-repeated story in this corner of the subject. secp256k1 is not the curve most of the internet uses; almost everything else standardized on NIST's P-256. The popular explanation is that Satoshi avoided NIST curves because their parameters come from an unexplained random seed nobody has ever justified, while secp256k1's are chosen transparently for speed — and, later, because the Dual_EC_DRBG affair showed the NSA really had pushed a backdoored construction through a NIST standard. Be careful with that timeline: cryptographers flagged Dual_EC_DRBG as suspicious in 2007, so the concern was available in 2008, but the documents that turned suspicion into consensus arrived with the Snowden leaks in 2013, four years after the genesis block. Satoshi left no documented reasoning. The defensible statement is narrower and still interesting: secp256k1 has a = 0, which admits a genuinely faster implementation, and rigid parameters anyone can re-derive. Whether that's why it was picked, nobody can say.

05 · Agreeing on a secret in public

Before signatures, the thing curves were invented for. Alice and Bob have never met and every byte between them is public. They each pick a secret number, multiply G by it, and send the resulting point. Then each multiplies the other's point by their own secret:

They land on the same point, and the reason is one line: a·(b·G) and b·(a·G) are the same walk taken in a different order. Scalar multiplication commutes, so the shared secret falls out with nothing transmitted that would let an eavesdropper reconstruct it. Eve saw G, a·G and b·G — three public points — and to get anywhere she needs a discrete log. On the toy curve she gets one for free; press the button. On secp256k1 the same button would need about 2¹²⁸ additions.

This is ECDH, and it is the handshake underneath essentially every https:// you have ever loaded — usually over Curve25519 rather than secp256k1, a different curve chosen for implementation safety, but the same argument shape. Bitcoin does not use it: Bitcoin never needs two parties to agree on a secret, it only ever needs one party to prove authorship. Which is the next section, and it's a different problem than most explanations admit.

06 · Signing, which is not encrypting

Let me restate the thing this series keeps insisting on, now that there's machinery to say it precisely. Bitcoin encrypts nothing. Every address, script, public key and signature on the chain is in the clear. A signature does not hide the message, does not lock anything, and does not have a "decrypt" operation. What it does is prove that whoever produced it knew a private key — without revealing that key, and while remaining bound to one specific message.

You cannot get that from encryption run backwards, and the popular "sign with your private key, verify by decrypting with the public key" story is wrong for elliptic curves in a way that isn't a simplification: there is no encryption operation here at all. ECDSA is its own algorithm. Here it is, and it fits in four lines.

To sign a message hash e with private key d:

  1. pick a random k — the nonce — in 1 … n−1
  2. compute the point R = k·G, and let r = R.x mod n
  3. compute s = k⁻¹(e + r·d) mod n
  4. the signature is (r, s) — two numbers, 64 bytes, and nothing else

To verify, using only the public key Q, the hash e, and (r, s):

  1. w = s⁻¹ mod n
  2. u₁ = e·w, u₂ = r·w, both mod n
  3. compute the point u₁·G + u₂·Q, and check its x-coordinate equals r

Sign something and watch both columns fill in. Then break it three different ways:

Things worth watching for:

  • The verifier reconstructs a point it was never sent. Substitute the definitions and the verification collapses: u₁·G + u₂·Q = (e·w)·G + (r·w)·(d·G) = w·(e + r·d)·G, and since s = k⁻¹(e + r·d), w = s⁻¹ is exactly k/(e + r·d) — so the whole expression is k·G, which is R, whose x-coordinate is r. The verifier never learns k or d. It confirms that two sides of an equation agree, and only someone holding d could have made them agree.
  • Editing one character of the message kills it. The hash changes completely, so e changes, so the reconstructed point lands somewhere unrelated. A signature is welded to exactly one message — which is what stops anyone lifting a valid signature off one Bitcoin transaction and pasting it onto another.
  • Tampering with r and tampering with s both fail, and neither errors. They just produce a different point, whose x-coordinate isn't r. There's no "signature rejected" branch in the math; verification is a comparison, and a bad signature is simply one where the comparison doesn't hold.

This is the exact computation OP_CHECKSIG performs on every Bitcoin spend — you stepped through one without the algebra. And it's ecrecover on Ethereum, with one addition: given (r, s) and one extra bit saying which of the two points with x-coordinate r was R, you can run the algebra the other way and reconstruct Q itself. That's why an Ethereum transaction has no from field: the sender isn't transmitted, it's recomputed from the signature. Your identity is a consequence of the one number you never reveal.

07 · The one number that must never repeat

Look again at step 1 of signing: pick a random k. Everything else in ECDSA is deterministic. That single random number is where the whole scheme lives or dies, and it dies harder than anything else in cryptography.

Here's why. Sign two different messages with the same k and you produce:

s₁ = k⁻¹(e₁ + r·d)
s₂ = k⁻¹(e₂ + r·d)

Two equations. Two unknowns, k and d. Subtract them and d cancels, leaving k = (e₁ − e₂)/(s₁ − s₂); substitute back and d = (s₁·k − e₁)/r. That's it. No brute force, no weakness in the curve, no supercomputer — a page of algebra a schoolchild could follow, and the private key is on the table. Worse, the tell is public: reusing k produces the same r twice, and every signature on a blockchain is there for anyone to scan.

Do it. Sign two messages with a reused nonce and take the key:

Things worth watching for:

  • r₁ = r₂ is the entire detection strategy. Flip to the fixed mode and the two r values diverge completely, the second equation stops existing, and the recovery has nothing to solve. One repeated number is the difference between a secure signature and a published one.
  • The recovered d is the victim's key, digit for digit. Not a candidate, not a narrowing — the exact 256-bit number. The key survived being used correctly any number of times and died the first time a random number came up twice.
  • Both incidents in the last panel are real. In December 2010 fail0verflow showed at 27C3 that Sony's PS3 signed with a constant where the nonce belonged; two signed binaries yielded the console's root signing key, and Sony couldn't rotate it without breaking every disc already pressed. In August 2013, a flaw in Android's SecureRandom made Bitcoin wallets sign different transactions with repeated k. Since every signature is on the chain, attackers scanned for duplicate r values and swept the wallets — 55.82 BTC, and an advisory naming Bitcoin Wallet, blockchain.info, BitcoinSpinner and Mycelium.

The fix, and it's a good one: stop asking for randomness. RFC 6979 derives k deterministically by HMAC from the private key and the message hash — unpredictable to anyone without the key, reproducible for the signer, and completely independent of the platform's random number generator. Sign the same message twice and you get the same signature, which is a feature. minichain does this, which is why signing in the previous figure is stable across reloads. Bitcoin's newer Schnorr signatures (BIP340, live since taproot in 2021) bake the same idea into the standard itself. The lesson generalizes past ECDSA: the most reliable way to survive a bad random number generator is to not need one.

08 · What would actually break this

Three honest threats, in increasing order of how much they should worry you.

The math, essentially not at all. The elliptic-curve discrete log has been public since 1985 and attacked continuously. The best general algorithms are still square-root — Pollard's rho, and Shanks before it — which is where 2¹²⁸ comes from. There are real curve classes that broke (anomalous curves, supersingular curves over small fields, curves with tiny embedding degrees), and that's why parameter choice is not a matter of taste. secp256k1 is not in any of them.

The implementation, quite a lot. This is where the failures actually happen. Section 07 is one whole family. The others: a pointMul whose running time depends on the bits of the scalar leaks the key to anything that can measure it, which is why real libraries use constant-time ladders; a verifier that fails to check an incoming point is even on the curve can be fed a point from a weaker curve and give the key away a few bits at a time; and a signature scheme that permits (r, s) and (r, n−s) both to verify has a malleability problem, which is why Bitcoin normalizes to low s. My library on this page does none of that hardening — it's built to be readable, and its own source says so in the first comment.

Quantum computers, eventually, and to elliptic curves first. Shor's algorithm (1994) solves discrete logarithms — including the elliptic-curve kind — in polynomial time on a machine that doesn't exist yet. This is worth stating carefully, because it's usually stated badly. Grover's algorithm gives only a square-root speedup against hash functions, so SHA-256 and the mining in part three degrade gracefully; ECDSA does not degrade, it ends. Bitcoin's specific exposure is uneven: coins locked to a plain address only ever revealed a hash of the public key, so the point itself is hidden until you spend — which is why part two's hash160 step, sold there as a small privacy win, turns out to be a quantum hedge nobody was designing for. Coins already spent from, addresses reused, and the early pay-to-public-key outputs from 2009 have their public keys sitting in the clear right now. NIST finished standardizing replacements in August 2024 — ML-KEM, ML-DSA and SLH-DSA, under FIPS 203/204/205 — and none of them are drop-in: the signatures are one to two orders of magnitude larger, which is a serious problem for a system that stores every signature forever on every node. Nobody credibly knows the timeline. What can be said is that the failure, if it comes, won't be a mistake in the mathematics. The mathematics is fine. The assumption would have expired.

09 · What's next

The compressed model, and it's shorter than the post suggests. Public-key cryptography needs an operation that's cheap forwards and hopeless backwards. Take a cubic curve, define "adding" two points as draw the line, take the third intersection, reflect it, and you get a group — the four ordinary rules of addition, on points. Run that same rule over integers mod a prime instead of over the reals and the picture disappears while the algebra survives intact, leaving a finite scatter of points that is still a group. Pick one point G and one 256-bit number d, and d·G costs a few hundred operations forwards and about 2¹²⁸ backwards. That gap is the key pair: d private, d·G public. Signing is two numbers derived from the key, a message hash, and a nonce, verified by an identity that only closes if the signer held the key; and the nonce must never, ever repeat.

Honesty about the edges, gathered in one place. My curve library is written to be read, not to be safe: no constant-time arithmetic, no windowed multiplication, no side-channel hygiene, no point validation beyond what the figures need. The toy field has 97 elements because 97 elements can be drawn, and its discrete log falls in 79 additions — the smallness is the pedagogy and also the thing that makes it worthless as security. I show ECDSA because it's what Bitcoin's 2010 transactions and Ethereum's transactions actually use; I name Schnorr and don't implement it. And I've said "believed hard" throughout rather than "proven hard," because it is: there is no proof that the elliptic-curve discrete logarithm is difficult, only forty years of people failing to make it easy. Every cryptosystem you use rests on that same kind of silence.

Where this goes, if you arrived here from the chain:

  • Ownership: the multiplication you just ran, wrapped in hashes to make an address, and OP_CHECKSIG verifying a real 2010 signature opcode by opcode
  • A transaction has no sender: the same (r, s) plus one recovery bit, run backwards to reconstruct an Ethereum address that was never transmitted
  • Bitcoin has no new parts: the prologue, where RSA does the same trick with a different one-way street, and Chaum builds untraceable money out of it in 1982

Same promise as always: it's algebra all the way down, no magic, and you can re-run every step of it.

$git log --oneline public/posts/elliptic-curves/
5c5dd91Elliptic curves: the cryptography the crypto series kept deferringtoday
© 2026 · v2.0 · santiago toscanini