-- |
-- Module      : Crypto.Random
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : stable
-- Portability : good
--
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Crypto.Random
    (
    -- * Deterministic instances
      ChaChaDRG
    , SystemDRG
    , Seed
    -- * Seed
    , seedNew
    , seedFromInteger
    , seedToInteger
    , seedFromBinary
    -- * Deterministic Random class
    , getSystemDRG
    , drgNew
    , drgNewSeed
    , drgNewTest
    , withDRG
    , withRandomBytes
    , DRG(..)
    -- * Random abstraction
    , MonadRandom(..)
    , MonadPseudoRandom
    ) where

import Crypto.Error
import Crypto.Random.Types
import Crypto.Random.ChaChaDRG
import Crypto.Random.SystemDRG
import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes)
import qualified Data.ByteArray as B
import Crypto.Internal.Imports
import Crypto.Hash (Digest, SHA512, hash)

import qualified Crypto.Number.Serialize as Serialize

newtype Seed = Seed ScrubbedBytes
    deriving (Seed -> Int
(Seed -> Int)
-> (forall p a. Seed -> (Ptr p -> IO a) -> IO a)
-> (forall p. Seed -> Ptr p -> IO ())
-> ByteArrayAccess Seed
forall p. Seed -> Ptr p -> IO ()
forall ba.
(ba -> Int)
-> (forall p a. ba -> (Ptr p -> IO a) -> IO a)
-> (forall p. ba -> Ptr p -> IO ())
-> ByteArrayAccess ba
forall p a. Seed -> (Ptr p -> IO a) -> IO a
$clength :: Seed -> Int
length :: Seed -> Int
$cwithByteArray :: forall p a. Seed -> (Ptr p -> IO a) -> IO a
withByteArray :: forall p a. Seed -> (Ptr p -> IO a) -> IO a
$ccopyByteArrayToPtr :: forall p. Seed -> Ptr p -> IO ()
copyByteArrayToPtr :: forall p. Seed -> Ptr p -> IO ()
ByteArrayAccess)

-- Length for ChaCha DRG seed
seedLength :: Int
seedLength :: Int
seedLength = Int
40

-- | Create a new Seed from system entropy
seedNew :: MonadRandom randomly => randomly Seed
-- The degree of its randomness depends on the source, e.g. for iOS we
-- have to compile with DoNotUseEntropy flag, as iOS doesn't allow
-- using getentropy, and on some other systems it can be also
-- potentially comprisable sources. Hashing of entropy before using
-- it as a seed is a common mitigation for attacks via RNG/entropy
-- source.
seedNew :: forall (randomly :: * -> *). MonadRandom randomly => randomly Seed
seedNew = (ScrubbedBytes -> Seed
Seed (ScrubbedBytes -> Seed)
-> (ScrubbedBytes -> ScrubbedBytes) -> ScrubbedBytes -> Seed
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> ScrubbedBytes -> ScrubbedBytes
forall bs. ByteArray bs => Int -> bs -> bs
B.take Int
seedLength (ScrubbedBytes -> ScrubbedBytes)
-> (ScrubbedBytes -> ScrubbedBytes)
-> ScrubbedBytes
-> ScrubbedBytes
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Digest SHA512 -> ScrubbedBytes
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert (Digest SHA512 -> ScrubbedBytes)
-> (ScrubbedBytes -> Digest SHA512)
-> ScrubbedBytes
-> ScrubbedBytes
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ScrubbedBytes -> Digest SHA512
forall ba a.
(ByteArrayAccess ba, HashAlgorithm a) =>
ba -> Digest a
hash :: ScrubbedBytes -> Digest SHA512)) (ScrubbedBytes -> Seed) -> randomly ScrubbedBytes -> randomly Seed
forall a b. (a -> b) -> randomly a -> randomly b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Int -> randomly ScrubbedBytes
forall byteArray. ByteArray byteArray => Int -> randomly byteArray
forall (m :: * -> *) byteArray.
(MonadRandom m, ByteArray byteArray) =>
Int -> m byteArray
getRandomBytes Int
64

-- | Convert a Seed to an integer
seedToInteger :: Seed -> Integer
seedToInteger :: Seed -> Integer
seedToInteger (Seed ScrubbedBytes
b) = ScrubbedBytes -> Integer
forall ba. ByteArrayAccess ba => ba -> Integer
Serialize.os2ip ScrubbedBytes
b

-- | Convert an integer to a Seed
seedFromInteger :: Integer -> Seed
seedFromInteger :: Integer -> Seed
seedFromInteger Integer
i = ScrubbedBytes -> Seed
Seed (ScrubbedBytes -> Seed) -> ScrubbedBytes -> Seed
forall a b. (a -> b) -> a -> b
$ Int -> Integer -> ScrubbedBytes
forall ba. ByteArray ba => Int -> Integer -> ba
Serialize.i2ospOf_ Int
seedLength (Integer
i Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
2Integer -> Int -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^(Int
seedLength Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
8))

-- | Convert a binary to a seed
seedFromBinary :: ByteArrayAccess b => b -> CryptoFailable Seed
seedFromBinary :: forall b. ByteArrayAccess b => b -> CryptoFailable Seed
seedFromBinary b
b
    | b -> Int
forall ba. ByteArrayAccess ba => ba -> Int
B.length b
b Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
40 = CryptoError -> CryptoFailable Seed
forall a. CryptoError -> CryptoFailable a
CryptoFailed (CryptoError
CryptoError_SeedSizeInvalid)
    | Bool
otherwise        = Seed -> CryptoFailable Seed
forall a. a -> CryptoFailable a
CryptoPassed (Seed -> CryptoFailable Seed) -> Seed -> CryptoFailable Seed
forall a b. (a -> b) -> a -> b
$ ScrubbedBytes -> Seed
Seed (ScrubbedBytes -> Seed) -> ScrubbedBytes -> Seed
forall a b. (a -> b) -> a -> b
$ b -> ScrubbedBytes
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
B.convert b
b

-- | Create a new DRG from system entropy
drgNew :: MonadRandom randomly => randomly ChaChaDRG
drgNew :: forall (randomly :: * -> *).
MonadRandom randomly =>
randomly ChaChaDRG
drgNew = Seed -> ChaChaDRG
drgNewSeed (Seed -> ChaChaDRG) -> randomly Seed -> randomly ChaChaDRG
forall a b. (a -> b) -> randomly a -> randomly b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` randomly Seed
forall (randomly :: * -> *). MonadRandom randomly => randomly Seed
seedNew

-- | Create a new DRG from a seed
drgNewSeed :: Seed -> ChaChaDRG
drgNewSeed :: Seed -> ChaChaDRG
drgNewSeed (Seed ScrubbedBytes
seed) = ScrubbedBytes -> ChaChaDRG
forall seed. ByteArrayAccess seed => seed -> ChaChaDRG
initialize ScrubbedBytes
seed

-- | Create a new DRG from 5 Word64.
--
-- This is a convenient interface to create deterministic interface
-- for quickcheck style testing.
--
-- It can also be used in other contexts provided the input
-- has been properly randomly generated.
--
-- Note that the @Arbitrary@ instance provided by QuickCheck for 'Word64' does
-- not have a uniform distribution.  It is often better to use instead
-- @arbitraryBoundedRandom@.
--
-- System endianness impacts how the tuple is interpreted and therefore changes
-- the resulting DRG.
drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG
drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG
drgNewTest = (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG
initializeWords

-- | Generate @len random bytes and mapped the bytes to the function @f.
--
-- This is equivalent to use Control.Arrow 'first' with 'randomBytesGenerate'
withRandomBytes :: (ByteArray ba, DRG g) => g -> Int -> (ba -> a) -> (a, g)
withRandomBytes :: forall ba g a.
(ByteArray ba, DRG g) =>
g -> Int -> (ba -> a) -> (a, g)
withRandomBytes g
rng Int
len ba -> a
f = (ba -> a
f ba
bs, g
rng')
  where (ba
bs, g
rng') = Int -> g -> (ba, g)
forall byteArray. ByteArray byteArray => Int -> g -> (byteArray, g)
forall gen byteArray.
(DRG gen, ByteArray byteArray) =>
Int -> gen -> (byteArray, gen)
randomBytesGenerate Int
len g
rng