module Foundation.System.Entropy.Unix
( EntropyCtx
, entropyOpen
, entropyGather
, entropyClose
, entropyMaximumSize
) where
import Foreign.Ptr
import Control.Exception as E
import Control.Monad
import System.IO
import System.IO.Unsafe (unsafePerformIO)
import Basement.Compat.Base
import Basement.Compat.C.Types
import Prelude (fromIntegral)
import Foundation.System.Entropy.Common
import Foundation.Numerical
data EntropyCtx =
EntropyCtx Handle
| EntropySyscall
entropyOpen :: IO EntropyCtx
entropyOpen = do
if supportSyscall
then return EntropySyscall
else do
mh <- openDev "/dev/urandom"
case mh of
Nothing -> E.throwIO EntropySystemMissing
Just h -> return $ EntropyCtx h
entropyGather :: EntropyCtx -> Ptr Word8 -> Int -> IO Bool
entropyGather (EntropyCtx h) ptr n = gatherDevEntropy h ptr n
entropyGather EntropySyscall ptr n = (==) 0 <$> c_sysrandom_linux ptr (fromIntegral n)
entropyClose :: EntropyCtx -> IO ()
entropyClose (EntropyCtx h) = hClose h
entropyClose EntropySyscall = return ()
entropyMaximumSize :: Int
entropyMaximumSize = 4096
openDev :: [Char] -> IO (Maybe Handle)
openDev filepath = (Just `fmap` openAndNoBuffering) `E.catch` \(_ :: IOException) -> return Nothing
where openAndNoBuffering = do
h <- openBinaryFile filepath ReadMode
hSetBuffering h NoBuffering
return h
gatherDevEntropy :: Handle -> Ptr Word8 -> Int -> IO Bool
gatherDevEntropy h ptr sz = loop ptr sz `E.catch` failOnException
where
loop _ 0 = return True
loop p n = do
r <- hGetBufSome h p n
if r >= 0
then loop (p `plusPtr` r) (n r)
else return False
failOnException :: E.IOException -> IO Bool
failOnException _ = return False
supportSyscall :: Bool
supportSyscall = unsafePerformIO ((==) 0 <$> c_sysrandom_linux nullPtr 0)
foreign import ccall unsafe "foundation_sysrandom_linux"
c_sysrandom_linux :: Ptr Word8 -> CSize -> IO Int