Skip to content
This repository was archived by the owner on Mar 24, 2022. It is now read-only.

Commit fa860a5

Browse files
committed
Added Algorithms to solve DLP
1 parent dd28d42 commit fa860a5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1257
-10
lines changed
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Baby Step Giant Step Algorithm
2+
3+
**Prerequisites**:
4+
1. [Cyclic Groups](https://github.com/ashutosh1206/Crypton/blob/master/Discrete-Logarithm-Problem/README.md#cyclic-groups)
5+
2. [Discrete Logarithm Problem](https://github.com/ashutosh1206/Crypton/blob/master/Discrete-Logarithm-Problem/README.md)
6+
7+
A method to reduce the time complexity of solving DLP is to use **Baby Step Giant Step Algorithm**. While brute forcing DLP takes polynomial time of the order ![picture](Pictures/2.gif), Baby Step Giant Step Algorithm can compute the value of `x` in ![picture](Pictures/1.gif) polynomial time complexity. Here, `n` is the order of the group.
8+
9+
This algorithm is a tradeoff between time and space complexity, as we will see when we discuss the algorithm.
10+
11+
## Algorithm
12+
`x` can be expressed as **x = i*m + j**, where ![picture](Pictures/3.gif) and `0 <= i,j < m`.
13+
14+
Hence, we have:
15+
![picture](Pictures/4.gif)
16+
![picture](Pictures/5.gif)
17+
18+
We can now use the above property for solving DLP as follows:
19+
1. Iterate `j` in its range and store all values of ![picture](Pictures/6.gif) with corresponding values of `j` in a lookup table.
20+
2. Run through each possible iteration of `i` and check if ![picture](Pictures/7.gif) exists in the table (ie. check if ![picture](Pictures/7.gif) == ![picture](Pictures/6.gif)).
21+
+ If it does then return **i*m + j** as the value of `x`
22+
+ Else, continue
23+
24+
## Shortcomings
25+
Although the algorithm is more efficient as compared to plain brute-forcing, other algorithms of the same time complexity (Pollard's rho) are used for solving DLPs because of the fact that storing the look up table requires quite a lot of space.
26+
27+
## Implementation
28+
I wrote an implementation of the above algorithm in python/sage:
29+
30+
```python
31+
from sage.all import *
32+
33+
def bsgs(g, y, p):
34+
mod_size = len(bin(p-1)[2:])
35+
36+
print "[+] Using BSGS algorithm to solve DLP"
37+
print "[+] Modulus size: " + str(mod_size) + ". Warning! BSGS not space efficient\n"
38+
39+
m = ceil(sqrt(p-1))
40+
# Baby Step
41+
lookup_table = {pow(g, j, p): j for j in range(m)}
42+
# Giant Step pre-computation
43+
c = pow(g, m*(p-2), p)
44+
# Giant Steps
45+
for i in range(m):
46+
temp = (y*pow(c, i, p)) % p
47+
if temp in lookup_table:
48+
# x found
49+
return i*m + lookup_table[temp]
50+
return None
51+
```
52+
You can check out the complete code here: [bsgs.py](bsgs.py)
53+
54+
## Resources & References
55+
1. [Rahul Sridhar- Survey of Discrete Log Algorithms](https://fortenf.org/e/crypto/2017/12/03/survey-of-discrete-log-algos.html)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from sage.all import *
2+
3+
def bsgs(g, y, p):
4+
"""
5+
Reference:
6+
7+
To solve DLP: y = g^x % p and get the value of x.
8+
We use the property that x = i*m + j, where m = ceil(sqrt(n))
9+
10+
:parameters:
11+
g : int/long
12+
Generator of the group
13+
y : int/long
14+
Result of g**x % p
15+
p : int/long
16+
Group over which DLP is generated. Commonly p is a prime number
17+
18+
:variables:
19+
m : int/long
20+
Upper limit of baby steps / giant steps
21+
x_poss : int/long
22+
Values calculated in each giant step
23+
c : int/long
24+
Giant Step pre-computation: c = g^(-m) % p
25+
i, j : int/long
26+
Giant Step, Baby Step variables
27+
lookup_table: dictionary
28+
Dictionary storing all the values computed in the baby step
29+
"""
30+
mod_size = len(bin(p-1)[2:])
31+
32+
print "[+] Using BSGS algorithm to solve DLP"
33+
print "[+] Modulus size: " + str(mod_size) + ". Warning! BSGS not space efficient\n"
34+
35+
m = ceil(sqrt(p-1))
36+
# Baby Step
37+
lookup_table = {pow(g, j, p): j for j in range(m)}
38+
# Giant Step pre-computation
39+
c = pow(g, m*(p-2), p)
40+
# Giant Steps
41+
for i in range(m):
42+
temp = (y*pow(c, i, p)) % p
43+
if temp in lookup_table:
44+
# x found
45+
return i*m + lookup_table[temp]
46+
return None
47+
48+
49+
if __name__ == "__main__":
50+
try:
51+
assert pow(2, bsgs(2, 4178319614, 6971096459), 6971096459) == 4178319614
52+
assert pow(3, bsgs(3, 362073897, 2500000001), 2500000001) == 362073897
53+
except:
54+
print "[+] Function inconsistent and incorrect, check the implementation"
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Pohlig Hellman Algorithm
2+
3+
**Prerequisites**:
4+
1. [Discrete Logarithm Problem](https://github.com/ashutosh1206/Crypton/tree/master/Discrete-Logarithm-Problem)
5+
6+
**Pohlig Hellman Algorithm** is applicable in cases where order of the group, over which DLP is defined, is a smooth number (ie. the number that can be factored into small prime numbers). First, let us define our DLP over the Cyclic Group `G` = ![picture](Pictures/1.gif) having order `n`.
7+
8+
There are different variations to Pohlig-Hellman algorithm that can be applied in different conditions:
9+
1. When **order** of a group **is a power of 2 only** ie. n = 2<sup>e</sup>
10+
2. When **order** of a group **is a power of a prime** ie. n = p<sup>e</sup>, where `p` is a prime number
11+
3. **General algorithm** ie. n = p<sub>1</sub><sup>e<sub>1</sub></sup> p<sub>2</sub><sup>e<sub>2</sub></sup> p<sub>3</sub><sup>e<sub>3</sub></sup>... p<sub>r</sub><sup>e<sub>r</sub></sup>
12+
13+
## Order of a group is a power of 2
14+
![picture](Pictures/2.png)
15+
*Source: https://crypto.stackexchange.com/questions/34180/discrete-logarithm-problem-is-easy-in-a-cyclic-group-of-order-a-power-of-two*
16+
17+
I implemented this algorithm in python here: [ph_orderp2.py](ph_orderp2.py)
18+
19+
So if you have a group whose order is a power of 2, you can now solve the DLP in ![picture](Pictures/2.gif), where `e` is the exponent in the group order n = 2<sup>e</sup>.
20+
21+
## Order of a group is a power of a prime number
22+
23+
**Source**: [http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf](http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf)
24+
25+
I implemented this algorithm in python here: [ph_orderpp.py](ph_orderpp.py)
26+
27+
## General Algorithm
28+
29+
**Source**: [http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf](http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf)
30+
31+
I implemented this algorithm in python/sage here:
32+
[pohlig_hellman.py](pohlig_hellman.py)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from Crypto.Util.number import *
2+
3+
def pohlig_hellman_p2(g, y, p, e):
4+
"""
5+
Computes `x` for the DLP y = g**x % p in the Group G = {0, 1, 2, ..., p-1},
6+
given that order of the group `n` is a power of 2, ie. n = p-1 = 2**e.
7+
8+
Note that to solve the DLP using this code, `g` must be the generator.
9+
10+
:parameters:
11+
g : int/long
12+
Generator of the group
13+
y : int/long
14+
Result of g**x % p
15+
p : int/long
16+
Group over which DLP is generated. Commonly p is a prime number
17+
e : int/long
18+
Exponent of 2 in the group order: n = p-1 = 2**e
19+
"""
20+
try:
21+
assert 2**e == p - 1
22+
except:
23+
print "[-] Error! 2**e is not equal to order!"
24+
return -1
25+
26+
k = e
27+
# x is a `k` bit number, max value of x is (2**k - 1)
28+
# x = (c_0 * 2**0) + (c_1 * 2**1) + (c_2 * 2**2) + ... + (c_(k-1) * 2**(k-1))
29+
# where c_0, c_1, c_2, ..., c_(k-1) belong to {0, 1}
30+
x = ""
31+
32+
for i in range(1, k+1):
33+
val = pow(y, 2**(k-i), p)
34+
if val == 1:
35+
# val == 1 ==> c_i == 0 (Euler's Theorem)
36+
# digit = c_i
37+
digit = 0
38+
x += "0"
39+
# y = y*(g**(-c_i*(2**i)) % p) % p = y*(g**0 % p) % p = y % p
40+
y = y
41+
elif val == p-1:
42+
# val == p-1 ==> c_i == 1
43+
# digit = c_i
44+
digit = 1
45+
x += "1"
46+
# We need to calculate y = y*(g**(-c_i*(2**i)) % p) % p
47+
# Computed using Step-1 and Step-2
48+
# Step-1: multiplier = g**(2**(i-1)) % p
49+
multiplier = pow(g, digit*(2**(i-1)), p)
50+
# To calculate inverse of `multiplier` mod p, their GCD should be equal to 1
51+
if GCD(multiplier, p) != 1:
52+
print "[-] GCD != 1, inverse cannot be calculated. Check your code!"
53+
return -1
54+
# Step-2: y = y*(g**(-2**(i-1)) % p) % p
55+
y = (y*inverse(multiplier, p)) % p
56+
else:
57+
58+
print "[-] Some error encountered! Check your code!"
59+
return -1
60+
# Values of c_i are appended to `x` in reverse order
61+
return int(x[::-1], 2)
62+
63+
if __name__ == "__main__":
64+
try:
65+
assert pow(3, pohlig_hellman_p2(3, 188, 257, 8), 257) == 188
66+
assert pow(3, pohlig_hellman_p2(3, 46777, 65537, 16), 65537) == 46777
67+
except:
68+
print "[-] Function implementation incorrect!"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from Crypto.Util.number import *
2+
3+
def brute_dlp(g, y, p):
4+
mod_size = len(bin(p-1)[2:])
5+
sol = pow(g, 2, p)
6+
if y == 1:
7+
return 0
8+
if y == g:
9+
return 1
10+
if sol == y:
11+
return y
12+
i = 3
13+
while i <= p-1:
14+
sol = sol*g % p
15+
if sol == y:
16+
return i
17+
i += 1
18+
return None
19+
20+
def pohlig_hellman_pp(g, y, p, q, e):
21+
"""
22+
Reference: http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf
23+
24+
Computes `x` = a mod (p-1) for the DLP g**x % p == y
25+
in the Group G = {0, 1, 2, ..., p-1}
26+
given that order `n` = p-1 is a power of a small prime,
27+
ie. n = p-1 = q**e, where q is a small prime
28+
29+
:parameters:
30+
g : int/long
31+
Generator of the group
32+
y : int/long
33+
Result of g**x % p
34+
p : int/long
35+
Group over which DLP is generated. Commonly p is a prime number
36+
e : int/long
37+
Exponent of 2 in the group order: n = p-1 = q**e
38+
"""
39+
40+
try:
41+
assert p-1 == q**e
42+
# Assume q is a factor of p-1
43+
assert (p-1) % q == 0
44+
except:
45+
print "[-] Error! q**e not a factor of p-1"
46+
return -1
47+
48+
# a = a_0*(q**0) + a_1*(q**1) + a_2*(q**2) + ... + a_(e-1)*(q**(e-1)) + s*(q**e)
49+
# where a_0, a_1, a_2, ..., a_(e-1) belong to {0,1,...,q-1} and s is some integer
50+
a = 0
51+
52+
b_j = y
53+
alpha = pow(g, (p-1)/q, p)
54+
for j in range(e):
55+
y_i = pow(b_j, (p-1)/(q**(j+1)), p)
56+
a_j = brute_dlp(alpha, y_i, p)
57+
assert a_j >= 0 and a_j <= q-1
58+
a += a_j*(q**j)
59+
60+
multiplier = pow(g, a_j*(q**j), p)
61+
assert GCD(multiplier, p) == 1
62+
b_j = (b_j * inverse(multiplier, p)) % p
63+
return a
64+
65+
if __name__ == "__main__":
66+
assert pow(3, pohlig_hellman_pp(3, 188, 257, 2, 8), 257) == 188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from Crypto.Util.number import *
2+
3+
def crt(list_a, list_m):
4+
try:
5+
assert len(list_a) == len(list_m)
6+
except:
7+
print "[+] Length of list_a should be equal to length of list_m"
8+
return -1
9+
for i in range(len(list_m)):
10+
for j in range(len(list_m)):
11+
if GCD(list_m[i], list_m[j])!= 1 and i!=j:
12+
print "[+] Moduli should be pairwise co-prime"
13+
return -1
14+
M = 1
15+
for i in list_m:
16+
M *= i
17+
list_b = [M/i for i in list_m]
18+
assert len(list_b) == len(list_m)
19+
try:
20+
assert [GCD(list_b[i], list_m[i]) == 1 for i in range(len(list_m))]
21+
list_b_inv = [int(inverse(list_b[i], list_m[i])) for i in range(len(list_m))]
22+
except:
23+
print "[+] Encountered an unusual error while calculating inverse using gmpy2.invert()"
24+
return -1
25+
x = 0
26+
for i in range(len(list_m)):
27+
x += list_a[i]*list_b[i]*list_b_inv[i]
28+
return x % M
29+
30+
def brute_dlp(g, y, p):
31+
mod_size = len(bin(p-1)[2:])
32+
sol = pow(g, 2, p)
33+
if y == 1:
34+
return 0
35+
if y == g:
36+
return 1
37+
if sol == y:
38+
return y
39+
i = 3
40+
while i <= p-1:
41+
sol = sol*g % p
42+
if sol == y:
43+
return i
44+
i += 1
45+
return None
46+
47+
def pohlig_hellman_pp(g, y, p, q, e):
48+
try:
49+
# Assume q is a factor of p-1
50+
assert (p-1) % q == 0
51+
except:
52+
print "[-] Error! q**e not a factor of p-1"
53+
return -1
54+
55+
# a = a_0*(q**0) + a_1*(q**1) + a_2*(q**2) + ... + a_(e-1)*(q**(e-1)) + s*(q**e)
56+
# where a_0, a_1, a_2, ..., a_(e-1) belong to {0,1,...,q-1} and s is some integer
57+
a = 0
58+
59+
b_j = y
60+
alpha = pow(g, (p-1)/q, p)
61+
for j in range(e):
62+
y_i = pow(b_j, (p-1)/(q**(j+1)), p)
63+
a_j = brute_dlp(alpha, y_i, p)
64+
assert a_j >= 0 and a_j <= q-1
65+
a += a_j*(q**j)
66+
67+
multiplier = pow(g, a_j*(q**j), p)
68+
assert GCD(multiplier, p) == 1
69+
b_j = (b_j * inverse(multiplier, p)) % p
70+
return a
71+
72+
def pohlig_hellman(g, y, p, list_q, list_e):
73+
x_list = [pohlig_hellman_pp(g, y, p, list_q[i], list_e[i]) for i in range(len(list_q))]
74+
mod_list = [list_q[i]**list_e[i] for i in range(len(list_q))]
75+
return crt(x_list, mod_list)
76+
77+
if __name__ == "__main__":
78+
p = 0xfffffed83c17
79+
print pohlig_hellman(5, 230152795807443, p, [2, 3, 7, 13, 47, 103, 107, 151], [1, 2, 1, 4, 1, 1, 1, 1])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Multiplayer-1
2+
3+
1. Challenge Description: [https://ctftime.org/task/6860](https://ctftime.org/task/6860)
4+
2. Writeups:
5+
+ [My writeup](https://ctftime.org/writeup/11832)
6+
7+
## Directory Contents
8+
1. [server.sage](server.sage)- given encryption script
9+
2. [parameters.sage](parameters.sage) - File containing public EC parameters required for hosting the challenge locally
10+
3. [points.db](points.db) - database essential for challenge hosting
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
param = { "hacklu":
2+
((889774351128949770355298446172353873, 12345, 67890),
3+
# Generator of Subgroup of prime order 73 bits, 79182553273022138539034276599687 to be excact
4+
(238266381988261346751878607720968495, 591153005086204165523829267245014771),
5+
# challenge Q = xP, x random from [0, 79182553273022138539034276599687)
6+
(341454032985370081366658659122300896, 775807209463167910095539163959068826)
7+
)
8+
}
9+
10+
serverAdress = '0.0.0.0'
11+
serverPort = 23426
12+
13+
(p, a, b), (px, py), (qx, qy) = param["hacklu"]
14+
E = EllipticCurve(GF(p), [a, b])
15+
P = E((px, py))
16+
Q = E((qx, qy))
17+
18+
def is_distinguished_point(p):
19+
return p[0] < 2^(100)
Binary file not shown.

0 commit comments

Comments
 (0)