hxp CTF 2021
December 20, 2021This is a writeup for infinity, a challenge based on CSIDH. This was actually my first time working with CSIDH, and I found the original paper quite helpful. If you aren’t familiar with isogenies in general, I would suggest reading section 3 of Craig Costello’s tutorial (the rest is only relevant to SIDH, a different isogeny-based cryptosystem). Also, here’s my solution code.
In this challenge, the server generates a private key and performs up to 500 key exchanges with us. However, this implementation of CSIDH is buggy — in fact, the computation is not even deterministic! Comparing against Algorithm 2 in the CSIDH paper, I found that the implementation does not check whether is the point at infinity. This occurs with probability , and when it happens, we obtain the identity map rather than an -isogeny. Evaluating it does nothing, but the code still decrements the exponent as if it evaluated an -isogeny.
The upshot is that Alice will often fail to apply the full class-group action given by her private key. Let be Alice’s public curve. Due to the faults, Alice instead computes , where and have the same sign, but . To sample these faulty cuves, we can submit a public key and apply to the response (make sure to patch the implementation!).
Let’s visualize a graph with curves as vertices and -isogenies as edges. Since faults don’t occur very often, the faulty curves are all clustered in one area of the graph. Finding paths between them can be done with a meet in the middle algorithm. For example, suppose we perform a depth-2 search from each sample and cache every curve we visit. Every time we encounter a collision, we obtain a path between curves. It follows that we recover all paths of length 4 or less. In practice, I was able to consistently link all 500 samples into a single connected component.
As an intermediate step, let’s identify . Suppose the first and last edges of a path in the connected component are isogenies of the same degree. Then none of the intermediate vertices in the path are , since Alice can only under-calculate the group action. This observation allows us to rule out many curves. Out of the remaining ones, I consistently found that was the most common sample.
Using , we can compute for each sample and make the following deductions:
If any , then . This is because we know . Similarly, if any , then .
If , then we would expect to observe some faults. In fact, the probability of no faults across is . Even for the largest prime 113, this is around one percent. Therefore if for all samples, we can safely guess .
In general, we expect to see faults for each . For the largest prime 113, the expected values are 4.24 and 8.81 for respectively.
By guessing private keys in order of likelihood, I was able to decrypt the flag within a few tries.