@@ -13,6 +13,7 @@ module System.EntropyNix
1313 , openHandle
1414 , hGetEntropy
1515 , closeHandle
16+ , hardwareRNG
1617 ) where
1718
1819import Control.Monad (liftM, when)
@@ -38,20 +39,29 @@ source = "/dev/urandom"
3839-- |Handle for manual resource management
3940data CryptHandle
4041 = CH Fd
42+
43+-- | Get random values from the hardward RNG or return Nothing if no
44+-- supported hardware RNG is available.
45+--
46+-- Supported hardware:
47+-- * RDRAND
48+-- * Patches welcome
49+hardwareRNG :: Int -> IO (Maybe B.ByteString)
4150#ifdef HAVE_RDRAND
42− | UseRdRand Fd
51+hardwareRNG n =
52+ do b <- cpuHasRdRand
53+ if b
54+ then Just <$> B.create n (\ptr ->
55+ do r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
56+ when (r /= 0) (fail "RDRand failed to gather entropy"))
57+ else pure Nothing
58+#else
59+hardwareRNG _ = pure Nothing
4360#endif
4461
4562-- |Open a `CryptHandle`
4663openHandle :: IO CryptHandle
47−openHandle = do
48−#ifdef HAVE_RDRAND
49− b <- cpuHasRdRand
50− if b then UseRdRand `fmap` nonRDRandHandle
51− else CH `fmap` nonRDRandHandle
52−#else
53− CH `fmap` nonRDRandHandle
54−#endif
64+openHandle = do CH `fmap` nonRDRandHandle
5565 where
5666 nonRDRandHandle :: IO Fd
5767 nonRDRandHandle = openFd source ReadOnly Nothing defaultFileFlags
@@ -59,22 +69,10 @@ openHandle = do
5969-- |Close the `CryptHandle`
6070closeHandle :: CryptHandle -> IO ()
6171closeHandle (CH h) = closeFd h
62−#ifdef HAVE_RDRAND
63−closeHandle (UseRdRand h) = closeFd h
64−#endif
6572
6673-- |Read random data from a `CryptHandle`
6774hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
6875hGetEntropy (CH h) = fdReadBS h
69−#ifdef HAVE_RDRAND
70−hGetEntropy (UseRdRand h) = \n ->
71− do bsURandom <- fdReadBS h n
72− bsRDRAND <- B.create n $ \ptr -> do
73− r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
74− when (r /= 0)
75− (fail "RDRand failed to gather entropy")
76− return $ B.pack $ B.zipWith xor bsURandom bsRDRAND
77−#endif
7876
7977fdReadBS :: Fd -> Int -> IO B.ByteString
8078fdReadBS fd n =