diff --git a/Benchmarks.hs b/Benchmarks.hs
deleted file mode 100644
--- a/Benchmarks.hs
+++ /dev/null
@@ -1,85 +0,0 @@
-import Criterion
-import Criterion.Environment
-import Criterion.Config
-import Criterion.Monad
-import Criterion.Analysis
-import Criterion.Measurement
-
-import Text.Printf
-
-import Control.Monad.Trans
-
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as L
-
-import qualified Crypto.Cipher.AES as AES
-import qualified Crypto.Cipher.RC4 as RC4
-import qualified Crypto.Cipher.Camellia as Camellia
-
-(Right key128) = AES.initKey128 $ B.replicate 16 0
-aesEncrypt128 = AES.encrypt key128
-aesEncrypt128CBC = AES.encryptCBC key128 (B.replicate 16 0)
-(Right key192) = AES.initKey192 $ B.replicate 24 0
-aesEncrypt192 = AES.encrypt key192
-aesEncrypt192CBC = AES.encryptCBC key192 (B.replicate 16 0)
-(Right key256) = AES.initKey256 $ B.replicate 32 0
-aesEncrypt256 = AES.encrypt key256
-aesEncrypt256CBC = AES.encryptCBC key256 (B.replicate 16 0)
-
-(Right camelliaKey128) = Camellia.initKey $ B.replicate 16 0
-camelliaEncrypt128 = Camellia.encrypt camelliaKey128
-
-rc4Key = RC4.initCtx $ replicate 16 0
-rc4Encrypt = snd . RC4.encrypt rc4Key 
-
-b16 f   = whnf f $ B.replicate 16 0
-b32 f   = whnf f $ B.replicate 32 0
-b128 f  = whnf f $ B.replicate 128 0
-b512 f  = whnf f $ B.replicate 512 0
-b1024 f = whnf f $ B.replicate 1024 0
-b4096 f = whnf f $ B.replicate 4096 0
-
-doCipher env f = do
-	mean16   <- runBenchmark env (b16 f)   >>= \sample -> analyseMean sample 100
-	mean32   <- runBenchmark env (b32 f)   >>= \sample -> analyseMean sample 100
-	mean128  <- runBenchmark env (b128 f)  >>= \sample -> analyseMean sample 100
-	mean512  <- runBenchmark env (b512 f)  >>= \sample -> analyseMean sample 100
-	mean1024 <- runBenchmark env (b1024 f) >>= \sample -> analyseMean sample 100
-	mean4096 <- runBenchmark env (b4096 f) >>= \sample -> analyseMean sample 100
-	return (mean16, mean32, mean128, mean512, mean1024, mean4096)
-
-norm :: Int -> Double -> Double
-norm n time
-	| n < 1024  = 1.0 / (time * (1024 / fromIntegral n))
-	| n == 1024 = 1.0 / time
-	| n > 1024  = 1.0 / (time / (fromIntegral n / 1024))
-
-pn :: Int -> Double -> String
-pn n time = printf "%.1f K/s" (norm n time)
-
-doOne env (cipherName, f) = do
-	(mean16, mean32, mean128, mean512, mean1024, mean4096) <- doCipher env f
-	let s = printf "%12s: %12s %12s %12s %12s %12s %12s\n                %12s %12s %12s %12s %12s %12s"
-	               cipherName
-	               (secs mean16) (secs mean32) (secs mean128)
-	               (secs mean512) (secs mean1024) (secs mean4096)
-	               (pn 16 mean16) (pn 32 mean32) (pn 128 mean128)
-	               (pn 512 mean512) (pn 1024 mean1024) (pn 4096 mean4096)
-	return s
-
-main = withConfig defaultConfig $ do
-	env <- measureEnvironment
-	l   <- mapM (doOne env)
-		[ ("RC4"        , rc4Encrypt)
-		, ("Camellia128", camelliaEncrypt128)
-		, ("AES128"     , aesEncrypt128)
-		, ("AES128-CBC" , aesEncrypt128CBC)
-		, ("AES192"     , aesEncrypt192)
-		, ("AES192-CBC" , aesEncrypt192CBC)
-		, ("AES256"     , aesEncrypt256)
-		, ("AES256-CBC" , aesEncrypt256CBC)
-		]
-	liftIO $ printf "%12s| %12s %12s %12s %12s %12s %12s\n"
-	                "cipher" "16 bytes" "32 bytes" "64 bytes" "512 bytes" "1024 bytes" "4096 bytes"
-	liftIO $ printf "=============================================================================================\n"
-	mapM_ (liftIO . putStrLn) l
diff --git a/Benchmarks/Benchmarks.hs b/Benchmarks/Benchmarks.hs
new file mode 100644
--- /dev/null
+++ b/Benchmarks/Benchmarks.hs
@@ -0,0 +1,108 @@
+{-# LANGUAGE CPP #-}
+import Criterion
+import Criterion.Environment
+import Criterion.Config
+import Criterion.Monad
+import Criterion.Analysis
+import Criterion.Measurement
+
+import Text.Printf
+
+import Control.Monad.Trans
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+
+import qualified Crypto.Cipher.AES.Haskell as AES
+#ifdef HAVE_AESNI
+import qualified Crypto.Cipher.AES.X86NI as AESNI
+#endif
+import qualified Crypto.Cipher.RC4 as RC4
+import qualified Crypto.Cipher.Camellia as Camellia
+
+import Crypto.Classes
+import qualified Crypto.Modes as CAPI
+
+(Right key128) = AES.initKey128 $ B.replicate 16 0
+aesEncrypt128 = AES.encrypt key128
+aesEncrypt128CBC = AES.encryptCBC key128 (B.replicate 16 0)
+
+(Right key192) = AES.initKey192 $ B.replicate 24 0
+aesEncrypt192 = AES.encrypt key192
+aesEncrypt192CBC = AES.encryptCBC key192 (B.replicate 16 0)
+(Right key256) = AES.initKey256 $ B.replicate 32 0
+aesEncrypt256 = AES.encrypt key256
+aesEncrypt256CBC = AES.encryptCBC key256 (B.replicate 16 0)
+
+(Just capi_key128) = buildKey (B.replicate 16 0) :: Maybe AES.AES128
+aesEncrypt128CBC_capi = fst . CAPI.cbc' capi_key128 CAPI.zeroIV
+
+#ifdef HAVE_AESNI
+key128_ni = AESNI.initKey128 $ B.replicate 16 0
+aesniEncrypt128 = AESNI.encrypt key128_ni
+
+aesniEncrypt128CBC = AESNI.encryptCBC key128_ni (B.replicate 16 0)
+#endif
+
+(Right camelliaKey128) = Camellia.initKey128 $ B.replicate 16 0
+camelliaEncrypt128 = Camellia.encrypt camelliaKey128
+
+rc4Key = RC4.initCtx $ replicate 16 0
+rc4Encrypt = snd . RC4.encrypt rc4Key 
+
+b16 f   = whnf f $ B.replicate 16 0
+b32 f   = whnf f $ B.replicate 32 0
+b128 f  = whnf f $ B.replicate 128 0
+b512 f  = whnf f $ B.replicate 512 0
+b1024 f = whnf f $ B.replicate 1024 0
+b4096 f = whnf f $ B.replicate 4096 0
+
+doCipher env f = do
+	mean16   <- runBenchmark env (b16 f)   >>= \sample -> analyseMean sample 100
+	mean32   <- runBenchmark env (b32 f)   >>= \sample -> analyseMean sample 100
+	mean128  <- runBenchmark env (b128 f)  >>= \sample -> analyseMean sample 100
+	mean512  <- runBenchmark env (b512 f)  >>= \sample -> analyseMean sample 100
+	mean1024 <- runBenchmark env (b1024 f) >>= \sample -> analyseMean sample 100
+	mean4096 <- runBenchmark env (b4096 f) >>= \sample -> analyseMean sample 100
+	return (mean16, mean32, mean128, mean512, mean1024, mean4096)
+
+norm :: Int -> Double -> Double
+norm n time
+	| n < 1024  = 1.0 / (time * (1024 / fromIntegral n))
+	| n == 1024 = 1.0 / time
+	| n > 1024  = 1.0 / (time / (fromIntegral n / 1024))
+
+pn :: Int -> Double -> String
+pn n time = printf "%.1f K/s" (norm n time)
+
+doOne env (cipherName, f) = do
+	(mean16, mean32, mean128, mean512, mean1024, mean4096) <- doCipher env f
+	let s = printf "%12s: %12s %12s %12s %12s %12s %12s\n                %12s %12s %12s %12s %12s %12s"
+	               cipherName
+	               (secs mean16) (secs mean32) (secs mean128)
+	               (secs mean512) (secs mean1024) (secs mean4096)
+	               (pn 16 mean16) (pn 32 mean32) (pn 128 mean128)
+	               (pn 512 mean512) (pn 1024 mean1024) (pn 4096 mean4096)
+	return s
+
+main = withConfig defaultConfig $ do
+	env <- measureEnvironment
+	l   <- mapM (doOne env)
+		[ ("RC4"        , rc4Encrypt)
+		, ("Camellia128", camelliaEncrypt128)
+		, ("AES128"     , aesEncrypt128)
+		, ("AES128-CBC" , aesEncrypt128CBC)
+#ifdef HAVE_AESNI
+		, ("AES128-ni"  , aesniEncrypt128)
+		, ("AES128ni-CBC" , aesniEncrypt128CBC)
+#endif
+		-- , ("AES128-CBC-capi", aesEncrypt128CBC_capi)
+		, ("AES192"     , aesEncrypt192)
+		, ("AES192-CBC" , aesEncrypt192CBC)
+		, ("AES256"     , aesEncrypt256)
+		, ("AES256-CBC" , aesEncrypt256CBC)
+		]
+	liftIO $ printf "%12s| %12s %12s %12s %12s %12s %12s\n"
+	                "cipher" "16 bytes" "32 bytes" "64 bytes" "512 bytes" "1024 bytes" "4096 bytes"
+	liftIO $ printf "=============================================================================================\n"
+	mapM_ (liftIO . putStrLn) l
diff --git a/Crypto/Cipher/AES.hs b/Crypto/Cipher/AES.hs
--- a/Crypto/Cipher/AES.hs
+++ b/Crypto/Cipher/AES.hs
@@ -1,1014 +1,12 @@
-{-# LANGUAGE BangPatterns, MagicHash #-}
--- |
--- Module      : Crypto.Cipher.AES
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : Good
-
-module Crypto.Cipher.AES
-	( Key
-	, IV
-	-- * Basic encryption and decryption
-	, encrypt
-	, decrypt
-	-- * CBC encryption and decryption
-	, encryptCBC
-	, decryptCBC
-	-- * key building mechanism
-	, initKey128
-	, initKey192
-	, initKey256
-	-- * Wrappers for "crypto-api" instances
-	, AES128
-	, AES192
-	, AES256
-	) where
-
-import Data.Word
-import Data.Vector.Unboxed (Vector)
-import qualified Data.Vector.Unboxed as V
-import Data.Bits
-import Data.ByteString (ByteString)
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Unsafe as B
-import qualified Data.ByteString.Internal as B
-
-import GHC.Prim (indexWord8OffAddr#, indexWord32OffAddr#, word2Int#, Addr#, remInt#)
-import GHC.Word
-import GHC.Types
-
-import Foreign.Ptr
-import Foreign.Storable
-
-import Data.Tagged (Tagged(..))
-import Crypto.Classes (BlockCipher(..))
-import Data.Serialize (Serialize(..), getByteString, putByteString)
-
-import Control.Monad (forM_)
-import Control.Monad.Primitive
-
-import Data.Primitive.ByteArray
-
-import System.Endian (littleEndian)
-
-newtype AES128 = A128 { unA128 :: Key }
-newtype AES192 = A192 { unA192 :: Key }
-newtype AES256 = A256 { unA256 :: Key }
-
-instance BlockCipher AES128 where
-	blockSize    = Tagged 128
-	encryptBlock = encrypt . unA128
-	decryptBlock = decrypt . unA128
-	buildKey b   = either (const Nothing) (Just . A128) $ initKey128 b
-	keyLength    = Tagged 128
-
-instance BlockCipher AES192 where
-	blockSize    = Tagged 128
-	encryptBlock = encrypt . unA192
-	decryptBlock = decrypt . unA192
-	buildKey b   = either (const Nothing) (Just . A192) $ initKey192 b
-	keyLength    = Tagged 192
-
-instance BlockCipher AES256 where
-	blockSize    = Tagged 128
-	encryptBlock = encrypt . unA256
-	decryptBlock = decrypt . unA256
-	buildKey b   = either (const Nothing) (Just . A256) $ initKey256 b
-	keyLength    = Tagged 256
-
-serializeKey :: Key -> ByteString
-serializeKey (Key v)
-	| V.length v == 176 = B.pack $ map (V.unsafeIndex v) [0..15]
-	| V.length v == 208 = B.pack $ map (V.unsafeIndex v) [0..23]
-	| otherwise         = B.pack $ map (V.unsafeIndex v) [0..31]
-
-instance Serialize AES128 where
-	put = putByteString . serializeKey . unA128
-	get = do
-		raw <- getByteString (128 `div` 8)
-		case buildKey raw of
-			Nothing -> fail "Invalid raw key material."
-			Just k  -> return k
-
-instance Serialize AES192 where
-	put = putByteString . serializeKey . unA192
-	get = do
-		raw <- getByteString (192 `div` 8)
-		case buildKey raw of
-			Nothing -> fail "Invalid raw key material."
-			Just k  -> return k
-
-instance Serialize AES256 where
-	put = putByteString . serializeKey . unA256
-	get = do
-		raw <- getByteString (256 `div` 8)
-		case buildKey raw of
-			Nothing -> fail "Invalid raw key material."
-			Just k  -> return k
-
-data Key = Key (Vector Word8)
-	deriving (Show,Eq)
-
-type IV = B.ByteString
-
-type AESState = MutableByteArray RealWorld
-
-{- | encrypt using CBC mode
- - IV need to be 16 bytes and the data to encrypt a multiple of 16 bytes -}
-encryptCBC :: Key -> IV -> B.ByteString -> B.ByteString
-encryptCBC key iv b
-	| B.length iv /= 16        = error "invalid IV length"
-	| B.length b `mod` 16 /= 0 = error "invalid data length"
-	| otherwise                = B.concat $ encryptIter iv (makeChunks b)
-		where
-			encryptIter _   []     = []
-			encryptIter iv' (x:xs) =
-				let r = coreEncrypt key $ B.pack $ B.zipWith xor iv' x in
-				r : encryptIter r xs
-
-{- | encrypt using simple EBC mode -}
-encrypt :: Key -> B.ByteString -> B.ByteString
-encrypt key b
-	| B.length b `mod` 16 /= 0 = error "invalid data length"
-	| otherwise                = B.concat $ doChunks (coreEncrypt key) b
-
-{- | decrypt using CBC mode
- - IV need to be 16 bytes and the data to decrypt a multiple of 16 bytes -}
-decryptCBC :: Key -> IV -> B.ByteString -> B.ByteString
-decryptCBC key iv b
-	| B.length iv /= 16        = error "invalid IV length"
-	| B.length b `mod` 16 /= 0 = error "invalid data length"
-	| otherwise                = B.concat $ decryptIter iv (makeChunks b)
-		where
-			decryptIter _   []     = []
-			decryptIter iv' (x:xs) =
-				let r = B.pack $ B.zipWith xor iv' $ coreDecrypt key x in
-				r : decryptIter x xs
-
-{- | decrypt using simple EBC mode -}
-decrypt :: Key -> B.ByteString -> B.ByteString
-decrypt key b
-	| B.length b `mod` 16 /= 0 = error "invalid data length"
-	| otherwise                = B.concat $ doChunks (coreDecrypt key) b
-
-doChunks :: (B.ByteString -> B.ByteString) -> B.ByteString -> [B.ByteString]
-doChunks f b =
-	let (x, rest) = B.splitAt 16 b in
-	if B.length rest >= 16
-		then f x : doChunks f rest
-		else [ f x ]
-
-makeChunks :: B.ByteString -> [B.ByteString]
-makeChunks = doChunks id
-
-newAESState :: IO AESState
-newAESState = newAlignedPinnedByteArray 16 16
-
-coreEncrypt :: Key -> ByteString -> ByteString
-coreEncrypt key input = B.unsafeCreate (B.length input) $ \ptr -> do
-	st <- newAESState
-	swapBlock input st
-	aesMain (getNbr key) key st
-	swapBlockInv st ptr
-
-coreDecrypt :: Key -> ByteString -> ByteString
-coreDecrypt key input = B.unsafeCreate (B.length input) $ \ptr -> do
-	st <- newAESState
-	swapBlock input st
-	aesMainInv (getNbr key) key st
-	swapBlockInv st ptr
-
-getNbr :: Key -> Int
-getNbr (Key v)
-	| V.length v == 176 = 10
-	| V.length v == 208 = 12
-	| otherwise         = 14
-
-initKey128, initKey192, initKey256 :: ByteString -> Either String Key
-
-initKey128 = initKey 16
-initKey192 = initKey 24
-initKey256 = initKey 32
-
-initKey :: Int -> ByteString -> Either String Key
-initKey sz b
-	| B.length b == sz = Right $ coreExpandKey (V.generate sz $ B.unsafeIndex b)
-	| otherwise        = Left "wrong key size"
-
-aesMain :: Int -> Key -> AESState -> IO ()
-aesMain nbr key blk = do
-	addRoundKey key 0 blk
-	forM_ [1..nbr-1] $ \i -> do
-		shiftRows blk >> mixColumns blk >> addRoundKey key i blk
-	shiftRows blk >> addRoundKey key nbr blk
-
-aesMainInv :: Int -> Key -> AESState -> IO ()
-aesMainInv nbr key blk = do
-	addRoundKey key nbr blk
-	forM_ (reverse [1..nbr-1]) $ \i -> do
-		shiftRowsInv blk >> addRoundKey key i blk >> mixColumnsInv blk
-	shiftRowsInv blk >> addRoundKey key 0 blk
-
-{-# INLINE swapIndex #-}
-swapIndex :: Int -> Int
-swapIndex 0 = 0
-swapIndex 1 = 4
-swapIndex 2 = 8
-swapIndex 3 = 12
-swapIndex 4 = 1
-swapIndex 5 = 5
-swapIndex 6 = 9
-swapIndex 7 = 13
-swapIndex 8 = 2
-swapIndex 9 = 6
-swapIndex 10 = 10
-swapIndex 11 = 14
-swapIndex 12 = 3
-swapIndex 13 = 7
-swapIndex 14 = 11
-swapIndex 15 = 15
-swapIndex _  = 0
-
-coreExpandKey :: Vector Word8 -> Key
-coreExpandKey vkey
-	| V.length vkey == 16 = Key (V.concat (ek0 : ekN16))
-	| V.length vkey == 24 = Key (V.concat (ek0 : ekN24))
-	| V.length vkey == 32 = Key (V.concat (ek0 : ekN32))
-	| otherwise           = Key (V.empty)
-	where
-		ek0 = vkey
-		ekN16 = reverse $ snd $ foldl (generateFold generate16) (ek0, []) [1..10]
-
-		ekN24 =
-			let (lk, acc) = foldl (generateFold generate24) (ek0, []) [1..7] in
-			let nk = generate16 lk 8 in
-			reverse (nk : acc)
-
-		ekN32 =
-			let (lk, acc) = foldl (generateFold generate32) (ek0, []) [1..6] in
-			let nk = generate16 lk 7 in
-			reverse (nk : acc)
-
-		generateFold gen (prevk, accK) it = let nk = gen prevk it in (nk, nk : accK)
-
-		generate16 prevk it =
-			let len = V.length prevk in
-			let v0 = cR0 it (V.unsafeIndex prevk $ len - 4)
-			                (V.unsafeIndex prevk $ len - 3)
-			                (V.unsafeIndex prevk $ len - 2)
-			                (V.unsafeIndex prevk $ len - 1) in
-			let eg0@(e0,e1,e2,e3)     = xorVector prevk 0 v0   in
-			let eg1@(e4,e5,e6,e7)     = xorVector prevk 4 eg0  in
-			let eg2@(e8,e9,e10,e11)   = xorVector prevk 8 eg1  in
-			let     (e12,e13,e14,e15) = xorVector prevk 12 eg2 in
-			V.fromList [e0,e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15]
-
-		generate24 prevk it =
-			let len = V.length prevk in
-			let v0 = cR0 it (V.unsafeIndex prevk $ len - 4)
-			                (V.unsafeIndex prevk $ len - 3)
-			                (V.unsafeIndex prevk $ len - 2)
-			                (V.unsafeIndex prevk $ len - 1) in
-			let eg0@(e0,e1,e2,e3)     = xorVector prevk 0 v0   in
-			let eg1@(e4,e5,e6,e7)     = xorVector prevk 4 eg0  in
-			let eg2@(e8,e9,e10,e11)   = xorVector prevk 8 eg1  in
-			let eg3@(e12,e13,e14,e15) = xorVector prevk 12 eg2 in
-			let eg4@(e16,e17,e18,e19) = xorVector prevk 16 eg3 in
-			let     (e20,e21,e22,e23) = xorVector prevk 20 eg4 in
-			V.fromList [e0,e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15,e16,e17,e18,e19,e20,e21,e22,e23]
-
-		generate32 prevk it =
-			let len = V.length prevk in
-			let v0 = cR0 it (V.unsafeIndex prevk $ len - 4)
-			                (V.unsafeIndex prevk $ len - 3)
-			                (V.unsafeIndex prevk $ len - 2)
-			                (V.unsafeIndex prevk $ len - 1) in
-			let eg0@(e0,e1,e2,e3)     = xorVector prevk 0 v0   in
-			let eg1@(e4,e5,e6,e7)     = xorVector prevk 4 eg0  in
-			let eg2@(e8,e9,e10,e11)   = xorVector prevk 8 eg1  in
-			let eg3@(e12,e13,e14,e15) = xorVector prevk 12 eg2 in
-			let eg4@(e16,e17,e18,e19) = xorSboxVector prevk 16 eg3 in
-			let eg5@(e20,e21,e22,e23) = xorVector prevk 20 eg4 in
-			let eg6@(e24,e25,e26,e27) = xorVector prevk 24 eg5 in
-			let     (e28,e29,e30,e31) = xorVector prevk 28 eg6 in
-			V.fromList [e0,e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15,e16
-			           ,e17,e18,e19,e20,e21,e22,e23,e24,e25,e26,e27,e28,e29,e30,e31]
-
-		xorVector k i (t0,t1,t2,t3) =
-			( V.unsafeIndex k (i+0) `xor` t0
-			, V.unsafeIndex k (i+1) `xor` t1
-			, V.unsafeIndex k (i+2) `xor` t2
-			, V.unsafeIndex k (i+3) `xor` t3
-			)
-
-		xorSboxVector k i (t0,t1,t2,t3) =
-			( V.unsafeIndex k (i+0) `xor` sbox t0
-			, V.unsafeIndex k (i+1) `xor` sbox t1
-			, V.unsafeIndex k (i+2) `xor` sbox t2
-			, V.unsafeIndex k (i+3) `xor` sbox t3
-			)
-
-		cR0 it r0 r1 r2 r3 =
-			(sbox r1 `xor` rcon it, sbox r2, sbox r3, sbox r0)
-
-{-# INLINE shiftRows #-}
-shiftRows :: AESState -> IO ()
-shiftRows blk = do
-	r32 blk 0 >>= w32 blk 0 . msbox32
-	r32 blk 1 >>= \t1 -> w32 blk 1 $ rotateR (msbox32 t1) 8
-	r32 blk 2 >>= \t2 -> w32 blk 2 $ rotateR (msbox32 t2) 16
-	r32 blk 3 >>= \t3 -> w32 blk 3 $ rotateR (msbox32 t3) 24
-
-{-# INLINE addRoundKey #-}
-addRoundKey :: Key -> Int -> AESState -> IO ()
-addRoundKey (Key key) i blk = forM_ [0..15] $ \n -> do
-	r8 blk n >>= \v1 -> w8 blk n $ v1 `xor` V.unsafeIndex key (16 * i + swapIndex n)
-
-{-# INLINE mixColumns #-}
-mixColumns :: AESState -> IO ()
-mixColumns state = pr 0 >> pr 1 >> pr 2 >> pr 3
-	where
-		{-# INLINE pr #-}
-		pr i = do
-			cpy0 <- r8 state (0 * 4 + i)
-			cpy1 <- r8 state (1 * 4 + i)
-			cpy2 <- r8 state (2 * 4 + i)
-			cpy3 <- r8 state (3 * 4 + i)
-
-			let state0 = gm2 cpy0 `xor` gm1 cpy3 `xor` gm1 cpy2 `xor` gm3 cpy1
-			let state4 = gm2 cpy1 `xor` gm1 cpy0 `xor` gm1 cpy3 `xor` gm3 cpy2
-			let state8 = gm2 cpy2 `xor` gm1 cpy1 `xor` gm1 cpy0 `xor` gm3 cpy3
-			let state12 = gm2 cpy3 `xor` gm1 cpy2 `xor` gm1 cpy1 `xor` gm3 cpy0
-
-			w8 state (0 * 4 + i) state0
-			w8 state (1 * 4 + i) state4
-			w8 state (2 * 4 + i) state8
-			w8 state (3 * 4 + i) state12
-		{-# INLINE gm1 #-}
-		gm1 a = a
-
-{-# INLINE shiftRowsInv #-}
-shiftRowsInv :: AESState -> IO ()
-shiftRowsInv blk = do
-	r32 blk 0 >>= w32 blk 0 . mrsbox32
-	r32 blk 1 >>= \t1 -> w32 blk 1 $ mrsbox32 $ rotateL t1 8
-	r32 blk 2 >>= \t2 -> w32 blk 2 $ mrsbox32 $ rotateL t2 16
-	r32 blk 3 >>= \t3 -> w32 blk 3 $ mrsbox32 $ rotateL t3 24
-
-{-# INLINE mixColumnsInv #-}
-mixColumnsInv :: AESState -> IO ()
-mixColumnsInv state = pr 0 >> pr 1 >> pr 2 >> pr 3
-	where
-		{-# INLINE pr #-}
-		pr i = do
-			cpy0 <- r8 state (0 * 4 + i)
-			cpy1 <- r8 state (1 * 4 + i)
-			cpy2 <- r8 state (2 * 4 + i)
-			cpy3 <- r8 state (3 * 4 + i)
-
-			let state0  = gm14 cpy0 `xor` gm9 cpy3 `xor` gm13 cpy2 `xor` gm11 cpy1
-			let state4  = gm14 cpy1 `xor` gm9 cpy0 `xor` gm13 cpy3 `xor` gm11 cpy2
-			let state8  = gm14 cpy2 `xor` gm9 cpy1 `xor` gm13 cpy0 `xor` gm11 cpy3
-			let state12 = gm14 cpy3 `xor` gm9 cpy2 `xor` gm13 cpy1 `xor` gm11 cpy0
-
-			w8 state (0 * 4 + i) state0
-			w8 state (1 * 4 + i) state4
-			w8 state (2 * 4 + i) state8
-			w8 state (3 * 4 + i) state12
-
-{-# INLINE r8 #-}
-r8 :: AESState -> Int -> IO Word8
-r8 = readByteArray
-
-{-# INLINE w8 #-}
-w8 :: AESState -> Int -> Word8 -> IO ()
-w8 = writeByteArray
-
-{-# INLINE r32 #-}
-r32 :: AESState -> Int -> IO Word32
-r32 = readByteArray
-
-{-# INLINE w32 #-}
-w32 :: AESState -> Int -> Word32 -> IO ()
-w32 = writeByteArray
-
-msbox32 :: Word32 -> Word32
-msbox32 w = sbox4 a .|. sbox3 b .|. sbox2 c .|. sbox1 d
-	where
-		a = fromIntegral (w `shiftR` 24)
-		b = fromIntegral (w `shiftR` 16)
-		c = fromIntegral (w `shiftR` 8)
-		d = fromIntegral w
-
-mrsbox32 :: Word32 -> Word32
-mrsbox32 w = fromIntegral (rsbox a) `shiftL` 24 .|.
-             fromIntegral (rsbox b) `shiftL` 16 .|.
-             fromIntegral (rsbox c) `shiftL` 8  .|.
-             fromIntegral (rsbox d)
-	where
-		a = fromIntegral (w `shiftR` 24)
-		b = fromIntegral (w `shiftR` 16)
-		c = fromIntegral (w `shiftR` 8)
-		d = fromIntegral w
-
-
-{-# INLINE swapBlock #-}
-swapBlock :: ByteString -> AESState -> IO ()
-swapBlock b blk = do -- V.generate 16 (\i -> B.unsafeIndex b $ swapIndex i)
-	forM_ [0..15] $ \i -> w8 blk i $ B.unsafeIndex b $ swapIndex i
-
-{-# INLINE swapBlockInv #-}
-swapBlockInv :: AESState -> Ptr Word8 -> IO ()
-swapBlockInv blk ptr = forM_ [0..15] $ \i -> r8 blk (swapIndex i) >>= pokeByteOff ptr i
-
-{-# INLINE sbox #-}
-sbox :: Word8 -> Word8
-sbox (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x63\x7c\x77\x7b\xf2\x6b\x6f\xc5\
-		\\x30\x01\x67\x2b\xfe\xd7\xab\x76\
-		\\xca\x82\xc9\x7d\xfa\x59\x47\xf0\
-		\\xad\xd4\xa2\xaf\x9c\xa4\x72\xc0\
-		\\xb7\xfd\x93\x26\x36\x3f\xf7\xcc\
-		\\x34\xa5\xe5\xf1\x71\xd8\x31\x15\
-		\\x04\xc7\x23\xc3\x18\x96\x05\x9a\
-		\\x07\x12\x80\xe2\xeb\x27\xb2\x75\
-		\\x09\x83\x2c\x1a\x1b\x6e\x5a\xa0\
-		\\x52\x3b\xd6\xb3\x29\xe3\x2f\x84\
-		\\x53\xd1\x00\xed\x20\xfc\xb1\x5b\
-		\\x6a\xcb\xbe\x39\x4a\x4c\x58\xcf\
-		\\xd0\xef\xaa\xfb\x43\x4d\x33\x85\
-		\\x45\xf9\x02\x7f\x50\x3c\x9f\xa8\
-		\\x51\xa3\x40\x8f\x92\x9d\x38\xf5\
-		\\xbc\xb6\xda\x21\x10\xff\xf3\xd2\
-		\\xcd\x0c\x13\xec\x5f\x97\x44\x17\
-		\\xc4\xa7\x7e\x3d\x64\x5d\x19\x73\
-		\\x60\x81\x4f\xdc\x22\x2a\x90\x88\
-		\\x46\xee\xb8\x14\xde\x5e\x0b\xdb\
-		\\xe0\x32\x3a\x0a\x49\x06\x24\x5c\
-		\\xc2\xd3\xac\x62\x91\x95\xe4\x79\
-		\\xe7\xc8\x37\x6d\x8d\xd5\x4e\xa9\
-		\\x6c\x56\xf4\xea\x65\x7a\xae\x08\
-		\\xba\x78\x25\x2e\x1c\xa6\xb4\xc6\
-		\\xe8\xdd\x74\x1f\x4b\xbd\x8b\x8a\
-		\\x70\x3e\xb5\x66\x48\x03\xf6\x0e\
-		\\x61\x35\x57\xb9\x86\xc1\x1d\x9e\
-		\\xe1\xf8\x98\x11\x69\xd9\x8e\x94\
-		\\x9b\x1e\x87\xe9\xce\x55\x28\xdf\
-		\\x8c\xa1\x89\x0d\xbf\xe6\x42\x68\
-		\\x41\x99\x2d\x0f\xb0\x54\xbb\x16"#
-
-{-# INLINE sbox1 #-}
-{-# INLINE sbox2 #-}
-{-# INLINE sbox3 #-}
-{-# INLINE sbox4 #-}
-sbox1, sbox2, sbox3, sbox4 :: Word8 -> Word32
-sbox1 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
-	where !(Table table) = sbox1Tab
-sbox2 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
-	where !(Table table) = sbox2Tab
-sbox3 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
-	where !(Table table) = sbox3Tab
-sbox4 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
-	where !(Table table) = sbox4Tab
-
-sbox1Tab, sbox2Tab, sbox3Tab, sbox4Tab :: Table
-sbox1Tab = if littleEndian then sbox_x000 else sbox_000x
-sbox2Tab = if littleEndian then sbox_0x00 else sbox_00x0
-sbox3Tab = if littleEndian then sbox_00x0 else sbox_0x00
-sbox4Tab = if littleEndian then sbox_000x else sbox_x000
-
-data Table = Table !Addr#
-
-sbox_000x, sbox_00x0, sbox_0x00, sbox_x000 :: Table
-sbox_000x = Table
-		"\x00\x00\x00\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\
-		\\x00\x00\x00\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\
-		\\x00\x00\x00\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\
-		\\x00\x00\x00\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\
-		\\x00\x00\x00\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\
-		\\x00\x00\x00\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\
-		\\x00\x00\x00\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\
-		\\x00\x00\x00\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\
-		\\x00\x00\x00\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\
-		\\x00\x00\x00\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\
-		\\x00\x00\x00\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\
-		\\x00\x00\x00\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\
-		\\x00\x00\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\
-		\\x00\x00\x00\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\
-		\\x00\x00\x00\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\
-		\\x00\x00\x00\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\
-		\\x00\x00\x00\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\
-		\\x00\x00\x00\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\
-		\\x00\x00\x00\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\
-		\\x00\x00\x00\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\
-		\\x00\x00\x00\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\
-		\\x00\x00\x00\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\
-		\\x00\x00\x00\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\
-		\\x00\x00\x00\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\
-		\\x00\x00\x00\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\
-		\\x00\x00\x00\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\
-		\\x00\x00\x00\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\
-		\\x00\x00\x00\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\
-		\\x00\x00\x00\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\
-		\\x00\x00\x00\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\
-		\\x00\x00\x00\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\
-		\\x00\x00\x00\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\
-		\\x00\x00\x00\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\
-		\\x00\x00\x00\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\
-		\\x00\x00\x00\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\
-		\\x00\x00\x00\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\
-		\\x00\x00\x00\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\
-		\\x00\x00\x00\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\
-		\\x00\x00\x00\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\
-		\\x00\x00\x00\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\
-		\\x00\x00\x00\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\
-		\\x00\x00\x00\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\
-		\\x00\x00\x00\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\
-		\\x00\x00\x00\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\
-		\\x00\x00\x00\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\
-		\\x00\x00\x00\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\
-		\\x00\x00\x00\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\
-		\\x00\x00\x00\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\
-		\\x00\x00\x00\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\
-		\\x00\x00\x00\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\
-		\\x00\x00\x00\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\
-		\\x00\x00\x00\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\
-		\\x00\x00\x00\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\
-		\\x00\x00\x00\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\
-		\\x00\x00\x00\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\
-		\\x00\x00\x00\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\
-		\\x00\x00\x00\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\
-		\\x00\x00\x00\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\
-		\\x00\x00\x00\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\
-		\\x00\x00\x00\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\
-		\\x00\x00\x00\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\
-		\\x00\x00\x00\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\
-		\\x00\x00\x00\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\
-		\\x00\x00\x00\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16"#
-
-sbox_00x0 = Table
-		"\x00\x00\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\x00\
-		\\x00\x00\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\x00\
-		\\x00\x00\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\x00\
-		\\x00\x00\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\x00\
-		\\x00\x00\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\x00\
-		\\x00\x00\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\x00\
-		\\x00\x00\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\x00\
-		\\x00\x00\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\x00\
-		\\x00\x00\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\x00\
-		\\x00\x00\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\x00\
-		\\x00\x00\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\x00\
-		\\x00\x00\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\x00\
-		\\x00\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\x00\
-		\\x00\x00\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\x00\
-		\\x00\x00\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\x00\
-		\\x00\x00\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\x00\
-		\\x00\x00\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\x00\
-		\\x00\x00\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\x00\
-		\\x00\x00\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\x00\
-		\\x00\x00\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\x00\
-		\\x00\x00\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\x00\
-		\\x00\x00\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\x00\
-		\\x00\x00\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\x00\
-		\\x00\x00\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\x00\
-		\\x00\x00\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\x00\
-		\\x00\x00\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\x00\
-		\\x00\x00\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\x00\
-		\\x00\x00\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\x00\
-		\\x00\x00\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\x00\
-		\\x00\x00\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\x00\
-		\\x00\x00\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\x00\
-		\\x00\x00\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\x00\
-		\\x00\x00\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\x00\
-		\\x00\x00\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\x00\
-		\\x00\x00\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\x00\
-		\\x00\x00\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\x00\
-		\\x00\x00\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\x00\
-		\\x00\x00\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\x00\
-		\\x00\x00\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\x00\
-		\\x00\x00\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\x00\
-		\\x00\x00\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\x00\
-		\\x00\x00\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\x00\
-		\\x00\x00\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\x00\
-		\\x00\x00\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\x00\
-		\\x00\x00\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\x00\
-		\\x00\x00\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\x00\
-		\\x00\x00\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\x00\
-		\\x00\x00\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\x00\
-		\\x00\x00\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\x00\
-		\\x00\x00\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\x00\
-		\\x00\x00\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\x00\
-		\\x00\x00\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\x00\
-		\\x00\x00\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\x00\
-		\\x00\x00\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\x00\
-		\\x00\x00\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\x00\
-		\\x00\x00\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\x00\
-		\\x00\x00\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\x00\
-		\\x00\x00\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\x00\
-		\\x00\x00\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\x00\
-		\\x00\x00\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\x00\
-		\\x00\x00\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\x00\
-		\\x00\x00\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\x00\
-		\\x00\x00\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\x00\
-		\\x00\x00\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16\x00"#
-
-sbox_0x00 = Table
-		"\x00\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\x00\x00\
-		\\x00\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\x00\x00\
-		\\x00\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\x00\x00\
-		\\x00\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\x00\x00\
-		\\x00\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\x00\x00\
-		\\x00\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\x00\x00\
-		\\x00\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\x00\x00\
-		\\x00\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\x00\x00\
-		\\x00\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\x00\x00\
-		\\x00\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\x00\x00\
-		\\x00\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\x00\x00\
-		\\x00\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\x00\x00\
-		\\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\x00\x00\
-		\\x00\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\x00\x00\
-		\\x00\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\x00\x00\
-		\\x00\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\x00\x00\
-		\\x00\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\x00\x00\
-		\\x00\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\x00\x00\
-		\\x00\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\x00\x00\
-		\\x00\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\x00\x00\
-		\\x00\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\x00\x00\
-		\\x00\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\x00\x00\
-		\\x00\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\x00\x00\
-		\\x00\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\x00\x00\
-		\\x00\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\x00\x00\
-		\\x00\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\x00\x00\
-		\\x00\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\x00\x00\
-		\\x00\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\x00\x00\
-		\\x00\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\x00\x00\
-		\\x00\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\x00\x00\
-		\\x00\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\x00\x00\
-		\\x00\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\x00\x00\
-		\\x00\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\x00\x00\
-		\\x00\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\x00\x00\
-		\\x00\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\x00\x00\
-		\\x00\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\x00\x00\
-		\\x00\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\x00\x00\
-		\\x00\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\x00\x00\
-		\\x00\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\x00\x00\
-		\\x00\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\x00\x00\
-		\\x00\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\x00\x00\
-		\\x00\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\x00\x00\
-		\\x00\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\x00\x00\
-		\\x00\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\x00\x00\
-		\\x00\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\x00\x00\
-		\\x00\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\x00\x00\
-		\\x00\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\x00\x00\
-		\\x00\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\x00\x00\
-		\\x00\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\x00\x00\
-		\\x00\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\x00\x00\
-		\\x00\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\x00\x00\
-		\\x00\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\x00\x00\
-		\\x00\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\x00\x00\
-		\\x00\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\x00\x00\
-		\\x00\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\x00\x00\
-		\\x00\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\x00\x00\
-		\\x00\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\x00\x00\
-		\\x00\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\x00\x00\
-		\\x00\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\x00\x00\
-		\\x00\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\x00\x00\
-		\\x00\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\x00\x00\
-		\\x00\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\x00\x00\
-		\\x00\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\x00\x00\
-		\\x00\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16\x00\x00"#
-
-sbox_x000 = Table
-		"\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\x00\x00\x00\
-		\\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\x00\x00\x00\
-		\\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\x00\x00\x00\
-		\\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\x00\x00\x00\
-		\\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\x00\x00\x00\
-		\\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\x00\x00\x00\
-		\\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\x00\x00\x00\
-		\\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\x00\x00\x00\
-		\\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\x00\x00\x00\
-		\\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\x00\x00\x00\
-		\\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\x00\x00\x00\
-		\\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\x00\x00\x00\
-		\\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\x00\x00\x00\
-		\\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\x00\x00\x00\
-		\\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\x00\x00\x00\
-		\\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\x00\x00\x00\
-		\\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\x00\x00\x00\
-		\\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\x00\x00\x00\
-		\\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\x00\x00\x00\
-		\\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\x00\x00\x00\
-		\\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\x00\x00\x00\
-		\\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\x00\x00\x00\
-		\\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\x00\x00\x00\
-		\\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\x00\x00\x00\
-		\\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\x00\x00\x00\
-		\\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\x00\x00\x00\
-		\\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\x00\x00\x00\
-		\\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\x00\x00\x00\
-		\\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\x00\x00\x00\
-		\\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\x00\x00\x00\
-		\\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\x00\x00\x00\
-		\\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\x00\x00\x00\
-		\\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\x00\x00\x00\
-		\\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\x00\x00\x00\
-		\\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\x00\x00\x00\
-		\\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\x00\x00\x00\
-		\\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\x00\x00\x00\
-		\\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\x00\x00\x00\
-		\\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\x00\x00\x00\
-		\\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\x00\x00\x00\
-		\\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\x00\x00\x00\
-		\\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\x00\x00\x00\
-		\\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\x00\x00\x00\
-		\\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\x00\x00\x00\
-		\\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\x00\x00\x00\
-		\\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\x00\x00\x00\
-		\\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\x00\x00\x00\
-		\\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\x00\x00\x00\
-		\\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\x00\x00\x00\
-		\\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\x00\x00\x00\
-		\\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\x00\x00\x00\
-		\\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\x00\x00\x00\
-		\\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\x00\x00\x00\
-		\\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\x00\x00\x00\
-		\\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\x00\x00\x00\
-		\\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\x00\x00\x00\
-		\\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\x00\x00\x00\
-		\\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\x00\x00\x00\
-		\\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\x00\x00\x00\
-		\\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\x00\x00\x00\
-		\\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\x00\x00\x00\
-		\\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\x00\x00\x00\
-		\\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\x00\x00\x00\
-		\\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16\x00\x00\x00"#
-
-
-{-# INLINE rsbox #-}
-rsbox :: Word8 -> Word8
-rsbox (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x52\x09\x6a\xd5\x30\x36\xa5\x38\
-		\\xbf\x40\xa3\x9e\x81\xf3\xd7\xfb\
-		\\x7c\xe3\x39\x82\x9b\x2f\xff\x87\
-		\\x34\x8e\x43\x44\xc4\xde\xe9\xcb\
-		\\x54\x7b\x94\x32\xa6\xc2\x23\x3d\
-		\\xee\x4c\x95\x0b\x42\xfa\xc3\x4e\
-		\\x08\x2e\xa1\x66\x28\xd9\x24\xb2\
-		\\x76\x5b\xa2\x49\x6d\x8b\xd1\x25\
-		\\x72\xf8\xf6\x64\x86\x68\x98\x16\
-		\\xd4\xa4\x5c\xcc\x5d\x65\xb6\x92\
-		\\x6c\x70\x48\x50\xfd\xed\xb9\xda\
-		\\x5e\x15\x46\x57\xa7\x8d\x9d\x84\
-		\\x90\xd8\xab\x00\x8c\xbc\xd3\x0a\
-		\\xf7\xe4\x58\x05\xb8\xb3\x45\x06\
-		\\xd0\x2c\x1e\x8f\xca\x3f\x0f\x02\
-		\\xc1\xaf\xbd\x03\x01\x13\x8a\x6b\
-		\\x3a\x91\x11\x41\x4f\x67\xdc\xea\
-		\\x97\xf2\xcf\xce\xf0\xb4\xe6\x73\
-		\\x96\xac\x74\x22\xe7\xad\x35\x85\
-		\\xe2\xf9\x37\xe8\x1c\x75\xdf\x6e\
-		\\x47\xf1\x1a\x71\x1d\x29\xc5\x89\
-		\\x6f\xb7\x62\x0e\xaa\x18\xbe\x1b\
-		\\xfc\x56\x3e\x4b\xc6\xd2\x79\x20\
-		\\x9a\xdb\xc0\xfe\x78\xcd\x5a\xf4\
-		\\x1f\xdd\xa8\x33\x88\x07\xc7\x31\
-		\\xb1\x12\x10\x59\x27\x80\xec\x5f\
-		\\x60\x51\x7f\xa9\x19\xb5\x4a\x0d\
-		\\x2d\xe5\x7a\x9f\x93\xc9\x9c\xef\
-		\\xa0\xe0\x3b\x4d\xae\x2a\xf5\xb0\
-		\\xc8\xeb\xbb\x3c\x83\x53\x99\x61\
-		\\x17\x2b\x04\x7e\xba\x77\xd6\x26\
-		\\xe1\x69\x14\x63\x55\x21\x0c\x7d"#
-
-{-# INLINE rcon #-}
-rcon :: Int -> Word8
-rcon (I# i) = W8# (indexWord8OffAddr# table (i `remInt#` 51#))
-	where !table =
-		"\x8d\x01\x02\x04\x08\x10\x20\x40\
-		\\x80\x1b\x36\x6c\xd8\xab\x4d\x9a\
-		\\x2f\x5e\xbc\x63\xc6\x97\x35\x6a\
-		\\xd4\xb3\x7d\xfa\xef\xc5\x91\x39\
-		\\x72\xe4\xd3\xbd\x61\xc2\x9f\x25\
-		\\x4a\x94\x33\x66\xcc\x83\x1d\x3a\
-		\\x74\xe8\xcb"#
-
-{-# INLINE gm2 #-}
-{-# INLINE gm3 #-}
-{-# INLINE gm9 #-}
-{-# INLINE gm11 #-}
-{-# INLINE gm13 #-}
-{-# INLINE gm14 #-}
-gm2, gm3, gm9, gm11, gm13, gm14 :: Word8 -> Word8
-gm2 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x00\x02\x04\x06\x08\x0a\x0c\x0e\
-		\\x10\x12\x14\x16\x18\x1a\x1c\x1e\
-		\\x20\x22\x24\x26\x28\x2a\x2c\x2e\
-		\\x30\x32\x34\x36\x38\x3a\x3c\x3e\
-		\\x40\x42\x44\x46\x48\x4a\x4c\x4e\
-		\\x50\x52\x54\x56\x58\x5a\x5c\x5e\
-		\\x60\x62\x64\x66\x68\x6a\x6c\x6e\
-		\\x70\x72\x74\x76\x78\x7a\x7c\x7e\
-		\\x80\x82\x84\x86\x88\x8a\x8c\x8e\
-		\\x90\x92\x94\x96\x98\x9a\x9c\x9e\
-		\\xa0\xa2\xa4\xa6\xa8\xaa\xac\xae\
-		\\xb0\xb2\xb4\xb6\xb8\xba\xbc\xbe\
-		\\xc0\xc2\xc4\xc6\xc8\xca\xcc\xce\
-		\\xd0\xd2\xd4\xd6\xd8\xda\xdc\xde\
-		\\xe0\xe2\xe4\xe6\xe8\xea\xec\xee\
-		\\xf0\xf2\xf4\xf6\xf8\xfa\xfc\xfe\
-		\\x1b\x19\x1f\x1d\x13\x11\x17\x15\
-		\\x0b\x09\x0f\x0d\x03\x01\x07\x05\
-		\\x3b\x39\x3f\x3d\x33\x31\x37\x35\
-		\\x2b\x29\x2f\x2d\x23\x21\x27\x25\
-		\\x5b\x59\x5f\x5d\x53\x51\x57\x55\
-		\\x4b\x49\x4f\x4d\x43\x41\x47\x45\
-		\\x7b\x79\x7f\x7d\x73\x71\x77\x75\
-		\\x6b\x69\x6f\x6d\x63\x61\x67\x65\
-		\\x9b\x99\x9f\x9d\x93\x91\x97\x95\
-		\\x8b\x89\x8f\x8d\x83\x81\x87\x85\
-		\\xbb\xb9\xbf\xbd\xb3\xb1\xb7\xb5\
-		\\xab\xa9\xaf\xad\xa3\xa1\xa7\xa5\
-		\\xdb\xd9\xdf\xdd\xd3\xd1\xd7\xd5\
-		\\xcb\xc9\xcf\xcd\xc3\xc1\xc7\xc5\
-		\\xfb\xf9\xff\xfd\xf3\xf1\xf7\xf5\
-		\\xeb\xe9\xef\xed\xe3\xe1\xe7\xe5"#
-
-gm3 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x00\x03\x06\x05\x0c\x0f\x0a\x09\
-		\\x18\x1b\x1e\x1d\x14\x17\x12\x11\
-		\\x30\x33\x36\x35\x3c\x3f\x3a\x39\
-		\\x28\x2b\x2e\x2d\x24\x27\x22\x21\
-		\\x60\x63\x66\x65\x6c\x6f\x6a\x69\
-		\\x78\x7b\x7e\x7d\x74\x77\x72\x71\
-		\\x50\x53\x56\x55\x5c\x5f\x5a\x59\
-		\\x48\x4b\x4e\x4d\x44\x47\x42\x41\
-		\\xc0\xc3\xc6\xc5\xcc\xcf\xca\xc9\
-		\\xd8\xdb\xde\xdd\xd4\xd7\xd2\xd1\
-		\\xf0\xf3\xf6\xf5\xfc\xff\xfa\xf9\
-		\\xe8\xeb\xee\xed\xe4\xe7\xe2\xe1\
-		\\xa0\xa3\xa6\xa5\xac\xaf\xaa\xa9\
-		\\xb8\xbb\xbe\xbd\xb4\xb7\xb2\xb1\
-		\\x90\x93\x96\x95\x9c\x9f\x9a\x99\
-		\\x88\x8b\x8e\x8d\x84\x87\x82\x81\
-		\\x9b\x98\x9d\x9e\x97\x94\x91\x92\
-		\\x83\x80\x85\x86\x8f\x8c\x89\x8a\
-		\\xab\xa8\xad\xae\xa7\xa4\xa1\xa2\
-		\\xb3\xb0\xb5\xb6\xbf\xbc\xb9\xba\
-		\\xfb\xf8\xfd\xfe\xf7\xf4\xf1\xf2\
-		\\xe3\xe0\xe5\xe6\xef\xec\xe9\xea\
-		\\xcb\xc8\xcd\xce\xc7\xc4\xc1\xc2\
-		\\xd3\xd0\xd5\xd6\xdf\xdc\xd9\xda\
-		\\x5b\x58\x5d\x5e\x57\x54\x51\x52\
-		\\x43\x40\x45\x46\x4f\x4c\x49\x4a\
-		\\x6b\x68\x6d\x6e\x67\x64\x61\x62\
-		\\x73\x70\x75\x76\x7f\x7c\x79\x7a\
-		\\x3b\x38\x3d\x3e\x37\x34\x31\x32\
-		\\x23\x20\x25\x26\x2f\x2c\x29\x2a\
-		\\x0b\x08\x0d\x0e\x07\x04\x01\x02\
-		\\x13\x10\x15\x16\x1f\x1c\x19\x1a"#
-
-gm9 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x00\x09\x12\x1b\x24\x2d\x36\x3f\
-		\\x48\x41\x5a\x53\x6c\x65\x7e\x77\
-		\\x90\x99\x82\x8b\xb4\xbd\xa6\xaf\
-		\\xd8\xd1\xca\xc3\xfc\xf5\xee\xe7\
-		\\x3b\x32\x29\x20\x1f\x16\x0d\x04\
-		\\x73\x7a\x61\x68\x57\x5e\x45\x4c\
-		\\xab\xa2\xb9\xb0\x8f\x86\x9d\x94\
-		\\xe3\xea\xf1\xf8\xc7\xce\xd5\xdc\
-		\\x76\x7f\x64\x6d\x52\x5b\x40\x49\
-		\\x3e\x37\x2c\x25\x1a\x13\x08\x01\
-		\\xe6\xef\xf4\xfd\xc2\xcb\xd0\xd9\
-		\\xae\xa7\xbc\xb5\x8a\x83\x98\x91\
-		\\x4d\x44\x5f\x56\x69\x60\x7b\x72\
-		\\x05\x0c\x17\x1e\x21\x28\x33\x3a\
-		\\xdd\xd4\xcf\xc6\xf9\xf0\xeb\xe2\
-		\\x95\x9c\x87\x8e\xb1\xb8\xa3\xaa\
-		\\xec\xe5\xfe\xf7\xc8\xc1\xda\xd3\
-		\\xa4\xad\xb6\xbf\x80\x89\x92\x9b\
-		\\x7c\x75\x6e\x67\x58\x51\x4a\x43\
-		\\x34\x3d\x26\x2f\x10\x19\x02\x0b\
-		\\xd7\xde\xc5\xcc\xf3\xfa\xe1\xe8\
-		\\x9f\x96\x8d\x84\xbb\xb2\xa9\xa0\
-		\\x47\x4e\x55\x5c\x63\x6a\x71\x78\
-		\\x0f\x06\x1d\x14\x2b\x22\x39\x30\
-		\\x9a\x93\x88\x81\xbe\xb7\xac\xa5\
-		\\xd2\xdb\xc0\xc9\xf6\xff\xe4\xed\
-		\\x0a\x03\x18\x11\x2e\x27\x3c\x35\
-		\\x42\x4b\x50\x59\x66\x6f\x74\x7d\
-		\\xa1\xa8\xb3\xba\x85\x8c\x97\x9e\
-		\\xe9\xe0\xfb\xf2\xcd\xc4\xdf\xd6\
-		\\x31\x38\x23\x2a\x15\x1c\x07\x0e\
-		\\x79\x70\x6b\x62\x5d\x54\x4f\x46"#
-
-gm11 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x00\x0b\x16\x1d\x2c\x27\x3a\x31\
-		\\x58\x53\x4e\x45\x74\x7f\x62\x69\
-		\\xb0\xbb\xa6\xad\x9c\x97\x8a\x81\
-		\\xe8\xe3\xfe\xf5\xc4\xcf\xd2\xd9\
-		\\x7b\x70\x6d\x66\x57\x5c\x41\x4a\
-		\\x23\x28\x35\x3e\x0f\x04\x19\x12\
-		\\xcb\xc0\xdd\xd6\xe7\xec\xf1\xfa\
-		\\x93\x98\x85\x8e\xbf\xb4\xa9\xa2\
-		\\xf6\xfd\xe0\xeb\xda\xd1\xcc\xc7\
-		\\xae\xa5\xb8\xb3\x82\x89\x94\x9f\
-		\\x46\x4d\x50\x5b\x6a\x61\x7c\x77\
-		\\x1e\x15\x08\x03\x32\x39\x24\x2f\
-		\\x8d\x86\x9b\x90\xa1\xaa\xb7\xbc\
-		\\xd5\xde\xc3\xc8\xf9\xf2\xef\xe4\
-		\\x3d\x36\x2b\x20\x11\x1a\x07\x0c\
-		\\x65\x6e\x73\x78\x49\x42\x5f\x54\
-		\\xf7\xfc\xe1\xea\xdb\xd0\xcd\xc6\
-		\\xaf\xa4\xb9\xb2\x83\x88\x95\x9e\
-		\\x47\x4c\x51\x5a\x6b\x60\x7d\x76\
-		\\x1f\x14\x09\x02\x33\x38\x25\x2e\
-		\\x8c\x87\x9a\x91\xa0\xab\xb6\xbd\
-		\\xd4\xdf\xc2\xc9\xf8\xf3\xee\xe5\
-		\\x3c\x37\x2a\x21\x10\x1b\x06\x0d\
-		\\x64\x6f\x72\x79\x48\x43\x5e\x55\
-		\\x01\x0a\x17\x1c\x2d\x26\x3b\x30\
-		\\x59\x52\x4f\x44\x75\x7e\x63\x68\
-		\\xb1\xba\xa7\xac\x9d\x96\x8b\x80\
-		\\xe9\xe2\xff\xf4\xc5\xce\xd3\xd8\
-		\\x7a\x71\x6c\x67\x56\x5d\x40\x4b\
-		\\x22\x29\x34\x3f\x0e\x05\x18\x13\
-		\\xca\xc1\xdc\xd7\xe6\xed\xf0\xfb\
-		\\x92\x99\x84\x8f\xbe\xb5\xa8\xa3"#
-
-gm13 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x00\x0d\x1a\x17\x34\x39\x2e\x23\
-		\\x68\x65\x72\x7f\x5c\x51\x46\x4b\
-		\\xd0\xdd\xca\xc7\xe4\xe9\xfe\xf3\
-		\\xb8\xb5\xa2\xaf\x8c\x81\x96\x9b\
-		\\xbb\xb6\xa1\xac\x8f\x82\x95\x98\
-		\\xd3\xde\xc9\xc4\xe7\xea\xfd\xf0\
-		\\x6b\x66\x71\x7c\x5f\x52\x45\x48\
-		\\x03\x0e\x19\x14\x37\x3a\x2d\x20\
-		\\x6d\x60\x77\x7a\x59\x54\x43\x4e\
-		\\x05\x08\x1f\x12\x31\x3c\x2b\x26\
-		\\xbd\xb0\xa7\xaa\x89\x84\x93\x9e\
-		\\xd5\xd8\xcf\xc2\xe1\xec\xfb\xf6\
-		\\xd6\xdb\xcc\xc1\xe2\xef\xf8\xf5\
-		\\xbe\xb3\xa4\xa9\x8a\x87\x90\x9d\
-		\\x06\x0b\x1c\x11\x32\x3f\x28\x25\
-		\\x6e\x63\x74\x79\x5a\x57\x40\x4d\
-		\\xda\xd7\xc0\xcd\xee\xe3\xf4\xf9\
-		\\xb2\xbf\xa8\xa5\x86\x8b\x9c\x91\
-		\\x0a\x07\x10\x1d\x3e\x33\x24\x29\
-		\\x62\x6f\x78\x75\x56\x5b\x4c\x41\
-		\\x61\x6c\x7b\x76\x55\x58\x4f\x42\
-		\\x09\x04\x13\x1e\x3d\x30\x27\x2a\
-		\\xb1\xbc\xab\xa6\x85\x88\x9f\x92\
-		\\xd9\xd4\xc3\xce\xed\xe0\xf7\xfa\
-		\\xb7\xba\xad\xa0\x83\x8e\x99\x94\
-		\\xdf\xd2\xc5\xc8\xeb\xe6\xf1\xfc\
-		\\x67\x6a\x7d\x70\x53\x5e\x49\x44\
-		\\x0f\x02\x15\x18\x3b\x36\x21\x2c\
-		\\x0c\x01\x16\x1b\x38\x35\x22\x2f\
-		\\x64\x69\x7e\x73\x50\x5d\x4a\x47\
-		\\xdc\xd1\xc6\xcb\xe8\xe5\xf2\xff\
-		\\xb4\xb9\xae\xa3\x80\x8d\x9a\x97"#
-
-gm14 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
-	where !table =
-		"\x00\x0e\x1c\x12\x38\x36\x24\x2a\
-		\\x70\x7e\x6c\x62\x48\x46\x54\x5a\
-		\\xe0\xee\xfc\xf2\xd8\xd6\xc4\xca\
-		\\x90\x9e\x8c\x82\xa8\xa6\xb4\xba\
-		\\xdb\xd5\xc7\xc9\xe3\xed\xff\xf1\
-		\\xab\xa5\xb7\xb9\x93\x9d\x8f\x81\
-		\\x3b\x35\x27\x29\x03\x0d\x1f\x11\
-		\\x4b\x45\x57\x59\x73\x7d\x6f\x61\
-		\\xad\xa3\xb1\xbf\x95\x9b\x89\x87\
-		\\xdd\xd3\xc1\xcf\xe5\xeb\xf9\xf7\
-		\\x4d\x43\x51\x5f\x75\x7b\x69\x67\
-		\\x3d\x33\x21\x2f\x05\x0b\x19\x17\
-		\\x76\x78\x6a\x64\x4e\x40\x52\x5c\
-		\\x06\x08\x1a\x14\x3e\x30\x22\x2c\
-		\\x96\x98\x8a\x84\xae\xa0\xb2\xbc\
-		\\xe6\xe8\xfa\xf4\xde\xd0\xc2\xcc\
-		\\x41\x4f\x5d\x53\x79\x77\x65\x6b\
-		\\x31\x3f\x2d\x23\x09\x07\x15\x1b\
-		\\xa1\xaf\xbd\xb3\x99\x97\x85\x8b\
-		\\xd1\xdf\xcd\xc3\xe9\xe7\xf5\xfb\
-		\\x9a\x94\x86\x88\xa2\xac\xbe\xb0\
-		\\xea\xe4\xf6\xf8\xd2\xdc\xce\xc0\
-		\\x7a\x74\x66\x68\x42\x4c\x5e\x50\
-		\\x0a\x04\x16\x18\x32\x3c\x2e\x20\
-		\\xec\xe2\xf0\xfe\xd4\xda\xc8\xc6\
-		\\x9c\x92\x80\x8e\xa4\xaa\xb8\xb6\
-		\\x0c\x02\x10\x1e\x34\x3a\x28\x26\
-		\\x7c\x72\x60\x6e\x44\x4a\x58\x56\
-		\\x37\x39\x2b\x25\x0f\x01\x13\x1d\
-		\\x47\x49\x5b\x55\x7f\x71\x63\x6d\
-		\\xd7\xd9\xcb\xc5\xef\xe1\xf3\xfd\
-		\\xa7\xa9\xbb\xb5\x9f\x91\x83\x8d"#
+-- |
+-- Module      : Crypto.Cipher.AES
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+
+module Crypto.Cipher.AES
+	( module Crypto.Cipher.AES.Haskell
+	) where
+
+import Crypto.Cipher.AES.Haskell
diff --git a/Crypto/Cipher/AES/Haskell.hs b/Crypto/Cipher/AES/Haskell.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/AES/Haskell.hs
@@ -0,0 +1,1014 @@
+{-# LANGUAGE BangPatterns, MagicHash #-}
+-- |
+-- Module      : Crypto.Cipher.AES.Haskell
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+
+module Crypto.Cipher.AES.Haskell
+	( Key
+	, IV
+	-- * Basic encryption and decryption
+	, encrypt
+	, decrypt
+	-- * CBC encryption and decryption
+	, encryptCBC
+	, decryptCBC
+	-- * key building mechanism
+	, initKey128
+	, initKey192
+	, initKey256
+	-- * Wrappers for "crypto-api" instances
+	, AES128
+	, AES192
+	, AES256
+	) where
+
+import Data.Word
+import Data.Vector.Unboxed (Vector)
+import qualified Data.Vector.Unboxed as V
+import Data.Bits
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Unsafe as B
+import qualified Data.ByteString.Internal as B
+
+import GHC.Prim (indexWord8OffAddr#, indexWord32OffAddr#, word2Int#, Addr#, remInt#)
+import GHC.Word
+import GHC.Types
+
+import Foreign.Ptr
+import Foreign.Storable
+
+import Data.Tagged (Tagged(..))
+import Crypto.Classes (BlockCipher(..))
+import Data.Serialize (Serialize(..), getByteString, putByteString)
+
+import Control.Monad (forM_)
+import Control.Monad.Primitive
+
+import Data.Primitive.ByteArray
+
+import System.Endian (getSystemEndianness, Endianness(..))
+
+newtype AES128 = A128 { unA128 :: Key }
+newtype AES192 = A192 { unA192 :: Key }
+newtype AES256 = A256 { unA256 :: Key }
+
+instance BlockCipher AES128 where
+	blockSize    = Tagged 128
+	encryptBlock = encrypt . unA128
+	decryptBlock = decrypt . unA128
+	buildKey b   = either (const Nothing) (Just . A128) $ initKey128 b
+	keyLength    = Tagged 128
+
+instance BlockCipher AES192 where
+	blockSize    = Tagged 128
+	encryptBlock = encrypt . unA192
+	decryptBlock = decrypt . unA192
+	buildKey b   = either (const Nothing) (Just . A192) $ initKey192 b
+	keyLength    = Tagged 192
+
+instance BlockCipher AES256 where
+	blockSize    = Tagged 128
+	encryptBlock = encrypt . unA256
+	decryptBlock = decrypt . unA256
+	buildKey b   = either (const Nothing) (Just . A256) $ initKey256 b
+	keyLength    = Tagged 256
+
+serializeKey :: Key -> ByteString
+serializeKey (Key v)
+	| V.length v == 176 = B.pack $ map (V.unsafeIndex v) [0..15]
+	| V.length v == 208 = B.pack $ map (V.unsafeIndex v) [0..23]
+	| otherwise         = B.pack $ map (V.unsafeIndex v) [0..31]
+
+instance Serialize AES128 where
+	put = putByteString . serializeKey . unA128
+	get = do
+		raw <- getByteString (128 `div` 8)
+		case buildKey raw of
+			Nothing -> fail "Invalid raw key material."
+			Just k  -> return k
+
+instance Serialize AES192 where
+	put = putByteString . serializeKey . unA192
+	get = do
+		raw <- getByteString (192 `div` 8)
+		case buildKey raw of
+			Nothing -> fail "Invalid raw key material."
+			Just k  -> return k
+
+instance Serialize AES256 where
+	put = putByteString . serializeKey . unA256
+	get = do
+		raw <- getByteString (256 `div` 8)
+		case buildKey raw of
+			Nothing -> fail "Invalid raw key material."
+			Just k  -> return k
+
+data Key = Key (Vector Word8)
+	deriving (Show,Eq)
+
+type IV = B.ByteString
+
+type AESState = MutableByteArray RealWorld
+
+{- | encrypt using CBC mode
+ - IV need to be 16 bytes and the data to encrypt a multiple of 16 bytes -}
+encryptCBC :: Key -> IV -> B.ByteString -> B.ByteString
+encryptCBC key iv b
+	| B.length iv /= 16        = error "invalid IV length"
+	| B.length b `mod` 16 /= 0 = error "invalid data length"
+	| otherwise                = B.concat $ encryptIter iv (makeChunks b)
+		where
+			encryptIter _   []     = []
+			encryptIter iv' (x:xs) =
+				let r = coreEncrypt key $ B.pack $ B.zipWith xor iv' x in
+				r : encryptIter r xs
+
+{- | encrypt using simple EBC mode -}
+encrypt :: Key -> B.ByteString -> B.ByteString
+encrypt key b
+	| B.length b `mod` 16 /= 0 = error "invalid data length"
+	| otherwise                = B.concat $ doChunks (coreEncrypt key) b
+
+{- | decrypt using CBC mode
+ - IV need to be 16 bytes and the data to decrypt a multiple of 16 bytes -}
+decryptCBC :: Key -> IV -> B.ByteString -> B.ByteString
+decryptCBC key iv b
+	| B.length iv /= 16        = error "invalid IV length"
+	| B.length b `mod` 16 /= 0 = error "invalid data length"
+	| otherwise                = B.concat $ decryptIter iv (makeChunks b)
+		where
+			decryptIter _   []     = []
+			decryptIter iv' (x:xs) =
+				let r = B.pack $ B.zipWith xor iv' $ coreDecrypt key x in
+				r : decryptIter x xs
+
+{- | decrypt using simple EBC mode -}
+decrypt :: Key -> B.ByteString -> B.ByteString
+decrypt key b
+	| B.length b `mod` 16 /= 0 = error "invalid data length"
+	| otherwise                = B.concat $ doChunks (coreDecrypt key) b
+
+doChunks :: (B.ByteString -> B.ByteString) -> B.ByteString -> [B.ByteString]
+doChunks f b =
+	let (x, rest) = B.splitAt 16 b in
+	if B.length rest >= 16
+		then f x : doChunks f rest
+		else [ f x ]
+
+makeChunks :: B.ByteString -> [B.ByteString]
+makeChunks = doChunks id
+
+newAESState :: IO AESState
+newAESState = newAlignedPinnedByteArray 16 16
+
+coreEncrypt :: Key -> ByteString -> ByteString
+coreEncrypt key input = B.unsafeCreate (B.length input) $ \ptr -> do
+	st <- newAESState
+	swapBlock input st
+	aesMain (getNbr key) key st
+	swapBlockInv st ptr
+
+coreDecrypt :: Key -> ByteString -> ByteString
+coreDecrypt key input = B.unsafeCreate (B.length input) $ \ptr -> do
+	st <- newAESState
+	swapBlock input st
+	aesMainInv (getNbr key) key st
+	swapBlockInv st ptr
+
+getNbr :: Key -> Int
+getNbr (Key v)
+	| V.length v == 176 = 10
+	| V.length v == 208 = 12
+	| otherwise         = 14
+
+initKey128, initKey192, initKey256 :: ByteString -> Either String Key
+
+initKey128 = initKey 16
+initKey192 = initKey 24
+initKey256 = initKey 32
+
+initKey :: Int -> ByteString -> Either String Key
+initKey sz b
+	| B.length b == sz = Right $ coreExpandKey (V.generate sz $ B.unsafeIndex b)
+	| otherwise        = Left "wrong key size"
+
+aesMain :: Int -> Key -> AESState -> IO ()
+aesMain nbr key blk = do
+	addRoundKey key 0 blk
+	forM_ [1..nbr-1] $ \i -> do
+		shiftRows blk >> mixColumns blk >> addRoundKey key i blk
+	shiftRows blk >> addRoundKey key nbr blk
+
+aesMainInv :: Int -> Key -> AESState -> IO ()
+aesMainInv nbr key blk = do
+	addRoundKey key nbr blk
+	forM_ (reverse [1..nbr-1]) $ \i -> do
+		shiftRowsInv blk >> addRoundKey key i blk >> mixColumnsInv blk
+	shiftRowsInv blk >> addRoundKey key 0 blk
+
+{-# INLINE swapIndex #-}
+swapIndex :: Int -> Int
+swapIndex 0 = 0
+swapIndex 1 = 4
+swapIndex 2 = 8
+swapIndex 3 = 12
+swapIndex 4 = 1
+swapIndex 5 = 5
+swapIndex 6 = 9
+swapIndex 7 = 13
+swapIndex 8 = 2
+swapIndex 9 = 6
+swapIndex 10 = 10
+swapIndex 11 = 14
+swapIndex 12 = 3
+swapIndex 13 = 7
+swapIndex 14 = 11
+swapIndex 15 = 15
+swapIndex _  = 0
+
+coreExpandKey :: Vector Word8 -> Key
+coreExpandKey vkey
+	| V.length vkey == 16 = Key (V.concat (ek0 : ekN16))
+	| V.length vkey == 24 = Key (V.concat (ek0 : ekN24))
+	| V.length vkey == 32 = Key (V.concat (ek0 : ekN32))
+	| otherwise           = Key (V.empty)
+	where
+		ek0 = vkey
+		ekN16 = reverse $ snd $ foldl (generateFold generate16) (ek0, []) [1..10]
+
+		ekN24 =
+			let (lk, acc) = foldl (generateFold generate24) (ek0, []) [1..7] in
+			let nk = generate16 lk 8 in
+			reverse (nk : acc)
+
+		ekN32 =
+			let (lk, acc) = foldl (generateFold generate32) (ek0, []) [1..6] in
+			let nk = generate16 lk 7 in
+			reverse (nk : acc)
+
+		generateFold gen (prevk, accK) it = let nk = gen prevk it in (nk, nk : accK)
+
+		generate16 prevk it =
+			let len = V.length prevk in
+			let v0 = cR0 it (V.unsafeIndex prevk $ len - 4)
+			                (V.unsafeIndex prevk $ len - 3)
+			                (V.unsafeIndex prevk $ len - 2)
+			                (V.unsafeIndex prevk $ len - 1) in
+			let eg0@(e0,e1,e2,e3)     = xorVector prevk 0 v0   in
+			let eg1@(e4,e5,e6,e7)     = xorVector prevk 4 eg0  in
+			let eg2@(e8,e9,e10,e11)   = xorVector prevk 8 eg1  in
+			let     (e12,e13,e14,e15) = xorVector prevk 12 eg2 in
+			V.fromList [e0,e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15]
+
+		generate24 prevk it =
+			let len = V.length prevk in
+			let v0 = cR0 it (V.unsafeIndex prevk $ len - 4)
+			                (V.unsafeIndex prevk $ len - 3)
+			                (V.unsafeIndex prevk $ len - 2)
+			                (V.unsafeIndex prevk $ len - 1) in
+			let eg0@(e0,e1,e2,e3)     = xorVector prevk 0 v0   in
+			let eg1@(e4,e5,e6,e7)     = xorVector prevk 4 eg0  in
+			let eg2@(e8,e9,e10,e11)   = xorVector prevk 8 eg1  in
+			let eg3@(e12,e13,e14,e15) = xorVector prevk 12 eg2 in
+			let eg4@(e16,e17,e18,e19) = xorVector prevk 16 eg3 in
+			let     (e20,e21,e22,e23) = xorVector prevk 20 eg4 in
+			V.fromList [e0,e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15,e16,e17,e18,e19,e20,e21,e22,e23]
+
+		generate32 prevk it =
+			let len = V.length prevk in
+			let v0 = cR0 it (V.unsafeIndex prevk $ len - 4)
+			                (V.unsafeIndex prevk $ len - 3)
+			                (V.unsafeIndex prevk $ len - 2)
+			                (V.unsafeIndex prevk $ len - 1) in
+			let eg0@(e0,e1,e2,e3)     = xorVector prevk 0 v0   in
+			let eg1@(e4,e5,e6,e7)     = xorVector prevk 4 eg0  in
+			let eg2@(e8,e9,e10,e11)   = xorVector prevk 8 eg1  in
+			let eg3@(e12,e13,e14,e15) = xorVector prevk 12 eg2 in
+			let eg4@(e16,e17,e18,e19) = xorSboxVector prevk 16 eg3 in
+			let eg5@(e20,e21,e22,e23) = xorVector prevk 20 eg4 in
+			let eg6@(e24,e25,e26,e27) = xorVector prevk 24 eg5 in
+			let     (e28,e29,e30,e31) = xorVector prevk 28 eg6 in
+			V.fromList [e0,e1,e2,e3,e4,e5,e6,e7,e8,e9,e10,e11,e12,e13,e14,e15,e16
+			           ,e17,e18,e19,e20,e21,e22,e23,e24,e25,e26,e27,e28,e29,e30,e31]
+
+		xorVector k i (t0,t1,t2,t3) =
+			( V.unsafeIndex k (i+0) `xor` t0
+			, V.unsafeIndex k (i+1) `xor` t1
+			, V.unsafeIndex k (i+2) `xor` t2
+			, V.unsafeIndex k (i+3) `xor` t3
+			)
+
+		xorSboxVector k i (t0,t1,t2,t3) =
+			( V.unsafeIndex k (i+0) `xor` sbox t0
+			, V.unsafeIndex k (i+1) `xor` sbox t1
+			, V.unsafeIndex k (i+2) `xor` sbox t2
+			, V.unsafeIndex k (i+3) `xor` sbox t3
+			)
+
+		cR0 it r0 r1 r2 r3 =
+			(sbox r1 `xor` rcon it, sbox r2, sbox r3, sbox r0)
+
+{-# INLINE shiftRows #-}
+shiftRows :: AESState -> IO ()
+shiftRows blk = do
+	r32 blk 0 >>= w32 blk 0 . msbox32
+	r32 blk 1 >>= \t1 -> w32 blk 1 $ rotateR (msbox32 t1) 8
+	r32 blk 2 >>= \t2 -> w32 blk 2 $ rotateR (msbox32 t2) 16
+	r32 blk 3 >>= \t3 -> w32 blk 3 $ rotateR (msbox32 t3) 24
+
+{-# INLINE addRoundKey #-}
+addRoundKey :: Key -> Int -> AESState -> IO ()
+addRoundKey (Key key) i blk = forM_ [0..15] $ \n -> do
+	r8 blk n >>= \v1 -> w8 blk n $ v1 `xor` V.unsafeIndex key (16 * i + swapIndex n)
+
+{-# INLINE mixColumns #-}
+mixColumns :: AESState -> IO ()
+mixColumns state = pr 0 >> pr 1 >> pr 2 >> pr 3
+	where
+		{-# INLINE pr #-}
+		pr i = do
+			cpy0 <- r8 state (0 * 4 + i)
+			cpy1 <- r8 state (1 * 4 + i)
+			cpy2 <- r8 state (2 * 4 + i)
+			cpy3 <- r8 state (3 * 4 + i)
+
+			let state0 = gm2 cpy0 `xor` gm1 cpy3 `xor` gm1 cpy2 `xor` gm3 cpy1
+			let state4 = gm2 cpy1 `xor` gm1 cpy0 `xor` gm1 cpy3 `xor` gm3 cpy2
+			let state8 = gm2 cpy2 `xor` gm1 cpy1 `xor` gm1 cpy0 `xor` gm3 cpy3
+			let state12 = gm2 cpy3 `xor` gm1 cpy2 `xor` gm1 cpy1 `xor` gm3 cpy0
+
+			w8 state (0 * 4 + i) state0
+			w8 state (1 * 4 + i) state4
+			w8 state (2 * 4 + i) state8
+			w8 state (3 * 4 + i) state12
+		{-# INLINE gm1 #-}
+		gm1 a = a
+
+{-# INLINE shiftRowsInv #-}
+shiftRowsInv :: AESState -> IO ()
+shiftRowsInv blk = do
+	r32 blk 0 >>= w32 blk 0 . mrsbox32
+	r32 blk 1 >>= \t1 -> w32 blk 1 $ mrsbox32 $ rotateL t1 8
+	r32 blk 2 >>= \t2 -> w32 blk 2 $ mrsbox32 $ rotateL t2 16
+	r32 blk 3 >>= \t3 -> w32 blk 3 $ mrsbox32 $ rotateL t3 24
+
+{-# INLINE mixColumnsInv #-}
+mixColumnsInv :: AESState -> IO ()
+mixColumnsInv state = pr 0 >> pr 1 >> pr 2 >> pr 3
+	where
+		{-# INLINE pr #-}
+		pr i = do
+			cpy0 <- r8 state (0 * 4 + i)
+			cpy1 <- r8 state (1 * 4 + i)
+			cpy2 <- r8 state (2 * 4 + i)
+			cpy3 <- r8 state (3 * 4 + i)
+
+			let state0  = gm14 cpy0 `xor` gm9 cpy3 `xor` gm13 cpy2 `xor` gm11 cpy1
+			let state4  = gm14 cpy1 `xor` gm9 cpy0 `xor` gm13 cpy3 `xor` gm11 cpy2
+			let state8  = gm14 cpy2 `xor` gm9 cpy1 `xor` gm13 cpy0 `xor` gm11 cpy3
+			let state12 = gm14 cpy3 `xor` gm9 cpy2 `xor` gm13 cpy1 `xor` gm11 cpy0
+
+			w8 state (0 * 4 + i) state0
+			w8 state (1 * 4 + i) state4
+			w8 state (2 * 4 + i) state8
+			w8 state (3 * 4 + i) state12
+
+{-# INLINE r8 #-}
+r8 :: AESState -> Int -> IO Word8
+r8 = readByteArray
+
+{-# INLINE w8 #-}
+w8 :: AESState -> Int -> Word8 -> IO ()
+w8 = writeByteArray
+
+{-# INLINE r32 #-}
+r32 :: AESState -> Int -> IO Word32
+r32 = readByteArray
+
+{-# INLINE w32 #-}
+w32 :: AESState -> Int -> Word32 -> IO ()
+w32 = writeByteArray
+
+msbox32 :: Word32 -> Word32
+msbox32 w = sbox4 a .|. sbox3 b .|. sbox2 c .|. sbox1 d
+	where
+		a = fromIntegral (w `shiftR` 24)
+		b = fromIntegral (w `shiftR` 16)
+		c = fromIntegral (w `shiftR` 8)
+		d = fromIntegral w
+
+mrsbox32 :: Word32 -> Word32
+mrsbox32 w = fromIntegral (rsbox a) `shiftL` 24 .|.
+             fromIntegral (rsbox b) `shiftL` 16 .|.
+             fromIntegral (rsbox c) `shiftL` 8  .|.
+             fromIntegral (rsbox d)
+	where
+		a = fromIntegral (w `shiftR` 24)
+		b = fromIntegral (w `shiftR` 16)
+		c = fromIntegral (w `shiftR` 8)
+		d = fromIntegral w
+
+
+{-# INLINE swapBlock #-}
+swapBlock :: ByteString -> AESState -> IO ()
+swapBlock b blk = do -- V.generate 16 (\i -> B.unsafeIndex b $ swapIndex i)
+	forM_ [0..15] $ \i -> w8 blk i $ B.unsafeIndex b $ swapIndex i
+
+{-# INLINE swapBlockInv #-}
+swapBlockInv :: AESState -> Ptr Word8 -> IO ()
+swapBlockInv blk ptr = forM_ [0..15] $ \i -> r8 blk (swapIndex i) >>= pokeByteOff ptr i
+
+{-# INLINE sbox #-}
+sbox :: Word8 -> Word8
+sbox (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x63\x7c\x77\x7b\xf2\x6b\x6f\xc5\
+		\\x30\x01\x67\x2b\xfe\xd7\xab\x76\
+		\\xca\x82\xc9\x7d\xfa\x59\x47\xf0\
+		\\xad\xd4\xa2\xaf\x9c\xa4\x72\xc0\
+		\\xb7\xfd\x93\x26\x36\x3f\xf7\xcc\
+		\\x34\xa5\xe5\xf1\x71\xd8\x31\x15\
+		\\x04\xc7\x23\xc3\x18\x96\x05\x9a\
+		\\x07\x12\x80\xe2\xeb\x27\xb2\x75\
+		\\x09\x83\x2c\x1a\x1b\x6e\x5a\xa0\
+		\\x52\x3b\xd6\xb3\x29\xe3\x2f\x84\
+		\\x53\xd1\x00\xed\x20\xfc\xb1\x5b\
+		\\x6a\xcb\xbe\x39\x4a\x4c\x58\xcf\
+		\\xd0\xef\xaa\xfb\x43\x4d\x33\x85\
+		\\x45\xf9\x02\x7f\x50\x3c\x9f\xa8\
+		\\x51\xa3\x40\x8f\x92\x9d\x38\xf5\
+		\\xbc\xb6\xda\x21\x10\xff\xf3\xd2\
+		\\xcd\x0c\x13\xec\x5f\x97\x44\x17\
+		\\xc4\xa7\x7e\x3d\x64\x5d\x19\x73\
+		\\x60\x81\x4f\xdc\x22\x2a\x90\x88\
+		\\x46\xee\xb8\x14\xde\x5e\x0b\xdb\
+		\\xe0\x32\x3a\x0a\x49\x06\x24\x5c\
+		\\xc2\xd3\xac\x62\x91\x95\xe4\x79\
+		\\xe7\xc8\x37\x6d\x8d\xd5\x4e\xa9\
+		\\x6c\x56\xf4\xea\x65\x7a\xae\x08\
+		\\xba\x78\x25\x2e\x1c\xa6\xb4\xc6\
+		\\xe8\xdd\x74\x1f\x4b\xbd\x8b\x8a\
+		\\x70\x3e\xb5\x66\x48\x03\xf6\x0e\
+		\\x61\x35\x57\xb9\x86\xc1\x1d\x9e\
+		\\xe1\xf8\x98\x11\x69\xd9\x8e\x94\
+		\\x9b\x1e\x87\xe9\xce\x55\x28\xdf\
+		\\x8c\xa1\x89\x0d\xbf\xe6\x42\x68\
+		\\x41\x99\x2d\x0f\xb0\x54\xbb\x16"#
+
+{-# INLINE sbox1 #-}
+{-# INLINE sbox2 #-}
+{-# INLINE sbox3 #-}
+{-# INLINE sbox4 #-}
+sbox1, sbox2, sbox3, sbox4 :: Word8 -> Word32
+sbox1 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
+	where !(Table table) = sbox1Tab
+sbox2 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
+	where !(Table table) = sbox2Tab
+sbox3 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
+	where !(Table table) = sbox3Tab
+sbox4 (W8# w) = W32# (indexWord32OffAddr# table (word2Int# w))
+	where !(Table table) = sbox4Tab
+
+sbox1Tab, sbox2Tab, sbox3Tab, sbox4Tab :: Table
+sbox1Tab = if getSystemEndianness == LittleEndian then sbox_x000 else sbox_000x
+sbox2Tab = if getSystemEndianness == LittleEndian then sbox_0x00 else sbox_00x0
+sbox3Tab = if getSystemEndianness == LittleEndian then sbox_00x0 else sbox_0x00
+sbox4Tab = if getSystemEndianness == LittleEndian then sbox_000x else sbox_x000
+
+data Table = Table !Addr#
+
+sbox_000x, sbox_00x0, sbox_0x00, sbox_x000 :: Table
+sbox_000x = Table
+		"\x00\x00\x00\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\
+		\\x00\x00\x00\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\
+		\\x00\x00\x00\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\
+		\\x00\x00\x00\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\
+		\\x00\x00\x00\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\
+		\\x00\x00\x00\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\
+		\\x00\x00\x00\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\
+		\\x00\x00\x00\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\
+		\\x00\x00\x00\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\
+		\\x00\x00\x00\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\
+		\\x00\x00\x00\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\
+		\\x00\x00\x00\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\
+		\\x00\x00\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\
+		\\x00\x00\x00\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\
+		\\x00\x00\x00\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\
+		\\x00\x00\x00\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\
+		\\x00\x00\x00\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\
+		\\x00\x00\x00\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\
+		\\x00\x00\x00\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\
+		\\x00\x00\x00\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\
+		\\x00\x00\x00\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\
+		\\x00\x00\x00\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\
+		\\x00\x00\x00\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\
+		\\x00\x00\x00\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\
+		\\x00\x00\x00\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\
+		\\x00\x00\x00\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\
+		\\x00\x00\x00\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\
+		\\x00\x00\x00\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\
+		\\x00\x00\x00\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\
+		\\x00\x00\x00\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\
+		\\x00\x00\x00\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\
+		\\x00\x00\x00\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\
+		\\x00\x00\x00\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\
+		\\x00\x00\x00\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\
+		\\x00\x00\x00\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\
+		\\x00\x00\x00\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\
+		\\x00\x00\x00\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\
+		\\x00\x00\x00\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\
+		\\x00\x00\x00\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\
+		\\x00\x00\x00\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\
+		\\x00\x00\x00\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\
+		\\x00\x00\x00\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\
+		\\x00\x00\x00\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\
+		\\x00\x00\x00\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\
+		\\x00\x00\x00\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\
+		\\x00\x00\x00\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\
+		\\x00\x00\x00\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\
+		\\x00\x00\x00\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\
+		\\x00\x00\x00\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\
+		\\x00\x00\x00\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\
+		\\x00\x00\x00\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\
+		\\x00\x00\x00\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\
+		\\x00\x00\x00\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\
+		\\x00\x00\x00\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\
+		\\x00\x00\x00\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\
+		\\x00\x00\x00\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\
+		\\x00\x00\x00\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\
+		\\x00\x00\x00\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\
+		\\x00\x00\x00\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\
+		\\x00\x00\x00\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\
+		\\x00\x00\x00\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\
+		\\x00\x00\x00\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\
+		\\x00\x00\x00\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\
+		\\x00\x00\x00\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16"#
+
+sbox_00x0 = Table
+		"\x00\x00\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\x00\
+		\\x00\x00\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\x00\
+		\\x00\x00\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\x00\
+		\\x00\x00\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\x00\
+		\\x00\x00\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\x00\
+		\\x00\x00\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\x00\
+		\\x00\x00\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\x00\
+		\\x00\x00\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\x00\
+		\\x00\x00\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\x00\
+		\\x00\x00\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\x00\
+		\\x00\x00\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\x00\
+		\\x00\x00\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\x00\
+		\\x00\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\x00\
+		\\x00\x00\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\x00\
+		\\x00\x00\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\x00\
+		\\x00\x00\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\x00\
+		\\x00\x00\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\x00\
+		\\x00\x00\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\x00\
+		\\x00\x00\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\x00\
+		\\x00\x00\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\x00\
+		\\x00\x00\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\x00\
+		\\x00\x00\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\x00\
+		\\x00\x00\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\x00\
+		\\x00\x00\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\x00\
+		\\x00\x00\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\x00\
+		\\x00\x00\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\x00\
+		\\x00\x00\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\x00\
+		\\x00\x00\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\x00\
+		\\x00\x00\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\x00\
+		\\x00\x00\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\x00\
+		\\x00\x00\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\x00\
+		\\x00\x00\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\x00\
+		\\x00\x00\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\x00\
+		\\x00\x00\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\x00\
+		\\x00\x00\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\x00\
+		\\x00\x00\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\x00\
+		\\x00\x00\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\x00\
+		\\x00\x00\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\x00\
+		\\x00\x00\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\x00\
+		\\x00\x00\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\x00\
+		\\x00\x00\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\x00\
+		\\x00\x00\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\x00\
+		\\x00\x00\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\x00\
+		\\x00\x00\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\x00\
+		\\x00\x00\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\x00\
+		\\x00\x00\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\x00\
+		\\x00\x00\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\x00\
+		\\x00\x00\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\x00\
+		\\x00\x00\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\x00\
+		\\x00\x00\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\x00\
+		\\x00\x00\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\x00\
+		\\x00\x00\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\x00\
+		\\x00\x00\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\x00\
+		\\x00\x00\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\x00\
+		\\x00\x00\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\x00\
+		\\x00\x00\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\x00\
+		\\x00\x00\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\x00\
+		\\x00\x00\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\x00\
+		\\x00\x00\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\x00\
+		\\x00\x00\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\x00\
+		\\x00\x00\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\x00\
+		\\x00\x00\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\x00\
+		\\x00\x00\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\x00\
+		\\x00\x00\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16\x00"#
+
+sbox_0x00 = Table
+		"\x00\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\x00\x00\
+		\\x00\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\x00\x00\
+		\\x00\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\x00\x00\
+		\\x00\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\x00\x00\
+		\\x00\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\x00\x00\
+		\\x00\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\x00\x00\
+		\\x00\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\x00\x00\
+		\\x00\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\x00\x00\
+		\\x00\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\x00\x00\
+		\\x00\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\x00\x00\
+		\\x00\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\x00\x00\
+		\\x00\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\x00\x00\
+		\\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\x00\x00\
+		\\x00\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\x00\x00\
+		\\x00\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\x00\x00\
+		\\x00\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\x00\x00\
+		\\x00\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\x00\x00\
+		\\x00\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\x00\x00\
+		\\x00\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\x00\x00\
+		\\x00\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\x00\x00\
+		\\x00\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\x00\x00\
+		\\x00\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\x00\x00\
+		\\x00\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\x00\x00\
+		\\x00\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\x00\x00\
+		\\x00\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\x00\x00\
+		\\x00\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\x00\x00\
+		\\x00\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\x00\x00\
+		\\x00\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\x00\x00\
+		\\x00\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\x00\x00\
+		\\x00\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\x00\x00\
+		\\x00\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\x00\x00\
+		\\x00\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\x00\x00\
+		\\x00\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\x00\x00\
+		\\x00\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\x00\x00\
+		\\x00\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\x00\x00\
+		\\x00\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\x00\x00\
+		\\x00\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\x00\x00\
+		\\x00\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\x00\x00\
+		\\x00\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\x00\x00\
+		\\x00\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\x00\x00\
+		\\x00\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\x00\x00\
+		\\x00\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\x00\x00\
+		\\x00\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\x00\x00\
+		\\x00\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\x00\x00\
+		\\x00\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\x00\x00\
+		\\x00\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\x00\x00\
+		\\x00\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\x00\x00\
+		\\x00\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\x00\x00\
+		\\x00\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\x00\x00\
+		\\x00\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\x00\x00\
+		\\x00\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\x00\x00\
+		\\x00\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\x00\x00\
+		\\x00\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\x00\x00\
+		\\x00\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\x00\x00\
+		\\x00\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\x00\x00\
+		\\x00\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\x00\x00\
+		\\x00\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\x00\x00\
+		\\x00\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\x00\x00\
+		\\x00\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\x00\x00\
+		\\x00\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\x00\x00\
+		\\x00\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\x00\x00\
+		\\x00\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\x00\x00\
+		\\x00\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\x00\x00\
+		\\x00\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16\x00\x00"#
+
+sbox_x000 = Table
+		"\x63\x00\x00\x00\x7c\x00\x00\x00\x77\x00\x00\x00\x7b\x00\x00\x00\
+		\\xf2\x00\x00\x00\x6b\x00\x00\x00\x6f\x00\x00\x00\xc5\x00\x00\x00\
+		\\x30\x00\x00\x00\x01\x00\x00\x00\x67\x00\x00\x00\x2b\x00\x00\x00\
+		\\xfe\x00\x00\x00\xd7\x00\x00\x00\xab\x00\x00\x00\x76\x00\x00\x00\
+		\\xca\x00\x00\x00\x82\x00\x00\x00\xc9\x00\x00\x00\x7d\x00\x00\x00\
+		\\xfa\x00\x00\x00\x59\x00\x00\x00\x47\x00\x00\x00\xf0\x00\x00\x00\
+		\\xad\x00\x00\x00\xd4\x00\x00\x00\xa2\x00\x00\x00\xaf\x00\x00\x00\
+		\\x9c\x00\x00\x00\xa4\x00\x00\x00\x72\x00\x00\x00\xc0\x00\x00\x00\
+		\\xb7\x00\x00\x00\xfd\x00\x00\x00\x93\x00\x00\x00\x26\x00\x00\x00\
+		\\x36\x00\x00\x00\x3f\x00\x00\x00\xf7\x00\x00\x00\xcc\x00\x00\x00\
+		\\x34\x00\x00\x00\xa5\x00\x00\x00\xe5\x00\x00\x00\xf1\x00\x00\x00\
+		\\x71\x00\x00\x00\xd8\x00\x00\x00\x31\x00\x00\x00\x15\x00\x00\x00\
+		\\x04\x00\x00\x00\xc7\x00\x00\x00\x23\x00\x00\x00\xc3\x00\x00\x00\
+		\\x18\x00\x00\x00\x96\x00\x00\x00\x05\x00\x00\x00\x9a\x00\x00\x00\
+		\\x07\x00\x00\x00\x12\x00\x00\x00\x80\x00\x00\x00\xe2\x00\x00\x00\
+		\\xeb\x00\x00\x00\x27\x00\x00\x00\xb2\x00\x00\x00\x75\x00\x00\x00\
+		\\x09\x00\x00\x00\x83\x00\x00\x00\x2c\x00\x00\x00\x1a\x00\x00\x00\
+		\\x1b\x00\x00\x00\x6e\x00\x00\x00\x5a\x00\x00\x00\xa0\x00\x00\x00\
+		\\x52\x00\x00\x00\x3b\x00\x00\x00\xd6\x00\x00\x00\xb3\x00\x00\x00\
+		\\x29\x00\x00\x00\xe3\x00\x00\x00\x2f\x00\x00\x00\x84\x00\x00\x00\
+		\\x53\x00\x00\x00\xd1\x00\x00\x00\x00\x00\x00\x00\xed\x00\x00\x00\
+		\\x20\x00\x00\x00\xfc\x00\x00\x00\xb1\x00\x00\x00\x5b\x00\x00\x00\
+		\\x6a\x00\x00\x00\xcb\x00\x00\x00\xbe\x00\x00\x00\x39\x00\x00\x00\
+		\\x4a\x00\x00\x00\x4c\x00\x00\x00\x58\x00\x00\x00\xcf\x00\x00\x00\
+		\\xd0\x00\x00\x00\xef\x00\x00\x00\xaa\x00\x00\x00\xfb\x00\x00\x00\
+		\\x43\x00\x00\x00\x4d\x00\x00\x00\x33\x00\x00\x00\x85\x00\x00\x00\
+		\\x45\x00\x00\x00\xf9\x00\x00\x00\x02\x00\x00\x00\x7f\x00\x00\x00\
+		\\x50\x00\x00\x00\x3c\x00\x00\x00\x9f\x00\x00\x00\xa8\x00\x00\x00\
+		\\x51\x00\x00\x00\xa3\x00\x00\x00\x40\x00\x00\x00\x8f\x00\x00\x00\
+		\\x92\x00\x00\x00\x9d\x00\x00\x00\x38\x00\x00\x00\xf5\x00\x00\x00\
+		\\xbc\x00\x00\x00\xb6\x00\x00\x00\xda\x00\x00\x00\x21\x00\x00\x00\
+		\\x10\x00\x00\x00\xff\x00\x00\x00\xf3\x00\x00\x00\xd2\x00\x00\x00\
+		\\xcd\x00\x00\x00\x0c\x00\x00\x00\x13\x00\x00\x00\xec\x00\x00\x00\
+		\\x5f\x00\x00\x00\x97\x00\x00\x00\x44\x00\x00\x00\x17\x00\x00\x00\
+		\\xc4\x00\x00\x00\xa7\x00\x00\x00\x7e\x00\x00\x00\x3d\x00\x00\x00\
+		\\x64\x00\x00\x00\x5d\x00\x00\x00\x19\x00\x00\x00\x73\x00\x00\x00\
+		\\x60\x00\x00\x00\x81\x00\x00\x00\x4f\x00\x00\x00\xdc\x00\x00\x00\
+		\\x22\x00\x00\x00\x2a\x00\x00\x00\x90\x00\x00\x00\x88\x00\x00\x00\
+		\\x46\x00\x00\x00\xee\x00\x00\x00\xb8\x00\x00\x00\x14\x00\x00\x00\
+		\\xde\x00\x00\x00\x5e\x00\x00\x00\x0b\x00\x00\x00\xdb\x00\x00\x00\
+		\\xe0\x00\x00\x00\x32\x00\x00\x00\x3a\x00\x00\x00\x0a\x00\x00\x00\
+		\\x49\x00\x00\x00\x06\x00\x00\x00\x24\x00\x00\x00\x5c\x00\x00\x00\
+		\\xc2\x00\x00\x00\xd3\x00\x00\x00\xac\x00\x00\x00\x62\x00\x00\x00\
+		\\x91\x00\x00\x00\x95\x00\x00\x00\xe4\x00\x00\x00\x79\x00\x00\x00\
+		\\xe7\x00\x00\x00\xc8\x00\x00\x00\x37\x00\x00\x00\x6d\x00\x00\x00\
+		\\x8d\x00\x00\x00\xd5\x00\x00\x00\x4e\x00\x00\x00\xa9\x00\x00\x00\
+		\\x6c\x00\x00\x00\x56\x00\x00\x00\xf4\x00\x00\x00\xea\x00\x00\x00\
+		\\x65\x00\x00\x00\x7a\x00\x00\x00\xae\x00\x00\x00\x08\x00\x00\x00\
+		\\xba\x00\x00\x00\x78\x00\x00\x00\x25\x00\x00\x00\x2e\x00\x00\x00\
+		\\x1c\x00\x00\x00\xa6\x00\x00\x00\xb4\x00\x00\x00\xc6\x00\x00\x00\
+		\\xe8\x00\x00\x00\xdd\x00\x00\x00\x74\x00\x00\x00\x1f\x00\x00\x00\
+		\\x4b\x00\x00\x00\xbd\x00\x00\x00\x8b\x00\x00\x00\x8a\x00\x00\x00\
+		\\x70\x00\x00\x00\x3e\x00\x00\x00\xb5\x00\x00\x00\x66\x00\x00\x00\
+		\\x48\x00\x00\x00\x03\x00\x00\x00\xf6\x00\x00\x00\x0e\x00\x00\x00\
+		\\x61\x00\x00\x00\x35\x00\x00\x00\x57\x00\x00\x00\xb9\x00\x00\x00\
+		\\x86\x00\x00\x00\xc1\x00\x00\x00\x1d\x00\x00\x00\x9e\x00\x00\x00\
+		\\xe1\x00\x00\x00\xf8\x00\x00\x00\x98\x00\x00\x00\x11\x00\x00\x00\
+		\\x69\x00\x00\x00\xd9\x00\x00\x00\x8e\x00\x00\x00\x94\x00\x00\x00\
+		\\x9b\x00\x00\x00\x1e\x00\x00\x00\x87\x00\x00\x00\xe9\x00\x00\x00\
+		\\xce\x00\x00\x00\x55\x00\x00\x00\x28\x00\x00\x00\xdf\x00\x00\x00\
+		\\x8c\x00\x00\x00\xa1\x00\x00\x00\x89\x00\x00\x00\x0d\x00\x00\x00\
+		\\xbf\x00\x00\x00\xe6\x00\x00\x00\x42\x00\x00\x00\x68\x00\x00\x00\
+		\\x41\x00\x00\x00\x99\x00\x00\x00\x2d\x00\x00\x00\x0f\x00\x00\x00\
+		\\xb0\x00\x00\x00\x54\x00\x00\x00\xbb\x00\x00\x00\x16\x00\x00\x00"#
+
+
+{-# INLINE rsbox #-}
+rsbox :: Word8 -> Word8
+rsbox (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x52\x09\x6a\xd5\x30\x36\xa5\x38\
+		\\xbf\x40\xa3\x9e\x81\xf3\xd7\xfb\
+		\\x7c\xe3\x39\x82\x9b\x2f\xff\x87\
+		\\x34\x8e\x43\x44\xc4\xde\xe9\xcb\
+		\\x54\x7b\x94\x32\xa6\xc2\x23\x3d\
+		\\xee\x4c\x95\x0b\x42\xfa\xc3\x4e\
+		\\x08\x2e\xa1\x66\x28\xd9\x24\xb2\
+		\\x76\x5b\xa2\x49\x6d\x8b\xd1\x25\
+		\\x72\xf8\xf6\x64\x86\x68\x98\x16\
+		\\xd4\xa4\x5c\xcc\x5d\x65\xb6\x92\
+		\\x6c\x70\x48\x50\xfd\xed\xb9\xda\
+		\\x5e\x15\x46\x57\xa7\x8d\x9d\x84\
+		\\x90\xd8\xab\x00\x8c\xbc\xd3\x0a\
+		\\xf7\xe4\x58\x05\xb8\xb3\x45\x06\
+		\\xd0\x2c\x1e\x8f\xca\x3f\x0f\x02\
+		\\xc1\xaf\xbd\x03\x01\x13\x8a\x6b\
+		\\x3a\x91\x11\x41\x4f\x67\xdc\xea\
+		\\x97\xf2\xcf\xce\xf0\xb4\xe6\x73\
+		\\x96\xac\x74\x22\xe7\xad\x35\x85\
+		\\xe2\xf9\x37\xe8\x1c\x75\xdf\x6e\
+		\\x47\xf1\x1a\x71\x1d\x29\xc5\x89\
+		\\x6f\xb7\x62\x0e\xaa\x18\xbe\x1b\
+		\\xfc\x56\x3e\x4b\xc6\xd2\x79\x20\
+		\\x9a\xdb\xc0\xfe\x78\xcd\x5a\xf4\
+		\\x1f\xdd\xa8\x33\x88\x07\xc7\x31\
+		\\xb1\x12\x10\x59\x27\x80\xec\x5f\
+		\\x60\x51\x7f\xa9\x19\xb5\x4a\x0d\
+		\\x2d\xe5\x7a\x9f\x93\xc9\x9c\xef\
+		\\xa0\xe0\x3b\x4d\xae\x2a\xf5\xb0\
+		\\xc8\xeb\xbb\x3c\x83\x53\x99\x61\
+		\\x17\x2b\x04\x7e\xba\x77\xd6\x26\
+		\\xe1\x69\x14\x63\x55\x21\x0c\x7d"#
+
+{-# INLINE rcon #-}
+rcon :: Int -> Word8
+rcon (I# i) = W8# (indexWord8OffAddr# table (i `remInt#` 51#))
+	where !table =
+		"\x8d\x01\x02\x04\x08\x10\x20\x40\
+		\\x80\x1b\x36\x6c\xd8\xab\x4d\x9a\
+		\\x2f\x5e\xbc\x63\xc6\x97\x35\x6a\
+		\\xd4\xb3\x7d\xfa\xef\xc5\x91\x39\
+		\\x72\xe4\xd3\xbd\x61\xc2\x9f\x25\
+		\\x4a\x94\x33\x66\xcc\x83\x1d\x3a\
+		\\x74\xe8\xcb"#
+
+{-# INLINE gm2 #-}
+{-# INLINE gm3 #-}
+{-# INLINE gm9 #-}
+{-# INLINE gm11 #-}
+{-# INLINE gm13 #-}
+{-# INLINE gm14 #-}
+gm2, gm3, gm9, gm11, gm13, gm14 :: Word8 -> Word8
+gm2 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x00\x02\x04\x06\x08\x0a\x0c\x0e\
+		\\x10\x12\x14\x16\x18\x1a\x1c\x1e\
+		\\x20\x22\x24\x26\x28\x2a\x2c\x2e\
+		\\x30\x32\x34\x36\x38\x3a\x3c\x3e\
+		\\x40\x42\x44\x46\x48\x4a\x4c\x4e\
+		\\x50\x52\x54\x56\x58\x5a\x5c\x5e\
+		\\x60\x62\x64\x66\x68\x6a\x6c\x6e\
+		\\x70\x72\x74\x76\x78\x7a\x7c\x7e\
+		\\x80\x82\x84\x86\x88\x8a\x8c\x8e\
+		\\x90\x92\x94\x96\x98\x9a\x9c\x9e\
+		\\xa0\xa2\xa4\xa6\xa8\xaa\xac\xae\
+		\\xb0\xb2\xb4\xb6\xb8\xba\xbc\xbe\
+		\\xc0\xc2\xc4\xc6\xc8\xca\xcc\xce\
+		\\xd0\xd2\xd4\xd6\xd8\xda\xdc\xde\
+		\\xe0\xe2\xe4\xe6\xe8\xea\xec\xee\
+		\\xf0\xf2\xf4\xf6\xf8\xfa\xfc\xfe\
+		\\x1b\x19\x1f\x1d\x13\x11\x17\x15\
+		\\x0b\x09\x0f\x0d\x03\x01\x07\x05\
+		\\x3b\x39\x3f\x3d\x33\x31\x37\x35\
+		\\x2b\x29\x2f\x2d\x23\x21\x27\x25\
+		\\x5b\x59\x5f\x5d\x53\x51\x57\x55\
+		\\x4b\x49\x4f\x4d\x43\x41\x47\x45\
+		\\x7b\x79\x7f\x7d\x73\x71\x77\x75\
+		\\x6b\x69\x6f\x6d\x63\x61\x67\x65\
+		\\x9b\x99\x9f\x9d\x93\x91\x97\x95\
+		\\x8b\x89\x8f\x8d\x83\x81\x87\x85\
+		\\xbb\xb9\xbf\xbd\xb3\xb1\xb7\xb5\
+		\\xab\xa9\xaf\xad\xa3\xa1\xa7\xa5\
+		\\xdb\xd9\xdf\xdd\xd3\xd1\xd7\xd5\
+		\\xcb\xc9\xcf\xcd\xc3\xc1\xc7\xc5\
+		\\xfb\xf9\xff\xfd\xf3\xf1\xf7\xf5\
+		\\xeb\xe9\xef\xed\xe3\xe1\xe7\xe5"#
+
+gm3 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x00\x03\x06\x05\x0c\x0f\x0a\x09\
+		\\x18\x1b\x1e\x1d\x14\x17\x12\x11\
+		\\x30\x33\x36\x35\x3c\x3f\x3a\x39\
+		\\x28\x2b\x2e\x2d\x24\x27\x22\x21\
+		\\x60\x63\x66\x65\x6c\x6f\x6a\x69\
+		\\x78\x7b\x7e\x7d\x74\x77\x72\x71\
+		\\x50\x53\x56\x55\x5c\x5f\x5a\x59\
+		\\x48\x4b\x4e\x4d\x44\x47\x42\x41\
+		\\xc0\xc3\xc6\xc5\xcc\xcf\xca\xc9\
+		\\xd8\xdb\xde\xdd\xd4\xd7\xd2\xd1\
+		\\xf0\xf3\xf6\xf5\xfc\xff\xfa\xf9\
+		\\xe8\xeb\xee\xed\xe4\xe7\xe2\xe1\
+		\\xa0\xa3\xa6\xa5\xac\xaf\xaa\xa9\
+		\\xb8\xbb\xbe\xbd\xb4\xb7\xb2\xb1\
+		\\x90\x93\x96\x95\x9c\x9f\x9a\x99\
+		\\x88\x8b\x8e\x8d\x84\x87\x82\x81\
+		\\x9b\x98\x9d\x9e\x97\x94\x91\x92\
+		\\x83\x80\x85\x86\x8f\x8c\x89\x8a\
+		\\xab\xa8\xad\xae\xa7\xa4\xa1\xa2\
+		\\xb3\xb0\xb5\xb6\xbf\xbc\xb9\xba\
+		\\xfb\xf8\xfd\xfe\xf7\xf4\xf1\xf2\
+		\\xe3\xe0\xe5\xe6\xef\xec\xe9\xea\
+		\\xcb\xc8\xcd\xce\xc7\xc4\xc1\xc2\
+		\\xd3\xd0\xd5\xd6\xdf\xdc\xd9\xda\
+		\\x5b\x58\x5d\x5e\x57\x54\x51\x52\
+		\\x43\x40\x45\x46\x4f\x4c\x49\x4a\
+		\\x6b\x68\x6d\x6e\x67\x64\x61\x62\
+		\\x73\x70\x75\x76\x7f\x7c\x79\x7a\
+		\\x3b\x38\x3d\x3e\x37\x34\x31\x32\
+		\\x23\x20\x25\x26\x2f\x2c\x29\x2a\
+		\\x0b\x08\x0d\x0e\x07\x04\x01\x02\
+		\\x13\x10\x15\x16\x1f\x1c\x19\x1a"#
+
+gm9 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x00\x09\x12\x1b\x24\x2d\x36\x3f\
+		\\x48\x41\x5a\x53\x6c\x65\x7e\x77\
+		\\x90\x99\x82\x8b\xb4\xbd\xa6\xaf\
+		\\xd8\xd1\xca\xc3\xfc\xf5\xee\xe7\
+		\\x3b\x32\x29\x20\x1f\x16\x0d\x04\
+		\\x73\x7a\x61\x68\x57\x5e\x45\x4c\
+		\\xab\xa2\xb9\xb0\x8f\x86\x9d\x94\
+		\\xe3\xea\xf1\xf8\xc7\xce\xd5\xdc\
+		\\x76\x7f\x64\x6d\x52\x5b\x40\x49\
+		\\x3e\x37\x2c\x25\x1a\x13\x08\x01\
+		\\xe6\xef\xf4\xfd\xc2\xcb\xd0\xd9\
+		\\xae\xa7\xbc\xb5\x8a\x83\x98\x91\
+		\\x4d\x44\x5f\x56\x69\x60\x7b\x72\
+		\\x05\x0c\x17\x1e\x21\x28\x33\x3a\
+		\\xdd\xd4\xcf\xc6\xf9\xf0\xeb\xe2\
+		\\x95\x9c\x87\x8e\xb1\xb8\xa3\xaa\
+		\\xec\xe5\xfe\xf7\xc8\xc1\xda\xd3\
+		\\xa4\xad\xb6\xbf\x80\x89\x92\x9b\
+		\\x7c\x75\x6e\x67\x58\x51\x4a\x43\
+		\\x34\x3d\x26\x2f\x10\x19\x02\x0b\
+		\\xd7\xde\xc5\xcc\xf3\xfa\xe1\xe8\
+		\\x9f\x96\x8d\x84\xbb\xb2\xa9\xa0\
+		\\x47\x4e\x55\x5c\x63\x6a\x71\x78\
+		\\x0f\x06\x1d\x14\x2b\x22\x39\x30\
+		\\x9a\x93\x88\x81\xbe\xb7\xac\xa5\
+		\\xd2\xdb\xc0\xc9\xf6\xff\xe4\xed\
+		\\x0a\x03\x18\x11\x2e\x27\x3c\x35\
+		\\x42\x4b\x50\x59\x66\x6f\x74\x7d\
+		\\xa1\xa8\xb3\xba\x85\x8c\x97\x9e\
+		\\xe9\xe0\xfb\xf2\xcd\xc4\xdf\xd6\
+		\\x31\x38\x23\x2a\x15\x1c\x07\x0e\
+		\\x79\x70\x6b\x62\x5d\x54\x4f\x46"#
+
+gm11 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x00\x0b\x16\x1d\x2c\x27\x3a\x31\
+		\\x58\x53\x4e\x45\x74\x7f\x62\x69\
+		\\xb0\xbb\xa6\xad\x9c\x97\x8a\x81\
+		\\xe8\xe3\xfe\xf5\xc4\xcf\xd2\xd9\
+		\\x7b\x70\x6d\x66\x57\x5c\x41\x4a\
+		\\x23\x28\x35\x3e\x0f\x04\x19\x12\
+		\\xcb\xc0\xdd\xd6\xe7\xec\xf1\xfa\
+		\\x93\x98\x85\x8e\xbf\xb4\xa9\xa2\
+		\\xf6\xfd\xe0\xeb\xda\xd1\xcc\xc7\
+		\\xae\xa5\xb8\xb3\x82\x89\x94\x9f\
+		\\x46\x4d\x50\x5b\x6a\x61\x7c\x77\
+		\\x1e\x15\x08\x03\x32\x39\x24\x2f\
+		\\x8d\x86\x9b\x90\xa1\xaa\xb7\xbc\
+		\\xd5\xde\xc3\xc8\xf9\xf2\xef\xe4\
+		\\x3d\x36\x2b\x20\x11\x1a\x07\x0c\
+		\\x65\x6e\x73\x78\x49\x42\x5f\x54\
+		\\xf7\xfc\xe1\xea\xdb\xd0\xcd\xc6\
+		\\xaf\xa4\xb9\xb2\x83\x88\x95\x9e\
+		\\x47\x4c\x51\x5a\x6b\x60\x7d\x76\
+		\\x1f\x14\x09\x02\x33\x38\x25\x2e\
+		\\x8c\x87\x9a\x91\xa0\xab\xb6\xbd\
+		\\xd4\xdf\xc2\xc9\xf8\xf3\xee\xe5\
+		\\x3c\x37\x2a\x21\x10\x1b\x06\x0d\
+		\\x64\x6f\x72\x79\x48\x43\x5e\x55\
+		\\x01\x0a\x17\x1c\x2d\x26\x3b\x30\
+		\\x59\x52\x4f\x44\x75\x7e\x63\x68\
+		\\xb1\xba\xa7\xac\x9d\x96\x8b\x80\
+		\\xe9\xe2\xff\xf4\xc5\xce\xd3\xd8\
+		\\x7a\x71\x6c\x67\x56\x5d\x40\x4b\
+		\\x22\x29\x34\x3f\x0e\x05\x18\x13\
+		\\xca\xc1\xdc\xd7\xe6\xed\xf0\xfb\
+		\\x92\x99\x84\x8f\xbe\xb5\xa8\xa3"#
+
+gm13 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x00\x0d\x1a\x17\x34\x39\x2e\x23\
+		\\x68\x65\x72\x7f\x5c\x51\x46\x4b\
+		\\xd0\xdd\xca\xc7\xe4\xe9\xfe\xf3\
+		\\xb8\xb5\xa2\xaf\x8c\x81\x96\x9b\
+		\\xbb\xb6\xa1\xac\x8f\x82\x95\x98\
+		\\xd3\xde\xc9\xc4\xe7\xea\xfd\xf0\
+		\\x6b\x66\x71\x7c\x5f\x52\x45\x48\
+		\\x03\x0e\x19\x14\x37\x3a\x2d\x20\
+		\\x6d\x60\x77\x7a\x59\x54\x43\x4e\
+		\\x05\x08\x1f\x12\x31\x3c\x2b\x26\
+		\\xbd\xb0\xa7\xaa\x89\x84\x93\x9e\
+		\\xd5\xd8\xcf\xc2\xe1\xec\xfb\xf6\
+		\\xd6\xdb\xcc\xc1\xe2\xef\xf8\xf5\
+		\\xbe\xb3\xa4\xa9\x8a\x87\x90\x9d\
+		\\x06\x0b\x1c\x11\x32\x3f\x28\x25\
+		\\x6e\x63\x74\x79\x5a\x57\x40\x4d\
+		\\xda\xd7\xc0\xcd\xee\xe3\xf4\xf9\
+		\\xb2\xbf\xa8\xa5\x86\x8b\x9c\x91\
+		\\x0a\x07\x10\x1d\x3e\x33\x24\x29\
+		\\x62\x6f\x78\x75\x56\x5b\x4c\x41\
+		\\x61\x6c\x7b\x76\x55\x58\x4f\x42\
+		\\x09\x04\x13\x1e\x3d\x30\x27\x2a\
+		\\xb1\xbc\xab\xa6\x85\x88\x9f\x92\
+		\\xd9\xd4\xc3\xce\xed\xe0\xf7\xfa\
+		\\xb7\xba\xad\xa0\x83\x8e\x99\x94\
+		\\xdf\xd2\xc5\xc8\xeb\xe6\xf1\xfc\
+		\\x67\x6a\x7d\x70\x53\x5e\x49\x44\
+		\\x0f\x02\x15\x18\x3b\x36\x21\x2c\
+		\\x0c\x01\x16\x1b\x38\x35\x22\x2f\
+		\\x64\x69\x7e\x73\x50\x5d\x4a\x47\
+		\\xdc\xd1\xc6\xcb\xe8\xe5\xf2\xff\
+		\\xb4\xb9\xae\xa3\x80\x8d\x9a\x97"#
+
+gm14 (W8# w) = W8# (indexWord8OffAddr# table (word2Int# w))
+	where !table =
+		"\x00\x0e\x1c\x12\x38\x36\x24\x2a\
+		\\x70\x7e\x6c\x62\x48\x46\x54\x5a\
+		\\xe0\xee\xfc\xf2\xd8\xd6\xc4\xca\
+		\\x90\x9e\x8c\x82\xa8\xa6\xb4\xba\
+		\\xdb\xd5\xc7\xc9\xe3\xed\xff\xf1\
+		\\xab\xa5\xb7\xb9\x93\x9d\x8f\x81\
+		\\x3b\x35\x27\x29\x03\x0d\x1f\x11\
+		\\x4b\x45\x57\x59\x73\x7d\x6f\x61\
+		\\xad\xa3\xb1\xbf\x95\x9b\x89\x87\
+		\\xdd\xd3\xc1\xcf\xe5\xeb\xf9\xf7\
+		\\x4d\x43\x51\x5f\x75\x7b\x69\x67\
+		\\x3d\x33\x21\x2f\x05\x0b\x19\x17\
+		\\x76\x78\x6a\x64\x4e\x40\x52\x5c\
+		\\x06\x08\x1a\x14\x3e\x30\x22\x2c\
+		\\x96\x98\x8a\x84\xae\xa0\xb2\xbc\
+		\\xe6\xe8\xfa\xf4\xde\xd0\xc2\xcc\
+		\\x41\x4f\x5d\x53\x79\x77\x65\x6b\
+		\\x31\x3f\x2d\x23\x09\x07\x15\x1b\
+		\\xa1\xaf\xbd\xb3\x99\x97\x85\x8b\
+		\\xd1\xdf\xcd\xc3\xe9\xe7\xf5\xfb\
+		\\x9a\x94\x86\x88\xa2\xac\xbe\xb0\
+		\\xea\xe4\xf6\xf8\xd2\xdc\xce\xc0\
+		\\x7a\x74\x66\x68\x42\x4c\x5e\x50\
+		\\x0a\x04\x16\x18\x32\x3c\x2e\x20\
+		\\xec\xe2\xf0\xfe\xd4\xda\xc8\xc6\
+		\\x9c\x92\x80\x8e\xa4\xaa\xb8\xb6\
+		\\x0c\x02\x10\x1e\x34\x3a\x28\x26\
+		\\x7c\x72\x60\x6e\x44\x4a\x58\x56\
+		\\x37\x39\x2b\x25\x0f\x01\x13\x1d\
+		\\x47\x49\x5b\x55\x7f\x71\x63\x6d\
+		\\xd7\xd9\xcb\xc5\xef\xe1\xf3\xfd\
+		\\xa7\xa9\xbb\xb5\x9f\x91\x83\x8d"#
diff --git a/Crypto/Cipher/AES/X86NI.hs b/Crypto/Cipher/AES/X86NI.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/AES/X86NI.hs
@@ -0,0 +1,80 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Crypto.Cipher.AES.X86NI
+    ( encrypt
+    , decrypt
+    , encryptCBC
+    , decryptCBC
+    , initKey128
+    ) where
+
+import Foreign.Storable
+import Foreign.Marshal.Alloc (alloca, allocaBytes)
+import Foreign.Ptr
+import Foreign.C.String
+import Foreign.C.Types
+import Data.ByteString (ByteString)
+import Data.ByteString.Unsafe (unsafeUseAsCString)
+import Data.ByteString.Internal (create, memcpy)
+import Data.Bits (shiftR)
+import qualified Data.ByteString as B
+
+import System.IO.Unsafe
+
+type IV = ByteString
+newtype Key = Key ByteString
+
+instance Storable Key where
+    sizeOf _         = 16 * 11 * 2
+    alignment _      = 16
+    poke ptr (Key b) = unsafeUseAsCString b (\cs -> memcpy (castPtr ptr) (castPtr cs) (16 * 11 * 2))
+    peek ptr         = Key `fmap` create (16*11*2) (\bptr -> memcpy bptr (castPtr ptr) (16 * 11 * 2))
+
+foreign import ccall safe "aes.h aes_generate_key128"
+        c_aes_generate_key128 :: Ptr Key -> CString -> IO ()
+
+foreign import ccall safe "aes.h aes_encrypt"
+        c_aes_encrypt :: CString -> Ptr Key -> CString -> CUInt -> IO ()
+
+foreign import ccall safe "aes.h aes_decrypt"
+        c_aes_decrypt :: CString -> Ptr Key -> CString -> CUInt -> IO ()
+
+foreign import ccall safe "aes.h aes_encrypt_cbc"
+        c_aes_encrypt_cbc :: CString -> Ptr Key -> CString -> CString -> CUInt -> IO ()
+
+foreign import ccall safe "aes.h aes_decrypt_cbc"
+        c_aes_decrypt_cbc :: CString -> Ptr Key -> CString -> CString -> CUInt -> IO ()
+
+withKey :: Key -> (Ptr Key -> IO a) -> IO a
+withKey k f = alloca (\ikey -> poke ikey k >> f ikey)
+
+{-# NOINLINE initKey128 #-}
+initKey128 :: ByteString -> Key
+initKey128 b = unsafePerformIO $ unsafeUseAsCString b (\ikey ->
+        alloca (\key -> c_aes_generate_key128 key ikey >> peek key))
+
+{-# NOINLINE encrypt #-}
+encrypt :: Key -> ByteString -> ByteString
+encrypt key input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
+    withKey key $ \k -> c_aes_encrypt o k i blocks
+
+{-# NOINLINE decrypt #-}
+decrypt :: Key -> ByteString -> ByteString
+decrypt key input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
+    withKey key $ \k -> c_aes_decrypt o k i blocks
+
+{-# NOINLINE encryptCBC #-}
+encryptCBC :: Key -> IV -> ByteString -> ByteString
+encryptCBC key iv input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
+    withKey key $ \k -> unsafeUseAsCString iv $ \ivptr -> c_aes_encrypt_cbc o k ivptr i blocks
+
+{-# NOINLINE decryptCBC #-}
+decryptCBC :: Key -> IV -> ByteString -> ByteString
+decryptCBC key iv input = unsafePerformIO $ allocateAndMapBlocks input $ \blocks o i ->
+    withKey key $ \k -> unsafeUseAsCString iv $ \ivptr -> c_aes_decrypt_cbc o k ivptr i blocks
+
+allocateAndMapBlocks :: ByteString -> (CUInt -> Ptr CChar -> CString -> IO ()) -> IO ByteString
+allocateAndMapBlocks input f = allocaBytes len (\output -> do unsafeUseAsCString input (f nbBlocks output)
+                                                              B.packCStringLen (output, len))
+    where len = B.length input
+          nbBlocks = fromIntegral (len `shiftR` 4)
+
diff --git a/Crypto/Cipher/Camellia.hs b/Crypto/Cipher/Camellia.hs
--- a/Crypto/Cipher/Camellia.hs
+++ b/Crypto/Cipher/Camellia.hs
@@ -10,7 +10,7 @@
 
 module Crypto.Cipher.Camellia
 	( Key(..)
-	, initKey
+	, initKey128
 	, encrypt
 	, decrypt
 	) where
@@ -177,8 +177,8 @@
 		in
 	(w64tow128 kL, w64tow128 kR, w64tow128 kA, w64tow128 kB)
 
-initKey :: B.ByteString -> Either String Key
-initKey keyseed
+initKey128 :: B.ByteString -> Either String Key
+initKey128 keyseed
 	| B.length keyseed /= 16 = Left "wrong key size"
 	| otherwise              =
 		let (kL, _, kA, _) = setKeyInterim keyseed in
diff --git a/Crypto/Cipher/DH.hs b/Crypto/Cipher/DH.hs
--- a/Crypto/Cipher/DH.hs
+++ b/Crypto/Cipher/DH.hs
@@ -18,7 +18,7 @@
 	, getShared
 	) where
 
-import Number.ModArithmetic (exponantiation_rtl_binary)
+import Number.ModArithmetic (exponantiation)
 import Number.Prime (generateSafePrime)
 import Number.Generate (generateOfSize)
 import Crypto.Types.PubKey.DH
@@ -39,8 +39,8 @@
 -- | generate a public number that is for the other party benefits.
 -- this number is usually called Y in DH text.
 generatePublic :: Params -> PrivateNumber -> PublicNumber
-generatePublic (p,g) (PrivateNumber x) = PublicNumber $ exponantiation_rtl_binary g x p
+generatePublic (p,g) (PrivateNumber x) = PublicNumber $ exponantiation g x p
 
 -- | generate a shared key using our private number and the other party public number
 getShared :: Params -> PrivateNumber -> PublicNumber -> SharedKey
-getShared (p,_) (PrivateNumber x) (PublicNumber y) = SharedKey $ exponantiation_rtl_binary y x p
+getShared (p,_) (PrivateNumber x) (PublicNumber y) = SharedKey $ exponantiation y x p
diff --git a/Crypto/Cipher/DSA.hs b/Crypto/Cipher/DSA.hs
--- a/Crypto/Cipher/DSA.hs
+++ b/Crypto/Cipher/DSA.hs
@@ -19,7 +19,7 @@
 import Crypto.Random
 import Data.Maybe
 import Data.ByteString (ByteString)
-import Number.ModArithmetic (exponantiation_rtl_binary, inverse)
+import Number.ModArithmetic (exponantiation, inverse)
 import Number.Serialize
 import Number.Generate
 import Crypto.Types.PubKey.DSA
@@ -64,4 +64,4 @@
 		v       = ((expmod g u1 p) * (expmod y u2 p)) `mod` p `mod` q
 
 expmod :: Integer -> Integer -> Integer -> Integer
-expmod = exponantiation_rtl_binary
+expmod = exponantiation
diff --git a/Crypto/Cipher/RSA.hs b/Crypto/Cipher/RSA.hs
--- a/Crypto/Cipher/RSA.hs
+++ b/Crypto/Cipher/RSA.hs
@@ -25,7 +25,7 @@
 import Crypto.Types.PubKey.RSA
 import Data.ByteString (ByteString)
 import qualified Data.ByteString as B
-import Number.ModArithmetic (exponantiation_rtl_binary, inverse)
+import Number.ModArithmetic (exponantiation, inverse)
 import Number.Prime (generatePrime)
 import Number.Serialize
 import Data.Maybe (fromJust)
@@ -178,4 +178,4 @@
 		bytes    = i2osp m
 
 expmod :: Integer -> Integer -> Integer -> Integer
-expmod = exponantiation_rtl_binary
+expmod = exponantiation
diff --git a/Number/ModArithmetic.hs b/Number/ModArithmetic.hs
--- a/Number/ModArithmetic.hs
+++ b/Number/ModArithmetic.hs
@@ -8,6 +8,7 @@
 
 module Number.ModArithmetic
 	( exponantiation_rtl_binary
+	, exponantiation
 	, inverse
 	) where
 
@@ -27,6 +28,16 @@
 		sq x          = (x * x) `mod` m
 		loop !0 _  !a = a `mod` m
 		loop !i !s !a = loop (i `shiftR` 1) (sq s) (if odd i then a * s else a)
+
+-- | exponantiation computes modular exponantiation as b^e mod m
+-- using repetitive squaring.
+exponantiation :: Integer -> Integer -> Integer -> Integer
+exponantiation b e m
+             | e == 0    = 1
+             | e == 1    = b `mod` m
+             | even e    = let p = (exponantiation b (e `div` 2) m) `mod` m
+                           in  (p^(2::Integer)) `mod` m
+             | otherwise = (b * exponantiation b (e-1) m) `mod` m
 
 -- | inverse computes the modular inverse as in g^(-1) mod m
 inverse :: Integer -> Integer -> Maybe Integer
diff --git a/Number/Polynomial.hs b/Number/Polynomial.hs
--- a/Number/Polynomial.hs
+++ b/Number/Polynomial.hs
@@ -44,12 +44,15 @@
 instance Show Polynomial where
 	show (Polynomial p) = intercalate "+" $ map show $ V.toList p
 
+toList :: Polynomial -> [Monomial]
 toList (Polynomial p) = V.toList p
 
+fromList :: [Monomial] -> Polynomial
 fromList = Polynomial . V.fromList . reverse . sort . filterZero
 	where
 		filterZero = filter (\(Monomial _ v) -> v /= 0)
 
+getWeight :: Polynomial -> Int -> Maybe Integer
 getWeight (Polynomial p) n = look 0
 	where
 		plen = V.length p
@@ -63,6 +66,7 @@
 					GT -> look (i+1)
 		
 
+mergePoly :: (Integer -> Integer -> Integer) -> Polynomial -> Polynomial -> Polynomial
 mergePoly f (Polynomial p1) (Polynomial p2) = fromList $ loop 0 0
 	where
 		l1 = V.length p1
@@ -74,18 +78,23 @@
 			| otherwise            =
 				let (coef, i1inc, i2inc) = addCoef (p1 ! i1) (p2 ! i2) in
 				coef : loop (i1+i1inc) (i2+i2inc)
-		addCoef m1@(Monomial w1 v1) m2@(Monomial w2 v2) =
+		addCoef m1@(Monomial w1 v1) (Monomial w2 v2) =
 			case compare w1 w2 of
 				LT -> (Monomial w2 (f 0 v2), 0, 1)
 				EQ -> (Monomial w1 (f v1 v2), 1, 1)
 				GT -> (m1, 1, 0)
 
+addPoly :: Polynomial -> Polynomial -> Polynomial
 addPoly = mergePoly (+)
+
+subPoly :: Polynomial -> Polynomial -> Polynomial
 subPoly = mergePoly (-)
 
+negPoly :: Polynomial -> Polynomial
 negPoly (Polynomial p) = Polynomial $ V.map negateMonomial p
 	where negateMonomial (Monomial w v) = Monomial w (-v)
 
+mulPoly :: Polynomial -> Polynomial -> Polynomial
 mulPoly p1@(Polynomial v1) p2@(Polynomial v2) =
 	fromList $ filter (\(Monomial _ v) -> v /= 0) $ map (\i -> Monomial i (c i)) $ reverse [0..(m+n)]
 	where
@@ -96,9 +105,11 @@
 				a = maybe 0 id . getWeight p1
 				b = maybe 0 id . getWeight p2
 
+squarePoly :: Polynomial -> Polynomial
 squarePoly p = p `mulPoly` p
 
-expPoly p e = loop e
+expPoly :: Polynomial -> Integer -> Polynomial
+expPoly p e = loop p e
 	where
 		loop t 0 = t
 		loop t n = loop (squarePoly t) (n-1)
diff --git a/Number/Prime.hs b/Number/Prime.hs
--- a/Number/Prime.hs
+++ b/Number/Prime.hs
@@ -14,6 +14,7 @@
 	, findPrimeFromWith
 	, primalityTestNaive
 	, primalityTestMillerRabin
+	, primalityTestFermat
 	, isCoprime
 	) where
 
@@ -21,15 +22,17 @@
 import Data.Bits
 import Number.Generate
 import Number.Basic (sqrti, gcde_binary)
-import Number.ModArithmetic (exponantiation_rtl_binary)
+import Number.ModArithmetic (exponantiation)
 
 -- | returns if the number is probably prime.
 -- first a list of small primes are implicitely tested for divisibility,
+-- then a fermat primality test is used with arbitrary numbers and
 -- then the Miller Rabin algorithm is used with an accuracy of 30 recursions
 isProbablyPrime :: CryptoRandomGen g => g -> Integer -> Either GenError (Bool, g)
 isProbablyPrime rng !n
 	| any (\p -> p `divides` n) (filter (< n) smallPrimes) = Right (False, rng)
-	| otherwise                                            = primalityTestMillerRabin rng 30 n
+	| primalityTestFermat 50 (n`div`2) n                   = primalityTestMillerRabin rng 30 n
+	| otherwise                                            = Right (False, rng)
 
 -- | generate a prime number of the required bitsize
 generatePrime :: CryptoRandomGen g => g -> Int -> Either GenError (Integer, g)
@@ -79,7 +82,7 @@
 		factorise !s !v
 			| v `testBit` 0 = (s, v)
 			| otherwise     = factorise (s+1) (v `shiftR` 1)
-		expmod = exponantiation_rtl_binary
+		expmod = exponantiation
 		-- when iteration reach zero, we have a probable prime
 		loop g _       0 = Right (True, g)
 		loop g t@(_,d) k = case generateBetween g 2 (n-2) of
@@ -95,6 +98,17 @@
 			| x2 == 1     = Right (False, g)
 			| x2 /= (n-1) = loop' g t km1 ((x2*x2) `mod` n) (r+1)
 			| otherwise   = loop g t km1
+
+-- | Probabilitic Test using Fermat primility test.
+-- Beware of Carmichael numbers that are Fermat liars, i.e. this test
+-- is useless for them. always combines with some other test.
+primalityTestFermat :: Int -- ^ number of iterations of the algorithm
+                    -> Integer -- ^ starting a
+                    -> Integer -- ^ number to test for primality
+                    -> Bool
+primalityTestFermat n a p = and $ map expTest [a..(a+fromIntegral n)]
+    where !pm1 = p-1
+          expTest i = exponantiation i pm1 p == 1
 
 -- | Test naively is integer is prime.
 -- while naive, we skip even number and stop iteration at i > sqrt(n)
diff --git a/System/Endian.hs b/System/Endian.hs
deleted file mode 100644
--- a/System/Endian.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-{-# LANGUAGE CPP #-}
-module System.Endian (littleEndian) where
-
-
-#ifdef BIG_ENDIAN
-littleEndian :: Bool
-littleEndian = False
-#elif defined(LITTLE_ENDIAN)
-littleEndian :: Bool
-littleEndian = True
-#else
-
-import System.Info (arch)
-
-littleEndian :: Bool
-littleEndian = arch /= "sparc" && arch /= "ppc"
-#endif
diff --git a/Tests.hs b/Tests.hs
deleted file mode 100644
--- a/Tests.hs
+++ /dev/null
@@ -1,518 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-import Test.HUnit ((~:), (~=?))
-import qualified Test.HUnit as Unit
-
-import Test.QuickCheck
-import Test.QuickCheck.Test
-import System.IO (hFlush, stdout)
-
-import Control.Monad
-import Control.Arrow (first)
-import Control.Applicative ((<$>))
-
-import Data.List (intercalate)
-import Data.Char
-import Data.Bits
-import Data.Word
-import qualified Data.Vector.Unboxed as V
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Char8 as BC
--- for DSA
-import qualified Crypto.Hash.SHA1 as SHA1
-
--- numbers
-import Number.ModArithmetic
-import Number.Basic
-import Number.Prime
-import Number.Serialize
--- ciphers/Kexch
-import qualified Crypto.Cipher.AES as AES
-import qualified Crypto.Cipher.RC4 as RC4
-import qualified Crypto.Cipher.Camellia as Camellia
-import qualified Crypto.Cipher.RSA as RSA
-import qualified Crypto.Cipher.DSA as DSA
-import qualified Crypto.Cipher.DH as DH
-import Crypto.Random
-
-encryptStream fi fc key plaintext = B.unpack $ snd $ fc (fi key) plaintext
-
-encryptBlock fi fc key plaintext =
-	let e = fi (B.pack key) in
-	case e of
-		Right k -> B.unpack $ fc k plaintext
-		Left  e -> error e
-
-wordify :: [Char] -> [Word8]
-wordify = map (toEnum . fromEnum)
-
-vectors_aes128_enc =
-	[
-	  ( [0x10, 0xa5, 0x88, 0x69, 0xd7, 0x4b, 0xe5, 0xa3,0x74,0xcf,0x86,0x7c,0xfb,0x47,0x38,0x59]
-	  , B.replicate 16 0
-	  , [0x6d,0x25,0x1e,0x69,0x44,0xb0,0x51,0xe0,0x4e,0xaa,0x6f,0xb4,0xdb,0xf7,0x84,0x65]
-	  )
-	, ( replicate 16 0
-	  , B.replicate 16 0
-	  , [0x66,0xe9,0x4b,0xd4,0xef,0x8a,0x2c,0x3b,0x88,0x4c,0xfa,0x59,0xca,0x34,0x2b,0x2e]
-	  )
-	, ( replicate 16 0
-	  , B.replicate 16 1
-	  , [0xe1,0x4d,0x5d,0x0e,0xe2,0x77,0x15,0xdf,0x08,0xb4,0x15,0x2b,0xa2,0x3d,0xa8,0xe0]
-	  )
-	, ( replicate 16 1
-	  , B.replicate 16 2
-	  , [0x17,0xd6,0x14,0xf3,0x79,0xa9,0x35,0x90,0x77,0xe9,0x55,0x77,0xfd,0x31,0xc2,0x0a]
-	  )
-	, ( replicate 16 2
-	  , B.replicate 16 1
-	  , [0x8f,0x42,0xc2,0x4b,0xee,0x6e,0x63,0x47,0x2b,0x16,0x5a,0xa9,0x41,0x31,0x2f,0x7c]
-	  )
-	, ( replicate 16 3
-	  , B.replicate 16 2
-	  , [0x90,0x98,0x85,0xe4,0x77,0xbc,0x20,0xf5,0x8a,0x66,0x97,0x1d,0xa0,0xbc,0x75,0xe3]
-	  )
-	]
-
-vectors_aes192_enc =
-	[
-	  ( replicate 24 0
-	  , B.replicate 16 0
-	  , [0xaa,0xe0,0x69,0x92,0xac,0xbf,0x52,0xa3,0xe8,0xf4,0xa9,0x6e,0xc9,0x30,0x0b,0xd7]
-	  )
-	, ( replicate 24 0
-	  , B.replicate 16 1
-	  , [0xcf,0x1e,0xce,0x3c,0x44,0xb0,0x78,0xfb,0x27,0xcb,0x0a,0x3e,0x07,0x1b,0x08,0x20]
-	  )
-	, ( replicate 24 1
-	  , B.replicate 16 2
-	  , [0xeb,0x8c,0x17,0x30,0x90,0xc7,0x5b,0x77,0xd6,0x72,0xb4,0x57,0xa7,0x78,0xd9,0xd0]
-	  )
-	, ( replicate 24 2
-	  , B.replicate 16 1
-	  , [0xf2,0xf0,0xae,0xd8,0xcd,0xc9,0x21,0xca,0x4b,0x55,0x84,0x5d,0xa4,0x15,0x21,0xc2]
-	  )
-	, ( replicate 24 3
-	  , B.replicate 16 2
-	  , [0xca,0xcc,0x30,0x79,0xe4,0xb7,0x95,0x27,0x63,0xd2,0x55,0xd6,0x34,0x10,0x46,0x14]
-	  )
-	]
-
-vectors_aes256_enc =
-	[ ( replicate 32 0
-	  , B.replicate 16 0
-	  , [0xdc,0x95,0xc0,0x78,0xa2,0x40,0x89,0x89,0xad,0x48,0xa2,0x14,0x92,0x84,0x20,0x87]
-	  )
-	, ( replicate 32 0
-	  , B.replicate 16 1
-	  , [0x7b,0xc3,0x02,0x6c,0xd7,0x37,0x10,0x3e,0x62,0x90,0x2b,0xcd,0x18,0xfb,0x01,0x63]
-	  )
-	, ( replicate 32 1
-	  , B.replicate 16 2
-	  , [0x62,0xae,0x12,0xf3,0x24,0xbf,0xea,0x08,0xd5,0xf6,0x75,0xb5,0x13,0x02,0x6b,0xbf]
-	  )
-	, ( replicate 32 2
-	  , B.replicate 16 1
-	  , [0x00,0xf9,0xc7,0x44,0x4b,0xb0,0xcc,0x80,0x6c,0x7c,0x39,0xee,0x22,0x11,0xf1,0x46]
-	  )
-	, ( replicate 32 3
-	  , B.replicate 16 2
-	  , [0xb4,0x05,0x87,0x3e,0xa0,0x76,0x1b,0x9c,0xa9,0x9f,0x70,0xb0,0x16,0x16,0xce,0xb1]
-	  )
-	]
-
-vectors_aes128_dec =
-	[ ( replicate 16 0
-	  , B.replicate 16 0
-	  , [0x14,0x0f,0x0f,0x10,0x11,0xb5,0x22,0x3d,0x79,0x58,0x77,0x17,0xff,0xd9,0xec,0x3a]
-	  )
-	, ( replicate 16 0
-	  , B.replicate 16 1
-	  , [0x15,0x6d,0x0f,0x85,0x75,0xd5,0x33,0x07,0x52,0xf8,0x4a,0xf2,0x72,0xff,0x30,0x50]
-	  )
-	, ( replicate 16 1
-	  , B.replicate 16 2
-	  , [0x34,0x37,0xd6,0xe2,0x31,0xd7,0x02,0x41,0x9b,0x51,0xb4,0x94,0x72,0x71,0xb6,0x11]
-	  )
-	, ( replicate 16 2
-	  , B.replicate 16 1
-	  , [0xe3,0xcd,0xe2,0x37,0xc8,0xf2,0xd9,0x7b,0x8d,0x79,0xf9,0x17,0x1d,0x4b,0xda,0xc1]
-	  )
-	, ( replicate 16 3
-	  , B.replicate 16 2
-	  , [0x5b,0x94,0xaa,0xed,0xd7,0x83,0x99,0x8c,0xd5,0x15,0x35,0x35,0x18,0xcc,0x45,0xe2]
-	  )
-	]
-
-vectors_aes192_dec =
-	[
-	  ( replicate 24 0
-	  , B.replicate 16 0
-	  , [0x13,0x46,0x0e,0x87,0xa8,0xfc,0x02,0x3e,0xf2,0x50,0x1a,0xfe,0x7f,0xf5,0x1c,0x51]
-	  )
-	, ( replicate 24 0
-	  , B.replicate 16 1
-	  , [0x92,0x17,0x07,0xc3,0x3d,0x1c,0xc5,0x96,0x7d,0xa5,0x1d,0xbb,0xb0,0x66,0xb2,0x6c]
-	  )
-	, ( replicate 24 1
-	  , B.replicate 16 2
-	  , [0xee,0x92,0x97,0xc6,0xba,0xe8,0x26,0x4d,0xff,0x08,0x0e,0xbb,0x1e,0x74,0x11,0xc1]
-	  )
-	, ( replicate 24 2
-	  , B.replicate 16 1
-	  , [0x49,0x67,0xdf,0x70,0xd2,0x9e,0x9a,0x7f,0x5d,0x7c,0xb9,0xc1,0x20,0xc3,0x8a,0x71]
-	  )
-	, ( replicate 24 3
-	  , B.replicate 16 2
-	  , [0x74,0x38,0x62,0x42,0x6b,0x56,0x7f,0xd5,0xf0,0x1d,0x1b,0x59,0x56,0x01,0x26,0x29]
-	  )
-	]
-
-vectors_aes256_dec =
-	[ ( replicate 32 0
-	  , B.replicate 16 0
-	  , [0x67,0x67,0x1c,0xe1,0xfa,0x91,0xdd,0xeb,0x0f,0x8f,0xbb,0xb3,0x66,0xb5,0x31,0xb4]
-	  )
-	, ( replicate 32 0
-	  , B.replicate 16 1
-	  , [0xcc,0x09,0x21,0xa3,0xc5,0xca,0x17,0xf7,0x48,0xb7,0xc2,0x7b,0x73,0xba,0x87,0xa2]
-	  )
-	, ( replicate 32 1
-	  , B.replicate 16 2
-	  , [0xc0,0x4b,0x27,0x90,0x1a,0x50,0xcf,0xfa,0xf1,0xbb,0x88,0x9f,0xc0,0x92,0x5e,0x14]
-	  )
-	, ( replicate 32 2
-	  , B.replicate 16 1
-	  , [0x24,0x61,0x53,0x5d,0x16,0x1c,0x15,0x39,0x88,0x32,0x77,0x29,0xc5,0x8c,0xc0,0x3a]
-	  )
-	, ( replicate 32 3
-	  , B.replicate 16 2
-	  , [0x30,0xc9,0x1c,0xce,0xfe,0x89,0x30,0xcf,0xff,0x31,0xdb,0xcc,0xfc,0x11,0xc5,0x23]
-	  )
-	]
-
-aes128InitKey = AES.initKey128
-aes192InitKey = AES.initKey192
-aes256InitKey = AES.initKey256
-
-vectors_rc4 =
-	[ (wordify "Key", "Plaintext", [ 0xBB,0xF3,0x16,0xE8,0xD9,0x40,0xAF,0x0A,0xD3 ])
-	, (wordify "Wiki", "pedia", [ 0x10,0x21,0xBF,0x04,0x20 ])
-	, (wordify "Secret", "Attack at dawn", [ 0x45,0xA0,0x1F,0x64,0x5F,0xC3,0x5B,0x38,0x35,0x52,0x54,0x4B,0x9B,0xF5 ])
-	]
-
-vectors_camellia128 =
-	[ 
-	  ( replicate 16 0
-	  , B.replicate 16 0
-	  , [0x3d,0x02,0x80,0x25,0xb1,0x56,0x32,0x7c,0x17,0xf7,0x62,0xc1,0xf2,0xcb,0xca,0x71]
-	  )
-	, ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
-	  , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
-	  , [0x67,0x67,0x31,0x38,0x54,0x96,0x69,0x73,0x08,0x57,0x06,0x56,0x48,0xea,0xbe,0x43]
-	  )
-	]
-
-vectors_camellia192 =
-	[
-	  ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77]
-	  , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
-	  ,[0xb4,0x99,0x34,0x01,0xb3,0xe9,0x96,0xf8,0x4e,0xe5,0xce,0xe7,0xd7,0x9b,0x09,0xb9]
-	  )
-	]
-
-vectors_camellia256 =
-	[
-	  ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10
-	    ,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff]
-	  , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
-	  , [0x9a,0xcc,0x23,0x7d,0xff,0x16,0xd7,0x6c,0x20,0xef,0x7c,0x91,0x9e,0x3a,0x75,0x09]
-	  )
-	]
-
-vectors =
-	[ ("RC4",        vectors_rc4,         encryptStream RC4.initCtx RC4.encrypt)
-	, ("AES128 Enc", vectors_aes128_enc,  encryptBlock aes128InitKey AES.encrypt)
-	, ("AES192 Enc", vectors_aes192_enc,  encryptBlock aes192InitKey AES.encrypt)
-	, ("AES256 Enc", vectors_aes256_enc,  encryptBlock aes256InitKey AES.encrypt)
-	, ("AES128 Dec", vectors_aes128_dec,  encryptBlock aes128InitKey AES.decrypt)
-	, ("AES192 Dec", vectors_aes192_dec,  encryptBlock aes192InitKey AES.decrypt)
-	, ("AES256 Dec", vectors_aes256_dec,  encryptBlock aes256InitKey AES.decrypt)
-	, ("Camellia",   vectors_camellia128, encryptBlock Camellia.initKey Camellia.encrypt)
-	]
-
-utests :: [Unit.Test]
-utests = concatMap (\(name, v, f) -> map (\(k,p,e) -> name ~: name ~: e ~=? f k p) v) vectors
-
-{- end of units tests -}
-{- start of QuickCheck verification -}
-
-prop_gcde_binary_valid (Positive a, Positive b) =
-	let (x,y,v)    = gcde_binary a b in
-	let (x',y',v') = gcde a b in
-	and [v==v', a*x' + b*y' == v', a*x + b*y == v, gcd a b == v]
-
-prop_modexp_rtl_valid (NonNegative a, NonNegative b, Positive m) =
-	exponantiation_rtl_binary a b m == ((a ^ b) `mod` m)
-
-prop_modinv_valid (Positive a, Positive m)
-	| m > 1 =
-		case inverse a m of
-			Just ainv -> (ainv * a) `mod` m == 1
-			Nothing   -> True
-	| otherwise       = True
-
-prop_sqrti_valid (Positive i) = l*l <= i && i <= u*u where (l, u) = sqrti i
-
-prop_generate_prime_valid i =
-	-- becuase of the next naive test, we can't generate easily number above 32 bits
-	-- otherwise it slows down the tests to uselessness. when AKS or ECPP is implemented
-	-- we can revisit the number here
-	let p = withAleasInteger rng i (\g -> generatePrime g 32) in
-	-- FIXME test if p is around 32 bits
-	primalityTestNaive p
-
-prop_miller_rabin_valid i
-	| i <= 3    = True
-	| otherwise =
-		-- miller rabin only returns with certitude that the integer is composite.
-		let b = withAleasInteger rng i (\g -> isProbablyPrime g i) in
-		(b == False && primalityTestNaive i == False) || b == True
-
-withAleasInteger rng i f = case reseed (i2osp (if i < 0 then -i else i)) rng of
-	Left _     -> error "impossible"
-	Right rng' -> case f rng' of
-		Left _  -> error "impossible"
-		Right v -> fst v
-
-newtype RSAMessage = RSAMessage B.ByteString deriving (Show, Eq)
-
-instance Arbitrary RSAMessage where
-	arbitrary = do
-		sz <- choose (0, 128 - 11)
-		ws <- replicateM sz (choose (0,255) :: Gen Int)
-		return $ RSAMessage $ B.pack $ map fromIntegral ws
-
-{- this is a just test rng. this is absolutely not a serious RNG. DO NOT use elsewhere -}
-data Rng = Rng (Int, Int)
-
-getByte :: Rng -> (Word8, Rng)
-getByte (Rng (mz, mw)) =
-	let mz2 = 36969 * (mz `mod` 65536) in
-	let mw2 = 18070 * (mw `mod` 65536) in
-	(fromIntegral (mz2 + mw2), Rng (mz2, mw2))
-
-getBytes 0 rng = ([], rng)
-getBytes n rng =
-	let (b, rng')  = getByte rng in
-	let (l, rng'') = getBytes (n-1) rng' in
-	(b:l, rng'')
-
-instance CryptoRandomGen Rng where
-	newGen _       = Right (Rng (2,3))
-	genSeedLength  = 0
-	genBytes len g = Right $ first B.pack $ getBytes len g
-	reseed bs (Rng (a,b)) = Right $ Rng (fromIntegral a', b) where
-		a' = ((fromIntegral a) + i * 36969) `mod` 65536
-		i = os2ip bs
-
-rng = Rng (1,2) 
-
-{-----------------------------------------------------------------------------------------------}
-{- testing RSA -}
-{-----------------------------------------------------------------------------------------------}
-
-prop_rsa_generate_valid (Positive i, RSAMessage msgz) =
-	let keysz = 64 in
-	let (pub,priv) = withAleasInteger rng i (\g -> RSA.generate g keysz 65537) in
-	let msg = B.take (keysz - 11) msgz in
-	(RSA.private_p priv * RSA.private_q priv == RSA.private_n priv) &&
-	((RSA.private_d priv * RSA.public_e pub) `mod` ((RSA.private_p priv - 1) * (RSA.private_q priv - 1)) == 1) &&
-	(either Left (RSA.decrypt priv . fst) $ RSA.encrypt rng pub msg) == Right msg
-
-prop_rsa_valid fast (RSAMessage msg) =
-	(either Left (RSA.decrypt pk . fst) $ RSA.encrypt rng rsaPublickey msg) == Right msg
-	where pk       = if fast then rsaPrivatekey else rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
-
-prop_rsa_fast_valid  = prop_rsa_valid True
-prop_rsa_slow_valid  = prop_rsa_valid False
-
-prop_rsa_sign_valid fast (RSAMessage msg) = (either Left (\smsg -> verify msg smsg) $ sign msg) == Right True
-	where
-		verify   = RSA.verify (SHA1.hash) sha1desc rsaPublickey
-		sign     = RSA.sign (SHA1.hash) sha1desc pk
-		sha1desc = B.pack [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03, 0x02,0x1a,0x05,0x00,0x04,0x14]
-		pk       = if fast then rsaPrivatekey else rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
-
-prop_rsa_sign_fast_valid = prop_rsa_sign_valid True
-prop_rsa_sign_slow_valid = prop_rsa_sign_valid False
-
-rsaPrivatekey = RSA.PrivateKey
-	{ RSA.private_size = 128
-	, RSA.private_n    = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151
-	, RSA.private_d    = 133764127300370985476360382258931504810339098611363623122953018301285450176037234703101635770582297431466449863745848961134143024057267778947569638425565153896020107107895924597628599677345887446144410702679470631826418774397895304952287674790343620803686034122942606764275835668353720152078674967983573326257
-	, RSA.private_p    = 12909745499610419492560645699977670082358944785082915010582495768046269235061708286800087976003942261296869875915181420265794156699308840835123749375331319
-	, RSA.private_q    = 10860278066550210927914375228722265675263011756304443428318337179619069537063135098400347475029673115805419186390580990519363257108008103841271008948795129
-	, RSA.private_dP   = 5014229697614831746694710412330921341325464081424013940131184365711243776469716106024020620858146547161326009604054855316321928968077674343623831428796843
-	, RSA.private_dQ   = 3095337504083058271243917403868092841421453478127022884745383831699720766632624326762288333095492075165622853999872779070009098364595318242383709601515849
-	, RSA.private_qinv = 11136639099661288633118187183300604127717437440459572124866697429021958115062007251843236337586667012492941414990095176435990146486852255802952814505784196
-	}
-
-rsaPublickey = RSA.PublicKey
-	{ RSA.public_size = 128
-	, RSA.public_n    = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151
-	, RSA.public_e    = 65537
-	}
-
-{-----------------------------------------------------------------------------------------------}
-{- testing DSA -}
-{-----------------------------------------------------------------------------------------------}
-
-
-dsaParams = (p,g,q)
-	where
-		p = 0x00a8c44d7d0bbce69a39008948604b9c7b11951993a5a1a1fa995968da8bb27ad9101c5184bcde7c14fb79f7562a45791c3d80396cefb328e3e291932a17e22edd
-		g = 0x0bf9fe6c75d2367b88912b2252d20fdcad06b3f3a234b92863a1e30a96a123afd8e8a4b1dd953e6f5583ef8e48fc7f47a6a1c8f24184c76dba577f0fec2fcd1c
-		q = 0x0096674b70ef58beaaab6743d6af16bb862d18d119
-
-dsaPrivatekey = DSA.PrivateKey
-	{ DSA.private_params = dsaParams
-	, DSA.private_x      = 0x229bac7aa1c7db8121bfc050a3426eceae23fae8
-	}
-
-dsaPublickey = DSA.PublicKey
-	{ DSA.public_params = dsaParams
-	, DSA.public_y      = 0x4fa505e86e32922f1fa1702a120abdba088bb4be801d4c44f7fc6b9094d85cd52c429cbc2b39514e30909b31e2e2e0752b0fc05c1a7d9c05c3e52e49e6edef4c
-	}
-
-prop_dsa_valid (RSAMessage msg) =
-	case DSA.verify signature (SHA1.hash) dsaPublickey msg of
-		Left err -> False
-		Right b  -> b
-	where
-		Right (signature, rng') = DSA.sign rng (SHA1.hash) dsaPrivatekey msg
-
-{-----------------------------------------------------------------------------------------------}
-{- testing DH -}
-{-----------------------------------------------------------------------------------------------}
-instance Arbitrary DH.PrivateNumber where
-	arbitrary = fromIntegral <$> (suchThat (arbitrary :: Gen Integer) (\x -> x >= 1))
-
-prop_dh_valid (xa, xb) = sa == sb
-	where
-		sa = DH.getShared dhparams xa yb
-		sb = DH.getShared dhparams xb ya
-		yb = DH.generatePublic dhparams xb
-		ya = DH.generatePublic dhparams xa
-		dhparams = (11, 7)
-
-{-----------------------------------------------------------------------------------------------}
-{- testing AES -}
-{-----------------------------------------------------------------------------------------------}
-data AES128Message = AES128Message B.ByteString B.ByteString B.ByteString deriving (Show, Eq)
-data AES192Message = AES192Message B.ByteString B.ByteString B.ByteString deriving (Show, Eq)
-data AES256Message = AES256Message B.ByteString B.ByteString B.ByteString deriving (Show, Eq)
-
-arbitraryAES keysize = do
-	sz <- choose (1, 12)
-	ws <- replicateM (sz*16) (choose (0,255) :: Gen Int)
-	key <- replicateM keysize (choose (0,255) :: Gen Int)
-	iv  <- replicateM 16 (choose (0,255) :: Gen Int)
-	return (ws, key, iv)
-
-instance Arbitrary AES128Message where
-	arbitrary = do
-		(ws, key, iv) <- arbitraryAES 16
-		return $ AES128Message (B.pack $ map fromIntegral key)
-		                       (B.pack $ map fromIntegral iv)
-		                       (B.pack $ map fromIntegral ws)
-
-instance Arbitrary AES192Message where
-	arbitrary = do
-		(ws, key, iv) <- arbitraryAES 24
-		return $ AES192Message (B.pack $ map fromIntegral key)
-		                       (B.pack $ map fromIntegral iv)
-		                       (B.pack $ map fromIntegral ws)
-
-instance Arbitrary AES256Message where
-	arbitrary = do
-		(ws, key, iv) <- arbitraryAES 32
-		return $ AES256Message (B.pack $ map fromIntegral key)
-		                       (B.pack $ map fromIntegral iv)
-		                       (B.pack $ map fromIntegral ws)
-
-
-prop_ecb_valid k msg = AES.decrypt k (AES.encrypt k msg) == msg
-prop_cbc_valid k iv msg = AES.decryptCBC k iv (AES.encryptCBC k iv msg) == msg
-
-prop_aes128_ecb_valid (AES128Message key _ msg) =
-	let (Right k) = AES.initKey128 key in
-	prop_ecb_valid k msg
-
-prop_aes192_ecb_valid (AES192Message key _ msg) =
-	let (Right k) = AES.initKey192 key in
-	prop_ecb_valid k msg
-
-prop_aes256_ecb_valid (AES256Message key _ msg) =
-	let (Right k) = AES.initKey256 key in
-	prop_ecb_valid k msg
-
-prop_aes128_cbc_valid (AES128Message key iv msg) =
-	let (Right k) = AES.initKey128 key in
-	prop_cbc_valid k iv msg
-
-prop_aes192_cbc_valid (AES192Message key iv msg) =
-	let (Right k) = AES.initKey192 key in
-	prop_cbc_valid k iv msg
-
-prop_aes256_cbc_valid (AES256Message key iv msg) =
-	let (Right k) = AES.initKey256 key in
-	prop_cbc_valid k iv msg
-
-{-----------------------------------------------------------------------------------------------}
-{- main -}
-{-----------------------------------------------------------------------------------------------}
-
-args = stdArgs
-	{ replay     = Nothing
-	, maxSuccess = 200
-	, maxDiscard = 1000
-	, maxSize    = 200
-	}
-
-run_test n t = putStr ("  " ++ n ++ " ... ") >> hFlush stdout >> quickCheckWith args t
-
-main = do
-	Unit.runTestTT (Unit.TestList utests)
-
-	-- Number Tests
-	run_test "gcde binary valid" prop_gcde_binary_valid
-	run_test "exponantiation RTL valid" prop_modexp_rtl_valid
-	run_test "inverse valid" prop_modinv_valid
-	run_test "sqrt integer valid" prop_sqrti_valid
-	run_test "primality test Miller Rabin" prop_miller_rabin_valid
-	run_test "Generate prime" prop_generate_prime_valid
-
-	-- AES Tests
-	run_test "AES128 (ECB) decrypt.encrypt = id" prop_aes128_ecb_valid
-	run_test "AES128 (CBC) decrypt.encrypt = id" prop_aes128_cbc_valid
-
-	run_test "AES192 (ECB) decrypt.encrypt = id" prop_aes192_ecb_valid
-	run_test "AES192 (CBC) decrypt.encrypt = id" prop_aes192_cbc_valid
-
-	run_test "AES256 (ECB) decrypt.encrypt = id" prop_aes256_ecb_valid
-	run_test "AES256 (CBC) decrypt.encrypt = id" prop_aes256_cbc_valid
-
-	-- DH Tests
-	run_test "DH test" prop_dh_valid
-
-	-- RSA Tests
-	run_test "RSA generate" prop_rsa_generate_valid
-	run_test "RSA verify . sign(slow) = true" prop_rsa_sign_slow_valid
-	run_test "RSA verify . sign(fast) = true" prop_rsa_sign_fast_valid
-
-	run_test "RSA decrypt(slow).encrypt = id" prop_rsa_slow_valid
-	run_test "RSA decrypt(fast).encrypt = id" prop_rsa_fast_valid
-
-	-- DSA Tests
-	run_test "DSA verify . sign = true" prop_dsa_valid
diff --git a/Tests/AES.hs b/Tests/AES.hs
new file mode 100644
--- /dev/null
+++ b/Tests/AES.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE CPP #-}
+module AES (aesTests) where
+
+-- unfortunately due to a bug in some version of cabal
+-- there's no way to have a condition cpp-options in the cabal file
+-- for test suite. to run test with AESni, uncomment the following
+-- #define HAVE_AESNI
+
+import qualified Crypto.Cipher.AES.Haskell as AESHs
+#ifdef HAVE_AESNI
+import qualified Crypto.Cipher.AES.X86NI as AESNI
+#endif
+
+import Crypto.Classes
+import qualified Crypto.Modes as CAPI
+
+import Data.Word
+import Data.List
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Control.Monad
+import Control.Applicative
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.QuickCheck
+
+newtype IV = IV ByteString
+    deriving (Show,Eq)
+newtype Key128 = Key128 ByteString
+    deriving (Show,Eq)
+newtype Message = Message ByteString
+    deriving (Show,Eq)
+
+arbitraryBS sz = B.pack <$> replicateM sz (choose (0,255) :: Gen Word8)
+
+instance Arbitrary IV where
+    arbitrary = IV <$> arbitraryBS 16
+
+instance Arbitrary Key128 where
+    arbitrary = Key128 <$> arbitraryBS 16
+
+instance Arbitrary Message where
+    arbitrary = choose (1,102) >>= \sz -> Message <$> arbitraryBS (16*sz)
+
+ebcTests l (Key128 k, Message m) = (== 1) $ length $ nub $ map (\f -> f k m) l
+cbcTests l (IV iv, Key128 k, Message m) = (== 1) $ length $ nub $ map (\f -> f k iv m) l
+
+unright (Right r) = r
+unright (Left e) = error e
+
+aesTests =
+    [ testProperty "ECB Encryption Equivalent" $ ebcTests
+        [ (\k m -> AESHs.encrypt (unright $ AESHs.initKey128 k) m)
+#ifdef HAVE_AESNI
+        , (\k m -> AESNI.encrypt (AESNI.initKey128 k) m)
+#endif
+        ]
+    , testProperty "CBC Encryption Equivalent" $ cbcTests
+        [ (\k iv m -> AESHs.encryptCBC (unright $ AESHs.initKey128 k) iv m)
+#ifdef HAVE_AESNI
+        , (\k iv m -> AESNI.encryptCBC (AESNI.initKey128 k) iv m)
+#endif
+        ]
+    , testProperty "ECB Decryption Equivalent" $ ebcTests
+        [ (\k m -> AESHs.decrypt (unright $ AESHs.initKey128 k) m)
+#ifdef HAVE_AESNI
+        , (\k m -> AESNI.decrypt (AESNI.initKey128 k) m)
+#endif
+        ]
+    , testProperty "CBC Decryption Equivalent" $ cbcTests
+        [ (\k iv m -> AESHs.decryptCBC (unright $ AESHs.initKey128 k) iv m)
+#ifdef HAVE_AESNI
+        , (\k iv m -> AESNI.decryptCBC (AESNI.initKey128 k) iv m)
+#endif
+        ]
+    ]
diff --git a/Tests/KAT.hs b/Tests/KAT.hs
new file mode 100644
--- /dev/null
+++ b/Tests/KAT.hs
@@ -0,0 +1,241 @@
+{-# LANGUAGE OverloadedStrings, CPP #-}
+module KAT (katTests) where
+
+-- unfortunately due to a bug in some version of cabal
+-- there's no way to have a condition cpp-options in the cabal file
+-- for test suite. to run test with AESni, uncomment the following
+-- #define HAVE_AESNI
+
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+
+import Data.Word
+
+import qualified Crypto.Cipher.AES.Haskell as AES
+#ifdef HAVE_AESNI
+import qualified Crypto.Cipher.AES.X86NI as AESNI
+#endif
+import qualified Crypto.Cipher.Camellia as Camellia
+import qualified Crypto.Cipher.RC4 as RC4
+
+encryptStream fi fc key plaintext = B.unpack $ snd $ fc (fi key) plaintext
+
+encryptBlock fi fc key plaintext =
+	let e = fi (B.pack key) in
+	case e of
+		Right k -> B.unpack $ fc k plaintext
+		Left  e -> error e
+
+wordify :: [Char] -> [Word8]
+wordify = map (toEnum . fromEnum)
+
+vectors_aes128_enc =
+	[
+	  ( [0x10, 0xa5, 0x88, 0x69, 0xd7, 0x4b, 0xe5, 0xa3,0x74,0xcf,0x86,0x7c,0xfb,0x47,0x38,0x59]
+	  , B.replicate 16 0
+	  , [0x6d,0x25,0x1e,0x69,0x44,0xb0,0x51,0xe0,0x4e,0xaa,0x6f,0xb4,0xdb,0xf7,0x84,0x65]
+	  )
+	, ( replicate 16 0
+	  , B.replicate 16 0
+	  , [0x66,0xe9,0x4b,0xd4,0xef,0x8a,0x2c,0x3b,0x88,0x4c,0xfa,0x59,0xca,0x34,0x2b,0x2e]
+	  )
+	, ( replicate 16 0
+	  , B.replicate 16 1
+	  , [0xe1,0x4d,0x5d,0x0e,0xe2,0x77,0x15,0xdf,0x08,0xb4,0x15,0x2b,0xa2,0x3d,0xa8,0xe0]
+	  )
+	, ( replicate 16 1
+	  , B.replicate 16 2
+	  , [0x17,0xd6,0x14,0xf3,0x79,0xa9,0x35,0x90,0x77,0xe9,0x55,0x77,0xfd,0x31,0xc2,0x0a]
+	  )
+	, ( replicate 16 2
+	  , B.replicate 16 1
+	  , [0x8f,0x42,0xc2,0x4b,0xee,0x6e,0x63,0x47,0x2b,0x16,0x5a,0xa9,0x41,0x31,0x2f,0x7c]
+	  )
+	, ( replicate 16 3
+	  , B.replicate 16 2
+	  , [0x90,0x98,0x85,0xe4,0x77,0xbc,0x20,0xf5,0x8a,0x66,0x97,0x1d,0xa0,0xbc,0x75,0xe3]
+	  )
+	]
+
+vectors_aes192_enc =
+	[
+	  ( replicate 24 0
+	  , B.replicate 16 0
+	  , [0xaa,0xe0,0x69,0x92,0xac,0xbf,0x52,0xa3,0xe8,0xf4,0xa9,0x6e,0xc9,0x30,0x0b,0xd7]
+	  )
+	, ( replicate 24 0
+	  , B.replicate 16 1
+	  , [0xcf,0x1e,0xce,0x3c,0x44,0xb0,0x78,0xfb,0x27,0xcb,0x0a,0x3e,0x07,0x1b,0x08,0x20]
+	  )
+	, ( replicate 24 1
+	  , B.replicate 16 2
+	  , [0xeb,0x8c,0x17,0x30,0x90,0xc7,0x5b,0x77,0xd6,0x72,0xb4,0x57,0xa7,0x78,0xd9,0xd0]
+	  )
+	, ( replicate 24 2
+	  , B.replicate 16 1
+	  , [0xf2,0xf0,0xae,0xd8,0xcd,0xc9,0x21,0xca,0x4b,0x55,0x84,0x5d,0xa4,0x15,0x21,0xc2]
+	  )
+	, ( replicate 24 3
+	  , B.replicate 16 2
+	  , [0xca,0xcc,0x30,0x79,0xe4,0xb7,0x95,0x27,0x63,0xd2,0x55,0xd6,0x34,0x10,0x46,0x14]
+	  )
+	]
+
+vectors_aes256_enc =
+	[ ( replicate 32 0
+	  , B.replicate 16 0
+	  , [0xdc,0x95,0xc0,0x78,0xa2,0x40,0x89,0x89,0xad,0x48,0xa2,0x14,0x92,0x84,0x20,0x87]
+	  )
+	, ( replicate 32 0
+	  , B.replicate 16 1
+	  , [0x7b,0xc3,0x02,0x6c,0xd7,0x37,0x10,0x3e,0x62,0x90,0x2b,0xcd,0x18,0xfb,0x01,0x63]
+	  )
+	, ( replicate 32 1
+	  , B.replicate 16 2
+	  , [0x62,0xae,0x12,0xf3,0x24,0xbf,0xea,0x08,0xd5,0xf6,0x75,0xb5,0x13,0x02,0x6b,0xbf]
+	  )
+	, ( replicate 32 2
+	  , B.replicate 16 1
+	  , [0x00,0xf9,0xc7,0x44,0x4b,0xb0,0xcc,0x80,0x6c,0x7c,0x39,0xee,0x22,0x11,0xf1,0x46]
+	  )
+	, ( replicate 32 3
+	  , B.replicate 16 2
+	  , [0xb4,0x05,0x87,0x3e,0xa0,0x76,0x1b,0x9c,0xa9,0x9f,0x70,0xb0,0x16,0x16,0xce,0xb1]
+	  )
+	]
+
+vectors_aes128_dec =
+	[ ( replicate 16 0
+	  , B.replicate 16 0
+	  , [0x14,0x0f,0x0f,0x10,0x11,0xb5,0x22,0x3d,0x79,0x58,0x77,0x17,0xff,0xd9,0xec,0x3a]
+	  )
+	, ( replicate 16 0
+	  , B.replicate 16 1
+	  , [0x15,0x6d,0x0f,0x85,0x75,0xd5,0x33,0x07,0x52,0xf8,0x4a,0xf2,0x72,0xff,0x30,0x50]
+	  )
+	, ( replicate 16 1
+	  , B.replicate 16 2
+	  , [0x34,0x37,0xd6,0xe2,0x31,0xd7,0x02,0x41,0x9b,0x51,0xb4,0x94,0x72,0x71,0xb6,0x11]
+	  )
+	, ( replicate 16 2
+	  , B.replicate 16 1
+	  , [0xe3,0xcd,0xe2,0x37,0xc8,0xf2,0xd9,0x7b,0x8d,0x79,0xf9,0x17,0x1d,0x4b,0xda,0xc1]
+	  )
+	, ( replicate 16 3
+	  , B.replicate 16 2
+	  , [0x5b,0x94,0xaa,0xed,0xd7,0x83,0x99,0x8c,0xd5,0x15,0x35,0x35,0x18,0xcc,0x45,0xe2]
+	  )
+	]
+
+vectors_aes192_dec =
+	[
+	  ( replicate 24 0
+	  , B.replicate 16 0
+	  , [0x13,0x46,0x0e,0x87,0xa8,0xfc,0x02,0x3e,0xf2,0x50,0x1a,0xfe,0x7f,0xf5,0x1c,0x51]
+	  )
+	, ( replicate 24 0
+	  , B.replicate 16 1
+	  , [0x92,0x17,0x07,0xc3,0x3d,0x1c,0xc5,0x96,0x7d,0xa5,0x1d,0xbb,0xb0,0x66,0xb2,0x6c]
+	  )
+	, ( replicate 24 1
+	  , B.replicate 16 2
+	  , [0xee,0x92,0x97,0xc6,0xba,0xe8,0x26,0x4d,0xff,0x08,0x0e,0xbb,0x1e,0x74,0x11,0xc1]
+	  )
+	, ( replicate 24 2
+	  , B.replicate 16 1
+	  , [0x49,0x67,0xdf,0x70,0xd2,0x9e,0x9a,0x7f,0x5d,0x7c,0xb9,0xc1,0x20,0xc3,0x8a,0x71]
+	  )
+	, ( replicate 24 3
+	  , B.replicate 16 2
+	  , [0x74,0x38,0x62,0x42,0x6b,0x56,0x7f,0xd5,0xf0,0x1d,0x1b,0x59,0x56,0x01,0x26,0x29]
+	  )
+	]
+
+vectors_aes256_dec =
+	[ ( replicate 32 0
+	  , B.replicate 16 0
+	  , [0x67,0x67,0x1c,0xe1,0xfa,0x91,0xdd,0xeb,0x0f,0x8f,0xbb,0xb3,0x66,0xb5,0x31,0xb4]
+	  )
+	, ( replicate 32 0
+	  , B.replicate 16 1
+	  , [0xcc,0x09,0x21,0xa3,0xc5,0xca,0x17,0xf7,0x48,0xb7,0xc2,0x7b,0x73,0xba,0x87,0xa2]
+	  )
+	, ( replicate 32 1
+	  , B.replicate 16 2
+	  , [0xc0,0x4b,0x27,0x90,0x1a,0x50,0xcf,0xfa,0xf1,0xbb,0x88,0x9f,0xc0,0x92,0x5e,0x14]
+	  )
+	, ( replicate 32 2
+	  , B.replicate 16 1
+	  , [0x24,0x61,0x53,0x5d,0x16,0x1c,0x15,0x39,0x88,0x32,0x77,0x29,0xc5,0x8c,0xc0,0x3a]
+	  )
+	, ( replicate 32 3
+	  , B.replicate 16 2
+	  , [0x30,0xc9,0x1c,0xce,0xfe,0x89,0x30,0xcf,0xff,0x31,0xdb,0xcc,0xfc,0x11,0xc5,0x23]
+	  )
+	]
+
+aes128InitKey = AES.initKey128
+aes192InitKey = AES.initKey192
+aes256InitKey = AES.initKey256
+
+vectors_rc4 =
+	[ (wordify "Key", "Plaintext", [ 0xBB,0xF3,0x16,0xE8,0xD9,0x40,0xAF,0x0A,0xD3 ])
+	, (wordify "Wiki", "pedia", [ 0x10,0x21,0xBF,0x04,0x20 ])
+	, (wordify "Secret", "Attack at dawn", [ 0x45,0xA0,0x1F,0x64,0x5F,0xC3,0x5B,0x38,0x35,0x52,0x54,0x4B,0x9B,0xF5 ])
+	]
+
+vectors_camellia128 =
+	[ 
+	  ( replicate 16 0
+	  , B.replicate 16 0
+	  , [0x3d,0x02,0x80,0x25,0xb1,0x56,0x32,0x7c,0x17,0xf7,0x62,0xc1,0xf2,0xcb,0xca,0x71]
+	  )
+	, ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
+	  , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
+	  , [0x67,0x67,0x31,0x38,0x54,0x96,0x69,0x73,0x08,0x57,0x06,0x56,0x48,0xea,0xbe,0x43]
+	  )
+	]
+
+vectors_camellia192 =
+	[
+	  ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77]
+	  , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
+	  ,[0xb4,0x99,0x34,0x01,0xb3,0xe9,0x96,0xf8,0x4e,0xe5,0xce,0xe7,0xd7,0x9b,0x09,0xb9]
+	  )
+	]
+
+vectors_camellia256 =
+	[
+	  ( [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10
+	    ,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff]
+	  , B.pack [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10]
+	  , [0x9a,0xcc,0x23,0x7d,0xff,0x16,0xd7,0x6c,0x20,0xef,0x7c,0x91,0x9e,0x3a,0x75,0x09]
+	  )
+	]
+
+vectors =
+	[ ("RC4",        vectors_rc4,         encryptStream RC4.initCtx RC4.encrypt)
+	-- AES haskell implementation
+	, ("AES 128 Enc", vectors_aes128_enc,  encryptBlock aes128InitKey AES.encrypt)
+	, ("AES 192 Enc", vectors_aes192_enc,  encryptBlock aes192InitKey AES.encrypt)
+	, ("AES 256 Enc", vectors_aes256_enc,  encryptBlock aes256InitKey AES.encrypt)
+	, ("AES 128 Dec", vectors_aes128_dec,  encryptBlock aes128InitKey AES.decrypt)
+	, ("AES 192 Dec", vectors_aes192_dec,  encryptBlock aes192InitKey AES.decrypt)
+	, ("AES 256 Dec", vectors_aes256_dec,  encryptBlock aes256InitKey AES.decrypt)
+#ifdef HAVE_AESNI
+	-- AES ni implementation
+	, ("AESNI 128 Enc", vectors_aes128_enc,  encryptBlock (Right . AESNI.initKey128) AESNI.encrypt)
+	, ("AESNI 128 Dec", vectors_aes128_dec,  encryptBlock (Right . AESNI.initKey128) AESNI.decrypt)
+#endif
+	-- Camellia implementation
+	, ("Camellia",   vectors_camellia128, encryptBlock Camellia.initKey128 Camellia.encrypt)
+	]
+
+katTests = map makeTests vectors
+	where makeTests (name, v, f) = testProperty name (and $ map makeTest v)
+		where makeTest (key,plaintext,expected) = assertEq expected $ f key plaintext
+
+assertEq expected got
+	| expected == got = True
+	| otherwise       = error ("expected: " ++ show expected ++ " got: " ++ show got)
diff --git a/Tests/tests.hs b/Tests/tests.hs
new file mode 100644
--- /dev/null
+++ b/Tests/tests.hs
@@ -0,0 +1,321 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+import Test.Framework (Test, defaultMain, testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Test.QuickCheck
+import Test.QuickCheck.Test
+import System.IO (hFlush, stdout)
+
+import Control.Monad
+import Control.Arrow (first)
+import Control.Applicative ((<$>))
+
+import Data.List (intercalate)
+import Data.Char
+import Data.Bits
+import Data.Word
+import qualified Data.Vector.Unboxed as V
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+-- for DSA
+import qualified Crypto.Hash.SHA1 as SHA1
+
+-- numbers
+{-
+import Number.ModArithmetic
+import Number.Basic
+import Number.Prime
+import Number.Serialize
+-}
+-- ciphers/Kexch
+import AES (aesTests)
+import qualified Crypto.Cipher.AES.Haskell as AES
+import qualified Crypto.Cipher.RSA as RSA
+import qualified Crypto.Cipher.DSA as DSA
+import qualified Crypto.Cipher.DH as DH
+import Crypto.Random
+import KAT
+
+{-
+prop_gcde_binary_valid (Positive a, Positive b) =
+	let (x,y,v)    = gcde_binary a b in
+	let (x',y',v') = gcde a b in
+	and [v==v', a*x' + b*y' == v', a*x + b*y == v, gcd a b == v]
+
+prop_modexp_rtl_valid (NonNegative a, NonNegative b, Positive m) =
+	exponantiation_rtl_binary a b m == ((a ^ b) `mod` m)
+
+prop_modinv_valid (Positive a, Positive m)
+	| m > 1 =
+		case inverse a m of
+			Just ainv -> (ainv * a) `mod` m == 1
+			Nothing   -> True
+	| otherwise       = True
+
+prop_sqrti_valid (Positive i) = l*l <= i && i <= u*u where (l, u) = sqrti i
+
+prop_generate_prime_valid i =
+	-- becuase of the next naive test, we can't generate easily number above 32 bits
+	-- otherwise it slows down the tests to uselessness. when AKS or ECPP is implemented
+	-- we can revisit the number here
+	let p = withAleasInteger rng i (\g -> generatePrime g 32) in
+	-- FIXME test if p is around 32 bits
+	primalityTestNaive p
+
+prop_miller_rabin_valid i
+	| i <= 3    = True
+	| otherwise =
+		-- miller rabin only returns with certitude that the integer is composite.
+		let b = withAleasInteger rng i (\g -> isProbablyPrime g i) in
+		(b == False && primalityTestNaive i == False) || b == True
+
+withAleasInteger rng i f = case reseed (i2osp (if i < 0 then -i else i)) rng of
+	Left _     -> error "impossible"
+	Right rng' -> case f rng' of
+		Left _  -> error "impossible"
+		Right v -> fst v
+-}
+
+newtype RSAMessage = RSAMessage B.ByteString deriving (Show, Eq)
+
+instance Arbitrary RSAMessage where
+	arbitrary = do
+		sz <- choose (0, 128 - 11)
+		ws <- replicateM sz (choose (0,255) :: Gen Int)
+		return $ RSAMessage $ B.pack $ map fromIntegral ws
+
+{- this is a just test rng. this is absolutely not a serious RNG. DO NOT use elsewhere -}
+data Rng = Rng (Int, Int)
+
+getByte :: Rng -> (Word8, Rng)
+getByte (Rng (mz, mw)) =
+	let mz2 = 36969 * (mz `mod` 65536) in
+	let mw2 = 18070 * (mw `mod` 65536) in
+	(fromIntegral (mz2 + mw2), Rng (mz2, mw2))
+
+getBytes 0 rng = ([], rng)
+getBytes n rng =
+	let (b, rng')  = getByte rng in
+	let (l, rng'') = getBytes (n-1) rng' in
+	(b:l, rng'')
+
+instance CryptoRandomGen Rng where
+	newGen _       = Right (Rng (2,3))
+	genSeedLength  = 0
+	genBytes len g = Right $ first B.pack $ getBytes len g
+	reseed bs (Rng (a,b)) = Right $ Rng (fromIntegral a', b) where
+		a' = ((fromIntegral a) + i * 36969) `mod` 65536
+		i = B.head bs
+
+rng = Rng (1,2) 
+
+{-----------------------------------------------------------------------------------------------}
+{- testing RSA -}
+{-----------------------------------------------------------------------------------------------}
+
+{-
+prop_rsa_generate_valid (Positive i, RSAMessage msgz) =
+	let keysz = 64 in
+	let (pub,priv) = withAleasInteger rng i (\g -> RSA.generate g keysz 65537) in
+	let msg = B.take (keysz - 11) msgz in
+	(RSA.private_p priv * RSA.private_q priv == RSA.private_n priv) &&
+	((RSA.private_d priv * RSA.public_e pub) `mod` ((RSA.private_p priv - 1) * (RSA.private_q priv - 1)) == 1) &&
+	(either Left (RSA.decrypt priv . fst) $ RSA.encrypt rng pub msg) == Right msg
+-}
+
+prop_rsa_valid fast (RSAMessage msg) =
+	(either Left (RSA.decrypt pk . fst) $ RSA.encrypt rng rsaPublickey msg) == Right msg
+	where pk       = if fast then rsaPrivatekey else rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
+
+prop_rsa_fast_valid  = prop_rsa_valid True
+prop_rsa_slow_valid  = prop_rsa_valid False
+
+prop_rsa_sign_valid fast (RSAMessage msg) = (either Left (\smsg -> verify msg smsg) $ sign msg) == Right True
+	where
+		verify   = RSA.verify (SHA1.hash) sha1desc rsaPublickey
+		sign     = RSA.sign (SHA1.hash) sha1desc pk
+		sha1desc = B.pack [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03, 0x02,0x1a,0x05,0x00,0x04,0x14]
+		pk       = if fast then rsaPrivatekey else rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
+
+prop_rsa_sign_fast_valid = prop_rsa_sign_valid True
+prop_rsa_sign_slow_valid = prop_rsa_sign_valid False
+
+rsaPrivatekey = RSA.PrivateKey
+	{ RSA.private_size = 128
+	, RSA.private_n    = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151
+	, RSA.private_d    = 133764127300370985476360382258931504810339098611363623122953018301285450176037234703101635770582297431466449863745848961134143024057267778947569638425565153896020107107895924597628599677345887446144410702679470631826418774397895304952287674790343620803686034122942606764275835668353720152078674967983573326257
+	, RSA.private_p    = 12909745499610419492560645699977670082358944785082915010582495768046269235061708286800087976003942261296869875915181420265794156699308840835123749375331319
+	, RSA.private_q    = 10860278066550210927914375228722265675263011756304443428318337179619069537063135098400347475029673115805419186390580990519363257108008103841271008948795129
+	, RSA.private_dP   = 5014229697614831746694710412330921341325464081424013940131184365711243776469716106024020620858146547161326009604054855316321928968077674343623831428796843
+	, RSA.private_dQ   = 3095337504083058271243917403868092841421453478127022884745383831699720766632624326762288333095492075165622853999872779070009098364595318242383709601515849
+	, RSA.private_qinv = 11136639099661288633118187183300604127717437440459572124866697429021958115062007251843236337586667012492941414990095176435990146486852255802952814505784196
+	}
+
+rsaPublickey = RSA.PublicKey
+	{ RSA.public_size = 128
+	, RSA.public_n    = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151
+	, RSA.public_e    = 65537
+	}
+
+{-----------------------------------------------------------------------------------------------}
+{- testing DSA -}
+{-----------------------------------------------------------------------------------------------}
+
+
+dsaParams = (p,g,q)
+	where
+		p = 0x00a8c44d7d0bbce69a39008948604b9c7b11951993a5a1a1fa995968da8bb27ad9101c5184bcde7c14fb79f7562a45791c3d80396cefb328e3e291932a17e22edd
+		g = 0x0bf9fe6c75d2367b88912b2252d20fdcad06b3f3a234b92863a1e30a96a123afd8e8a4b1dd953e6f5583ef8e48fc7f47a6a1c8f24184c76dba577f0fec2fcd1c
+		q = 0x0096674b70ef58beaaab6743d6af16bb862d18d119
+
+dsaPrivatekey = DSA.PrivateKey
+	{ DSA.private_params = dsaParams
+	, DSA.private_x      = 0x229bac7aa1c7db8121bfc050a3426eceae23fae8
+	}
+
+dsaPublickey = DSA.PublicKey
+	{ DSA.public_params = dsaParams
+	, DSA.public_y      = 0x4fa505e86e32922f1fa1702a120abdba088bb4be801d4c44f7fc6b9094d85cd52c429cbc2b39514e30909b31e2e2e0752b0fc05c1a7d9c05c3e52e49e6edef4c
+	}
+
+prop_dsa_valid (RSAMessage msg) =
+	case DSA.verify signature (SHA1.hash) dsaPublickey msg of
+		Left err -> False
+		Right b  -> b
+	where
+		Right (signature, rng') = DSA.sign rng (SHA1.hash) dsaPrivatekey msg
+
+{-----------------------------------------------------------------------------------------------}
+{- testing DH -}
+{-----------------------------------------------------------------------------------------------}
+instance Arbitrary DH.PrivateNumber where
+	arbitrary = fromIntegral <$> (suchThat (arbitrary :: Gen Integer) (\x -> x >= 1))
+
+prop_dh_valid (xa, xb) = sa == sb
+	where
+		sa = DH.getShared dhparams xa yb
+		sb = DH.getShared dhparams xb ya
+		yb = DH.generatePublic dhparams xb
+		ya = DH.generatePublic dhparams xa
+		dhparams = (11, 7)
+
+{-----------------------------------------------------------------------------------------------}
+{- testing AES -}
+{-----------------------------------------------------------------------------------------------}
+data AES128Message = AES128Message B.ByteString B.ByteString B.ByteString deriving (Show, Eq)
+data AES192Message = AES192Message B.ByteString B.ByteString B.ByteString deriving (Show, Eq)
+data AES256Message = AES256Message B.ByteString B.ByteString B.ByteString deriving (Show, Eq)
+
+arbitraryAES keysize = do
+	sz <- choose (1, 12)
+	ws <- replicateM (sz*16) (choose (0,255) :: Gen Int)
+	key <- replicateM keysize (choose (0,255) :: Gen Int)
+	iv  <- replicateM 16 (choose (0,255) :: Gen Int)
+	return (ws, key, iv)
+
+instance Arbitrary AES128Message where
+	arbitrary = do
+		(ws, key, iv) <- arbitraryAES 16
+		return $ AES128Message (B.pack $ map fromIntegral key)
+		                       (B.pack $ map fromIntegral iv)
+		                       (B.pack $ map fromIntegral ws)
+
+instance Arbitrary AES192Message where
+	arbitrary = do
+		(ws, key, iv) <- arbitraryAES 24
+		return $ AES192Message (B.pack $ map fromIntegral key)
+		                       (B.pack $ map fromIntegral iv)
+		                       (B.pack $ map fromIntegral ws)
+
+instance Arbitrary AES256Message where
+	arbitrary = do
+		(ws, key, iv) <- arbitraryAES 32
+		return $ AES256Message (B.pack $ map fromIntegral key)
+		                       (B.pack $ map fromIntegral iv)
+		                       (B.pack $ map fromIntegral ws)
+
+
+prop_ecb_valid k msg = AES.decrypt k (AES.encrypt k msg) == msg
+prop_cbc_valid k iv msg = AES.decryptCBC k iv (AES.encryptCBC k iv msg) == msg
+
+prop_aes128_ecb_valid (AES128Message key _ msg) =
+	let (Right k) = AES.initKey128 key in
+	prop_ecb_valid k msg
+
+prop_aes192_ecb_valid (AES192Message key _ msg) =
+	let (Right k) = AES.initKey192 key in
+	prop_ecb_valid k msg
+
+prop_aes256_ecb_valid (AES256Message key _ msg) =
+	let (Right k) = AES.initKey256 key in
+	prop_ecb_valid k msg
+
+prop_aes128_cbc_valid (AES128Message key iv msg) =
+	let (Right k) = AES.initKey128 key in
+	prop_cbc_valid k iv msg
+
+prop_aes192_cbc_valid (AES192Message key iv msg) =
+	let (Right k) = AES.initKey192 key in
+	prop_cbc_valid k iv msg
+
+prop_aes256_cbc_valid (AES256Message key iv msg) =
+	let (Right k) = AES.initKey256 key in
+	prop_cbc_valid k iv msg
+
+{-----------------------------------------------------------------------------------------------}
+{- main -}
+{-----------------------------------------------------------------------------------------------}
+
+symCipherExpectedTests = testGroup "symmetric cipher KAT" katTests
+
+symCipherMarshallTests = testGroup "symmetric cipher marshall"
+	[ testProperty "AES128 (ECB)" prop_aes128_ecb_valid
+	, testProperty "AES128 (CBC)" prop_aes128_cbc_valid
+	, testProperty "AES192 (ECB)" prop_aes192_ecb_valid
+	, testProperty "AES192 (CBC)" prop_aes192_cbc_valid
+	, testProperty "AES256 (ECB)" prop_aes256_ecb_valid
+	, testProperty "AES256 (CBC)" prop_aes256_cbc_valid
+	]
+
+asymEncryptionTests = testGroup "assymmetric cipher encryption"
+	[ testProperty "RSA (slow)" prop_rsa_slow_valid
+	, testProperty "RSA (fast)" prop_rsa_fast_valid
+	]
+
+asymSignatureTests = testGroup "assymmetric cipher signature"
+	[ testProperty "RSA (slow)" prop_rsa_sign_slow_valid
+	, testProperty "RSA (fast)" prop_rsa_sign_fast_valid
+	, testProperty "DSA" prop_dsa_valid
+	]
+
+asymOtherTests = testGroup "assymetric other tests"
+	[ testProperty "DH valid" prop_dh_valid
+	]
+
+arithmeticTests = testGroup "arithmetic"
+	[]
+
+{- run_test "RSA generate" prop_rsa_generate_valid -}
+
+tests :: [Test]
+tests =
+	[ symCipherExpectedTests
+	, symCipherMarshallTests
+	, testGroup "AES" aesTests
+	, arithmeticTests
+	, asymEncryptionTests
+	, asymSignatureTests
+	, asymOtherTests
+	]
+
+main = defaultMain tests
+{-
+	-- Number Tests
+	run_test "gcde binary valid" prop_gcde_binary_valid
+	run_test "exponantiation RTL valid" prop_modexp_rtl_valid
+	run_test "inverse valid" prop_modinv_valid
+	run_test "sqrt integer valid" prop_sqrti_valid
+	run_test "primality test Miller Rabin" prop_miller_rabin_valid
+	run_test "Generate prime" prop_generate_prime_valid
+	-}
diff --git a/cbits/aes/x86ni.c b/cbits/aes/x86ni.c
new file mode 100644
--- /dev/null
+++ b/cbits/aes/x86ni.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2012 Vincent Hanquez <vincent@snarc.org>
+ * 
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of his contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <wmmintrin.h>
+#include <tmmintrin.h>
+#include "aes.h"
+
+static __m128i aes_128_key_expansion(__m128i key, __m128i keygened)
+{
+	keygened = _mm_shuffle_epi32(keygened, _MM_SHUFFLE(3,3,3,3));
+	key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+	key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+	key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
+	return _mm_xor_si128(key, keygened);
+}
+
+void aes_generate_key128(aes_key128 *key, uint8_t *ikey)
+{
+	__m128i *k = (__m128i *) key->_data;
+	k[0] = _mm_loadu_si128((const __m128i*) ikey);
+
+#define AES_128_key_exp(K, RCON) aes_128_key_expansion(K, _mm_aeskeygenassist_si128(K, RCON))
+	k[1]  = AES_128_key_exp(k[0], 0x01);
+	k[2]  = AES_128_key_exp(k[1], 0x02);
+	k[3]  = AES_128_key_exp(k[2], 0x04);
+	k[4]  = AES_128_key_exp(k[3], 0x08);
+	k[5]  = AES_128_key_exp(k[4], 0x10);
+	k[6]  = AES_128_key_exp(k[5], 0x20);
+	k[7]  = AES_128_key_exp(k[6], 0x40);
+	k[8]  = AES_128_key_exp(k[7], 0x80);
+	k[9]  = AES_128_key_exp(k[8], 0x1B);
+	k[10] = AES_128_key_exp(k[9], 0x36);
+
+	/* generate decryption keys in reverse order.
+	 * k[10] is shared by last encryption and first decryption rounds
+	 * k[20] is shared by first encryption round (and is the original user key) */
+	k[11] = _mm_aesimc_si128(k[9]);
+	k[12] = _mm_aesimc_si128(k[8]);
+	k[13] = _mm_aesimc_si128(k[7]);
+	k[14] = _mm_aesimc_si128(k[6]);
+	k[15] = _mm_aesimc_si128(k[5]);
+	k[16] = _mm_aesimc_si128(k[4]);
+	k[17] = _mm_aesimc_si128(k[3]);
+	k[18] = _mm_aesimc_si128(k[2]);
+	k[19] = _mm_aesimc_si128(k[1]);
+}
+
+#define PRELOAD_ENC_KEYS(k) \
+	__m128i K0  = k[0]; __m128i K1  = k[1]; __m128i K2  = k[2]; __m128i K3  = k[3]; \
+	__m128i K4  = k[4]; __m128i K5  = k[5]; __m128i K6  = k[6]; __m128i K7  = k[7]; \
+	__m128i K8  = k[8]; __m128i K9  = k[9]; __m128i K10 = k[10];
+
+#define DO_ENC_BLOCK(m) \
+	m = _mm_xor_si128(m, K0); \
+	m = _mm_aesenc_si128(m, K1); \
+	m = _mm_aesenc_si128(m, K2); \
+	m = _mm_aesenc_si128(m, K3); \
+	m = _mm_aesenc_si128(m, K4); \
+	m = _mm_aesenc_si128(m, K5); \
+	m = _mm_aesenc_si128(m, K6); \
+	m = _mm_aesenc_si128(m, K7); \
+	m = _mm_aesenc_si128(m, K8); \
+	m = _mm_aesenc_si128(m, K9); \
+	m = _mm_aesenclast_si128(m, K10);
+
+#define PRELOAD_DEC_KEYS(k) \
+	__m128i K0  = k[10+0]; __m128i K1  = k[10+1]; __m128i K2  = k[10+2]; __m128i K3  = k[10+3]; \
+	__m128i K4  = k[10+4]; __m128i K5  = k[10+5]; __m128i K6  = k[10+6]; __m128i K7  = k[10+7]; \
+	__m128i K8  = k[10+8]; __m128i K9  = k[10+9]; __m128i K10 = k[0];
+
+#define DO_DEC_BLOCK(m) \
+	m = _mm_xor_si128(m, K0); \
+	m = _mm_aesdec_si128(m, K1); \
+	m = _mm_aesdec_si128(m, K2); \
+	m = _mm_aesdec_si128(m, K3); \
+	m = _mm_aesdec_si128(m, K4); \
+	m = _mm_aesdec_si128(m, K5); \
+	m = _mm_aesdec_si128(m, K6); \
+	m = _mm_aesdec_si128(m, K7); \
+	m = _mm_aesdec_si128(m, K8); \
+	m = _mm_aesdec_si128(m, K9); \
+	m = _mm_aesdeclast_si128(m, K10);
+
+void aes_encrypt(uint8_t *out, aes_key128 *key, uint8_t *in, uint32_t blocks)
+{
+	uint32_t i;
+	uint64_t _out[2] __attribute__((aligned(16)));
+	__m128i *k = (__m128i *) key->_data;
+
+	PRELOAD_ENC_KEYS(k);
+
+	for (i = 0; i < blocks; i++, in += 16, out += 16) {
+		__m128i m = _mm_loadu_si128((__m128i *) in);
+
+		DO_ENC_BLOCK(m);
+
+		_mm_store_si128((__m128i *) _out, m);
+		((uint64_t *) out)[0] = (_out[0]);
+		((uint64_t *) out)[1] = (_out[1]);
+	}
+}
+
+void aes_decrypt(uint8_t *out, aes_key128 *key, uint8_t *in, uint32_t blocks)
+{
+	uint32_t i;
+	uint64_t _out[2] __attribute__((aligned(16)));
+	__m128i *k = (__m128i *) key->_data;
+
+	PRELOAD_DEC_KEYS(k);
+
+	for (i = 0; i < blocks; i++, in += 16, out += 16) {
+		__m128i m = _mm_loadu_si128((__m128i *) in);
+
+		DO_DEC_BLOCK(m);
+
+		_mm_store_si128((__m128i *) _out, m);
+		((uint64_t *) out)[0] = (_out[0]);
+		((uint64_t *) out)[1] = (_out[1]);
+	}
+}
+
+void aes_encrypt_cbc(uint8_t *out, aes_key128 *key, uint8_t *_iv, uint8_t *in, uint32_t blocks)
+{
+	uint32_t i;
+	uint64_t _out[2] __attribute__((aligned(16)));
+	__m128i *k = (__m128i *) key->_data;
+	__m128i iv = _mm_loadu_si128((__m128i *) _iv);
+
+	PRELOAD_ENC_KEYS(k);
+
+	for (i = 0; i < blocks; i++, in += 16, out += 16) {
+		__m128i m = _mm_loadu_si128((__m128i *) in);
+		m = _mm_xor_si128(m, iv);
+
+		DO_ENC_BLOCK(m);
+
+		_mm_store_si128((__m128i *) _out, m);
+		iv = m;
+		((uint64_t *) out)[0] = (_out[0]);
+		((uint64_t *) out)[1] = (_out[1]);
+	}
+}
+
+void aes_decrypt_cbc(uint8_t *out, aes_key128 *key, uint8_t *_iv, uint8_t *in, uint32_t blocks)
+{
+	uint32_t i;
+	uint64_t _out[2] __attribute__((aligned(16)));
+	__m128i *k = (__m128i *) key->_data;
+	__m128i iv = _mm_loadu_si128((__m128i *) _iv);
+
+	PRELOAD_DEC_KEYS(k);
+
+	for (i = 0; i < blocks; i++, in += 16, out += 16) {
+		__m128i m = _mm_loadu_si128((__m128i *) in);
+		__m128i ivnext = m;
+
+		DO_DEC_BLOCK(m);
+		m = _mm_xor_si128(m, iv);
+
+		_mm_store_si128((__m128i *) _out, m);
+		iv = ivnext;
+		((uint64_t *) out)[0] = (_out[0]);
+		((uint64_t *) out)[1] = (_out[1]);
+	}
+}
diff --git a/cryptocipher.cabal b/cryptocipher.cabal
--- a/cryptocipher.cabal
+++ b/cryptocipher.cabal
@@ -1,5 +1,5 @@
 Name:                cryptocipher
-Version:             0.3.0
+Version:             0.3.1
 Description:         Symmetrical Block, Stream and PubKey Ciphers
 License:             BSD3
 License-file:        LICENSE
@@ -10,20 +10,22 @@
 Category:            Cryptography
 Build-Type:          Simple
 Homepage:            http://github.com/vincenthz/hs-cryptocipher
-Cabal-Version:       >=1.6
-
-Flag test
-  Description:       Build unit test
-  Default:           False
+Cabal-Version:       >=1.8
+Extra-Source-Files:  Tests/*.hs
 
 Flag benchmark
   Description:       Build benchmarks
   Default:           False
 
+Flag aesni
+  Description:       Use fast aesni operations that are cpu dependant
+  Default:           False
+
 Library
   Build-Depends:     base >= 4 && < 5
                    , bytestring
                    , vector >= 0.7
+                   , cpu >= 0.1 && < 0.2
                    , ghc-prim
                    , primitive
                    , crypto-api >= 0.5
@@ -32,6 +34,7 @@
                    , cereal
   Exposed-modules:   Crypto.Cipher.RC4
                      Crypto.Cipher.AES
+                     Crypto.Cipher.AES.Haskell
                      Crypto.Cipher.Camellia
                      Crypto.Cipher.RSA
                      Crypto.Cipher.DSA
@@ -42,42 +45,45 @@
                      Number.Basic
                      Number.Polynomial
                      Number.Prime
-                     System.Endian
   ghc-options:       -Wall
-  if arch(ppc) || arch(sparc)
-    cpp-options: -DBIG_ENDIAN
-  else
-    cpp-options: -DLITTLE_ENDIAN
+  if flag(aesni)
+    cpp-options:     -DHAVE_AESNI
+    Exposed-modules: Crypto.Cipher.AES.X86NI
+    C-sources:       cbits/aes/x86ni.c
+    CC-options:      -mssse3 -maes
 
-Executable           Tests
-  Main-Is:           Tests.hs
-  if flag(test)
-    Buildable:       True
-    Build-depends:   base >= 4 && < 5
-                   , HUnit
+Test-Suite test-cryptocipher
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    Tests
+  Main-Is:           tests.hs
+  Build-depends:     base >= 4 && < 5
+                   , crypto-api >= 0.5
+                   , cryptocipher
                    , bytestring
                    , cryptohash
+                   , vector
+                   , entropy
                    , QuickCheck >= 2
-  else
-    Buildable:       False
-  if arch(ppc) || arch(sparc)
-    cpp-options: -DBIG_ENDIAN
-  else
-    cpp-options: -DLITTLE_ENDIAN
+                   , test-framework >= 0.3.3 && < 0.7
+                   , test-framework-quickcheck2 >= 0.2.9 && < 0.3
 
 Executable           Benchmarks
+  hs-source-dirs:    Benchmarks
   Main-Is:           Benchmarks.hs
   if flag(benchmark)
     Buildable:       True
     Build-depends:   base >= 4 && < 5
+                   , bytestring
+                   , crypto-api
+                   , cryptocipher
                    , criterion
                    , mtl
   else
     Buildable:       False
-  if arch(ppc) || arch(sparc)
-    cpp-options: -DBIG_ENDIAN
-  else
-    cpp-options: -DLITTLE_ENDIAN
+  if flag(aesni)
+    cpp-options:     -DHAVE_AESNI
+    C-sources:       cbits/aes/x86ni.c
+    CC-options:      -mssse3 -maes
 
 source-repository head
   type:     git
