Kernel searcher
6 minutos de lectura
I have a super secret isogeny. Wanna know where your point ends up? Just send me your favourite coordinates.
Challenge contributed by CryptoHack
Challenge files:
Se nos proporciona un script en SageMath que oculta la flag bajo una isogenia y nos permite evaluar la isogenia en cualquier punto que deseemos.
Análisis del código fuente
La parte relevante del script es esta:
import json
from Crypto.Util.number import bytes_to_long
proof.all(False)
A, B = 2**216, 3**137
p = A * B - 1
F.<i> = GF(p^2, modulus=[1,0,1])
E = EllipticCurve(F, [0, 6, 0, 1, 0])
# Torsion Basis for E[A]
P, Q = canonical_torsion_basis(E, A)
import os
flag = os.environ.get("FLAG", "ECSC{fake_flag_for_testing}").strip().encode()
ker = P + bytes_to_long(flag) * Q
challenge_isogeny = E.isogeny(
ker, algorithm="factored", model="montgomery"
)
def accept_point(data):
data = json.loads(data)
try:
x0 = int(data["x0"], 16)
x1 = int(data["x1"], 16)
x = F([x0, x1])
except Exception as e:
print(e)
return {"error": "Invalid Fp2 value"}
if not E.is_x_coord(x):
return {"error": "Invalid Point"}
P = E.lift_x(x)
imP = challenge_isogeny(P)
return json.dumps({"x0": hex(imP[0][0]), "x1": hex(imP[0][1])})
def main():
print(f"Welcome to my isogeny factory!")
print(f"My isogeny is so secret, I'll let you evaluate any point you like!")
while True:
data = input("Send the point you wish to evaluate: ")
output = accept_point(data)
print(output)
if __name__ == "__main__":
main()
Básicamente, el servidor usa una curva elíptica dada
La curva elíptica se define como:
Nótese que de
Luego, el servidor encuentra dos puntos
Isogenia
Una isogenia es un homomorfismo entre dos curvas elípticas (supersingulares). Y el kernel de un homomorfismo es el conjunto de elementos que se mapean al elemento identidad.
Si denotamos la isogenia por
Además, como
Solución
Podemos obtener
Encontrando los parámetros de la curva
Después de un poco de investigación y unas pocas pruebas, podemos ver que el codominio es una curva elíptica con la siguiente estructura:
Para un cierto
Podemos obtener ese valor de
Aquí, probé algunos puntos interesantes. Por ejemplo, envié el punto
io = get_process()
io.sendlineafter(b'Send the point you wish to evaluate: ',
json.dumps({'x0': '0', 'x1': '0'}).encode())
data = json.loads(io.recvline().decode())
x0, x1 = int(data.get('x0'), 16), int(data.get('x1'), 16)
X = F([x0, x1])
C = (- X ** 3 - X) / (X ** 2)
EE = EllipticCurve(F, [0, C, 0, 1, 0])
Logaritmo discreto
Y ahora estamos listos para resolver el logaritmo discreto con las imágenes de los puntos
io.sendlineafter(b'Send the point you wish to evaluate: ',
json.dumps({'x0': hex(P[0][0]), 'x1': hex(P[0][1])}).encode())
data = json.loads(io.recvline().decode())
x0, x1 = int(data.get('x0'), 16), int(data.get('x1'), 16)
imP = EE.lift_x(F([x0, x1]))
io.sendlineafter(b'Send the point you wish to evaluate: ',
json.dumps({'x0': hex(Q[0][0]), 'x1': hex(Q[0][1])}).encode())
data = json.loads(io.recvline().decode())
x0, x1 = int(data.get('x0'), 16), int(data.get('x1'), 16)
imQ = EE.lift_x(F([x0, x1]))
io.close()
Y esto es fácil de resolver porque el orden de
Definimos
Dado que el orden de
Si multiplicamos por el orden de
Ahora, si
Después de eso, podemos ver que
Ahora multiplicamos a ambos lados por
De nuevo, si
Luego,
Y multiplicaríamos por
def dlog(G, nG):
i = -1
bits = []
order = A
while order:
i += 1
order //= 2
if order * (nG - (sum(bits)) * G) != 0 * G:
bits.append(2 ** i)
return sum(bits)
d = dlog(imQ, -imP)
io.success(bytes.fromhex(hex(int(d))[2:]).decode())
Flag
Y finalmente, encontramos la flag:
$ python3 solve.py
[+] Starting local process '/usr/bin/sage': pid 7514
[*] Stopped process '/usr/bin/sage' (pid 7514)
[+] ECSC{learning_to_isogeny}
El script completo se puede encontrar aquí: solve.py
.