{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE CApiFFI                  #-}
-- | PRNG services
--   See <http://www.openssl.org/docs/crypto/rand.html>
--   For random Integer generation, see "OpenSSL.BN"
module OpenSSL.Random
    ( -- * Random byte generation
      randBytes
    , prandBytes
    , add
    ) where
import           Foreign
import           Foreign.C.Types
import qualified Data.ByteString as BS
import           OpenSSL.Utils

foreign import capi unsafe "openssl/rand.h RAND_bytes"
        _RAND_bytes :: Ptr CChar -> CInt -> IO CInt

foreign import capi unsafe "openssl/rand.h RAND_pseudo_bytes"
        _RAND_pseudo_bytes :: Ptr CChar -> CInt -> IO ()

foreign import capi unsafe "openssl/rand.h RAND_add"
        _RAND_add :: Ptr CChar -> CInt -> CInt -> IO ()

-- | Return a bytestring consisting of the given number of strongly random
--   bytes
randBytes :: Int  -- ^ the number of bytes requested
          -> IO BS.ByteString
randBytes :: Int -> IO ByteString
randBytes Int
n =
  Int -> (Ptr CChar -> IO ByteString) -> IO ByteString
forall a b. Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray Int
n ((Ptr CChar -> IO ByteString) -> IO ByteString)
-> (Ptr CChar -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
bufPtr ->
  do Ptr CChar -> CInt -> IO CInt
_RAND_bytes Ptr CChar
bufPtr (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n) IO CInt -> (CInt -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (CInt -> Bool) -> CInt -> IO ()
forall a. (a -> Bool) -> a -> IO ()
failIf_ (CInt -> CInt -> Bool
forall a. Eq a => a -> a -> Bool
/= CInt
1)
     CStringLen -> IO ByteString
BS.packCStringLen (Ptr CChar
bufPtr, Int
n)

-- | Return a bytestring consisting of the given number of pseudo random
--   bytes
prandBytes :: Int  -- ^ the number of bytes requested
           -> IO BS.ByteString
prandBytes :: Int -> IO ByteString
prandBytes Int
n =
  Int -> (Ptr CChar -> IO ByteString) -> IO ByteString
forall a b. Storable a => Int -> (Ptr a -> IO b) -> IO b
allocaArray Int
n ((Ptr CChar -> IO ByteString) -> IO ByteString)
-> (Ptr CChar -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
bufPtr ->
  do Ptr CChar -> CInt -> IO ()
_RAND_pseudo_bytes Ptr CChar
bufPtr (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n)
     CStringLen -> IO ByteString
BS.packCStringLen (Ptr CChar
bufPtr, Int
n)

-- | Add data to the entropy pool. It's safe to add sensitive information
--   (e.g. user passwords etc) to the pool. Also, adding data with an entropy
--   of 0 can never hurt.
add :: BS.ByteString  -- ^ random data to be added to the pool
    -> Int  -- ^ the number of bits of entropy in the first argument
    -> IO ()
add :: ByteString -> Int -> IO ()
add ByteString
bs Int
entropy =
  ByteString -> (CStringLen -> IO ()) -> IO ()
forall a. ByteString -> (CStringLen -> IO a) -> IO a
BS.useAsCStringLen ByteString
bs ((CStringLen -> IO ()) -> IO ()) -> (CStringLen -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(Ptr CChar
ptr, Int
len) ->
  Ptr CChar -> CInt -> CInt -> IO ()
_RAND_add Ptr CChar
ptr (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
len) (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
entropy)