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

Commit

Permalink
Added Algorithms to solve DLP
Browse files Browse the repository at this point in the history
  • Loading branch information
ashutosh1206 committed Mar 6, 2019
1 parent dd28d42 commit 385b72b
Show file tree
Hide file tree
Showing 43 changed files with 1,257 additions and 10 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions Discrete-Logarithm-Problem/Algo-Baby-Step-Giant-Step/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Baby Step Giant Step Algorithm

**Prerequisites**:
1. [Cyclic Groups](https://github.com/ashutosh1206/Crypton/blob/master/Discrete-Logarithm-Problem/README.md#cyclic-groups)
2. [Discrete Logarithm Problem](https://github.com/ashutosh1206/Crypton/blob/master/Discrete-Logarithm-Problem/README.md)

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.

This algorithm is a tradeoff between time and space complexity, as we will see when we discuss the algorithm.

## Algorithm
`x` can be expressed as **x = i*m + j**, where ![picture](Pictures/3.gif) and `0 <= i,j < m`.

Hence, we have:
![picture](Pictures/4.gif)
![picture](Pictures/5.gif)

We can now use the above property for solving DLP as follows:
1. Iterate `j` in its range and store all values of ![picture](Pictures/6.gif) with corresponding values of `j` in a lookup table.
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)).
+ If it does then return **i*m + j** as the value of `x`
+ Else, continue

## Shortcomings
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.

## Implementation
I wrote an implementation of the above algorithm in python/sage:

```python
from sage.all import *

def bsgs(g, y, p):
mod_size = len(bin(p-1)[2:])

print "[+] Using BSGS algorithm to solve DLP"
print "[+] Modulus size: " + str(mod_size) + ". Warning! BSGS not space efficient\n"

m = ceil(sqrt(p-1))
# Baby Step
lookup_table = {pow(g, j, p): j for j in range(m)}
# Giant Step pre-computation
c = pow(g, m*(p-2), p)
# Giant Steps
for i in range(m):
temp = (y*pow(c, i, p)) % p
if temp in lookup_table:
# x found
return i*m + lookup_table[temp]
return None
```
You can check out the complete code here: [bsgs.py](bsgs.py)

## Resources & References
1. [Rahul Sridhar- Survey of Discrete Log Algorithms](https://fortenf.org/e/crypto/2017/12/03/survey-of-discrete-log-algos.html)
54 changes: 54 additions & 0 deletions Discrete-Logarithm-Problem/Algo-Baby-Step-Giant-Step/bsgs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from sage.all import *

def bsgs(g, y, p):
"""
Reference:
To solve DLP: y = g^x % p and get the value of x.
We use the property that x = i*m + j, where m = ceil(sqrt(n))
:parameters:
g : int/long
Generator of the group
y : int/long
Result of g**x % p
p : int/long
Group over which DLP is generated. Commonly p is a prime number
:variables:
m : int/long
Upper limit of baby steps / giant steps
x_poss : int/long
Values calculated in each giant step
c : int/long
Giant Step pre-computation: c = g^(-m) % p
i, j : int/long
Giant Step, Baby Step variables
lookup_table: dictionary
Dictionary storing all the values computed in the baby step
"""
mod_size = len(bin(p-1)[2:])

print "[+] Using BSGS algorithm to solve DLP"
print "[+] Modulus size: " + str(mod_size) + ". Warning! BSGS not space efficient\n"

m = ceil(sqrt(p-1))
# Baby Step
lookup_table = {pow(g, j, p): j for j in range(m)}
# Giant Step pre-computation
c = pow(g, m*(p-2), p)
# Giant Steps
for i in range(m):
temp = (y*pow(c, i, p)) % p
if temp in lookup_table:
# x found
return i*m + lookup_table[temp]
return None


if __name__ == "__main__":
try:
assert pow(2, bsgs(2, 4178319614, 6971096459), 6971096459) == 4178319614
assert pow(3, bsgs(3, 362073897, 2500000001), 2500000001) == 362073897
except:
print "[+] Function inconsistent and incorrect, check the implementation"
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions Discrete-Logarithm-Problem/Algo-Pohlig-Hellman/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Pohlig Hellman Algorithm

**Prerequisites**:
1. [Discrete Logarithm Problem](https://github.com/ashutosh1206/Crypton/tree/master/Discrete-Logarithm-Problem)

**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`.

There are different variations to Pohlig-Hellman algorithm that can be applied in different conditions:
1. When **order** of a group **is a power of 2 only** ie. n = 2<sup>e</sup>
2. When **order** of a group **is a power of a prime** ie. n = p<sup>e</sup>, where `p` is a prime number
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>

## Order of a group is a power of 2
![picture](Pictures/2.png)
*Source: https://crypto.stackexchange.com/questions/34180/discrete-logarithm-problem-is-easy-in-a-cyclic-group-of-order-a-power-of-two*

I implemented this algorithm in python here: [ph_orderp2.py](ph_orderp2.py)

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>.

## Order of a group is a power of a prime number

**Source**: [http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf](http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf)

I implemented this algorithm in python here: [ph_orderpp.py](ph_orderpp.py)

## General Algorithm

**Source**: [http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf](http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf)

I implemented this algorithm in python/sage here:
[pohlig_hellman.py](pohlig_hellman.py)
68 changes: 68 additions & 0 deletions Discrete-Logarithm-Problem/Algo-Pohlig-Hellman/ph_orderp2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from Crypto.Util.number import *

def pohlig_hellman_p2(g, y, p, e):
"""
Computes `x` for the DLP y = g**x % p in the Group G = {0, 1, 2, ..., p-1},
given that order of the group `n` is a power of 2, ie. n = p-1 = 2**e.
Note that to solve the DLP using this code, `g` must be the generator.
:parameters:
g : int/long
Generator of the group
y : int/long
Result of g**x % p
p : int/long
Group over which DLP is generated. Commonly p is a prime number
e : int/long
Exponent of 2 in the group order: n = p-1 = 2**e
"""
try:
assert 2**e == p - 1
except:
print "[-] Error! 2**e is not equal to order!"
return -1

k = e
# x is a `k` bit number, max value of x is (2**k - 1)
# x = (c_0 * 2**0) + (c_1 * 2**1) + (c_2 * 2**2) + ... + (c_(k-1) * 2**(k-1))
# where c_0, c_1, c_2, ..., c_(k-1) belong to {0, 1}
x = ""

for i in range(1, k+1):
val = pow(y, 2**(k-i), p)
if val == 1:
# val == 1 ==> c_i == 0 (Euler's Theorem)
# digit = c_i
digit = 0
x += "0"
# y = y*(g**(-c_i*(2**i)) % p) % p = y*(g**0 % p) % p = y % p
y = y
elif val == p-1:
# val == p-1 ==> c_i == 1
# digit = c_i
digit = 1
x += "1"
# We need to calculate y = y*(g**(-c_i*(2**i)) % p) % p
# Computed using Step-1 and Step-2
# Step-1: multiplier = g**(2**(i-1)) % p
multiplier = pow(g, digit*(2**(i-1)), p)
# To calculate inverse of `multiplier` mod p, their GCD should be equal to 1
if GCD(multiplier, p) != 1:
print "[-] GCD != 1, inverse cannot be calculated. Check your code!"
return -1
# Step-2: y = y*(g**(-2**(i-1)) % p) % p
y = (y*inverse(multiplier, p)) % p
else:

print "[-] Some error encountered! Check your code!"
return -1
# Values of c_i are appended to `x` in reverse order
return int(x[::-1], 2)

if __name__ == "__main__":
try:
assert pow(3, pohlig_hellman_p2(3, 188, 257, 8), 257) == 188
assert pow(3, pohlig_hellman_p2(3, 46777, 65537, 16), 65537) == 46777
except:
print "[-] Function implementation incorrect!"
66 changes: 66 additions & 0 deletions Discrete-Logarithm-Problem/Algo-Pohlig-Hellman/ph_orderpp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from Crypto.Util.number import *

def brute_dlp(g, y, p):
mod_size = len(bin(p-1)[2:])
sol = pow(g, 2, p)
if y == 1:
return 0
if y == g:
return 1
if sol == y:
return y
i = 3
while i <= p-1:
sol = sol*g % p
if sol == y:
return i
i += 1
return None

def pohlig_hellman_pp(g, y, p, q, e):
"""
Reference: http://anh.cs.luc.edu/331/notes/PohligHellmanp_k2p.pdf
Computes `x` = a mod (p-1) for the DLP g**x % p == y
in the Group G = {0, 1, 2, ..., p-1}
given that order `n` = p-1 is a power of a small prime,
ie. n = p-1 = q**e, where q is a small prime
:parameters:
g : int/long
Generator of the group
y : int/long
Result of g**x % p
p : int/long
Group over which DLP is generated. Commonly p is a prime number
e : int/long
Exponent of 2 in the group order: n = p-1 = q**e
"""

try:
assert p-1 == q**e
# Assume q is a factor of p-1
assert (p-1) % q == 0
except:
print "[-] Error! q**e not a factor of p-1"
return -1

# a = a_0*(q**0) + a_1*(q**1) + a_2*(q**2) + ... + a_(e-1)*(q**(e-1)) + s*(q**e)
# where a_0, a_1, a_2, ..., a_(e-1) belong to {0,1,...,q-1} and s is some integer
a = 0

b_j = y
alpha = pow(g, (p-1)/q, p)
for j in range(e):
y_i = pow(b_j, (p-1)/(q**(j+1)), p)
a_j = brute_dlp(alpha, y_i, p)
assert a_j >= 0 and a_j <= q-1
a += a_j*(q**j)

multiplier = pow(g, a_j*(q**j), p)
assert GCD(multiplier, p) == 1
b_j = (b_j * inverse(multiplier, p)) % p
return a

if __name__ == "__main__":
assert pow(3, pohlig_hellman_pp(3, 188, 257, 2, 8), 257) == 188
79 changes: 79 additions & 0 deletions Discrete-Logarithm-Problem/Algo-Pohlig-Hellman/pohlig_hellman.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from Crypto.Util.number import *

def crt(list_a, list_m):
try:
assert len(list_a) == len(list_m)
except:
print "[+] Length of list_a should be equal to length of list_m"
return -1
for i in range(len(list_m)):
for j in range(len(list_m)):
if GCD(list_m[i], list_m[j])!= 1 and i!=j:
print "[+] Moduli should be pairwise co-prime"
return -1
M = 1
for i in list_m:
M *= i
list_b = [M/i for i in list_m]
assert len(list_b) == len(list_m)
try:
assert [GCD(list_b[i], list_m[i]) == 1 for i in range(len(list_m))]
list_b_inv = [int(inverse(list_b[i], list_m[i])) for i in range(len(list_m))]
except:
print "[+] Encountered an unusual error while calculating inverse using gmpy2.invert()"
return -1
x = 0
for i in range(len(list_m)):
x += list_a[i]*list_b[i]*list_b_inv[i]
return x % M

def brute_dlp(g, y, p):
mod_size = len(bin(p-1)[2:])
sol = pow(g, 2, p)
if y == 1:
return 0
if y == g:
return 1
if sol == y:
return y
i = 3
while i <= p-1:
sol = sol*g % p
if sol == y:
return i
i += 1
return None

def pohlig_hellman_pp(g, y, p, q, e):
try:
# Assume q is a factor of p-1
assert (p-1) % q == 0
except:
print "[-] Error! q**e not a factor of p-1"
return -1

# a = a_0*(q**0) + a_1*(q**1) + a_2*(q**2) + ... + a_(e-1)*(q**(e-1)) + s*(q**e)
# where a_0, a_1, a_2, ..., a_(e-1) belong to {0,1,...,q-1} and s is some integer
a = 0

b_j = y
alpha = pow(g, (p-1)/q, p)
for j in range(e):
y_i = pow(b_j, (p-1)/(q**(j+1)), p)
a_j = brute_dlp(alpha, y_i, p)
assert a_j >= 0 and a_j <= q-1
a += a_j*(q**j)

multiplier = pow(g, a_j*(q**j), p)
assert GCD(multiplier, p) == 1
b_j = (b_j * inverse(multiplier, p)) % p
return a

def pohlig_hellman(g, y, p, list_q, list_e):
x_list = [pohlig_hellman_pp(g, y, p, list_q[i], list_e[i]) for i in range(len(list_q))]
mod_list = [list_q[i]**list_e[i] for i in range(len(list_q))]
return crt(x_list, mod_list)

if __name__ == "__main__":
p = 0xfffffed83c17
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 number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Multiplayer-1

1. Challenge Description: [https://ctftime.org/task/6860](https://ctftime.org/task/6860)
2. Writeups:
+ [My writeup](https://ctftime.org/writeup/11832)

## Directory Contents
1. [server.sage](server.sage)- given encryption script
2. [parameters.sage](parameters.sage) - File containing public EC parameters required for hosting the challenge locally
3. [points.db](points.db) - database essential for challenge hosting
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
param = { "hacklu":
((889774351128949770355298446172353873, 12345, 67890),
# Generator of Subgroup of prime order 73 bits, 79182553273022138539034276599687 to be excact
(238266381988261346751878607720968495, 591153005086204165523829267245014771),
# challenge Q = xP, x random from [0, 79182553273022138539034276599687)
(341454032985370081366658659122300896, 775807209463167910095539163959068826)
)
}

serverAdress = '0.0.0.0'
serverPort = 23426

(p, a, b), (px, py), (qx, qy) = param["hacklu"]
E = EllipticCurve(GF(p), [a, b])
P = E((px, py))
Q = E((qx, qy))

def is_distinguished_point(p):
return p[0] < 2^(100)
Binary file not shown.
Loading

0 comments on commit 385b72b

Please sign in to comment.