packages feed

cprng-aes 0.4.0 → 0.5.0

raw patch · 4 files changed

+145/−130 lines, 4 filesdep +byteabledep +crypto-randomdep −cerealdep −crypto-random-apidep −entropyPVP ok

version bump matches the API change (PVP)

Dependencies added: byteable, crypto-random

Dependencies removed: cereal, crypto-random-api, entropy

API changes (from Hackage documentation)

- Crypto.Random.AESCtr: genRandomBytes :: CPRG g => Int -> g -> (ByteString, g)
- Crypto.Random.AESCtr: make :: ByteString -> Maybe AESRNG
+ Crypto.Random.AESCtr: make :: EntropyPool -> AESRNG

Files

Benchmarks/Benchmarks.hs view
@@ -4,14 +4,15 @@  import qualified Data.ByteString as B import Crypto.Random.AESCtr+import Crypto.Random import System.IO.Unsafe (unsafePerformIO) import Data.IORef -gen rng n = fst (genRandomBytes n rng)+gen rng n = fst (cprgGenerate n rng)  gen2 rngref n = unsafePerformIO $ do     rng <- readIORef rngref-    let (b, rng2) = genRandomBytes n rng+    let (b, rng2) = cprgGenerate n rng     writeIORef rngref rng2     return b 
Crypto/Random/AESCtr.hs view
@@ -13,128 +13,52 @@ --   aes (IV `xor` counter) -> 16 bytes output -- {-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-} module Crypto.Random.AESCtr     ( AESRNG     , make     , makeSystem-    , genRandomBytes     ) where -import Control.Applicative ((<$>))--import Crypto.Random.API-+import Crypto.Random import System.Random (RandomGen(..))-import System.Entropy (getEntropy)-import qualified Crypto.Cipher.AES as AES+import Crypto.Random.AESCtr.Internal+import Control.Arrow (second)  import Data.ByteString (ByteString) import qualified Data.ByteString as B -import Data.Maybe (fromJust)-import Data.Word+import Data.Byteable import Data.Bits (xor, (.&.)) -#ifdef USE_CEREAL-import Data.Serialize-#else-import Foreign.Ptr--#if __GLASGOW_HASKELL__ > 704-import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)-#else-import Foreign.ForeignPtr (unsafeForeignPtrToPtr)-#endif--import Foreign.Storable-import qualified Data.ByteString.Internal as B-#endif--data Word128 = Word128 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64--{-| An opaque object containing an AES CPRNG -}-data RNG = RNG-    {-# UNPACK #-} !Word128-    {-# UNPACK #-} !Word128-    {-# UNPACK #-} !Word64-    {-# UNPACK #-} !AES.AES--data AESRNG = AESRNG { aesrngState :: RNG-                     , aesrngCache :: ByteString }+data AESRNG = AESRNG { aesrngState     :: RNG+                     , aesrngEntropy   :: EntropyPool+                     , aesrngThreshold :: Int -- ^ in number of generated block+                     , aesrngCache     :: ByteString }  instance Show AESRNG where     show _ = "aesrng[..]" --- using serialize to grab a w128 as a non-negligeable cost,--- the Bytestring pointer manipulation are much faster.-#if USE_CEREAL--put128 :: Word128 -> ByteString-put128 (Word128 a b) = runPut (putWord64host a >> putWord64host b)--get128 :: ByteString -> Word128-get128 = either (\_ -> Word128 0 0) id . runGet (getWord64host >>= \a -> (getWord64host >>= \b -> return $ Word128 a b))--#else--put128 :: Word128 -> ByteString-put128 (Word128 a b) = B.unsafeCreate 16 (write64 . castPtr)-    where write64 :: Ptr Word64 -> IO ()-          write64 ptr = poke ptr a >> poke (ptr `plusPtr` 8) b--get128 :: ByteString -> Word128-get128 (B.PS ps s _) = B.inlinePerformIO $ do-    let ptr = castPtr (unsafeForeignPtrToPtr ps `plusPtr` s) :: Ptr Word64-    a <- peek ptr-    b <- peek (ptr `plusPtr` 8)-    return $ Word128 a b-#endif--xor128 :: Word128 -> Word128 -> Word128-xor128 (Word128 a1 b1) (Word128 a2 b2) = Word128 (a1 `xor` a2) (b1 `xor` b2)--add64 :: Word128 -> Word128-add64 (Word128 a b) = if b >= (0xffffffffffffffff-63) then Word128 (a+1) (b+64) else Word128 a (b+64)--makeParams :: ByteString -> (AES.AES, ByteString, ByteString)-makeParams b = (key, cnt, iv)-    where-        key          = AES.initAES $ B.take 32 left2-        (cnt, left2) = B.splitAt 16 left1-        (iv, left1)  = B.splitAt 16 b+makeFrom :: EntropyPool -> B.ByteString -> AESRNG+makeFrom entPool b = AESRNG+    { aesrngState        = rng+    , aesrngEntropy      = entPool+    , aesrngThreshold    = 1024 -- in blocks generated, so 1mb+    , aesrngCache        = B.empty }+  where rng            = RNG (get128 iv) (get128 cnt) 0 key+        (key, cnt, iv) = makeParams b --- | make an AES RNG from a bytestring seed. the bytestring need to be at least 64 bytes.--- if the bytestring is longer, the extra bytes will be ignored and will not take part in--- the initialization.+-- | make an AES RNG from an EntropyPool. ----- use `makeSystem` to not have to deal with the generator seed.-make :: B.ByteString -> Maybe AESRNG-make b-    | B.length b < 64 = Nothing-    | otherwise       = Just $ AESRNG { aesrngState = rng, aesrngCache = B.empty }-        where-            rng            = RNG (get128 iv) (get128 cnt) 0 key-            (key, cnt, iv) = makeParams b--chunkSize :: Int-chunkSize = 1024--genNextChunk :: RNG -> (ByteString, RNG)-genNextChunk (RNG iv counter sz key) = (chunk, newrng)-    where-        newrng = RNG (get128 chunk) (add64 counter) (sz+fromIntegral chunkSize) key-        chunk  = AES.genCTR key bytes 1024-        bytes  = put128 (iv `xor128` counter)--getRNGReseedLimit :: RNG -> Int-getRNGReseedLimit (RNG _ _ sz _)-    | sz >= limit = 0-    | otherwise   = fromIntegral (limit - sz)-    where limit = 2^(24 :: Int)+-- use `makeSystem` to not have to deal with the entropy pool.+make :: EntropyPool -> AESRNG+make entPool = makeFrom entPool b+  where !b = toBytes $ grabEntropy 64 entPool  -- | Initialize a new AES RNG using the system entropy.+-- {-# DEPRECATED makeSystem "use cprgCreate with an entropy pool" #-} makeSystem :: IO AESRNG-makeSystem = fromJust . make <$> getEntropy 64+makeSystem = make `fmap` createEntropyPool  -- | get a Random number of bytes from the RNG. -- it generate randomness by block of chunkSize bytes and will returns@@ -144,30 +68,47 @@     | n <= chunkSize = genNextChunk rng     | otherwise      = let (bs, rng') = acc 0 [] rng                         in (B.concat bs, rng')-    where-        acc l bs g+  where acc l bs g             | l * chunkSize >= n = (bs, g)             | otherwise          = let (b, g') = genNextChunk g                                     in acc (l+1) (b:bs) g' -genRanBytes :: AESRNG -> Int -> (ByteString, AESRNG)-genRanBytes rng n+genRanBytesNoCheck :: AESRNG -> Int -> (ByteString, AESRNG)+genRanBytesNoCheck rng n     | B.length (aesrngCache rng) >= n = let (b1,b2) = B.splitAt n (aesrngCache rng)                                          in (b1, rng { aesrngCache = b2 })     | otherwise                       =-            let (b, rng') = genRandomBytesState (aesrngState rng) n-                (b1, b2)  = B.splitAt n b-             in (b1, rng { aesrngState = rng', aesrngCache = b2 })+        let (b, rng') = genRandomBytesState (aesrngState rng) n+            (b1, b2)  = B.splitAt n b+         in (b1, rng { aesrngState = rng', aesrngCache = b2 }) -reseedState :: ByteString -> RNG -> RNG-reseedState b rng@(RNG _ cnt1 _ _) = RNG (get128 r16 `xor128` get128 iv2) (cnt1 `xor128` get128 cnt2) 0 key2-    where (r16, _)          = genNextChunk rng-          (key2, cnt2, iv2) = makeParams b+-- | generate a random set of bytes+genRanBytes :: AESRNG -> Int -> (ByteString, AESRNG)+genRanBytes rng n = second reseedThreshold $ genRanBytesNoCheck rng n +reseedThreshold :: AESRNG -> AESRNG+reseedThreshold rng+    | getNbChunksGenerated (aesrngState rng) >= lvl =+         let ent = toBytes $ grabEntropy 64 (aesrngEntropy rng)+          in rng { aesrngState = reseedState ent (aesrngState rng) }+    | otherwise  = rng+  where+        lvl = aesrngThreshold rng+        reseedState :: ByteString -> RNG -> RNG+        reseedState b g@(RNG _ cnt1 _ _) = RNG (get128 r16 `xor128` get128 iv2) (cnt1 `xor128` get128 cnt2) 0 key2+            where (r16, _)          = genNextChunk g+                  (key2, cnt2, iv2) = makeParams b+ instance CPRG AESRNG where-    cprgGenBytes len rng          = genRanBytes rng len-    cprgSupplyEntropy entropy rng = rng { aesrngState = reseedState entropy (aesrngState rng) }-    cprgNeedReseed rng            = ReseedInBytes $ fromIntegral $ getRNGReseedLimit (aesrngState rng)+    cprgCreate                      = make+    cprgSetReseedThreshold lvl rng  = reseedThreshold (rng { aesrngThreshold = lvl })+    cprgGenerate len rng            = genRanBytes rng len+    cprgGenerateWithEntropy len rng =+        let ent        = toBytes $ grabEntropy len (aesrngEntropy rng)+            (bs, rng') = genRanBytes rng len+         in (B.pack $ B.zipWith xor ent bs, rng')+    cprgFork rng = let (b,rng') = genRanBytes rng 64+                    in (rng', makeFrom (aesrngEntropy rng) b)  instance RandomGen AESRNG where     next rng =@@ -176,8 +117,6 @@         let n = fromIntegral (a .&. 0x7fffffff) in         (n, rng')     split rng =-        let (bs, rng') = genRanBytes rng 64 in-        case make bs of-            Nothing    -> error "assert"-            Just rng'' -> (rng', rng'')+        let rng' = make (aesrngEntropy rng)+         in (rng, rng')     genRange _ = (0, 0x7fffffff)
+ Crypto/Random/AESCtr/Internal.hs view
@@ -0,0 +1,80 @@+-- |+-- Module      : Crypto.Random.AESCtr.Internal+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : stable+-- Portability : unknown+--+{-# LANGUAGE CPP #-}+module Crypto.Random.AESCtr.Internal where++import qualified Crypto.Cipher.AES as AES++import Data.ByteString (ByteString)+import qualified Data.ByteString as B++import Data.Word+import Data.Bits (xor)++import Foreign.Ptr+#if __GLASGOW_HASKELL__ > 704+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)+#else+import Foreign.ForeignPtr (unsafeForeignPtrToPtr)+#endif++import Foreign.Storable+import qualified Data.ByteString.Internal as B++data Word128 = Word128 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64++{-| An opaque object containing an AES CPRNG -}+data RNG = RNG+    {-# UNPACK #-} !Word128 -- iv+    {-# UNPACK #-} !Word128 -- cnt+    {-# UNPACK #-} !Int     -- number of chunks generated since reseed+    {-# UNPACK #-} !AES.AES -- AES context++getNbChunksGenerated :: RNG -> Int+getNbChunksGenerated (RNG _ _ c _) = c++put128 :: Word128 -> ByteString+put128 (Word128 a b) = B.unsafeCreate 16 (write64 . castPtr)+    where write64 :: Ptr Word64 -> IO ()+          write64 ptr = poke ptr a >> poke (ptr `plusPtr` 8) b++get128 :: ByteString -> Word128+get128 (B.PS ps s _) = B.inlinePerformIO $ do+    let ptr = castPtr (unsafeForeignPtrToPtr ps `plusPtr` s) :: Ptr Word64+    a <- peek ptr+    b <- peek (ptr `plusPtr` 8)+    return $ Word128 a b++xor128 :: Word128 -> Word128 -> Word128+xor128 (Word128 a1 b1) (Word128 a2 b2) = Word128 (a1 `xor` a2) (b1 `xor` b2)++add64 :: Word128 -> Word128+add64 (Word128 a b) = if nb < 64 then Word128 (a+1) nb else Word128 a nb+  where nb = b + 64++{-+withBsPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a+withBsPtr (B.PS ps s _) f = withForeignPtr ps $ \ptr -> f (ptr `plusPtr` s)+-}++makeParams :: ByteString -> (AES.AES, ByteString, ByteString)+makeParams b = (key, cnt, iv)+    where+        key          = AES.initAES $ B.take 32 left2+        (cnt, left2) = B.splitAt 16 left1+        (iv, left1)  = B.splitAt 16 b++chunkSize :: Int+chunkSize = 1024++genNextChunk :: RNG -> (ByteString, RNG)+genNextChunk (RNG iv counter nbChunks key) = (chunk, newrng)+  where+        newrng = RNG (get128 chunk) (add64 counter) (nbChunks+1) key+        chunk  = AES.genCTR key bytes chunkSize+        bytes  = put128 (iv `xor128` counter)
cprng-aes.cabal view
@@ -1,5 +1,5 @@ Name:                cprng-aes-Version:             0.4.0+Version:             0.5.0 Description:              Simple crypto pseudo-random-number-generator with really good randomness property.     .@@ -30,23 +30,17 @@ Homepage:            http://github.com/vincenthz/hs-cprng-aes data-files:          README.md -Flag cereal-  Description:       Use cereal-  Default:           False- Library   Build-Depends:     base >= 3 && < 5                    , bytestring+                   , byteable                    , random-                   , crypto-random-api >= 0.2 && < 0.3-                   , entropy >= 0.2+                   , crypto-random >= 0.0 && < 0.1   Exposed-modules:   Crypto.Random.AESCtr+  Other-modules:     Crypto.Random.AESCtr.Internal   ghc-options:       -Wall    Build-Depends:   cipher-aes >= 0.2 && < 0.3-  if flag(cereal)-    Build-Depends:   cereal >= 0.3.0 && < 0.4.0-    cpp-options:     -DUSE_CEREAL  Benchmark bench-cprng-aes   hs-source-dirs:    Benchmarks@@ -54,6 +48,7 @@   type:              exitcode-stdio-1.0   Build-depends:     base >= 4 && < 5                    , bytestring+                   , crypto-random                    , cprng-aes                    , criterion                    , mtl