    {-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-}
    {-|
     Maintainer: Thomas.DuBuisson@gmail.com
     Stability: beta
     Portability: portable

     Obtain entropy from system sources or x86 RDRAND when available.

     Currently supporting:

        - Windows via CryptoAPI
        - *nix systems via @\/dev\/urandom@
           - Includes QNX
        - ghcjs/browser via JavaScript crypto API.
    -}

    module System.Entropy
            ( getEntropy,
              getHardwareEntropy,
              CryptHandle,
              openHandle,
              hGetEntropy,
              closeHandle
            ) where

    #ifdef ghcjs_HOST_OS
    import System.EntropyGhcjs
    #elif defined(isWindows)
    import System.EntropyWindows
    #else
    import System.EntropyNix
    #endif

    import qualified Data.ByteString as B
    import Control.Exception (bracket)

    -- |Get a specific number of bytes of cryptographically
    -- secure random data using the *system-specific* sources.
    -- (As of 0.4.  Versions <0.4 mixed system and hardware sources)
    --
    -- The returned random value is considered cryptographically secure but not true entropy.
    --
    -- On some platforms this requires a file handle which can lead to resource
    -- exhaustion in some situations.
    getEntropy :: Int               -- ^ Number of bytes
               -> IO B.ByteString
    getEntropy :: Int -> IO ByteString
    getEntropy = IO CryptHandle
    -> (CryptHandle -> IO ())
    -> (CryptHandle -> IO ByteString)
    -> IO ByteString
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
    bracket IO CryptHandle
    openHandle CryptHandle -> IO ()
    closeHandle ((CryptHandle -> IO ByteString) -> IO ByteString)
    -> (Int -> CryptHandle -> IO ByteString) -> Int -> IO ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (CryptHandle -> Int -> IO ByteString)
    -> Int -> CryptHandle -> IO ByteString
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip CryptHandle -> Int -> IO ByteString
    hGetEntropy

    -- |Get a specific number of bytes of cryptographically
    -- secure random data using a supported *hardware* random bit generator.
    --
    -- If there is no hardware random number generator then @Nothing@ is returned.
    -- If any call returns non-Nothing then it should never be @Nothing@ unless
    -- there has been a hardware failure.
    --
    -- If trust of the CPU allows it and no context switching is important,
    -- a bias to the hardware rng with system rng as fall back is trivial:
    --
    -- @
    -- let fastRandom nr = maybe (getEntropy nr) pure =<< getHardwareEntropy nr
    -- @
    --
    -- The old, @<0.4@, behavior is possible using @xor@ from 'Data.Bits':
    --
    -- @
    -- let oldRandom nr =
    --      do hwRnd  <- maybe (replicate nr 0) BS.unpack <$> getHardwareEntropy nr
    --         sysRnd <- BS.unpack <$> getEntropy nr
    --         pure $ BS.pack $ zipWith xor sysRnd hwRnd
    -- @
    --
    -- A less maliable mixing can be accomplished by replacing `xor` with a
    -- composition of concat and cryptographic hash.
    getHardwareEntropy :: Int                       -- ^ Number of bytes
                       -> IO (Maybe B.ByteString)
    getHardwareEntropy :: Int -> IO (Maybe ByteString)
    getHardwareEntropy = Int -> IO (Maybe ByteString)
    hardwareRandom