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