@@ -4,7 +4,7 @@
4 Stability: beta
4 Stability: beta
5 Portability: portable
5 Portability: portable
7− Obtain entropy from system sources.
7+ Obtain entropy from system sources or x86 RDRAND when available.
10module System.EntropyWindows
10module System.EntropyWindows
@@ -12,12 +12,10 @@ module System.EntropyWindows
12 , openHandle
12 , openHandle
13 , hGetEntropy
13 , hGetEntropy
14 , closeHandle
14 , closeHandle
18import Control.Monad (liftM, when)
17import Control.Monad (liftM, when)
19import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString)
18import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString)
20−import System.Win32.Types (ULONG_PTR, errorWin)
21import Foreign (allocaBytes)
19import Foreign (allocaBytes)
22import Data.ByteString as B
20import Data.ByteString as B
23import Data.ByteString.Internal as BI
21import Data.ByteString.Internal as BI
@@ -31,26 +29,22 @@ import Foreign.Marshal.Alloc (alloca)
31import Foreign.Marshal.Utils (toBool)
29import Foreign.Marshal.Utils (toBool)
32import Foreign.Storable (peek)
30import Foreign.Storable (peek)
34−-- C example for windows rng - taken from a blog, can't recall which one but thank you!
32+{- C example for windows rng - taken from a blog, can't recall which one but thank you!
35−-- #include <Windows.h>
33+ #include <Windows.h>
36−-- #include <Wincrypt.h>
34+ #include <Wincrypt.h>
39−-- // DISCLAIMER: Don't forget to check your error codes!!
37+ // DISCLAIMER: Don't forget to check your error codes!!
40−-- // I am not checking as to make the example simple...
38+ // I am not checking as to make the example simple...
42−-- HCRYPTPROV hCryptCtx = NULL;
40+ HCRYPTPROV hCryptCtx = NULL;
43−-- BYTE randomArray[128];
41+ BYTE randomArray[128];
45−-- CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
43+ CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
46−-- CryptGenRandom(hCryptCtx, 128, randomArray);
44+ CryptGenRandom(hCryptCtx, 128, randomArray);
47−-- CryptReleaseContext(hCryptCtx, 0);
45+ CryptReleaseContext(hCryptCtx, 0);
51−-- See .cabal wrt GCC 4.8.2 asm compilation bug
55#ifdef HAVE_RDRAND
49#ifdef HAVE_RDRAND
56foreign import ccall unsafe "cpu_has_rdrand"
50foreign import ccall unsafe "cpu_has_rdrand"
@@ -63,31 +57,13 @@ cpuHasRdRand :: IO Bool
63cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand
57cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand
66−type HCRYPTPROV = ULONG_PTR
67data CryptHandle
60data CryptHandle
68− = CH HCRYPTPROV
61+ = CH Word32
71−-- | Get random values from the hardware RNG or return Nothing if no
72−-- supported hardware RNG is available.
74−-- Supported hardware:
77−hardwareRandom :: Int -> IO (Maybe B.ByteString)
78#ifdef HAVE_RDRAND
62#ifdef HAVE_RDRAND
79−hardwareRandom n =
63+ | UseRdRand Word32
82− then Just <$> BI.create n (\ptr ->
83− do r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
84− when (r /= 0) (fail "RDRand failed to gather entropy"))
87−hardwareRandom _ = pure Nothing
90−-- Define the constants we need from WinCrypt.h
66+-- Define the constants we need from WinCrypt.h
91msDefProv :: String
67msDefProv :: String
92msDefProv = "Microsoft Base Cryptographic Provider v1.0"
68msDefProv = "Microsoft Base Cryptographic Provider v1.0"
93provRSAFull :: Word32
69provRSAFull :: Word32
@@ -95,46 +71,64 @@ provRSAFull = 1
95cryptVerifyContext :: Word32
71cryptVerifyContext :: Word32
96cryptVerifyContext = fromIntegral 0xF0000000
72cryptVerifyContext = fromIntegral 0xF0000000
98−-- Declare the required CryptoAPI imports
74+-- Declare the required CryptoAPI imports
99foreign import stdcall unsafe "CryptAcquireContextA"
75foreign import stdcall unsafe "CryptAcquireContextA"
100− c_cryptAcquireCtx :: Ptr HCRYPTPROV -> CString -> CString -> Word32 -> Word32 -> IO Int32
76+ c_cryptAcquireCtx :: Ptr Word32 -> CString -> CString -> Word32 -> Word32 -> IO Int32
101foreign import stdcall unsafe "CryptGenRandom"
77foreign import stdcall unsafe "CryptGenRandom"
102− c_cryptGenRandom :: HCRYPTPROV -> Word32 -> Ptr Word8 -> IO Int32
78+ c_cryptGenRandom :: Word32 -> Word32 -> Ptr Word8 -> IO Int32
103foreign import stdcall unsafe "CryptReleaseContext"
79foreign import stdcall unsafe "CryptReleaseContext"
104− c_cryptReleaseCtx :: HCRYPTPROV -> Word32 -> IO Int32
80+ c_cryptReleaseCtx :: Word32 -> Word32 -> IO Int32
106−cryptAcquireCtx :: IO HCRYPTPROV
82+cryptAcquireCtx :: IO Word32
107cryptAcquireCtx =
83cryptAcquireCtx =
108− alloca $ \handlePtr ->
84+ alloca $ \handlePtr ->
109 withCString msDefProv $ \provName -> do
85 withCString msDefProv $ \provName -> do
110 stat <- c_cryptAcquireCtx handlePtr nullPtr provName provRSAFull cryptVerifyContext
86 stat <- c_cryptAcquireCtx handlePtr nullPtr provName provRSAFull cryptVerifyContext
111 if (toBool stat)
87 if (toBool stat)
112 then peek handlePtr
88 then peek handlePtr
113− else errorWin "c_cryptAcquireCtx"
89+ else fail "c_cryptAcquireCtx"
115−cryptGenRandom :: HCRYPTPROV -> Int -> IO B.ByteString
91+cryptGenRandom :: Word32 -> Int -> IO B.ByteString
116−cryptGenRandom h i =
92+cryptGenRandom h i =
117 BI.create i $ \c_buffer -> do
93 BI.create i $ \c_buffer -> do
118 stat <- c_cryptGenRandom h (fromIntegral i) c_buffer
94 stat <- c_cryptGenRandom h (fromIntegral i) c_buffer
119 if (toBool stat)
95 if (toBool stat)
120 then return ()
96 then return ()
121− else errorWin "c_cryptGenRandom"
97+ else fail "c_cryptGenRandom"
123−cryptReleaseCtx :: HCRYPTPROV -> IO ()
99+cryptReleaseCtx :: Word32 -> IO ()
124cryptReleaseCtx h = do
100cryptReleaseCtx h = do
125 stat <- c_cryptReleaseCtx h 0
101 stat <- c_cryptReleaseCtx h 0
126 if (toBool stat)
102 if (toBool stat)
127 then return ()
103 then return ()
128− else errorWin "c_cryptReleaseCtx"
104+ else fail "c_cryptReleaseCtx"
130-- |Open a handle from which random data can be read
106-- |Open a handle from which random data can be read
131openHandle :: IO CryptHandle
107openHandle :: IO CryptHandle
111+ if b then UseRdRand `fmap` cryptAcquireCtx
132−openHandle = CH `fmap` cryptAcquireCtx
114+ CH `fmap` cryptAcquireCtx
134-- |Close the `CryptHandle`
116-- |Close the `CryptHandle`
135closeHandle :: CryptHandle -> IO ()
117closeHandle :: CryptHandle -> IO ()
136closeHandle (CH h) = cryptReleaseCtx h
118closeHandle (CH h) = cryptReleaseCtx h
120+closeHandle (UseRdRand h) = cryptReleaseCtx h
138-- |Read from `CryptHandle`
123-- |Read from `CryptHandle`
139−hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
124+hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
140hGetEntropy (CH h) n = cryptGenRandom h n
125hGetEntropy (CH h) n = cryptGenRandom h n
127+hGetEntropy (UseRdRand h) n =
128+ do bsRDRAND <- BI.create n $ \ptr -> do
129+ r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
131+ (fail "RDRand failed to gather entropy")
132+ bsWinCrypt <- cryptGenRandom h n
133+ return $ B.pack $ B.zipWith xor bsRDRAND bsWinCrypt