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

Commit 054e27d

Browse files
committed
Modified ElGamal Encryption code and README
1 parent 80c9c17 commit 054e27d

File tree

10 files changed

+199
-79
lines changed

10 files changed

+199
-79
lines changed
-3 Bytes
Loading
11 Bytes
Loading
15 Bytes
Loading
65 Bytes
Loading
286 Bytes
Loading
270 Bytes
Loading
-738 Bytes
Binary file not shown.
-638 Bytes
Binary file not shown.

Elgamal-Encryption/README.md

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,97 @@
11
# ElGamal Encryption
2-
2+
33
Prerequisites:
44
1. Cyclic Groups, Generators
55
2. Basic Number Theory (Euler's Theorem, Fermat's Little Theorem)
6-
7-
86

9-
ElGamal Encryption System is an Asymmetric Key Encryption System based on [Discrete Logarithm Problem](../Discrete-Logarithm-Problem/) (DLP) and Diffie-Hellman Key Exchange. Like every other Public Key encryption, it has a public and a private key, we will see that as we move forward.
10-
7+
8+
ElGamal Encryption System is an Asymmetric Key Encryption System based on [Discrete Logarithm Problem](../Discrete-Logarithm-Problem/) (DLP) and Diffie-Hellman Key Exchange.
9+
1110
For illustrative purposes, we will consider `Alice` as the receiver and `Bob` as the sender.
12-
13-
There are different steps involved while encrypting/decrypting data with ElGamal, let's list them first and then study each of them in detail:
14-
1. **Key Generation**: Alice generates a pair of public and private keys. Shares the public key.
11+
12+
There are different steps involved while encrypting or decrypting data with ElGamal, let's list them first and then study each of them in detail:
13+
1. **Key Generation**: Alice generates a public and private key and shares the public key.
1514
2. **Encryption**: Bob uses Alice's public to encrypt message that he wants to send to Alice, and hence generates a pair of ciphertext (c1, c2). Shares the ciphertext pair.
16-
3. **Decryption**: Alice then uses his private key to decrypt the ciphertext (c1, c2).
17-
18-
15+
3. **Decryption**: Alice then uses her private key to decrypt the ciphertext (c1, c2).
16+
17+
1918

2019
## Key Generation
21-
This is the first step in the process of transferring a messages securely, between Alice and Bob. In this step, Alice does the following:
22-
1. Selects a Cyclic Group `G` of order `q` and generator `g`
23-
2. Selects a random number `x` such that `1 <= x <= q-1`
20+
This is the first step in the process of transferring a messages securely, between Alice and Bob. In this step, Alice does the following:
21+
1. Selects a Cyclic Group `G` of order `q` and generator `g`. Note that either the cyclic group should be generated over a [safe prime](https://en.wikipedia.org/wiki/Safe_prime) `p`, or the generator `g` should generate prime-order-subgroups; otherwise it is vulnerable to [Small Subgroup Attacks](https://florianjw.de/en/insecure_generators.html).
22+
2. Generates a random number `x` such that `1 <= x <= q-1` and assigns it as the private key (Note that `q` is the group order).
2423
3. Calculates ![picture1](Pictures/picture1.gif)
2524
4. Shares `h`, `g`, `q` as Public Key
2625
5. `x` is the Private Key which only Alice should know, and that's where the security of the encryption system lies.
27-
26+
2827

2928
Here is a python-2.7 implementation of the above step:
3029
```python
31-
from Crypto.Util.number import *
32-
import random
33-
34-
def generate_key(size):
35-
p = getPrime(size)
36-
x = random.randint(2, p-2)
37-
g = 2
38-
h = pow(g, x, q)
39-
return (h, g, q)
30+
def _generate_key():
31+
# Assigning the largest 1024-bit safe prime as p
32+
p = (1 << 1024) - 1093337
33+
x = randint(2, p-2)
34+
g = 23
35+
q = p - 1
36+
h = pow(g, x, p)
37+
pubkey = PublicKey(h, p, g, q)
38+
privkey = PrivateKey(x, p, g, q)
39+
return (pubkey, privkey)
4040
```
41-
42-
41+
4342

4443
## Encryption
4544
Bob receives Alice's Public Key and encrypts the message that he wants to send to Alice as follows:
46-
1. Selects a random number `y` --> {1, ..., q-1}
47-
2. Chooses a message `m` such that `1 <= m < q-1`
48-
2. Diffie Hellman Step: Calculates ![picture2](Pictures/picture2.gif)
45+
1. Selects a random number `y` such that `1 <= y <= q-1`. Note that a new value of `y` is chosen for sending every message. Hence, `y` is known as ephemeral key.
46+
2. Chooses a message `m` such that `1 < m < q-1`
47+
2. Diffie Hellman Step: Calculates ![picture2](Pictures/picture2.gif)
4948
3. Diffie Hellman Step, also `s` is known as the shared secret: Calculates ![picture3](Pictures/picture3.gif)
5049
4. Calculates ![picture4](Pictures/picture4.gif)
51-
5. Shares (c1, c2) as the ciphertext
52-
50+
5. Shares (c<sub>1</sub>, c<sub>2</sub>) as the ciphertext
51+
5352

5453
Here is a python-2.7 implementation of the above step:
5554
```python
56-
from Crypto.Util.number import *
57-
import random
58-
59-
def encrypt(message, h, g, q):
60-
message = bytes_to_long(message)
61-
y = random.randint(2, p-2)
62-
c1 = pow(g, y, q)
63-
s = pow(h, y, q)
64-
c2 = (m*s) % q
55+
def _encrypt(message, pubkey):
56+
h = pubkey.h
57+
p = pubkey.p
58+
g = pubkey.g
59+
q = pubkey.q
60+
m = bytes_to_long(message)
61+
# Generating ephemeral key: `y`
62+
y = randint(2, p-2)
63+
c1 = pow(g, y, p)
64+
# Computing the shared secret: `s`
65+
s = pow(h, y, p)
66+
c2 = (m*s) % p
6567
return (c1, c2)
68+
6669
```
6770

68-
69-
7071

7172
## Decryption
72-
Alice receives (c1, c2), we can write them as:
73-
![picture5](Pictures/picture5.gif)
74-
![picture6](Pictures/picture6.gif)
75-
Alice then calculates the following to get the shared secret `s`: ![picture7](Pictures/picture7.gif)
76-
To get back the message `m` from c<sub>2</sub>, Alice does the following:
77-
![picture8](Pictures/picture8.gif)
78-
73+
Alice receives (c<sub>1</sub>, c<sub>2</sub>), we can write them as:
74+
![picture](Pictures/picture2.gif)
75+
![picture](Pictures/picture4.gif)
76+
1. Alice then calculates the following to get the shared secret `s`: ![picture7](Pictures/picture5.gif)
77+
2. To get back the message `m` from c<sub>2</sub>, Alice does the following:
78+
![picture8](Pictures/picture6.gif)
7979

80-
Here is a python-2.7 implementation of the above step:
81-
```python
82-
from Crypto.Util.number import *
83-
import random
8480

85-
def decrypt(c1, c2, g, q, x):
86-
s = pow(c1, x, q)
87-
m = (c2*inverse(s, q)) % q
81+
Here is a python-2.7 implementation of the above step:
82+
```python
83+
def _decrypt(ciphertext, privkey):
84+
c1, c2 = ciphertext
85+
g = privkey.g
86+
p = privkey.p
87+
x = privkey.x
88+
s = pow(c1, x, p)
89+
m = (c2*inverse(s, p)) % p
8890
return m
8991
```
90-
91-
Check out a trivial implementation/example of ElGamal encryption/decryption [here](example.py)
92+
93+
Check out the full implementation/example of ElGamal encryption/decryption [here](example.py)
9294

9395
## References
94-
1. [Wikipedia- ElGamal Encryption](https://en.wikipedia.org/wiki/ElGamal_encryption)
96+
1. [Wikipedia- ElGamal Encryption](https://en.wikipedia.org/wiki/ElGamal_encryption)
97+
2. [Small Subgroup Attacks- Florianjw's blog](https://florianjw.de/en/insecure_generators.html)

Elgamal-Encryption/example.py

Lines changed: 137 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,139 @@
1-
from Crypto.Util.number import *
2-
import random
3-
4-
def generate_key(size):
5-
p = getPrime(size)
6-
x = random.randint(2, p-2)
7-
g = 2
8-
h = pow(g, x, q)
9-
return (h, g, q)
10-
11-
def encrypt(message, h, g, q):
12-
message = bytes_to_long(message)
13-
y = random.randint(2, p-2)
14-
c1 = pow(g, y, q)
15-
s = pow(h, y, q)
16-
c2 = (m*s) % q
1+
#!/usr/bin/env python2.7
2+
3+
from Crypto.Util.number import bytes_to_long, inverse, long_to_bytes
4+
from Crypto.Random.random import randint
5+
6+
class PublicKey:
7+
def __init__(self, h, p, g, q):
8+
self.h = h
9+
self.p = p
10+
self.g = g
11+
self.q = q
12+
13+
class PrivateKey:
14+
def __init__(self, x, p, g, q):
15+
self.x = x
16+
self.p = p
17+
self.g = g
18+
self.q = q
19+
20+
21+
def _generate_key():
22+
"""
23+
Generate private-public key pair.
24+
For security reasons, either p should be a safe prime or g should have a
25+
prime subgroup order. Otherwise it is vulnerable to Short Subgroup Attack.
26+
27+
:Parameters: _None_
28+
29+
:Variables:
30+
g : int/long
31+
Base point for modular exponentiation.
32+
p : int/long
33+
Modulus for modular exponentiation. Should be a safe prime.
34+
x : int/long
35+
Receiver's private key, should be kept secret.
36+
h : int/long
37+
Receiver's public key
38+
q : int/long
39+
Order of group generated by p and equals p-1
40+
41+
:Return: A tuple containing a Public Key object (class `PublicKey`) and
42+
a Private Key object (class `PrivateKey`)
43+
"""
44+
# Assigning the largest 1024-bit safe prime as p
45+
p = (1 << 1024) - 1093337
46+
x = randint(2, p-2)
47+
g = 23
48+
q = p - 1
49+
h = pow(g, x, p)
50+
pubkey = PublicKey(h, p, g, q)
51+
privkey = PrivateKey(x, p, g, q)
52+
return (pubkey, privkey)
53+
54+
def _encrypt(message, pubkey):
55+
"""
56+
Encrypt message using ElGamal encryption system
57+
58+
:Parameters:
59+
message : str
60+
plaintext to be encrypted
61+
pubkey : instance of `PublicKey` class
62+
Alice's public key parameters used for encryption
63+
64+
:Variables:
65+
g : int/long
66+
Base point for modular exponentiation.
67+
p : int/long
68+
Modulus for modular exponentiation. Should be a safe prime.
69+
h : int/long
70+
Receiver's public key
71+
q : int/long
72+
Order of group generated by p and equals p-1
73+
y : int/long
74+
Ephemeral key generated by the sender
75+
c1, c2: int/long
76+
Ciphertext pair
77+
s : int/long
78+
Shared secret
79+
80+
:Return:
81+
A tuple containing ciphertext pair c1, c2
82+
"""
83+
h = pubkey.h
84+
p = pubkey.p
85+
g = pubkey.g
86+
q = pubkey.q
87+
m = bytes_to_long(message)
88+
# Generating ephemeral key: `y`
89+
y = randint(2, p-2)
90+
c1 = pow(g, y, p)
91+
# Computing the shared secret: `s`
92+
s = pow(h, y, p)
93+
c2 = (m*s) % p
1794
return (c1, c2)
1895

19-
def decrypt(c1, c2, g, q, x):
20-
s = pow(c1, x, q)
21-
m = (c2*inverse(s, q)) % q
22-
return m
96+
def _decrypt(ciphertext, privkey):
97+
"""
98+
Decrypt ciphertext using ElGamal Encryption System
99+
100+
:Parameters:
101+
ciphertext : int/long tuple
102+
Ciphertext of ElGamal encrypted plaintext
103+
privkey : instance of `PrivateKey` class
104+
Receiver's private key used for decryption
105+
106+
:Variables:
107+
g : int/long
108+
Base point for modular exponentiation.
109+
p : int/long
110+
Modulus for modular exponentiation. Should be a safe prime.
111+
q : int/long
112+
Order of group generated by p and equals p-1
113+
c1, c2: int/long
114+
Ciphertext pair
115+
x : int/long
116+
Receiver's private key, should be kept secret.
117+
s : int/long
118+
Shared secret
119+
120+
"""
121+
c1, c2 = ciphertext
122+
g = privkey.g
123+
p = privkey.p
124+
x = privkey.x
125+
s = pow(c1, x, p)
126+
m = (c2*inverse(s, p)) % p
127+
return m
128+
129+
def test():
130+
pubkey, privkey = _generate_key()
131+
print "h: ", pubkey.h
132+
print "p: ", pubkey.p
133+
print "g: ", pubkey.g
134+
print "q: ", pubkey.q
135+
print "x: ", privkey.x
136+
print "\n"
137+
ct = _encrypt("test string", pubkey)
138+
print long_to_bytes(_decrypt(ct, privkey))
139+
test()

0 commit comments

Comments
 (0)