crypto-random-effect 0.1.1 → 0.2.0
raw patch · 2 files changed
+70/−33 lines, 2 files
Files
- crypto-random-effect.cabal +1/−1
- src/Crypto/Random/Effect.hs +69/−32
crypto-random-effect.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: crypto-random-effect-version: 0.1.1+version: 0.2.0 synopsis: A random effect using crypto-random description: Any help (especially documentation) is welcome homepage: https://github.com/ibotty/crypto-random-effect
src/Crypto/Random/Effect.hs view
@@ -1,16 +1,17 @@+-- | An effect that can generate random bytes.+--+-- It is essentially a 'State' monad with a given 'CPRG'. {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeOperators #-}--- | A Random effect.------ Any ideas to let the user specify the random number generator ('C.CPRG')--- instead of hardcoding 'C.SystemRNG' without complicating the api and--- reinventing state as 'SetMember' is very welcome. module Crypto.Random.Effect- ( RNG()- , runRNG+ ( RNG+ , runSystemRNG , runRNGWithPool+ , runRNG , withRNG , withRNGIO , rngFork@@ -20,10 +21,10 @@ , createEntropyPool , grabEntropy , unsafeGrabEntropy- -- | reexports from 'crypto-random'- , C.CPRG()- , C.SystemRNG()- , C.EntropyPool()+ -- * Re-exports+ , CPRG()+ , SystemRNG()+ , EntropyPool() ) where import Control.Eff@@ -33,58 +34,94 @@ import Data.ByteString (ByteString) import Data.SecureMem (SecureMem) import Data.Typeable (Typeable)+import Crypto.Random (CPRG, EntropyPool, SystemRNG) import qualified Crypto.Random as C -type RNG = State C.SystemRNG+-- | Type marker to ensure that there is only one RNG.+data RNG -deriving instance Typeable C.SystemRNG-deriving instance Typeable C.EntropyPool+instance SetMember RNG (State gen) (State gen :> a) --- | Run the effect.-runRNG+deriving instance Typeable SystemRNG+deriving instance Typeable EntropyPool++-- | Run the effect using 'SystemRNG'.+runSystemRNG :: SetMember Lift (Lift IO) r- => Eff (RNG :> Reader C.EntropyPool :> r) a -> Eff r a-runRNG e = lift C.createEntropyPool >>= flip runRNGWithPool e+ => Eff (State SystemRNG :> Reader EntropyPool :> r) a -> Eff r a+runSystemRNG = runRNG +-- | Run the effect without specifying the 'CPRG'.+--+-- This is only useful when the type of the 'CPRG' is bound by an explicit+-- type annotation (see 'runSystemRNG' which is 'runRNG' with bound type)+-- or any function within the effect binds it.+runRNG :: (SetMember Lift (Lift IO) r, Typeable gen, CPRG gen)+ => Eff (State gen :> Reader EntropyPool :> r) a -> Eff r a+runRNG e = createEntropyPool >>= flip runRNGWithPool e++-- | Run the effect with a given 'EntropyPool'. runRNGWithPool- :: SetMember Lift (Lift IO) r- => C.EntropyPool -> Eff (RNG :> Reader C.EntropyPool :> r) a -> Eff r a+ :: (SetMember Lift (Lift IO) r, Typeable gen, CPRG gen)+ => EntropyPool -> Eff (State gen :> Reader EntropyPool :> r) a -> Eff r a runRNGWithPool pool = flip runReader pool . evalState (C.cprgCreate pool) -withRNG :: Member RNG r => (C.SystemRNG -> Eff r (a, C.SystemRNG)) -> Eff r a+-- | Wrap an effect that uses the 'CPRG' directly.+withRNG :: (SetMember RNG (State gen) r, Typeable gen) => (gen -> Eff r (a, gen)) -> Eff r a withRNG f = do rng <- get (a, rng') <- f rng put rng' return a -withRNGPure :: Member RNG r => (C.SystemRNG -> (a, C.SystemRNG)) -> Eff r a+-- | Wrap a pure function that uses the 'CPRG' directly.+withRNGPure :: (SetMember RNG (State gen) r, Typeable gen) => (gen -> (a, gen)) -> Eff r a withRNGPure f = withRNG (return . f) +-- | Wrap an IO action that uses the 'CPRG' directly. withRNGIO- :: (SetMember Lift (Lift IO) r, Member RNG r)- => (C.SystemRNG -> IO (a, C.SystemRNG)) -> Eff r a+ :: (SetMember Lift (Lift IO) r, SetMember RNG (State gen) r, Typeable gen)+ => (gen -> IO (a, gen)) -> Eff r a withRNGIO f = withRNG (lift . f) -rngFork :: Member RNG r => Eff r C.SystemRNG+-- | Fork a CPRG into a new independent CPRG.+--+-- As entropy is mixed to generate safely a new generator, 2 calls with the+-- same CPRG will not produce the same output.+rngFork :: (SetMember RNG (State gen) r, CPRG gen, Typeable gen) => Eff r gen rngFork = withRNGPure C.cprgFork -randomBytes :: Member RNG r => Int -> Eff r ByteString+-- | Generate a number of bytes using the CPRG.+randomBytes :: (SetMember RNG (State gen) r, CPRG gen, Typeable gen) => Int -> Eff r ByteString randomBytes = withRNGPure . C.cprgGenerate -randomBytesWithEntropy :: Member RNG r => Int -> Eff r ByteString+-- | Similar to 'randomBytes' except that the random data is mixed with pure+-- entropy, so the result is not reproducible after use, but it provides+-- more guarantee, theorically speaking, in term of the randomness+-- generated.+randomBytesWithEntropy :: (SetMember RNG (State gen) r, CPRG gen, Typeable gen) => Int -> Eff r ByteString randomBytesWithEntropy = withRNGPure . C.cprgGenerateWithEntropy -withRandomBytes :: Member RNG r => Int -> (ByteString -> Eff r a) -> Eff r a-withRandomBytes cnt f = randomBytes cnt >>= f+-- | Consume a number of random bytes with a pure function.+--+-- Note, that this is simply+--+-- > randomBytes cnt >>= return . f+withRandomBytes :: (SetMember RNG (State gen) r, CPRG gen, Typeable gen) => Int -> (ByteString -> a) -> Eff r a+withRandomBytes cnt f = randomBytes cnt >>= return . f -createEntropyPool :: SetMember Lift (Lift IO) r => Eff r C.EntropyPool+-- Create a new entropy pool.+createEntropyPool :: SetMember Lift (Lift IO) r => Eff r EntropyPool createEntropyPool = lift C.createEntropyPool +-- | Grab a chunk of entropy from the entropy pool. grabEntropy- :: (SetMember Lift (Lift IO) r, Member (Reader C.EntropyPool) r)+ :: (SetMember Lift (Lift IO) r, Member (Reader EntropyPool) r) => Int -> Eff r SecureMem grabEntropy cnt = ask >>= lift . C.grabEntropyIO cnt -unsafeGrabEntropy :: Member (Reader C.EntropyPool) r => Int -> Eff r SecureMem+-- | Grab a chunk of entropy from the entropy pool.+--+-- Beware: uses unsafePerformIO under the hood.+unsafeGrabEntropy :: Member (Reader EntropyPool) r => Int -> Eff r SecureMem unsafeGrabEntropy cnt = fmap (C.grabEntropy cnt) ask