// ------------------------------------------------------------ 60. Single-Coordinate Ladders and Insecure Twists All our hard work is about to pay some dividends. Here's a list of cool-kids jargon you'll be able to deploy after completing this challenge: * Montgomery curve * single-coordinate ladder * isomorphism * birational equivalence * quadratic twist * trace of Frobenius Not that you'll understand it all; you won't. But you'll at least be able to silence crypto-dilettantes on Twitter. Now, to the task at hand. In the last problem, we implemented ECDH using a short Weierstrass curve form, like this: y^2 = x^3 + a*x + b For a long time, this has been the most popular curve form. The NIST P-curves standardized in the 90s look like this. It's what you'll see first in most elliptic curve tutorials (including this one). We can do a lot better. Meet the Montgomery curve: B*v^2 = u^3 + A*u^2 + u Although it's almost as old as the Weierstrass form, it's been buried in the literature until somewhat recently. The Montgomery curve has a killer feature in the form of a simple and efficient algorithm to compute scalar multiplication: the Montgomery ladder. Here's the ladder: function ladder(u, k): u2, w2 := (1, 0) u3, w3 := (u, 1) for i in reverse(range(bitlen(p))): b := 1 & (k >> i) u2, u3 := cswap(u2, u3, b) w2, w3 := cswap(w2, w3, b) u3, w3 := ((u2*u3 - w2*w3)^2, u * (u2*w3 - w2*u3)^2) u2, w2 := ((u2^2 - w2^2)^2, 4*u2*w2 * (u2^2 + A*u2*w2 + w2^2)) u2, u3 := cswap(u2, u3, b) w2, w3 := cswap(w2, w3, b) return u2 * w2^(p-2) You are not expected to understand this. No, really! Most people don't understand it. Instead, they visit the Explicit-Formulas Database (https://www.hyperelliptic.org/EFD/), the one-stop shop for state-of-the-art ECC implementation techniques. It's like cheat codes for elliptic curves. Worth visiting for the bibliography alone. With that said, we should try to demystify this a little bit. Here's the CliffsNotes: 1. Points on a Montgomery curve are (u, v) pairs, but this function only takes u as an input. Given *just* the u coordinate of a point P, this function computes *just* the u coordinate of k*P. Since we only care about u, this is a single-coordinate ladder. 2. So what the heck is w? It's part of an alternate point representation. Instead of a coordinate u, we have a coordinate u/w. Think of it as a way to defer expensive division (read: inversion) operations until the very end. 3. cswap is a function that swaps its first two arguments (or not) depending on whether its third argument is one or zero. Choosy implementers choose arithmetic implementations of cswap, not branching ones. 4. The core of the inner loop is a differential addition followed by a doubling operation. Differential addition means we can add two points P and Q only if we already know P - Q. We'll take this difference to be the input u and maintain it as an invariant throughout the ladder. Indeed, our two initial points are: u2, w2 := (1, 0) u3, w3 := (u, 1) Representing the identity and the input u. 5. The return statement performs the modular inversion using a trick due to Fermat's Little Theorem: a^p = a mod p a^(p-1) = 1 mod p a^(p-2) = a^-1 mod p 6. A consequence of the Montgomery ladder is that we conflate (u, v) and (u, -v). But this encoding also conflates zero and infinity. Both are represented as zero. Note that the usual exceptional case where w = 0 is handled gracefully: our trick for doing the inversion with exponentiation outputs zero as expected. This is fine: we're still working in a subgroup of prime order. Go ahead and implement the ladder. Remember that all computations are in GF(233970423115425145524320034830162017933). Oh yeah, the curve parameters. You might be thinking that since we're switching to a new curve format, we also need to pick out a whole new curve. But you'd be totally wrong! It turns out that some short Weierstrass curves can be converted into Montgomery curves. This is because all finite cyclic groups with an equal number of elements share a kind of equivalence we call "isomorphism". It makes sense, if you think about it - if the order is the same, all the same subgroups will be present, and in the same proportions. So all we need to do is: 1. Find a Montgomery curve with an equal order to our curve. 2. Figure out how to map points back and forth between curves. You can perform this conversion algebraically. But it's kind of a pain, so here you go: v^2 = u^3 + 534*u^2 + u Through cunning and foresight, I have chosen this curve specifically to have a really simple map between Weierstrass and Montgomery forms. Here it is: u = x - 178 v = y Which makes our base point: (4, 85518893674295321206118380980485522083) Or, you know. Just 4. Anyway, implement the ladder. Verify ladder(4, n) = 0. Map some points back and forth between your Weierstrass and Montgomery representations and verify them. One nice thing about the Montgomery ladder is its lack of special cases. Specifically, no special handling of: P1 = O; P2 = O; P1 = P2; or P1 = -P2. Contrast that with our Weierstrass addition function and its battalion of ifs. And there's a security benefit, too: by ignoring the v coordinate, we take away a lot of leeway from the attacker. Recall that the ability to choose arbitrary (x, y) pairs let them cherry-pick points from any curve they can think of. The single-coordinate ladder robs the attacker of that freedom. But hang on a tick! Give this a whirl: ladder(76600469441198017145391791613091732004, 11) What the heck? What's going on here? Let's do a quick sanity check. Here's the curve equation again: v^2 = u^3 + 534*u^2 + u Plug in u and take the square root to recover v. You should detect that something is quite wrong. This u does not represent a point on our curve! Not every u does. This means that even though we can only submit one coordinate, we still have a little bit of leeway to find invalid points. Specifically, an input u such that u^3 + 534*u^2 + u is not a quadratic residue can never represent a point on our curve. So where the heck are we? The other curve we're on is a sister curve called a "quadratic twist", or simply "the twist". There is actually a whole family of quadratic twists to our curve, but they're all isomorphic to each other. Remember that that means they have the same number of points, the same subgroups, etc. So it doesn't really matter which particular twist we use; in fact, we don't even need to pick one. We're mostly interested in the subgroups present on the twist, which means we need to know how many points it contains. Fortunately, it turns out to be easier to count the combined set of points on the curve and its twist at the same time. Let's do it: 1. For every nonzero u up to the modulus p, if u^3 + A*u^2 + u is a square in GF(p), there are two points on the original curve. 2. If the above sum is a nonsquare in GF(p), there are two points on the twisted curve. It should be clear that these add up to 2*(p-1) points in total, since there are p-1 nonzero integers in GF(p) and two points for each. Let's continue: 3. Both the original curve and its twist have a point (0, 0). This is just a regular point, not the group identity. 4. Both the original curve and its twist have an abstract point at infinity which serves as the group identity. So we have 2*p + 2 points across both curves. Since we already know how many points are on the original curve, we can easily calculate the order of the twist. If Alice chose a curve with an insecure twist, i.e. one with a partially smooth order, then some doors open back up for Eve. She can choose low-order points on the twisted curve, send them to Alice, and perform the invalid-curve attack as before. The only caveat is that she won't be able to recover the full secret using off-curve points, only a fraction of it. But we know how to handle that. So: 1. Calculate the order of the twist and find its small factors. This one should have a bunch under 2^24. 2. Find points with those orders. This is simple: a. Choose a random u mod p and verify that u^3 + A*u^2 + u is a nonsquare in GF(p). b. Call the order of the twist n. To find an element of order q, calculate ladder(u, n/q). 3. Send these points to Alice to recover portions of her secret. 4. When you've exhausted all the small subgroups in the twist, recover the remainder of Alice's secret with the kangaroo attack. HINT: You may come to notice that k*u = -k*u, resulting in a combinatorial explosion of potential CRT outputs. Try sending extra queries to narrow the range of possibilities.