Skip to content

Commit 29b310d

Browse files
D-Sinuskeon
authored andcommitted
Add diffie hellman key exchange (#615)
* [Add] Find order algorithm and its supplemental * [Add] Find primitive root algorithm * [Edit] Add 'find_' in front of primitive root algorithm file * [Add/Fix] Supplemental & exception handling for the case n = 1 * [Fix] Exception handling for the case a == n == 1 in findOrder function * [Delete] Algorithms in my other branch * [Add] Diffie-Hellman Key Exchange Illustration * [Add] Description for Diffie-Hellman Key exchange * [Add] Supplemental for DH Key Exchange * [Add] Test and its operating environment * [Edit] Add link to README.md file * [Edit] For not primitive root case, code raise ValueError now * [Edit] Change test cases according to immediate before commit * [Edit] For not primitive root case, code return False now, for consistency (return Bool only) * Change test cases according to immediate before commit * [Edit] If optional parameter is given, additional explanation would arise
1 parent 63fa0a2 commit 29b310d

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ If you want to uninstall algorithms, it is as simple as:
225225
- [recursive_binomial_coefficient](algorithms/maths/recursive_binomial_coefficient.py)
226226
- [find_order](algorithms/maths/find_order_simple.py)
227227
- [find_primitive_root](algorithms/maths/find_primitive_root_simple.py)
228+
- [diffie_hellman_key_exchange](algorithms/maths/diffie_hellman_key_exchange.py)
228229
- [matrix](algorithms/matrix)
229230
- [sudoku_validator](algorithms/matrix/sudoku_validator.py)
230231
- [bomb_enemy](algorithms/matrix/bomb_enemy.py)

algorithms/maths/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
from .cosine_similarity import *
1818
from .find_order_simple import *
1919
from .find_primitive_root_simple import *
20+
from .diffie_hellman_key_exchange import *
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import math
2+
from random import randint
3+
4+
"""
5+
Code from /algorithms/maths/prime_check.py,
6+
written by 'goswami-rahul' and 'Hai Honag Dang'
7+
"""
8+
def prime_check(n):
9+
"""Return True if n is a prime number
10+
Else return False.
11+
"""
12+
13+
if n <= 1:
14+
return False
15+
if n == 2 or n == 3:
16+
return True
17+
if n % 2 == 0 or n % 3 == 0:
18+
return False
19+
j = 5
20+
while j * j <= n:
21+
if n % j == 0 or n % (j + 2) == 0:
22+
return False
23+
j += 6
24+
return True
25+
26+
27+
"""
28+
For positive integer n and given integer a that satisfies gcd(a, n) = 1,
29+
the order of a modulo n is the smallest positive integer k that satisfies
30+
pow (a, k) % n = 1. In other words, (a^k) ≡ 1 (mod n).
31+
Order of certain number may or may not be exist. If so, return -1.
32+
"""
33+
def find_order(a, n):
34+
if ((a == 1) & (n == 1)):
35+
return 1
36+
""" Exception Handeling :
37+
1 is the order of of 1 """
38+
else:
39+
if (math.gcd(a, n) != 1):
40+
print ("a and n should be relative prime!")
41+
return -1
42+
else:
43+
for i in range(1, n):
44+
if (pow(a, i) % n == 1):
45+
return i
46+
return -1
47+
48+
"""
49+
Euler's totient function, also known as phi-function ϕ(n),
50+
counts the number of integers between 1 and n inclusive,
51+
which are coprime to n.
52+
(Two numbers are coprime if their greatest common divisor (GCD) equals 1).
53+
Code from /algorithms/maths/euler_totient.py, written by 'goswami-rahul'
54+
"""
55+
def euler_totient(n):
56+
"""Euler's totient function or Phi function.
57+
Time Complexity: O(sqrt(n))."""
58+
result = n;
59+
for i in range(2, int(n ** 0.5) + 1):
60+
if n % i == 0:
61+
while n % i == 0:
62+
n //= i
63+
result -= result // i
64+
if n > 1:
65+
result -= result // n;
66+
return result;
67+
68+
"""
69+
For positive integer n and given integer a that satisfies gcd(a, n) = 1,
70+
a is the primitive root of n, if a's order k for n satisfies k = ϕ(n).
71+
Primitive roots of certain number may or may not be exist.
72+
If so, return empty list.
73+
"""
74+
75+
def find_primitive_root(n):
76+
if (n == 1):
77+
return [0]
78+
""" Exception Handeling :
79+
0 is the only primitive root of 1 """
80+
else:
81+
phi = euler_totient(n)
82+
p_root_list = []
83+
""" It will return every primitive roots of n. """
84+
for i in range (1, n):
85+
if (math.gcd(i, n) != 1):
86+
continue
87+
""" To have order, a and n must be
88+
relative prime with each other. """
89+
else:
90+
order = find_order(i, n)
91+
if (order == phi):
92+
p_root_list.append(i)
93+
else:
94+
continue
95+
return p_root_list
96+
97+
98+
"""
99+
Diffie-Hellman key exchange is the method that enables
100+
two entities (in here, Alice and Bob), not knowing each other,
101+
to share common secret key through not-encrypted communication network.
102+
This method use the property of one-way function (discrete logarithm)
103+
For example, given a, b and n, it is easy to calculate x
104+
that satisfies (a^b) ≡ x (mod n).
105+
However, it is very hard to calculate x that satisfies (a^x) ≡ b (mod n).
106+
For using this method, large prime number p and its primitive root a must be given.
107+
"""
108+
109+
def alice_private_key(p):
110+
"""Alice determine her private key
111+
in the range of 1 ~ p-1.
112+
This must be kept in secret"""
113+
return randint(1, p-1)
114+
115+
116+
def alice_public_key(a_pr_k, a, p):
117+
"""Alice calculate her public key
118+
with her private key.
119+
This is open to public"""
120+
return pow(a, a_pr_k) % p
121+
122+
123+
def bob_private_key(p):
124+
"""Bob determine his private key
125+
in the range of 1 ~ p-1.
126+
This must be kept in secret"""
127+
return randint(1, p-1)
128+
129+
130+
def bob_public_key(b_pr_k, a, p):
131+
"""Bob calculate his public key
132+
with his private key.
133+
This is open to public"""
134+
return pow(a, b_pr_k) % p
135+
136+
137+
def alice_shared_key(b_pu_k, a_pr_k, p):
138+
""" Alice calculate secret key shared with Bob,
139+
with her private key and Bob's public key.
140+
This must be kept in secret"""
141+
return pow(b_pu_k, a_pr_k) % p
142+
143+
144+
def bob_shared_key(a_pu_k, b_pr_k, p):
145+
""" Bob calculate secret key shared with Alice,
146+
with his private key and Alice's public key.
147+
This must be kept in secret"""
148+
return pow(a_pu_k, b_pr_k) % p
149+
150+
151+
def diffie_hellman_key_exchange(a, p, option = None):
152+
if (option != None):
153+
option = 1
154+
""" Print explanation of process
155+
when option parameter is given """
156+
if (prime_check(p) == False):
157+
print("%d is not a prime number" % p)
158+
return False
159+
"""p must be large prime number"""
160+
else:
161+
try:
162+
p_root_list = find_primitive_root(p)
163+
p_root_list.index(a)
164+
except ValueError:
165+
print("%d is not a primitive root of %d" % (a, p))
166+
return False
167+
""" a must be primitive root of p """
168+
169+
a_pr_k = alice_private_key(p)
170+
a_pu_k = alice_public_key(a_pr_k, a, p)
171+
172+
173+
b_pr_k = bob_private_key(p)
174+
b_pu_k = bob_public_key(b_pr_k, a, p)
175+
176+
if (option == 1):
177+
print ("Private key of Alice = %d" % a_pr_k)
178+
print ("Public key of Alice = %d" % a_pu_k)
179+
print ("Private key of Bob = %d" % b_pr_k)
180+
print ("Public key of Bob = %d" % b_pu_k)
181+
182+
""" In here, Alice send her public key to Bob,
183+
and Bob also send his public key to Alice."""
184+
185+
a_sh_k = alice_shared_key(b_pu_k, a_pr_k, p)
186+
b_sh_k = bob_shared_key(a_pu_k, b_pr_k, p)
187+
print ("Shared key calculated by Alice = %d" % a_sh_k)
188+
print ("Shared key calculated by Bob = %d" % b_sh_k)
189+
190+
return (a_sh_k == b_sh_k)

tests/test_maths.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
cosine_similarity,
2020
find_order,
2121
find_primitive_root,
22+
alice_private_key, alice_public_key, bob_private_key, bob_public_key, alice_shared_key, bob_shared_key, diffie_hellman_key_exchange
2223
)
2324

2425
import unittest
@@ -326,6 +327,7 @@ def test_cosine_similarity(self):
326327
self.assertAlmostEqual(cosine_similarity(vec_a, vec_b), -1)
327328
self.assertAlmostEqual(cosine_similarity(vec_a, vec_c), 0.4714045208)
328329

330+
329331
class TestFindPrimitiveRoot(unittest.TestCase):
330332
"""[summary]
331333
Test for the file find_primitive_root_simple.py
@@ -353,6 +355,20 @@ def test_find_order_simple(self):
353355
self.assertEqual(-1, find_order(128, 256))
354356
self.assertEqual(352, find_order(3, 353))
355357

358+
359+
class TestDiffieHellmanKeyExchange(unittest.TestCase):
360+
"""[summary]
361+
Test for the file diffie_hellman_key_exchange.py
362+
363+
Arguments:
364+
unittest {[type]} -- [description]
365+
"""
366+
def test_find_order_simple(self):
367+
self.assertFalse(diffie_hellman_key_exchange(3, 6))
368+
self.assertTrue(diffie_hellman_key_exchange(3, 353))
369+
self.assertFalse(diffie_hellman_key_exchange(5, 211))
370+
self.assertTrue(diffie_hellman_key_exchange(11, 971))
371+
356372
if __name__ == "__main__":
357373
unittest.main()
358374

0 commit comments

Comments
 (0)