2022-10-31
Consider the group of points induced by the curve
\[E: y^2 = x^3 + ax + b \pmod{p}\]
With generator \(G\) and \(P = nG\). The fastest known algorithms solve ECDLP over \(E(\mathbb{F}_p)\) in \(O(\sqrt{p})\).
This is a list of speedup scenarios.
E is singular (has a double/triple root). There exists an efficient map from the set of non-singular points in E to the multiplicative base group of E.
= 0x9c07797788e4959f91bcae7ecfdc65ead5311ac6fad817534681c8
a = 0x1270895856df5bce5e86d023160ac61c13925b588549e150ba4cf38accd806
b = 0x9a888cfd499443d7637469ef4e20d6bbb2dcd1ddf928778cdea3501cf8dea17
p
= (0x4f2b1892ba1acd7ffea403db4a460bde0da7e49e824cd6fb9914e36afcbb2, 0x7fe796eacf863f5c6978fa9cb04c85e3a0dbbd1bbf0b105b28f7cab52b03e8)
G = (0x5b5e2d0616a293aa121c66a982a36ed163fad1608ce5cc9601d6331d7c5a431, 0x55a9fd40e74741d7b8867c17fbb8db2cbceb8a5b1f48230aaf9234f682a7424)
P
<x>=GF(p)[]
K.= x^3 + a*x + b
f assert(f.discriminant() == 0)
# change of variables
= f.roots()[1][0]
double_root = f.subs(x=x + double_root)
t = GF(p)(t(1) - 1).square_root()
c
= (G[0] - double_root, G[1])
G = (P[0] - double_root, P[1])
P
# compute mapping
= (G[1] + c*G[0]) / (G[1] - c*G[0]) % p
u = (P[1] + c*P[0]) / (P[1] - c*P[0]) % p
v
# DLOG over multiplicative group
= discrete_log(v, u) n
E is of smooth order. The usual Pohlig-Hellman attack works.
= GF(0xe9c1eb62c2e8d8777cd419d03008e72f)
K = EllipticCurve(K, [2, 3])
E
# smooth
print(E.order().factor())
= E.gens()[0]
G = K.random_element()*G
P
= discrete_log(P, G, G.order(), operation="+")
n assert(n*G == P)
The log is constrained to some interval \((a, b)\). Pollard’s lambda algorithm runs in \(O(\sqrt{b - a})\).
from random import getrandbits
= EllipticCurve(GF(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F), [0, 7])
E = E.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
G = getrandbits(32)*G
P
= discrete_log_lambda(P, G, (0, 1<<32), operation="+")
n assert(n*G == P)
E has a small embedding degree \(k < \log^2 p\). The MOV attack uses a bilinear map to transfer the log to the extended field \(\mathbb{F}_{p^k}\).
= 0x10cd3de04a2aa57345a2b75aa5
p = GF(p)
K = EllipticCurve(K, [-35, 98])
E
= E.gens()[0]
G = E.lift_x(0x84651664b83b8ef185c5a2e08)
P
# compute embedding degree
= 1
k = G.order()
o while (p**k - 1) % o != 0:
+= 1
k
= P.order()
N assert(gcd(N, p) == 1)
# extend field
= E.change_ring(GF(p^k))
E = E.random_element()
T = T.order()
M = gcd(M, N)
d = (M//d)*T
T
# bilinear mapping to the extended field
= E(G).weil_pairing(T, M)
u = E(P).weil_pairing(T, M)
v
# DLOG over multiplicative group
= v.log(u)
n assert(n*G == P)
#\(E(\mathbb{F}_p) = p\). Smart’s attack reduces to the p-adic log over \(E(\mathbb{Q}_p)\).
= 0xa15c4fb663a578d8b2496d3151a946119ee42695e18e13e90600192b1d0abdbb6f787f90c8d102ff88e284dd4526f5f6b6c980bf88f1d0490714b67e8a2a2b77
p = 0x5e009506fcc7eff573bc960d88638fe25e76a9b6c7caeea072a27dcd1fa46abb15b7b6210cf90caba982893ee2779669bac06e267013486b22ff3e24abae2d42
a = 0x2ce7d1ca4493b0977f088f6d30d9241f8048fdea112cc385b793bce953998caae680864a7d3aa437ea3ffd1441ca3fb352b0b710bb3f053e980e503be9a7fece
b
= GF(p)
K = EllipticCurve(K, [a, b])
E = E.gens()[0]
G = K.random_element()*G
P
# anomalous curve
assert(E.order() == p)
# p-adic lift
= EllipticCurve(Qp(p), [ZZ(t) + randint(0,p)*p for t in E.a_invariants()])
E_
for G_ in E_.lift_x(ZZ(G.xy()[0]), all=True):
if K(G_.xy()[1]) == G.xy()[1]:
break
= p*G_
pG_
for P_ in E_.lift_x(ZZ(P.xy()[0]), all=True):
if K(P_.xy()[1]) == P.xy()[1]:
break
= p*P_
pP_
# p-adic elliptic log
= -(pG_.xy()[0] / pG_.xy()[1])
psi_G = -(pP_.xy()[0] / pP_.xy()[1])
psi_P = ZZ(psi_P/psi_G)
n assert(n*G == P)