From 3ad20dd729e2f118ce65a683e6a8ac305f652870 Mon Sep 17 00:00:00 2001 From: Ryan Newton Date: Thu, 25 Aug 2011 00:29:33 -0400 Subject: [PATCH] Hid nonessential modules. Changed namespace. Various cleanup. Changed the way benchmarks are run. --- Codec/Crypto/ConvertRNG.hs | 14 +- Codec/Crypto/IntelAES.hs | 129 ------------------ Codec/Crypto/IntelAES/AESNI.hs | 8 +- Setup.hs | 3 +- System/Random/AES.hs | 128 +++++++++++++++++ .../Random/AES/Tests.hs | 37 +++-- benchmark-intel-aes-rng.hs | 8 ++ intel-aes.cabal | 29 ++-- 8 files changed, 175 insertions(+), 181 deletions(-) delete mode 100644 Codec/Crypto/IntelAES.hs create mode 100644 System/Random/AES.hs rename SimpleRNGBench.hs => System/Random/AES/Tests.hs (92%) create mode 100755 benchmark-intel-aes-rng.hs diff --git a/Codec/Crypto/ConvertRNG.hs b/Codec/Crypto/ConvertRNG.hs index 1450d6e..4cc9bbc 100644 --- a/Codec/Crypto/ConvertRNG.hs +++ b/Codec/Crypto/ConvertRNG.hs @@ -26,7 +26,7 @@ module Codec.Crypto.ConvertRNG ( BCtoCRG(..), convertCRG , CRGtoRG() - , CRGtoRG0(..) -- Inefficient version for testing... + , CRGtoRG_Unbuffered(..) -- Inefficient version for testing... ) where @@ -73,21 +73,21 @@ import Foreign.Storable -- | Converting CryptoRandomGen to RandomGen. -- This naive version is probably pretty inefficent: -data CRGtoRG0 a = CRGtoRG0 a -instance CryptoRandomGen g => RandomGen (CRGtoRG0 g) where - next (CRGtoRG0 g) = +data CRGtoRG_Unbuffered a = CRGtoRG_Unbuffered a +instance CryptoRandomGen g => RandomGen (CRGtoRG_Unbuffered g) where + next (CRGtoRG_Unbuffered g) = -- case genBytes (max bytes_in_int (keyLength g `quot` 8)) g of case genBytes bytes_in_int g of Left err -> error$ "CryptoRandomGen genBytes error: " ++ show err Right (bytes,g') -> case decode bytes of Left err -> error$ "Deserialization error:"++ show err - Right n -> (n, CRGtoRG0 g') + Right n -> (n, CRGtoRG_Unbuffered g') - split (CRGtoRG0 g) = + split (CRGtoRG_Unbuffered g) = case splitGen g of Left err -> error$ "CryptoRandomGen splitGen error:"++ show err - Right (g1,g2) -> (CRGtoRG0 g1, CRGtoRG0 g2) + Right (g1,g2) -> (CRGtoRG_Unbuffered g1, CRGtoRG_Unbuffered g2) -- Another option would be to amortize overhead by generating a large -- buffer of random bits at once. diff --git a/Codec/Crypto/IntelAES.hs b/Codec/Crypto/IntelAES.hs deleted file mode 100644 index d7ee799..0000000 --- a/Codec/Crypto/IntelAES.hs +++ /dev/null @@ -1,129 +0,0 @@ -{-| - Module : Codec.Crypto.IntelAES - Copyright : (c) Ryan Newton 2011 - License : BSD-style (see the file LICENSE) - Maintainer : rrnewton@gmail.com - Stability : experimental - Portability : linux only (NEEDS PORTING) - - This module provides an AES implementation that will test the CPU - ID and use hardware acceleration where available, otherwise it will - fall back to Dr. Brian Gladman's software implementation. - - This module also exports a random number generator based on AES - both using the System.Random.RandomGen interface and the - Codec.Crypto.Random. - - -} -{-# OPTIONS_GHC -fwarn-unused-imports #-} -{-# LANGUAGE ForeignFunctionInterface, CPP, ScopedTypeVariables #-} - -module Codec.Crypto.IntelAES - ( - mkAESGen, - CompoundAESRNG(), - supportsAESNI, - -- Plus, instances exported of course. - testIntelAES - ) -where - -import qualified Codec.Crypto.IntelAES.AESNI as NI -import qualified Codec.Crypto.GladmanAES as GA -import GHC.IO (unsafeDupablePerformIO) -import Data.Tagged -import Data.Word -import Data.Serialize -import qualified Data.ByteString as B -import Crypto.Random (CryptoRandomGen(..), GenError(..), splitGen, genBytes) -import Crypto.Types -import Codec.Crypto.ConvertRNG -import Debug.Trace - -newtype CompoundCRG = - CompoundCRG - (Either (BCtoCRG (NI.IntelAES NI.N128)) - (BCtoCRG (GA.AES GA.N128))) - --- | A type representing an AES-based random number generator which --- will use AESNI instructions when available, and invoke the --- portable Gladman implementation when not. -type CompoundAESRNG = CRGtoRG CompoundCRG - --- | Simple function to create a random number generator from an Int, --- analogous to `System.Random.newStdGen`. Only 128-bit encryption --- is provided for now. -mkAESGen :: Int -> CompoundAESRNG -mkAESGen int = convertCRG gen - where - Right (gen :: CompoundCRG) = newGen (B.append halfseed halfseed ) - halfseed = encode word64 - word64 = fromIntegral int :: Word64 - - - --- foreign import ccall unsafe "iaesni.h" check_for_aes_instructions :: IO Bool -foreign import ccall unsafe "iaesni.h" check_for_aes_instructions :: Bool - --- | Does the machine support AESNI instructions? -supportsAESNI :: Bool -supportsAESNI = check_for_aes_instructions - -{-# INLINE mapRight #-} -mapRight fn x@(Left _) = x -mapRight fn (Right x) = Right$ fn x - -{-# INLINE mapSnd #-} -mapSnd fn (x,y) = (x,fn y) - - -instance CryptoRandomGen CompoundCRG where - --- newGen :: B.ByteString -> Either GenError CompoundCRG - newGen = --- if unsafeDupablePerformIO check_for_aes_instructions --- trace ("Checked for AES instructions: "++ show check_for_aes_instructions)$ - if check_for_aes_instructions - -- Ick, boilerplate: - then \bytes -> case newGen bytes of Left err -> Left err - Right gen -> Right$ CompoundCRG$ Left gen - else \bytes -> case newGen bytes of Left err -> Left err - Right gen -> Right$ CompoundCRG$ Right gen - - genSeedLength = Tagged 128 - - - -- ByteLength -> CompoundCRG -> Either GenError (B.ByteString, CompoundCRG) - genBytes req (CompoundCRG (Left gen)) = --- Let's try to reduce that boilerplate if we can... -#if 0 - mapRight (mapSnd (CompoundCRG . Left) ) $ genBytes req gen -#else - case genBytes req gen of - Left err -> Left err - Right (bytes,gen') -> Right (bytes, CompoundCRG (Left gen')) -#endif - --- OUCH - genBytes req (CompoundCRG (Right gen)) = - case genBytes req gen of - Left err -> Left err - Right (bytes,gen') -> Right (bytes, CompoundCRG (Right gen')) - reseed bs (CompoundCRG (Left gen)) = - case reseed bs gen of - Left err -> Left err - Right gen' -> Right (CompoundCRG (Left gen')) - reseed bs (CompoundCRG (Right gen)) = - case reseed bs gen of - Left err -> Left err - Right gen' -> Right (CompoundCRG (Right gen')) --- - - - -testIntelAES = do - putStrLn$ "Running crude tests." --- b <- check_for_aes_instructions - let b = check_for_aes_instructions - putStrLn$ "Machine supports AESNI: "++ show b - diff --git a/Codec/Crypto/IntelAES/AESNI.hs b/Codec/Crypto/IntelAES/AESNI.hs index 7a1a18d..3d19263 100644 --- a/Codec/Crypto/IntelAES/AESNI.hs +++ b/Codec/Crypto/IntelAES/AESNI.hs @@ -29,7 +29,7 @@ module Codec.Crypto.IntelAES.AESNI , mkAESGen192, mkAESGen256 -- Inefficient version for testing: - , mkAESGen0, SimpleAESRNG0 + , mkAESGen0, SimpleAESRNG_Unbuffered , IntelAES, N128, N192, N256 -- Plus, instances exported of course. ) @@ -87,9 +87,9 @@ mkAESGen256 seed = convertCRG gen -- | TEMP: Inefficient version for testing. -type SimpleAESRNG0 = CRGtoRG0 (BCtoCRG (IntelAES N128)) -mkAESGen0 :: Int -> SimpleAESRNG0 -mkAESGen0 int = CRGtoRG0 gen +type SimpleAESRNG_Unbuffered = CRGtoRG_Unbuffered (BCtoCRG (IntelAES N128)) +mkAESGen0 :: Int -> SimpleAESRNG_Unbuffered +mkAESGen0 int = CRGtoRG_Unbuffered gen where Right (gen :: BCtoCRG (IntelAES N128)) = newGen (B.append halfseed halfseed ) halfseed = encode word64 diff --git a/Setup.hs b/Setup.hs index 9b6ef98..fd7116b 100644 --- a/Setup.hs +++ b/Setup.hs @@ -39,6 +39,7 @@ my_clean desc () hooks flags = do setCurrentDirectory "./cbits/" system "make clean" setCurrentDirectory ".." + system "rm -f benchmark-intel-aes-rng" putStrLn$ " [intel-aes] Done. Now running normal cabal clean action.\n" (cleanHook simpleUserHooks) desc () hooks flags @@ -88,7 +89,7 @@ patchDesc desc localinfo = do -- Whew... nested record updates are painful: desc3 = desc { library = Just (lib { libBuildInfo = newlbi})} - putStrLn$ " [intel-aes] Modified package info. " + putStrLn$ " [intel-aes] Modified package description structure with extra options. " return desc3 diff --git a/System/Random/AES.hs b/System/Random/AES.hs new file mode 100644 index 0000000..100d887 --- /dev/null +++ b/System/Random/AES.hs @@ -0,0 +1,128 @@ +{-| + Module : System.Random.AES + Copyright : (c) Ryan Newton 2011 + License : BSD-style (see the file LICENSE) + Maintainer : rrnewton@gmail.com + Stability : experimental + Portability : Mac OS, Linux, Untested on Windows + + This module provides a random number generator based on AES both + using the System.Random.RandomGen interface and the + Codec.Crypto.Random one. The AES implementation that will test + the CPU ID and use hardware acceleration where available, + otherwise it will fall back to Dr. Brian Gladman's software + implementation. + + -} +{-# OPTIONS_GHC -fwarn-unused-imports #-} +{-# LANGUAGE ForeignFunctionInterface, CPP, ScopedTypeVariables #-} + +module System.Random.AES + ( + mkAESGen, mkAESGenCRG, + AesCRG(), AesRG(), + supportsAESNI, + -- Plus, instances exported of course. +-- testIntelAES + ) +where + +import qualified Codec.Crypto.IntelAES.AESNI as NI +import qualified Codec.Crypto.GladmanAES as GA +-- import GHC.IO (unsafeDupablePerformIO) +import Data.Tagged +import Data.Word +import Data.Serialize +import qualified Data.ByteString as B +-- import Crypto.Random (CryptoRandomGen(..), GenError(..), splitGen, genBytes) +import Crypto.Random (CryptoRandomGen(..)) +-- import Crypto.Types +import Codec.Crypto.ConvertRNG + +-- This type represents an RNG which may have one of two different +-- representations, corresponding to the software or the hardware +-- supported version. +newtype AesCRG = + AesCRG + (Either (BCtoCRG (NI.IntelAES NI.N128)) + (BCtoCRG (GA.AES GA.N128))) + +-- | A type representing an AES-based random number generator which +-- will use AESNI instructions when available, and invoke the +-- portable Gladman implementation when not. +type AesRG = CRGtoRG AesCRG + +-- | Simple function to create a random number generator from an Int, +-- exposing the `System.Random.RandomGen` interface, analogous to +-- `System.Random.newStdGen`. Only 128-bit encryption is currently +-- provided. +mkAESGen :: Int -> AesRG +mkAESGen int = convertCRG (mkAESGenCRG int) + +-- | This variant creates an random number generator which exposes the +-- `Crypto.Random.CryptoRandomGen` interface. +mkAESGenCRG :: Int -> AesCRG +mkAESGenCRG int = gen + where + Right (gen :: AesCRG) = newGen (B.append halfseed halfseed ) + halfseed = encode word64 + word64 = fromIntegral int :: Word64 + + + +foreign import ccall unsafe "iaesni.h" check_for_aes_instructions :: Bool + +-- | Does the machine support AESNI instructions? +supportsAESNI :: Bool +supportsAESNI = check_for_aes_instructions + +-- | This instance provides the CryptoRandomGen interface, which +-- allows bulk generation of random bytes. +instance CryptoRandomGen AesCRG where + +-- newGen :: B.ByteString -> Either GenError AesCRG + newGen = + if check_for_aes_instructions + -- Ick, boilerplate: + then \bytes -> case newGen bytes of Left err -> Left err + Right gen -> Right$ AesCRG$ Left gen + else \bytes -> case newGen bytes of Left err -> Left err + Right gen -> Right$ AesCRG$ Right gen + + genSeedLength = Tagged 128 + + -- ByteLength -> AesCRG -> Either GenError (B.ByteString, AesCRG) + genBytes req (AesCRG (Left gen)) = + +#if 0 + -- UNFINISHED: Let's try to reduce that boilerplate if we can... + mapRight (mapSnd (AesCRG . Left) ) $ genBytes req gen +#else + case genBytes req gen of + Left err -> Left err + Right (bytes,gen') -> Right (bytes, AesCRG (Left gen')) +#endif + + +-- OUCH + genBytes req (AesCRG (Right gen)) = + case genBytes req gen of + Left err -> Left err + Right (bytes,gen') -> Right (bytes, AesCRG (Right gen')) + reseed bs (AesCRG (Left gen)) = + case reseed bs gen of + Left err -> Left err + Right gen' -> Right (AesCRG (Left gen')) + reseed bs (AesCRG (Right gen)) = + case reseed bs gen of + Left err -> Left err + Right gen' -> Right (AesCRG (Right gen')) +-- + +-- UNFINISHED Refactoring: +{-# INLINE mapRight #-} +mapRight fn x@(Left _) = x +mapRight fn (Right x) = Right$ fn x +{-# INLINE mapSnd #-} +mapSnd fn (x,y) = (x,fn y) + diff --git a/SimpleRNGBench.hs b/System/Random/AES/Tests.hs similarity index 92% rename from SimpleRNGBench.hs rename to System/Random/AES/Tests.hs index 158907e..e5ca71c 100644 --- a/SimpleRNGBench.hs +++ b/System/Random/AES/Tests.hs @@ -1,28 +1,26 @@ -#!/usr/bin/env runhaskell {-# LANGUAGE BangPatterns, ScopedTypeVariables, ForeignFunctionInterface #-} --- | A simple script to do some very basic timing of the RNGs. --- It is important that we also run established stastical tests on --- these RNGs a some point... +{- | A simple script to do some very basic timing of the RNGs. This + module is here to be part of the same compilation unit as the + library itself (e.g. to access hidden modules). -module Main where + TODO: It is important that we also run established stastical tests on + these RNGs a some point... + -} -import qualified Codec.Encryption.BurtonRNGSlow as BS +module System.Random.AES.Tests (runTests) where ---import qualified Codec.Crypto.IntelAES.GladmanAES as GA +import qualified System.Random.AES as IA +import qualified Codec.Encryption.BurtonRNGSlow as BS import qualified Codec.Crypto.GladmanAES as GA import qualified Codec.Crypto.IntelAES.AESNI as NI -import qualified Codec.Crypto.IntelAES as IA import qualified Codec.Crypto.ConvertRNG as CR --- import qualified Codec.Crypto.AES.Random as Svein import System.Exit (exitSuccess, exitFailure) import System.Environment import System.Random --- import System.PosixCompat (sleep) import System.Posix (sleep) import System.CPUTime (getCPUTime) --- import Data.Time.Clock (diffUTCTime) import System.CPUTime.Rdtsc import System.Console.GetOpt @@ -60,8 +58,8 @@ mkAESGen_gladman int = CR.convertCRG gen word64 = fromIntegral int :: Word64 -mkAESGen_gladman0 :: Int -> CR.CRGtoRG0 (CR.BCtoCRG (GA.AES GA.N128)) -mkAESGen_gladman0 int = CR.CRGtoRG0 gen +mkAESGen_gladman_unbuffered :: Int -> CR.CRGtoRG_Unbuffered (CR.BCtoCRG (GA.AES GA.N128)) +mkAESGen_gladman_unbuffered int = CR.CRGtoRG_Unbuffered gen where Right (gen :: CR.BCtoCRG (GA.AES GA.N128)) = newGen (B.append halfseed halfseed ) halfseed = encode word64 @@ -298,13 +296,12 @@ options = , Option ['t'] ["test"] (NoArg Test) "run some basic tests" ] - -main = do +runTests = do argv <- getArgs let (opts,_,other) = getOpt Permute options argv + putStrLn$ "Does machine supports AESNI?: " ++ show IA.supportsAESNI when (Test `elem` opts)$ do - IA.testIntelAES NI.testAESNI exitSuccess @@ -334,15 +331,15 @@ main = do timeit th freq "System.Random stdGen" mkStdGen timeit th freq "PureHaskell/reference" BS.mkBurtonGen_reference timeit th freq "PureHaskell" BS.mkBurtonGen --- timeit th freq "Gladman inefficient" GA.mkAESGen0 +-- timeit th freq "Gladman unbuffered" GA.mkAESGen0 -- timeit th freq "Gladman" GA.mkAESGen - timeit th freq "Gladman inefficient" mkAESGen_gladman0 - timeit th freq "Gladman" mkAESGen_gladman + timeit th freq "Gladman unbuffered" mkAESGen_gladman_unbuffered + timeit th freq "Gladman" mkAESGen_gladman timeit th freq "Compound gladman/intel" IA.mkAESGen -- timeit th freq "Svein's Gladman package" (const svein) if IA.supportsAESNI then do - timeit th freq "IntelAES inefficient" NI.mkAESGen0 + timeit th freq "IntelAES unbuffered" NI.mkAESGen0 timeit th freq "IntelAES" NI.mkAESGen else putStrLn$ " [Skipping AESNI-only tests, current machine does not support these instructions.]" diff --git a/benchmark-intel-aes-rng.hs b/benchmark-intel-aes-rng.hs new file mode 100755 index 0000000..3c0aabf --- /dev/null +++ b/benchmark-intel-aes-rng.hs @@ -0,0 +1,8 @@ +#!/usr/bin/env runhaskell -i + +-- Note the -i to prevent GHC from searching ./ as part of the library search path. + +module Main where +import System.Random.AES.Tests (runTests) + +main = runTests diff --git a/intel-aes.cabal b/intel-aes.cabal index eddf6fa..0fd08f2 100644 --- a/intel-aes.cabal +++ b/intel-aes.cabal @@ -89,19 +89,22 @@ source-repository head library build-depends: base >= 4 && < 5, random, DRBG, split, process, haskell98, time, crypto-api >= 0.5, - bytestring, cereal, tagged, largeword + bytestring, cereal, tagged, largeword, + -- For AES.Tests only: + rdtsc, unix - exposed-modules: Codec.Encryption.BurtonRNGSlow - , Codec.Crypto.IntelAES - , Codec.Crypto.IntelAES.AESNI + exposed-modules: + System.Random.AES , Codec.Crypto.ConvertRNG --- , Codec.Crypto.IntelAES.GladmanAES - , Codec.Crypto.GladmanAES + , System.Random.AES.Tests other-modules: Benchmark.BinSearch , Codec.Encryption.AES , Codec.Encryption.AESAux , Codec.Utils + , Codec.Encryption.BurtonRNGSlow + , Codec.Crypto.IntelAES.AESNI + , Codec.Crypto.GladmanAES GHC-Options: -O2 extra-libraries: intel_aes @@ -110,20 +113,6 @@ library cbits/gladman/aes_modes.c, cbits/gladman/ctr_inc.c Include-Dirs: cbits - -- ---------------------------------------------------------------------------------------------------- -Executable benchmark-intel-aes-rng - Main-is: SimpleRNGBench.hs - Build-Depends: base >= 4 && < 5, split, rdtsc, random, DRBG - , crypto-api >= 0.5 --- , unix-compat - , unix - , tagged, cereal, bytestring, process, haskell98, time, largeword - , intel-aes - GHC-Options: -O2 -threaded -rtsopts - C-sources: cbits/c_test.c - Include-dirs: cbits - - -- cabal haddock --hoogle --executables --hyperlink-source --haddock-options="--html"