module Crypto.Random.Entropy.Unix
( DevRandom
, DevURandom
) where
import Foreign.Ptr
import Data.Word (Word8)
import Crypto.Random.Entropy.Source
import Control.Exception as E
import System.Posix.Types (Fd)
import System.Posix.IO
type H = Fd
type DeviceName = String
newtype DevRandom = DevRandom DeviceName
newtype DevURandom = DevURandom DeviceName
instance EntropySource DevRandom where
entropyOpen = fmap DevRandom `fmap` testOpen "/dev/random"
entropyGather (DevRandom name) ptr n =
withDev name $ \h -> gatherDevEntropy h ptr n
entropyClose (DevRandom _) = return ()
instance EntropySource DevURandom where
entropyOpen = fmap DevURandom `fmap` testOpen "/dev/urandom"
entropyGather (DevURandom name) ptr n =
withDev name $ \h -> gatherDevEntropy h ptr n
entropyClose (DevURandom _) = return ()
testOpen :: DeviceName -> IO (Maybe DeviceName)
testOpen filepath = do
d <- openDev filepath
case d of
Nothing -> return Nothing
Just h -> closeDev h >> return (Just filepath)
openDev :: String -> IO (Maybe H)
openDev filepath = (Just `fmap` openFd filepath ReadOnly Nothing fileFlags)
`E.catch` \(_ :: IOException) -> return Nothing
where fileFlags = defaultFileFlags { nonBlock = True }
withDev :: String -> (H -> IO a) -> IO a
withDev filepath f = openDev filepath >>= \h ->
case h of
Nothing -> error ("device " ++ filepath ++ " cannot be grabbed")
Just fd -> f fd >>= \r -> (closeDev fd >> return r)
closeDev :: H -> IO ()
closeDev h = closeFd h
gatherDevEntropy :: H -> Ptr Word8 -> Int -> IO Int
gatherDevEntropy h ptr sz =
(fromIntegral `fmap` fdReadBuf h ptr (fromIntegral sz))
`E.catch` \(_ :: IOException) -> return 0