Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pwang00
GitHub Repository: pwang00/Cryptographic-Attacks
Path: blob/master/Public Key/Diffie Hellman/pohlig_hellman.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=15):
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
# Currently the best way I can think of to generate primes p such that p - 1 is B-smooth
12
while not is_prime(p):
13
p = 2 * prod([random_prime(B, proof=False) for i in range(num_factors)]) + 1
14
15
F = GF(p)
16
g, a, b = [F(randint(0, p)) for i in range(3)]
17
18
A, B = g^a, g^b
19
20
assert A^b == B^a and gcd(g, p - 1) == 1
21
return g, A, B, F
22
23
# The Baby-Step Giant Step (BSGS) algorithm helps reduce the complexity of calculating the discrete logarithm
24
# 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
25
# 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
26
# 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
27
# 0 <= j <= p, multiplying by a^-m for every y not equal to
28
29
def BSGS(g, A, n, G):
30
# Normally ceil(sqrt(n)) should work but for some reason some test cases break this
31
m = ceil(sqrt(n)) + 1
32
y = A
33
log_table = {}
34
35
for j in range(m):
36
log_table[j] = (j, g^j)
37
38
inv = g^-m
39
40
for i in range(m):
41
for x in log_table.keys():
42
if log_table[x][1] == y:
43
return i * m + log_table[x][0]
44
45
y *= inv
46
47
return None
48
49
# The Pohlig-Hellman attack on Diffie-Hellman works as such:
50
# Given the generator, public keys of either Alice or Bob, as well as the multiplicative order
51
# Of the group (which in Diffie-Hellman is p - 1 due to prime modulus),
52
# one can factor the group order (which by construction here is B-smooth) into
53
# Small primes. By Lagrange's theorem, we have that
54
55
56
def pohlig_hellman(g, A, F, debug=True):
57
""" Attempts to use Pohlig-Hellman to compute discrete logarithm of A = g^a mod p"""
58
59
# This code is pretty clunky, naive, and unoptimized at the moment, but it works.
60
61
p = F.order()
62
factors = [p_i ^ e_i for (p_i, e_i) in factor(F.order() - 1)]
63
crt_array = []
64
65
if debug:
66
print("[x] Factored |F_p| = p - 1 into %s" % factors)
67
68
for p_i in factors:
69
g_i = g ^ ((p - 1) // p_i)
70
h_i = A ^ ((p - 1) // p_i)
71
x_i = BSGS(g_i, h_i, p_i, GF(p_i))
72
if debug and x_i != None:
73
print("[x] Found discrete logarithm %d for factor %d" % (x_i, p_i))
74
crt_array += [x_i]
75
elif x_i == None:
76
print("[] Did not find discrete logarithm for factor %d" % p_i)
77
78
79
return crt(crt_array, factors)
80
81
def test():
82
83
# Not sure why B is here, it isn't even used
84
print("Generating parameters...")
85
g, A, B, F = generate_params()
86
print("Attempting Pohlig-Hellman factorization with \ng = %d\nA = %d\nF is a finite field with order %d\n"
87
% (g, A, F.order()))
88
a = pohlig_hellman(g, A, F)
89
assert g^a == A
90
print("[x] Recovered exponent a such that g^a = A through Pohlig-Hellman: %d" % a)
91
92
if __name__ == "__main__":
93
test()
94
95
# Test case that broke BSGS (missed calculating the discrete logarithm for p = 2 apparently)
96
97
#F = GF(57709937095736748707766266121061070270736984391755037161746092145800428699901267064992500100452120323)
98
#g = F(6862230423439704800599635372588241766443241516687730839067524319148439812277727596294137046769507421)
99
#A = F(35109578903356652282583091316434534076329783688364219256182342958633264942467664701017935316905285284)
100
101
102