Elliptic Curve Cryptography Attack Methods and Practical Examples
Elliptic Curve Cryptography (ECC) presents several attack scenarios depending on curve parameters. This analysis covers discrete logarithm computation, Smart's attack, and the Pohlig-Hellman algorithm with practical implementations.
Discrete Logarithm Function
The discrete_log() function provides a general-purpose solution for elliptic curve discrete logarithm problems:
discrete_log(target_point, base_point, order=None, operation='+')
This function computes the logarithm of target_point relative to base_point. The order parameter specifies the base point's order, while operation defaults to elliptic curve point addition.
Smart's Attack Implementation
Smart's attack applies when the curve's order equals the field characteristic (anomalous curves).
def smart_attack(P, Q, prime):
curve = P.curve()
# Lift curve to p-adic numbers
E_padic = EllipticCurve(Qp(prime, 2),
[ZZ(coeff) + randint(0,prime)*prime
for coeff in curve.a_invariants()])
# Lift points to p-adic field
P_padic_points = E_padic.lift_x(ZZ(P.xy()[0]), all=True)
for P_padic in P_padic_points:
if GF(prime)(P_padic.xy()[1]) == P.xy()[1]:
break
Q_padic_points = E_padic.lift_x(ZZ(Q.xy()[0]), all=True)
for Q_padic in Q_padic_points:
if GF(prime)(Q_padic.xy()[1]) == Q.xy()[1]:
break
# Compute p-multiples
pP = prime * P_padic
pQ = prime * Q_padic
xP, yP = pP.xy()
xQ, yQ = pQ.xy()
# Calculate p-adic logarithms
phi_P = -(xP / yP)
phi_Q = -(xQ / yQ)
return ZZ(phi_Q / phi_P)
secret = smart_attack(P, Q, p)
Pohlig-Hellman Algorithm
The Pohlig-Hellman method works when the curve order factors into small primes.
def pohlig_hellman(curve_order, generator, target):
factors = list(factor(curve_order))
prime_powers = [factors[i][0] ** factors[i][1]
for i in range(len(factors))][:-1]
discrete_logs = []
for prime in prime_powers:
multiplier = curve_order // prime
log_val = discrete_log(multiplier * target,
multiplier * generator,
operation="+")
discrete_logs.append(log_val)
return crt(discrete_logs, prime_powers)
solution = pohlig_hellman(n, G, H)
ECC Challenge Analysis
A three-part challenge demonstrates these techniques:
Part 1: Direct Discrete Logarithm
p = 146808027458411567
curve = EllipticCurve(GF(p), [46056180, 2316783294673])
P = curve(119851377153561800, 50725039619018388)
Q = curve(22306318711744209, 111808951703508717)
result = discrete_log(Q, P, operation='+')
Part 2: Pohlig-Hellman Application
p = 1256438680873352167711863680253958927079458741172412327087203
curve = EllipticCurve(GF(p), [377999945830334462584412960368612,
604811648267717218711247799143415167229480])
G = curve(550637390822762334900354060650869238926454800955557622817950,
700751312208881169841494663466728684704743091638451132521079)
H = curve(1152079922659509908913443110457333432642379532625238229329830,
819973744403969324837069647827669815566569448190043645544592)
n = curve.order()
answer = pohlig_hellman(n, G, H)
Part 3: Smart's Attack Deployment
p = 0xd3ceec4c84af8fa5f3e9af91e00cabacaaaecec3da619400e29a25abececfdc9bd678e2708a58acb1bd15370acc39c596807dab6229dca11fd3a217510258d1b
curve = EllipticCurve(GF(p), [0x95fc77eb3119991a0022168c83eee7178e6c3eeaf75e0fdf1853b8ef4cb97a9058c271ee193b8b27938a07052f918c35eccb027b0b168b4e2566b247b91dc07,
0x926b0e42376d112ca971569a8d3b3eda12172dfb4929aea13da7f10fb81f3b96bf1e28b4a396a1fcf38d80b463582e45d06a548e0dc0d567fc668bd119c346b2])
G = curve(10121571443191913072732572831490534620810835306892634555532657696255506898960536955568544782337611042739846570602400973952350443413585203452769205144937861,
8425218582467077730409837945083571362745388328043930511865174847436798990397124804357982565055918658197831123970115905304092351218676660067914209199149610)
H = curve(964864009142237137341389653756165935542611153576641370639729304570649749004810980672415306977194223081235401355646820597987366171212332294914445469010927,
5162185780511783278449342529269970453734248460302908455520831950343371147566682530583160574217543701164101226640565768860451999819324219344705421407572537)
key = smart_attack(G, H, p)
AES-ECC Hybrid Encryption
Some challenges combine ECC with symmetric encryption:
# Recover ECC secret
p = 12506217790875063466368723611056175369923
curve = EllipticCurve(GF(p), [12506217790875063466368723611052784275139,
12506217790875063466368723533070038257347])
G = curve(7493372729181057645036574086903590138065, 359098907392057890604329721532958479621)
H = curve(9505420031620208163682758801913524369821, 5460936589331844194485299189975059431657)
secret = smart_attack(G, H, p)
# Decrypt AES payload
from Crypto.Cipher import AES
import hashlib
key = hashlib.sha256(str(secret).encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext)
Invalid Point Attacks
When points don't satisfy the curve equation but standard formulas are used:
def point_addition(P1, P2, modulus, a_coeff):
if P1 is None: return P2
if P2 is None: return P1
x1, y1 = P1
x2, y2 = P2
if x1 == x2 and (y1 + y2) % modulus == 0:
return None
if x1 == x2:
lam = (3 * x1 * x1 + a_coeff) * pow(2 * y1, -1, modulus) % modulus
else:
lam = (y2 - y1) * pow(x2 - x1, -1, modulus) % modulus
x3 = (lam * lam - x1 - x2) % modulus
y3 = (lam * (x1 - x3) - y1) % modulus
return (x3, y3)
def baby_step_giant_step(generator, target, modulus, curve_a, max_steps=1<<16):
baby_steps = {}
current = None
for j in range(max_steps):
baby_steps[current] = j
current = point_addition(current, generator, modulus, curve_a)
step_size = max_steps
giant_step = current
neg_giant = (giant_step[0], (-giant_step[1]) % modulus)
current_point = target
for i in range(max_steps + 1):
if current_point in baby_steps:
return i * max_steps + baby_steps[current_point]
current_point = point_addition(current_point, neg_giant, modulus, curve_a)
return None
XOR-Based ECC Encryption
Some challenges use ECC secrets for stream cipher-like encryption:
# Recover XOR key from known plaintext
ciphertext_hex = 'ac73a774a25bd512d543dc468542c9428141800dd041d043c918d112850dd515d6128214d1138211d71599'
encrypted = bytes.fromhex(ciphertext_hex)
# Assume known plaintext header
xor_key = [encrypted[0] ^ ord('H'), encrypted[1] ^ ord('S')]
plaintext = ''
for i in range(len(encrypted)):
plaintext += chr(encrypted[i] ^ xor_key[i % 2])