Skip to content

Commit

Permalink
Merge branch 'lucas'
Browse files Browse the repository at this point in the history
  • Loading branch information
Legrandin committed Aug 9, 2018
2 parents f64df6f + 66fdb1f commit 8bdb02e
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 14 deletions.
9 changes: 9 additions & 0 deletions Changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Changelog
=========

3.6.5 (xx xxxxx 2018)
+++++++++++++++++++++

Resolved issues
---------------

* GH#188: In the Lucas probable prime test, do an exhaustive search for D values,
as opposed to trying only the first few.

3.6.4 (10 July 2018)
+++++++++++++++++++++

Expand Down
28 changes: 19 additions & 9 deletions lib/Crypto/Math/Primality.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ def miller_rabin_test(candidate, iterations, randfunc=None):
if not isinstance(candidate, Integer):
candidate = Integer(candidate)

if candidate in (1, 2, 3, 5):
return PROBABLY_PRIME

if candidate.is_even():
return COMPOSITE

Expand Down Expand Up @@ -129,25 +132,30 @@ def lucas_test(candidate):
candidate = Integer(candidate)

# Step 1
if candidate in (1, 2, 3, 5):
return PROBABLY_PRIME
if candidate.is_even() or candidate.is_perfect_square():
return COMPOSITE

# Step 2
def alternate():
sgn = 1
value = 5
for x in xrange(20):
yield sgn * value
sgn, value = -sgn, value + 2
while True:
yield value
if value > 0:
value += 2
else:
value -= 2
value = -value

for D in alternate():
if candidate in (D, -D):
continue
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
Expand Down Expand Up @@ -205,7 +213,7 @@ def alternate():
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]
_sieve_base = set(_sieve_base[:100])


def test_probable_prime(candidate, randfunc=None):
Expand Down Expand Up @@ -236,11 +244,13 @@ def test_probable_prime(candidate, randfunc=None):
if not isinstance(candidate, Integer):
candidate = Integer(candidate)

# First, check trial division by the smallest primes
# First, check trial division by the smallest primes
if int(candidate) in _sieve_base:
return PROBABLY_PRIME
try:
map(candidate.fail_if_divisible_by, _sieve_base)
except ValueError:
return False
return COMPOSITE

# 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
Expand Down
17 changes: 13 additions & 4 deletions lib/Crypto/SelfTest/Math/test_Primality.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,24 @@

class TestPrimality(unittest.TestCase):

primes = (13, 17, 19, 23, 2**127-1, 175637383534939453397801320455508570374088202376942372758907369518414308188137781042871856139027160010343454418881888953150175357127346872102307696660678617989191485418582475696230580407111841072614783095326672517315988762029036079794994990250662362650625650262324085116467511357592728695033227611029693067539)
composites = (12, 7*23, (2**19-1)*(2**67-1), 9746347772161,)
primes = (1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 2**127-1, 175637383534939453397801320455508570374088202376942372758907369518414308188137781042871856139027160010343454418881888953150175357127346872102307696660678617989191485418582475696230580407111841072614783095326672517315988762029036079794994990250662362650625650262324085116467511357592728695033227611029693067539)
composites = (0, 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 7*23, (2**19-1)*(2**67-1), 9746347772161,)

def test_miller_rabin(self):
for prime in self.primes:
self.assertEqual(miller_rabin_test(prime, 3), PROBABLY_PRIME)
for composite in self.composites:
self.assertEqual(miller_rabin_test(composite, 3), COMPOSITE)
self.assertRaises(ValueError, miller_rabin_test, -1, 3)

def test_lucas(self):
for prime in self.primes:
self.assertEqual(lucas_test(prime), PROBABLY_PRIME)
res = lucas_test(prime)
self.assertEqual(res, PROBABLY_PRIME)
for composite in self.composites:
self.assertEqual(lucas_test(composite), COMPOSITE)
res = lucas_test(composite)
self.assertEqual(res, COMPOSITE)
self.assertRaises(ValueError, lucas_test, -1)

def test_is_prime(self):
primes = (170141183460469231731687303715884105727,
Expand All @@ -82,6 +86,11 @@ def test_is_prime(self):
for np in not_primes:
self.assertEqual(test_probable_prime(np), COMPOSITE)

from Crypto.Util.number import sieve_base
for p in sieve_base[:100]:
res = test_probable_prime(p)
self.assertEqual(res, PROBABLY_PRIME)

def test_generate_prime_bit_size(self):
p = generate_probable_prime(exact_bits=512)
self.assertEqual(p.size_in_bits(), 512)
Expand Down
2 changes: 1 addition & 1 deletion lib/Crypto/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature',
'IO', 'Math']

version_info = (3, 6, 4)
version_info = (3, 6, 5, "a0")

__version__ = ".".join([str(x) for x in version_info])

0 comments on commit 8bdb02e

Please sign in to comment.