355 lines
11 KiB
Python
355 lines
11 KiB
Python
# ===================================================================
|
|
#
|
|
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
#
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in
|
|
# the documentation and/or other materials provided with the
|
|
# distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
# ===================================================================
|
|
|
|
"""Functions to create and test prime numbers.
|
|
|
|
:undocumented: __package__
|
|
"""
|
|
|
|
from Crypto.Math.Numbers import Integer
|
|
from Crypto import Random
|
|
|
|
COMPOSITE = 0
|
|
PROBABLY_PRIME = 1
|
|
|
|
|
|
def miller_rabin_test(candidate, iterations, randfunc=None):
|
|
"""Perform a Miller-Rabin primality test on an integer.
|
|
|
|
The test is specified in Section C.3.1 of `FIPS PUB 186-4`__.
|
|
|
|
:Parameters:
|
|
candidate : integer
|
|
The number to test for primality.
|
|
iterations : integer
|
|
The maximum number of iterations to perform before
|
|
declaring a candidate a probable prime.
|
|
randfunc : callable
|
|
An RNG function where bases are taken from.
|
|
|
|
:Returns:
|
|
``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``.
|
|
|
|
.. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
|
|
"""
|
|
|
|
if not isinstance(candidate, Integer):
|
|
candidate = Integer(candidate)
|
|
|
|
if candidate.is_even():
|
|
return COMPOSITE
|
|
|
|
one = Integer(1)
|
|
minus_one = Integer(candidate - 1)
|
|
|
|
if randfunc is None:
|
|
randfunc = Random.new().read
|
|
|
|
# Step 1 and 2
|
|
m = Integer(minus_one)
|
|
a = 0
|
|
while m.is_even():
|
|
m >>= 1
|
|
a += 1
|
|
|
|
# Skip step 3
|
|
|
|
# Step 4
|
|
for i in range(iterations):
|
|
|
|
# Step 4.1-2
|
|
base = 1
|
|
while base in (one, minus_one):
|
|
base = Integer.random_range(min_inclusive=2,
|
|
max_inclusive=candidate - 2)
|
|
assert(2 <= base <= candidate - 2)
|
|
|
|
# Step 4.3-4.4
|
|
z = pow(base, m, candidate)
|
|
if z in (one, minus_one):
|
|
continue
|
|
|
|
# Step 4.5
|
|
for j in range(1, a):
|
|
z = pow(z, 2, candidate)
|
|
if z == minus_one:
|
|
break
|
|
if z == one:
|
|
return COMPOSITE
|
|
else:
|
|
return COMPOSITE
|
|
|
|
# Step 5
|
|
return PROBABLY_PRIME
|
|
|
|
|
|
def lucas_test(candidate):
|
|
"""Perform a Lucas primality test on an integer.
|
|
|
|
The test is specified in Section C.3.3 of `FIPS PUB 186-4`__.
|
|
|
|
:Parameters:
|
|
candidate : integer
|
|
The number to test for primality.
|
|
|
|
:Returns:
|
|
``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``.
|
|
|
|
.. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
|
|
"""
|
|
|
|
if not isinstance(candidate, Integer):
|
|
candidate = Integer(candidate)
|
|
|
|
# Step 1
|
|
if candidate.is_even() or candidate.is_perfect_square():
|
|
return COMPOSITE
|
|
|
|
# Step 2
|
|
def alternate():
|
|
sgn = 1
|
|
value = 5
|
|
for x in range(10):
|
|
yield sgn * value
|
|
sgn, value = -sgn, value + 2
|
|
|
|
for D in alternate():
|
|
js = Integer.jacobi_symbol(D, candidate)
|
|
if js == 0:
|
|
return COMPOSITE
|
|
if js == -1:
|
|
break
|
|
else:
|
|
return COMPOSITE
|
|
# Found D. P=1 and Q=(1-D)/4 (note that Q is guaranteed to be an integer)
|
|
|
|
# Step 3
|
|
# This is \delta(n) = n - jacobi(D/n)
|
|
K = candidate + 1
|
|
# Step 4
|
|
r = K.size_in_bits() - 1
|
|
# Step 5
|
|
# U_1=1 and V_1=P
|
|
U_i = Integer(1)
|
|
V_i = Integer(1)
|
|
U_temp = Integer(0)
|
|
V_temp = Integer(0)
|
|
# Step 6
|
|
for i in range(r - 1, -1, -1):
|
|
# Square
|
|
# U_temp = U_i * V_i % candidate
|
|
U_temp.set(U_i)
|
|
U_temp *= V_i
|
|
U_temp %= candidate
|
|
# V_temp = (((V_i ** 2 + (U_i ** 2 * D)) * K) >> 1) % candidate
|
|
V_temp.set(U_i)
|
|
V_temp *= U_i
|
|
V_temp *= D
|
|
V_temp.multiply_accumulate(V_i, V_i)
|
|
if V_temp.is_odd():
|
|
V_temp += candidate
|
|
V_temp >>= 1
|
|
V_temp %= candidate
|
|
# Multiply
|
|
if K.get_bit(i):
|
|
# U_i = (((U_temp + V_temp) * K) >> 1) % candidate
|
|
U_i.set(U_temp)
|
|
U_i += V_temp
|
|
if U_i.is_odd():
|
|
U_i += candidate
|
|
U_i >>= 1
|
|
U_i %= candidate
|
|
# V_i = (((V_temp + U_temp * D) * K) >> 1) % candidate
|
|
V_i.set(V_temp)
|
|
V_i.multiply_accumulate(U_temp, D)
|
|
if V_i.is_odd():
|
|
V_i += candidate
|
|
V_i >>= 1
|
|
V_i %= candidate
|
|
else:
|
|
U_i.set(U_temp)
|
|
V_i.set(V_temp)
|
|
# Step 7
|
|
if U_i == 0:
|
|
return PROBABLY_PRIME
|
|
return COMPOSITE
|
|
|
|
|
|
from Crypto.Util.number import sieve_base as _sieve_base
|
|
## The optimal number of small primes to use for the sieve
|
|
## is probably dependent on the platform and the candidate size
|
|
_sieve_base = _sieve_base[:100]
|
|
|
|
|
|
def test_probable_prime(candidate, randfunc=None):
|
|
"""Test if a number is prime.
|
|
|
|
A number is qualified as prime if it passes a certain
|
|
number of Miller-Rabin tests (dependent on the size
|
|
of the number, but such that probability of a false
|
|
positive is less than 10^-30) and a single Lucas test.
|
|
|
|
For instance, a 1024-bit candidate will need to pass
|
|
4 Miller-Rabin tests.
|
|
|
|
:Parameters:
|
|
candidate : integer
|
|
The number to test for primality.
|
|
randfunc : callable
|
|
The routine to draw random bytes from to select Miller-Rabin bases.
|
|
:Returns:
|
|
``PROBABLE_PRIME`` if the number if prime with very high probability.
|
|
``COMPOSITE`` if the number is a composite.
|
|
For efficiency reasons, ``COMPOSITE`` is also returned for small primes.
|
|
"""
|
|
|
|
if randfunc is None:
|
|
randfunc = Random.new().read
|
|
|
|
if not isinstance(candidate, Integer):
|
|
candidate = Integer(candidate)
|
|
|
|
# First, check trial division by the smallest primes
|
|
try:
|
|
list(map(candidate.fail_if_divisible_by, _sieve_base))
|
|
except ValueError:
|
|
return False
|
|
|
|
# These are the number of Miller-Rabin iterations s.t. p(k, t) < 1E-30,
|
|
# with p(k, t) being the probability that a randomly chosen k-bit number
|
|
# is composite but still survives t MR iterations.
|
|
mr_ranges = ((220, 30), (280, 20), (390, 15), (512, 10),
|
|
(620, 7), (740, 6), (890, 5), (1200, 4),
|
|
(1700, 3), (3700, 2))
|
|
|
|
bit_size = candidate.size_in_bits()
|
|
try:
|
|
mr_iterations = list([x for x in mr_ranges if bit_size < x[0]])[0][1]
|
|
except IndexError:
|
|
mr_iterations = 1
|
|
|
|
if miller_rabin_test(candidate, mr_iterations,
|
|
randfunc=randfunc) == COMPOSITE:
|
|
return COMPOSITE
|
|
if lucas_test(candidate) == COMPOSITE:
|
|
return COMPOSITE
|
|
return PROBABLY_PRIME
|
|
|
|
|
|
def generate_probable_prime(**kwargs):
|
|
"""Generate a random probable prime.
|
|
|
|
The prime will not have any specific properties
|
|
(e.g. it will not be a *strong* prime).
|
|
|
|
Random numbers are evaluated for primality until one
|
|
passes all tests, consisting of a certain number of
|
|
Miller-Rabin tests with random bases followed by
|
|
a single Lucas test.
|
|
|
|
The number of Miller-Rabin iterations is chosen such that
|
|
the probability that the output number is a non-prime is
|
|
less than 1E-30 (roughly 2^{-100}).
|
|
|
|
This approach is compliant to `FIPS PUB 186-4`__.
|
|
|
|
:Keywords:
|
|
exact_bits : integer
|
|
The desired size in bits of the probable prime.
|
|
It must be at least 160.
|
|
randfunc : callable
|
|
An RNG function where candidate primes are taken from.
|
|
prime_filter : callable
|
|
A function that takes an Integer as parameter and returns
|
|
True if the number can be passed to further primality tests,
|
|
False if it should be immediately discarded.
|
|
|
|
:Return:
|
|
A probable prime in the range 2^exact_bits > p > 2^(exact_bits-1).
|
|
|
|
.. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
|
|
"""
|
|
|
|
exact_bits = kwargs.pop("exact_bits", None)
|
|
randfunc = kwargs.pop("randfunc", None)
|
|
prime_filter = kwargs.pop("prime_filter", lambda x: True)
|
|
if kwargs:
|
|
print("Unknown parameters:", list(kwargs.keys()))
|
|
|
|
if exact_bits is None:
|
|
raise ValueError("Missing exact_bits parameter")
|
|
if exact_bits < 160:
|
|
raise ValueError("Prime number is not big enough.")
|
|
|
|
if randfunc is None:
|
|
randfunc = Random.new().read
|
|
|
|
result = COMPOSITE
|
|
while result == COMPOSITE:
|
|
candidate = Integer.random(exact_bits=exact_bits,
|
|
randfunc=randfunc) | 1
|
|
if not prime_filter(candidate):
|
|
continue
|
|
result = test_probable_prime(candidate, randfunc)
|
|
return candidate
|
|
|
|
|
|
def generate_probable_safe_prime(**kwargs):
|
|
"""Generate a random, probable safe prime.
|
|
|
|
Note this operation is much slower than generating a simple prime.
|
|
|
|
:Keywords:
|
|
exact_bits : integer
|
|
The desired size in bits of the probable safe prime.
|
|
randfunc : callable
|
|
An RNG function where candidate primes are taken from.
|
|
|
|
:Return:
|
|
A probable safe prime in the range
|
|
2^exact_bits > p > 2^(exact_bits-1).
|
|
"""
|
|
|
|
exact_bits = kwargs.pop("exact_bits", None)
|
|
randfunc = kwargs.pop("randfunc", None)
|
|
if kwargs:
|
|
print("Unknown parameters:", list(kwargs.keys()))
|
|
|
|
if randfunc is None:
|
|
randfunc = Random.new().read
|
|
|
|
result = COMPOSITE
|
|
while result == COMPOSITE:
|
|
q = generate_probable_prime(exact_bits=exact_bits - 1, randfunc=randfunc)
|
|
candidate = q * 2 + 1
|
|
if candidate.size_in_bits() != exact_bits:
|
|
continue
|
|
result = test_probable_prime(candidate, randfunc=randfunc)
|
|
return candidate
|