entropy 0.2.2.4 → 0.3
raw patch · 6 files changed
+279/−207 lines, 6 filesdep ~basesetup-changed
Dependency ranges changed: base
Files
- README.md +2/−0
- Setup.hs +1/−1
- System/Entropy.hs +17/−200
- System/EntropyNix.hs +111/−0
- System/EntropyWindows.hs +128/−0
- entropy.cabal +20/−6
README.md view
@@ -3,3 +3,5 @@ This package allows Haskell users to easily acquire entropy for use in critical security applications by calling out to either windows crypto api, unix/linux's `/dev/urandom`, or the RDRAND instruction.++This package supports Windows, {li,u}nix, QNX, and has preliminary support for HaLVM.
Setup.hs view
@@ -29,7 +29,7 @@ cArgs = ["-DHAVE_RDRAND"] cArgsHC :: [String]-cArgsHC = map ("-optc" ++) cArgs+cArgsHC = cArgs ++ map ("-optc" ++) cArgs canUseRDRAND :: FilePath -> IO Bool canUseRDRAND cc = do
System/Entropy.hs view
@@ -4,216 +4,35 @@ Stability: beta Portability: portable - Obtain entropy from system sources.- Currently, windows and *nix systems with a @/dev/urandom@ are supported.--}--module System.Entropy- ( getEntropy- , CryptHandle- , openHandle- , hGetEntropy- , closeHandle- ) where--import Control.Monad (liftM)-import Data.ByteString as B-import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString)-import Foreign (allocaBytes)+ Obtain entropy from system sources or x86 RDRAND when available. -#if defined(isWindows)-{- C example for windows rng - taken from a blog, can't recall which one but thank you!- #include <Windows.h>- #include <Wincrypt.h>- ...- //- // DISCLAIMER: Don't forget to check your error codes!!- // I am not checking as to make the example simple...- //- HCRYPTPROV hCryptCtx = NULL;- BYTE randomArray[128];+ Currently supporting: - CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);- CryptGenRandom(hCryptCtx, 128, randomArray);- CryptReleaseContext(hCryptCtx, 0);+ - Windows via CryptoAPO+ - *nix systems via @\/dev\/urandom@+ - QNX+ - Xen only when RDRAND is available. -} -import Data.ByteString.Internal as BI-import Data.Int (Int32)-import Data.Word (Word32, Word8)-import Foreign.C.String (CString, withCString)-import Foreign.C.Types-import Foreign.Ptr (Ptr, nullPtr, castPtr)-import Foreign.Marshal.Alloc (alloca)-import Foreign.Marshal.Utils (toBool)-import Foreign.Storable (peek)--#ifdef HAVE_RDRAND-foreign import ccall unsafe "cpu_has_rdrand"- c_cpu_has_rdrand :: IO CInt--foreign import ccall unsafe "get_rand_bytes"- c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt--cpuHasRdRand :: IO Bool-cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand-#endif--data CryptHandle- = CH Word32-#ifdef HAVE_RDRAND- | UseRdRand-#endif---- Define the constants we need from WinCrypt.h -msDefProv :: String-msDefProv = "Microsoft Base Cryptographic Provider v1.0"-provRSAFull :: Word32-provRSAFull = fromIntegral 1-cryptVerifyContext :: Word32-cryptVerifyContext = fromIntegral 0xF0000000---- Declare the required CryptoAPI imports -foreign import stdcall unsafe "CryptAcquireContextA"- c_cryptAcquireCtx :: Ptr Word32 -> CString -> CString -> Word32 -> Word32 -> IO Int32-foreign import stdcall unsafe "CryptGenRandom"- c_cryptGenRandom :: Word32 -> Word32 -> Ptr Word8 -> IO Int32-foreign import stdcall unsafe "CryptReleaseContext"- c_cryptReleaseCtx :: Word32 -> Word32 -> IO Int32--cryptAcquireCtx :: IO Word32-cryptAcquireCtx = - alloca $ \handlePtr -> - withCString msDefProv $ \provName -> do- stat <- c_cryptAcquireCtx handlePtr nullPtr provName (fromIntegral 1) (fromIntegral cryptVerifyContext)- if (toBool stat)- then peek handlePtr- else fail "c_cryptAcquireCtx"--cryptGenRandom :: Word32 -> Int -> IO B.ByteString-cryptGenRandom h i = - BI.create i $ \c_buffer -> do- stat <- c_cryptGenRandom (fromIntegral h) (fromIntegral i) c_buffer- if (toBool stat)- then return ()- else fail "c_cryptGenRandom"--cryptReleaseCtx :: Word32 -> IO ()-cryptReleaseCtx h = do- stat <- c_cryptReleaseCtx h 0- if (toBool stat)- then return ()- else fail "c_cryptReleaseCtx"---- |Inefficiently get a specific number of bytes of cryptographically--- secure random data using the system-specific facilities.------ This function will return zero bytes--- on platforms without a secure RNG!-getEntropy :: Int -> IO B.ByteString-getEntropy n = do-#ifdef HAVE_RDRAND- b <- cpuHasRdRand- if b then hGetEntropy UseRdRand n- else do-#endif- h <- cryptAcquireCtx- bs <- cryptGenRandom h n- let !bs' = bs- cryptReleaseCtx h- return bs'---- |Open a handle from which random data can be read-openHandle :: IO CryptHandle-openHandle = do-#ifdef HAVE_RDRAND- b <- cpuHasRdRand- if b then return UseRdRand- else do-#endif- liftM CH cryptAcquireCtx---- |Close the `CryptHandle`-closeHandle (CH h) = cryptReleaseCtx h---- |Read from `CryptHandle`-hGetEntropy :: CryptHandle -> Int -> IO B.ByteString -hGetEntropy (CH h) = cryptGenRandom h-#ifdef HAVE_RDRAND-hGetEntropy UseRdRand =- B.create n $ \ptr -> do- r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)- when (r /= 0)- (fail "RDRand failed to gather entropy")-#endif-+module System.Entropy+ ( getEntropy,+#if defined(isWindows)+ module System.EntropyWindows+ ) where+import System.EntropyWindows #else-{- Not windows, assuming nix with a /dev/urandom -}-import System.Posix (openFd, closeFd, fdReadBuf, OpenMode(..), defaultFileFlags, Fd)-import Foreign.Ptr--source :: FilePath-source = "/dev/urandom"---- |Handle for manual resource mangement-data CryptHandle- = CH Fd-#ifdef HAVE_RDRAND- | UseRdRand-#endif---- |Open a `CryptHandle`-openHandle :: IO CryptHandle-openHandle = do-#ifdef HAVE_RDRAND- b <- cpuHasRdRand- if b then return UseRdRand- else do-#endif- liftM CH (openFd source ReadOnly Nothing defaultFileFlags)---- |Close the `CryptHandle`-closeHandle :: CryptHandle -> IO ()-closeHandle (CH h) = closeFd h-#ifdef HAVE_RDRAND-closeHandle UseRdRand = return ()-#endif--fdReadBS :: Fd -> Int -> IO B.ByteString-fdReadBS fd n = do- allocaBytes n $ \buf -> do- rc <- fdReadBuf fd buf (fromIntegral n)- case rc of- 0 -> ioError (ioeSetErrorString (mkIOError eofErrorType "fdRead" Nothing Nothing) "EOF")- n' -> B.packCStringLen (castPtr buf, fromIntegral n')---- |Read random data from a `CryptHandle`-hGetEntropy :: CryptHandle -> Int -> IO B.ByteString-hGetEntropy (CH h) = fdReadBS h-#ifdef HAVE_RDRAND-hGetEntropy UseRdRand = \n -> do- B.create n $ \ptr -> do- r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)- when (r /= 0)- (fail "RDRand failed to gather entropy")+ module System.EntropyNix+ ) where+import System.EntropyNix #endif -#ifdef HAVE_RDRAND-foreign import ccall unsafe "cpu_has_rdrand"- c_cpu_has_rdrand :: IO CInt--foreign import ccall unsafe "get_rand_bytes"- c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt--cpuHasRdRand :: IO Bool-cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand-#endif+import qualified Data.ByteString as B -- |Inefficiently get a specific number of bytes of cryptographically -- secure random data using the system-specific facilities. -- -- Use '/dev/urandom' on *nix and CryptAPI when on Windows. In short,--- this entropy is considered "cryptographically secure" but not true+-- this entropy is considered cryptographically secure but not true -- entropy. getEntropy :: Int -> IO B.ByteString getEntropy n = do@@ -221,5 +40,3 @@ e <- hGetEntropy h n closeHandle h return e-#endif-{- OS Test -}
+ System/EntropyNix.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-}+{-|+ Maintainer: Thomas.DuBuisson@gmail.com+ Stability: beta+ Portability: portable++ Obtain entropy from system sources or x86 RDRAND when available.++-}++module System.EntropyNix+ ( CryptHandle+ , openHandle+ , hGetEntropy+ , closeHandle+ ) where++import Control.Monad (liftM, when)+import Data.ByteString as B+import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString)++import Foreign (allocaBytes)+import Foreign.Ptr+import Foreign.C.Types+import Data.ByteString.Internal as B++#ifdef XEN+data CryptHandle = UseRdRand -- or die trying+#else++import System.Posix (openFd, closeFd, fdReadBuf, OpenMode(..), defaultFileFlags, Fd)++source :: FilePath+source = "/dev/urandom"++-- |Handle for manual resource mangement+data CryptHandle+ = CH Fd+#ifdef HAVE_RDRAND+ | UseRdRand+#endif+#endif++-- |Open a `CryptHandle`+openHandle :: IO CryptHandle+openHandle = do+#ifdef HAVE_RDRAND+ b <- cpuHasRdRand+ if b then return UseRdRand+ else nonRDRandHandle+#else+ nonRDRandHandle+#endif+ where+#ifdef XEN+ nonRDRandHandle :: IO CryptHandle+ nonRDRandHandle = error "entropy: On halvm there is no entropy other than RDRAND."+#else+ nonRDRandHandle :: IO CryptHandle+ nonRDRandHandle = liftM CH (openFd source ReadOnly Nothing defaultFileFlags)+#endif++-- |Close the `CryptHandle`+closeHandle :: CryptHandle -> IO ()+#ifndef XEN+closeHandle (CH h) = closeFd h+#endif+#ifdef HAVE_RDRAND+closeHandle UseRdRand = return ()+#endif++-- |Read random data from a `CryptHandle`+#ifdef XEN+hGetEntropy :: CryptHandle -> Int -> IO B.ByteString+hGetEntropy UseRdRand = \n -> do+ B.create n $ \ptr -> do+ r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)+ when (r /= 0)+ (fail "RDRand failed to gather entropy")+#else+hGetEntropy :: CryptHandle -> Int -> IO B.ByteString+hGetEntropy (CH h) = fdReadBS h+#ifdef HAVE_RDRAND+hGetEntropy UseRdRand = \n -> do+ B.create n $ \ptr -> do+ r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)+ when (r /= 0)+ (fail "RDRand failed to gather entropy")+#endif++fdReadBS :: Fd -> Int -> IO B.ByteString+fdReadBS fd n = do+ allocaBytes n $ \buf -> do+ rc <- fdReadBuf fd buf (fromIntegral n)+ case rc of+ 0 -> ioError (ioeSetErrorString (mkIOError eofErrorType "fdRead" Nothing Nothing) "EOF")+ n' -> B.packCStringLen (castPtr buf, fromIntegral n')+#endif+++#ifdef HAVE_RDRAND+foreign import ccall unsafe "cpu_has_rdrand"+ c_cpu_has_rdrand :: IO CInt++foreign import ccall unsafe "get_rand_bytes"+ c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt++cpuHasRdRand :: IO Bool+cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand+#endif+
+ System/EntropyWindows.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables #-}+{-|+ Maintainer: Thomas.DuBuisson@gmail.com+ Stability: beta+ Portability: portable++ Obtain entropy from system sources or x86 RDRAND when available.+-}++module System.EntropyWindows+ ( CryptHandle+ , openHandle+ , hGetEntropy+ , closeHandle+ ) where++import Control.Monad (liftM, when)+import System.IO.Error (mkIOError, eofErrorType, ioeSetErrorString)+import Foreign (allocaBytes)+import Data.ByteString as B+import Data.ByteString.Internal as BI+import Data.Int (Int32)+import Data.Word (Word32, Word8)+import Foreign.C.String (CString, withCString)+import Foreign.C.Types+import Foreign.Ptr (Ptr, nullPtr, castPtr)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Marshal.Utils (toBool)+import Foreign.Storable (peek)++{- C example for windows rng - taken from a blog, can't recall which one but thank you!+ #include <Windows.h>+ #include <Wincrypt.h>+ ...+ //+ // DISCLAIMER: Don't forget to check your error codes!!+ // I am not checking as to make the example simple...+ //+ HCRYPTPROV hCryptCtx = NULL;+ BYTE randomArray[128];++ CryptAcquireContext(&hCryptCtx, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);+ CryptGenRandom(hCryptCtx, 128, randomArray);+ CryptReleaseContext(hCryptCtx, 0);+-}+++#ifdef HAVE_RDRAND+foreign import ccall unsafe "cpu_has_rdrand"+ c_cpu_has_rdrand :: IO CInt++foreign import ccall unsafe "get_rand_bytes"+ c_get_rand_bytes :: Ptr CUChar -> CSize -> IO CInt++cpuHasRdRand :: IO Bool+cpuHasRdRand = (/= 0) `fmap` c_cpu_has_rdrand+#endif++data CryptHandle+ = CH Word32+#ifdef HAVE_RDRAND+ | UseRdRand+#endif++-- Define the constants we need from WinCrypt.h +msDefProv :: String+msDefProv = "Microsoft Base Cryptographic Provider v1.0"+provRSAFull :: Word32+provRSAFull = 1+cryptVerifyContext :: Word32+cryptVerifyContext = fromIntegral 0xF0000000++-- Declare the required CryptoAPI imports +foreign import stdcall unsafe "CryptAcquireContextA"+ c_cryptAcquireCtx :: Ptr Word32 -> CString -> CString -> Word32 -> Word32 -> IO Int32+foreign import stdcall unsafe "CryptGenRandom"+ c_cryptGenRandom :: Word32 -> Word32 -> Ptr Word8 -> IO Int32+foreign import stdcall unsafe "CryptReleaseContext"+ c_cryptReleaseCtx :: Word32 -> Word32 -> IO Int32++cryptAcquireCtx :: IO Word32+cryptAcquireCtx =+ alloca $ \handlePtr -> + withCString msDefProv $ \provName -> do+ stat <- c_cryptAcquireCtx handlePtr nullPtr provName provRSAFull cryptVerifyContext+ if (toBool stat)+ then peek handlePtr+ else fail "c_cryptAcquireCtx"++cryptGenRandom :: Word32 -> Int -> IO B.ByteString+cryptGenRandom h i = + BI.create i $ \c_buffer -> do+ stat <- c_cryptGenRandom h (fromIntegral i) c_buffer+ if (toBool stat)+ then return ()+ else fail "c_cryptGenRandom"++cryptReleaseCtx :: Word32 -> IO ()+cryptReleaseCtx h = do+ stat <- c_cryptReleaseCtx h 0+ if (toBool stat)+ then return ()+ else fail "c_cryptReleaseCtx"++-- |Open a handle from which random data can be read+openHandle :: IO CryptHandle+openHandle = do+#ifdef HAVE_RDRAND+ b <- cpuHasRdRand+ if b then return UseRdRand+ else do+#endif+ liftM CH cryptAcquireCtx++-- |Close the `CryptHandle`+closeHandle :: CryptHandle -> IO ()+closeHandle (CH h) = cryptReleaseCtx h++-- |Read from `CryptHandle`+hGetEntropy :: CryptHandle -> Int -> IO B.ByteString +hGetEntropy (CH h) n = cryptGenRandom h n+#ifdef HAVE_RDRAND+hGetEntropy UseRdRand n =+ BI.create n $ \ptr -> do+ r <- c_get_rand_bytes (castPtr ptr) (fromIntegral n)+ when (r /= 0)+ (fail "RDRand failed to gather entropy")+#endif
entropy.cabal view
@@ -1,5 +1,5 @@ name: entropy-version: 0.2.2.4+version: 0.3 description: A platform independent method to obtain cryptographically strong entropy (urandom on Linux, CryptAPI on Windows, patches welcome). Users looking for cryptographically strong (number-theoretically@@ -14,20 +14,33 @@ homepage: https://github.com/TomMD/entropy bug-reports: https://github.com/TomMD/entropy/issues stability: stable+-- build-type: Simple+-- ^^ Used for HaLVM build-type: Custom-cabal-version: >=1.10+-- ^^ Test for RDRAND support using 'ghc'+cabal-version: >=1.10 tested-with: GHC == 7.6.3 -- data-files: extra-source-files: ./cbits/rdrand.c, ./cbits/rdrand.h, README.md +-- Notice to compile with HaLVM the above 'build-type' must be changed+-- to 'Simple' instead of 'Custom'. The current build system naively+-- runs GHC to determine if the compiler supports RDRAND before proceeding.+flag halvm+ description: Build for the HaLVM+ default: False library- ghc-options: -Wall -O2+ ghc-options: -O2 exposed-modules: System.Entropy+ if os(windows)+ other-modules: System.EntropyWindows+ else+ other-modules: System.EntropyNix other-extensions: CPP, ForeignFunctionInterface, BangPatterns, ScopedTypeVariables build-depends: base == 4.*, bytestring- -- hs-source-dirs:- -- other-modules: default-language: Haskell2010+ if(flag(halvm))+ cpp-options: -DXEN -DHAVE_RDRAND if arch(x86_64) cpp-options: -Darch_x86_64 c-sources: cbits/rdrand.c@@ -36,7 +49,8 @@ cpp-options: -DisWindows extra-libraries: advapi32 else- Build-Depends: unix+ if !flag(halvm)+ Build-Depends: unix source-repository head type: git