git.iko.soy/typeable/entropy
on f5b8563 help sign in
143 lines 4.4 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)
20import Data.ByteString as B
21import Data.ByteString.Lazy as L
22import System.IO (openFile, hClose, IOMode(..), Handle, withBinaryFile)
24#if defined(isWindows)
25{- C example for windows rng - taken from a blog, can't recall which one but thank you!
26 #include <Windows.h>
27 #include <Wincrypt.h>
28 ...
29 //
30 // DISCLAIMER: Don't forget to check your error codes!!
31 // I am not checking as to make the example simple...
32 //
33 HCRYPTPROV hCryptCtx = NULL;
34 BYTE randomArray[128];
36 CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
37 CryptGenRandom(hCryptCtx, 128, randomArray);
38 CryptReleaseContext(hCryptCtx, 0);
39-}
41import Data.ByteString.Internal as BI
42import Data.Int (Int32)
43import Data.Word (Word32, Word8)
44import Foreign.C.String (CString, withCString)
45import Foreign.Ptr (Ptr, nullPtr)
46import Foreign.Marshal.Alloc (alloca)
47import Foreign.Marshal.Utils (toBool)
48import Foreign.Storable (peek)
50newtype CryptHandle = CH Word32
52-- Define the constants we need from WinCrypt.h
53msDefProv :: String
54msDefProv = "Microsoft Base Cryptographic Provider v1.0"
55provRSAFull :: Word32
56provRSAFull = fromIntegral 1
57cryptVerifyContext :: Word32
58cryptVerifyContext = fromIntegral 0xF0000000
60-- Declare the required CryptoAPI imports
61foreign import stdcall unsafe "CryptAcquireContextA"
62 c_cryptAcquireCtx :: Ptr Word32 -> CString -> CString -> Word32 -> Word32 -> IO Int32
63foreign import stdcall unsafe "CryptGenRandom"
64 c_cryptGenRandom :: Word32 -> Word32 -> Ptr Word8 -> IO Int32
65foreign import stdcall unsafe "CryptReleaseContext"
66 c_cryptReleaseCtx :: Word32 -> Word32 -> IO Int32
68cryptAcquireCtx :: IO Word32
69cryptAcquireCtx =
70 alloca $ \handlePtr ->
71 withCString msDefProv $ \provName -> do
72 stat <- c_cryptAcquireCtx handlePtr nullPtr provName (fromIntegral 1) (fromIntegral cryptVerifyContext)
73 if (toBool stat)
74 then peek handlePtr
75 else fail "c_cryptAcquireCtx"
77cryptGenRandom :: Word32 -> Int -> IO B.ByteString
78cryptGenRandom h i =
79 BI.create i $ \c_buffer -> do
80 stat <- c_cryptGenRandom (fromIntegral h) (fromIntegral i) c_buffer
81 if (toBool stat)
82 then return ()
83 else fail "c_cryptGenRandom"
85cryptReleaseCtx :: Word32 -> IO ()
86cryptReleaseCtx h = do
87 stat <- c_cryptReleaseCtx h 0
88 if (toBool stat)
89 then return ()
90 else fail "c_cryptReleaseCtx"
92-- |Inefficiently get a specific number of bytes of cryptographically
93-- secure random data using the system-specific facilities.
94--
95-- This function will return zero bytes
96-- on platforms without a secure RNG!
97getEntropy :: Int -> IO B.ByteString
98getEntropy n = do
99 h <- cryptAcquireCtx
100 bs <- cryptGenRandom h n
101 let !bs' = bs
102 cryptReleaseCtx h
103 return bs'
105-- |Open a handle from which random data can be read
106openHandle :: IO CryptHandle
107openHandle = liftM CH cryptAcquireCtx
109-- |Close the `CryptHandle`
110closeHandle (CH h) = cryptReleaseCtx h
112-- |Read from `CryptHandle`
113hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
114hGetEntropy (CH h) = cryptGenRandom h
116#else
117source = "/dev/urandom"
119-- |Handle for manual resource mangement
120newtype CryptHandle = CH Handle
122-- |Open a `CryptHandle`
123openHandle :: IO CryptHandle
124openHandle = liftM CH (openFile source ReadMode)
126-- |Close the `CryptHandle`
127closeHandle :: CryptHandle -> IO ()
128closeHandle (CH h) = hClose h
130-- |Read random data from a `CryptHandle`
131hGetEntropy :: CryptHandle -> Int -> IO B.ByteString
132hGetEntropy (CH h) = B.hGet h
134-- |Inefficiently get a specific number of bytes of cryptographically
135-- secure random data using the system-specific facilities.
136--
137-- Use '/dev/urandom' on *nix and CryptAPI when on Windows. In short,
138-- this entropy is considered "cryptographically secure" but not true
139-- entropy.
140getEntropy :: Int -> IO B.ByteString
141getEntropy n = withBinaryFile source ReadMode (`B.hGet` n)
142#endif