budget bag - LA CTF 2024
Challenge:
import random
from hidden import DollarStore
p = 95773813056613526295315889615423014145567365190638271416026411091272805051097
def legendre(a, p):
return pow(a, (p - 1) // 2, p)
def tonelli(n, p):
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
if s == 1:
return pow(n, (p + 1) // 4, p)
for z in range(2, p):
if p - 1 == legendre(z, p):
break
c = pow(z, q, p)
r = pow(n, (q + 1) // 2, p)
t = pow(n, q, p)
m = s
t2 = 0
while (t - 1) % p != 0:
t2 = (t * t) % p
for i in range(1, m):
if (t2 - 1) % p == 0:
break
t2 = (t2 * t2) % p
b = pow(c, 1 << (m - i - 1), p)
r = (r * b) % p
c = (b * b) % p
t = (t * c) % p
m = i
return r
class EllipticCurve:
def __init__ (self, a, b, p):
self.p = p
self.a = a
self.b = b
def getPoint(self, x):
ys = (x**3 + self.a*x + self.b) % p
if legendre(ys, self.p) != 1:
return None
y = tonelli( ys ,p)
return CurvePoint(x, y, self)
class CurvePoint:
def __init__(self,x,y, curve):
self.curve = curve
self.x = x % curve.p
self.y = y % curve.p
def __str__(self):
return f"({self.x},{self.y})"
def __eq__(self, other):
return str(self) == str(other)
__repr__ = __str__
def __add__(self, other):
x1,y1 = self.x, self.y
x2,y2 = other.x, other.y
if x2 == 0 and y2 == 1:
return self
if x1 == 0 and y1 == 1:
return other
if x1 == x2 and y2 == self.curve.p - y1:
return CurvePoint(0, 1, self.curve)
if x1 == x2 and y1 == y2:
lam = ((3*pow(x1, 2, self.curve.p) + self.curve.a) * pow(2* y1, -1, self.curve.p))% self.curve.p
else:
lam = ((y2 - y1) * pow(x2 - x1, -1, self.curve.p)) % self.curve.p
x = (lam**2 - x1 - x2) % self.curve.p
y = (lam*(x1 -x) - y1) % self.curve.p
return CurvePoint(x, y, self.curve)
def scalar_mult(x, n, ec) -> CurvePoint:
Q = x
R = CurvePoint(0,1 ,ec)
if n == 0: return R
if n == 1: return x
while n > 0:
if n % 2 == 1:
R = R + Q
Q = Q + Q
n = n // 2
return R
flag = "lactf{REDACTED}"
flag = flag.lstrip("lactf{").rstrip("}")
flagarray = [((random.randrange(2**10) >> 8) << 8) + ord(flag[i]) for i in range(len(flag))]
ec = DollarStore()
points = []
while len(points) < len(flagarray):
x = random.randrange(p)
point = ec.getPoint(x)
if point != None:
points.append(point)
s = scalar_mult(points[0], flagarray[0], ec)
for i in range(1,len(flagarray)):
s += (scalar_mult(points[i], flagarray[i], ec))
print(f"s= {s}")
print(f"points = {points}")
Output:
s= (4690331917760414380672348505790486524786128272326163170078478915876334878778,77902523131087061897126273610460347147805642819184490444996378236375931739511)
points = [(48868244275342945713292068450286493306842109652612873048852850861527337784625,8598765896895208028227058726713353098258128734049351946507804225327296634514), (72658254142472216221352003377742816858998248904595208415554148006123670275598,71428807550814004521976397789820845661680868197262344890701351814974388342261), (67870089007566708724312416903523370462932434169535354040601985891516954713089,8943567664814248988234978385637474582139875658136599562153182844933110782161), (33253625948635661442757699647500419774784100109285386672321733886811106048412,12192737861983844212244996021850136772489630870822316368037176061423925074233), (66860062037424526223822695383825242848539148827022939438364758097578810377221,58417846450117166884245720025471073320694519590707170823524720597081409174976), (28494672393178906436249791305135018577103930778774058381902322150406763293156,62195276890799494520787073550388771924399268413246266294642567344115775718432), (6537035443546098222408014705048663302006517570643500213251743726265246911764,63349815264954414001975570394948853390727370840181019491330859064400691177034), (9320970269062399750695489734826172234568831562993926393303183822807296243087,34696165501221265426922128243581570921860165265147807824051427532585333222152), (18573558660261827041235280489756954152038727051914639466830055088004904233178,19919769712016625050600762893366674039791157427716018448797516577097120142300), (16615614708193334022500574592689709918593068922396962286039360740901902767297,60958319794367285409532630915520850753365486357306708351189641978639088395040), (83083247653266404965046108107297707899250352411953326131401506377288013650507,88351052151438822336155560751456315613347554340471433918143267841048354530206), (13931833861234698857264824825124064884725829615801172933808997361805649306770,1863009430945401243652911885817518397081699859060155426336485589307965515267), (82335726721747347474239651051504187390571418091882782437152555488102933140061,75024532808592482514720186660630997309420973593419858494992760319543418493327), (15679700475641029146383479082391061723840711201191220489551315259170835916116,4902015468380176276259510377701509023242706106231852614815501538737950056248), (69563107209744135957874607288632923528358557330713606279779688661831937343313,47637699504526623569498379978585572196126141235987754666684744952203096777656), (12240300385069346445384926360689199410381482418493148959106963671703717348546,23386458637256108310036365005044561845597767155973474723611711176033278672500), (2686945382892345707901179849163807293774497407910463972517367748359032899306,63993175447658077329987948939524654748779134053698498595737451593308819319039)]
Solve:
First we’ll assume the given p is the curve p, then solve the curve parameters a and b.
p = 95773813056613526295315889615423014145567365190638271416026411091272805051097
points = [(48868244275342945713292068450286493306842109652612873048852850861527337784625,8598765896895208028227058726713353098258128734049351946507804225327296634514), (72658254142472216221352003377742816858998248904595208415554148006123670275598,71428807550814004521976397789820845661680868197262344890701351814974388342261), (67870089007566708724312416903523370462932434169535354040601985891516954713089,8943567664814248988234978385637474582139875658136599562153182844933110782161), (33253625948635661442757699647500419774784100109285386672321733886811106048412,12192737861983844212244996021850136772489630870822316368037176061423925074233), (66860062037424526223822695383825242848539148827022939438364758097578810377221,58417846450117166884245720025471073320694519590707170823524720597081409174976), (28494672393178906436249791305135018577103930778774058381902322150406763293156,62195276890799494520787073550388771924399268413246266294642567344115775718432), (6537035443546098222408014705048663302006517570643500213251743726265246911764,63349815264954414001975570394948853390727370840181019491330859064400691177034), (9320970269062399750695489734826172234568831562993926393303183822807296243087,34696165501221265426922128243581570921860165265147807824051427532585333222152), (18573558660261827041235280489756954152038727051914639466830055088004904233178,19919769712016625050600762893366674039791157427716018448797516577097120142300), (16615614708193334022500574592689709918593068922396962286039360740901902767297,60958319794367285409532630915520850753365486357306708351189641978639088395040), (83083247653266404965046108107297707899250352411953326131401506377288013650507,88351052151438822336155560751456315613347554340471433918143267841048354530206), (13931833861234698857264824825124064884725829615801172933808997361805649306770,1863009430945401243652911885817518397081699859060155426336485589307965515267), (82335726721747347474239651051504187390571418091882782437152555488102933140061,75024532808592482514720186660630997309420973593419858494992760319543418493327), (15679700475641029146383479082391061723840711201191220489551315259170835916116,4902015468380176276259510377701509023242706106231852614815501538737950056248), (69563107209744135957874607288632923528358557330713606279779688661831937343313,47637699504526623569498379978585572196126141235987754666684744952203096777656), (12240300385069346445384926360689199410381482418493148959106963671703717348546,23386458637256108310036365005044561845597767155973474723611711176033278672500), (2686945382892345707901179849163807293774497407910463972517367748359032899306,63993175447658077329987948939524654748779134053698498595737451593308819319039)]
p1x, p1y = points[0][0], points[0][1]
p2x, p2y = points[1][0], points[1][1]
M = Matrix(GF(p), [[p1x,1],[p2x,1]])
a, b = M.solve_right(vector([p1y^2-p1x^3,p2y^2-p2x^3]))
print(a, b)
Interesting, a=0 and b=0. So the curve equation is y^2 = x^3, and we have an elliptic cusp.
It was kinda similar to this chall but we need a way to take discrete logs with the cusp.
The second answer here is helpful: https://crypto.stackexchange.com/questions/61302/how-to-solve-this-ecdlp
So we pick a random g, take the discrete log of everything, transforming it to a regular knapsack problem.
load('https://gist.githubusercontent.com/Connor-McCartney/952583ecac836f843f50b785c7cb283d/raw/5718ebd8c9b4f9a549746094877a97e7796752eb/solvelinmod.py')
p = 95773813056613526295315889615423014145567365190638271416026411091272805051097
s = (4690331917760414380672348505790486524786128272326163170078478915876334878778,77902523131087061897126273610460347147805642819184490444996378236375931739511)
points = [(48868244275342945713292068450286493306842109652612873048852850861527337784625,8598765896895208028227058726713353098258128734049351946507804225327296634514), (72658254142472216221352003377742816858998248904595208415554148006123670275598,71428807550814004521976397789820845661680868197262344890701351814974388342261), (67870089007566708724312416903523370462932434169535354040601985891516954713089,8943567664814248988234978385637474582139875658136599562153182844933110782161), (33253625948635661442757699647500419774784100109285386672321733886811106048412,12192737861983844212244996021850136772489630870822316368037176061423925074233), (66860062037424526223822695383825242848539148827022939438364758097578810377221,58417846450117166884245720025471073320694519590707170823524720597081409174976), (28494672393178906436249791305135018577103930778774058381902322150406763293156,62195276890799494520787073550388771924399268413246266294642567344115775718432), (6537035443546098222408014705048663302006517570643500213251743726265246911764,63349815264954414001975570394948853390727370840181019491330859064400691177034), (9320970269062399750695489734826172234568831562993926393303183822807296243087,34696165501221265426922128243581570921860165265147807824051427532585333222152), (18573558660261827041235280489756954152038727051914639466830055088004904233178,19919769712016625050600762893366674039791157427716018448797516577097120142300), (16615614708193334022500574592689709918593068922396962286039360740901902767297,60958319794367285409532630915520850753365486357306708351189641978639088395040), (83083247653266404965046108107297707899250352411953326131401506377288013650507,88351052151438822336155560751456315613347554340471433918143267841048354530206), (13931833861234698857264824825124064884725829615801172933808997361805649306770,1863009430945401243652911885817518397081699859060155426336485589307965515267), (82335726721747347474239651051504187390571418091882782437152555488102933140061,75024532808592482514720186660630997309420973593419858494992760319543418493327), (15679700475641029146383479082391061723840711201191220489551315259170835916116,4902015468380176276259510377701509023242706106231852614815501538737950056248), (69563107209744135957874607288632923528358557330713606279779688661831937343313,47637699504526623569498379978585572196126141235987754666684744952203096777656), (12240300385069346445384926360689199410381482418493148959106963671703717348546,23386458637256108310036365005044561845597767155973474723611711176033278672500), (2686945382892345707901179849163807293774497407910463972517367748359032899306,63993175447658077329987948939524654748779134053698498595737451593308819319039)]
def cusp_log(P, G, p):
gx, gy = G
px, py = P
d = ( pow(gx * pow(gy, -1, p), -1, p) * (px * pow(py, -1, p)) ) % p
return d
G = points[0]
logs = [cusp_log(P, G, p) for P in points]
xs = [var(f"x_{i}") for i in range(len(points))]
eq = (sum(x*i for x, i in zip(xs, logs)) == cusp_log(s, G, p))
bounds = {x: 1024 for x in xs}
sol = solve_linear_mod([(eq, p)], bounds)
print(bytes([i%256 for i in sol.values()]))
# lactf{im_w4y_t00_br0k3!}