intel-aes-0.2.0.0: System/Random/AES.hs
{-|
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 cryptographic-strength random number
generators based on AES.
The generators are exposed both using the
'System.Random.RandomGen' interface and the
'Codec.Crypto.CryptoRandomGen' one.
Internally, this module uses two different AES implementations.
If the CPU ID indicates that AESNI instructions are available, it
will use an Intel-provided hardware-accelerated implementation,
otherwise, it will fall back to Dr. Brian Gladman's well-known
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
-- <boilerplate> 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'))
-- </boilerplate>
-- 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)