Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pwang00
GitHub Repository: pwang00/Cryptographic-Attacks
Path: blob/master/Public Key/Diffie Hellman/pohlig_hellman_EC.sage
336 views
1
# Sage already comes built-in with a discrete logarithm function that uses a Pohlig-Hellman / BSGS backend.
2
# However, it can never hurt to implement the algorithm yourself for sake of understanding.
3
4
5
def generate_params(B=2^10, num_factors=8):
6
""" Generates the public and private parameters for Diffie-Hellman """
7
8
# Generates num_factors primes and multiplies them together to form a modulus
9
p = 1
10
11
# Apparently it's possible to generate curves of desired order using complex multiplication,
12
# but that's out of scope for this script. Instead, we'll set a bound on the private keys based on $B$.
13
14
while not is_prime(p):
15
p = 2 * prod([random_prime(B, proof=False) for i in range(num_factors)]) + 1
16
17
F = GF(p)
18
19
a4 = randint(0, num_factors * B)
20
a6 = randint(0, num_factors * B)
21
kA = randint(0, p)
22
kB = randint(0, p)
23
24
E = EllipticCurve(F, [a4, a6])
25
G = E.gens()[0]
26
27
PA = kA * G
28
PB = kB * G
29
30
assert PA * kB == PB * kA
31
return G, PA, E
32
33
# The Baby-Step Giant Step (BSGS) algorithm helps reduce the complexity of calculating the discrete logarithm
34
# g_i^x_i mod p_i = h_i to O(sqrt(p_i)) instead of O(p_i) with traditional brute force. The way BSGS works is that
35
# We rewrite the discrete logarithm x_i in terms of im + j, where m = ceil(sqrt(n)). This allows for a meet-in-the-middle
36
# style calculation of $x$--namely, we first calculate g^j mod p for every 0 <= j < m, and then calculate g^i mod p for
37
# 0 <= j <= p, multiplying by a^-m for every y not equal to
38
39
def BSGS(G, PA, n, E):
40
41
# Normally ceil(sqrt(n)) should work but for some reason some test cases break this
42
M = ceil(sqrt(n)) + 1
43
y = PA
44
log_table = {}
45
46
for j in range(M):
47
log_table[j] = (j, j * G)
48
49
inv = -M * G
50
51
for i in range(M):
52
for x in log_table:
53
if log_table[x][1] == y:
54
return i * M + log_table[x][0]
55
56
y += inv
57
58
return None
59
60
# The Pohlig-Hellman attack on Diffie-Hellman works as such:
61
# Given the generator, public keys of either Alice or Bob, as well as the multiplicative order
62
# Of the group (which in Diffie-Hellman is p - 1 due to prime modulus),
63
# one can factor the group order (which by construction here is B-smooth) into
64
# Small primes. By Lagrange's theorem, we have that
65
66
67
def pohlig_hellman_EC(G, PA, E, debug=True):
68
""" Attempts to use Pohlig-Hellman to compute discrete logarithm of A = g^a mod p"""
69
70
# This code is pretty clunky, naive, and unoptimized at the moment, but it works.
71
72
n = E.order()
73
factors = [p_i ^ e_i for (p_i, e_i) in factor(n)]
74
crt_array = []
75
76
if debug:
77
print("[x] Factored #E(F_p) into %s" % factors)
78
79
for p_i in factors:
80
g_i = G * (n // p_i)
81
h_i = PA * (n // p_i)
82
x_i = BSGS(g_i, h_i, p_i, E)
83
if debug and x_i != None:
84
print("[x] Found discrete logarithm %d for factor %d" % (x_i, p_i))
85
crt_array += [x_i]
86
87
elif x_i == None:
88
print("[] Did not find discrete logarithm for factor %d" % p_i)
89
90
91
return crt(crt_array, factors)
92
93
def test():
94
95
# Not sure why B is here, it isn't even used
96
print("Generating parameters...")
97
G, PA, E = generate_params()
98
print("Attempting Pohlig-Hellman factorization with \nG = %s\nPA = %s\nE is an %s\n"
99
% (G, PA, E))
100
kA = pohlig_hellman_EC(G, PA, E)
101
assert kA * G == PA
102
print("[x] Recovered scalar kA such that PA = G * kA through Pohlig-Hellman: %d" % kA)
103
104
if __name__ == "__main__":
105
test()
106