{-|
Module      : Botan.Low.RNG
Description : Random number generators
Copyright   : (c) Leo D, 2023
License     : BSD-3-Clause
Maintainer  : leo@apotheca.io
Stability   : experimental
Portability : POSIX

Pseudo-random number generation.
-}

module Botan.Low.RNG
( 

-- * Random number generators
-- $introduction

-- * Usage
-- $usage

  RNG(..)
, RNGType(..)
, withRNG
, rngInit
, rngDestroy
, rngGet
, systemRNGGet
, rngReseed
, rngReseedFromRNG
, rngAddEntropy

-- * RNG Types

, pattern SystemRNG
, pattern UserRNG
, pattern UserThreadsafeRNG
, pattern RDRandRNG

) where

import qualified Data.ByteString as ByteString

import Botan.Bindings.RNG

import Botan.Low.Error ( throwBotanIfNegative_ )
import Botan.Low.Make ( mkWithTemp1 )
import Botan.Low.Remake
import Botan.Low.Prelude

{- $introduction

A `random number generator` is used to generate uniform samples of pseudorandom
bytes.

-}

{- $usage

You can always use the system `RNG`:

> import Botan.Low.RNG
> randomBytes <- systemRNGGet 16

Unless you need a specific `RNG`, it is strongly recommended that you use the
autoseeded `user` RNG.

> import Botan.Low.RNG
> rng <- rngInit "user"
> randomBytes <- rngGet rng 16

You can reseed a generator using the system generator:

> rngReseed rng 64

You can also reseed a generator using a specific generator:

> systemRNG <- rngInit "system"
> rngReseedFromRNG rng systemRNG 64

You can also seed it with your own entropy; this is safe and can never
*decrease* the amount of entropy in the generator.

> rngAddEntropy rng "Fee fi fo fum!"

-}

-- NOTE: Does not take advantage of Remake
-- NOTE: Uses ConstPtr / unConstPtr manually
-- TODO: Take advantage of Remake / better peek / (peekConst or constPeek) functions

newtype RNG = MkRNG { RNG -> ForeignPtr BotanRNGStruct
getRNGForeignPtr :: ForeignPtr BotanRNGStruct }

newRNG      :: BotanRNG -> IO RNG
withRNG     :: RNG -> (BotanRNG -> IO a) -> IO a
-- | Destroy a random number generator object immediately
rngDestroy  :: RNG -> IO ()
createRNG   :: (Ptr BotanRNG -> IO CInt) -> IO RNG
(BotanRNG -> IO RNG
newRNG, RNG -> (BotanRNG -> IO a) -> IO a
withRNG, RNG -> IO ()
rngDestroy, (Ptr BotanRNG -> IO CInt) -> IO RNG
createRNG, (Ptr BotanRNG -> Ptr CSize -> IO CInt) -> IO [RNG]
_)
    = (Ptr BotanRNGStruct -> BotanRNG)
-> (BotanRNG -> Ptr BotanRNGStruct)
-> (ForeignPtr BotanRNGStruct -> RNG)
-> (RNG -> ForeignPtr BotanRNGStruct)
-> FinalizerPtr BotanRNGStruct
-> (BotanRNG -> IO RNG, RNG -> (BotanRNG -> IO a) -> IO a,
    RNG -> IO (), (Ptr BotanRNG -> IO CInt) -> IO RNG,
    (Ptr BotanRNG -> Ptr CSize -> IO CInt) -> IO [RNG])
forall botan struct object a.
Storable botan =>
(Ptr struct -> botan)
-> (botan -> Ptr struct)
-> (ForeignPtr struct -> object)
-> (object -> ForeignPtr struct)
-> FinalizerPtr struct
-> (botan -> IO object, object -> (botan -> IO a) -> IO a,
    object -> IO (), (Ptr botan -> IO CInt) -> IO object,
    (Ptr botan -> Ptr CSize -> IO CInt) -> IO [object])
mkBindings Ptr BotanRNGStruct -> BotanRNG
MkBotanRNG BotanRNG -> Ptr BotanRNGStruct
runBotanRNG ForeignPtr BotanRNGStruct -> RNG
MkRNG RNG -> ForeignPtr BotanRNGStruct
getRNGForeignPtr FinalizerPtr BotanRNGStruct
botan_rng_destroy

type RNGType = ByteString

pattern SystemRNG           -- ^ system RNG
    ,   UserRNG             -- ^ userspace RNG
    ,   UserThreadsafeRNG   -- ^ userspace RNG, with internal locking
    ,   RDRandRNG           -- ^ directly read RDRAND
    ::  RNGType
pattern $mSystemRNG :: forall {r}. RNGType -> ((# #) -> r) -> ((# #) -> r) -> r
$bSystemRNG :: RNGType
SystemRNG           = BOTAN_RNG_TYPE_SYSTEM
pattern $mUserRNG :: forall {r}. RNGType -> ((# #) -> r) -> ((# #) -> r) -> r
$bUserRNG :: RNGType
UserRNG             = BOTAN_RNG_TYPE_USER
pattern $mUserThreadsafeRNG :: forall {r}. RNGType -> ((# #) -> r) -> ((# #) -> r) -> r
$bUserThreadsafeRNG :: RNGType
UserThreadsafeRNG   = BOTAN_RNG_TYPE_USER_THREADSAFE
pattern $mRDRandRNG :: forall {r}. RNGType -> ((# #) -> r) -> ((# #) -> r) -> r
$bRDRandRNG :: RNGType
RDRandRNG           = BOTAN_RNG_TYPE_RDRAND

{- |
Initialize a random number generator object

rng_type has the possible values:

    - "system": system RNG
    - "user": userspace RNG
    - "user-threadsafe": userspace RNG, with internal locking
    - "rdrand": directly read RDRAND

Set rng_type to null to let the library choose some default.
-}
rngInit
    :: RNGType  -- ^ __rng_type__: type of the rng
    -> IO RNG   -- ^ __rng__
rngInit :: RNGType -> IO RNG
rngInit = ((Ptr BotanRNG -> IO CInt) -> IO RNG)
-> (Ptr BotanRNG -> ConstPtr CChar -> IO CInt) -> RNGType -> IO RNG
forall botan object.
((Ptr botan -> IO CInt) -> IO object)
-> (Ptr botan -> ConstPtr CChar -> IO CInt) -> RNGType -> IO object
mkCreateObjectCString (Ptr BotanRNG -> IO CInt) -> IO RNG
createRNG Ptr BotanRNG -> ConstPtr CChar -> IO CInt
botan_rng_init

-- WARNING: withFooInit-style limited lifetime functions moved to high-level botan
withRNGInit :: RNGType -> (RNG -> IO a) -> IO a
withRNGInit :: forall a. RNGType -> (RNG -> IO a) -> IO a
withRNGInit = (RNGType -> IO RNG)
-> (RNG -> IO ()) -> RNGType -> (RNG -> IO a) -> IO a
forall x t a.
(x -> IO t) -> (t -> IO ()) -> x -> (t -> IO a) -> IO a
mkWithTemp1 RNGType -> IO RNG
rngInit RNG -> IO ()
rngDestroy

-- | Get random bytes from a random number generator
rngGet
    :: RNG              -- ^ __rng__: rng object
    -> Int              -- ^ __out_len__: number of requested bytes
    -> IO ByteString    -- ^ __out__: output buffer of size out_len
rngGet :: RNG -> Int -> IO RNGType
rngGet RNG
rng Int
len = RNG -> (BotanRNG -> IO RNGType) -> IO RNGType
forall a. RNG -> (BotanRNG -> IO a) -> IO a
withRNG RNG
rng ((BotanRNG -> IO RNGType) -> IO RNGType)
-> (BotanRNG -> IO RNGType) -> IO RNGType
forall a b. (a -> b) -> a -> b
$ \ BotanRNG
botanRNG -> do
    Int -> (Ptr Word8 -> IO ()) -> IO RNGType
forall byte. Int -> (Ptr byte -> IO ()) -> IO RNGType
allocBytes Int
len ((Ptr Word8 -> IO ()) -> IO RNGType)
-> (Ptr Word8 -> IO ()) -> IO RNGType
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
bytesPtr -> do
        HasCallStack => IO CInt -> IO ()
IO CInt -> IO ()
throwBotanIfNegative_ (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ BotanRNG -> Ptr Word8 -> CSize -> IO CInt
botan_rng_get BotanRNG
botanRNG Ptr Word8
bytesPtr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)

-- | Get random bytes from system random number generator
systemRNGGet
    :: Int              -- ^ __out_len__: number of requested bytes
    -> IO ByteString    -- ^ __out__: output buffer of size out_len
systemRNGGet :: Int -> IO RNGType
systemRNGGet Int
len = Int -> (Ptr Word8 -> IO ()) -> IO RNGType
forall byte. Int -> (Ptr byte -> IO ()) -> IO RNGType
allocBytes Int
len ((Ptr Word8 -> IO ()) -> IO RNGType)
-> (Ptr Word8 -> IO ()) -> IO RNGType
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
bytesPtr -> do
    HasCallStack => IO CInt -> IO ()
IO CInt -> IO ()
throwBotanIfNegative_ (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ Ptr Word8 -> CSize -> IO CInt
botan_system_rng_get Ptr Word8
bytesPtr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len)

{- |
Reseed a random number generator

Uses the System_RNG as a seed generator.
-}
rngReseed
    :: RNG  -- ^ __rng__: rng object
    -> Int  -- ^ __bits__: number of bits to reseed with
    -> IO ()
rngReseed :: RNG -> Int -> IO ()
rngReseed RNG
rng Int
bits = RNG -> (BotanRNG -> IO ()) -> IO ()
forall a. RNG -> (BotanRNG -> IO a) -> IO a
withRNG RNG
rng ((BotanRNG -> IO ()) -> IO ()) -> (BotanRNG -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BotanRNG
botanRNG -> do
    HasCallStack => IO CInt -> IO ()
IO CInt -> IO ()
throwBotanIfNegative_ (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ BotanRNG -> CSize -> IO CInt
botan_rng_reseed BotanRNG
botanRNG (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
bits)

-- | Reseed a random number generator
rngReseedFromRNG
    :: RNG      -- ^ __rng__: rng object
    -> RNG      -- ^ __source_rng__: the rng that will be read from
    -> Int      -- ^ __bits__: number of bits to reseed with
    -> IO ()
rngReseedFromRNG :: RNG -> RNG -> Int -> IO ()
rngReseedFromRNG RNG
rng RNG
source Int
bits = RNG -> (BotanRNG -> IO ()) -> IO ()
forall a. RNG -> (BotanRNG -> IO a) -> IO a
withRNG RNG
rng ((BotanRNG -> IO ()) -> IO ()) -> (BotanRNG -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BotanRNG
botanRNG -> do
    RNG -> (BotanRNG -> IO ()) -> IO ()
forall a. RNG -> (BotanRNG -> IO a) -> IO a
withRNG RNG
source ((BotanRNG -> IO ()) -> IO ()) -> (BotanRNG -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BotanRNG
sourcePtr -> do
        HasCallStack => IO CInt -> IO ()
IO CInt -> IO ()
throwBotanIfNegative_ (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ BotanRNG -> BotanRNG -> CSize -> IO CInt
botan_rng_reseed_from_rng BotanRNG
botanRNG BotanRNG
sourcePtr (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
bits)

-- | Add some seed material to a random number generator
rngAddEntropy
    :: RNG          -- ^ __rng__: rng object
    -> ByteString   -- ^ __entropy__: the data to add
    -> IO ()
rngAddEntropy :: RNG -> RNGType -> IO ()
rngAddEntropy RNG
rng RNGType
bytes = RNG -> (BotanRNG -> IO ()) -> IO ()
forall a. RNG -> (BotanRNG -> IO a) -> IO a
withRNG RNG
rng ((BotanRNG -> IO ()) -> IO ()) -> (BotanRNG -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BotanRNG
botanRNG -> do
    RNGType -> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall byte a. RNGType -> (Ptr byte -> CSize -> IO a) -> IO a
asBytesLen RNGType
bytes ((Ptr Word8 -> CSize -> IO ()) -> IO ())
-> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
bytesPtr CSize
bytesLen -> do
        HasCallStack => IO CInt -> IO ()
IO CInt -> IO ()
throwBotanIfNegative_ (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ BotanRNG -> ConstPtr Word8 -> CSize -> IO CInt
botan_rng_add_entropy BotanRNG
botanRNG (Ptr Word8 -> ConstPtr Word8
forall a. Ptr a -> ConstPtr a
ConstPtr Ptr Word8
bytesPtr) CSize
bytesLen