Try   HackMD

pure division - zer0pts ctf 2021

challenge

It’s just a division, so it is "pure". We are given the following code and outputs.
Anyway, we have to solve the ECDLP. In general, ECDLP is hard. But in this case, the curve is over quotient ring

Fp=Z/p3Z, so we can get the flag. (note: This "
Fp
" is not
Fp
. It indicates the name of variable in the code.)

# task.sage
from Crypto.Util.number import *
from flag import flag

assert len(flag) == 40

p = 74894047922780452080480621188147614680859459381887703650502711169525598419741
a1 = 22457563127094032648529052905270083323161530718333104214029365341184039143821
a2 = 82792468191695528560800352263039950790995753333968972067250646020461455719312
Fp = Zmod(p^3)
E = EllipticCurve(Fp, [a1, a2])

m = bytes_to_long(flag)
S = E(201395103510950985196528886887600944697931024970644444173327129750000389064102542826357168547230875812115987973230106228243893553395960867041978131850021580112077013996963515239128729448812815223970675917812499157323530103467271226, 217465854493032911836659600850860977113580889059985393999460199722148747745817726547235063418161407320876958474804964632767671151534736727858801825385939645586103320316229199221863893919847277366752070948157424716070737997662741835)
T = m * S

with open("output.txt", "w") as f:
    f.write("T: {}".format(T))
# output.txt
T: (49376632602749543055345783411902198690599351794957124343389298933965298693663616388441379424236401744560279599744281594405742089477317921152802669021421009909184865835968088427615238677007575776072993333868804852765473010336459028 : 342987792080103175522504176026047089398654876852013925736156942540831035248585067987750805770826115548602899760190394686399806864247192767745458016875262322097116857255158318478943025083293316585095725393024663165264177646858125759 : 1)

solution

Z/pnZ can be seen as a set of numbers with
n
digits of precision in
Qp
. Since the flag is 320 bits and
p3
is large enough, it is no problem to solve the ECDLP over
Qp
. The main part of the solution was meant to be here. In this section, one of the solution methods which is similar to SSSA Attack is introduced.
Let
E
be an elliptic curve over
Qp
and let
Ef
be the reduction curve of
E
over
Fp
. For simplicity, we assume
Ef
is not singular.
Let
π:P2(Qp)P2(Fp)
be a reduction map and we equate
(x,y,1)P2(Q)
with
(x,y)A2
. Then let
E
be a formal group associated
E
, the isomorphism is constructed as follows
kerπϕE(pZp)logEpZp

where
ϕ:kerπ(x,y,z)x/yE(pZp)

Also, let
N
be
N=Ef(Fp)
. Obviously,
NPkerπ
is holded for
PE(Qp)
.
Thus we can construct a isomorphism as follows
E(Qp)kerπpZp

Since
pZpFp+
, the ECDLP is solvable.

implementation

In implementation, p-adic expansion is useful. Using the p-adic valuation

vp, we consider subsets
En(Qp)={PE(Qp)vp(Px)2n}{O}
of
E(Qp)
. Then
En(Qp)E(pnZp)
.
By using identity map, the following holds.
E(pnZp)/E(pn+1Zp)pnZp/pn+1ZpFp+

By this result, we can sequentially find each coefficient

ai of the p-adic expansion
aipi
of require value.
Anyway, look at this solver.

from Crypto.Util.number import * p = 74894047922780452080480621188147614680859459381887703650502711169525598419741 a1 = 22457563127094032648529052905270083323161530718333104214029365341184039143821 a2 = 82792468191695528560800352263039950790995753333968972067250646020461455719312 Fp = Zmod(p^3) E = EllipticCurve(Fp, [a1, a2]) S = E(201395103510950985196528886887600944697931024970644444173327129750000389064102542826357168547230875812115987973230106228243893553395960867041978131850021580112077013996963515239128729448812815223970675917812499157323530103467271226, 217465854493032911836659600850860977113580889059985393999460199722148747745817726547235063418161407320876958474804964632767671151534736727858801825385939645586103320316229199221863893919847277366752070948157424716070737997662741835) T = E(49376632602749543055345783411902198690599351794957124343389298933965298693663616388441379424236401744560279599744281594405742089477317921152802669021421009909184865835968088427615238677007575776072993333868804852765473010336459028, 342987792080103175522504176026047089398654876852013925736156942540831035248585067987750805770826115548602899760190394686399806864247192767745458016875262322097116857255158318478943025083293316585095725393024663165264177646858125759) # --------------- solve ----------------- prec = 3 Qp = pAdicField(p, prec) E = EllipticCurve(Qp, [a1, a2]) Fp = GF(p) Ef = EllipticCurve(Fp, [a1, a2]) N = Ef.order() S = E(S[0], S[1]) T = E(T[0], T[1]) NS = N * S a = Fp(-NS[0] / (p * NS[1])) n = 0 l = 1 Sp = S Tp = T ds = [] while Tp != 0: NTp = N*Tp w = -NTp[0] / NTp[1] b = w / p^l d = Fp(Integer(b)/a) ds.append(Integer(d)) Tp = Tp - Integer(d)*Sp Sp = p*Sp n += 1 l += 1 if n > prec: break solve = 0 for i in range(len(ds)): solve += ds[i] * p^i print(long_to_bytes(solve))

Here is the flag zer0pts{elliptic_curve_over_p-adic!yey!}