git.iko.soy/typeable/entropy
on 750063e help sign in
170 lines 5.1 kB history raw
1{-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-}
2{-|
3 Maintainer: Thomas.DuBuisson@gmail.com
4 Stability: beta
5 Portability: portable
7 Obtain entropy from system sources.
8 Currently, windows and *nix systems with a /dev/urandom are supported.
9-}
11module System.Entropy
12 ( getEntropy
13 , CryptHandle
14 , openHandle
15 , hGetEntropy
16 , closeHandle
17 ) where
19import Control.Monad (liftM, when)
20import Data.ByteString as B
21import System.IO (openFile, hClose, IOMode(..), Handle, withBinaryFile)
23#if defined(isWindows)
24{- C example for windows rng - taken from a blog, can't recall which one but thank you!
25 #include <Windows.h>
26 #include <Wincrypt.h>
27 ...
28 //
29 // DISCLAIMER: Don't forget to check your error codes!!
30 // I am not checking as to make the example simple...
31 //
32 HCRYPTPROV hCryptCtx = NULL;
33 BYTE randomArray[128];
35 CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
36 CryptGenRandom(hCryptCtx, 128, randomArray);
37 CryptReleaseContext(hCryptCtx, 0);
38-}
40import Data.ByteString.Internal as BI
41import Data.Int (Int32)
42import Data.Word (Word32, Word8)
43import Foreign.C.String (CString, withCString)
44import Foreign.Ptr (Ptr, nullPtr)
45import Foreign.Marshal.Alloc (alloca)
46import Foreign.Marshal.Utils (toBool)
47import Foreign.Storable (peek)
49newtype CryptHandle = CH Word32
51-- Define the constants we need from WinCrypt.h
52msDefProv :: String
53msDefProv = "Microsoft Base Cryptographic Provider v1.0"
54provRSAFull :: Word32
55provRSAFull = fromIntegral 1
56cryptVerifyContext :: Word32
57cryptVerifyContext = fromIntegral 0xF0000000
59-- Declare the required CryptoAPI imports
60foreign import stdcall unsafe "CryptAcquireContextA"
61 c_cryptAcquireCtx :: Ptr Word32 -> CString -> CString -> Word32 -> Word32 -> IO Int32
62foreign import stdcall unsafe "CryptGenRandom"
63 c_cryptGenRandom :: Word32 -> Word32 -> Ptr Word8 -> IO Int32
64foreign import stdcall unsafe "CryptReleaseContext"
65 c_cryptReleaseCtx :: Word32 -> Word32 -> IO Int32
67cryptAcquireCtx :: IO Word32
68cryptAcquireCtx =
69 alloca $ \handlePtr ->
70 withCString msDefProv $ \provName -> do
71 stat <- c_cryptAcquireCtx handlePtr nullPtr provName (fromIntegral 1) (fromIntegral cryptVerifyContext)
72 if (toBool stat)
73 then peek handlePtr
74 else fail "c_cryptAcquireCtx"
76cryptGenRandom :: Word32 -> Int -> IO B.ByteString
77cryptGenRandom h i =
78 BI.create i $ \c_buffer -> do
79 stat <- c_cryptGenRandom (fromIntegral h) (fromIntegral i) c_buffer
80 if (toBool stat)
81 then return ()
82 else fail "c_cryptGenRandom"
84cryptReleaseCtx :: Word32 -> IO ()
85cryptReleaseCtx h = do
86 stat <- c_cryptReleaseCtx h 0
87 if (toBool stat)
88 then return ()
89 else fail "c_cryptReleaseCtx"
91-- |Inefficiently get a specific number of bytes of cryptographically
92-- secure random data using the system-specific facilities.
93--
94-- This function will return zero bytes
95-- on platforms without a secure RNG!
96getEntropy :: Int -> IO B.ByteString
97getEntropy n = do
98 h <- cryptAcquireCtx
99 bs <- cryptGenRandom h n
100 let !bs' = bs
101 cryptReleaseCtx h
102 return bs'
104-- |Open a handle from which random data can be read
105openHandle :: IO CryptHandle
106openHandle = liftM CH cryptAcquireCtx
108-- |Close the `CryptHandle`
109closeHandle (CH h) = cryptReleaseCtx h
111-- |Read from `CryptHandle`
112hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
113hGetEntropy (CH h) = cryptGenRandom h
115#else
116{- Not windows, assuming nix with a /dev/urandom -}
117import Foreign.C.Types
118import Foreign.Ptr
119import Data.ByteString.Internal as B
121source :: FilePath
122source = "/dev/urandom"
124-- |Handle for manual resource mangement
125newtype CryptHandle = CH Handle
127-- |Open a `CryptHandle`
128openHandle :: IO CryptHandle
129openHandle = liftM CH (openFile source ReadMode)
131-- |Close the `CryptHandle`
132closeHandle :: CryptHandle -> IO ()
133closeHandle (CH h) = hClose h
135-- |Read random data from a `CryptHandle`
136hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
137hGetEntropy (CH h) = B.hGet h
139#ifdef arch_x86_64
140foreign import ccall unsafe "cpu_has_rdrand"
141 c_cpu_has_rdrand :: IO CInt
143foreign import ccall unsafe "get_rand_bytes"
144 c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt
146cpuHasRdRand :: IO Bool
147cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand
148#endif
150-- |Inefficiently get a specific number of bytes of cryptographically
151-- secure random data using the system-specific facilities.
152--
153-- Use '/dev/urandom' on *nix and CryptAPI when on Windows. In short,
154-- this entropy is considered "cryptographically secure" but not true
155-- entropy.
156getEntropy :: Int -> IO B.ByteString
157getEntropy n = do
158#if arch_x86_64
159 b <- cpuHasRdRand
160 if b then B.create n $ \ptr -> do
161 r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)
162 when (r /= 0)
163 (fail "RDRand failed to gather entropy")
164 else do
165#endif
166{- arch_x86 -}
167 withBinaryFile source ReadMode (`B.hGet` n)
168#endif
169{- OS Test -}