[number] remove the need for a random generator for testing primality

a DRG is implicitely created when calling the non gmp primality test
This commit is contained in:
Vincent Hanquez 2015-05-23 12:55:29 +01:00
parent ed48246740
commit c0e50547ad
3 changed files with 48 additions and 20 deletions

View File

@ -26,6 +26,7 @@ import Crypto.Number.Generate
import Crypto.Number.Basic (sqrti, gcde) import Crypto.Number.Basic (sqrti, gcde)
import Crypto.Number.ModArithmetic (expSafe) import Crypto.Number.ModArithmetic (expSafe)
import Crypto.Random.Types import Crypto.Random.Types
import Crypto.Random.Probabilistic
import Data.Bits import Data.Bits
@ -33,17 +34,17 @@ import Data.Bits
-- first a list of small primes are implicitely tested for divisibility, -- first a list of small primes are implicitely tested for divisibility,
-- then a fermat primality test is used with arbitrary numbers and -- then a fermat primality test is used with arbitrary numbers and
-- then the Miller Rabin algorithm is used with an accuracy of 30 recursions -- then the Miller Rabin algorithm is used with an accuracy of 30 recursions
isProbablyPrime :: MonadRandom m => Integer -> m Bool isProbablyPrime :: Integer -> Bool
isProbablyPrime !n isProbablyPrime !n
| any (\p -> p `divides` n) (filter (< n) firstPrimes) = return False | any (\p -> p `divides` n) (filter (< n) firstPrimes) = False
| primalityTestFermat 50 (n`div`2) n = primalityTestMillerRabin 30 n | primalityTestFermat 50 (n`div`2) n = primalityTestMillerRabin 30 n
| otherwise = return False | otherwise = False
-- | generate a prime number of the required bitsize -- | generate a prime number of the required bitsize
generatePrime :: MonadRandom m => Int -> m Integer generatePrime :: MonadRandom m => Int -> m Integer
generatePrime bits = do generatePrime bits = do
sp <- generateOfSize bits sp <- generateParams bits (Just SetTwoHighest) True
findPrimeFrom sp return $ findPrimeFrom sp
-- | generate a prime number of the form 2p+1 where p is also prime. -- | generate a prime number of the form 2p+1 where p is also prime.
-- it is also knowed as a Sophie Germaine prime or safe prime. -- it is also knowed as a Sophie Germaine prime or safe prime.
@ -53,37 +54,35 @@ generatePrime bits = do
generateSafePrime :: MonadRandom m => Int -> m Integer generateSafePrime :: MonadRandom m => Int -> m Integer
generateSafePrime bits = do generateSafePrime bits = do
sp <- generateOfSize bits sp <- generateOfSize bits
p <- findPrimeFromWith (\i -> isProbablyPrime (2*i+1)) (sp `div` 2) let p = findPrimeFromWith (\i -> isProbablyPrime (2*i+1)) (sp `div` 2)
return (2*p+1) return (2*p+1)
-- | find a prime from a starting point where the property hold. -- | find a prime from a starting point where the property hold.
findPrimeFromWith :: MonadRandom m => (Integer -> m Bool) -> Integer -> m Integer findPrimeFromWith :: (Integer -> Bool) -> Integer -> Integer
findPrimeFromWith prop !n findPrimeFromWith prop !n
| even n = findPrimeFromWith prop (n+1) | even n = findPrimeFromWith prop (n+1)
| otherwise = do | otherwise =
primed <- isProbablyPrime n if not (isProbablyPrime n)
if not primed
then findPrimeFromWith prop (n+2) then findPrimeFromWith prop (n+2)
else do else
validate <- prop n if prop n
if validate then n
then return n
else findPrimeFromWith prop (n+2) else findPrimeFromWith prop (n+2)
-- | find a prime from a starting point with no specific property. -- | find a prime from a starting point with no specific property.
findPrimeFrom :: MonadRandom m => Integer -> m Integer findPrimeFrom :: Integer -> Integer
findPrimeFrom n = findPrimeFrom n =
case gmpNextPrime n of case gmpNextPrime n of
GmpSupported p -> return p GmpSupported p -> p
GmpUnsupported -> findPrimeFromWith (\_ -> return True) n GmpUnsupported -> findPrimeFromWith (\_ -> True) n
-- | Miller Rabin algorithm return if the number is probably prime or composite. -- | Miller Rabin algorithm return if the number is probably prime or composite.
-- the tries parameter is the number of recursion, that determines the accuracy of the test. -- the tries parameter is the number of recursion, that determines the accuracy of the test.
primalityTestMillerRabin :: MonadRandom m => Int -> Integer -> m Bool primalityTestMillerRabin :: Int -> Integer -> Bool
primalityTestMillerRabin tries !n = primalityTestMillerRabin tries !n =
case gmpTestPrimeMillerRabin tries n of case gmpTestPrimeMillerRabin tries n of
GmpSupported b -> return b GmpSupported b -> b
GmpUnsupported -> run GmpUnsupported -> probabilistic run
where where
run run
| n <= 3 = error "Miller-Rabin requires tested value to be > 3" | n <= 3 = error "Miller-Rabin requires tested value to be > 3"

View File

@ -0,0 +1,28 @@
-- |
-- Module : Crypto.Random.Probabilistic
-- License : BSD-style
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
-- Stability : experimental
-- Portability : Good
--
module Crypto.Random.Probabilistic
( probabilistic
) where
import Crypto.Internal.Compat
import Crypto.Random.Types
import Crypto.Random
-- | This create a random number generator out of thin air with
-- the system entropy; don't generally use as the IO is not exposed
-- this can have unexpected random for.
--
-- This is useful for probabilistic algorithm like Miller Rabin
-- probably prime algorithm, given appropriate choice of the heuristic
--
-- Generally, it's advise not to use this function.
probabilistic :: MonadPseudoRandom ChaChaDRG a -> a
probabilistic f = fst $ withDRG drg f
where {-# NOINLINE drg #-}
drg = unsafeDoIO drgNew
{-# NOINLINE probabilistic #-}

View File

@ -147,6 +147,7 @@ Library
Crypto.Random.Entropy.Source Crypto.Random.Entropy.Source
Crypto.Random.Entropy.Backend Crypto.Random.Entropy.Backend
Crypto.Random.ChaChaDRG Crypto.Random.ChaChaDRG
Crypto.Random.Probabilistic
Crypto.PubKey.Internal Crypto.PubKey.Internal
Crypto.PubKey.ElGamal Crypto.PubKey.ElGamal
Crypto.Internal.ByteArray Crypto.Internal.ByteArray