packages feed

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 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