From 0cec622ddf7d344669797f460806cec737b812ac Mon Sep 17 00:00:00 2001 From: Colin Atkinson Date: Sat, 24 Dec 2016 17:43:54 -0500 Subject: [PATCH 1/3] Fix generate(Safe)Prime to guarantee prime size Add check for size in generatePrime Add size test in generateSafePrime Require only that top bit is set, instead of top 2 This is the general standard, see e.g. OpenSSL Add an error for too few bits being supplied to prime generator, and add documentation Add some documentation and require highest two bits set Simplify return syntax in generatePrime and generateSafePrime Switch exponent to bit-shift for small performance boost --- .gitignore | 2 ++ Crypto/Error/Types.hs | 2 ++ Crypto/Number/Prime.hs | 35 +++++++++++++++++++++++++++++------ cryptonite.cabal | 2 +- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 33ee2ff..a213015 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ QA benchs/Bench benchs/Hash *.sublime-workspace +.cabal-sandbox/ +cabal.sandbox.config diff --git a/Crypto/Error/Types.hs b/Crypto/Error/Types.hs index 4aaf4e0..cb512d2 100644 --- a/Crypto/Error/Types.hs +++ b/Crypto/Error/Types.hs @@ -43,6 +43,8 @@ data CryptoError = -- Message authentification error | CryptoError_MacKeyInvalid | CryptoError_AuthenticationTagSizeInvalid + -- Prime generation error + | CryptoError_PrimeSizeInvalid deriving (Show,Eq,Enum,Data,Typeable) instance E.Exception CryptoError diff --git a/Crypto/Number/Prime.hs b/Crypto/Number/Prime.hs index 89a92da..c993648 100644 --- a/Crypto/Number/Prime.hs +++ b/Crypto/Number/Prime.hs @@ -27,6 +27,7 @@ import Crypto.Number.Basic (sqrti, gcde) import Crypto.Number.ModArithmetic (expSafe) import Crypto.Random.Types import Crypto.Random.Probabilistic +import Crypto.Error import Data.Bits @@ -40,22 +41,44 @@ isProbablyPrime !n | primalityTestFermat 50 (n`div`2) n = primalityTestMillerRabin 30 n | otherwise = False --- | generate a prime number of the required bitsize +-- | generate a prime number of the required bitsize (i.e. in the range +-- [2^(b-1)+2^(b-2), 2^b)). +-- +-- May throw a CryptoError_PrimeSizeInvalid if the requested size is less +-- than 5 bits, as the smallest prime meeting these conditions is 29. +-- This function requires that the two highest bits are set, so that when +-- multiplied with another prime to create a key, it is guaranteed to be of +-- the proper size. generatePrime :: MonadRandom m => Int -> m Integer generatePrime bits = do - sp <- generateParams bits (Just SetTwoHighest) True - return $ findPrimeFrom sp + if bits < 5 then + throwCryptoError $ CryptoFailed $ CryptoError_PrimeSizeInvalid + else do + sp <- generateParams bits (Just SetTwoHighest) True + let prime = findPrimeFrom sp + if prime < 1 `shiftL` bits then + return $ prime + else generatePrime bits -- | 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. +-- +-- May throw a CryptoError_PrimeSizeInvalid if the requested size is less than +-- 6 bits, as the smallest safe prime with the two highest bits set is 59. generateSafePrime :: MonadRandom m => Int -> m Integer generateSafePrime bits = do - sp <- generateParams bits (Just SetTwoHighest) True - let p = findPrimeFromWith (\i -> isProbablyPrime (2*i+1)) (sp `div` 2) - return (2*p+1) + if bits < 6 then + throwCryptoError $ CryptoFailed $ CryptoError_PrimeSizeInvalid + else do + sp <- generateParams bits (Just SetTwoHighest) True + let p = findPrimeFromWith (\i -> isProbablyPrime (2*i+1)) (sp `div` 2) + let val = 2 * p + 1 + if val < 1 `shiftL` bits then + return $ val + else generateSafePrime bits -- | find a prime from a starting point where the property hold. findPrimeFromWith :: (Integer -> Bool) -> Integer -> Integer diff --git a/cryptonite.cabal b/cryptonite.cabal index 1ea74f7..e82f1e5 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -205,7 +205,7 @@ Library , bytestring , memory >= 0.8 , ghc-prim - ghc-options: -Wall -fwarn-tabs -optc-O3 -fno-warn-unused-imports + ghc-options: -Wall -fwarn-tabs -optc-O3 -fno-warn-unused-imports -fobject-code default-language: Haskell2010 cc-options: -std=gnu99 if flag(old_toolchain_inliner) From 345f4cd141916977ec1a09da03242103c421061e Mon Sep 17 00:00:00 2001 From: Colin Atkinson Date: Fri, 30 Dec 2016 17:18:42 -0500 Subject: [PATCH 2/3] Fix bug in isProbablyPrime for small numbers Fix bug in isProbablyPrime where too many iterations were specified for numbers less than 100 Add clause to isProbablyPrime to use hardcoded values <= 2903 --- Crypto/Number/Prime.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Crypto/Number/Prime.hs b/Crypto/Number/Prime.hs index c993648..10e0be0 100644 --- a/Crypto/Number/Prime.hs +++ b/Crypto/Number/Prime.hs @@ -38,7 +38,8 @@ import Data.Bits isProbablyPrime :: Integer -> Bool isProbablyPrime !n | any (\p -> p `divides` n) (filter (< n) firstPrimes) = False - | primalityTestFermat 50 (n`div`2) n = primalityTestMillerRabin 30 n + | n >= 2 && n <= 2903 = True + | primalityTestFermat 50 (n `div` 2) n = primalityTestMillerRabin 30 n | otherwise = False -- | generate a prime number of the required bitsize (i.e. in the range From a218b4ea3bd319f38f049194abb999ed49b88b55 Mon Sep 17 00:00:00 2001 From: Colin Atkinson Date: Wed, 11 Jan 2017 02:19:58 -0500 Subject: [PATCH 3/3] Update tests for new generate(Safe)Prime Update generatePrime test to test smaller bit sizes Add test for generateSafePrime Remove -fobject-code --- cryptonite.cabal | 2 +- tests/Number.hs | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cryptonite.cabal b/cryptonite.cabal index e82f1e5..1ea74f7 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -205,7 +205,7 @@ Library , bytestring , memory >= 0.8 , ghc-prim - ghc-options: -Wall -fwarn-tabs -optc-O3 -fno-warn-unused-imports -fobject-code + ghc-options: -Wall -fwarn-tabs -optc-O3 -fno-warn-unused-imports default-language: Haskell2010 cc-options: -std=gnu99 if flag(old_toolchain_inliner) diff --git a/tests/Number.hs b/tests/Number.hs index 24ae96f..8016e70 100644 --- a/tests/Number.hs +++ b/tests/Number.hs @@ -42,17 +42,19 @@ tests = testGroup "number" in 0 <= r && r < range , testProperty "generate-prime" $ \testDRG (Int0_2901 baseBits') -> let baseBits = baseBits' `mod` 800 - bits = 48 + baseBits -- no point generating lower than 48 bits .. + bits = 5 + baseBits -- generating lower than 5 bits causes an error .. prime = withTestDRG testDRG $ generatePrime bits - -- with small base bits numbers, the probability that we "cross" this bit size ness - -- to the next is quite high, as the number generated has two highest bit set. - -- - in bits == numBits prime || (if baseBits < 64 then (bits + 1) == numBits prime else False) + in bits == numBits prime + , testProperty "generate-safe-prime" $ \testDRG (Int0_2901 baseBits') -> + let baseBits = baseBits' `mod` 200 + bits = 6 + baseBits + prime = withTestDRG testDRG $ generateSafePrime bits + in bits == numBits prime , testProperty "marshalling" $ \qaInt -> getQAInteger qaInt == os2ip (i2osp (getQAInteger qaInt) :: Bytes) , testGroup "marshalling-kat-to-bytearray" $ map toSerializationKat $ zip [katZero..] serializationVectors , testGroup "marshalling-kat-to-integer" $ map toSerializationKatInteger $ zip [katZero..] serializationVectors - ] + ] where toSerializationKat (i, (sz, n, ba)) = testCase (show i) (ba @=? i2ospOf_ sz n) toSerializationKatInteger (i, (_, n, ba)) = testCase (show i) (n @=? os2ip ba)