diff --git a/Crypto/Number/Prime.hs b/Crypto/Number/Prime.hs index 8909901..33750f2 100644 --- a/Crypto/Number/Prime.hs +++ b/Crypto/Number/Prime.hs @@ -1,3 +1,10 @@ +-- | +-- Module : Crypto.Number.Prime +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : Good + {-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-} #ifndef MIN_VERSION_integer_gmp @@ -6,31 +13,25 @@ #if MIN_VERSION_integer_gmp(0,5,1) {-# LANGUAGE MagicHash #-} #endif --- | --- Module : Crypto.Number.Prime --- License : BSD-style --- Maintainer : Vincent Hanquez --- Stability : experimental --- Portability : Good - module Crypto.Number.Prime ( -{- - generatePrime + generatePrime , generateSafePrime , isProbablyPrime , findPrimeFrom , findPrimeFromWith , primalityTestMillerRabin --} - primalityTestNaive + , primalityTestNaive , primalityTestFermat , isCoprime ) where +import Control.Applicative + import Crypto.Number.Generate import Crypto.Number.Basic (sqrti, gcde_binary) import Crypto.Number.ModArithmetic (exponantiation) +import Crypto.Random.Types #if MIN_VERSION_integer_gmp(0,5,1) import GHC.Integer.GMP.Internals @@ -39,101 +40,103 @@ import GHC.Base import Data.Bits #endif -{- -- | returns if the number is probably prime. -- first a list of small primes are implicitely tested for divisibility, -- then a fermat primality test is used with arbitrary numbers and -- then the Miller Rabin algorithm is used with an accuracy of 30 recursions -isProbablyPrime :: CPRG g => g -> Integer -> (Bool, g) -isProbablyPrime rng !n - | any (\p -> p `divides` n) (filter (< n) firstPrimes) = (False, rng) - | primalityTestFermat 50 (n`div`2) n = primalityTestMillerRabin rng 30 n - | otherwise = (False, rng) +isProbablyPrime :: MonadRandom m => Integer -> m Bool +isProbablyPrime !n + | any (\p -> p `divides` n) (filter (< n) firstPrimes) = return False + | primalityTestFermat 50 (n`div`2) n = primalityTestMillerRabin 30 n + | otherwise = return False -- | generate a prime number of the required bitsize -generatePrime :: CPRG g => g -> Int -> (Integer, g) -generatePrime rng bits = - let (sp, rng') = generateOfSize rng bits - in findPrimeFrom rng' sp +generatePrime :: MonadRandom m => Int -> m Integer +generatePrime bits = do + sp <- generateOfSize bits + findPrimeFrom sp -- | 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. -- -- The number of safe prime is significantly smaller to the number of prime, -- as such it shouldn't be used if this number is supposed to be kept safe. -generateSafePrime :: CPRG g => g -> Int -> (Integer, g) -generateSafePrime rng bits = - let (sp, rng') = generateOfSize rng bits - (p, rng'') = findPrimeFromWith rng' (\g i -> isProbablyPrime g (2*i+1)) (sp `div` 2) - in (2*p+1, rng'') +generateSafePrime :: MonadRandom m => Int -> m Integer +generateSafePrime bits = do + sp <- generateOfSize bits + p <- findPrimeFromWith (\i -> isProbablyPrime (2*i+1)) (sp `div` 2) + return (2*p+1) -- | find a prime from a starting point where the property hold. -findPrimeFromWith :: CPRG g => g -> (g -> Integer -> (Bool,g)) -> Integer -> (Integer, g) -findPrimeFromWith rng prop !n - | even n = findPrimeFromWith rng prop (n+1) - | otherwise = case isProbablyPrime rng n of - (False, rng') -> findPrimeFromWith rng' prop (n+2) - (True, rng') -> - case prop rng' n of - (False, rng'') -> findPrimeFromWith rng'' prop (n+2) - (True, rng'') -> (n, rng'') +findPrimeFromWith :: MonadRandom m => (Integer -> m Bool) -> Integer -> m Integer +findPrimeFromWith prop !n + | even n = findPrimeFromWith prop (n+1) + | otherwise = do + primed <- isProbablyPrime n + if not primed + then findPrimeFromWith prop (n+2) + else do + validate <- prop n + if validate + then return n + else findPrimeFromWith prop (n+2) -- | find a prime from a starting point with no specific property. -findPrimeFrom :: CPRG g => g -> Integer -> (Integer, g) -findPrimeFrom rng n = +findPrimeFrom :: MonadRandom m => Integer -> m Integer +findPrimeFrom n = #if MIN_VERSION_integer_gmp(0,5,1) - (nextPrimeInteger n, rng) + return $ nextPrimeInteger n #else - findPrimeFromWith rng (\g _ -> (True, g)) n + findPrimeFromWith (\_ -> return True) n #endif -- | 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. -primalityTestMillerRabin :: CPRG g => g -> Int -> Integer -> (Bool, g) +primalityTestMillerRabin :: MonadRandom m => Int -> Integer -> m Bool #if MIN_VERSION_integer_gmp(0,5,1) -primalityTestMillerRabin rng (I# tries) !n = +primalityTestMillerRabin (I# tries) !n = case testPrimeInteger n tries of - 0# -> (False, rng) - _ -> (True, rng) + 0# -> return False + _ -> return True #else -primalityTestMillerRabin rng tries !n +primalityTestMillerRabin tries !n | n <= 3 = error "Miller-Rabin requires tested value to be > 3" - | even n = (False, rng) + | even n = return False | tries <= 0 = error "Miller-Rabin tries need to be > 0" - | otherwise = let (witnesses, rng') = generateTries tries rng - in (loop witnesses, rng') - where !nm1 = n-1 - !nm2 = n-2 + | otherwise = loop <$> generateTries tries + where + !nm1 = n-1 + !nm2 = n-2 - (!s,!d) = (factorise 0 nm1) + (!s,!d) = (factorise 0 nm1) - generateTries 0 g = ([], g) - generateTries t g = let (v,g') = generateBetween g 2 nm2 - (vs,g'') = generateTries (t-1) g' - in (v:vs, g'') + generateTries 0 = return [] + generateTries t = do + v <- generateBetween 2 nm2 + vs <- generateTries (t-1) + return (v:vs) - -- factorise n-1 into the form 2^s*d - factorise :: Integer -> Integer -> (Integer, Integer) - factorise !si !vi - | vi `testBit` 0 = (si, vi) - | otherwise = factorise (si+1) (vi `shiftR` 1) -- probably faster to not shift v continously, but just once. - expmod = exponantiation + -- factorise n-1 into the form 2^s*d + factorise :: Integer -> Integer -> (Integer, Integer) + factorise !si !vi + | vi `testBit` 0 = (si, vi) + | otherwise = factorise (si+1) (vi `shiftR` 1) -- probably faster to not shift v continously, but just once. + expmod = exponantiation - -- when iteration reach zero, we have a probable prime - loop [] = True - loop (w:ws) = let x = expmod w d n - in if x == (1 :: Integer) || x == nm1 - then loop ws - else loop' ws ((x*x) `mod` n) 1 + -- when iteration reach zero, we have a probable prime + loop [] = True + loop (w:ws) = let x = expmod w d n + in if x == (1 :: Integer) || x == nm1 + then loop ws + else loop' ws ((x*x) `mod` n) 1 - -- loop from 1 to s-1. if we reach the end then it's composite - loop' ws !x2 !r - | r == s = False - | x2 == 1 = False - | x2 /= nm1 = loop' ws ((x2*x2) `mod` n) (r+1) - | otherwise = loop ws + -- loop from 1 to s-1. if we reach the end then it's composite + loop' ws !x2 !r + | r == s = False + | x2 == 1 = False + | x2 /= nm1 = loop' ws ((x2*x2) `mod` n) (r+1) + | otherwise = loop ws #endif --} {- n < z -> witness to test