La Seine - 404ctf 2024
Challenge:
from Crypto.Util.number import *
import os
from secret import flag
class LaSeine:
def __init__(self, size):
self.l = 20
self.p = getStrongPrime(size)
self.a = getRandomNBitInteger(size // 16)
self.b = getRandomNBitInteger(size // 16)
def split(self, f):
if len(f) & 1:
f += b"\x00"
h = len(f) // 2
return (bytes_to_long(f[:h]), bytes_to_long(f[h:]))
def sign(self, m, b):
xn, yn = self.split(m)
k = (getRandomNBitInteger(self.l - 1) << 1) + b
for _ in range(k):
xnp1 = (self.a*xn + self.b*yn) % self.p
ynp1 = (self.b*xn - self.a*yn) % self.p
xn, yn = xnp1, ynp1
return (k, xn, yn)
seine = LaSeine(1024)
_, xf, yf = seine.sign(flag, 1)
s2 = seine.sign(b"L'eau est vraiment froide par ici (et pas tres propre)", 0)
f = open("out.txt", "w")
f.write(str(seine.p) + "\n")
f.write(str((xf, yf)) + "\n")
f.write(str(s2))
f.close()
Output:
179358513830906148619403250482250880334528756349120678091666297907253922185623290723862265402777434007178297319701286775733620488613530869850160450412929764046707392082705800333548316425165863556480623955587411083384086805686199851628022437853672200835000268893800610064747558825805271528526924659142504913631
(151683403543233106224623577311980037274441590153911847119566701699367001537936290730922138442040542620222943385810242081949211326676472369180020899628646165132503185978510932501521730827126356422842852151275382840062708701174847422687809816503983740455064453231285796998931373590630224653066573035583863902921, 76688287388975729010764722746414768266232185597001389966088556498895611351239273625106383329192109917896575986761053032041287081527278426860237114874927478625771306887851752909713110684616229318569024945188998933167888234990912716799093707141023542980852524005127986940863843004517549295449194995101172400759)
(929382, 118454610237220659897316062413105144789761952332893713333891996727204456010112572423850661749643268291339194773488138402728325770671625196790011560475297285424138262812704729573910897903628228179414627406601128765472041473647769084599481166191241495167773352105622894240398746332477947478817552973851804951566, 65891615565820497528921288257089595342791556688007325193257144738940922602117787746412089423500836495505254334866586155889060897532850381510520943387446058037766901712521471259853536310481267471645770625452422081411718151580380288380630522313377397166067417623947500542258985636659962524606869196898543973764)
Solve:
from sympy.abc import x, y, t
from sympy.solvers.diophantine.diophantine import diop_quadratic
from Crypto.Util.number import *
from tqdm import *
p = 179358513830906148619403250482250880334528756349120678091666297907253922185623290723862265402777434007178297319701286775733620488613530869850160450412929764046707392082705800333548316425165863556480623955587411083384086805686199851628022437853672200835000268893800610064747558825805271528526924659142504913631
def split(f):
if len(f) & 1:
f += b"\x00"
h = len(f) // 2
return (bytes_to_long(f[:h]), bytes_to_long(f[h:]))
xn_, yn_ = split(b"L'eau est vraiment froide par ici (et pas tres propre)")
k, xn, yn = (929382, 118454610237220659897316062413105144789761952332893713333891996727204456010112572423850661749643268291339194773488138402728325770671625196790011560475297285424138262812704729573910897903628228179414627406601128765472041473647769084599481166191241495167773352105622894240398746332477947478817552973851804951566, 65891615565820497528921288257089595342791556688007325193257144738940922602117787746412089423500836495505254334866586155889060897532850381510520943387446058037766901712521471259853536310481267471645770625452422081411718151580380288380630522313377397166067417623947500542258985636659962524606869196898543973764)
w = vector(GF(p), [xn, yn]) / vector(GF(p), [xn_, yn_])
v = int(min(GF(p)(w).nth_root(k//2, all=True))) # a^2 + b^2
xn, yn = (151683403543233106224623577311980037274441590153911847119566701699367001537936290730922138442040542620222943385810242081949211326676472369180020899628646165132503185978510932501521730827126356422842852151275382840062708701174847422687809816503983740455064453231285796998931373590630224653066573035583863902921, 76688287388975729010764722746414768266232185597001389966088556498895611351239273625106383329192109917896575986761053032041287081527278426860237114874927478625771306887851752909713110684616229318569024945188998933167888234990912716799093707141023542980852524005127986940863843004517549295449194995101172400759)
X = vector([xn, yn])
for a, b in tqdm(diop_quadratic(x**2 + y**2 - v, t)):
a, b = int(a), int(b)
if a<0 or b<0 or a.bit_length() != 64 or b.bit_length() != 64:
continue
M = matrix(GF(p), [
[a, b],
[b, -a]
])
#for k in trange(2**20):
for k in range(691699, 691699+1): #cheat
flag1, flag2 = (M**k).inverse() * X
try:
print(long_to_bytes(int(flag1)).decode() + long_to_bytes(int(flag2)).decode())
print(k)
except:
pass