-- |
-- 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 Crypto.Random.Entropy.Backend
import Data.Word (Word8)
import Foreign.Ptr (Ptr, plusPtr)

-- | 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 a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"crypton: 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
_ [EntropyBackend]
_ Ptr Word8
_ Int
0 = () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    loop Int
retry [] Ptr Word8
p Int
n
        | Int
retry Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
3 = String -> IO ()
forall a. HasCallStack => String -> a
error String
"crypton: random: cannot fully replenish"
        | Bool
otherwise = Int -> [EntropyBackend] -> Ptr Word8 -> Int -> IO ()
loop (Int
retry Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [EntropyBackend]
backends Ptr Word8
p Int
n
    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)