-- |
-- Module      : Crypto.Random.Entropy.Unsafe
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good
--
module Crypto.Random.Entropy.Unsafe
    ( replenish
    , module Crypto.Random.Entropy.Backend
    ) where

import Data.Word (Word8)
import Foreign.Ptr (Ptr, plusPtr)
import Crypto.Random.Entropy.Backend

-- | Refill the entropy in a buffer
--
-- Call each entropy backend in turn until the buffer has
-- been replenished.
--
-- If the buffer cannot be refill after 3 loopings, this will raise
-- an User Error exception
replenish :: Int -> [EntropyBackend] -> Ptr Word8 -> IO ()
replenish :: Int -> [EntropyBackend] -> Ptr Word8 -> IO ()
replenish Int
_        []       Ptr Word8
_   = String -> IO ()
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"cryptonite: random: cannot get any source of entropy on this system"
replenish Int
poolSize [EntropyBackend]
backends Ptr Word8
ptr = Int -> [EntropyBackend] -> Ptr Word8 -> Int -> IO ()
loop Int
0 [EntropyBackend]
backends Ptr Word8
ptr Int
poolSize
  where loop :: Int -> [EntropyBackend] -> Ptr Word8 -> Int -> IO ()
        loop :: Int -> [EntropyBackend] -> Ptr Word8 -> Int -> IO ()
loop Int
retry [] Ptr Word8
p Int
n | Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0     = () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
                          | Int
retry Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
3 = String -> IO ()
forall a. HasCallStack => String -> a
error String
"cryptonite: random: cannot fully replenish"
                          | Bool
otherwise  = Int -> [EntropyBackend] -> Ptr Word8 -> Int -> IO ()
loop (Int
retryInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) [EntropyBackend]
backends Ptr Word8
p Int
n
        loop Int
_     (EntropyBackend
_:[EntropyBackend]
_)  Ptr Word8
_ Int
0 = () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
        loop Int
retry (EntropyBackend
b:[EntropyBackend]
bs) Ptr Word8
p Int
n = do
            Int
r <- EntropyBackend -> Ptr Word8 -> Int -> IO Int
gatherBackend EntropyBackend
b Ptr Word8
p Int
n
            Int -> [EntropyBackend] -> Ptr Word8 -> Int -> IO ()
loop Int
retry [EntropyBackend]
bs (Ptr Word8
p Ptr Word8 -> Int -> Ptr Word8
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
r) (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
r)