module Randomization
( Randomization (..)
, deterministicallyRandom
, DeterministicRandomization (..)
, hoistRandomization
, defaultRandomization
) where
import Internal.Prelude
import Crypto.Random (ChaChaDRG, DRG (randomBytesGenerate), drgNew)
import Data.IORef (atomicModifyIORef', newIORef)
-- | General means of obtaining randomness
newtype Randomization m = Randomization
{ getRandomBytes :: Natural -> m ByteString
-- ^ Given a requested number of bytes, this action
-- should produce a 'ByteString' of that length.
}
hoistRandomization
:: (forall a. m a -> m' a) -> Randomization m -> Randomization m'
hoistRandomization f (Randomization g) = Randomization (f . g)
-- | Convert from a deterministic generator to an effectful one
deterministicallyRandom
:: DeterministicRandomization -> IO (Randomization IO)
deterministicallyRandom =
liftIO . newIORef >=> pure . \ref ->
Randomization $ \n ->
liftIO $ atomicModifyIORef' ref $ \(DeterministicRandomization gen) ->
swap $ gen n
-- | A deterministic random generator
newtype DeterministicRandomization = DeterministicRandomization
{ nextRandomBytes :: Natural -> (ByteString, DeterministicRandomization)
-- ^ Given a requested number of bytes, this function should give a
-- 'ByteString' of that length and a new deterministic generator.
}
-- | Cryptographically secure deterministic randomization seeded from
-- system entropy using @ChaChaDRG@ from the @crypton@ package
defaultRandomization :: IO (Randomization IO)
defaultRandomization =
deterministicallyRandom . makeDeterministicRandomization =<< liftIO drgNew
where
makeDeterministicRandomization :: ChaChaDRG -> DeterministicRandomization
makeDeterministicRandomization drg =
DeterministicRandomization $ \n ->
let (bs, drg') = randomBytesGenerate (fromIntegral n) drg
in (bs, makeDeterministicRandomization drg')