{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
module Crypto.Random.AESCtr
( AESRNG
, make
, makeSystem
) where
import Crypto.Random
import System.Random (RandomGen(..))
import Crypto.Random.AESCtr.Internal
import Control.Arrow (second)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Byteable
import Data.Bits (xor, (.&.))
data AESRNG = AESRNG { aesrngState :: RNG
, aesrngEntropy :: EntropyPool
, aesrngThreshold :: Int
, aesrngCache :: ByteString }
instance Show AESRNG where
show _ = "aesrng[..]"
makeFrom :: EntropyPool -> B.ByteString -> AESRNG
makeFrom entPool b = AESRNG
{ aesrngState = rng
, aesrngEntropy = entPool
, aesrngThreshold = 1024
, aesrngCache = B.empty }
where rng = RNG (get128 iv) (get128 cnt) 0 key
(key, cnt, iv) = makeParams b
make :: EntropyPool -> AESRNG
make entPool = makeFrom entPool b
where !b = toBytes $ grabEntropy 64 entPool
makeSystem :: IO AESRNG
makeSystem = make `fmap` createEntropyPool
genRandomBytesState :: RNG -> Int -> (ByteString, RNG)
genRandomBytesState rng n
| n <= chunkSize = genNextChunk rng
| otherwise = let (bs, rng') = acc 0 [] rng
in (B.concat bs, rng')
where acc l bs g
| l * chunkSize >= n = (bs, g)
| otherwise = let (b, g') = genNextChunk g
in acc (l+1) (b:bs) g'
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 })
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
cprgCreate = make
cprgSetReseedThreshold lvl rng = reseedThreshold (rng { aesrngThreshold = if nbChunks > 0 then nbChunks else 1 })
where nbChunks = lvl `div` chunkSize
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 =
let (bs, rng') = genRanBytes rng 16 in
let (Word128 a _) = get128 bs in
let n = fromIntegral (a .&. 0x7fffffff) in
(n, rng')
split rng =
let rng' = make (aesrngEntropy rng)
in (rng, rng')
genRange _ = (0, 0x7fffffff)