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