monadcryptorandom (empty) → 0.1
raw patch · 4 files changed
+257/−0 lines, 4 filesdep +basedep +bytestringdep +crypto-apisetup-changed
Dependencies added: base, bytestring, crypto-api, mtl
Files
- Control/Monad/Crypto/Random.hs +193/−0
- LICENSE +35/−0
- Setup.lhs +3/−0
- monadcryptorandom.cabal +26/−0
+ Control/Monad/Crypto/Random.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, FlexibleContexts, GeneralizedNewtypeDeriving #-}+{-|+ Maintainer: Thomas.DuBuisson@gmail.com+ Stability: beta+ Portability: portable ++Much like the "MonadRandom" package ("Control.Monad.Random"), this module+provides plumbing for the CryptoRandomGen generators.++-}+ +module Control.Monad.Crypto.Random+ ( CRandom(..)+ , MonadCryptoRandom(..)+ , CRandT+ , CRand+ , runCRandT+ , evalCRandT+ , runCRand+ , evalCRand+ ) where++import Crypto.Random (CryptoRandomGen(..), GenError(..))+import qualified Data.ByteString as B+import Data.Bits (xor, setBit, shiftR, shiftL, (.&.))+import Data.List (foldl')+import Data.Word+import Data.Int+import Control.Arrow (right)+import Control.Monad (liftM)+import Control.Monad.Identity+import Control.Monad.Error+import Control.Monad.State++-- |@MonadCryptoRandom m@ represents a monad that can produce+-- random values (or fail with a 'GenError'). It is suggestd+-- you use the 'CRandT' transformer in your monad stack.+class (MonadError GenError m) => MonadCryptoRandom m where+ getCRandom :: CRandom a => m a+ getCRandomR :: CRandom a => (a,a) -> m a+ getBytes :: Int -> m B.ByteString+ getBytesWithEntropy :: Int -> B.ByteString -> m B.ByteString+ doReseed :: B.ByteString -> m ()++-- |@CRandom a@ is much like the 'Random' class from the "System.Random" module in the "random" package.+-- The main difference is CRandom builds on "crypto-api"'s 'CryptoRandomGen', so it allows+-- explicit failure.+--+-- @crandomR (low,high) g@ as typically instantiated will generate a value between+-- [low, high] inclusively, swapping the pair if high < low.+--+-- Provided instances for @crandom g@ generates randoms between the bounds and between +/- 2^256+-- for Integer.+-- +-- The 'crandomR' function has degraded (theoretically unbounded, probabilitically decent) performance+-- the closer your range size (high - low) is to 2^n (from the top).+class CRandom a where+ crandom :: (CryptoRandomGen g) => g -> Either GenError (a, g)+ crandomR :: (CryptoRandomGen g) => (a, a) -> g -> Either GenError (a, g)+ crandoms :: (CryptoRandomGen g) => g -> [a]+ crandoms g =+ case crandom g of+ Left _ -> []+ Right (a,g') -> a : crandoms g'+ crandomRs :: (CryptoRandomGen g) => (a, a) -> g -> [a]+ crandomRs r g =+ case crandomR r g of+ Left _ -> []+ Right (a,g') -> a : crandomRs r g'++instance CRandom Integer where+ crandom = crandomR ((-(2^256)), 2^256)+ crandomR = crandomR_Num++instance CRandom Int where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num++instance CRandom Word8 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num+instance CRandom Word16 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num+instance CRandom Word32 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num+instance CRandom Word64 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num++instance CRandom Int8 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num+instance CRandom Int16 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num+instance CRandom Int32 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num+instance CRandom Int64 where+ crandom = crandomR (minBound, maxBound)+ crandomR = crandomR_Num++-- FIXME specialize+crandomR_Num :: (Integral a, CryptoRandomGen g) => (a,a) -> g -> Either GenError (a,g)+crandomR_Num (low, high) g+ | high < low = crandomR_Num (high,low) g+ | high == low = Right (high, g)+ | otherwise = go g+ where+ mask = foldl' setBit 0 [0 .. fromIntegral nrBits - 1]+ nrBits = base2Log range+ range :: Integer+ range = (fromIntegral high) - (fromIntegral low) + 1+ nrBytes = (nrBits + 7) `div` 8+ go gen =+ let offset = genBytes (fromIntegral nrBytes) gen+ in case offset of+ Left err -> Left err+ Right (bs, g') ->+ let res = fromIntegral $ fromIntegral low + (bs2i bs .&. mask)+ in if res > high then go g' else Right (res, g')++wrap :: (Monad m) => (g -> Either GenError (a,g)) -> CRandT g m a+wrap f = CRandT $ do+ g <- get+ case f g of+ Right (a,g') -> put g' >> return a+ Left x -> throwError x++-- |CRandT is the transformer suggested for MonadCryptoRandom.+newtype CRandT g m a = CRandT { unCRandT :: StateT g (ErrorT GenError m) a } deriving (MonadError GenError, Monad)++-- |Simple users of generators can use CRand for+-- quick and easy generation of randoms. See+-- below for a simple use of 'newGenIO' (from "crypto-api"),+-- 'getCRandom', 'getBytes', and 'runCRandom'.+--+-- @+-- getRandPair = do+-- int <- getCRandom+-- bytes <- getBytes 100+-- return (int, bytes)+--+-- func =+-- g <- newGenIO+-- case runCRand getRandPair g of+-- Right ((int,bytes), g') -> useRandomVals (int,bytes)+-- Left x -> handleGenError x+-- @+type CRand g = CRandT g Identity++runCRandT :: CRandT g m a -> g -> m (Either GenError (a,g))+runCRandT m g = runErrorT . flip runStateT g . unCRandT $ m++evalCRandT :: Monad m => CRandT g m a -> g -> m (Either GenError a)+evalCRandT m g = liftM (right fst) (runCRandT m g)++runCRand :: CRand g a -> g -> Either GenError (a, g)+runCRand m = runIdentity . runCRandT m++evalCRand :: CRand g a -> g -> Either GenError a+evalCRand m = runIdentity . evalCRandT m++instance (Monad m, CryptoRandomGen g) => MonadCryptoRandom (CRandT g m) where+ getCRandom = wrap crandom+ getCRandomR = wrap . crandomR+ getBytes i = wrap (genBytes i)+ getBytesWithEntropy i e = wrap (genBytesWithEntropy i e)+ doReseed bs = CRandT $ do+ get >>= \g ->+ case reseed bs g of+ Right g' -> put g'+ Left x -> throwError x++instance Error GenError where+ noMsg = GenErrorOther "noMsg"+ strMsg = GenErrorOther++base2Log :: Integer -> Integer+base2Log i+ | i >= setBit 0 64 = 64 + base2Log (i `shiftR` 64)+ | i >= setBit 0 32 = 32 + base2Log (i `shiftR` 32)+ | i >= setBit 0 16 = 16 + base2Log (i `shiftR` 16)+ | i >= setBit 0 8 = 8 + base2Log (i `shiftR` 8)+ | i >= setBit 0 0 = 1 + base2Log (i `shiftR` 1)+ | otherwise = 0++bs2i :: B.ByteString -> Integer+bs2i bs = B.foldl' (\i b -> (i `shiftL` 8) + fromIntegral b) 0 bs+{-# INLINE bs2i #-}+
+ LICENSE view
@@ -0,0 +1,35 @@+Copyright (c) Thomas DuBuisson++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.++++LargeWord is the work of Dominic Steinitz. See LargeWord.hs for copyright information.+{http://www.opensource.org/licenses/bsd-license.php}
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ monadcryptorandom.cabal view
@@ -0,0 +1,26 @@+name: monadcryptorandom+version: 0.1+license: BSD3+license-file: LICENSE+copyright: Thomas DuBuisson <thomas.dubuisson@gmail.com>+author: Thomas DuBuisson <thomas.dubuisson@gmail.com>+maintainer: Thomas DuBuisson <thomas.dubuisson@gmail.com>+description: A monad for using CryptoRandomGen+synopsis: A monad for using CryptoRandomGen+category: Control, Cryptography+homepage: http://trac.haskell.org/crypto-api/wiki+bug-reports: http://trac.haskell.org/crypto-api/report/1+stability: stable+build-type: Simple+cabal-version: >= 1.6+tested-with: GHC == 6.12.1+Data-Files:+extra-source-files:++Library+ Build-Depends: base == 4.*,+ bytestring >= 0.9 && < 0.10,+ mtl >= 2.0, crypto-api >= 0.2+ ghc-options:+ hs-source-dirs:+ exposed-modules: Control.Monad.Crypto.Random