{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE CPP #-} module Raaz.Core.Random ( PRG(..), Random(..) #ifdef HAVE_SYSTEM_PRG , SystemPRG #endif ) where import Control.Applicative import Control.Monad (void) import Data.Word import Foreign.Ptr (castPtr) import Foreign.Storable(Storable, peek) import System.IO ( openBinaryFile, Handle, IOMode(ReadMode) , BufferMode(NoBuffering), hSetBuffering ) import Raaz.Core.ByteSource(InfiniteSource, slurpBytes) import Raaz.Core.Types -- | The class that captures pseudo-random generators. Essentially the -- a pseudo-random generator (PRG) is a byte sources that can be -- seeded. class InfiniteSource prg => PRG prg where -- | Associated type that captures the seed for the PRG. type Seed prg :: * -- | Creates a new pseudo-random generators newPRG :: Seed prg -> IO prg -- | Re-seeding the prg. reseed :: Seed prg -> prg -> IO () -- | Stuff that can be generated by a pseudo-random generator. class Random r where random :: PRG prg => prg -> IO r default random :: (PRG prg, Storable r) => prg -> IO r random = go undefined where go :: (PRG prg, Storable a) => a -> prg -> IO a go w prg = let sz = byteSize w in allocaBuffer sz $ \ ptr -> do void $ slurpBytes sz prg ptr peek $ castPtr ptr instance Random Word instance Random Word16 instance Random Word32 instance Random Word64 instance Random w => Random (LE w) where random = fmap littleEndian . random instance Random w => Random (BE w) where random = fmap bigEndian . random instance (Random a, Random b) => Random (a,b) where random prg = (,) <$> random prg <*> random prg instance (Random a, Random b, Random c) => Random (a,b,c) where random prg = (,,) <$> random prg <*> random prg <*> random prg #ifdef HAVE_SYSTEM_PRG -- | The system wide pseudo-random generator. The source is expected -- to be of high quality, albeit a bit slow due to system call -- overheads. It is expected that this source is automatically seeded -- from the entropy pool maintained by the platform. Hence, it is -- neither necessary nor possible to seed this generator which -- reflected by the fact that the associated type @`Seed` `SystemPRG`@ -- is the unit type @()@. #endif -- Currently only POSIX platforms are supported where the file -- @\/dev\/urandom@ acts as the underlying randomness source. -- -- TODO: Support other platforms. -- #ifdef HAVE_DEV_URANDOM newtype SystemPRG = SystemPRG Handle instance InfiniteSource SystemPRG where slurpBytes sz sprg@(SystemPRG hand) cptr = hFillBuf hand cptr sz >> return sprg instance PRG SystemPRG where type Seed SystemPRG = () newPRG _ = do h <- openBinaryFile "/dev/urandom" ReadMode hSetBuffering h NoBuffering return $ SystemPRG h reseed _ _ = return () #endif