@@ -18,6 +18,7 @@ module System.EntropyNix
18import Control.Monad (liftM, when)
18import Control.Monad (liftM, when)
19import Data.ByteString as B
19import Data.ByteString as B
20import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString)
20import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString)
21+import Data.Bits (xor)
22import Foreign (allocaBytes)
23import Foreign (allocaBytes)
23import Foreign.Ptr
24import Foreign.Ptr
@@ -29,13 +30,6 @@ import Data.ByteString.Internal as B
29#undef HAVE_RDRAND
30#undef HAVE_RDRAND
34−#error "The entropy package requires RDRAND support when using the halvm/Xen"
36−data CryptHandle = UseRdRand -- or die trying
39import System.Posix (openFd, closeFd, fdReadBuf, OpenMode(..), defaultFileFlags, Fd)
33import System.Posix (openFd, closeFd, fdReadBuf, OpenMode(..), defaultFileFlags, Fd)
41source :: FilePath
35source :: FilePath
@@ -45,8 +39,7 @@ source = "/dev/urandom"
45data CryptHandle
39data CryptHandle
47#ifdef HAVE_RDRAND
41#ifdef HAVE_RDRAND
48− | UseRdRand
42+ | UseRdRand Fd
52-- |Open a `CryptHandle`
45-- |Open a `CryptHandle`
@@ -54,46 +47,31 @@ openHandle :: IO CryptHandle
54openHandle = do
47openHandle = do
55#ifdef HAVE_RDRAND
48#ifdef HAVE_RDRAND
56 b <- cpuHasRdRand
49 b <- cpuHasRdRand
57− if b then return UseRdRand
50+ if b then UseRdRand `fmap` nonRDRandHandle
58− else nonRDRandHandle
51+ else CH `fmap` nonRDRandHandle
60− nonRDRandHandle
53+ CH `fmap` nonRDRandHandle
64− nonRDRandHandle :: IO CryptHandle
56+ nonRDRandHandle :: IO Fd
65− nonRDRandHandle = error "entropy: On halvm there is no entropy other than RDRAND."
67− nonRDRandHandle :: IO CryptHandle
68− nonRDRandHandle = liftM CH (openFd source ReadOnly Nothing defaultFileFlags)
57+ nonRDRandHandle = openFd source ReadOnly Nothing defaultFileFlags
71-- |Close the `CryptHandle`
59-- |Close the `CryptHandle`
72closeHandle :: CryptHandle -> IO ()
60closeHandle :: CryptHandle -> IO ()
74closeHandle (CH h) = closeFd h
61closeHandle (CH h) = closeFd h
77−closeHandle UseRdRand = return ()
62+closeHandle (UseRdRand h) = closeFd h
80-- |Read random data from a `CryptHandle`
64-- |Read random data from a `CryptHandle`
82−hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
83−hGetEntropy UseRdRand = \n -> do
84− B.create n $ \ptr -> do
85− r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
87− (fail "RDRand failed to gather entropy")
89hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
65hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
90hGetEntropy (CH h) = fdReadBS h
66hGetEntropy (CH h) = fdReadBS h
91#ifdef HAVE_RDRAND
67#ifdef HAVE_RDRAND
92−hGetEntropy UseRdRand = \n -> do
68+hGetEntropy (UseRdRand h) = \n ->
69+ do bsURandom <- fdReadBS h n
93− B.create n $ \ptr -> do
70+ bsRDRAND <- B.create n $ \ptr -> do
94− r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
71+ r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
95− when (r /= 0)
72+ when (r /= 0)
96− (fail "RDRand failed to gather entropy")
73+ (fail "RDRand failed to gather entropy")
74+ return $ B.pack $ B.zipWith xor bsURandom bsRDRAND
99fdReadBS :: Fd -> Int -> IO B.ByteString
77fdReadBS :: Fd -> Int -> IO B.ByteString
@@ -103,8 +81,6 @@ fdReadBS fd n = do
103 case rc of
81 case rc of
104 0 -> ioError (ioeSetErrorString (mkIOError eofErrorType "fdRead" Nothing Nothing) "EOF")
82 0 -> ioError (ioeSetErrorString (mkIOError eofErrorType "fdRead" Nothing Nothing) "EOF")
105 n' -> B.packCStringLen (castPtr buf, fromIntegral n')
83 n' -> B.packCStringLen (castPtr buf, fromIntegral n')
109#ifdef HAVE_RDRAND
85#ifdef HAVE_RDRAND
110foreign import ccall unsafe "cpu_has_rdrand"
86foreign import ccall unsafe "cpu_has_rdrand"