Skip to content

Commit

Permalink
Merge pull request #15825 from czgdp1807/bg_crypto
Browse files Browse the repository at this point in the history
[WIP] Added Blum Goldwasser cryptosystem
  • Loading branch information
oscarbenjamin committed Feb 18, 2019
2 parents 5c69952 + be9884f commit 33349d0
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 3 deletions.
2 changes: 1 addition & 1 deletion sympy/crypto/__init__.py
Expand Up @@ -11,4 +11,4 @@
encipher_elgamal, dh_private_key, dh_public_key, dh_shared_key,
padded_key, encipher_bifid, decipher_bifid, bifid_square, bifid5,
bifid6, bifid10, decipher_gm, encipher_gm, gm_public_key,
gm_private_key)
gm_private_key, bg_private_key, bg_public_key, encipher_bg, decipher_bg)
171 changes: 170 additions & 1 deletion sympy/crypto/crypto.py
Expand Up @@ -28,7 +28,7 @@
from sympy.polys.polytools import gcd, Poly
from sympy.utilities.misc import filldedent, translate
from sympy.utilities.iterables import uniq
from sympy.utilities.randtest import _randrange
from sympy.utilities.randtest import _randrange, _randint


def AZ(s=None):
Expand Down Expand Up @@ -2245,3 +2245,172 @@ def decipher_gm(message, key):
m <<= 1
m += not b
return m

################ Blum–Goldwasser cryptosystem #########################

def bg_private_key(p, q):
"""
Check if p and q can be used as private keys for
the Blum–Goldwasser cryptosystem.
The three necessary checks for p and q to pass
so that they can be used as private keys:
1. p and q must both be prime
2. p and q must be distinct
3. p and q must be congruent to 3 mod 4
Parameters
==========
p, q : the keys to be checked
Returns
=======
p, q : input values
Raises
======
ValueError : if p and q do not pass the above conditions
"""

if not isprime(p) or not isprime(q):
raise ValueError("the two arguments must be prime, "
"got %i and %i" %(p, q))
elif p == q:
raise ValueError("the two arguments must be distinct, "
"got two copies of %i. " %p)
elif (p - 3) % 4 != 0 or (q - 3) % 4 != 0:
raise ValueError("the two arguments must be congruent to 3 mod 4, "
"got %i and %i" %(p, q))
return p, q

def bg_public_key(p, q):
"""
Calculates public keys from private keys.
The function first checks the validity of
private keys passed as arguments and
then returns their product.
Parameters
==========
p, q : the private keys
Returns
=======
N : the public key
"""
p, q = bg_private_key(p, q)
N = p * q
return N

def encipher_bg(i, key, seed=None):
"""
Encrypts the message using public key and seed.
ALGORITHM:
1. Encodes i as a string of L bits, m.
2. Select a random element r, where 1 < r < key, and computes
x = r^2 mod key.
3. Use BBS pseudo-random number generator to generate L random bits, b,
using the initial seed as x.
4. Encrypted message, c_i = m_i XOR b_i, 1 <= i <= L.
5. x_L = x^(2^L) mod key.
6. Return (c, x_L)
Parameters
==========
i : message, a non-negative integer
key : the public key
Returns
=======
(encrypted_message, x_L) : Tuple
Raises
======
ValueError : if i is negative
"""

if i < 0:
raise ValueError(
"message must be a non-negative "
"integer: got %d instead" % i)

enc_msg = []
while i > 0:
enc_msg.append(i % 2)
i //= 2
enc_msg.reverse()
L = len(enc_msg)

r = _randint(seed)(2, key - 1)
x = r**2 % key
x_L = pow(int(x), int(2**L), int(key))

rand_bits = []
for k in range(L):
rand_bits.append(x % 2)
x = x**2 % key

encrypt_msg = [m ^ b for (m, b) in zip(enc_msg, rand_bits)]

return (encrypt_msg, x_L)

def decipher_bg(message, key):
"""
Decrypts the message using private keys.
ALGORITHM:
1. Let, c be the encrypted message, y the second number received,
and p and q be the private keys.
2. Compute, r_p = y^((p+1)/4 ^ L) mod p and
r_q = y^((q+1)/4 ^ L) mod q.
3. Compute x_0 = (q(q^-1 mod p)r_p + p(p^-1 mod q)r_q) mod N.
4. From, recompute the bits using the BBS generator, as in the
encryption algorithm.
5. Compute original message by XORing c and b.
Parameters
==========
message : Tuple of encrypted message and a non-negative integer.
key : Tuple of private keys
Returns
=======
orig_msg : The original message
"""

p, q = key
encrypt_msg, y = message
public_key = p * q
L = len(encrypt_msg)
p_t = ((p + 1)/4)**L
q_t = ((q + 1)/4)**L
r_p = pow(int(y), int(p_t), int(p))
r_q = pow(int(y), int(q_t), int(q))

x = (q * mod_inverse(q, p) * r_p + p * mod_inverse(p, q) * r_q) % public_key

orig_bits = []
for k in range(L):
orig_bits.append(x % 2)
x = x**2 % public_key

orig_msg = 0
for (m, b) in zip(encrypt_msg, orig_bits):
orig_msg = orig_msg * 2
orig_msg += (m ^ b)

return orig_msg
32 changes: 31 additions & 1 deletion sympy/crypto/tests/test_crypto.py
Expand Up @@ -13,7 +13,8 @@
encipher_elgamal, decipher_elgamal, dh_private_key, dh_public_key,
dh_shared_key, decipher_shift, decipher_affine, encipher_bifid,
decipher_bifid, bifid_square, padded_key, uniq, decipher_gm,
encipher_gm, gm_public_key, gm_private_key)
encipher_gm, gm_public_key, gm_private_key, encipher_bg, decipher_bg,
bg_private_key, bg_public_key)
from sympy.matrices import Matrix
from sympy.ntheory import isprime, is_primitive_root
from sympy.polys.domains import FF
Expand Down Expand Up @@ -335,3 +336,32 @@ def test_gm_public_key():
assert 323 == gm_public_key(17, 19)[1]
assert 15 == gm_public_key(3, 5)[1]
raises(ValueError, lambda: gm_public_key(15, 19))

def test_encipher_decipher_bg():
ps = [67, 7, 71, 103, 11, 43, 107, 47,
79, 19, 83, 23, 59, 127, 31]
qs = qs = [7, 71, 103, 11, 43, 107, 47,
79, 19, 83, 23, 59, 127, 31, 67]
messages = [
0, 328, 343, 148, 1280, 758, 383,
724, 603, 516, 766, 618, 186,
]

for p, q in zip(ps, qs):
pri = bg_private_key(p, q)
for msg in messages:
pub = bg_public_key(p, q)
enc = encipher_bg(msg, pub)
dec = decipher_bg(enc, pri)
assert dec == msg

def test_bg_private_key():
raises(ValueError, lambda: bg_private_key(8, 16))
raises(ValueError, lambda: bg_private_key(8, 8))
raises(ValueError, lambda: bg_private_key(13, 17))
assert 23, 31 == bg_private_key(23, 31)

def test_bg_public_key():
assert 5293 == bg_public_key(67, 79)
assert 713 == bg_public_key(23, 31)
raises(ValueError, lambda: bg_private_key(13, 17))

0 comments on commit 33349d0

Please sign in to comment.