module Crypto.Random.EntropyPool
( EntropyPool
, createEntropyPool
, createEntropyPoolWith
, getEntropyFrom
) where
import Control.Concurrent.MVar
import Crypto.Random.Entropy.Unsafe
import Crypto.Internal.ByteArray (ByteArray, ScrubbedBytes)
import qualified Crypto.Internal.ByteArray as B
import Data.Word (Word8)
import Data.Maybe (catMaybes)
import Foreign.Marshal.Utils (copyBytes)
import Foreign.Ptr (plusPtr, Ptr)
data EntropyPool = EntropyPool [EntropyBackend] (MVar Int) ScrubbedBytes
defaultPoolSize :: Int
defaultPoolSize = 4096
createEntropyPoolWith :: Int -> [EntropyBackend] -> IO EntropyPool
createEntropyPoolWith poolSize backends = do
m <- newMVar 0
sm <- B.alloc poolSize (replenish poolSize backends)
return $ EntropyPool backends m sm
createEntropyPool :: IO EntropyPool
createEntropyPool = do
backends <- catMaybes `fmap` sequence supportedBackends
createEntropyPoolWith defaultPoolSize backends
getEntropyPtr :: EntropyPool -> Int -> Ptr Word8 -> IO ()
getEntropyPtr (EntropyPool backends posM sm) n outPtr =
B.withByteArray sm $ \entropyPoolPtr ->
modifyMVar_ posM $ \pos ->
copyLoop outPtr entropyPoolPtr pos n
where poolSize = B.length sm
copyLoop d s pos left
| left == 0 = return pos
| otherwise = do
wrappedPos <-
if pos == poolSize
then replenish poolSize backends s >> return 0
else return pos
let m = min (poolSize - wrappedPos) left
copyBytes d (s `plusPtr` wrappedPos) m
copyLoop (d `plusPtr` m) s (wrappedPos + m) (left - m)
getEntropyFrom :: ByteArray byteArray => EntropyPool -> Int -> IO byteArray
getEntropyFrom pool n = B.alloc n (getEntropyPtr pool n)