intel-aes-0.2.0.0: Codec/Crypto/GladmanAES.hsc
-- | ECB AES operation. This code is based on the "AES" package from
-- Svein Ove Aas (University of Tromsø), though it is heavily modified
-- and any bugs should be blamed on me, Thomas M. DuBuisson.
{-# LANGUAGE FlexibleInstances, EmptyDataDecls, FlexibleContexts,
ForeignFunctionInterface, ViewPatterns,
ScopedTypeVariables
#-}
{-# CFILES cbits/gladman/aescrypt.c cbits/gladman/aeskey.c cbits/gladman/aestab.c cbits/gladman/aes_modes.c #-}
module Codec.Crypto.GladmanAES
( AES
, N128, N192, N256
, module Crypto.Classes
, module Crypto.Modes) where
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as BI
import Crypto.Modes
import Crypto.Classes
import Crypto.Types
import Data.Tagged
-- use import list for Serialize to avoid conflict with `ensure` function
import Data.Serialize(Serialize, Get, get, put, getByteString, putByteString)
import Foreign
import Control.Applicative
import Control.Monad
-- import Crypto.Random (CryptoRandomGen(newGen))
-- The following line will cause a link problem currently [2011.02.02]:
-- Linking dist/build/benchmark-intel-aes-rng/benchmark-intel-aes-rng ...
-- /home/newton/Dropbox/working_copies/intel-aes/dist/build/libHSintel-aes-0.1.1.a(GladmanAES.o): In function `s3ho_info':
-- (.text+0x34c3): undefined reference to `__stginit_intelzmaeszm0zi1zi1_CodecziCryptoziConvertRNG_'
-- collect2: ld returned 1 exit status
-- import qualified Codec.Crypto.ConvertRNG as CR
#include "gladman/aesopt.h"
#include "gladman/aes.h"
#include "gladman/aestab.h"
#include "gladman/brg_endian.h"
#include "gladman/ctr_inc.h"
data N128
data N192
data N256
data AES n = AES
{ encCtx :: EncryptCtxP
, decCtx :: DecryptCtxP
, aesKeyRaw :: B.ByteString }
-- Because of the above link problem I can't move these:
{-
mkAESGen :: Int -> CR.CRGtoRG (CR.BCtoCRG (AES N128))
mkAESGen int = CR.convertCRG gen
where
Right (gen :: CR.BCtoCRG (AES N128)) = newGen (B.append halfseed halfseed )
halfseed = encode word64
word64 = fromIntegral int :: Word64
mkAESGen0 :: Int -> CR.CRGtoRG0 (CR.BCtoCRG (AES N128))
mkAESGen0 int = CR.CRGtoRG0 gen
where
Right (gen :: CR.BCtoCRG (AES N128)) = newGen (B.append halfseed halfseed )
halfseed = encode word64
word64 = fromIntegral int :: Word64
-}
--------------------------------------------------------------------------------
-- | Create an encryption/decryption context for incremental
-- encryption/decryption
--
-- You may create an ECB context this way, in which case you may pass
-- undefined for the IV
newCtx :: B.ByteString -> IO (AES n)
newCtx key = do
e <- (encryptCtx key)
d <- (decryptCtx key)
return $ AES e d key
instance BlockCipher (AES N128) where
blockSize = Tagged 128
keyLength = Tagged 128
encryptBlock = aesEnc
decryptBlock = aesDec
buildKey = aesBK 128
instance BlockCipher (AES N192) where
blockSize = Tagged 192
keyLength = Tagged 192
encryptBlock = aesEnc
decryptBlock = aesDec
buildKey = aesBK 192
instance BlockCipher (AES N256) where
blockSize = Tagged 256
keyLength = Tagged 256
encryptBlock = aesEnc
decryptBlock = aesDec
buildKey = aesBK 256
aesEnc :: AES n -> B.ByteString -> B.ByteString
aesEnc k m = unsafePerformIO $ call _aes_ecb_encrypt (encCtx k) m
aesDec :: AES n -> B.ByteString -> B.ByteString
aesDec k m = unsafePerformIO $ call _aes_ecb_decrypt (decCtx k) m
aesBK :: Int -> B.ByteString -> Maybe (AES n)
aesBK n bs
| B.length bs == n `div` 8 = Just $ unsafePerformIO (newCtx bs)
| otherwise = Nothing
aesKL :: AES n -> BitLength
aesKL = (*8) . B.length . aesKeyRaw
instance Serialize (AES N128) where
get = getGeneral 16
put = putByteString . aesKeyRaw
instance Serialize (AES N192) where
get = getGeneral 24
put = putByteString . aesKeyRaw
instance Serialize (AES N256) where
get = getGeneral 32
put = putByteString . aesKeyRaw
getGeneral :: BlockCipher (AES n) => Int -> Get (AES n)
getGeneral n = do
bs <- getByteString n
case buildKey bs of
Nothing -> fail "Could not build key from serialized bytestring"
Just x -> return x
call :: (Ptr b -> Ptr Word8 -> Int -> Ptr a -> IO Int)
-> ForeignPtr a -> B.ByteString -> IO B.ByteString
call f ctx (BI.toForeignPtr -> (bs,offset,len)) =
withForeignPtr ctx $ \ctxp ->
withForeignPtr bs $ \bsp ->
BI.create len $ \obuf ->
ensure $ f (bsp `plusPtr` offset) obuf len ctxp
foreign import ccall unsafe "aes_ecb_encrypt" _aes_ecb_encrypt
:: Ptr Word8 -> Ptr Word8 -> Int -> Ptr EncryptCtxStruct -> IO Int
foreign import ccall unsafe "aes_ecb_decrypt" _aes_ecb_decrypt
:: Ptr Word8 -> Ptr Word8 -> Int -> Ptr DecryptCtxStruct -> IO Int
type EncryptCtxP = ForeignPtr EncryptCtxStruct
type DecryptCtxP = ForeignPtr DecryptCtxStruct
data EncryptCtxStruct
instance Storable EncryptCtxStruct where
sizeOf _ = #size aes_encrypt_ctx
alignment _ = 16 -- FIXME: Maybe overkill, maybe underkill, definitely iffy
data DecryptCtxStruct
instance Storable DecryptCtxStruct where
sizeOf _ = #size aes_decrypt_ctx
alignment _ = 16
wrap :: Int -> Bool
wrap r | r == (#const EXIT_SUCCESS) = True
| otherwise = False
ensure :: IO Int -> IO ()
ensure act = do
r <- wrap <$> act
unless r (fail "AES function failed")
foreign import ccall unsafe "aes_encrypt_key" _aes_encrypt_key
:: Ptr Word8 -> Int -> Ptr EncryptCtxStruct -> IO Int
encryptCtx :: B.ByteString -> IO EncryptCtxP
encryptCtx bs = do
ctx <- mallocForeignPtr
let (key,offset,len) = BI.toForeignPtr bs
withForeignPtr ctx $ \ctx' ->
withForeignPtr key $ \key' ->
ensure $ _aes_encrypt_key (key' `plusPtr` offset) len ctx'
return ctx
foreign import ccall unsafe "aes_decrypt_key" _aes_decrypt_key
:: Ptr Word8 -> Int -> Ptr DecryptCtxStruct -> IO Int
decryptCtx :: B.ByteString -> IO DecryptCtxP
decryptCtx bs = do
ctx <- mallocForeignPtr
let (key,offset,len) = BI.toForeignPtr bs
withForeignPtr ctx $ \ctx' ->
withForeignPtr key $ \key' ->
ensure $ _aes_decrypt_key (key' `plusPtr` offset) len ctx'
return ctx