@@ -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,14 +12,17 @@ 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
22import Data.Int (Int32)
24import Data.Int (Int32)
25+import Data.Bits (xor)
23import Data.Word (Word32, Word8)
26import Data.Word (Word32, Word8)
24import Foreign.C.String (CString, withCString)
27import Foreign.C.String (CString, withCString)
25import Foreign.C.Types
28import Foreign.C.Types
@@ -28,22 +31,26 @@ import Foreign.Marshal.Alloc (alloca)
28import Foreign.Marshal.Utils (toBool)
31import Foreign.Marshal.Utils (toBool)
29import Foreign.Storable (peek)
32import Foreign.Storable (peek)
31−{- 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!
32− #include <Windows.h>
35+-- #include <Windows.h>
33− #include <Wincrypt.h>
36+-- #include <Wincrypt.h>
36− // DISCLAIMER: Don't forget to check your error codes!!
39+-- // DISCLAIMER: Don't forget to check your error codes!!
37− // I am not checking as to make the example simple...
40+-- // I am not checking as to make the example simple...
39− HCRYPTPROV hCryptCtx = NULL;
42+-- HCRYPTPROV hCryptCtx = NULL;
40− BYTE randomArray[128];
43+-- BYTE randomArray[128];
42− CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
45+-- CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
43− CryptGenRandom(hCryptCtx, 128, randomArray);
46+-- CryptGenRandom(hCryptCtx, 128, randomArray);
44− CryptReleaseContext(hCryptCtx, 0);
47+-- CryptReleaseContext(hCryptCtx, 0);
51+-- See .cabal wrt GCC 4.8.2 asm compilation bug
48#ifdef HAVE_RDRAND
55#ifdef HAVE_RDRAND
49foreign import ccall unsafe "cpu_has_rdrand"
56foreign import ccall unsafe "cpu_has_rdrand"
@@ -56,72 +63,78 @@ cpuHasRdRand :: IO Bool
56cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand
63cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand
66+type HCRYPTPROV = ULONG_PTR
59data CryptHandle
67data CryptHandle
60− = 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)
61#ifdef HAVE_RDRAND
78#ifdef HAVE_RDRAND
62− | UseRdRand
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
65−-- Define the constants we need from WinCrypt.h
90+-- Define the constants we need from WinCrypt.h
66msDefProv :: String
91msDefProv :: String
67msDefProv = "Microsoft Base Cryptographic Provider v1.0"
92msDefProv = "Microsoft Base Cryptographic Provider v1.0"
68provRSAFull :: Word32
93provRSAFull :: Word32
69−provRSAFull = fromIntegral 1
94+provRSAFull = 1
70cryptVerifyContext :: Word32
95cryptVerifyContext :: Word32
71cryptVerifyContext = fromIntegral 0xF0000000
96cryptVerifyContext = fromIntegral 0xF0000000
73−-- Declare the required CryptoAPI imports
98+-- Declare the required CryptoAPI imports
74foreign import stdcall unsafe "CryptAcquireContextA"
99foreign import stdcall unsafe "CryptAcquireContextA"
75− c_cryptAcquireCtx :: Ptr Word32 -> CString -> CString -> Word32 -> Word32 -> IO Int32
100+ c_cryptAcquireCtx :: Ptr HCRYPTPROV -> CString -> CString -> Word32 -> Word32 -> IO Int32
76foreign import stdcall unsafe "CryptGenRandom"
101foreign import stdcall unsafe "CryptGenRandom"
77− c_cryptGenRandom :: Word32 -> Word32 -> Ptr Word8 -> IO Int32
102+ c_cryptGenRandom :: HCRYPTPROV -> Word32 -> Ptr Word8 -> IO Int32
78foreign import stdcall unsafe "CryptReleaseContext"
103foreign import stdcall unsafe "CryptReleaseContext"
79− c_cryptReleaseCtx :: Word32 -> Word32 -> IO Int32
104+ c_cryptReleaseCtx :: HCRYPTPROV -> Word32 -> IO Int32
81−cryptAcquireCtx :: IO Word32
106+cryptAcquireCtx :: IO HCRYPTPROV
82−cryptAcquireCtx =
107+cryptAcquireCtx =
83− alloca $ \handlePtr ->
108+ alloca $ \handlePtr ->
84 withCString msDefProv $ \provName -> do
109 withCString msDefProv $ \provName -> do
85− stat <- c_cryptAcquireCtx handlePtr nullPtr provName (fromIntegral 1) (fromIntegral cryptVerifyContext)
110+ stat <- c_cryptAcquireCtx handlePtr nullPtr provName provRSAFull cryptVerifyContext
86 if (toBool stat)
111 if (toBool stat)
87 then peek handlePtr
112 then peek handlePtr
88− else fail "c_cryptAcquireCtx"
113+ else errorWin "c_cryptAcquireCtx"
90−cryptGenRandom :: Word32 -> Int -> IO B.ByteString
115+cryptGenRandom :: HCRYPTPROV -> Int -> IO B.ByteString
91−cryptGenRandom h i =
116+cryptGenRandom h i =
92 BI.create i $ \c_buffer -> do
117 BI.create i $ \c_buffer -> do
93− stat <- c_cryptGenRandom (fromIntegral h) (fromIntegral i) c_buffer
118+ stat <- c_cryptGenRandom h (fromIntegral i) c_buffer
94 if (toBool stat)
119 if (toBool stat)
95 then return ()
120 then return ()
96− else fail "c_cryptGenRandom"
121+ else errorWin "c_cryptGenRandom"
98−cryptReleaseCtx :: Word32 -> IO ()
123+cryptReleaseCtx :: HCRYPTPROV -> IO ()
99cryptReleaseCtx h = do
124cryptReleaseCtx h = do
100 stat <- c_cryptReleaseCtx h 0
125 stat <- c_cryptReleaseCtx h 0
101 if (toBool stat)
126 if (toBool stat)
102 then return ()
127 then return ()
103− else fail "c_cryptReleaseCtx"
128+ else errorWin "c_cryptReleaseCtx"
105-- |Open a handle from which random data can be read
130-- |Open a handle from which random data can be read
106openHandle :: IO CryptHandle
131openHandle :: IO CryptHandle
110− if b then return UseRdRand
113− liftM CH cryptAcquireCtx
132+openHandle = CH `fmap` cryptAcquireCtx
115-- |Close the `CryptHandle`
134-- |Close the `CryptHandle`
135+closeHandle :: CryptHandle -> IO ()
116−closeHandle (CH h) = cryptReleaseCtx h
136+closeHandle (CH h) = cryptReleaseCtx h
118-- |Read from `CryptHandle`
138-- |Read from `CryptHandle`
119−hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
139+hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
120hGetEntropy (CH h) n = cryptGenRandom h n
140hGetEntropy (CH h) n = cryptGenRandom h n
122−hGetEntropy UseRdRand n =
123− BI.create n $ \ptr -> do
124− r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
126− (fail "RDRand failed to gather entropy")