DRBG 0.2.3 → 0.3
raw patch · 2 files changed
+240/−211 lines, 2 filesdep +cipher-aes128dep −cryptocipherPVP ok
version bump matches the API change (PVP)
Dependencies added: cipher-aes128
Dependencies removed: cryptocipher
API changes (from Hackage documentation)
- Crypto.Random.DRBG: type GenAES = GenCounter AES128
+ Crypto.Random.DRBG: type GenAES = GenCounter AESKey
Files
- Crypto/Random/DRBG.hs +221/−196
- DRBG.cabal +19/−15
Crypto/Random/DRBG.hs view
@@ -6,25 +6,48 @@ This module is the convenience interface for the DRBG (NIST standardized number-theoretically secure random number generator). Everything is setup-for using the "crypto-api" 'CryptoRandomGen' type class. For example,-to seed a new generator with the system secure random ('System.Crypto.Random')-and generate some bytes (stepping the generator along the way) one would do:+for using the "crypto-api" 'CryptoRandomGen' type class. +To instantiate the base types of 'HmacDRBG', 'HashDRBG', or 'GenAES' just use+the 'CryptoRandomGen' primitives of 'newGen' or 'newGenIO'.++For example, to seed a new generator with the system secure random+('System.Entropy') and generate some bytes (stepping the generator along+the way) one would do:+ @ gen <- newGenIO :: IO HashDRBG let Right (randomBytes, newGen) = genBytes 1024 gen @ +or the same thing with your own entropy (throwing exceptions instead of dealing+with 'Either' this time):++@+ let gen = throwLeft (newGen entropy)+ (bytes,gen') = throwLeft (genBytes 1024 gen)+ in ...+@+ Selecting the underlying hash algorithm is supporting using *DRBGWith types: @ gen <- newGenIO :: IO (HmacDRBGWith SHA224) @ -Composition of generators is supported using two trivial compositions, 'GenXor'-and 'GenAutoReseed'. Additional compositions can be built by instanciating-a 'CryptoRandomGen' as desired.+There are several modifiers that allow you to compose generators together, producing+generators with modified security, reseed, and performance properties. 'GenXor'+will xor the random bytes of two generators. 'GenBuffered' will spark off work+to generate several megabytes of random data and keep that data buffered for+quick use. 'GenAutoReseed' will use one generator to automatically reseed+another after every 32 kilobytes of requested randoms. +For a complex example, here is a generator that buffers several megabytes of+random values which are an Xor of AES with a SHA384 hash that are each reseeded+every 32kb with the output of a SHA512 HMAC generator. (Not to claim this has+any enhanced security properties, but just to show the composition can be+nested).+ @ gen <- newGenIO :: IO (GenBuffered (GenAutoReseed (GenXor AesCntDRBG (HashDRBGWith SHA384)) HmacDRBG)) @@@ -33,21 +56,22 @@ -} module Crypto.Random.DRBG- (- -- * Basic Hash-based Generators- HmacDRBG, HashDRBG- , HmacDRBGWith, HashDRBGWith- -- * Basic Cipher-based Generator- , GenAES, GenCounter- -- * CryptoRandomGen Transformers- , GenXor- , GenBuffered- , GenAutoReseed- -- * AutoReseed generator construction with custom reseed interval- , newGenAutoReseed, newGenAutoReseedIO- -- * Helper Re-exports- , module Crypto.Random- ) where+ (+ -- * Basic Hash-based Generators+ HmacDRBG, HashDRBG+ , HmacDRBGWith, HashDRBGWith+ -- * Basic Cipher-based Generator+ , GenAES, GenCounter+ -- * CryptoRandomGen Transformers+ , GenXor+ , GenBuffered+ , GenAutoReseed+ -- * AutoReseed generator construction with custom reseed interval+ , newGenAutoReseed, newGenAutoReseedIO+ -- * Helper Re-exports+ , module Crypto.Random+ , module Crypto.Types+ ) where import qualified Crypto.Random.DRBG.HMAC as M import qualified Crypto.Random.DRBG.Hash as H@@ -60,7 +84,8 @@ import Crypto.Hash.SHA256 (SHA256) import Crypto.Hash.SHA224 (SHA224) import Crypto.Hash.SHA1 (SHA1)-import Crypto.Cipher.AES (AES128)+import Crypto.Cipher.AES128 (AESKey)+import Crypto.Types import System.Entropy import qualified Data.ByteString as B import qualified Data.ByteString.Internal as BI@@ -74,19 +99,19 @@ import Data.Word instance H.SeedLength SHA512 where- seedlen = Tagged 888+ seedlen = Tagged 888 instance H.SeedLength SHA384 where- seedlen = Tagged 888+ seedlen = Tagged 888 instance H.SeedLength SHA256 where- seedlen = Tagged 440+ seedlen = Tagged 440 instance H.SeedLength SHA224 where- seedlen = Tagged 440+ seedlen = Tagged 440 instance H.SeedLength SHA1 where- seedlen = Tagged 440+ seedlen = Tagged 440 -- |The HMAC DRBG state (of kind * -> *) allowing selection -- of the underlying hash algorithm (SHA1, SHA224 ... SHA512)@@ -119,14 +144,14 @@ -- 2^49 terabytes of random values (128 byte reseeds every 2^48 bytes generated). newGenAutoReseed :: (CryptoRandomGen a, CryptoRandomGen b) => B.ByteString -> Int -> Either GenError (GenAutoReseed a b) newGenAutoReseed bs rsInterval=- let (b1,b2) = B.splitAt (genSeedLength `for` fromRight g1) bs- g1 = newGen b1- g2 = newGen b2- fromRight (Right x) = x- in case (g1, g2) of- (Right a, Right b) -> Right $ GenAutoReseed a b rsInterval 0- (Left e, _) -> Left e- (_, Left e) -> Left e+ let (b1,b2) = B.splitAt (genSeedLength `for` fromRight g1) bs+ g1 = newGen b1+ g2 = newGen b2+ fromRight (Right x) = x+ in case (g1, g2) of+ (Right a, Right b) -> Right $ GenAutoReseed a b rsInterval 0+ (Left e, _) -> Left e+ (_, Left e) -> Left e -- |@newGenAutoReseedIO i@ creates a new 'GenAutoReseed' with a custom -- interval of @i@ bytes, using the system random number generator as a seed.@@ -134,9 +159,9 @@ -- See 'newGenAutoReseed'. newGenAutoReseedIO :: (CryptoRandomGen a, CryptoRandomGen b) => Int -> IO (GenAutoReseed a b) newGenAutoReseedIO i = do- g1 <- newGenIO- g2 <- newGenIO- return $ GenAutoReseed g1 g2 i 0+ g1 <- newGenIO+ g2 <- newGenIO+ return $ GenAutoReseed g1 g2 i 0 seed :: CryptoRandomGen g => Proxy g -> Int seed x = proxy genSeedLength x@@ -145,50 +170,50 @@ rightProxy = reproxy instance CryptoRandomGen HmacDRBG where- newGen bs =- let res = M.instantiate bs B.empty B.empty- in if B.length bs < genSeedLength `for` res- then Left NotEnoughEntropy- else Right res- genSeedLength = Tagged (512 `div` 8)- genBytes req g =- let res = M.generate g (req * 8) B.empty- in case res of- Nothing -> Left NeedReseed- Just (r,s) -> Right (r, s)- genBytesWithEntropy req ai g =- let res = M.generate g (req * 8) ai- in case res of- Nothing -> Left NeedReseed- Just (r,s) -> Right (r, s)- reseed ent g =- let res = M.reseed g ent B.empty- in if B.length ent < genSeedLength `for` res- then Left NotEnoughEntropy- else Right res+ newGen bs =+ let res = M.instantiate bs B.empty B.empty+ in if B.length bs < genSeedLength `for` res+ then Left NotEnoughEntropy+ else Right res+ genSeedLength = Tagged (512 `div` 8)+ genBytes req g =+ let res = M.generate g (req * 8) B.empty+ in case res of+ Nothing -> Left NeedReseed+ Just (r,s) -> Right (r, s)+ genBytesWithEntropy req ai g =+ let res = M.generate g (req * 8) ai+ in case res of+ Nothing -> Left NeedReseed+ Just (r,s) -> Right (r, s)+ reseed ent g =+ let res = M.reseed g ent B.empty+ in if B.length ent < genSeedLength `for` res+ then Left NotEnoughEntropy+ else Right res instance CryptoRandomGen HashDRBG where- newGen bs =- let res = H.instantiate bs B.empty B.empty- in if B.length bs < genSeedLength `for` res- then Left NotEnoughEntropy- else Right res- genSeedLength = Tagged $ 512 `div` 8- genBytes req g = - let res = H.generate g (req * 8) B.empty- in case res of- Nothing -> Left NeedReseed- Just (r,s) -> Right (r, s)- genBytesWithEntropy req ai g =- let res = H.generate g (req * 8) ai- in case res of- Nothing -> Left NeedReseed- Just (r,s) -> Right (r, s)- reseed ent g =- let res = H.reseed g ent B.empty- in if B.length ent < genSeedLength `for` res- then Left NotEnoughEntropy- else Right res+ newGen bs =+ let res = H.instantiate bs B.empty B.empty+ in if B.length bs < genSeedLength `for` res+ then Left NotEnoughEntropy+ else Right res+ genSeedLength = Tagged $ 512 `div` 8+ genBytes req g = + let res = H.generate g (req * 8) B.empty+ in case res of+ Nothing -> Left NeedReseed+ Just (r,s) -> Right (r, s)+ genBytesWithEntropy req ai g =+ let res = H.generate g (req * 8) ai+ in case res of+ Nothing -> Left NeedReseed+ Just (r,s) -> Right (r, s)+ reseed ent g =+ let res = H.reseed g ent B.empty+ in if B.length ent < genSeedLength `for` res+ then Left NotEnoughEntropy+ else Right res helper1 :: Tagged (GenAutoReseed a b) Int -> a helper1 = const undefined@@ -218,58 +243,58 @@ data GenAutoReseed a b = GenAutoReseed !a !b !Int !Int instance (CryptoRandomGen a, CryptoRandomGen b) => CryptoRandomGen (GenAutoReseed a b) where- {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HmacDRBG HmacDRBG) #-}- {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HashDRBG HashDRBG) #-}- {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HashDRBG HmacDRBG) #-}- {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HmacDRBG HashDRBG) #-}- newGen bs = newGenAutoReseed bs (2^15)- newGenIO = newGenAutoReseedIO (2^15)- genSeedLength =- let a = helper1 res- b = helper2 res- res = Tagged $ genSeedLength `for` a + genSeedLength `for` b- in res- genBytes req (GenAutoReseed a b rs cnt) =- case genBytes req a of- Left NeedReseed -> do- (ent,b') <- genBytes (genSeedLength `for` a) b- a' <- reseed ent a- (res, aNew) <- genBytes req a'- return (res,GenAutoReseed aNew b' rs 0)- Left err -> Left err- Right (res,aNew) -> do- gNew <- if (cnt + req) > rs- then do - (ent,b') <- genBytes (genSeedLength `for` a) b- a' <- reseed ent aNew- return (GenAutoReseed a' b' rs 0)- else return $ GenAutoReseed aNew b rs (cnt + req)- return (res, gNew)- genBytesWithEntropy req entropy (GenAutoReseed a b rs cnt) = do- case genBytesWithEntropy req entropy a of- Left NeedReseed -> do- (ent,b') <- genBytes (genSeedLength `for` a) b- a' <- reseed ent a- (res, aNew) <- genBytesWithEntropy req entropy a'- return (res,GenAutoReseed aNew b' rs 0)- Left err -> Left err- Right (res,aNew) -> do- gNew <- if (cnt + req) > rs- then do - (ent,b') <- genBytes (genSeedLength `for` a) b- a' <- reseed ent aNew- return (GenAutoReseed a' b' rs 0)- else return $ GenAutoReseed aNew b rs (cnt + req)- return (res, gNew)- reseed ent gen@(GenAutoReseed a b rs _) - | genSeedLength `for` gen > B.length ent = Left NotEnoughEntropy- | otherwise = do- let (e1,e2) = B.splitAt (genSeedLength `for` a) ent- a' <- reseed e1 a- b' <- if B.length e2 /= 0- then reseed e2 b- else return b- return $ GenAutoReseed a' b' rs 0+ {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HmacDRBG HmacDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HashDRBG HashDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HashDRBG HmacDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenAutoReseed HmacDRBG HashDRBG) #-}+ newGen bs = newGenAutoReseed bs (2^15)+ newGenIO = newGenAutoReseedIO (2^15)+ genSeedLength =+ let a = helper1 res+ b = helper2 res+ res = Tagged $ genSeedLength `for` a + genSeedLength `for` b+ in res+ genBytes req (GenAutoReseed a b rs cnt) =+ case genBytes req a of+ Left NeedReseed -> do+ (ent,b') <- genBytes (genSeedLength `for` a) b+ a' <- reseed ent a+ (res, aNew) <- genBytes req a'+ return (res,GenAutoReseed aNew b' rs 0)+ Left err -> Left err+ Right (res,aNew) -> do+ gNew <- if (cnt + req) > rs+ then do + (ent,b') <- genBytes (genSeedLength `for` a) b+ a' <- reseed ent aNew+ return (GenAutoReseed a' b' rs 0)+ else return $ GenAutoReseed aNew b rs (cnt + req)+ return (res, gNew)+ genBytesWithEntropy req entropy (GenAutoReseed a b rs cnt) = do+ case genBytesWithEntropy req entropy a of+ Left NeedReseed -> do+ (ent,b') <- genBytes (genSeedLength `for` a) b+ a' <- reseed ent a+ (res, aNew) <- genBytesWithEntropy req entropy a'+ return (res,GenAutoReseed aNew b' rs 0)+ Left err -> Left err+ Right (res,aNew) -> do+ gNew <- if (cnt + req) > rs+ then do + (ent,b') <- genBytes (genSeedLength `for` a) b+ a' <- reseed ent aNew+ return (GenAutoReseed a' b' rs 0)+ else return $ GenAutoReseed aNew b rs (cnt + req)+ return (res, gNew)+ reseed ent gen@(GenAutoReseed a b rs _) + | genSeedLength `for` gen > B.length ent = Left NotEnoughEntropy+ | otherwise = do+ let (e1,e2) = B.splitAt (genSeedLength `for` a) ent+ a' <- reseed e1 a+ b' <- if B.length e2 /= 0+ then reseed e2 b+ else return b+ return $ GenAutoReseed a' b' rs 0 -- |@g :: GenXor a b@ generates bytes with sub-generators a and b -- and exclusive-or's the outputs to produce the resulting bytes.@@ -281,40 +306,40 @@ helperXor2 = const undefined instance (CryptoRandomGen a, CryptoRandomGen b) => CryptoRandomGen (GenXor a b) where- {-# SPECIALIZE instance CryptoRandomGen (GenXor HmacDRBG HmacDRBG) #-}- {-# SPECIALIZE instance CryptoRandomGen (GenXor HashDRBG HmacDRBG) #-}- {-# SPECIALIZE instance CryptoRandomGen (GenXor HmacDRBG HashDRBG) #-}- {-# SPECIALIZE instance CryptoRandomGen (GenXor HashDRBG HashDRBG) #-}- newGen bs = do- let g1 = newGen b1- g2 = newGen b2- (b1,b2) = B.splitAt (genSeedLength `for` fromRight g1) bs- fromRight (Right x) = x- a <- g1- b <- g2- return (GenXor a b)- newGenIO = do- a <- newGenIO- b <- newGenIO- return (GenXor a b)- genSeedLength =- let a = helperXor1 res- b = helperXor2 res- res = Tagged $ (genSeedLength `for` a) + (genSeedLength `for` b)- in res- genBytes req (GenXor a b) = do- (r1, a') <- genBytes req a- (r2, b') <- genBytes req b- return (zwp' r1 r2, GenXor a' b')- genBytesWithEntropy req ent (GenXor a b) = do- (r1, a') <- genBytesWithEntropy req ent a- (r2, b') <- genBytesWithEntropy req ent b- return (zwp' r1 r2, GenXor a' b')- reseed ent (GenXor a b) = do- let (b1, b2) = B.splitAt (genSeedLength `for` a) ent- a' <- reseed b1 a- b' <- reseed b2 b- return (GenXor a' b')+ {-# SPECIALIZE instance CryptoRandomGen (GenXor HmacDRBG HmacDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenXor HashDRBG HmacDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenXor HmacDRBG HashDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenXor HashDRBG HashDRBG) #-}+ newGen bs = do+ let g1 = newGen b1+ g2 = newGen b2+ (b1,b2) = B.splitAt (genSeedLength `for` fromRight g1) bs+ fromRight (Right x) = x+ a <- g1+ b <- g2+ return (GenXor a b)+ newGenIO = do+ a <- newGenIO+ b <- newGenIO+ return (GenXor a b)+ genSeedLength =+ let a = helperXor1 res+ b = helperXor2 res+ res = Tagged $ (genSeedLength `for` a) + (genSeedLength `for` b)+ in res+ genBytes req (GenXor a b) = do+ (r1, a') <- genBytes req a+ (r2, b') <- genBytes req b+ return (zwp' r1 r2, GenXor a' b')+ genBytesWithEntropy req ent (GenXor a b) = do+ (r1, a') <- genBytesWithEntropy req ent a+ (r2, b') <- genBytesWithEntropy req ent b+ return (zwp' r1 r2, GenXor a' b')+ reseed ent (GenXor a b) = do+ let (b1, b2) = B.splitAt (genSeedLength `for` a) ent+ a' <- reseed b1 a+ b' <- reseed b2 b+ return (GenXor a' b') -- |@g :: GenBuffered a@ is a generator of type @a@ that attempts to -- maintain a buffer of random values size >= 1MB and <= 5MB at any time.@@ -343,17 +368,17 @@ return gBuf instance (CryptoRandomGen g) => CryptoRandomGen (GenBuffered g) where- {-# SPECIALIZE instance CryptoRandomGen (GenBuffered HmacDRBG) #-}- {-# SPECIALIZE instance CryptoRandomGen (GenBuffered HashDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenBuffered HmacDRBG) #-}+ {-# SPECIALIZE instance CryptoRandomGen (GenBuffered HashDRBG) #-} newGen = newGenBuffered bufferMinDef bufferMaxDef newGenIO = newGenBufferedIO bufferMinDef bufferMaxDef- genSeedLength =- let a = help res- res = Tagged $ genSeedLength `for` a- in res- where- help :: Tagged (GenBuffered g) c -> g- help = const undefined+ genSeedLength =+ let a = help res+ res = Tagged $ genSeedLength `for` a+ in res+ where+ help :: Tagged (GenBuffered g) c -> g+ help = const undefined genBytes req gb@(GenBuffered min max g bs) | remSize >= min = Right (B.take req bs, GenBuffered min max g (B.drop req bs)) | B.length bs < min =@@ -392,8 +417,8 @@ eval (Left x) = Left x eval (Right (g,bs)) = bs `seq` (g `seq` (Right (g, bs))) --- |A random number generator using AES128 in ctr mode.-type GenAES = GenCounter AES128+-- |A random number generator using AESKey in ctr mode.+type GenAES = GenCounter AESKey -- |@GenCounter k@ is a cryptographic BlockCipher with key @k@ -- being used in 'ctr' mode to generate random bytes.@@ -401,30 +426,30 @@ instance BlockCipher x => CryptoRandomGen (GenCounter x) where newGen bytes =- let kl = keyLength- in case buildKey (B.take (untag kl `div` 8) bytes) of- Nothing -> Left NotEnoughEntropy- Just x -> Right (GenCounter 0 (x `asTaggedTypeOf` kl) zeroIV)+ let kl = keyLength+ in case buildKey (B.take (untag kl `div` 8) bytes) of+ Nothing -> Left NotEnoughEntropy+ Just x -> Right (GenCounter 0 (x `asTaggedTypeOf` kl) zeroIV) newGenIO = do- let b = keyLength- kd <- getEntropy ((untag b + 7) `div` 8)- case buildKey kd of- Nothing -> error "Failed to generate key for GenCounter"- Just k -> return $ GenCounter 0 (k `asTaggedTypeOf` b) zeroIV+ let b = keyLength+ kd <- getEntropy ((untag b + 7) `div` 8)+ case buildKey kd of+ Nothing -> error "Failed to generate key for GenCounter"+ Just k -> return $ GenCounter 0 (k `asTaggedTypeOf` b) zeroIV genSeedLength =- let rt :: Tagged x Int -> Tagged (GenCounter x) Int- rt = Tagged . (`div` 8) . unTagged- in rt keyLength+ let rt :: Tagged x Int -> Tagged (GenCounter x) Int+ rt = Tagged . (`div` 8) . unTagged+ in rt keyLength -- If this is called for less than blockSize data genBytes req (GenCounter rs k counter) =- let bs = B.replicate (req' * blkSz) 0- blkSz = blockSizeBytes `for` k- (rnd,iv) = ctr' incIV k counter bs- req' = (req + blkSz - 1) `div` blkSz- in if rs >= 2^48- then Left NeedReseed- else Right (B.take req rnd, GenCounter (rs+1) k iv)+ let bs = B.replicate (req' * blkSz) 0+ blkSz = blockSizeBytes `for` k+ (rnd,iv) = ctr' incIV k counter bs+ req' = (req + blkSz - 1) `div` blkSz+ in if rs >= 2^48+ then Left NeedReseed+ else Right (B.take req rnd, GenCounter (rs+1) k iv) reseed bs (GenCounter _ k _) = newGen (xorExtendBS (encode k) bs)
DRBG.cabal view
@@ -1,16 +1,17 @@-name: DRBG-version: 0.2.3-license: BSD3-license-file: LICENSE-author: Thomas DuBuisson <thomas.dubuisson@gmail.com>-maintainer: Thomas DuBuisson-description: Cryptographically secure RNGs-synopsis: Deterministic random bit generator (aka RNG, PRNG) based HMACs, Hashes, and Ciphers. -category: Cryptography-stability: stable-build-type: Simple-cabal-version: >= 1.6-tested-with: GHC == 6.10.1+name: DRBG+version: 0.3+license: BSD3+license-file: LICENSE+author: Thomas DuBuisson <thomas.dubuisson@gmail.com>+maintainer: Thomas DuBuisson+description: Cryptographically secure RNGs+synopsis: Deterministic random bit generator (aka RNG, PRNG) based + HMACs, Hashes, and Ciphers. +category: Cryptography+stability: stable+build-type: Simple+cabal-version: >= 1.6+tested-with: GHC == 6.10.1 Data-Files: Test/HMAC_DRBG.txt Test/Hash_DRBG.txt Test/CTR_DRBG.txt Test/Dual_EC_DRBG.txt CHANGELOG extra-source-files: Test/HMAC_DRBG.txt Test/Hash_DRBG.txt Test/CTR_DRBG.txt Test/Dual_EC_DRBG.txt@@ -23,10 +24,13 @@ Build-Depends: base >= 4.0 && < 5, cereal >= 0.2, bytestring, prettyclass, tagged >= 0.2, crypto-api >= 0.6, cryptohash >= 0.6.1, parallel, mtl >= 2.0,- cryptocipher, entropy+ cipher-aes128, entropy ghc-options: -O2 hs-source-dirs:- exposed-modules: Crypto.Random.DRBG.Hash, Crypto.Random.DRBG.HMAC, Crypto.Random.DRBG, Crypto.Random.DRBG.Types+ exposed-modules: Crypto.Random.DRBG.Hash,+ Crypto.Random.DRBG.HMAC,+ Crypto.Random.DRBG,+ Crypto.Random.DRBG.Types other-modules: Crypto.Random.DRBG.HashDF Crypto.Random.DRBG.Util Executable drbg_test