1-- |
2-- Module      : Crypto.Random
3-- License     : BSD-style
4-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
5-- Stability   : stable
6-- Portability : good
7--
8{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9module Crypto.Random
10    (
11    -- * Deterministic instances
12      ChaChaDRG
13    , SystemDRG
14    , Seed
15    -- * Seed
16    , seedNew
17    , seedFromInteger
18    , seedToInteger
19    , seedFromBinary
20    -- * Deterministic Random class
21    , getSystemDRG
22    , drgNew
23    , drgNewSeed
24    , drgNewTest
25    , withDRG
26    , withRandomBytes
27    , DRG(..)
28    -- * Random abstraction
29    , MonadRandom(..)
30    , MonadPseudoRandom
31    ) where
32
33import Crypto.Error
34import Crypto.Random.Types
35import Crypto.Random.ChaChaDRG
36import Crypto.Random.SystemDRG
37import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes)
38import qualified Data.ByteArray as B
39import Crypto.Internal.Imports
40
41import qualified Crypto.Number.Serialize as Serialize
42
43newtype Seed = Seed ScrubbedBytes
44    deriving (ByteArrayAccess)
45
46-- Length for ChaCha DRG seed
47seedLength :: Int
48seedLength = 40
49
50-- | Create a new Seed from system entropy
51seedNew :: MonadRandom randomly => randomly Seed
52seedNew = Seed `fmap` getRandomBytes seedLength
53
54-- | Convert a Seed to an integer
55seedToInteger :: Seed -> Integer
56seedToInteger (Seed b) = Serialize.os2ip b
57
58-- | Convert an integer to a Seed
59seedFromInteger :: Integer -> Seed
60seedFromInteger i = Seed $ Serialize.i2ospOf_ seedLength (i `mod` 2^(seedLength * 8))
61
62-- | Convert a binary to a seed
63seedFromBinary :: ByteArrayAccess b => b -> CryptoFailable Seed
64seedFromBinary b
65    | B.length b /= 40 = CryptoFailed (CryptoError_SeedSizeInvalid)
66    | otherwise        = CryptoPassed $ Seed $ B.convert b
67
68-- | Create a new DRG from system entropy
69drgNew :: MonadRandom randomly => randomly ChaChaDRG
70drgNew = drgNewSeed `fmap` seedNew
71
72-- | Create a new DRG from a seed
73drgNewSeed :: Seed -> ChaChaDRG
74drgNewSeed (Seed seed) = initialize seed
75
76-- | Create a new DRG from 5 Word64.
77--
78-- This is a convenient interface to create deterministic interface
79-- for quickcheck style testing.
80--
81-- It can also be used in other contexts provided the input
82-- has been properly randomly generated.
83drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG
84drgNewTest = initializeWords
85
86-- | Generate @len random bytes and mapped the bytes to the function @f.
87--
88-- This is equivalent to use Control.Arrow 'first' with 'randomBytesGenerate'
89withRandomBytes :: (ByteArray ba, DRG g) => g -> Int -> (ba -> a) -> (a, g)
90withRandomBytes rng len f = (f bs, rng')
91  where (bs, rng') = randomBytesGenerate len rng
92