-- |
-- Module      : Crypto.Random
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good
--
-- Provide a safe abstraction for cryptographic pseudo
-- random generator.
--
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Crypto.Random
    (
    -- * Entropy
      EntropyPool
    , createEntropyPool
    , grabEntropy
    , grabEntropyIO
    -- * Random generation
    , CPRG(..)
    , withRandomBytes
    -- * System generator
    , SystemRNG
    -- * Testing and mocking
    , createTestEntropyPool
    ) where

import Crypto.Random.Entropy
import Crypto.Random.Generator
import Data.ByteString (ByteString)
import Data.Typeable (Typeable)
import qualified Data.ByteString.Internal as B (unsafeCreate)

-- | System entropy generator.
--
-- This generator doesn't use the entropy reseed level, as the only bytes
-- generated are comping from the entropy pool already.
--
-- This generator doesn't create reproducible output, and might be difficult to
-- use for testing and debugging purpose, but otherwise for real world use case
-- should be fine.
data SystemRNG = SystemRNG EntropyPool
  deriving Typeable

instance CPRG SystemRNG where
    cprgCreate :: EntropyPool -> SystemRNG
cprgCreate entPool :: EntropyPool
entPool                   = EntropyPool -> SystemRNG
SystemRNG EntropyPool
entPool
    cprgSetReseedThreshold :: Int -> SystemRNG -> SystemRNG
cprgSetReseedThreshold _ r :: SystemRNG
r           = SystemRNG
r
    cprgFork :: SystemRNG -> (SystemRNG, SystemRNG)
cprgFork r :: SystemRNG
r@(SystemRNG entPool :: EntropyPool
entPool)       = (SystemRNG
r, EntropyPool -> SystemRNG
forall gen. CPRG gen => EntropyPool -> gen
cprgCreate EntropyPool
entPool)
    cprgGenerate :: Int -> SystemRNG -> (ByteString, SystemRNG)
cprgGenerate n :: Int
n g :: SystemRNG
g@(SystemRNG entPool :: EntropyPool
entPool) = (Int -> (Ptr Word8 -> IO ()) -> ByteString
B.unsafeCreate Int
n (Int -> EntropyPool -> Ptr Word8 -> IO ()
grabEntropyPtr Int
n EntropyPool
entPool), SystemRNG
g)
    -- we don't need to do anything different when generating withEntropy, as the generated
    -- bytes are already stricly entropy bytes.
    cprgGenerateWithEntropy :: Int -> SystemRNG -> (ByteString, SystemRNG)
cprgGenerateWithEntropy n :: Int
n g :: SystemRNG
g          = Int -> SystemRNG -> (ByteString, SystemRNG)
forall gen. CPRG gen => Int -> gen -> (ByteString, gen)
cprgGenerate Int
n SystemRNG
g

-- | generate @len random bytes and mapped the bytes to the function @f.
--
-- This is equivalent to use Control.Arrow 'first' with 'cprgGenerate'
withRandomBytes :: CPRG g => g -> Int -> (ByteString -> a) -> (a, g)
withRandomBytes :: g -> Int -> (ByteString -> a) -> (a, g)
withRandomBytes rng :: g
rng len :: Int
len f :: ByteString -> a
f = (ByteString -> a
f ByteString
bs, g
rng')
  where (bs :: ByteString
bs, rng' :: g
rng') = Int -> g -> (ByteString, g)
forall gen. CPRG gen => Int -> gen -> (ByteString, gen)
cprgGenerate Int
len g
rng