{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- |
-- Module      : Crypto.Random
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : stable
-- Portability : good
--
-- Random bytes, drawn either from the system or from a generator whose
-- seed you hold.
--
-- == Drawing from the system
--
-- 'getRandomBytes' in 'IO' is the ordinary way to get bytes nobody can
-- predict.  The length is in bytes and the type is any 'ByteArray', so the
-- result is usually pinned down by where it goes:
--
-- > import Crypto.Random (getRandomBytes)
-- > import Data.ByteString (ByteString)
-- >
-- > nonce <- getRandomBytes 12 :: IO ByteString
--
-- Everything in this library that needs randomness takes it the same way,
-- through a @MonadRandom m =>@ constraint, so running it in 'IO' is the
-- whole of choosing this source:
--
-- > import qualified Crypto.PubKey.RSA as RSA
-- >
-- > (pub, priv) <- RSA.generate 256 0x10001
--
-- Where the operating system offers @getrandom(2)@ or @getentropy(3)@ the
-- bytes come from a ChaCha20 generator belonging to the operating system
-- thread the call runs on, seeded from the system and reseeded as it goes;
-- see "Crypto.Random.SysDRG" for what that is and what it is not.  Where
-- it does not -- Windows, for now -- they come from the system on every
-- call, as they always did.  Either way the caller writes the same line.
--
-- Any Haskell thread may draw, and none has to say which generator it
-- wants:
--
-- > import Control.Concurrent (forkIO)
-- >
-- > mapM_ (\_ -> forkIO (getRandomBytes 32 >>= use)) [1 .. 64 :: Int]
--
-- The generator belongs to the operating system thread that happens to be
-- carrying the Haskell thread when the call is made, and is found through
-- that thread's own storage rather than by anything the caller passes.  A
-- @forkIO@ thread moves between capabilities, so two draws from one Haskell
-- thread may be answered by two generators; they are seeded independently
-- of each other, so it makes no difference which one answers.
--
-- == Drawing from a generator you hold
--
-- A generator of your own is the way to draw many times from one seed.
-- The system is asked once, when the generator is made, and not again.
-- That is how @tls@ does a connection: 'seedNew' when it opens, and
-- everything the handshake needs afterwards from the generator, whose
-- state the connection carries from one draw to the next.
--
-- > import Crypto.Random (ChaChaDRG, drgNewSeed, seedNew, withDRG)
-- >
-- > data Connection = Connection { connRNG :: ChaChaDRG }
-- >
-- > newConnection :: IO Connection
-- > newConnection = do
-- >     seed <- seedNew                   -- the only draw from the system
-- >     return (Connection (drgNewSeed seed))
-- >
-- > -- every later draw advances the connection's own generator
-- > connRandom :: Int -> Connection -> (ByteString, Connection)
-- > connRandom n conn =
-- >     let (bytes, rng') = withDRG (connRNG conn) (getRandomBytes n)
-- >      in (bytes, conn{connRNG = rng'})
--
-- Inside 'withDRG' the same 'getRandomBytes' resolves to the instance for
-- 'MonadPseudoRandom' rather than the one for 'IO', so it touches neither
-- the system nor the per-thread generator: it advances the generator it was
-- given and hands it back.  Keep that generator and the bytes are
-- reproducible from the seed, which is what makes a test repeatable -- and
-- what makes a generator unfit for keys unless its seed came from the
-- system.
--
-- 'drgNew' is 'seedNew' and 'drgNewSeed' in one step; 'drgNewTest' takes
-- four numbers instead of a seed, for a test that must give the same answer
-- twice.
--
-- == Drawing from a monad stacked on IO
--
-- There are two instances, one for 'IO' and one for 'MonadPseudoRandom',
-- and none for the transformers, so a @ReaderT env IO@ or a @StateT s IO@
-- reaches the system through 'liftIO':
--
-- > import Control.Monad.IO.Class (liftIO)
-- > import Control.Monad.Trans.Reader (ReaderT)
-- >
-- > newKey :: ReaderT env IO ByteString
-- > newKey = liftIO (getRandomBytes 32)
--
-- What that draws is what the first section describes, no more and no less:
-- the 'IO' instance, the generator of the operating system thread the call
-- lands on, seeded from the system and reseeded as it goes.  'liftIO' says
-- which monad the draw happens in and nothing about where the bytes come
-- from.
--
-- Writing an instance for the stack instead would make the choice invisible
-- at the call site, and the class cannot tell a strong source from a weak
-- one -- see 'MonadRandom'.
--
-- == When the system will not give any
--
-- Drawing randomness is the one thing here with nothing to fall back on,
-- so the failure is an exception rather than a value: there is no sensible
-- 'Maybe' to return and no partial answer worth having.  Since 2.2 it is an
-- 'EntropyError' and not a @Control.Exception.ErrorCall@ or an
-- @Control.Exception.IOException@, which is worth knowing if you were
-- catching one of those:
--
-- > import Control.Exception (catch)
-- > import Crypto.Random (EntropyError (..), getRandomBytes)
-- >
-- > key <- getRandomBytes 32 `catch` \e -> case e of
-- >     NoEntropySource   -> fail "this system offers no randomness at all"
-- >     EntropyShort w g  -> fail (show w ++ " bytes wanted, " ++ show g ++ " arrived")
-- >     EntropySourceLost s -> fail ("the source " ++ s ++ " went away")
--
-- Catching it at all is a decision rather than a default.  A program that
-- cannot get randomness cannot make a key, and stopping is usually the
-- honest thing; the reason to catch is to say so in the program's own
-- terms rather than in crypton's.
module Crypto.Random (
    -- * Drawing from the system
    MonadRandom (..),

    -- * Drawing from a generator you hold
    Seed,
    seedNew,
    seedFromInteger,
    seedToInteger,
    seedFromBinary,
    drgNewSeed,
    drgNew,
    drgNewTest,
    withDRG,
    withRandomBytes,
    MonadPseudoRandom,
    DRG (..),

    -- * The generators
    ChaChaDRG,
    SystemDRG,
    getSystemDRG,

    -- * When the system will not give any
    EntropyError (..),
) where

import Crypto.Error
import Crypto.Internal.Imports
import Crypto.Random.ChaChaDRG
import Crypto.Random.Entropy (EntropyError (..))
import Crypto.Random.SystemDRG
import Crypto.Random.Types
import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes)
import qualified Data.ByteArray as B

import qualified Crypto.Number.Serialize as Serialize

#ifdef INSECURE_ENTROPY
import Crypto.Hash (SHA512, Context)
import Crypto.Hash.IO
import Data.Memory.PtrMethods (memSet)
import Foreign.Ptr (Ptr, castPtr)
#endif

-- | The material a deterministic generator is built from.  Two generators
-- made from one seed produce the same bytes, which is what 'drgNewSeed' is
-- for and why a seed kept anywhere is as good as the keys drawn from it.
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

#ifdef INSECURE_ENTROPY
-- 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 = (Seed . scrubbedHash512) `fmap` getRandomBytes 64

scrubbedHash512 :: ScrubbedBytes -> ScrubbedBytes
scrubbedHash512 = B.take seedLength . hash512
  where
    hash512 ba = B.unsafeCreate (hashDigestSize (undefined :: SHA512)) $ hashIO ba
    hashIO ba ptr = do
        ctx <- hashMutableInit
        hashMutableUpdate (ctx :: MutableContext SHA512) ba
        B.withByteArray ctx $ \pctx -> do
            hashInternalFinalize (castPtr pctx :: Ptr (Context SHA512)) ptr
            memSet pctx 0 $ hashInternalContextSize (undefined :: SHA512)
#else
seedNew :: forall (randomly :: * -> *). MonadRandom randomly => randomly Seed
seedNew = ScrubbedBytes -> Seed
Seed (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
seedLength
#endif

-- | 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
2 Integer -> 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