owned this note
owned this note
Published
Linked with GitHub
# Mapna CTF 2024 - Isogenies
Writeup by. **EggRoll**
## Challenge Overview
First of all, the flag is encrypted by AES with its hash as encryption key.
```python
h = sha256(flag).digest()
A = bytes_to_long(h)
iv = token_bytes(16)
key = sha256(h).digest()[:16]
enc = AES.new(key, AES.MODE_CBC, iv).encrypt(pad(flag, 16))
```
So our goal is to retrieve $A$ and decrypt the ciphertext. This variable is used as a coefficient of an elliptic curve $E$ whose order is a multiple of $3$, and then a $3$-torsion point $P$ is generated.
```python
p = getPrime(512)
E = EllipticCurve(GF(p), [0, A, 0, 1, 0])
if E.order() % 3 != 0:
continue
for _ in range(50):
P = E.random_point() * (E.order() // 3)
if P != 0:
break
else:
continue
```
Next, an isogeny from $E$ to $E_a$ is computed according to
```python
def to_montgomery(E):
R = E.base_ring()
j = E.j_invariant()
x = polygen(R)
eq = 256 * (x - 3)**3 - (x - 4) * j
for A2 in eq.roots(multiplicities=False):
for A in A2.nth_root(2, all=True):
Ea = EllipticCurve(R, [0, A, 0, 1, 0])
if Ea.is_isomorphic(E):
return Ea
Ea = to_montgomery(E.isogeny_codomain(P))
if Ea is None:
continue
```
Finally, the higher bits of montgomery coefficients of $E, E_a$ and $p, P[0]$ are provided.
## Problem Analysis
Because two elliptic curves are isogenies, the j-invariants form a root of modular polynomial. In other words, the following relationship holds. $$\phi_3\left(\frac{256(A^2-3)^3}{(A^2-4)}, \frac{256(B^2-3)^3}{(B^2-4)}\right) = 0$$ The explicit form of such polynomials are listed [here](https://math.mit.edu/~drew/ClassicalModPolys.html). In our case, $$\phi_3(x, y) = x^4-x^3y^3+2232(x^3y^2+x^2y^3)-1069956(x^3y+xy^3)+36864000(x^3+y^3)+2587918086x^2y^2+8900222976000(x^2y+xy^2)+452984832000000(x^2+y^2)-770845966336000000xy+1855425871872000000000(x+y)$$
On the other hand, since $P$ is a $3$-torsion point, the x-coordinate $P[0]$ must be a root of the third [division polynomial](https://en.wikipedia.org/wiki/Division_polynomials), say $$\psi_3(x) = 3x^4+6ax^2+12bx-a^2$$ if we write the equation of the elliptic curve in the form $y^2 = x^3+ax+b$.
However, the equation of $E$ we have is $y^2=x^3+Ax^2+x$. We need to transform it into the standard form, and this could be done by shifting. Precisely, consider $(x, y) \to (x-\frac{A}{3}, y)$, $$y^2 = \left(x-\frac{A}{3}\right)^3+A\left(x-\frac{A}{3}\right)^2+\left(x-\frac{A}{3}\right)=x^3+\left(1-\frac{A^2}{3}\right)x+\frac{2A^3-9A}{27}$$
Plug $(a, b) = \left(1-\frac{A^2}{3}, \frac{2A^3-9A}{27}\right)$ into $\psi_3$, we get another equation about $A$, and these two are all we have.
As $A, B$ are masked, let's say $A = A'+x, B = B'+y$ where $A', B'$ are known and $x, y$ are unknowns. Notice that the number of equations and unknowns are exactly the same, we should be able to find them in an **algebraic** way, i.e. [resultant](https://en.wikipedia.org/wiki/Resultant).
## Implementation
The polynomials are constructed as we analyzed.
```python
# 3rd modular poly
# each entry contains degrees of x, y and the coefficient
phi3 = [
[1, 0, 1855425871872000000000],
[1, 1, -770845966336000000],
[2, 0, 452984832000000],
[2, 1, 8900222976000],
[2, 2, 2587918086],
[3, 0, 36864000],
[3, 1, -1069956],
[3, 2, 2232],
[3, 3, -1],
[4, 0, 1]
]
PR.<x, y> = PolynomialRing(GF(p), 2)
# The numberators and denominators of j-invariants of E, Ea
jA_n = 256 * ((A + x)^2 - 3)^3
jA_d = ((A + x)^2 - 4)
jB_n = 256 * ((B + y)^2 - 3)^3
jB_d = ((B + y)^2 - 4)
# Compute the numerator of modular poly
f = 0
x_deg_max, y_deg_max = 4, 4
for _ in phi3:
x_deg, y_deg, coef = _
f += coef * jA_n^x_deg * jA_d^(x_deg_max - x_deg) * jB_n^y_deg * jB_d^(y_deg_max - y_deg)
if x_deg != y_deg:
f += coef * jA_n^y_deg * jA_d^(y_deg_max - y_deg) * jB_n^x_deg * jB_d^(x_deg_max - x_deg)
# Division poly
g = 3 * (hint + (A + x) / 3)^4 + 6 * (1 - (A + x)^2 / 3) * (hint + (A + x) / 3)^2 + 12 * (2 * (A + x)^3 / 27 - (A + x) / 3) * (hint + (A + x) / 3) - (1 - (A + x)^2 / 3)^2
```
However, we are not able to invoke resultant like
```
f.resultant(g, y)
```
This will result in Signature Error because the modulus is too big. Instead, we follow the definition explained in wiki:
```
h = f.sylvester_matrix(g, y).determinant()
h = h.polynomial(x)
```
It remains to compute the roots of $h$ and see whether it's in fact the unknown. Put all the pieces together, the flag is returned immediately!
:::success
MAPNA{S1mpl3_p0lYnOm!4L_r3lA7i0n_0F_l0w_degr3E!}
:::
## Reference
1. Modular Polynomials. https://math.mit.edu/~drew/ClassicalModPolys.html
2. Division Polynomial. https://en.wikipedia.org/wiki/Division_polynomials
3. Resultant. https://en.wikipedia.org/wiki/Resultant