{-# LANGUAGE GeneralizedNewtypeDeriving, PackageImports #-} -- | Defines a newtype wrapper for crypto-api's 'CryptoRandomGen', and a corresponding 'RandomGen' instance. -- 'CryptoRandomGen' generators can only be used to get 'ByteString's; the 'RandomGen' instance allows them -- to be used to generate any 'Random' type. Typical usage is with Control.Monad.Random's 'RandT' monad. module Crypto.Lol.Types.Random (CryptoRand) where import "crypto-api" Crypto.Random import Data.Binary.Get import Data.ByteString hiding (pack) import Data.ByteString.Lazy (pack) import System.Random newtype CryptoRand g = CryptoRand g deriving (CryptoRandomGen) intBytes = let bits = intLog 2 $ (1 + (fromIntegral (maxBound :: Int)) - (fromIntegral (minBound :: Int)) :: Integer) in if (bits `mod` 8) == 0 then bits `div` 8 else error "invalid Int bits in `intBytes`" bytesToInt :: ByteString -> Int bytesToInt bs = case intBytes of 4 -> fromIntegral $ runGet getWord32host $ pack $ unpack bs 8 -> fromIntegral $ runGet getWord64host $ pack $ unpack bs _ -> error "Unsupported Int size in `bytesToInt`" -- | Yield @log_b(n)@ when it is a non-negative integer (otherwise -- error). intLog :: (Integral i) => i -> i -> Int intLog _ 1 = 0 -- correct because ceil (lg (x)) == ceil (log (ceil (x))) intLog b n | (n `mod` b) == 0 = 1 + intLog b (n `div` b) | otherwise = error "invalid arguments to intLog" instance (CryptoRandomGen g) => RandomGen (CryptoRand g) where next (CryptoRand g) = case genBytes intBytes g of Right (bs, g') -> (bytesToInt bs, CryptoRand g') Left err -> error $ "Next error: " ++ show err genRange _ = (minBound, maxBound) split (CryptoRand g) = case splitGen g of Right (g1, g2) -> (CryptoRand g1, CryptoRand g2) Left err -> error $ "Split error: " ++ show err