Geni
Contents
Last weekend, I participated in BSides Algiers CTF, hosted by Shellmates, and I was able to win 1st place! I managed to solve all the cryptography challenges. This writeup focuses on the one I found to be the most interesting and hardest: Geni.
At first, I didn’t have a deep understanding of isogenies, but by reading the CryptoHack Isogeny Challenges, I gained a basic understanding that allowed me to start reading the challenge files and understand the underlying mechanics.
Challenge Info
- CTF: BSides Algiers 2025
- Challenge: Geni
- Category: Crypto
- Description: what about an isogeny challenge?
- Author: scalio
File Analysis
The challenge provided several SageMath files that implemented the cryptosystem:
CSIDH.sage: Contains the core logic for the Commutative Supersingular Isogeny Diffie-Hellman (CSIDH) group action over 74 small primes.scheme.sage: The main logic for the signature scheme. It defines key generation, Fiat-Shamir signing, and verification. It defines the structure of the public key.HKZbasis.sage: Provides lattice reduction functions (LLL/BKZ) to find short vectors in the relation basis of the class group, ensuring secret keys are manageable.out.txt: The challenge output containing thePoints_list(public keys), parameters, message signature, and the encrypted flag.
Detailed Vulnerability Analysis
The vulnerability is a combination of Torsion Point Leakage and a Linear Secret Sequence.
1. Torsion Point Leakage
In standard CSIDH, a public key is just the target curve $E$. However, scheme.sage discloses the images of the generators $Q_1$ and $Q_2$ of the base curve $E_0$ under the secret isogeny $\Phi$:
| |
This disclosure is fatal. In isogeny-based cryptography, knowing where a torsion basis $(Q_1, Q_2)$ maps allows an attacker to compute the image of any point $T$ in the torsion group $E_0[p+1]$. If $T = uQ_1 + vQ_2$, then $\Phi(T) = u\Phi(Q_1) + v\Phi(Q_2)$.
2. The Linear Sequence Leak
The secret key is a list of 16 vectors (SK) where each vector is derived linearly:
$$ SK[j] = SK[j-1] + \text{quick} \quad \text{where} \quad \text{quick} = -\text{sign}(SK[1]) $$
This means that for every prime lane $\ell_i$, the exponent $v_i$ is slowly counting down toward zero across the 16 curves provided in the public key. This allows us to “read” the secret exponents by watching when the image of a torsion point stops being zero.
Behind the Math
Frobenius Eigenspaces and Kernels
In CSIDH, the group action by a prime ideal $\mathfrak{l}_i$ corresponds to an isogeny whose kernel is a subgroup of the $\ell_i$-torsion $E_0[\ell_i]$. On a supersingular curve over $\mathbb{F}_p$, the $\ell_i$-torsion splits into two eigenspaces under the Frobenius endomorphism $\pi$:
- Positive Eigenspace ($T^+$): Points where $\pi(T) = T$.
- Negative Eigenspace ($T^-$): Points where $\pi(T) = -T$.
If the secret exponent $v_i$ is positive, the isogeny $\Phi$ “swallows” the positive eigenspace. This means $\Phi(T^+) = 0$ (the point at infinity) on the target curve. If $v_i$ is negative, it swallows $T^-$.
The Weil Pairing
To track these points, we must express $T^+$ and $T^-$ as linear combinations of the generators $Q_1, Q_2$. Since we are working with the full $(p+1)$-torsion, we use the Weil Pairing $e(\cdot, \cdot)$ to solve the discrete logarithm problem on the torsion group: $$ u \equiv \log_{e(Q_1, Q_2)}(e(T, Q_2)) \pmod{\ell_i} $$ $$ v \equiv \log_{e(Q_1, Q_2)}(e(Q_1, T)) \pmod{\ell_i} $$
Exploitation Process
Step 1: Reconstructing the Curves
We reconstruct the coefficients of the 16 target curves from the coordinates in Points_list by solving the curve equation $y^2 = x^3 + Ax + B$ for $A$ and $B$.
Step 2: Tracking Torsion Images
We compute the coefficients $(u, v)$ for the $+1$ and $-1$ torsion points for each prime. We then evaluate their images across the 16 curves to check if they map to the point at infinity:
| |
Step 3: Recovering the Exponents
Because the 16 curves count down the exponent to zero, we check how many curves in the sequence map the torsion point to Zero. If it stops being zero at curve $j+1$, the exponent was exactly $j$.
| |
By repeating this for all 74 primes, we perfectly reconstruct the secret vector $SK[1]$, derive the full $SK$ list, and hash it to obtain the AES key.
Full Solver Script
| |
Flag: shellmates{Weil_Pairing_and_Frobenius_u_get_torsion_group_Kernel_Sign}