Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gmp optional #5

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pip install -e .
```
*Note that the `-e` flag will instruct pip to install the package as "editable". That is, when changes are made to any part of the package during development, those changes will immediately be available system-wide on the activated python environment.*

For best performance, [install gmpy2](https://gmpy2.readthedocs.io/en/latest/intro.html#installation).
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nickboucher Do you think this documentation change is adequate?


All requirements for this package should be added to `setup.py`.

## Public and Private Keys
Expand Down
16 changes: 7 additions & 9 deletions damgard_jurik/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@
from secrets import randbelow
from typing import Any, List, Tuple

from gmpy2 import mpz

from damgard_jurik.prime_gen import gen_safe_prime_pair
from damgard_jurik.shamir import share_secret
from damgard_jurik.utils import int_to_mpz, crm, inv_mod, pow_mod
from damgard_jurik.utils import int_to_mpz, crm, inv_mod, pow_mod, mpzCast


class EncryptedNumber:
Expand Down Expand Up @@ -169,7 +167,7 @@ def encrypt(self, m: int) -> EncryptedNumber:
:return: An EncryptedNumber containing the encryption of `m`.
"""
# Choose random r in Z_n^*
r = mpz(randbelow(self.n - 1)) + 1
r = mpzCast(randbelow(self.n - 1)) + 1
c = pow(self.n + 1, m, self.n_s_1) * pow(r, self.n_s, self.n_s_1) % self.n_s_1

return EncryptedNumber(value=c, public_key=self)
Expand Down Expand Up @@ -271,17 +269,17 @@ def n_pow(p: int) -> int:
@lru_cache(int(s))
@int_to_mpz
def fact(k: int) -> int:
return mpz(factorial(k))
return mpzCast(factorial(k))

i = mpz(0)
i = mpzCast(0)
for j in range(1, s + 1):
j = mpz(j)
j = mpzCast(j)

t_1 = L(a % n_pow(j + 1))
t_2 = i

for k in range(2, j + 1):
k = mpz(k)
k = mpzCast(k)

i = i - 1
t_2 = t_2 * i % n_pow(j)
Expand Down Expand Up @@ -337,7 +335,7 @@ def lam(i: int) -> int:
return l

# Decrypt
c_prime = mpz(1)
c_prime = mpzCast(1)
for c_i, i in zip(c_list, self.i_list):
c_prime = (c_prime * pow_mod(c_i, (2 * lam(i)), self.public_key.n_s_1)) % self.public_key.n_s_1

Expand Down
37 changes: 31 additions & 6 deletions damgard_jurik/prime_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,25 @@
from secrets import randbits
from typing import Tuple

from gmpy2 import bit_set, is_prime, next_prime


def gen_prime(n_bits: int) -> int:
try:
try:
from gmpy2 import bit_set, is_prime, next_prime
except ImportError:
from gmpy import bit_set, is_prime, next_prime
except ImportError:
GMP_AVAILABLE = False
else:
GMP_AVAILABLE = True

if (not GMP_AVAILABLE):
try:
from Cryptodome.Util.number import isPrime as is_prime
from Cryptodome.Util.number import getPrime as crypto_prime
except ImportError:
from Crypto.Util.number import isPrime as is_prime
from Crypto.Util.number import getPrime as crypto_prime

def gmp_prime(n_bits: int) -> int:
"""Returns a prime number with `n_bits` bits.

:param n_bits: The number of bits the prime number should contain.
Expand All @@ -24,6 +39,12 @@ def gen_prime(n_bits: int) -> int:

return p

if (GMP_AVAILABLE):
gen_prime = gmp_prime
else:
def gen_prime (n_bits: int) -> int:
return crypto_prime(n_bits)


def gen_safe_prime(n_bits: int) -> int:
"""Returns a safe prime p with `n_bits` bits.
Expand All @@ -39,8 +60,12 @@ def gen_safe_prime(n_bits: int) -> int:
if is_prime(q):
p = 2 * q + 1

if is_prime(p):
return p
if (GMP_AVAILABLE):
if is_prime(p):
return p
else:
if is_prime(p, 15):
return p


def gen_safe_prime_pair(n_bits: int) -> Tuple[int, int]:
Expand Down
18 changes: 8 additions & 10 deletions damgard_jurik/shamir.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
from secrets import randbelow
from typing import List, Tuple

from gmpy2 import mpz

from damgard_jurik.utils import int_to_mpz, inv_mod
from damgard_jurik.utils import int_to_mpz, inv_mod, mpzCast


class Polynomial:
Expand All @@ -24,7 +22,7 @@ def __init__(self, coeffs: List[int], modulus: int):
:param coeffs: The coefficients of x^0, x^1, x^2, ...
:param modulus: The modulus of the field the polynomial is in.
"""
self.coeffs = [mpz(c_i) for c_i in coeffs]
self.coeffs = [mpzCast(c_i) for c_i in coeffs]
self.modulus = modulus

@int_to_mpz
Expand All @@ -34,10 +32,10 @@ def __call__(self, x: int) -> int:
:param x: The input to the polynomial.
:return: The integer f(x) where f is this polynomial.
"""
f_x = mpz(0)
f_x = mpzCast(0)

for i, c_i in enumerate(self.coeffs):
f_x = (f_x + c_i * pow(x, mpz(i), self.modulus) % self.modulus) % self.modulus
f_x = (f_x + c_i * pow(x, mpzCast(i), self.modulus) % self.modulus) % self.modulus

return f_x

Expand Down Expand Up @@ -70,7 +68,7 @@ def share_secret(secret: int,
f = Polynomial(coeffs, modulus)

# Use the polynomial to share the secret
X = [mpz(x) for x in range(1, n_shares + 1)]
X = [mpzCast(x) for x in range(1, n_shares + 1)]
shares = [(x, f(x)) for x in X]

return shares
Expand All @@ -88,12 +86,12 @@ def reconstruct(shares: List[Tuple[int, int]],
:return: The secret.
"""
# Convert to mpz
shares = [(mpz(x), mpz(f_x)) for x, f_x in shares]
shares = [(mpzCast(x), mpzCast(f_x)) for x, f_x in shares]

# Reconstruct secret
secret = mpz(0)
secret = mpzCast(0)
for i, (x_i, f_x_i) in enumerate(shares):
product = mpz(1)
product = mpzCast(1)

for j, (x_j, _) in enumerate(shares):
if i != j:
Expand Down
38 changes: 27 additions & 11 deletions damgard_jurik/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,45 @@
from math import gcd
from typing import Callable, List, Tuple

from gmpy2 import mpz

try:
try:
from gmpy2 import mpz
except ImportError:
from gmpy import mpz
except ImportError:
GMP_AVAILABLE = False
else:
GMP_AVAILABLE = True

def mpzCast(num: int) -> int:
if (GMP_AVAILABLE):
return mpz(num)
else:
return num

def int_to_mpz(func: Callable) -> Callable:
"""A decorator which converts all int arguments to mpz.

:param func: The function to decorate.
:return: The function `func` but with all int arguments converted to mpz.
"""
@wraps(func)
def func_wrapper(*args, **kwargs):
return func(*[mpz(arg) if isinstance(arg, int) else arg for arg in args],
**{key: mpz(value) if isinstance(value, int) else value for key, value in kwargs.items()})
return func_wrapper

if (GMP_AVAILABLE):
@wraps(func)
def func_wrapper(*args, **kwargs):
return func(*[mpz(arg) if isinstance(arg, int) else arg for arg in args],
**{key: mpz(value) if isinstance(value, int) else value for key, value in kwargs.items()})
return func_wrapper
else:
return func

def prod(nums: List[int]) -> int:
"""Returns the product of the numbers in the list.

:param nums: A list of integers.
:return: The product of the numbers in `nums`.
"""
product = mpz(1)
product = mpzCast(1)

for num in nums:
product *= num
Expand Down Expand Up @@ -66,7 +82,7 @@ def extended_euclidean(a: int, b: int) -> Tuple[int, int]:
:param b: The integer b in the above equation.
:return: A tuple of integers x and y such that ax + by = gcd(a, b).
"""
x0, x1, y0, y1 = mpz(0), mpz(1), mpz(1), mpz(0)
x0, x1, y0, y1 = mpzCast(0), mpzCast(1), mpzCast(1), mpzCast(0)

while a != 0:
q, b, a = b // a, a, b % a
Expand Down Expand Up @@ -104,8 +120,8 @@ def crm(a_list: List[int], n_list: List[int]) -> int:
:param n_list: A list of integers b_i in the above equation.
:return: The unique integer x such that x = a_i (mod n_i) for all i.
"""
a_list = [mpz(a_i) for a_i in a_list]
n_list = [mpz(n_i) for n_i in n_list]
a_list = [mpzCast(a_i) for a_i in a_list]
n_list = [mpzCast(n_i) for n_i in n_list]

N = prod(n_list)
y_list = [N // n_i for n_i in n_list]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
url='https://github.com/cryptovoting/damgard-jurik',
packages=setuptools.find_packages(),
install_requires=[
'gmpy2'
'pycryptodomex'
],
classifiers=[
'Programming Language :: Python :: 3',
Expand Down