diff --git a/AUTHORS b/AUTHORS
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,2 +1,2 @@
-Thomas DuBuisson: AES trampolines, Haskell code
-Vincent Hanquez: AES algorithms for x86 and NI
+Thomas DuBuisson: Haskell code, refactoring of aes C code (extract mutable portion of GCM struct)
+Vincent Hanquez: C AES implementations for x86 and NI
diff --git a/Benchmark/bench.hs b/Benchmark/bench.hs
--- a/Benchmark/bench.hs
+++ b/Benchmark/bench.hs
@@ -1,6 +1,7 @@
 import Crypto.Cipher.AES128
 import Crypto.Cipher.AES
 import Crypto.Classes
+import Crypto.Types
 import Crypto.Modes (zeroIV)
 import Criterion
 import Criterion.Main
@@ -9,12 +10,14 @@
 import qualified Data.ByteString as B
 
 main = do
-    let iv = zeroIV
-        ivV = IV (B.replicate 16 0)
-    pt <- getEntropy (2^12)
-    k  <- buildKeyIO :: IO AESKey
-    let kV = initKey (B.pack [0..15])
+    let iv  = zeroIV
+        ivV = B.replicate 16 0
+    pt <- getEntropy (2^16)
+    k  <- buildKeyIO :: IO AESKey128
+    let kV = initAES (B.pack [0..15])
     defaultMain
-        [ bench "aes-ctr cipher-aes128" $ nf (fst . ctr k iv) pt
-        , bench "aes-ctr cipher-aes" $ nf (encryptCTR kV ivV) pt
-        , bench "aes-gcm cipher-aes" $ nf (encryptGCM kV ivV B.empty) pt]
+        [ bench "aes-ecb cipher-aes128" $ nf (encryptBlock k) pt
+        , bench "aes-ctr cipher-aes128" $ nf (fst . ctr k iv) pt
+        , bench "aes-ecb cipher-aes"    $ nf (encryptECB kV) pt
+        , bench "aes-ctr cipher-aes"    $ nf (encryptCTR kV ivV) pt
+        , bench "aes-gcm cipher-aes"    $ nf (fst . encryptGCM kV ivV B.empty) pt]
diff --git a/Crypto/Cipher/AES128.hs b/Crypto/Cipher/AES128.hs
--- a/Crypto/Cipher/AES128.hs
+++ b/Crypto/Cipher/AES128.hs
@@ -1,11 +1,11 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Crypto.Cipher.AES128
-  ( AESKey
+  ( AESKey128, AESKey192, AESKey256
+  , BlockCipher(..), buildKeyIO, zeroIV
   ) where
 
 import Crypto.Cipher.AES128.Internal
 import Crypto.Classes
-import Crypto.Types
 import Control.Monad (when)
 import Data.Serialize
 import Data.Tagged
@@ -16,9 +16,9 @@
 import qualified Data.ByteString.Internal as B
 import qualified Data.ByteString.Unsafe as B
 
-instance Serialize AESKey where
+instance Serialize AESKey128 where
     put k = do
-        let RKey l h = (rawKey k)
+        let RKey128 l h = (rawKey128 k)
         putWord64be h
         putWord64be l
 
@@ -28,14 +28,39 @@
             Nothing -> fail "Invalid key on 'get'"
             Just k  -> return k
 
-instance BlockCipher AESKey where
+instance Serialize AESKey192 where
+    put k = do
+        let RKey192 a b c = (rawKey192 k)
+        putWord64be c
+        putWord64be b
+        putWord64be a
+
+    get = do
+        b <- getByteString 24
+        case buildKey b of
+            Nothing -> fail "Invalid key on 'get'"
+            Just k  -> return k
+
+instance Serialize AESKey256 where
+    put k = do
+        let RKey256 a b c d = (rawKey256 k)
+        putWord64be d
+        putWord64be c
+        putWord64be b
+        putWord64be a
+
+    get = do
+        b <- getByteString 32
+        case buildKey b of
+            Nothing -> fail "Invalid key on 'get'"
+            Just k  -> return k
+
+instance BlockCipher AESKey128 where
     blockSize = Tagged 128
     keyLength = Tagged 128
     buildKey bs
       | B.length bs >= 16 = unsafePerformIO $
-          B.unsafeUseAsCString bs $ \ptr -> do
-            k <- generateKey (castPtr ptr)
-            return (Just k)
+          B.unsafeUseAsCString bs (\p -> generateKey128 (castPtr p))
       | otherwise = Nothing
     encryptBlock k b = unsafePerformIO $ do
         B.unsafeUseAsCStringLen b $ \(inP,len) -> do
@@ -45,6 +70,8 @@
         B.unsafeUseAsCStringLen b $ \(inP,len) -> do
          B.create (B.length b) $ \outP -> do
             decryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize)
+    ecb   = encryptBlock
+    unEcb = decryptBlock
     ctr k (IV bs) pt = unsafePerformIO $ do
         B.unsafeUseAsCStringLen pt $ \(inP, len) -> do
          B.unsafeUseAsCStringLen bs $ \(ivP, ivLen) -> do
@@ -55,7 +82,68 @@
                 encryptCTR k (castPtr ivP) (castPtr newIVP) (castPtr outP) (castPtr inP) len
             let newIV = B.fromForeignPtr newIVFP 0 ivLen
             return (ct,IV newIV)
+    {-# INLINE ctr #-}
     unCtr = ctr
 
 blkSize :: Int
 blkSize = 16
+
+instance BlockCipher AESKey192 where
+    blockSize = Tagged 128
+    keyLength = Tagged 192
+    buildKey bs
+      | B.length bs >= 16 = unsafePerformIO $
+          B.unsafeUseAsCString bs (\p -> generateKey192 (castPtr p))
+      | otherwise = Nothing
+    encryptBlock k b = unsafePerformIO $ do
+        B.unsafeUseAsCStringLen b $ \(inP,len) -> do
+         B.create (B.length b) $ \outP -> do
+            encryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize)
+    decryptBlock k b = unsafePerformIO $ do
+        B.unsafeUseAsCStringLen b $ \(inP,len) -> do
+         B.create (B.length b) $ \outP -> do
+            decryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize)
+    ecb   = encryptBlock
+    unEcb = decryptBlock
+    ctr k (IV bs) pt = unsafePerformIO $ do
+        B.unsafeUseAsCStringLen pt $ \(inP, len) -> do
+         B.unsafeUseAsCStringLen bs $ \(ivP, ivLen) -> do
+            when (ivLen /= (blockSizeBytes .::. k))
+                (error "Cipher-AES128: IV wrong length!  They type system would have/should have caught this if you didn't use the IV constructor...")
+            newIVFP <- B.mallocByteString ivLen
+            ct <- B.create len $ \outP -> withForeignPtr newIVFP $ \newIVP -> do
+                encryptCTR k (castPtr ivP) (castPtr newIVP) (castPtr outP) (castPtr inP) len
+            let newIV = B.fromForeignPtr newIVFP 0 ivLen
+            return (ct,IV newIV)
+    {-# INLINE ctr #-}
+    unCtr = ctr
+
+instance BlockCipher AESKey256 where
+    blockSize = Tagged 128
+    keyLength = Tagged 256
+    buildKey bs
+      | B.length bs >= 16 = unsafePerformIO $
+          B.unsafeUseAsCString bs (\p -> generateKey256 (castPtr p))
+      | otherwise = Nothing
+    encryptBlock k b = unsafePerformIO $ do
+        B.unsafeUseAsCStringLen b $ \(inP,len) -> do
+         B.create (B.length b) $ \outP -> do
+            encryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize)
+    decryptBlock k b = unsafePerformIO $ do
+        B.unsafeUseAsCStringLen b $ \(inP,len) -> do
+         B.create (B.length b) $ \outP -> do
+            decryptECB k (castPtr outP) (castPtr inP) (len`div`blkSize)
+    ecb   = encryptBlock
+    unEcb = decryptBlock
+    ctr k (IV bs) pt = unsafePerformIO $ do
+        B.unsafeUseAsCStringLen pt $ \(inP, len) -> do
+         B.unsafeUseAsCStringLen bs $ \(ivP, ivLen) -> do
+            when (ivLen /= (blockSizeBytes .::. k))
+                (error "Cipher-AES128: IV wrong length!  They type system would have/should have caught this if you didn't use the IV constructor...")
+            newIVFP <- B.mallocByteString ivLen
+            ct <- B.create len $ \outP -> withForeignPtr newIVFP $ \newIVP -> do
+                encryptCTR k (castPtr ivP) (castPtr newIVP) (castPtr outP) (castPtr inP) len
+            let newIV = B.fromForeignPtr newIVFP 0 ivLen
+            return (ct,IV newIV)
+    {-# INLINE ctr #-}
+    unCtr = ctr
diff --git a/Crypto/Cipher/AES128/Internal.hs b/Crypto/Cipher/AES128/Internal.hs
--- a/Crypto/Cipher/AES128/Internal.hs
+++ b/Crypto/Cipher/AES128/Internal.hs
@@ -1,14 +1,17 @@
-{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
+{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls, ViewPatterns #-}
 module Crypto.Cipher.AES128.Internal
-        ( AESKey(..), RawKey(..), GCM(..)
-        , generateKey, generateGCM
+        ( AESKey128(..), AESKey192(..), AESKey256(..), RawKey128(..), RawKey192(..), RawKey256(..), GCM(..)
+        , generateKey128, generateKey192, generateKey256
+        , generateGCM
         , encryptECB
         , decryptECB
-        , encryptGCM
-        , decryptGCM
-        , finishGCM
         , encryptCTR
         , decryptCTR
+        , encryptGCM, decryptGCM
+        -- * Piece-meal functions
+        , cipherOnlyGCM
+        , decipherOnlyGCM
+        , finishGCM, aadGCM
         ) where
 
 import Foreign.Ptr
@@ -21,81 +24,152 @@
 -- AES Bindings
 data AESKeyStruct
 type AESKeyPtr = Ptr AESKeyStruct
-data RawKey = RKey { lowK,highK :: {-# UNPACK #-} !Word64 }
-data AESKey = AESKey { rawKey      :: !RawKey
-                     , expandedKey :: ForeignPtr AESKeyStruct }
 
+data RawKey128 = RKey128 { lowK128,highK128 :: {-# UNPACK #-} !Word64 }
+data AESKey128 = AESKey128 { rawKey128      :: !RawKey128
+                           , expandedKey128 :: ForeignPtr AESKeyStruct }
+
+data RawKey192 = RKey192 { lowK192,midK192,highK192 :: {-# UNPACK #-} !Word64 }
+data AESKey192 = AESKey192 { rawKey192      :: !RawKey192
+                           , expandedKey192 :: ForeignPtr AESKeyStruct }
+
+data RawKey256 = RKey256 { aK256,bK256,cK256,dK256 :: {-# UNPACK #-} !Word64 }
+data AESKey256 = AESKey256 { rawKey256      :: !RawKey256
+                           , expandedKey256 :: ForeignPtr AESKeyStruct }
+
+class GetExpanded a where
+    expandedKey :: a -> ForeignPtr AESKeyStruct
+
+instance GetExpanded AESKey256 where
+    expandedKey = expandedKey256
+instance GetExpanded AESKey192 where
+    expandedKey = expandedKey192
+instance GetExpanded AESKey128 where
+    expandedKey = expandedKey128
+
 type AESGcmPtr = Ptr GCMStruct
 data GCMStruct
-data GCM = GCM { _gcmFP  :: ForeignPtr GCMStruct }
+data GCM k = GCM { _gcmFP   :: GCMpc
+                 , _keyFP   :: k
+                 , _ctxFP2  :: ForeignPtr CTXStruct
+                 }
 
-foreign import ccall unsafe "aes/aes.h generate_key128"
-        c_generate_key128 :: AESKeyPtr -> Ptr Word8 -> IO ()
+newtype GCMpc = GCMpc { unGCMpc :: ForeignPtr GCMStruct }
 
-foreign import ccall unsafe "aes/aes.h allocate_key128"
-        c_allocate_key128 :: IO AESKeyPtr
+type AESCtxPtr = Ptr CTXStruct
+data CTXStruct
+-- data CTX = CTX { _ctxFP :: ForeignPtr CTXStruct }
 
-foreign import ccall unsafe "aes/aes.h &free_key128"
-        c_free_key128 :: FunPtr (AESKeyPtr -> IO ())
+foreign import ccall unsafe "aes.h tmd_aes_initkey"
+        c_aes_initkey :: AESKeyPtr -> Ptr Word8 -> Word8 -> IO ()
 
-foreign import ccall unsafe "aes/aes.h free_key128"
-        c_key128_free :: AESKeyPtr -> IO ()
+foreign import ccall unsafe "aes.h tmd_allocatekey"
+        c_allocate_key :: IO AESKeyPtr
 
-foreign import ccall unsafe "aes/aes.h encrypt_ecb"
-        c_encrypt_ecb :: AESKeyPtr -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()
+foreign import ccall unsafe "aes.h &tmd_freekey"
+        c_free_key :: FunPtr (AESKeyPtr -> IO ())
 
-foreign import ccall unsafe "aes/aes.h decrypt_ecb"
-        c_decrypt_ecb :: AESKeyPtr -> Ptr Word8 -> Ptr Word8 -> Word32 -> IO ()
+-- foreign import ccall unsafe "aes.h tmd_freekey"
+--         c_key_free :: AESKeyPtr -> IO ()
 
-foreign import ccall unsafe "aes/aes.h allocate_gcm"
-    c_gcm_allocate :: IO AESGcmPtr
+foreign import ccall unsafe "aes.h tmd_allocatectx"
+        c_allocate_ctx :: IO AESCtxPtr
 
-foreign import ccall unsafe "aes/aes.h aes_gcm_init"
+foreign import ccall unsafe "aes.h &tmd_freectx"
+        c_free_ctx :: FunPtr (AESCtxPtr -> IO ())
+
+-- foreign import ccall unsafe "aes.h tmd_freectx"
+--         c_ctx_free :: AESCtxPtr -> IO ()
+
+foreign import ccall unsafe "aes.h tmd_allocategcm"
+        c_allocate_gcm :: IO AESGcmPtr
+
+foreign import ccall unsafe "aes.h &tmd_freegcm"
+        c_free_gcm :: FunPtr (AESGcmPtr -> IO ())
+
+-- foreign import ccall unsafe "aes.h tmd_freegcm"
+--         c_gcm_free :: AESGcmPtr -> IO ()
+
+foreign import ccall unsafe "aes.h tmd_aes_gcm_init"
     c_gcm_init :: AESGcmPtr
                -> AESKeyPtr
+               -> IO ()
+
+foreign import ccall unsafe "aes.h tmd_aes_ctx_init"
+    c_ctx_init :: AESGcmPtr
+               -> AESCtxPtr
+               -> AESKeyPtr
                -> Ptr Word8 -> Word32 -- ^ IV and length
                -> IO ()
 
-foreign import ccall unsafe "aes/aes.h aes_gcm_enc_finish"
-    c_gcm_encrypt :: Ptr Word8           -- Output
-                  -> Ptr Word8           -- Tag
+
+foreign import ccall unsafe "aes.h tmd_aes_encrypt_ecb"
+        c_encrypt_ecb :: Ptr Word8 -> AESKeyPtr -> Ptr Word8 -> Word32 -> IO ()
+
+foreign import ccall unsafe "aes.h tmd_aes_decrypt_ecb"
+        c_decrypt_ecb :: Ptr Word8 -> AESKeyPtr -> Ptr Word8 -> Word32 -> IO ()
+
+
+foreign import ccall unsafe "aes.h tmd_aes_gcm_finish"
+    c_gcm_finish  :: Ptr Word8           -- Tag
                   -> AESGcmPtr
-                  -> Ptr Word8 -> Word32 -- IV and length
-                  -> Ptr Word8 -> Word32 -- PT and length
-                  -> Ptr Word8 -> Word32 -- AAD and length
+                  -> AESKeyPtr           -- Key
+                  -> AESCtxPtr           -- Context
                   -> IO ()
 
-foreign import ccall unsafe "aes/aes.h aes_gcm_dec_finish"
+foreign import ccall unsafe "aes.h tmd_aes_gcm_aad"
+    c_gcm_aad     :: AESGcmPtr
+                  -> AESCtxPtr
+                  -> Ptr Word8 -> Word32 -- AAD, len
+                  -> IO ()
+
+foreign import ccall unsafe "aes.h tmd_aes_gcm_decrypt"
     c_gcm_decrypt :: Ptr Word8           -- Output
-                  -> Ptr Word8           -- Tag
                   -> AESGcmPtr
-                  -> Ptr Word8 -> Word32 -- IV and length
-                  -> Ptr Word8 -> Word32 -- PT and length
-                  -> Ptr Word8 -> Word32 -- AAD and length
+                  -> AESCtxPtr
+                  -> AESKeyPtr
+                  -> Ptr Word8 -> Word32 -- CT and length
                   -> IO ()
 
-foreign import ccall unsafe "aes/aes.h allocate_gcm"
-    c_gcm_finish :: Ptr Word8 -> AESGcmPtr -> IO ()
+foreign import ccall unsafe "aes.h tmd_aes_gcm_encrypt"
+    c_gcm_encrypt :: Ptr Word8           -- Output
+                  -> AESGcmPtr
+                  -> AESCtxPtr
+                  -> AESKeyPtr
+                  -> Ptr Word8 -> Word32 -- PT and length
+                  -> IO ()
 
-foreign import ccall unsafe "aes/aes.h &free_gcm"
-    c_free_gcm :: FunPtr (AESGcmPtr -> IO ())
+foreign import ccall unsafe "aes.h tmd_aes_gcm_full_encrypt"
+    c_gcm_full_encrypt :: AESKeyPtr -> AESGcmPtr
+                       -> Ptr Word8 -> Word32           -- IV, IVLen
+                       -> Ptr Word8 -> Word32           -- AAD, AADLen
+                       -> Ptr Word8 -> Word32           -- PT, PTLen
+                       -> Ptr Word8                     -- CT
+                       -> Ptr Word8                     -- Tag
+                       -> IO ()
 
-foreign import ccall unsafe "aes/aes.h free_gcm"
-    _c_gcm_free :: AESGcmPtr -> IO ()
+foreign import ccall unsafe "aes.h tmd_aes_gcm_full_decrypt"
+    c_gcm_full_decrypt :: AESKeyPtr -> AESGcmPtr
+                       -> Ptr Word8 -> Word32           -- IV, IVLen
+                       -> Ptr Word8 -> Word32           -- AAD, AADLen
+                       -> Ptr Word8 -> Word32           -- PT, PTLen
+                       -> Ptr Word8                     -- CT
+                       -> Ptr Word8                     -- Tag
+                       -> IO ()
 
-foreign import ccall unsafe "aes/aes.h encrypt_ctr"
-    c_encrypt_ctr :: AESKeyPtr
+foreign import ccall unsafe "aes.h tmd_aes_encrypt_ctr"
+    c_encrypt_ctr :: Ptr Word8 -- ^ Output
+                  -> AESKeyPtr
                   -> Ptr Word8 -- ^ 128 bit IV
                   -> Ptr Word8 -- ^ 128 bit new IV
-                  -> Ptr Word8 -- ^ Result
                   -> Ptr Word8 -- ^ Input
                   -> Word32    -- ^ Input length in bytes
                   -> IO ()
 
-c_decrypt_ctr :: AESKeyPtr
+c_decrypt_ctr :: Ptr Word8 -- ^ Result
+              -> AESKeyPtr
               -> Ptr Word8 -- ^ 128 bit IV
               -> Ptr Word8 -- ^ 128 bit new IV
-              -> Ptr Word8 -- ^ Result
               -> Ptr Word8 -- ^ Input
               -> Word32    -- ^ Input length in bytes
               -> IO ()
@@ -105,17 +179,18 @@
 blkSzC = 16
 
 -- Given a 16 byte buffer, allocate and return an AESKey
-generateKey :: Ptr Word64 -- ^ Buffer of 16 bytes of key material
-            -> IO AESKey
-generateKey keyPtr  = do
+generateKey128 :: Ptr Word64
+            -- ^ Buffer of 16 bytes of key material
+            -> IO (Maybe AESKey128)
+generateKey128 keyPtr = do
     raw <- do
             a <- peekLE (castPtr keyPtr)
             let keyPtr2 = (castPtr keyPtr) `plusPtr` sizeOf a
             b <- peekLE keyPtr2
-            return (RKey b a)
-    k <- c_allocate_key128
-    c_generate_key128 k (castPtr keyPtr)
-    fmap (AESKey raw) (newForeignPtr c_free_key128 k)
+            return (RKey128 b a)
+    k <- c_allocate_key
+    c_aes_initkey k (castPtr keyPtr) 16
+    fmap (Just . AESKey128 raw) (newForeignPtr c_free_key k)
  where
      peekLE :: Ptr Word8 -> IO Word64
      peekLE p = do
@@ -132,92 +207,197 @@
                 (f a4 32) .|. (f a5 24) .|. (f a6 16) .|.
                 (f a7 8)  .|. fromIntegral a8
         return a
-{-# INLINE generateKey #-}
+{-# INLINE generateKey128 #-}
 
+-- Given a 16 byte buffer, allocate and return an AESKey
+generateKey192 :: Ptr Word64
+            -- ^ Buffer of 16 bytes of key material
+            -> IO (Maybe AESKey192)
+generateKey192 keyPtr = do
+    raw <- do
+            a <- peekLE (castPtr keyPtr)
+            let keyPtr2 = (castPtr keyPtr) `plusPtr` sizeOf a
+            b <- peekLE keyPtr2
+            let keyPtr3 = (castPtr keyPtr) `plusPtr` sizeOf a `plusPtr` sizeOf b
+            c <- peekLE keyPtr3
+            return (RKey192 c b a)
+    k <- c_allocate_key
+    c_aes_initkey k (castPtr keyPtr) 24
+    fmap (Just . AESKey192 raw) (newForeignPtr c_free_key k)
+ where
+     peekLE :: Ptr Word8 -> IO Word64
+     peekLE p = do
+        a1 <- peekElemOff p 0
+        a2 <- peekElemOff p 1
+        a3 <- peekElemOff p 2
+        a4 <- peekElemOff p 3
+        a5 <- peekElemOff p 4
+        a6 <- peekElemOff p 5
+        a7 <- peekElemOff p 6
+        a8 <- peekElemOff p 7
+        let f n s = fromIntegral n `shiftL` s
+        let a = (f a1 56) .|. (f a2 48) .|. (f a3 40) .|.
+                (f a4 32) .|. (f a5 24) .|. (f a6 16) .|.
+                (f a7 8)  .|. fromIntegral a8
+        return a
+{-# INLINE generateKey192 #-}
+
+-- Given a 16 byte buffer, allocate and return an AESKey
+generateKey256 :: Ptr Word64
+            -- ^ Buffer of 16 bytes of key material
+            -> IO (Maybe AESKey256)
+generateKey256 keyPtr = do
+    raw <- do
+            a <- peekLE (castPtr keyPtr)
+            let keyPtr2 = (castPtr keyPtr) `plusPtr` sizeOf a
+            b <- peekLE keyPtr2
+            let keyPtr3 = (castPtr keyPtr) `plusPtr` sizeOf a `plusPtr` sizeOf b
+            c <- peekLE keyPtr3
+            let keyPtr4 = (castPtr keyPtr) `plusPtr` sizeOf a `plusPtr` sizeOf b `plusPtr` sizeOf c
+            d <- peekLE keyPtr4
+            return (RKey256 d c b a)
+    k <- c_allocate_key
+    c_aes_initkey k (castPtr keyPtr) 32
+    fmap (Just . AESKey256 raw) (newForeignPtr c_free_key k)
+ where
+     peekLE :: Ptr Word8 -> IO Word64
+     peekLE p = do
+        a1 <- peekElemOff p 0
+        a2 <- peekElemOff p 1
+        a3 <- peekElemOff p 2
+        a4 <- peekElemOff p 3
+        a5 <- peekElemOff p 4
+        a6 <- peekElemOff p 5
+        a7 <- peekElemOff p 6
+        a8 <- peekElemOff p 7
+        let f n s = fromIntegral n `shiftL` s
+        let a = (f a1 56) .|. (f a2 48) .|. (f a3 40) .|.
+                (f a4 32) .|. (f a5 24) .|. (f a6 16) .|.
+                (f a7 8)  .|. fromIntegral a8
+        return a
+{-# INLINE generateKey256 #-}
+
 -- Given a 16 byte buffer, allocate and return an key expansion useful for
 -- GCM
-generateGCM :: Ptr Word64 -- ^ Buffer of 16 bytes of key material
-            -> IO GCM
-generateGCM keyPtr  = do
-    g <- c_gcm_allocate
-    k <- c_allocate_key128
-    c_generate_key128 k (castPtr keyPtr)
-    allocaBytes 12 $ \ivPtr -> do
-        mapM_ (\i -> pokeElemOff ivPtr i (0::Word8)) [0..11]
-        c_gcm_init g k ivPtr 12
-    c_key128_free k
-    fmap GCM (newForeignPtr c_free_gcm g)
+generateGCM :: GetExpanded k
+            => k
+            -> IO (Maybe (GCM k))
+generateGCM keyStruct
+    | otherwise                   = do
+    withForeignPtr (expandedKey keyStruct) $ \k -> do
+      g <- c_allocate_gcm
+      c <- c_allocate_ctx
+      allocaBytes 12 $ \ivPtr -> do
+          mapM_ (\i -> pokeElemOff ivPtr i (0::Word8)) [0..11]
+          c_gcm_init g k
+          c_ctx_init g c k ivPtr 12
+      gFP <- newForeignPtr c_free_gcm g
+      cFP <- newForeignPtr c_free_ctx c
+      return (Just $ GCM (GCMpc gFP) keyStruct cFP)
 {-# INLINE generateGCM #-}
 
 -- An encrypt function that can handle up to blks < maxBound `div` 16 :: Word32
 -- simultaneous blocks.
-encryptECB :: AESKey    -- ^ The key
+encryptECB :: GetExpanded k
+           => k         -- ^ The key
            -> Ptr Word8 -- ^ The result buffer
            -> Ptr Word8 -- ^ The source buffer
            -> Int       -- ^ The input size in blocks
            -> IO ()
-encryptECB (AESKey _ k) dst src blks = withForeignPtr k $ \p -> c_encrypt_ecb p dst src (fromIntegral blks)
+encryptECB (expandedKey -> k) dst src blks = withForeignPtr k $ \p -> c_encrypt_ecb dst p src (fromIntegral blks)
 {-# INLINE encryptECB #-}
 
-decryptECB :: AESKey    -- ^ The key
+decryptECB :: GetExpanded k
+           => k         -- ^ The key
            -> Ptr Word8 -- ^ The result buffer
            -> Ptr Word8 -- ^ The source buffer
            -> Int       -- ^ The input size in blocks
            -> IO ()
-decryptECB (AESKey _ k) dst src blks
+decryptECB (expandedKey -> k) dst src blks
   | blks > fromIntegral (maxBound `div` blkSzC :: Word32) = error "Can not decrypt so many blocks at once"
-  | otherwise = withForeignPtr k $ \p -> c_decrypt_ecb p dst src (fromIntegral blks)
+  | otherwise = withForeignPtr k $ \p -> c_decrypt_ecb dst p src (fromIntegral blks)
 {-# INLINE decryptECB #-}
 
-encryptGCM :: GCM
-           -> Ptr Word8 -> Int  -- IV and length
-           -> Ptr Word8 -> Int  -- PT and length
-           -> Ptr Word8 -> Int  -- AAD and length
-           -> Ptr Word8         -- CT  (length assumed to match PT)
-           -> Ptr Word8         -- Tag (length assumed to match blocksize)
-           -> IO ()
-encryptGCM (GCM k) iv ivlen pt ptlen aad aadlen ct tg = withForeignPtr k $ \g ->
-    c_gcm_encrypt ct tg g iv  (fromIntegral ivlen)
-                          pt  (fromIntegral ptlen)
-                          aad (fromIntegral aadlen)
+aadGCM :: GetExpanded k => GCM k -> Ptr Word8 -> Int -> IO ()
+aadGCM gcm aad aadLen = withForeignGCM gcm $ \(g,_k,c) ->
+    c_gcm_aad g c aad (fromIntegral aadLen)
 
-decryptGCM :: GCM
-           -> Ptr Word8 -> Int  -- IV and length
-           -> Ptr Word8 -> Int  -- CT and length
-           -> Ptr Word8 -> Int  -- AAD and length
-           -> Ptr Word8         -- PT (length assumed to match PT)
-           -> Ptr Word8         -- Tag (length assumed to match blocksize)
-           -> IO ()
-decryptGCM (GCM k) iv ivlen ct ctlen aad aadlen pt tg = withForeignPtr k $ \g ->
-    c_gcm_decrypt pt tg g
-                       iv  (fromIntegral ivlen)
-                       ct  (fromIntegral ctlen)
-                       aad (fromIntegral aadlen)
+cipherOnlyGCM :: GetExpanded k
+              => GCM k
+              -> Ptr Word8         -- CT  (length assumed to match PT)
+              -> Ptr Word8 -> Int  -- PT and length
+              -> IO ()
+cipherOnlyGCM gcm ct pt ptlen = withForeignGCM gcm $ \(g,k,c) ->
+    c_gcm_encrypt ct g c k pt  (fromIntegral ptlen)
 
-finishGCM :: GCM
+decipherOnlyGCM :: GetExpanded k
+                => GCM k
+                -> Ptr Word8         -- PT (length assumed to match CT)
+                -> Ptr Word8 -> Int  -- CT and length
+                -> IO ()
+decipherOnlyGCM gcm pt ct ctlen = withForeignGCM gcm $ \(g,k,c) ->
+    c_gcm_decrypt pt g c k ct (fromIntegral ctlen)
+
+finishGCM :: GetExpanded k
+          => GCM k
           -> Ptr Word8 -- Tag, must point to 16 byte buffer (or larger)
           -> IO ()
-finishGCM (GCM k) tagPtr = withForeignPtr k $ \g -> c_gcm_finish tagPtr g
+finishGCM gcm tagPtr =
+    withForeignGCM gcm $ \(gp,kp,cp) -> c_gcm_finish tagPtr gp kp cp
 
-encryptCTR :: AESKey
+withForeignGCM :: GetExpanded k => GCM k -> ((AESGcmPtr, AESKeyPtr, AESCtxPtr) -> IO a) -> IO a
+withForeignGCM (GCM g k c) f =
+    withForeignPtr (unGCMpc g) $ \gp -> withForeignPtr (expandedKey k) $ \kp -> withForeignPtr c $ \cp -> f (gp,kp,cp)
+
+encryptCTR :: GetExpanded k
+           => k
            -> Ptr Word8 -- ^ IV
            -> Ptr Word8 -- ^ NEW IV
            -> Ptr Word8 -- ^ CT
            -> Ptr Word8 -- ^ PT
            -> Int       -- ^ Length in bytes
            -> IO ()
-encryptCTR (AESKey _ k) iv niv ct pt len = withForeignPtr k $ \p -> do
-    c_encrypt_ctr p iv niv ct pt (fromIntegral len)
+encryptCTR (expandedKey -> k) iv niv ct pt len = withForeignPtr k $ \p -> do
+    c_encrypt_ctr ct p iv niv pt (fromIntegral len)
 {-# INLINE encryptCTR #-}
 
-decryptCTR :: AESKey
+decryptCTR :: GetExpanded k
+           => k
            -> Ptr Word8 -- ^ IV
            -> Ptr Word8 -- ^ NEW IV
-           -> Ptr Word8 -- ^ CT
            -> Ptr Word8 -- ^ PT
+           -> Ptr Word8 -- ^ CT
            -> Int       -- ^ Length in bytes
            -> IO ()
+decryptCTR (expandedKey -> k) iv niv pt ct len = withForeignPtr k $ \p -> do
+    c_decrypt_ctr pt p iv niv ct (fromIntegral len)
 
-decryptCTR (AESKey _ k) iv niv ct pt len = withForeignPtr k $ \p -> do
-    c_decrypt_ctr p iv niv ct pt (fromIntegral len)
+encryptGCM :: GetExpanded k
+           => k
+           -> GCMpc
+           -> Ptr Word8 -> Word32 -- IV
+           -> Ptr Word8 -> Word32 -- AAD
+           -> Ptr Word8 -> Word32 -- PT
+           -> Ptr Word8 -- CT
+           -> Ptr Word8 -- Tag
+           -> IO ()
+encryptGCM (expandedKey -> k) (GCMpc g) iv ivLen aad aadLen pt ptLen ct tag =
+    withForeignPtr k $ \kp ->
+      withForeignPtr g $ \gp ->
+       c_gcm_full_encrypt kp gp iv ivLen aad aadLen pt ptLen ct tag
+
+decryptGCM :: GetExpanded k
+           => k
+           -> GCMpc
+           -> Ptr Word8 -> Word32 -- IV
+           -> Ptr Word8 -> Word32 -- AAD
+           -> Ptr Word8 -> Word32 -- CT
+           -> Ptr Word8 -- PT
+           -> Ptr Word8 -- Tag
+           -> IO ()
+decryptGCM (expandedKey -> k) (GCMpc g) iv ivLen aad aadLen ct ctLen pt tag =
+    withForeignPtr k $ \kp ->
+      withForeignPtr g $ \gp ->
+       c_gcm_full_decrypt kp gp iv ivLen aad aadLen ct ctLen pt tag
+
 {-# INLINE decryptCTR #-}
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -13,10 +13,13 @@
  where
  hk = simpleUserHooks { buildHook = \pd lbi uh bf -> do
                                         let ccProg = Program "gcc" undefined undefined undefined
-                                            mConf = lookupProgram ccProg (withPrograms lbi)
+                                            hcProg = Program "ghc" undefined undefined undefined
+                                            mConf  = lookupProgram ccProg (withPrograms lbi)
+                                            hcConf = lookupProgram hcProg (withPrograms lbi)
                                             err = error "Could not determine C compiler"
-                                            cc = locationPath . programLocation  . maybe err id $ mConf
-                                        b <- canUseAesIntrinsicsFlag cc
+                                            cc  = locationPath . programLocation  . maybe err id $ mConf
+                                            hc  = locationPath . programLocation  . maybe err id $ hcConf
+                                        b <- canUseAesIntrinsicsFlag hc
                                         let newWithPrograms1 = userSpecifyArgs "gcc" aesArgs (withPrograms lbi)
                                             newWithPrograms  = userSpecifyArgs "ghc" aesArgsHC newWithPrograms1
                                             lbiNew = if b then (lbi {withPrograms = newWithPrograms }) else lbi
@@ -24,7 +27,7 @@
                       }
 
 aesArgs :: [String]
-aesArgs = ["-maes", "-mssse3", "-DHAVE_AES_INTRINSICS"]
+aesArgs = ["-mpclmul", "-maes", "-mssse3", "-DHAVE_AES_INTRINSICS", "-DWITH_AESNI"]
 
 aesArgsHC :: [String]
 aesArgsHC = map ("-optc" ++) aesArgs
@@ -34,9 +37,9 @@
         withTempDirectory normal "" "testIntrinsic" $ \tmpDir -> do
         writeFile (tmpDir ++ "/testIntrinsic.c")
                 (unlines        [ "#include <wmmintrin.h>"
-                                , "int main() {"
+                                , "int real_main() {"
                                 , "return 0; }"
                                 ])
-        ec <- rawSystemExitCode normal cc ["-maes",tmpDir ++ "/testIntrinsic.c"]
+        ec <- rawSystemExitCode normal cc (aesArgsHC ++ ["-c", tmpDir ++ "/testIntrinsic.c"])
         notice normal $ "Result of NI Intrinsics Test: " ++ show (ec == ExitSuccess)
         return (ec == ExitSuccess)
diff --git a/Test/test.hs b/Test/test.hs
new file mode 100644
--- /dev/null
+++ b/Test/test.hs
@@ -0,0 +1,9 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PackageImports #-}
+import Test.AES
+import Crypto.Cipher.AES128
+import "test-framework" Test.Framework
+
+main = do
+    ts <- makeAESTests (undefined :: AESKey128)
+    defaultMain ts
diff --git a/cbits/LICENSE b/cbits/LICENSE
new file mode 100644
--- /dev/null
+++ b/cbits/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2008-2013 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.
diff --git a/cbits/aes.c b/cbits/aes.c
new file mode 100644
--- /dev/null
+++ b/cbits/aes.c
@@ -0,0 +1,591 @@
+/*
+ * 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 "cpu.h"
+#include "aes.h"
+#include "aes_generic.h"
+#include "bitfn.h"
+#include <string.h>
+#include <stdio.h>
+
+#include "gf.h"
+#include "aes_x86ni.h"
+
+void tmd_aes_generic_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_decrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t length);
+void tmd_aes_generic_encrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+                             uint32_t spoint, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_decrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+                             uint32_t spoint, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
+void tmd_aes_generic_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
+
+enum {
+        /* init */
+        INIT_128, INIT_192, INIT_256,
+        /* single block */
+        ENCRYPT_BLOCK_128, ENCRYPT_BLOCK_192, ENCRYPT_BLOCK_256,
+        DECRYPT_BLOCK_128, DECRYPT_BLOCK_192, DECRYPT_BLOCK_256,
+        /* ecb */
+        ENCRYPT_ECB_128, ENCRYPT_ECB_192, ENCRYPT_ECB_256,
+        DECRYPT_ECB_128, DECRYPT_ECB_192, DECRYPT_ECB_256,
+        /* cbc */
+        ENCRYPT_CBC_128, ENCRYPT_CBC_192, ENCRYPT_CBC_256,
+        DECRYPT_CBC_128, DECRYPT_CBC_192, DECRYPT_CBC_256,
+        /* ctr */
+        ENCRYPT_CTR_128, ENCRYPT_CTR_192, ENCRYPT_CTR_256,
+        /* xts */
+        ENCRYPT_XTS_128, ENCRYPT_XTS_192, ENCRYPT_XTS_256,
+        DECRYPT_XTS_128, DECRYPT_XTS_192, DECRYPT_XTS_256,
+        /* xts */
+        ENCRYPT_GCM_128, ENCRYPT_GCM_192, ENCRYPT_GCM_256,
+        DECRYPT_GCM_128, DECRYPT_GCM_192, DECRYPT_GCM_256,
+};
+
+void *tmd_branch_table[] = {
+        /* INIT */
+        [INIT_128]          = tmd_aes_generic_init,
+        [INIT_192]          = tmd_aes_generic_init,
+        [INIT_256]          = tmd_aes_generic_init,
+        /* BLOCK */
+        [ENCRYPT_BLOCK_128] = tmd_aes_generic_encrypt_block,
+        [ENCRYPT_BLOCK_192] = tmd_aes_generic_encrypt_block,
+        [ENCRYPT_BLOCK_256] = tmd_aes_generic_encrypt_block,
+        [DECRYPT_BLOCK_128] = tmd_aes_generic_decrypt_block,
+        [DECRYPT_BLOCK_192] = tmd_aes_generic_decrypt_block,
+        [DECRYPT_BLOCK_256] = tmd_aes_generic_decrypt_block,
+        /* ECB */
+        [ENCRYPT_ECB_128]   = tmd_aes_generic_encrypt_ecb,
+        [ENCRYPT_ECB_192]   = tmd_aes_generic_encrypt_ecb,
+        [ENCRYPT_ECB_256]   = tmd_aes_generic_encrypt_ecb,
+        [DECRYPT_ECB_128]   = tmd_aes_generic_decrypt_ecb,
+        [DECRYPT_ECB_192]   = tmd_aes_generic_decrypt_ecb,
+        [DECRYPT_ECB_256]   = tmd_aes_generic_decrypt_ecb,
+        /* CBC */
+        [ENCRYPT_CBC_128]   = tmd_aes_generic_encrypt_cbc,
+        [ENCRYPT_CBC_192]   = tmd_aes_generic_encrypt_cbc,
+        [ENCRYPT_CBC_256]   = tmd_aes_generic_encrypt_cbc,
+        [DECRYPT_CBC_128]   = tmd_aes_generic_decrypt_cbc,
+        [DECRYPT_CBC_192]   = tmd_aes_generic_decrypt_cbc,
+        [DECRYPT_CBC_256]   = tmd_aes_generic_decrypt_cbc,
+        /* CTR */
+        [ENCRYPT_CTR_128]   = tmd_aes_generic_encrypt_ctr,
+        [ENCRYPT_CTR_192]   = tmd_aes_generic_encrypt_ctr,
+        [ENCRYPT_CTR_256]   = tmd_aes_generic_encrypt_ctr,
+        /* XTS */
+        [ENCRYPT_XTS_128]   = tmd_aes_generic_encrypt_xts,
+        [ENCRYPT_XTS_192]   = tmd_aes_generic_encrypt_xts,
+        [ENCRYPT_XTS_256]   = tmd_aes_generic_encrypt_xts,
+        [DECRYPT_XTS_128]   = tmd_aes_generic_decrypt_xts,
+        [DECRYPT_XTS_192]   = tmd_aes_generic_decrypt_xts,
+        [DECRYPT_XTS_256]   = tmd_aes_generic_decrypt_xts,
+        /* GCM */
+        [ENCRYPT_GCM_128]   = tmd_aes_generic_gcm_encrypt,
+        [ENCRYPT_GCM_192]   = tmd_aes_generic_gcm_encrypt,
+        [ENCRYPT_GCM_256]   = tmd_aes_generic_gcm_encrypt,
+        [DECRYPT_GCM_128]   = tmd_aes_generic_gcm_decrypt,
+        [DECRYPT_GCM_192]   = tmd_aes_generic_gcm_decrypt,
+        [DECRYPT_GCM_256]   = tmd_aes_generic_gcm_decrypt,
+};
+
+typedef void (*init_f)(aes_key *, uint8_t *, uint8_t);
+typedef void (*ecb_f)(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
+typedef void (*cbc_f)(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
+typedef void (*ctr_f)(uint8_t *output, aes_key *key, aes_block *iv, aes_block *niv, uint8_t *input, uint32_t length);
+typedef void (*xts_f)(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit, uint32_t spoint, aes_block *input, uint32_t nb_blocks);
+typedef void (*gcm_crypt_f)(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
+typedef void (*block_f)(aes_block *output, aes_key *key, aes_block *input);
+
+#ifdef WITH_AESNI
+#define GET_INIT(strength) \
+        ((init_f) (tmd_branch_table[INIT_128 + strength]))
+#define GET_ECB_ENCRYPT(strength) \
+        ((ecb_f) (tmd_branch_table[ENCRYPT_ECB_128 + strength]))
+#define GET_ECB_DECRYPT(strength) \
+        ((ecb_f) (tmd_branch_table[DECRYPT_ECB_128 + strength]))
+#define GET_CBC_ENCRYPT(strength) \
+        ((cbc_f) (tmd_branch_table[ENCRYPT_CBC_128 + strength]))
+#define GET_CBC_DECRYPT(strength) \
+        ((cbc_f) (tmd_branch_table[DECRYPT_CBC_128 + strength]))
+#define GET_CTR_ENCRYPT(strength) \
+        ((ctr_f) (tmd_branch_table[ENCRYPT_CTR_128 + strength]))
+#define GET_XTS_ENCRYPT(strength) \
+        ((xts_f) (tmd_branch_table[ENCRYPT_XTS_128 + strength]))
+#define GET_XTS_DECRYPT(strength) \
+        ((xts_f) (tmd_branch_table[DECRYPT_XTS_128 + strength]))
+#define GET_GCM_ENCRYPT(strength) \
+        ((gcm_crypt_f) (tmd_branch_table[ENCRYPT_GCM_128 + strength]))
+#define GET_GCM_DECRYPT(strength) \
+        ((gcm_crypt_f) (tmd_branch_table[DECRYPT_GCM_128 + strength]))
+#define aes_encrypt_block(o,k,i) \
+        (((block_f) (tmd_branch_table[ENCRYPT_BLOCK_128 + k->strength]))(o,k,i))
+#define aes_decrypt_block(o,k,i) \
+        (((block_f) (tmd_branch_table[DECRYPT_BLOCK_128 + k->strength]))(o,k,i))
+#else
+#define GET_INIT(strenght) tmd_aes_generic_init
+#define GET_ECB_ENCRYPT(strength) tmd_aes_generic_encrypt_ecb
+#define GET_ECB_DECRYPT(strength) tmd_aes_generic_decrypt_ecb
+#define GET_CBC_ENCRYPT(strength) tmd_aes_generic_encrypt_cbc
+#define GET_CBC_DECRYPT(strength) tmd_aes_generic_decrypt_cbc
+#define GET_CTR_ENCRYPT(strength) tmd_aes_generic_encrypt_ctr
+#define GET_XTS_ENCRYPT(strength) tmd_aes_generic_encrypt_xts
+#define GET_XTS_DECRYPT(strength) tmd_aes_generic_decrypt_xts
+#define GET_GCM_ENCRYPT(strength) tmd_aes_generic_gcm_encrypt
+#define GET_GCM_DECRYPT(strength) tmd_aes_generic_gcm_decrypt
+#define aes_encrypt_block(o,k,i) tmd_aes_generic_encrypt_block(o,k,i)
+#define aes_decrypt_block(o,k,i) tmd_aes_generic_decrypt_block(o,k,i)
+#endif
+
+#if defined(ARCH_X86) && defined(WITH_AESNI)
+void tmd_initialize_table_ni(int aesni, int pclmul)
+{
+        if (!aesni)
+                return;
+        tmd_branch_table[INIT_128] = tmd_aes_ni_init;
+        tmd_branch_table[INIT_256] = tmd_aes_ni_init;
+
+        tmd_branch_table[ENCRYPT_BLOCK_128] = tmd_aes_ni_encrypt_block128;
+        tmd_branch_table[DECRYPT_BLOCK_128] = tmd_aes_ni_decrypt_block128;
+        tmd_branch_table[ENCRYPT_BLOCK_256] = tmd_aes_ni_encrypt_block256;
+        tmd_branch_table[DECRYPT_BLOCK_256] = tmd_aes_ni_decrypt_block256;
+        /* ECB */
+        tmd_branch_table[ENCRYPT_ECB_128] = tmd_aes_ni_encrypt_ecb128;
+        tmd_branch_table[DECRYPT_ECB_128] = tmd_aes_ni_decrypt_ecb128;
+        tmd_branch_table[ENCRYPT_ECB_256] = tmd_aes_ni_encrypt_ecb256;
+        tmd_branch_table[DECRYPT_ECB_256] = tmd_aes_ni_decrypt_ecb256;
+        /* CBC */
+        tmd_branch_table[ENCRYPT_CBC_128] = tmd_aes_ni_encrypt_cbc128;
+        tmd_branch_table[DECRYPT_CBC_128] = tmd_aes_ni_decrypt_cbc128;
+        tmd_branch_table[ENCRYPT_CBC_256] = tmd_aes_ni_encrypt_cbc256;
+        tmd_branch_table[DECRYPT_CBC_256] = tmd_aes_ni_decrypt_cbc256;
+        /* CTR */
+        tmd_branch_table[ENCRYPT_CTR_128] = tmd_aes_ni_encrypt_ctr128;
+        tmd_branch_table[ENCRYPT_CTR_256] = tmd_aes_ni_encrypt_ctr256;
+        /* XTS */
+        tmd_branch_table[ENCRYPT_XTS_128] = tmd_aes_ni_encrypt_xts128;
+        tmd_branch_table[ENCRYPT_XTS_256] = tmd_aes_ni_encrypt_xts256;
+        /* GCM */
+        tmd_branch_table[ENCRYPT_GCM_128] = tmd_aes_ni_gcm_encrypt128;
+        tmd_branch_table[ENCRYPT_GCM_256] = tmd_aes_ni_gcm_encrypt256;
+}
+#endif
+
+aes_key *tmd_allocatekey()
+{
+    aes_key *res = (aes_key *)malloc(sizeof(aes_key));
+    return res;
+}
+
+aes_ctx *tmd_allocatectx()
+{
+    aes_ctx *res = (aes_ctx *)malloc(sizeof(aes_ctx));
+    return res;
+}
+
+aes_gcm *tmd_allocategcm()
+{
+    aes_gcm *res = (aes_gcm *)malloc(sizeof(aes_gcm));
+    return res;
+}
+
+void tmd_freekey(aes_key *k)
+{
+    free(k);
+}
+
+void tmd_freectx(aes_ctx *c)
+{
+    free(c);
+}
+
+void tmd_freegcm(aes_gcm *g)
+{
+    free(g);
+}
+
+void tmd_aes_initkey(aes_key *key, uint8_t *origkey, uint8_t size)
+{
+        switch (size) {
+        case 16: key->nbr = 10; key->strength = 0; break;
+        case 24: key->nbr = 12; key->strength = 1; break;
+        case 32: key->nbr = 14; key->strength = 2; break;
+        }
+#if defined(ARCH_X86) && defined(WITH_AESNI)
+        initialize_hw(tmd_initialize_table_ni);
+#endif
+        init_f _init = GET_INIT(key->strength);
+        _init(key, origkey, size);
+}
+
+void tmd_aes_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+{
+        ecb_f e = GET_ECB_ENCRYPT(key->strength);
+        e(output, key, input, nb_blocks);
+}
+
+void tmd_aes_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+{
+        ecb_f d = GET_ECB_DECRYPT(key->strength);
+        d(output, key, input, nb_blocks);
+}
+
+void tmd_aes_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks)
+{
+        cbc_f e = GET_CBC_ENCRYPT(key->strength);
+        e(output, key, iv, input, nb_blocks);
+}
+
+void tmd_aes_decrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks)
+{
+        cbc_f d = GET_CBC_DECRYPT(key->strength);
+        d(output, key, iv, input, nb_blocks);
+}
+
+void tmd_aes_gen_ctr(aes_block *output, aes_key *key, aes_block *iv, uint32_t nb_blocks)
+{
+        aes_block block;
+
+        /* preload IV in block */
+        block128_copy(&block, iv);
+
+        for ( ; nb_blocks-- > 0; output++, block128_inc_be(&block)) {
+                aes_encrypt_block(output, key, &block);
+        }
+}
+
+void tmd_aes_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t len)
+{
+        ctr_f e = GET_CTR_ENCRYPT(key->strength);
+        e(output, key, iv, newIV, input, len);
+}
+
+void tmd_aes_encrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+                     uint32_t spoint, aes_block *input, uint32_t nb_blocks)
+{
+        xts_f e = GET_XTS_ENCRYPT(k1->strength);
+        e(output, k1, k2, dataunit, spoint, input, nb_blocks);
+}
+
+void tmd_aes_decrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+                     uint32_t spoint, aes_block *input, uint32_t nb_blocks)
+{
+        tmd_aes_generic_decrypt_xts(output, k1, k2, dataunit, spoint, input, nb_blocks);
+}
+
+void tmd_aes_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+{
+        gcm_crypt_f e = GET_GCM_ENCRYPT(key->strength);
+        e(output, gcm, ctx, key, input, length);
+}
+
+void tmd_aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+{
+        gcm_crypt_f d = GET_GCM_DECRYPT(key->strength);
+        d(output, gcm, ctx, key, input, length);
+}
+
+static void gcm_ghash_add(aes_gcm *gcm, aes_ctx *ctx, block128 *b)
+{
+        block128_xor(&ctx->tag, b);
+        gf_mul(&ctx->tag, &gcm->h);
+}
+
+void tmd_aes_gcm_init(aes_gcm *gcm, aes_key *key)
+{
+        block128_zero(&gcm->h);
+        /* prepare H : encrypt_K(0^128) */
+        aes_encrypt_block(&gcm->h, key, &gcm->h);
+}
+
+void tmd_aes_ctx_init(const aes_gcm *gcm, aes_ctx *ctx, const aes_key *key, uint8_t *iv, uint32_t len)
+{
+        ctx->length_aad = 0;
+        ctx->length_input = 0;
+
+        block128_zero(&ctx->tag);
+        block128_zero(&ctx->iv);
+
+        if (len == 12) {
+                block128_copy_bytes(&ctx->iv, iv, 12);
+                ctx->iv.b[15] = 0x01;
+        } else {
+                uint32_t origlen = len << 3;
+                int i;
+                for (; len >= 16; len -= 16, iv += 16) {
+                        block128_xor(&ctx->iv, (block128 *) iv);
+                        gf_mul(&ctx->iv, &gcm->h);
+                }
+                if (len > 0) {
+                        block128_xor_bytes(&ctx->iv, iv, len);
+                        gf_mul(&ctx->iv, &gcm->h);
+                }
+                for (i = 15; origlen; --i, origlen >>= 8)
+                        ctx->iv.b[i] ^= (uint8_t) origlen;
+                gf_mul(&ctx->iv, &gcm->h);
+        }
+
+        block128_copy(&ctx->civ, &ctx->iv);
+}
+
+void tmd_aes_gcm_aad(aes_gcm *gcm, aes_ctx *ctx, uint8_t *input, uint32_t length)
+{
+        ctx->length_aad += length;
+        for (; length >= 16; input += 16, length -= 16) {
+                gcm_ghash_add(gcm, ctx, (block128 *) input);
+        }
+        if (length > 0) {
+                aes_block tmp;
+                block128_zero(&tmp);
+                block128_copy_bytes(&tmp, input, length);
+                gcm_ghash_add(gcm, ctx, &tmp);
+        }
+
+}
+
+void tmd_aes_gcm_finish(uint8_t *tag, aes_gcm *gcm, aes_key *key, aes_ctx *ctx)
+{
+        aes_block lblock;
+        int i;
+
+        /* tag = (tag-1 xor (lenbits(a) | lenbits(c)) ) . H */
+        lblock.q[0] = cpu_to_be64(ctx->length_aad << 3);
+        lblock.q[1] = cpu_to_be64(ctx->length_input << 3);
+        gcm_ghash_add(gcm, ctx, &lblock);
+
+        aes_encrypt_block(&lblock, key, &ctx->iv);
+        block128_xor(&ctx->tag, &lblock);
+
+        for (i = 0; i < 16; i++) {
+                tag[i] = ctx->tag.b[i];
+        }
+}
+
+void tmd_aes_gcm_full_encrypt(aes_key *key, aes_gcm *gcm
+                             , uint8_t *iv, uint32_t ivLen
+                             , uint8_t *aad, uint32_t aadLen
+                             , uint8_t *pt, uint32_t ptLen
+                             , uint8_t *ct, uint8_t *tag)
+{
+    aes_ctx ctx;
+    tmd_aes_ctx_init(gcm, &ctx, key, iv, ivLen);
+    tmd_aes_gcm_encrypt(ct, gcm, &ctx, key, pt, ptLen);
+    tmd_aes_gcm_aad(gcm, &ctx, aad, aadLen);
+    tmd_aes_gcm_finish(tag, gcm, key, &ctx);
+}
+
+void tmd_aes_gcm_full_decrypt(aes_key *key, aes_gcm *gcm
+                             , uint8_t *iv, uint32_t ivLen
+                             , uint8_t *aad, uint32_t aadLen
+                             , uint8_t *ct, uint32_t ctLen
+                             , uint8_t *pt, uint8_t *tag)
+{
+    aes_ctx ctx;
+    tmd_aes_ctx_init(gcm, &ctx, key, iv, ivLen);
+    tmd_aes_gcm_decrypt(pt, gcm, &ctx, key, ct, ctLen);
+    tmd_aes_gcm_aad(gcm, &ctx, aad, aadLen);
+    tmd_aes_gcm_finish(tag, gcm, key, &ctx);
+}
+
+void tmd_aes_generic_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+{
+        for ( ; nb_blocks-- > 0; input++, output++) {
+                tmd_aes_generic_encrypt_block(output, key, input);
+        }
+}
+
+void tmd_aes_generic_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+{
+        for ( ; nb_blocks-- > 0; input++, output++) {
+                tmd_aes_generic_decrypt_block(output, key, input);
+        }
+}
+
+void tmd_aes_generic_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks)
+{
+        aes_block block;
+
+        /* preload IV in block */
+        block128_copy(&block, iv);
+        for ( ; nb_blocks-- > 0; input++, output++) {
+                block128_xor(&block, (block128 *) input);
+                tmd_aes_generic_encrypt_block(&block, key, &block);
+                block128_copy((block128 *) output, &block);
+        }
+}
+
+void tmd_aes_generic_decrypt_cbc(aes_block *output, aes_key *key, aes_block *ivini, aes_block *input, uint32_t nb_blocks)
+{
+        aes_block block, blocko;
+        aes_block iv;
+
+        /* preload IV in block */
+        block128_copy(&iv, ivini);
+        for ( ; nb_blocks-- > 0; input++, output++) {
+                block128_copy(&block, (block128 *) input);
+                tmd_aes_generic_decrypt_block(&blocko, key, &block);
+                block128_vxor((block128 *) output, &blocko, &iv);
+                block128_copy(&iv, &block);
+        }
+}
+
+void tmd_aes_generic_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t len)
+{
+        aes_block block, o;
+        uint32_t nb_blocks = len / 16;
+        int i;
+
+        /* preload IV in block */
+        block128_copy(&block, iv);
+
+        for ( ; nb_blocks-- > 0; block128_inc_be(&block), output += 16, input += 16) {
+                aes_encrypt_block(&o, key, &block);
+                block128_vxor((block128 *) output, &o, (block128 *) input);
+        }
+
+        if ((len % 16) != 0) {
+                aes_encrypt_block(&o, key, &block);
+                for (i = 0; i < (len % 16); i++) {
+                        *output = ((uint8_t *) &o)[i] ^ *input;
+                        output++;
+                        input++;
+                }
+        }
+        if(NULL != newIV)
+            block128_copy(newIV, &block);
+}
+
+void tmd_aes_generic_encrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+                             uint32_t spoint, aes_block *input, uint32_t nb_blocks)
+{
+        aes_block block, tweak;
+
+        /* load IV and encrypt it using k2 as the tweak */
+        block128_copy(&tweak, dataunit);
+        aes_encrypt_block(&tweak, k2, &tweak);
+
+        /* TO OPTIMISE: this is really inefficient way to do that */
+        while (spoint-- > 0)
+                gf_mulx(&tweak);
+
+        for ( ; nb_blocks-- > 0; input++, output++, gf_mulx(&tweak)) {
+                block128_vxor(&block, input, &tweak);
+                aes_encrypt_block(&block, k1, &block);
+                block128_vxor(output, &block, &tweak);
+        }
+}
+
+void tmd_aes_generic_decrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+                             uint32_t spoint, aes_block *input, uint32_t nb_blocks)
+{
+        aes_block block, tweak;
+
+        /* load IV and encrypt it using k2 as the tweak */
+        block128_copy(&tweak, dataunit);
+        aes_encrypt_block(&tweak, k2, &tweak);
+
+        /* TO OPTIMISE: this is really inefficient way to do that */
+        while (spoint-- > 0)
+                gf_mulx(&tweak);
+
+        for ( ; nb_blocks-- > 0; input++, output++, gf_mulx(&tweak)) {
+                block128_vxor(&block, input, &tweak);
+                aes_decrypt_block(&block, k1, &block);
+                block128_vxor(output, &block, &tweak);
+        }
+}
+
+void tmd_aes_generic_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+{
+        aes_block out;
+
+        ctx->length_input += length;
+        for (; length >= 16; input += 16, output += 16, length -= 16) {
+                block128_inc_be(&ctx->civ);
+
+                aes_encrypt_block(&out, key, &ctx->civ);
+                block128_xor(&out, (block128 *) input);
+                gcm_ghash_add(gcm, ctx, &out);
+                block128_copy((block128 *) output, &out);
+        }
+        if (length > 0) {
+                aes_block tmp;
+                int i;
+
+                block128_inc_be(&ctx->civ);
+                /* create e(civ) in out */
+                aes_encrypt_block(&out, key, &ctx->civ);
+                /* initialize a tmp as input and xor it to e(civ) */
+                block128_zero(&tmp);
+                block128_copy_bytes(&tmp, input, length);
+                block128_xor_bytes(&tmp, out.b, length);
+
+                gcm_ghash_add(gcm, ctx, &tmp);
+
+                for (i = 0; i < length; i++) {
+                        output[i] = tmp.b[i];
+                }
+        }
+}
+
+void tmd_aes_generic_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+{
+        aes_block out;
+
+        ctx->length_input += length;
+        for (; length >= 16; input += 16, output += 16, length -= 16) {
+                block128_inc_be(&ctx->civ);
+
+                aes_encrypt_block(&out, key, &ctx->civ);
+                gcm_ghash_add(gcm, ctx, (block128 *) input);
+                block128_xor(&out, (block128 *) input);
+                block128_copy((block128 *) output, &out);
+        }
+        if (length > 0) {
+                aes_block tmp;
+                int i;
+
+                block128_inc_be(&ctx->civ);
+
+                block128_zero(&tmp);
+                block128_copy_bytes(&tmp, input, length);
+                gcm_ghash_add(gcm, ctx, &tmp);
+
+                aes_encrypt_block(&out, key, &ctx->civ);
+                block128_xor_bytes(&tmp, out.b, length);
+
+                for (i = 0; i < length; i++) {
+                        output[i] = tmp.b[i];
+                }
+        }
+}
+
diff --git a/cbits/aes.h b/cbits/aes.h
new file mode 100644
--- /dev/null
+++ b/cbits/aes.h
@@ -0,0 +1,95 @@
+/*
+ *	Copyright (C) 2008 Vincent Hanquez <tab@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.
+ *
+ * AES implementation
+ */
+#ifndef AES_H
+#define AES_H
+
+#include <stdint.h>
+#include "block128.h"
+
+typedef block128 aes_block;
+
+/* size = 456 */
+typedef struct {
+	uint8_t nbr; /* number of rounds: 10 (128), 12 (192), 14 (256) */
+	uint8_t strength; /* 128 = 0, 192 = 1, 256 = 2 */
+	uint8_t _padding[6];
+	uint8_t data[16*14*2];
+} aes_key;
+
+/* size = 4*16+2*8= 80 */
+typedef struct {
+	aes_block h;
+} aes_gcm;
+
+typedef struct {
+	aes_block tag;
+	aes_block iv;
+	aes_block civ;
+	uint64_t length_aad;
+	uint64_t length_input;
+} aes_ctx;
+
+aes_key *tmd_allocatekey();
+aes_ctx *tmd_allocatectx();
+aes_gcm *tmd_allocategcm();
+void tmd_freekey(aes_key *k);
+void tmd_freectx(aes_ctx *c);
+void tmd_freegcm(aes_gcm *g);
+
+/* in bytes: either 16,24,32 */
+void tmd_aes_initkey(aes_key *ctx, uint8_t *key, uint8_t size);
+
+void tmd_aes_encrypt(aes_block *output, aes_key *key, aes_block *input);
+void tmd_aes_decrypt(aes_block *output, aes_key *key, aes_block *input);
+
+void tmd_aes_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
+
+void tmd_aes_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_decrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
+
+void tmd_aes_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t len);
+void tmd_aes_gen_ctr(aes_block *output, aes_key *key, aes_block *iv, uint32_t nb_blocks);
+
+void tmd_aes_encrypt_xts(aes_block *output, aes_key *key, aes_key *key2, aes_block *sector,
+                     uint32_t spoint, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_decrypt_xts(aes_block *output, aes_key *key, aes_key *key2, aes_block *sector,
+                     uint32_t spoint, aes_block *input, uint32_t nb_blocks);
+
+void tmd_aes_gcm_init(aes_gcm *gcm, aes_key *key);
+void tmd_aes_ctx_init(const aes_gcm *gcm, aes_ctx *ctx, const aes_key *key, uint8_t *iv, uint32_t len);
+void tmd_aes_gcm_aad(aes_gcm *gcm, aes_ctx *ctx, uint8_t *input, uint32_t length);
+void tmd_aes_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
+void tmd_aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
+void tmd_aes_gcm_finish(uint8_t *tag, aes_gcm *gcm, aes_key *key, aes_ctx *ctx);
+
+#endif
diff --git a/cbits/aes/LICENSE b/cbits/aes/LICENSE
deleted file mode 100644
--- a/cbits/aes/LICENSE
+++ /dev/null
@@ -1,29 +0,0 @@
-This license applies to the ni/* generic/* source code and associated header files.
-
-Copyright (c) 2010-2011 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.
diff --git a/cbits/aes/aes.c b/cbits/aes/aes.c
deleted file mode 100644
--- a/cbits/aes/aes.c
+++ /dev/null
@@ -1,441 +0,0 @@
-#include "aes_generic.h"
-#include "aes.h"
-#include <string.h>
-#include <stdlib.h>
-
-#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_AES_INTRINSICS) && !defined(AVOID_NI)
-#define TRY_NI
-#include "aes_x86ni.h"
-#endif
-#include "bitfn.h"
-static void gf_mul(block128 *a, const block128 *b);
-
-#ifdef TRY_NI
-/**
- * Returns zero if false, non-zero otherwise
- */
-static int cpu_has_ni()
-{
-       uint32_t ax,bx,cx,dx,func=1;
-
-       __asm__ volatile ("cpuid":\
-           "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));
-       return (cx & 0x2000000);
-}
-#endif
-
-/* Allocation */
-AESKey *allocate_key128()
-{
-        void *k=NULL;
-        k = (void *)malloc(sizeof(aes_key));
-        return k;
-}
-
-aes_gcm *allocate_gcm()
-{
-    void *g=NULL;
-    g = (void *)malloc(sizeof(aes_gcm));
-    return g;
-}
-
-/* Generate Key */
-#ifdef TRY_NI
-static void detect_and_generate_key128(AESKey *k, const uint8_t *bytes);
-static void (*generate_key128_ptr)(AESKey *,const uint8_t *) = &detect_and_generate_key128;
-
-/**
- * Expand a 128 bit AES key.
- */
-void generate_key128(AESKey *k, const uint8_t *bytes)
-{
-    (*generate_key128_ptr)(k,bytes);
-}
-
-static void generate_key128_generic(AESKey *k, const uint8_t *bytes)
-{
-    aes128_generic_init(k, bytes, 16);
-}
-
-static void generate_key128_ni(AESKey *k, const uint8_t *bytes)
-{
-    aes128_ni_init((aes_key *)k, bytes, 16);
-}
-
-static void detect_and_generate_key128(AESKey *k, const uint8_t *bytes)
-{
-    if(cpu_has_ni()) {
-            generate_key128_ptr = &generate_key128_ni;
-    } else {
-            generate_key128_ptr = &generate_key128_generic;
-    }
-    (*generate_key128_ptr)(k,bytes);
-}
-
-#else
-void generate_key128(AESKey *k, const uint8_t *bytes)
-{
-    aes128_generic_init(k, bytes, 16);
-}
-#endif
-
-void free_key128(AESKey *k)
-{
-        memset(k, 0, sizeof(AESKey));
-        free(k);
-}
-
-void free_gcm(aes_gcm *g)
-{
-        memset(g, 0, sizeof(aes_gcm));
-        free(g);
-}
-
-/* ECB Encrypt */
-#ifdef TRY_NI
-static void detect_and_encrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr);
-static void (*encrypt_ecb_ptr)(const AESKey *, uint8_t *, const uint8_t *, const uint32_t) = &detect_and_encrypt_ecb;
-
-static void encrypt_ecb_ni(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-        aes128_ni_encrypt_ecb(dst, (aes_key *)k, src, nr);
-}
-
-static void encrypt_ecb_generic(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-    int i;
-    for(i = 0; i<nr*16; i+=16) {
-        aes128_generic_encrypt_block
-                       ( (aes_block*) dst+i
-                       , k
-                       , (const aes_block*)src+i);
-    }
-}
-
-void encrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-        (*encrypt_ecb_ptr)(k,dst,src,nr);
-}
-
-static void detect_and_encrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-        if(cpu_has_ni()) encrypt_ecb_ptr = &encrypt_ecb_ni;
-        else encrypt_ecb_ptr = &encrypt_ecb_generic;
-        (*encrypt_ecb_ptr)(k,dst,src,nr);
-}
-#else
-void encrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-    int i;
-    for(i = 0; i<nr*16; i+=16) {
-        aes128_generic_encrypt_block
-                       ( (aes_block*) dst+i
-                       , k
-                       , (const aes_block*)src+i);
-    }
-}
-#endif
-
-/* ECB Decrypt */
-#ifdef TRY_NI
-static void detect_and_decrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr);
-static void (*decrypt_ecb_ptr)(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr) = &detect_and_decrypt_ecb;
-
-void decrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-        (*decrypt_ecb_ptr)(k,dst,src,nr);
-}
-
-static void decrypt_ecb_ni(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-        aes128_ni_decrypt_ecb(dst, (aes_key *)k, src, nr);
-}
-
-static void decrypt_ecb_generic(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-    int i;
-    for(i = 0; i<nr*16; i+=16) {
-        aes128_generic_decrypt_block ( (aes_block*) dst+i
-                                  , k
-                                  , (const aes_block*)src+i);
-    }
-}
-
-static void detect_and_decrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-        if(cpu_has_ni()) decrypt_ecb_ptr = &decrypt_ecb_ni;
-        else decrypt_ecb_ptr = &decrypt_ecb_generic;
-        (*decrypt_ecb_ptr)(k,dst,src,nr);
-}
-#else
-void decrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
-{
-    int i;
-    for(i = 0; i<nr*16; i+=16) {
-        aes128_generic_decrypt_block ( (aes_block*) dst+i
-                                  , k
-                                  , (const aes_block*)src+i);
-    }
-}
-#endif
-
-
-/* GCM */
-static void gcm_ghash_add(const aes_gcm *gcm, aes_gcm_ctx *ctx, block128 *b)
-{
-        block128_xor(&ctx->tag, b);
-        gf_mul(&ctx->tag, &gcm->h);
-}
-
-void aes_gcm_ctx_init(aes_gcm_ctx *ctx, const aes_gcm *gcm, const uint8_t *iv, uint32_t len)
-{
-        ctx->length_aad = 0;
-        ctx->length_input = 0;
-        block128_zero(&ctx->tag);
-
-        block128_zero(&ctx->iv);
-        if (len == 12) {
-                block128_copy_bytes(&ctx->iv, iv, 12);
-                ctx->iv.b[15] = 0x01;
-        } else {
-                uint32_t origlen = len << 3;
-                int i;
-                for (; len >= 16; len -= 16, iv += 16) {
-                        block128_xor(&ctx->iv, (block128 *) iv);
-                        gf_mul(&ctx->iv, &gcm->h);
-                }
-                if (len > 0) {
-                        block128_xor_bytes(&ctx->iv, iv, len);
-                        gf_mul(&ctx->iv, &gcm->h);
-                }
-                for (i = 15; origlen; --i, origlen >>= 8)
-                        ctx->iv.b[i] ^= (uint8_t) origlen;
-                gf_mul(&ctx->iv, &gcm->h);
-        }
-
-        block128_copy(&ctx->civ, &ctx->iv);
-}
-
-void aes_gcm_init(aes_gcm *gcm, const aes_key *key)
-{
-        block128_zero(&gcm->h);
-
-        memcpy(&gcm->key, key, sizeof(aes_key));
-
-        /* prepare H : encrypt_K(0^128) */
-        encrypt_ecb(key, (uint8_t *)&gcm->h, (const uint8_t *)&gcm->h, 1);
-}
-
-void aes_gcm_aad(const aes_gcm *gcm, aes_gcm_ctx *ctx, uint8_t *input, uint32_t length)
-{
-        ctx->length_aad += length;
-        for (; length >= 16; input += 16, length -= 16) {
-                gcm_ghash_add(gcm, ctx, (block128 *) input);
-        }
-        if (length > 0) {
-                aes_block tmp;
-                block128_zero(&tmp);
-                block128_copy_bytes(&tmp, input, length);
-                gcm_ghash_add(gcm, ctx, &tmp);
-        }
-
-}
-
-void aes_gcm_enc_finish( uint8_t *output, uint8_t *tag
-                       , const aes_gcm *gcm
-                       , uint8_t *iv, uint32_t ivLen
-                       , uint8_t *input, uint32_t inputLen
-                       , uint8_t *aad, uint32_t aadLen)
-{
-    aes_gcm_ctx ctx;
-    aes_gcm_ctx_init(&ctx, gcm, iv, ivLen);
-    aes_gcm_encrypt(output, gcm, &ctx, input, inputLen);
-    aes_gcm_aad(gcm, &ctx, aad, aadLen);
-    aes_gcm_finish(tag, gcm, &ctx);
-}
-
-void aes_gcm_dec_finish( uint8_t *output, uint8_t *tag
-                       , const aes_gcm *gcm
-                       , uint8_t *iv, uint32_t ivLen
-                       , uint8_t *input, uint32_t inputLen
-                       , uint8_t *aad, uint32_t aadLen)
-{
-    aes_gcm_ctx ctx;
-    aes_gcm_ctx_init(&ctx, gcm, iv, ivLen);
-    aes_gcm_decrypt(output, gcm, &ctx, input, inputLen);
-    aes_gcm_aad(gcm, &ctx, aad, aadLen);
-    aes_gcm_finish(tag, gcm, &ctx);
-}
-
-void aes_gcm_encrypt(uint8_t *output, const aes_gcm *gcm, aes_gcm_ctx *ctx, const uint8_t *input, uint32_t length)
-{
-        aes_block out;
-
-        ctx->length_input += length;
-        for (; length >= 16; input += 16, output += 16, length -= 16) {
-                block128_inc_be(&ctx->civ);
-
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&ctx->civ, 1);
-                block128_xor(&out, (block128 *) input);
-                gcm_ghash_add(gcm, ctx, &out);
-                block128_copy((block128 *) output, &out);
-        }
-        if (length > 0) {
-                aes_block tmp;
-                int i;
-
-                block128_inc_be(&ctx->civ);
-                /* create e(civ) in out */
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&ctx->civ, 1);
-                /* initialize a tmp as input and xor it to e(civ) */
-                block128_zero(&tmp);
-                block128_copy_bytes(&tmp, input, length);
-                block128_xor_bytes(&tmp, out.b, length); 
-
-                gcm_ghash_add(gcm, ctx, &tmp);
-
-                for (i = 0; i < length; i++) {
-                        output[i] = tmp.b[i];
-                }
-        }
-}
-
-void aes_gcm_decrypt(uint8_t *output, const aes_gcm *gcm, aes_gcm_ctx *ctx, const uint8_t *input, uint32_t length)
-{
-        aes_block out;
-
-        ctx->length_input += length;
-        for (; length >= 16; input += 16, output += 16, length -= 16) {
-                block128_inc_be(&ctx->civ);
-
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&ctx->civ, 1);
-                gcm_ghash_add(gcm, ctx, (block128 *) input);
-                block128_xor(&out, (block128 *) input);
-                block128_copy((block128 *) output, &out);
-        }
-        if (length > 0) {
-                aes_block tmp;
-                int i;
-
-                block128_inc_be(&ctx->civ);
-
-                block128_zero(&tmp);
-                block128_copy_bytes(&tmp, input, length);
-                gcm_ghash_add(gcm, ctx, &tmp);
-
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&ctx->civ, 1);
-                block128_xor_bytes(&tmp, out.b, length); 
-
-                for (i = 0; i < length; i++) {
-                        output[i] = tmp.b[i];
-                }
-        }
-}
-
-void aes_gcm_finish(uint8_t *tag, const aes_gcm *gcm, aes_gcm_ctx *ctx)
-{
-        aes_block lblock;
-        int i;
-
-        /* tag = (tag-1 xor (lenbits(a) | lenbits(c)) ) . H */
-        lblock.q[0] = cpu_to_be64(ctx->length_aad << 3);
-        lblock.q[1] = cpu_to_be64(ctx->length_input << 3);
-        gcm_ghash_add(gcm, ctx, &lblock);
-
-        encrypt_ecb(&gcm->key, (uint8_t *)&lblock, (const uint8_t*)&ctx->iv, 1);
-        block128_xor(&ctx->tag, &lblock);
-
-        for (i = 0; i < 16; i++) {
-                tag[i] = ctx->tag.b[i];
-        }
-}
-
-void aes_gcm_full_encrypt( const AESKey *k
-                         , uint8_t *iv, uint32_t ivLen
-                         , uint8_t *aad, uint32_t aadLen
-                         , uint8_t *pt, uint32_t ptLen
-                         , uint8_t *ct, uint8_t *tag)
-{
-    aes_gcm_ctx ctx;
-    aes_gcm gcm;
-    aes_gcm_init(&gcm, k);
-    aes_gcm_ctx_init(&ctx, &gcm, iv, ivLen);
-    aes_gcm_aad(&gcm, &ctx, aad, aadLen);
-    aes_gcm_encrypt(ct, &gcm, &ctx, pt, ptLen);
-    aes_gcm_finish(tag, &gcm, &ctx);
-}
-
-void aes_gcm_full_decrypt( const AESKey *k
-                         , uint8_t *iv, uint32_t ivLen
-                         , uint8_t *aad, uint32_t aadLen
-                         , uint8_t *ct, uint32_t ctLen
-                         , uint8_t *pt, uint8_t *tag)
-{
-    aes_gcm gcm;
-    aes_gcm_ctx ctx;
-    aes_gcm_init(&gcm, k);
-    aes_gcm_ctx_init(&ctx, &gcm, iv, ivLen);
-    aes_gcm_aad(&gcm, &ctx, aad, aadLen);
-    aes_gcm_decrypt(pt, &gcm, &ctx, ct, ctLen);
-    aes_gcm_finish(tag, &gcm, &ctx);
-}
-
-
-/* this is a really inefficient way to GF multiply.
- * the alternative without hw accel is building small tables
- * to speed up the multiplication.
- * TODO: optimise with tables
- */
-static void gf_mul(block128 *a, const block128 *b)
-{
-        uint64_t a0, a1, v0, v1;
-        int i, j;
-
-        a0 = a1 = 0;
-        v0 = cpu_to_be64(a->q[0]);
-        v1 = cpu_to_be64(a->q[1]);
-
-        for (i = 0; i < 16; i++)
-                for (j = 0x80; j != 0; j >>= 1) {
-                        uint8_t x = b->b[i] & j;
-                        a0 ^= x ? v0 : 0;
-                        a1 ^= x ? v1 : 0;
-                        x = (uint8_t) v1 & 1;
-                        v1 = (v1 >> 1) | (v0 << 63);
-                        v0 = (v0 >> 1) ^ (x ? (0xe1ULL << 56) : 0);
-                }
-        a->q[0] = cpu_to_be64(a0);
-        a->q[1] = cpu_to_be64(a1);
-}
-
-void encrypt_ctr(const AESKey *key, const uint8_t *iv, uint8_t *newIV, uint8_t *output, const uint8_t *input, uint32_t len)
-{
-        aes_block *block, o;
-        uint32_t nb_blocks = len / 16;
-        int i;
-
-        /* preload IV in block */
-        if(NULL != newIV)
-            block = (aes_block*) newIV;
-        else
-            block = alloca(sizeof(aes_block));
-
-        block128_copy(block, (aes_block *)iv);
-
-        for ( ; nb_blocks-- > 0; output += 16, input += 16) {
-                encrypt_ecb(key, (uint8_t *)&o, (uint8_t *)block, 1);
-                block128_vxor((block128 *) output, &o, (block128 *) input);
-                block128_inc_be(block);
-        }
-
-        if ((len % 16) != 0) {
-                encrypt_ecb(key, (uint8_t *)&o, (uint8_t *)block, 1);
-                for (i = 0; i < (len % 16); i++) {
-                        *output = ((uint8_t *) &o)[i] ^ *input;
-                        output += 1;
-                        input += 1;
-                }
-                block128_inc_be(block);
-        }
-}
-
diff --git a/cbits/aes/generic/aes_generic.c b/cbits/aes/generic/aes_generic.c
deleted file mode 100644
--- a/cbits/aes/generic/aes_generic.c
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- *
- * AES implementation
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include "aes_types.h"
-#include "bitfn.h"
-
-static uint8_t sbox[256] = {
-	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe,
-	0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4,
-	0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7,
-	0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3,
-	0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09,
-	0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3,
-	0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe,
-	0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
-	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92,
-	0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c,
-	0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
-	0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
-	0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2,
-	0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5,
-	0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
-	0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
-	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86,
-	0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e,
-	0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42,
-	0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
-};
-
-static uint8_t rsbox[256] = {
-	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81,
-	0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e,
-	0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23,
-	0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66,
-	0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72,
-	0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65,
-	0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46,
-	0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
-	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca,
-	0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91,
-	0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
-	0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
-	0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f,
-	0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2,
-	0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8,
-	0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
-	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93,
-	0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb,
-	0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6,
-	0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
-};
-
-static uint8_t Rcon[] = {
-	0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
-	0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
-	0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
-	0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb,
-};
-
-#define G(a,b,c,d,e,f) { a,b,c,d,e,f }
-static uint8_t gmtab[256][6] =
-{
-	G(0x00, 0x00, 0x00, 0x00, 0x00, 0x00), G(0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e),
-	G(0x04, 0x06, 0x12, 0x16, 0x1a, 0x1c), G(0x06, 0x05, 0x1b, 0x1d, 0x17, 0x12),
-	G(0x08, 0x0c, 0x24, 0x2c, 0x34, 0x38), G(0x0a, 0x0f, 0x2d, 0x27, 0x39, 0x36),
-	G(0x0c, 0x0a, 0x36, 0x3a, 0x2e, 0x24), G(0x0e, 0x09, 0x3f, 0x31, 0x23, 0x2a),
-	G(0x10, 0x18, 0x48, 0x58, 0x68, 0x70), G(0x12, 0x1b, 0x41, 0x53, 0x65, 0x7e),
-	G(0x14, 0x1e, 0x5a, 0x4e, 0x72, 0x6c), G(0x16, 0x1d, 0x53, 0x45, 0x7f, 0x62),
-	G(0x18, 0x14, 0x6c, 0x74, 0x5c, 0x48), G(0x1a, 0x17, 0x65, 0x7f, 0x51, 0x46),
-	G(0x1c, 0x12, 0x7e, 0x62, 0x46, 0x54), G(0x1e, 0x11, 0x77, 0x69, 0x4b, 0x5a),
-	G(0x20, 0x30, 0x90, 0xb0, 0xd0, 0xe0), G(0x22, 0x33, 0x99, 0xbb, 0xdd, 0xee),
-	G(0x24, 0x36, 0x82, 0xa6, 0xca, 0xfc), G(0x26, 0x35, 0x8b, 0xad, 0xc7, 0xf2),
-	G(0x28, 0x3c, 0xb4, 0x9c, 0xe4, 0xd8), G(0x2a, 0x3f, 0xbd, 0x97, 0xe9, 0xd6),
-	G(0x2c, 0x3a, 0xa6, 0x8a, 0xfe, 0xc4), G(0x2e, 0x39, 0xaf, 0x81, 0xf3, 0xca),
-	G(0x30, 0x28, 0xd8, 0xe8, 0xb8, 0x90), G(0x32, 0x2b, 0xd1, 0xe3, 0xb5, 0x9e),
-	G(0x34, 0x2e, 0xca, 0xfe, 0xa2, 0x8c), G(0x36, 0x2d, 0xc3, 0xf5, 0xaf, 0x82),
-	G(0x38, 0x24, 0xfc, 0xc4, 0x8c, 0xa8), G(0x3a, 0x27, 0xf5, 0xcf, 0x81, 0xa6),
-	G(0x3c, 0x22, 0xee, 0xd2, 0x96, 0xb4), G(0x3e, 0x21, 0xe7, 0xd9, 0x9b, 0xba),
-	G(0x40, 0x60, 0x3b, 0x7b, 0xbb, 0xdb), G(0x42, 0x63, 0x32, 0x70, 0xb6, 0xd5),
-	G(0x44, 0x66, 0x29, 0x6d, 0xa1, 0xc7), G(0x46, 0x65, 0x20, 0x66, 0xac, 0xc9),
-	G(0x48, 0x6c, 0x1f, 0x57, 0x8f, 0xe3), G(0x4a, 0x6f, 0x16, 0x5c, 0x82, 0xed),
-	G(0x4c, 0x6a, 0x0d, 0x41, 0x95, 0xff), G(0x4e, 0x69, 0x04, 0x4a, 0x98, 0xf1),
-	G(0x50, 0x78, 0x73, 0x23, 0xd3, 0xab), G(0x52, 0x7b, 0x7a, 0x28, 0xde, 0xa5),
-	G(0x54, 0x7e, 0x61, 0x35, 0xc9, 0xb7), G(0x56, 0x7d, 0x68, 0x3e, 0xc4, 0xb9),
-	G(0x58, 0x74, 0x57, 0x0f, 0xe7, 0x93), G(0x5a, 0x77, 0x5e, 0x04, 0xea, 0x9d),
-	G(0x5c, 0x72, 0x45, 0x19, 0xfd, 0x8f), G(0x5e, 0x71, 0x4c, 0x12, 0xf0, 0x81),
-	G(0x60, 0x50, 0xab, 0xcb, 0x6b, 0x3b), G(0x62, 0x53, 0xa2, 0xc0, 0x66, 0x35),
-	G(0x64, 0x56, 0xb9, 0xdd, 0x71, 0x27), G(0x66, 0x55, 0xb0, 0xd6, 0x7c, 0x29),
-	G(0x68, 0x5c, 0x8f, 0xe7, 0x5f, 0x03), G(0x6a, 0x5f, 0x86, 0xec, 0x52, 0x0d),
-	G(0x6c, 0x5a, 0x9d, 0xf1, 0x45, 0x1f), G(0x6e, 0x59, 0x94, 0xfa, 0x48, 0x11),
-	G(0x70, 0x48, 0xe3, 0x93, 0x03, 0x4b), G(0x72, 0x4b, 0xea, 0x98, 0x0e, 0x45),
-	G(0x74, 0x4e, 0xf1, 0x85, 0x19, 0x57), G(0x76, 0x4d, 0xf8, 0x8e, 0x14, 0x59),
-	G(0x78, 0x44, 0xc7, 0xbf, 0x37, 0x73), G(0x7a, 0x47, 0xce, 0xb4, 0x3a, 0x7d),
-	G(0x7c, 0x42, 0xd5, 0xa9, 0x2d, 0x6f), G(0x7e, 0x41, 0xdc, 0xa2, 0x20, 0x61),
-	G(0x80, 0xc0, 0x76, 0xf6, 0x6d, 0xad), G(0x82, 0xc3, 0x7f, 0xfd, 0x60, 0xa3),
-	G(0x84, 0xc6, 0x64, 0xe0, 0x77, 0xb1), G(0x86, 0xc5, 0x6d, 0xeb, 0x7a, 0xbf),
-	G(0x88, 0xcc, 0x52, 0xda, 0x59, 0x95), G(0x8a, 0xcf, 0x5b, 0xd1, 0x54, 0x9b),
-	G(0x8c, 0xca, 0x40, 0xcc, 0x43, 0x89), G(0x8e, 0xc9, 0x49, 0xc7, 0x4e, 0x87),
-	G(0x90, 0xd8, 0x3e, 0xae, 0x05, 0xdd), G(0x92, 0xdb, 0x37, 0xa5, 0x08, 0xd3),
-	G(0x94, 0xde, 0x2c, 0xb8, 0x1f, 0xc1), G(0x96, 0xdd, 0x25, 0xb3, 0x12, 0xcf),
-	G(0x98, 0xd4, 0x1a, 0x82, 0x31, 0xe5), G(0x9a, 0xd7, 0x13, 0x89, 0x3c, 0xeb),
-	G(0x9c, 0xd2, 0x08, 0x94, 0x2b, 0xf9), G(0x9e, 0xd1, 0x01, 0x9f, 0x26, 0xf7),
-	G(0xa0, 0xf0, 0xe6, 0x46, 0xbd, 0x4d), G(0xa2, 0xf3, 0xef, 0x4d, 0xb0, 0x43),
-	G(0xa4, 0xf6, 0xf4, 0x50, 0xa7, 0x51), G(0xa6, 0xf5, 0xfd, 0x5b, 0xaa, 0x5f),
-	G(0xa8, 0xfc, 0xc2, 0x6a, 0x89, 0x75), G(0xaa, 0xff, 0xcb, 0x61, 0x84, 0x7b),
-	G(0xac, 0xfa, 0xd0, 0x7c, 0x93, 0x69), G(0xae, 0xf9, 0xd9, 0x77, 0x9e, 0x67),
-	G(0xb0, 0xe8, 0xae, 0x1e, 0xd5, 0x3d), G(0xb2, 0xeb, 0xa7, 0x15, 0xd8, 0x33),
-	G(0xb4, 0xee, 0xbc, 0x08, 0xcf, 0x21), G(0xb6, 0xed, 0xb5, 0x03, 0xc2, 0x2f),
-	G(0xb8, 0xe4, 0x8a, 0x32, 0xe1, 0x05), G(0xba, 0xe7, 0x83, 0x39, 0xec, 0x0b),
-	G(0xbc, 0xe2, 0x98, 0x24, 0xfb, 0x19), G(0xbe, 0xe1, 0x91, 0x2f, 0xf6, 0x17),
-	G(0xc0, 0xa0, 0x4d, 0x8d, 0xd6, 0x76), G(0xc2, 0xa3, 0x44, 0x86, 0xdb, 0x78),
-	G(0xc4, 0xa6, 0x5f, 0x9b, 0xcc, 0x6a), G(0xc6, 0xa5, 0x56, 0x90, 0xc1, 0x64),
-	G(0xc8, 0xac, 0x69, 0xa1, 0xe2, 0x4e), G(0xca, 0xaf, 0x60, 0xaa, 0xef, 0x40),
-	G(0xcc, 0xaa, 0x7b, 0xb7, 0xf8, 0x52), G(0xce, 0xa9, 0x72, 0xbc, 0xf5, 0x5c),
-	G(0xd0, 0xb8, 0x05, 0xd5, 0xbe, 0x06), G(0xd2, 0xbb, 0x0c, 0xde, 0xb3, 0x08),
-	G(0xd4, 0xbe, 0x17, 0xc3, 0xa4, 0x1a), G(0xd6, 0xbd, 0x1e, 0xc8, 0xa9, 0x14),
-	G(0xd8, 0xb4, 0x21, 0xf9, 0x8a, 0x3e), G(0xda, 0xb7, 0x28, 0xf2, 0x87, 0x30),
-	G(0xdc, 0xb2, 0x33, 0xef, 0x90, 0x22), G(0xde, 0xb1, 0x3a, 0xe4, 0x9d, 0x2c),
-	G(0xe0, 0x90, 0xdd, 0x3d, 0x06, 0x96), G(0xe2, 0x93, 0xd4, 0x36, 0x0b, 0x98),
-	G(0xe4, 0x96, 0xcf, 0x2b, 0x1c, 0x8a), G(0xe6, 0x95, 0xc6, 0x20, 0x11, 0x84),
-	G(0xe8, 0x9c, 0xf9, 0x11, 0x32, 0xae), G(0xea, 0x9f, 0xf0, 0x1a, 0x3f, 0xa0),
-	G(0xec, 0x9a, 0xeb, 0x07, 0x28, 0xb2), G(0xee, 0x99, 0xe2, 0x0c, 0x25, 0xbc),
-	G(0xf0, 0x88, 0x95, 0x65, 0x6e, 0xe6), G(0xf2, 0x8b, 0x9c, 0x6e, 0x63, 0xe8),
-	G(0xf4, 0x8e, 0x87, 0x73, 0x74, 0xfa), G(0xf6, 0x8d, 0x8e, 0x78, 0x79, 0xf4),
-	G(0xf8, 0x84, 0xb1, 0x49, 0x5a, 0xde), G(0xfa, 0x87, 0xb8, 0x42, 0x57, 0xd0),
-	G(0xfc, 0x82, 0xa3, 0x5f, 0x40, 0xc2), G(0xfe, 0x81, 0xaa, 0x54, 0x4d, 0xcc),
-	G(0x1b, 0x9b, 0xec, 0xf7, 0xda, 0x41), G(0x19, 0x98, 0xe5, 0xfc, 0xd7, 0x4f),
-	G(0x1f, 0x9d, 0xfe, 0xe1, 0xc0, 0x5d), G(0x1d, 0x9e, 0xf7, 0xea, 0xcd, 0x53),
-	G(0x13, 0x97, 0xc8, 0xdb, 0xee, 0x79), G(0x11, 0x94, 0xc1, 0xd0, 0xe3, 0x77),
-	G(0x17, 0x91, 0xda, 0xcd, 0xf4, 0x65), G(0x15, 0x92, 0xd3, 0xc6, 0xf9, 0x6b),
-	G(0x0b, 0x83, 0xa4, 0xaf, 0xb2, 0x31), G(0x09, 0x80, 0xad, 0xa4, 0xbf, 0x3f),
-	G(0x0f, 0x85, 0xb6, 0xb9, 0xa8, 0x2d), G(0x0d, 0x86, 0xbf, 0xb2, 0xa5, 0x23),
-	G(0x03, 0x8f, 0x80, 0x83, 0x86, 0x09), G(0x01, 0x8c, 0x89, 0x88, 0x8b, 0x07),
-	G(0x07, 0x89, 0x92, 0x95, 0x9c, 0x15), G(0x05, 0x8a, 0x9b, 0x9e, 0x91, 0x1b),
-	G(0x3b, 0xab, 0x7c, 0x47, 0x0a, 0xa1), G(0x39, 0xa8, 0x75, 0x4c, 0x07, 0xaf),
-	G(0x3f, 0xad, 0x6e, 0x51, 0x10, 0xbd), G(0x3d, 0xae, 0x67, 0x5a, 0x1d, 0xb3),
-	G(0x33, 0xa7, 0x58, 0x6b, 0x3e, 0x99), G(0x31, 0xa4, 0x51, 0x60, 0x33, 0x97),
-	G(0x37, 0xa1, 0x4a, 0x7d, 0x24, 0x85), G(0x35, 0xa2, 0x43, 0x76, 0x29, 0x8b),
-	G(0x2b, 0xb3, 0x34, 0x1f, 0x62, 0xd1), G(0x29, 0xb0, 0x3d, 0x14, 0x6f, 0xdf),
-	G(0x2f, 0xb5, 0x26, 0x09, 0x78, 0xcd), G(0x2d, 0xb6, 0x2f, 0x02, 0x75, 0xc3),
-	G(0x23, 0xbf, 0x10, 0x33, 0x56, 0xe9), G(0x21, 0xbc, 0x19, 0x38, 0x5b, 0xe7),
-	G(0x27, 0xb9, 0x02, 0x25, 0x4c, 0xf5), G(0x25, 0xba, 0x0b, 0x2e, 0x41, 0xfb),
-	G(0x5b, 0xfb, 0xd7, 0x8c, 0x61, 0x9a), G(0x59, 0xf8, 0xde, 0x87, 0x6c, 0x94),
-	G(0x5f, 0xfd, 0xc5, 0x9a, 0x7b, 0x86), G(0x5d, 0xfe, 0xcc, 0x91, 0x76, 0x88),
-	G(0x53, 0xf7, 0xf3, 0xa0, 0x55, 0xa2), G(0x51, 0xf4, 0xfa, 0xab, 0x58, 0xac),
-	G(0x57, 0xf1, 0xe1, 0xb6, 0x4f, 0xbe), G(0x55, 0xf2, 0xe8, 0xbd, 0x42, 0xb0),
-	G(0x4b, 0xe3, 0x9f, 0xd4, 0x09, 0xea), G(0x49, 0xe0, 0x96, 0xdf, 0x04, 0xe4),
-	G(0x4f, 0xe5, 0x8d, 0xc2, 0x13, 0xf6), G(0x4d, 0xe6, 0x84, 0xc9, 0x1e, 0xf8),
-	G(0x43, 0xef, 0xbb, 0xf8, 0x3d, 0xd2), G(0x41, 0xec, 0xb2, 0xf3, 0x30, 0xdc),
-	G(0x47, 0xe9, 0xa9, 0xee, 0x27, 0xce), G(0x45, 0xea, 0xa0, 0xe5, 0x2a, 0xc0),
-	G(0x7b, 0xcb, 0x47, 0x3c, 0xb1, 0x7a), G(0x79, 0xc8, 0x4e, 0x37, 0xbc, 0x74),
-	G(0x7f, 0xcd, 0x55, 0x2a, 0xab, 0x66), G(0x7d, 0xce, 0x5c, 0x21, 0xa6, 0x68),
-	G(0x73, 0xc7, 0x63, 0x10, 0x85, 0x42), G(0x71, 0xc4, 0x6a, 0x1b, 0x88, 0x4c),
-	G(0x77, 0xc1, 0x71, 0x06, 0x9f, 0x5e), G(0x75, 0xc2, 0x78, 0x0d, 0x92, 0x50),
-	G(0x6b, 0xd3, 0x0f, 0x64, 0xd9, 0x0a), G(0x69, 0xd0, 0x06, 0x6f, 0xd4, 0x04),
-	G(0x6f, 0xd5, 0x1d, 0x72, 0xc3, 0x16), G(0x6d, 0xd6, 0x14, 0x79, 0xce, 0x18),
-	G(0x63, 0xdf, 0x2b, 0x48, 0xed, 0x32), G(0x61, 0xdc, 0x22, 0x43, 0xe0, 0x3c),
-	G(0x67, 0xd9, 0x39, 0x5e, 0xf7, 0x2e), G(0x65, 0xda, 0x30, 0x55, 0xfa, 0x20),
-	G(0x9b, 0x5b, 0x9a, 0x01, 0xb7, 0xec), G(0x99, 0x58, 0x93, 0x0a, 0xba, 0xe2),
-	G(0x9f, 0x5d, 0x88, 0x17, 0xad, 0xf0), G(0x9d, 0x5e, 0x81, 0x1c, 0xa0, 0xfe),
-	G(0x93, 0x57, 0xbe, 0x2d, 0x83, 0xd4), G(0x91, 0x54, 0xb7, 0x26, 0x8e, 0xda),
-	G(0x97, 0x51, 0xac, 0x3b, 0x99, 0xc8), G(0x95, 0x52, 0xa5, 0x30, 0x94, 0xc6),
-	G(0x8b, 0x43, 0xd2, 0x59, 0xdf, 0x9c), G(0x89, 0x40, 0xdb, 0x52, 0xd2, 0x92),
-	G(0x8f, 0x45, 0xc0, 0x4f, 0xc5, 0x80), G(0x8d, 0x46, 0xc9, 0x44, 0xc8, 0x8e),
-	G(0x83, 0x4f, 0xf6, 0x75, 0xeb, 0xa4), G(0x81, 0x4c, 0xff, 0x7e, 0xe6, 0xaa),
-	G(0x87, 0x49, 0xe4, 0x63, 0xf1, 0xb8), G(0x85, 0x4a, 0xed, 0x68, 0xfc, 0xb6),
-	G(0xbb, 0x6b, 0x0a, 0xb1, 0x67, 0x0c), G(0xb9, 0x68, 0x03, 0xba, 0x6a, 0x02),
-	G(0xbf, 0x6d, 0x18, 0xa7, 0x7d, 0x10), G(0xbd, 0x6e, 0x11, 0xac, 0x70, 0x1e),
-	G(0xb3, 0x67, 0x2e, 0x9d, 0x53, 0x34), G(0xb1, 0x64, 0x27, 0x96, 0x5e, 0x3a),
-	G(0xb7, 0x61, 0x3c, 0x8b, 0x49, 0x28), G(0xb5, 0x62, 0x35, 0x80, 0x44, 0x26),
-	G(0xab, 0x73, 0x42, 0xe9, 0x0f, 0x7c), G(0xa9, 0x70, 0x4b, 0xe2, 0x02, 0x72),
-	G(0xaf, 0x75, 0x50, 0xff, 0x15, 0x60), G(0xad, 0x76, 0x59, 0xf4, 0x18, 0x6e),
-	G(0xa3, 0x7f, 0x66, 0xc5, 0x3b, 0x44), G(0xa1, 0x7c, 0x6f, 0xce, 0x36, 0x4a),
-	G(0xa7, 0x79, 0x74, 0xd3, 0x21, 0x58), G(0xa5, 0x7a, 0x7d, 0xd8, 0x2c, 0x56),
-	G(0xdb, 0x3b, 0xa1, 0x7a, 0x0c, 0x37), G(0xd9, 0x38, 0xa8, 0x71, 0x01, 0x39),
-	G(0xdf, 0x3d, 0xb3, 0x6c, 0x16, 0x2b), G(0xdd, 0x3e, 0xba, 0x67, 0x1b, 0x25),
-	G(0xd3, 0x37, 0x85, 0x56, 0x38, 0x0f), G(0xd1, 0x34, 0x8c, 0x5d, 0x35, 0x01),
-	G(0xd7, 0x31, 0x97, 0x40, 0x22, 0x13), G(0xd5, 0x32, 0x9e, 0x4b, 0x2f, 0x1d),
-	G(0xcb, 0x23, 0xe9, 0x22, 0x64, 0x47), G(0xc9, 0x20, 0xe0, 0x29, 0x69, 0x49),
-	G(0xcf, 0x25, 0xfb, 0x34, 0x7e, 0x5b), G(0xcd, 0x26, 0xf2, 0x3f, 0x73, 0x55),
-	G(0xc3, 0x2f, 0xcd, 0x0e, 0x50, 0x7f), G(0xc1, 0x2c, 0xc4, 0x05, 0x5d, 0x71),
-	G(0xc7, 0x29, 0xdf, 0x18, 0x4a, 0x63), G(0xc5, 0x2a, 0xd6, 0x13, 0x47, 0x6d),
-	G(0xfb, 0x0b, 0x31, 0xca, 0xdc, 0xd7), G(0xf9, 0x08, 0x38, 0xc1, 0xd1, 0xd9),
-	G(0xff, 0x0d, 0x23, 0xdc, 0xc6, 0xcb), G(0xfd, 0x0e, 0x2a, 0xd7, 0xcb, 0xc5),
-	G(0xf3, 0x07, 0x15, 0xe6, 0xe8, 0xef), G(0xf1, 0x04, 0x1c, 0xed, 0xe5, 0xe1),
-	G(0xf7, 0x01, 0x07, 0xf0, 0xf2, 0xf3), G(0xf5, 0x02, 0x0e, 0xfb, 0xff, 0xfd),
-	G(0xeb, 0x13, 0x79, 0x92, 0xb4, 0xa7), G(0xe9, 0x10, 0x70, 0x99, 0xb9, 0xa9),
-	G(0xef, 0x15, 0x6b, 0x84, 0xae, 0xbb), G(0xed, 0x16, 0x62, 0x8f, 0xa3, 0xb5),
-	G(0xe3, 0x1f, 0x5d, 0xbe, 0x80, 0x9f), G(0xe1, 0x1c, 0x54, 0xb5, 0x8d, 0x91),
-	G(0xe7, 0x19, 0x4f, 0xa8, 0x9a, 0x83), G(0xe5, 0x1a, 0x46, 0xa3, 0x97, 0x8d),
-};
-#undef G
-
-static void expand_key(uint8_t *expandedKey, const uint8_t *key, int size, size_t expandedKeySize)
-{
-	int csz;
-	int i;
-	uint8_t t[4] = { 0 };
-
-	for (i = 0; i < size; i++)
-		expandedKey[i] = key[i];
-	csz = size;
-
-	i = 1;
-	while (csz < expandedKeySize) {
-		t[0] = expandedKey[(csz - 4) + 0];
-		t[1] = expandedKey[(csz - 4) + 1];
-		t[2] = expandedKey[(csz - 4) + 2];
-		t[3] = expandedKey[(csz - 4) + 3];
-
-		if (csz % size == 0) {
-			uint8_t tmp;
-
-			tmp = t[0];
-			t[0] = sbox[t[1]] ^ Rcon[i++ % sizeof(Rcon)];
-			t[1] = sbox[t[2]];
-			t[2] = sbox[t[3]];
-			t[3] = sbox[tmp];
-		}
-
-		if (size == 32 && ((csz % size) == 16)) {
-			t[0] = sbox[t[0]];
-			t[1] = sbox[t[1]];
-			t[2] = sbox[t[2]];
-			t[3] = sbox[t[3]];
-		}
-
-		expandedKey[csz] = expandedKey[csz - size] ^ t[0]; csz++;
-		expandedKey[csz] = expandedKey[csz - size] ^ t[1]; csz++;
-		expandedKey[csz] = expandedKey[csz - size] ^ t[2]; csz++;
-		expandedKey[csz] = expandedKey[csz - size] ^ t[3]; csz++;
-	}
-}
-
-static void shift_rows(uint8_t *state)
-{
-	uint32_t *s32;
-	int i;
-
-	for (i = 0; i < 16; i++)
-		state[i] = sbox[state[i]];
-	s32 = (uint32_t *) state;
-	s32[1] = rol32_be(s32[1], 8);
-	s32[2] = rol32_be(s32[2], 16);
-	s32[3] = rol32_be(s32[3], 24);
-}
-
-static void add_round_key(uint8_t *state, uint8_t *rk)
-{
-	uint32_t *s32, *r32;
-
-	s32 = (uint32_t *) state;
-	r32 = (uint32_t *) rk;
-	s32[0] ^= r32[0];
-	s32[1] ^= r32[1];
-	s32[2] ^= r32[2];
-	s32[3] ^= r32[3];
-}
-
-#define gm1(a) (a)
-#define gm2(a) gmtab[a][0]
-#define gm3(a) gmtab[a][1]
-#define gm9(a) gmtab[a][2]
-#define gm11(a) gmtab[a][3]
-#define gm13(a) gmtab[a][4]
-#define gm14(a) gmtab[a][5]
-
-static void mix_columns(uint8_t *state)
-{
-	int i;
-	uint8_t cpy[4];
-
-	for (i = 0; i < 4; i++) {
-		cpy[0] = state[0 * 4 + i];
-		cpy[1] = state[1 * 4 + i];
-		cpy[2] = state[2 * 4 + i];
-		cpy[3] = state[3 * 4 + i];
-		state[i] = gm2(cpy[0]) ^ gm1(cpy[3]) ^ gm1(cpy[2]) ^ gm3(cpy[1]);
-		state[4+i] = gm2(cpy[1]) ^ gm1(cpy[0]) ^ gm1(cpy[3]) ^ gm3(cpy[2]);
-		state[8+i] = gm2(cpy[2]) ^ gm1(cpy[1]) ^ gm1(cpy[0]) ^ gm3(cpy[3]);
-		state[12+i] = gm2(cpy[3]) ^ gm1(cpy[2]) ^ gm1(cpy[1]) ^ gm3(cpy[0]);
-	}
-}
-
-static void create_round_key(const uint8_t *expandedKey, uint8_t *rk)
-{
-	int i,j;
-	for (i = 0; i < 4; i++)
-		for (j = 0; j < 4; j++)
-			rk[i + j * 4] = expandedKey[i * 4 + j];
-}
-
-static void aes_main(const aes_key *key, uint8_t *state)
-{
-	int i = 0;
-	uint8_t rk[16];
-
-	create_round_key(key->data, rk);
-	add_round_key(state, rk);
-
-	for (i = 1; i < key->nbr; i++) {
-		create_round_key(key->data + 16 * i, rk);
-		shift_rows(state);
-		mix_columns(state);
-		add_round_key(state, rk);
-	}
-
-	create_round_key(key->data + 16 * key->nbr, rk);
-	shift_rows(state);
-	add_round_key(state, rk);
-}
-
-static void shift_rows_inv(uint8_t *state)
-{
-	uint32_t *s32;
-	int i;
-
-	s32 = (uint32_t *) state;
-	s32[1] = ror32_be(s32[1], 8);
-	s32[2] = ror32_be(s32[2], 16);
-	s32[3] = ror32_be(s32[3], 24);
-	for (i = 0; i < 16; i++)
-		state[i] = rsbox[state[i]];
-}
-
-static void mix_columns_inv(uint8_t *state)
-{
-	int i;
-	uint8_t cpy[4];
-
-	for (i = 0; i < 4; i++) {
-		cpy[0] = state[0 * 4 + i];
-		cpy[1] = state[1 * 4 + i];
-		cpy[2] = state[2 * 4 + i];
-		cpy[3] = state[3 * 4 + i];
-		state[i] = gm14(cpy[0]) ^ gm9(cpy[3]) ^ gm13(cpy[2]) ^ gm11(cpy[1]);
-		state[4+i] = gm14(cpy[1]) ^ gm9(cpy[0]) ^ gm13(cpy[3]) ^ gm11(cpy[2]);
-		state[8+i] = gm14(cpy[2]) ^ gm9(cpy[1]) ^ gm13(cpy[0]) ^ gm11(cpy[3]);
-		state[12+i] = gm14(cpy[3]) ^ gm9(cpy[2]) ^ gm13(cpy[1]) ^ gm11(cpy[0]);
-	}
-}
-
-static void aes_main_inv(const aes_key *key, uint8_t *state)
-{
-	int i = 0;
-	uint8_t rk[16];
-
-	create_round_key(key->data + 16 * key->nbr, rk);
-	add_round_key(state, rk);
-
-	for (i = key->nbr - 1; i > 0; i--) {
-		create_round_key(key->data + 16 * i, rk);
-		shift_rows_inv(state);
-		add_round_key(state, rk);
-		mix_columns_inv(state);
-	}
-
-	create_round_key(key->data, rk);
-	shift_rows_inv(state);
-	add_round_key(state, rk);
-}
-
-/* Set the block values, for the block:
- * a0,0 a0,1 a0,2 a0,3
- * a1,0 a1,1 a1,2 a1,3 -> a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
- * a2,0 a2,1 a2,2 a2,3
- * a3,0 a3,1 a3,2 a3,3
- */
-#define swap_block(t, f) \
-	t[0] = f[0]; t[4] = f[1]; t[8] = f[2]; t[12] = f[3]; \
-	t[1] = f[4]; t[5] = f[5]; t[9] = f[6]; t[13] = f[7]; \
-	t[2] = f[8]; t[6] = f[9]; t[10] = f[10]; t[14] = f[11]; \
-	t[3] = f[12]; t[7] = f[13]; t[11] = f[14]; t[15] = f[15]
-
-void aes128_generic_encrypt_block(aes_block *output, const aes_key *key, const aes_block *input)
-{
-	uint8_t block[16];
-	uint8_t *iptr, *optr;
-
-	iptr = (uint8_t *) input;
-	optr = (uint8_t *) output;
-	swap_block(block, iptr);
-	aes_main(key, block);
-	swap_block(optr, block);
-}
-
-void aes128_generic_decrypt_block(aes_block *output, const aes_key *key, const aes_block *input)
-{
-	uint8_t block[16];
-	uint8_t *iptr, *optr;
-
-	iptr = (uint8_t *) input;
-	optr = (uint8_t *) output;
-	swap_block(block, iptr);
-	aes_main_inv(key, block);
-	swap_block(optr, block);
-}
-
-void aes128_generic_init(aes_key *key, const uint8_t *origkey, uint8_t size)
-{
-	int esz;
-
-	switch (size) {
-	case 16: key->nbr = 10; esz = 176; break;
-	case 24: key->nbr = 12; esz = 208; break;
-	case 32: key->nbr = 14; esz = 240; break;
-	default: return;
-	}
-	expand_key(key->data, origkey, size, esz);
-	return;
-}
-
diff --git a/cbits/aes/ni/aes_x86ni.c b/cbits/aes/ni/aes_x86ni.c
deleted file mode 100644
--- a/cbits/aes/ni/aes_x86ni.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * 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.
- */
-#if defined(HAVE_AES_INTRINSICS) && !defined(AVOID_NI)
-
-#include <wmmintrin.h>
-#include <tmmintrin.h>
-#include "aes.h"
-#include "aes_x86ni.h"
-// #include "cpu.h"
-
-#if (defined(__i386__) || defined(__x86_64__))
-#define ALIGN_UP(addr, size) (((addr) + ((size) - 1)) & (~((size) - 1)))
-#define ALIGNMENT(n) __attribute__((aligned(n)))
-
-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);
-}
-
-static void aes_generate_key128(aes_key *key, const uint8_t *ikey)
-{
-	__m128i k[20];
-	uint64_t *out = (uint64_t *) key->data;
-	int i;
-
-	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]);
-
-	for (i = 0; i < 20; i++)
-		_mm_storeu_si128(((__m128i *) out) + i, k[i]);
-}
-
-void aes128_ni_init(aes_key *key, const uint8_t *origkey, uint8_t size)
-{
-	switch (size) {
-	case 16: aes_generate_key128(key, origkey); break;
-	default: break;
-	}
-}
-
-
-#define PRELOAD_ENC_KEYS(k) \
-	__m128i K0  = _mm_loadu_si128(((__m128i *) k)+0); \
-	__m128i K1  = _mm_loadu_si128(((__m128i *) k)+1); \
-	__m128i K2  = _mm_loadu_si128(((__m128i *) k)+2); \
-	__m128i K3  = _mm_loadu_si128(((__m128i *) k)+3); \
-	__m128i K4  = _mm_loadu_si128(((__m128i *) k)+4); \
-	__m128i K5  = _mm_loadu_si128(((__m128i *) k)+5); \
-	__m128i K6  = _mm_loadu_si128(((__m128i *) k)+6); \
-	__m128i K7  = _mm_loadu_si128(((__m128i *) k)+7); \
-	__m128i K8  = _mm_loadu_si128(((__m128i *) k)+8); \
-	__m128i K9  = _mm_loadu_si128(((__m128i *) k)+9); \
-	__m128i K10 = _mm_loadu_si128(((__m128i *) 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  = _mm_loadu_si128(((__m128i *) k)+10+0); \
-	__m128i K1  = _mm_loadu_si128(((__m128i *) k)+10+1); \
-	__m128i K2  = _mm_loadu_si128(((__m128i *) k)+10+2); \
-	__m128i K3  = _mm_loadu_si128(((__m128i *) k)+10+3); \
-	__m128i K4  = _mm_loadu_si128(((__m128i *) k)+10+4); \
-	__m128i K5  = _mm_loadu_si128(((__m128i *) k)+10+5); \
-	__m128i K6  = _mm_loadu_si128(((__m128i *) k)+10+6); \
-	__m128i K7  = _mm_loadu_si128(((__m128i *) k)+10+7); \
-	__m128i K8  = _mm_loadu_si128(((__m128i *) k)+10+8); \
-	__m128i K9  = _mm_loadu_si128(((__m128i *) k)+10+9); \
-	__m128i K10 = _mm_loadu_si128(((__m128i *) 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 aes128_ni_encrypt_ecb(uint8_t *out, aes_key *key, const uint8_t *in, uint32_t blocks)
-{
-	__m128i *k = (__m128i *) key->data;
-
-	PRELOAD_ENC_KEYS(k);
-
-	while (blocks-- > 0) {
-		__m128i m = _mm_loadu_si128((__m128i *) in);
-
-		DO_ENC_BLOCK(m);
-
-		_mm_storeu_si128((__m128i *) out, m);
-		in += 16;
-		out += 16;
-	}
-}
-
-void aes128_ni_decrypt_ecb(uint8_t *out, aes_key *key, const uint8_t *in, uint32_t blocks)
-{
-	__m128i *k = (__m128i *) key->data;
-
-	PRELOAD_DEC_KEYS(k);
-
-	while (blocks-- > 0) {
-		__m128i m = _mm_loadu_si128((__m128i *) in);
-
-		DO_DEC_BLOCK(m);
-
-		_mm_storeu_si128((__m128i *) out, m);
-		in += 16;
-		out += 16;
-	}
-}
-
-void aes128_ni_encrypt_cbc(uint8_t *out, aes_key *key, uint8_t *_iv, uint8_t *in, uint32_t blocks)
-{
-	__m128i *k = (__m128i *) key->data;
-	__m128i iv = _mm_loadu_si128((__m128i *) _iv);
-
-	PRELOAD_ENC_KEYS(k);
-
-	while (blocks-- > 0) {
-		__m128i m = _mm_loadu_si128((__m128i *) in);
-		m = _mm_xor_si128(m, iv);
-
-		DO_ENC_BLOCK(m);
-
-		_mm_storeu_si128((__m128i *) out, m);
-		iv = m;
-
-		in += 16;
-		out += 16;
-	}
-}
-
-void aes128_ni_decrypt_cbc(uint8_t *out, aes_key *key, uint8_t *_iv, uint8_t *in, uint32_t blocks)
-{
-	__m128i *k = (__m128i *) key->data;
-	__m128i iv = _mm_loadu_si128((__m128i *) _iv);
-
-	PRELOAD_DEC_KEYS(k);
-
-	while (blocks-- > 0) {
-		__m128i m = _mm_loadu_si128((__m128i *) in);
-		__m128i ivnext = m;
-
-		DO_DEC_BLOCK(m);
-		m = _mm_xor_si128(m, iv);
-
-		_mm_storeu_si128((__m128i *) out, m);
-		iv = ivnext;
-
-		in += 16;
-		out += 16;
-	}
-}
-
-/* TO OPTIMISE: use pcmulqdq... or some faster code.
- * this is the lamest way of doing it, but i'm out of time.
- * this is basically a copy of gf_mulx in gf.c */
-static __m128i gfmulx(__m128i v)
-{
-	uint64_t v_[2] ALIGNMENT(16);
-	const uint64_t gf_mask = 0x8000000000000000;
-
-	_mm_store_si128((__m128i *) v_, v);
-	uint64_t r = ((v_[1] & gf_mask) ? 0x87 : 0);
-	v_[1] = (v_[1] << 1) | (v_[0] & gf_mask ? 1 : 0);
-	v_[0] = (v_[0] << 1) ^ r;
-	v = _mm_load_si128((__m128i *) v_);
-	return v;
-}
-
-void aes128_ni_encrypt_xts(uint8_t *out, aes_key *key1, aes_key *key2,
-                        uint8_t *_tweak, uint32_t spoint, uint8_t *in, uint32_t blocks)
-{
-	__m128i tweak = _mm_loadu_si128((__m128i *) _tweak);
-
-	do {
-		__m128i *k2 = (__m128i *) key2->data;
-		PRELOAD_ENC_KEYS(k2);
-		DO_ENC_BLOCK(tweak);
-
-		while (spoint-- > 0)
-			tweak = gfmulx(tweak);
-	} while (0) ;
-
-	do {
-		__m128i *k1 = (__m128i *) key1->data;
-		PRELOAD_ENC_KEYS(k1);
-
-		for ( ; blocks-- > 0; in += 16, out += 16, tweak = gfmulx(tweak)) {
-			__m128i m = _mm_loadu_si128((__m128i *) in);
-
-			m = _mm_xor_si128(m, tweak);
-			DO_ENC_BLOCK(m);
-			m = _mm_xor_si128(m, tweak);
-
-			_mm_storeu_si128((__m128i *) out, m);
-		}
-	} while (0);
-}
-
-#endif /* x86 */
-
-#endif /* HAVE_AES_INTRINSICS */
diff --git a/cbits/aes_generic.c b/cbits/aes_generic.c
new file mode 100644
--- /dev/null
+++ b/cbits/aes_generic.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2008 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.
+ *
+ * AES implementation
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include "aes.h"
+#include "bitfn.h"
+
+static uint8_t sbox[256] = {
+	0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe,
+	0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4,
+	0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7,
+	0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3,
+	0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09,
+	0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3,
+	0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe,
+	0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+	0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92,
+	0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c,
+	0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19,
+	0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14,
+	0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2,
+	0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5,
+	0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
+	0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+	0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86,
+	0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e,
+	0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42,
+	0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+};
+
+static uint8_t rsbox[256] = {
+	0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81,
+	0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e,
+	0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23,
+	0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66,
+	0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72,
+	0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65,
+	0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46,
+	0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
+	0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca,
+	0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91,
+	0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6,
+	0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8,
+	0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f,
+	0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2,
+	0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8,
+	0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
+	0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93,
+	0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb,
+	0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6,
+	0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
+};
+
+static uint8_t Rcon[] = {
+	0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
+	0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
+	0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
+	0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb,
+};
+
+#define G(a,b,c,d,e,f) { a,b,c,d,e,f }
+static uint8_t gmtab[256][6] =
+{
+	G(0x00, 0x00, 0x00, 0x00, 0x00, 0x00), G(0x02, 0x03, 0x09, 0x0b, 0x0d, 0x0e),
+	G(0x04, 0x06, 0x12, 0x16, 0x1a, 0x1c), G(0x06, 0x05, 0x1b, 0x1d, 0x17, 0x12),
+	G(0x08, 0x0c, 0x24, 0x2c, 0x34, 0x38), G(0x0a, 0x0f, 0x2d, 0x27, 0x39, 0x36),
+	G(0x0c, 0x0a, 0x36, 0x3a, 0x2e, 0x24), G(0x0e, 0x09, 0x3f, 0x31, 0x23, 0x2a),
+	G(0x10, 0x18, 0x48, 0x58, 0x68, 0x70), G(0x12, 0x1b, 0x41, 0x53, 0x65, 0x7e),
+	G(0x14, 0x1e, 0x5a, 0x4e, 0x72, 0x6c), G(0x16, 0x1d, 0x53, 0x45, 0x7f, 0x62),
+	G(0x18, 0x14, 0x6c, 0x74, 0x5c, 0x48), G(0x1a, 0x17, 0x65, 0x7f, 0x51, 0x46),
+	G(0x1c, 0x12, 0x7e, 0x62, 0x46, 0x54), G(0x1e, 0x11, 0x77, 0x69, 0x4b, 0x5a),
+	G(0x20, 0x30, 0x90, 0xb0, 0xd0, 0xe0), G(0x22, 0x33, 0x99, 0xbb, 0xdd, 0xee),
+	G(0x24, 0x36, 0x82, 0xa6, 0xca, 0xfc), G(0x26, 0x35, 0x8b, 0xad, 0xc7, 0xf2),
+	G(0x28, 0x3c, 0xb4, 0x9c, 0xe4, 0xd8), G(0x2a, 0x3f, 0xbd, 0x97, 0xe9, 0xd6),
+	G(0x2c, 0x3a, 0xa6, 0x8a, 0xfe, 0xc4), G(0x2e, 0x39, 0xaf, 0x81, 0xf3, 0xca),
+	G(0x30, 0x28, 0xd8, 0xe8, 0xb8, 0x90), G(0x32, 0x2b, 0xd1, 0xe3, 0xb5, 0x9e),
+	G(0x34, 0x2e, 0xca, 0xfe, 0xa2, 0x8c), G(0x36, 0x2d, 0xc3, 0xf5, 0xaf, 0x82),
+	G(0x38, 0x24, 0xfc, 0xc4, 0x8c, 0xa8), G(0x3a, 0x27, 0xf5, 0xcf, 0x81, 0xa6),
+	G(0x3c, 0x22, 0xee, 0xd2, 0x96, 0xb4), G(0x3e, 0x21, 0xe7, 0xd9, 0x9b, 0xba),
+	G(0x40, 0x60, 0x3b, 0x7b, 0xbb, 0xdb), G(0x42, 0x63, 0x32, 0x70, 0xb6, 0xd5),
+	G(0x44, 0x66, 0x29, 0x6d, 0xa1, 0xc7), G(0x46, 0x65, 0x20, 0x66, 0xac, 0xc9),
+	G(0x48, 0x6c, 0x1f, 0x57, 0x8f, 0xe3), G(0x4a, 0x6f, 0x16, 0x5c, 0x82, 0xed),
+	G(0x4c, 0x6a, 0x0d, 0x41, 0x95, 0xff), G(0x4e, 0x69, 0x04, 0x4a, 0x98, 0xf1),
+	G(0x50, 0x78, 0x73, 0x23, 0xd3, 0xab), G(0x52, 0x7b, 0x7a, 0x28, 0xde, 0xa5),
+	G(0x54, 0x7e, 0x61, 0x35, 0xc9, 0xb7), G(0x56, 0x7d, 0x68, 0x3e, 0xc4, 0xb9),
+	G(0x58, 0x74, 0x57, 0x0f, 0xe7, 0x93), G(0x5a, 0x77, 0x5e, 0x04, 0xea, 0x9d),
+	G(0x5c, 0x72, 0x45, 0x19, 0xfd, 0x8f), G(0x5e, 0x71, 0x4c, 0x12, 0xf0, 0x81),
+	G(0x60, 0x50, 0xab, 0xcb, 0x6b, 0x3b), G(0x62, 0x53, 0xa2, 0xc0, 0x66, 0x35),
+	G(0x64, 0x56, 0xb9, 0xdd, 0x71, 0x27), G(0x66, 0x55, 0xb0, 0xd6, 0x7c, 0x29),
+	G(0x68, 0x5c, 0x8f, 0xe7, 0x5f, 0x03), G(0x6a, 0x5f, 0x86, 0xec, 0x52, 0x0d),
+	G(0x6c, 0x5a, 0x9d, 0xf1, 0x45, 0x1f), G(0x6e, 0x59, 0x94, 0xfa, 0x48, 0x11),
+	G(0x70, 0x48, 0xe3, 0x93, 0x03, 0x4b), G(0x72, 0x4b, 0xea, 0x98, 0x0e, 0x45),
+	G(0x74, 0x4e, 0xf1, 0x85, 0x19, 0x57), G(0x76, 0x4d, 0xf8, 0x8e, 0x14, 0x59),
+	G(0x78, 0x44, 0xc7, 0xbf, 0x37, 0x73), G(0x7a, 0x47, 0xce, 0xb4, 0x3a, 0x7d),
+	G(0x7c, 0x42, 0xd5, 0xa9, 0x2d, 0x6f), G(0x7e, 0x41, 0xdc, 0xa2, 0x20, 0x61),
+	G(0x80, 0xc0, 0x76, 0xf6, 0x6d, 0xad), G(0x82, 0xc3, 0x7f, 0xfd, 0x60, 0xa3),
+	G(0x84, 0xc6, 0x64, 0xe0, 0x77, 0xb1), G(0x86, 0xc5, 0x6d, 0xeb, 0x7a, 0xbf),
+	G(0x88, 0xcc, 0x52, 0xda, 0x59, 0x95), G(0x8a, 0xcf, 0x5b, 0xd1, 0x54, 0x9b),
+	G(0x8c, 0xca, 0x40, 0xcc, 0x43, 0x89), G(0x8e, 0xc9, 0x49, 0xc7, 0x4e, 0x87),
+	G(0x90, 0xd8, 0x3e, 0xae, 0x05, 0xdd), G(0x92, 0xdb, 0x37, 0xa5, 0x08, 0xd3),
+	G(0x94, 0xde, 0x2c, 0xb8, 0x1f, 0xc1), G(0x96, 0xdd, 0x25, 0xb3, 0x12, 0xcf),
+	G(0x98, 0xd4, 0x1a, 0x82, 0x31, 0xe5), G(0x9a, 0xd7, 0x13, 0x89, 0x3c, 0xeb),
+	G(0x9c, 0xd2, 0x08, 0x94, 0x2b, 0xf9), G(0x9e, 0xd1, 0x01, 0x9f, 0x26, 0xf7),
+	G(0xa0, 0xf0, 0xe6, 0x46, 0xbd, 0x4d), G(0xa2, 0xf3, 0xef, 0x4d, 0xb0, 0x43),
+	G(0xa4, 0xf6, 0xf4, 0x50, 0xa7, 0x51), G(0xa6, 0xf5, 0xfd, 0x5b, 0xaa, 0x5f),
+	G(0xa8, 0xfc, 0xc2, 0x6a, 0x89, 0x75), G(0xaa, 0xff, 0xcb, 0x61, 0x84, 0x7b),
+	G(0xac, 0xfa, 0xd0, 0x7c, 0x93, 0x69), G(0xae, 0xf9, 0xd9, 0x77, 0x9e, 0x67),
+	G(0xb0, 0xe8, 0xae, 0x1e, 0xd5, 0x3d), G(0xb2, 0xeb, 0xa7, 0x15, 0xd8, 0x33),
+	G(0xb4, 0xee, 0xbc, 0x08, 0xcf, 0x21), G(0xb6, 0xed, 0xb5, 0x03, 0xc2, 0x2f),
+	G(0xb8, 0xe4, 0x8a, 0x32, 0xe1, 0x05), G(0xba, 0xe7, 0x83, 0x39, 0xec, 0x0b),
+	G(0xbc, 0xe2, 0x98, 0x24, 0xfb, 0x19), G(0xbe, 0xe1, 0x91, 0x2f, 0xf6, 0x17),
+	G(0xc0, 0xa0, 0x4d, 0x8d, 0xd6, 0x76), G(0xc2, 0xa3, 0x44, 0x86, 0xdb, 0x78),
+	G(0xc4, 0xa6, 0x5f, 0x9b, 0xcc, 0x6a), G(0xc6, 0xa5, 0x56, 0x90, 0xc1, 0x64),
+	G(0xc8, 0xac, 0x69, 0xa1, 0xe2, 0x4e), G(0xca, 0xaf, 0x60, 0xaa, 0xef, 0x40),
+	G(0xcc, 0xaa, 0x7b, 0xb7, 0xf8, 0x52), G(0xce, 0xa9, 0x72, 0xbc, 0xf5, 0x5c),
+	G(0xd0, 0xb8, 0x05, 0xd5, 0xbe, 0x06), G(0xd2, 0xbb, 0x0c, 0xde, 0xb3, 0x08),
+	G(0xd4, 0xbe, 0x17, 0xc3, 0xa4, 0x1a), G(0xd6, 0xbd, 0x1e, 0xc8, 0xa9, 0x14),
+	G(0xd8, 0xb4, 0x21, 0xf9, 0x8a, 0x3e), G(0xda, 0xb7, 0x28, 0xf2, 0x87, 0x30),
+	G(0xdc, 0xb2, 0x33, 0xef, 0x90, 0x22), G(0xde, 0xb1, 0x3a, 0xe4, 0x9d, 0x2c),
+	G(0xe0, 0x90, 0xdd, 0x3d, 0x06, 0x96), G(0xe2, 0x93, 0xd4, 0x36, 0x0b, 0x98),
+	G(0xe4, 0x96, 0xcf, 0x2b, 0x1c, 0x8a), G(0xe6, 0x95, 0xc6, 0x20, 0x11, 0x84),
+	G(0xe8, 0x9c, 0xf9, 0x11, 0x32, 0xae), G(0xea, 0x9f, 0xf0, 0x1a, 0x3f, 0xa0),
+	G(0xec, 0x9a, 0xeb, 0x07, 0x28, 0xb2), G(0xee, 0x99, 0xe2, 0x0c, 0x25, 0xbc),
+	G(0xf0, 0x88, 0x95, 0x65, 0x6e, 0xe6), G(0xf2, 0x8b, 0x9c, 0x6e, 0x63, 0xe8),
+	G(0xf4, 0x8e, 0x87, 0x73, 0x74, 0xfa), G(0xf6, 0x8d, 0x8e, 0x78, 0x79, 0xf4),
+	G(0xf8, 0x84, 0xb1, 0x49, 0x5a, 0xde), G(0xfa, 0x87, 0xb8, 0x42, 0x57, 0xd0),
+	G(0xfc, 0x82, 0xa3, 0x5f, 0x40, 0xc2), G(0xfe, 0x81, 0xaa, 0x54, 0x4d, 0xcc),
+	G(0x1b, 0x9b, 0xec, 0xf7, 0xda, 0x41), G(0x19, 0x98, 0xe5, 0xfc, 0xd7, 0x4f),
+	G(0x1f, 0x9d, 0xfe, 0xe1, 0xc0, 0x5d), G(0x1d, 0x9e, 0xf7, 0xea, 0xcd, 0x53),
+	G(0x13, 0x97, 0xc8, 0xdb, 0xee, 0x79), G(0x11, 0x94, 0xc1, 0xd0, 0xe3, 0x77),
+	G(0x17, 0x91, 0xda, 0xcd, 0xf4, 0x65), G(0x15, 0x92, 0xd3, 0xc6, 0xf9, 0x6b),
+	G(0x0b, 0x83, 0xa4, 0xaf, 0xb2, 0x31), G(0x09, 0x80, 0xad, 0xa4, 0xbf, 0x3f),
+	G(0x0f, 0x85, 0xb6, 0xb9, 0xa8, 0x2d), G(0x0d, 0x86, 0xbf, 0xb2, 0xa5, 0x23),
+	G(0x03, 0x8f, 0x80, 0x83, 0x86, 0x09), G(0x01, 0x8c, 0x89, 0x88, 0x8b, 0x07),
+	G(0x07, 0x89, 0x92, 0x95, 0x9c, 0x15), G(0x05, 0x8a, 0x9b, 0x9e, 0x91, 0x1b),
+	G(0x3b, 0xab, 0x7c, 0x47, 0x0a, 0xa1), G(0x39, 0xa8, 0x75, 0x4c, 0x07, 0xaf),
+	G(0x3f, 0xad, 0x6e, 0x51, 0x10, 0xbd), G(0x3d, 0xae, 0x67, 0x5a, 0x1d, 0xb3),
+	G(0x33, 0xa7, 0x58, 0x6b, 0x3e, 0x99), G(0x31, 0xa4, 0x51, 0x60, 0x33, 0x97),
+	G(0x37, 0xa1, 0x4a, 0x7d, 0x24, 0x85), G(0x35, 0xa2, 0x43, 0x76, 0x29, 0x8b),
+	G(0x2b, 0xb3, 0x34, 0x1f, 0x62, 0xd1), G(0x29, 0xb0, 0x3d, 0x14, 0x6f, 0xdf),
+	G(0x2f, 0xb5, 0x26, 0x09, 0x78, 0xcd), G(0x2d, 0xb6, 0x2f, 0x02, 0x75, 0xc3),
+	G(0x23, 0xbf, 0x10, 0x33, 0x56, 0xe9), G(0x21, 0xbc, 0x19, 0x38, 0x5b, 0xe7),
+	G(0x27, 0xb9, 0x02, 0x25, 0x4c, 0xf5), G(0x25, 0xba, 0x0b, 0x2e, 0x41, 0xfb),
+	G(0x5b, 0xfb, 0xd7, 0x8c, 0x61, 0x9a), G(0x59, 0xf8, 0xde, 0x87, 0x6c, 0x94),
+	G(0x5f, 0xfd, 0xc5, 0x9a, 0x7b, 0x86), G(0x5d, 0xfe, 0xcc, 0x91, 0x76, 0x88),
+	G(0x53, 0xf7, 0xf3, 0xa0, 0x55, 0xa2), G(0x51, 0xf4, 0xfa, 0xab, 0x58, 0xac),
+	G(0x57, 0xf1, 0xe1, 0xb6, 0x4f, 0xbe), G(0x55, 0xf2, 0xe8, 0xbd, 0x42, 0xb0),
+	G(0x4b, 0xe3, 0x9f, 0xd4, 0x09, 0xea), G(0x49, 0xe0, 0x96, 0xdf, 0x04, 0xe4),
+	G(0x4f, 0xe5, 0x8d, 0xc2, 0x13, 0xf6), G(0x4d, 0xe6, 0x84, 0xc9, 0x1e, 0xf8),
+	G(0x43, 0xef, 0xbb, 0xf8, 0x3d, 0xd2), G(0x41, 0xec, 0xb2, 0xf3, 0x30, 0xdc),
+	G(0x47, 0xe9, 0xa9, 0xee, 0x27, 0xce), G(0x45, 0xea, 0xa0, 0xe5, 0x2a, 0xc0),
+	G(0x7b, 0xcb, 0x47, 0x3c, 0xb1, 0x7a), G(0x79, 0xc8, 0x4e, 0x37, 0xbc, 0x74),
+	G(0x7f, 0xcd, 0x55, 0x2a, 0xab, 0x66), G(0x7d, 0xce, 0x5c, 0x21, 0xa6, 0x68),
+	G(0x73, 0xc7, 0x63, 0x10, 0x85, 0x42), G(0x71, 0xc4, 0x6a, 0x1b, 0x88, 0x4c),
+	G(0x77, 0xc1, 0x71, 0x06, 0x9f, 0x5e), G(0x75, 0xc2, 0x78, 0x0d, 0x92, 0x50),
+	G(0x6b, 0xd3, 0x0f, 0x64, 0xd9, 0x0a), G(0x69, 0xd0, 0x06, 0x6f, 0xd4, 0x04),
+	G(0x6f, 0xd5, 0x1d, 0x72, 0xc3, 0x16), G(0x6d, 0xd6, 0x14, 0x79, 0xce, 0x18),
+	G(0x63, 0xdf, 0x2b, 0x48, 0xed, 0x32), G(0x61, 0xdc, 0x22, 0x43, 0xe0, 0x3c),
+	G(0x67, 0xd9, 0x39, 0x5e, 0xf7, 0x2e), G(0x65, 0xda, 0x30, 0x55, 0xfa, 0x20),
+	G(0x9b, 0x5b, 0x9a, 0x01, 0xb7, 0xec), G(0x99, 0x58, 0x93, 0x0a, 0xba, 0xe2),
+	G(0x9f, 0x5d, 0x88, 0x17, 0xad, 0xf0), G(0x9d, 0x5e, 0x81, 0x1c, 0xa0, 0xfe),
+	G(0x93, 0x57, 0xbe, 0x2d, 0x83, 0xd4), G(0x91, 0x54, 0xb7, 0x26, 0x8e, 0xda),
+	G(0x97, 0x51, 0xac, 0x3b, 0x99, 0xc8), G(0x95, 0x52, 0xa5, 0x30, 0x94, 0xc6),
+	G(0x8b, 0x43, 0xd2, 0x59, 0xdf, 0x9c), G(0x89, 0x40, 0xdb, 0x52, 0xd2, 0x92),
+	G(0x8f, 0x45, 0xc0, 0x4f, 0xc5, 0x80), G(0x8d, 0x46, 0xc9, 0x44, 0xc8, 0x8e),
+	G(0x83, 0x4f, 0xf6, 0x75, 0xeb, 0xa4), G(0x81, 0x4c, 0xff, 0x7e, 0xe6, 0xaa),
+	G(0x87, 0x49, 0xe4, 0x63, 0xf1, 0xb8), G(0x85, 0x4a, 0xed, 0x68, 0xfc, 0xb6),
+	G(0xbb, 0x6b, 0x0a, 0xb1, 0x67, 0x0c), G(0xb9, 0x68, 0x03, 0xba, 0x6a, 0x02),
+	G(0xbf, 0x6d, 0x18, 0xa7, 0x7d, 0x10), G(0xbd, 0x6e, 0x11, 0xac, 0x70, 0x1e),
+	G(0xb3, 0x67, 0x2e, 0x9d, 0x53, 0x34), G(0xb1, 0x64, 0x27, 0x96, 0x5e, 0x3a),
+	G(0xb7, 0x61, 0x3c, 0x8b, 0x49, 0x28), G(0xb5, 0x62, 0x35, 0x80, 0x44, 0x26),
+	G(0xab, 0x73, 0x42, 0xe9, 0x0f, 0x7c), G(0xa9, 0x70, 0x4b, 0xe2, 0x02, 0x72),
+	G(0xaf, 0x75, 0x50, 0xff, 0x15, 0x60), G(0xad, 0x76, 0x59, 0xf4, 0x18, 0x6e),
+	G(0xa3, 0x7f, 0x66, 0xc5, 0x3b, 0x44), G(0xa1, 0x7c, 0x6f, 0xce, 0x36, 0x4a),
+	G(0xa7, 0x79, 0x74, 0xd3, 0x21, 0x58), G(0xa5, 0x7a, 0x7d, 0xd8, 0x2c, 0x56),
+	G(0xdb, 0x3b, 0xa1, 0x7a, 0x0c, 0x37), G(0xd9, 0x38, 0xa8, 0x71, 0x01, 0x39),
+	G(0xdf, 0x3d, 0xb3, 0x6c, 0x16, 0x2b), G(0xdd, 0x3e, 0xba, 0x67, 0x1b, 0x25),
+	G(0xd3, 0x37, 0x85, 0x56, 0x38, 0x0f), G(0xd1, 0x34, 0x8c, 0x5d, 0x35, 0x01),
+	G(0xd7, 0x31, 0x97, 0x40, 0x22, 0x13), G(0xd5, 0x32, 0x9e, 0x4b, 0x2f, 0x1d),
+	G(0xcb, 0x23, 0xe9, 0x22, 0x64, 0x47), G(0xc9, 0x20, 0xe0, 0x29, 0x69, 0x49),
+	G(0xcf, 0x25, 0xfb, 0x34, 0x7e, 0x5b), G(0xcd, 0x26, 0xf2, 0x3f, 0x73, 0x55),
+	G(0xc3, 0x2f, 0xcd, 0x0e, 0x50, 0x7f), G(0xc1, 0x2c, 0xc4, 0x05, 0x5d, 0x71),
+	G(0xc7, 0x29, 0xdf, 0x18, 0x4a, 0x63), G(0xc5, 0x2a, 0xd6, 0x13, 0x47, 0x6d),
+	G(0xfb, 0x0b, 0x31, 0xca, 0xdc, 0xd7), G(0xf9, 0x08, 0x38, 0xc1, 0xd1, 0xd9),
+	G(0xff, 0x0d, 0x23, 0xdc, 0xc6, 0xcb), G(0xfd, 0x0e, 0x2a, 0xd7, 0xcb, 0xc5),
+	G(0xf3, 0x07, 0x15, 0xe6, 0xe8, 0xef), G(0xf1, 0x04, 0x1c, 0xed, 0xe5, 0xe1),
+	G(0xf7, 0x01, 0x07, 0xf0, 0xf2, 0xf3), G(0xf5, 0x02, 0x0e, 0xfb, 0xff, 0xfd),
+	G(0xeb, 0x13, 0x79, 0x92, 0xb4, 0xa7), G(0xe9, 0x10, 0x70, 0x99, 0xb9, 0xa9),
+	G(0xef, 0x15, 0x6b, 0x84, 0xae, 0xbb), G(0xed, 0x16, 0x62, 0x8f, 0xa3, 0xb5),
+	G(0xe3, 0x1f, 0x5d, 0xbe, 0x80, 0x9f), G(0xe1, 0x1c, 0x54, 0xb5, 0x8d, 0x91),
+	G(0xe7, 0x19, 0x4f, 0xa8, 0x9a, 0x83), G(0xe5, 0x1a, 0x46, 0xa3, 0x97, 0x8d),
+};
+#undef G
+
+static void expand_key(uint8_t *expandedKey, uint8_t *key, int size, size_t expandedKeySize)
+{
+	int csz;
+	int i;
+	uint8_t t[4] = { 0 };
+
+	for (i = 0; i < size; i++)
+		expandedKey[i] = key[i];
+	csz = size;
+
+	i = 1;
+	while (csz < expandedKeySize) {
+		t[0] = expandedKey[(csz - 4) + 0];
+		t[1] = expandedKey[(csz - 4) + 1];
+		t[2] = expandedKey[(csz - 4) + 2];
+		t[3] = expandedKey[(csz - 4) + 3];
+
+		if (csz % size == 0) {
+			uint8_t tmp;
+
+			tmp = t[0];
+			t[0] = sbox[t[1]] ^ Rcon[i++ % sizeof(Rcon)];
+			t[1] = sbox[t[2]];
+			t[2] = sbox[t[3]];
+			t[3] = sbox[tmp];
+		}
+
+		if (size == 32 && ((csz % size) == 16)) {
+			t[0] = sbox[t[0]];
+			t[1] = sbox[t[1]];
+			t[2] = sbox[t[2]];
+			t[3] = sbox[t[3]];
+		}
+
+		expandedKey[csz] = expandedKey[csz - size] ^ t[0]; csz++;
+		expandedKey[csz] = expandedKey[csz - size] ^ t[1]; csz++;
+		expandedKey[csz] = expandedKey[csz - size] ^ t[2]; csz++;
+		expandedKey[csz] = expandedKey[csz - size] ^ t[3]; csz++;
+	}
+}
+
+static void shift_rows(uint8_t *state)
+{
+	uint32_t *s32;
+	int i;
+
+	for (i = 0; i < 16; i++)
+		state[i] = sbox[state[i]];
+	s32 = (uint32_t *) state;
+	s32[1] = rol32_be(s32[1], 8);
+	s32[2] = rol32_be(s32[2], 16);
+	s32[3] = rol32_be(s32[3], 24);
+}
+
+static void add_round_key(uint8_t *state, uint8_t *rk)
+{
+	uint32_t *s32, *r32;
+
+	s32 = (uint32_t *) state;
+	r32 = (uint32_t *) rk;
+	s32[0] ^= r32[0];
+	s32[1] ^= r32[1];
+	s32[2] ^= r32[2];
+	s32[3] ^= r32[3];
+}
+
+#define gm1(a) (a)
+#define gm2(a) gmtab[a][0]
+#define gm3(a) gmtab[a][1]
+#define gm9(a) gmtab[a][2]
+#define gm11(a) gmtab[a][3]
+#define gm13(a) gmtab[a][4]
+#define gm14(a) gmtab[a][5]
+
+static void mix_columns(uint8_t *state)
+{
+	int i;
+	uint8_t cpy[4];
+
+	for (i = 0; i < 4; i++) {
+		cpy[0] = state[0 * 4 + i];
+		cpy[1] = state[1 * 4 + i];
+		cpy[2] = state[2 * 4 + i];
+		cpy[3] = state[3 * 4 + i];
+		state[i] = gm2(cpy[0]) ^ gm1(cpy[3]) ^ gm1(cpy[2]) ^ gm3(cpy[1]);
+		state[4+i] = gm2(cpy[1]) ^ gm1(cpy[0]) ^ gm1(cpy[3]) ^ gm3(cpy[2]);
+		state[8+i] = gm2(cpy[2]) ^ gm1(cpy[1]) ^ gm1(cpy[0]) ^ gm3(cpy[3]);
+		state[12+i] = gm2(cpy[3]) ^ gm1(cpy[2]) ^ gm1(cpy[1]) ^ gm3(cpy[0]);
+	}
+}
+
+static void create_round_key(uint8_t *expandedKey, uint8_t *rk)
+{
+	int i,j;
+	for (i = 0; i < 4; i++)
+		for (j = 0; j < 4; j++)
+			rk[i + j * 4] = expandedKey[i * 4 + j];
+}
+
+static void aes_main(aes_key *key, uint8_t *state)
+{
+	int i = 0;
+	uint8_t rk[16];
+
+	create_round_key(key->data, rk);
+	add_round_key(state, rk);
+
+	for (i = 1; i < key->nbr; i++) {
+		create_round_key(key->data + 16 * i, rk);
+		shift_rows(state);
+		mix_columns(state);
+		add_round_key(state, rk);
+	}
+
+	create_round_key(key->data + 16 * key->nbr, rk);
+	shift_rows(state);
+	add_round_key(state, rk);
+}
+
+static void shift_rows_inv(uint8_t *state)
+{
+	uint32_t *s32;
+	int i;
+
+	s32 = (uint32_t *) state;
+	s32[1] = ror32_be(s32[1], 8);
+	s32[2] = ror32_be(s32[2], 16);
+	s32[3] = ror32_be(s32[3], 24);
+	for (i = 0; i < 16; i++)
+		state[i] = rsbox[state[i]];
+}
+
+static void mix_columns_inv(uint8_t *state)
+{
+	int i;
+	uint8_t cpy[4];
+
+	for (i = 0; i < 4; i++) {
+		cpy[0] = state[0 * 4 + i];
+		cpy[1] = state[1 * 4 + i];
+		cpy[2] = state[2 * 4 + i];
+		cpy[3] = state[3 * 4 + i];
+		state[i] = gm14(cpy[0]) ^ gm9(cpy[3]) ^ gm13(cpy[2]) ^ gm11(cpy[1]);
+		state[4+i] = gm14(cpy[1]) ^ gm9(cpy[0]) ^ gm13(cpy[3]) ^ gm11(cpy[2]);
+		state[8+i] = gm14(cpy[2]) ^ gm9(cpy[1]) ^ gm13(cpy[0]) ^ gm11(cpy[3]);
+		state[12+i] = gm14(cpy[3]) ^ gm9(cpy[2]) ^ gm13(cpy[1]) ^ gm11(cpy[0]);
+	}
+}
+
+static void aes_main_inv(aes_key *key, uint8_t *state)
+{
+	int i = 0;
+	uint8_t rk[16];
+
+	create_round_key(key->data + 16 * key->nbr, rk);
+	add_round_key(state, rk);
+
+	for (i = key->nbr - 1; i > 0; i--) {
+		create_round_key(key->data + 16 * i, rk);
+		shift_rows_inv(state);
+		add_round_key(state, rk);
+		mix_columns_inv(state);
+	}
+
+	create_round_key(key->data, rk);
+	shift_rows_inv(state);
+	add_round_key(state, rk);
+}
+
+/* Set the block values, for the block:
+ * a0,0 a0,1 a0,2 a0,3
+ * a1,0 a1,1 a1,2 a1,3 -> a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3
+ * a2,0 a2,1 a2,2 a2,3
+ * a3,0 a3,1 a3,2 a3,3
+ */
+#define swap_block(t, f) \
+	t[0] = f[0]; t[4] = f[1]; t[8] = f[2]; t[12] = f[3]; \
+	t[1] = f[4]; t[5] = f[5]; t[9] = f[6]; t[13] = f[7]; \
+	t[2] = f[8]; t[6] = f[9]; t[10] = f[10]; t[14] = f[11]; \
+	t[3] = f[12]; t[7] = f[13]; t[11] = f[14]; t[15] = f[15]
+
+void tmd_aes_generic_encrypt_block(aes_block *output, aes_key *key, aes_block *input)
+{
+	uint8_t block[16];
+	uint8_t *iptr, *optr;
+
+	iptr = (uint8_t *) input;
+	optr = (uint8_t *) output;
+	swap_block(block, iptr);
+	aes_main(key, block);
+	swap_block(optr, block);
+}
+
+void tmd_aes_generic_decrypt_block(aes_block *output, aes_key *key, aes_block *input)
+{
+	uint8_t block[16];
+	uint8_t *iptr, *optr;
+
+	iptr = (uint8_t *) input;
+	optr = (uint8_t *) output;
+	swap_block(block, iptr);
+	aes_main_inv(key, block);
+	swap_block(optr, block);
+}
+
+void tmd_aes_generic_init(aes_key *key, uint8_t *origkey, uint8_t size)
+{
+	int esz;
+
+	switch (size) {
+	case 16: key->nbr = 10; esz = 176; break;
+	case 24: key->nbr = 12; esz = 208; break;
+	case 32: key->nbr = 14; esz = 240; break;
+	default: return;
+	}
+	expand_key(key->data, origkey, size, esz);
+	return;
+}
diff --git a/cbits/aes_generic.h b/cbits/aes_generic.h
new file mode 100644
--- /dev/null
+++ b/cbits/aes_generic.h
@@ -0,0 +1,34 @@
+/*
+ * 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 "aes.h"
+
+void tmd_aes_generic_encrypt_block(aes_block *output, aes_key *key, aes_block *input);
+void tmd_aes_generic_decrypt_block(aes_block *output, aes_key *key, aes_block *input);
+void tmd_aes_generic_init(aes_key *key, uint8_t *origkey, uint8_t size);
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,331 @@
+/*
+ * Copyright (c) 2012-2013 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.
+ */
+
+#ifdef WITH_AESNI
+
+#include <wmmintrin.h>
+#include <tmmintrin.h>
+#include <string.h>
+#include "aes.h"
+#include "aes_x86ni.h"
+#include "block128.h"
+#include "cpu.h"
+
+#ifdef ARCH_X86
+#define ALIGN_UP(addr, size) (((addr) + ((size) - 1)) & (~((size) - 1)))
+#define ALIGNMENT(n) __attribute__((aligned(n)))
+
+/* old GCC version doesn't cope with the shuffle parameters, that can take 2 values (0xff and 0xaa)
+ * in our case, passed as argument despite being a immediate 8 bits constant anyway.
+ * un-factorise aes_128_key_expansion into 2 version that have the shuffle parameter explicitly set */
+static __m128i aes_128_key_expansion_ff(__m128i key, __m128i keygened)
+{
+	keygened = _mm_shuffle_epi32(keygened, 0xff);
+	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);
+}
+
+static __m128i aes_128_key_expansion_aa(__m128i key, __m128i keygened)
+{
+	keygened = _mm_shuffle_epi32(keygened, 0xaa);
+	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 tmd_aes_ni_init(aes_key *key, uint8_t *ikey, uint8_t size)
+{
+	__m128i k[28];
+	uint64_t *out = (uint64_t *) key->data;
+	int i;
+
+	switch (size) {
+	case 16:
+		k[0] = _mm_loadu_si128((const __m128i*) ikey);
+
+		#define AES_128_key_exp(K, RCON) aes_128_key_expansion_ff(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]);
+
+		for (i = 0; i < 20; i++)
+			_mm_storeu_si128(((__m128i *) out) + i, k[i]);
+		break;
+	case 32:
+#define AES_256_key_exp_1(K1, K2, RCON) aes_128_key_expansion_ff(K1, _mm_aeskeygenassist_si128(K2, RCON))
+#define AES_256_key_exp_2(K1, K2)       aes_128_key_expansion_aa(K1, _mm_aeskeygenassist_si128(K2, 0x00))
+		k[0]  = _mm_loadu_si128((const __m128i*) ikey);
+		k[1]  = _mm_loadu_si128((const __m128i*) (ikey+16));
+		k[2]  = AES_256_key_exp_1(k[0], k[1], 0x01);
+		k[3]  = AES_256_key_exp_2(k[1], k[2]);
+		k[4]  = AES_256_key_exp_1(k[2], k[3], 0x02);
+		k[5]  = AES_256_key_exp_2(k[3], k[4]);
+		k[6]  = AES_256_key_exp_1(k[4], k[5], 0x04);
+		k[7]  = AES_256_key_exp_2(k[5], k[6]);
+		k[8]  = AES_256_key_exp_1(k[6], k[7], 0x08);
+		k[9]  = AES_256_key_exp_2(k[7], k[8]);
+		k[10] = AES_256_key_exp_1(k[8], k[9], 0x10);
+		k[11] = AES_256_key_exp_2(k[9], k[10]);
+		k[12] = AES_256_key_exp_1(k[10], k[11], 0x20);
+		k[13] = AES_256_key_exp_2(k[11], k[12]);
+		k[14] = AES_256_key_exp_1(k[12], k[13], 0x40);
+
+		k[15] = _mm_aesimc_si128(k[13]);
+		k[16] = _mm_aesimc_si128(k[12]);
+		k[17] = _mm_aesimc_si128(k[11]);
+		k[18] = _mm_aesimc_si128(k[10]);
+		k[19] = _mm_aesimc_si128(k[9]);
+		k[20] = _mm_aesimc_si128(k[8]);
+		k[21] = _mm_aesimc_si128(k[7]);
+		k[22] = _mm_aesimc_si128(k[6]);
+		k[23] = _mm_aesimc_si128(k[5]);
+		k[24] = _mm_aesimc_si128(k[4]);
+		k[25] = _mm_aesimc_si128(k[3]);
+		k[26] = _mm_aesimc_si128(k[2]);
+		k[27] = _mm_aesimc_si128(k[1]);
+		for (i = 0; i < 28; i++)
+			_mm_storeu_si128(((__m128i *) out) + i, k[i]);
+		break;
+	default:
+		break;
+	}
+}
+
+/* TO OPTIMISE: use pcmulqdq... or some faster code.
+ * this is the lamest way of doing it, but i'm out of time.
+ * this is basically a copy of gf_mulx in gf.c */
+static __m128i gfmulx(__m128i v)
+{
+	uint64_t v_[2] ALIGNMENT(16);
+	const uint64_t gf_mask = 0x8000000000000000;
+
+	_mm_store_si128((__m128i *) v_, v);
+	uint64_t r = ((v_[1] & gf_mask) ? 0x87 : 0);
+	v_[1] = (v_[1] << 1) | (v_[0] & gf_mask ? 1 : 0);
+	v_[0] = (v_[0] << 1) ^ r;
+	v = _mm_load_si128((__m128i *) v_);
+	return v;
+}
+
+static void unopt_gf_mul(block128 *a, block128 *b)
+{
+	uint64_t a0, a1, v0, v1;
+	int i, j;
+
+	a0 = a1 = 0;
+	v0 = cpu_to_be64(a->q[0]);
+	v1 = cpu_to_be64(a->q[1]);
+
+	for (i = 0; i < 16; i++)
+		for (j = 0x80; j != 0; j >>= 1) {
+			uint8_t x = b->b[i] & j;
+			a0 ^= x ? v0 : 0;
+			a1 ^= x ? v1 : 0;
+			x = (uint8_t) v1 & 1;
+			v1 = (v1 >> 1) | (v0 << 63);
+			v0 = (v0 >> 1) ^ (x ? (0xe1ULL << 56) : 0);
+		}
+	a->q[0] = cpu_to_be64(a0);
+	a->q[1] = cpu_to_be64(a1);
+}
+
+static __m128i ghash_add(__m128i tag, __m128i h, __m128i m)
+{
+	aes_block _t, _h;
+	tag = _mm_xor_si128(tag, m);
+
+	_mm_store_si128((__m128i *) &_t, tag);
+	_mm_store_si128((__m128i *) &_h, h);
+	unopt_gf_mul(&_t, &_h);
+	tag = _mm_load_si128((__m128i *) &_t);
+	return tag;
+}
+
+#define PRELOAD_ENC_KEYS128(k) \
+	__m128i K0  = _mm_loadu_si128(((__m128i *) k)+0); \
+	__m128i K1  = _mm_loadu_si128(((__m128i *) k)+1); \
+	__m128i K2  = _mm_loadu_si128(((__m128i *) k)+2); \
+	__m128i K3  = _mm_loadu_si128(((__m128i *) k)+3); \
+	__m128i K4  = _mm_loadu_si128(((__m128i *) k)+4); \
+	__m128i K5  = _mm_loadu_si128(((__m128i *) k)+5); \
+	__m128i K6  = _mm_loadu_si128(((__m128i *) k)+6); \
+	__m128i K7  = _mm_loadu_si128(((__m128i *) k)+7); \
+	__m128i K8  = _mm_loadu_si128(((__m128i *) k)+8); \
+	__m128i K9  = _mm_loadu_si128(((__m128i *) k)+9); \
+	__m128i K10 = _mm_loadu_si128(((__m128i *) k)+10);
+
+#define PRELOAD_ENC_KEYS256(k) \
+	PRELOAD_ENC_KEYS128(k) \
+	__m128i K11 = _mm_loadu_si128(((__m128i *) k)+11); \
+	__m128i K12 = _mm_loadu_si128(((__m128i *) k)+12); \
+	__m128i K13 = _mm_loadu_si128(((__m128i *) k)+13); \
+	__m128i K14 = _mm_loadu_si128(((__m128i *) k)+14);
+
+#define DO_ENC_BLOCK128(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 DO_ENC_BLOCK256(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_aesenc_si128(m, K10); \
+	m = _mm_aesenc_si128(m, K11); \
+	m = _mm_aesenc_si128(m, K12); \
+	m = _mm_aesenc_si128(m, K13); \
+	m = _mm_aesenclast_si128(m, K14);
+
+/* load K0 at K9 from index 'at' */
+#define PRELOAD_DEC_KEYS_AT(k, at) \
+	__m128i K0  = _mm_loadu_si128(((__m128i *) k)+at+0); \
+	__m128i K1  = _mm_loadu_si128(((__m128i *) k)+at+1); \
+	__m128i K2  = _mm_loadu_si128(((__m128i *) k)+at+2); \
+	__m128i K3  = _mm_loadu_si128(((__m128i *) k)+at+3); \
+	__m128i K4  = _mm_loadu_si128(((__m128i *) k)+at+4); \
+	__m128i K5  = _mm_loadu_si128(((__m128i *) k)+at+5); \
+	__m128i K6  = _mm_loadu_si128(((__m128i *) k)+at+6); \
+	__m128i K7  = _mm_loadu_si128(((__m128i *) k)+at+7); \
+	__m128i K8  = _mm_loadu_si128(((__m128i *) k)+at+8); \
+	__m128i K9  = _mm_loadu_si128(((__m128i *) k)+at+9); \
+
+#define PRELOAD_DEC_KEYS128(k) \
+	PRELOAD_DEC_KEYS_AT(k, 10) \
+	__m128i K10 = _mm_loadu_si128(((__m128i *) k)+0);
+
+#define PRELOAD_DEC_KEYS256(k) \
+	PRELOAD_DEC_KEYS_AT(k, 14) \
+	__m128i K10 = _mm_loadu_si128(((__m128i *) k)+14+10); \
+	__m128i K11 = _mm_loadu_si128(((__m128i *) k)+14+11); \
+	__m128i K12 = _mm_loadu_si128(((__m128i *) k)+14+12); \
+	__m128i K13 = _mm_loadu_si128(((__m128i *) k)+14+13); \
+	__m128i K14 = _mm_loadu_si128(((__m128i *) k)+0);
+
+#define DO_DEC_BLOCK128(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);
+
+#define DO_DEC_BLOCK256(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_aesdec_si128(m, K10); \
+	m = _mm_aesdec_si128(m, K11); \
+	m = _mm_aesdec_si128(m, K12); \
+	m = _mm_aesdec_si128(m, K13); \
+	m = _mm_aesdeclast_si128(m, K14);
+
+#define SIZE 128
+#define SIZED(m) m##128
+#define PRELOAD_ENC PRELOAD_ENC_KEYS128
+#define DO_ENC_BLOCK DO_ENC_BLOCK128
+#define PRELOAD_DEC PRELOAD_DEC_KEYS128
+#define DO_DEC_BLOCK DO_DEC_BLOCK128
+#include "aes_x86ni_impl.c"
+
+#undef SIZE
+#undef SIZED
+#undef PRELOAD_ENC
+#undef PRELOAD_DEC
+#undef DO_ENC_BLOCK
+#undef DO_DEC_BLOCK
+
+#define SIZED(m) m##256
+#define SIZE 256
+#define PRELOAD_ENC PRELOAD_ENC_KEYS256
+#define DO_ENC_BLOCK DO_ENC_BLOCK256
+#define PRELOAD_DEC PRELOAD_DEC_KEYS256
+#define DO_DEC_BLOCK DO_DEC_BLOCK256
+#include "aes_x86ni_impl.c"
+
+#undef SIZE
+#undef SIZED
+#undef PRELOAD_ENC
+#undef PRELOAD_DEC
+#undef DO_ENC_BLOCK
+#undef DO_DEC_BLOCK
+
+#endif
+
+#endif
diff --git a/cbits/aes_x86ni.h b/cbits/aes_x86ni.h
new file mode 100644
--- /dev/null
+++ b/cbits/aes_x86ni.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef AES_X86NI_H
+#define AES_X86NI_H
+
+#ifdef WITH_AESNI
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <wmmintrin.h>
+#include <tmmintrin.h>
+#include "aes.h"
+#include "block128.h"
+
+#ifdef IMPL_DEBUG
+static void block128_sse_print(__m128i m)
+{
+	block128 b;
+	_mm_storeu_si128((__m128i *) &b.b, m);
+	block128_print(&b);
+}
+#endif
+
+void tmd_aes_ni_init(aes_key *key, uint8_t *origkey, uint8_t size);
+void tmd_aes_ni_encrypt_block128(aes_block *out, aes_key *key, aes_block *in);
+void tmd_aes_ni_encrypt_block256(aes_block *out, aes_key *key, aes_block *in);
+void tmd_aes_ni_decrypt_block128(aes_block *out, aes_key *key, aes_block *in);
+void tmd_aes_ni_decrypt_block256(aes_block *out, aes_key *key, aes_block *in);
+void tmd_aes_ni_encrypt_ecb128(aes_block *out, aes_key *key, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_encrypt_ecb256(aes_block *out, aes_key *key, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_decrypt_ecb128(aes_block *out, aes_key *key, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_decrypt_ecb256(aes_block *out, aes_key *key, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_encrypt_cbc128(aes_block *out, aes_key *key, aes_block *_iv, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_encrypt_cbc256(aes_block *out, aes_key *key, aes_block *_iv, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_decrypt_cbc128(aes_block *out, aes_key *key, aes_block *_iv, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_decrypt_cbc256(aes_block *out, aes_key *key, aes_block *_iv, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_encrypt_ctr128(uint8_t *out, aes_key *key, aes_block *_iv, aes_block *niv, uint8_t *in, uint32_t length);
+void tmd_aes_ni_encrypt_ctr256(uint8_t *out, aes_key *key, aes_block *_iv, aes_block *niv, uint8_t *in, uint32_t length);
+void tmd_aes_ni_encrypt_xts128(aes_block *out, aes_key *key1, aes_key *key2,
+                               aes_block *_tweak, uint32_t spoint, aes_block *in, uint32_t blocks);
+void tmd_aes_ni_encrypt_xts256(aes_block *out, aes_key *key1, aes_key *key2,
+                               aes_block *_tweak, uint32_t spoint, aes_block *in, uint32_t blocks);
+
+void tmd_aes_ni_gcm_encrypt128(uint8_t *out, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *in, uint32_t length, aes_ctx *newCTX);
+void tmd_aes_ni_gcm_encrypt256(uint8_t *out, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *in, uint32_t length, aes_ctx *newCTX);
+
+void tmd_gf_mul_x86ni(block128 *res, block128 *a_, block128 *b_);
+
+#endif
+
+#endif
+
+#endif
diff --git a/cbits/aes_x86ni_impl.c b/cbits/aes_x86ni_impl.c
new file mode 100644
--- /dev/null
+++ b/cbits/aes_x86ni_impl.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2012-2013 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.
+ */
+
+void SIZED(tmd_aes_ni_encrypt_block)(aes_block *out, aes_key *key, aes_block *in)
+{
+        __m128i *k = (__m128i *) key->data;
+        PRELOAD_ENC(k);
+        __m128i m = _mm_loadu_si128((__m128i *) in);
+        DO_ENC_BLOCK(m);
+        _mm_storeu_si128((__m128i *) out, m);
+}
+
+void SIZED(tmd_aes_ni_decrypt_block)(aes_block *out, aes_key *key, aes_block *in)
+{
+        __m128i *k = (__m128i *) key->data;
+        PRELOAD_DEC(k);
+        __m128i m = _mm_loadu_si128((__m128i *) in);
+        DO_DEC_BLOCK(m);
+        _mm_storeu_si128((__m128i *) out, m);
+}
+
+void SIZED(tmd_aes_ni_encrypt_ecb)(aes_block *out, aes_key *key, aes_block *in, uint32_t blocks)
+{
+        __m128i *k = (__m128i *) key->data;
+
+        PRELOAD_ENC(k);
+        for (; blocks-- > 0; in += 1, out += 1) {
+                __m128i m = _mm_loadu_si128((__m128i *) in);
+                DO_ENC_BLOCK(m);
+                _mm_storeu_si128((__m128i *) out, m);
+        }
+}
+
+void SIZED(tmd_aes_ni_decrypt_ecb)(aes_block *out, aes_key *key, aes_block *in, uint32_t blocks)
+{
+        __m128i *k = (__m128i *) key->data;
+
+        PRELOAD_DEC(k);
+
+        for (; blocks-- > 0; in += 1, out += 1) {
+                __m128i m = _mm_loadu_si128((__m128i *) in);
+                DO_DEC_BLOCK(m);
+                _mm_storeu_si128((__m128i *) out, m);
+        }
+}
+
+void SIZED(tmd_aes_ni_encrypt_cbc)(aes_block *out, aes_key *key, aes_block *_iv, aes_block *in, uint32_t blocks)
+{
+        __m128i *k = (__m128i *) key->data;
+        __m128i iv = _mm_loadu_si128((__m128i *) _iv);
+
+        PRELOAD_ENC(k);
+
+        for (; blocks-- > 0; in += 1, out += 1) {
+                __m128i m = _mm_loadu_si128((__m128i *) in);
+                m = _mm_xor_si128(m, iv);
+                DO_ENC_BLOCK(m);
+                iv = m;
+                _mm_storeu_si128((__m128i *) out, m);
+        }
+}
+
+void SIZED(tmd_aes_ni_decrypt_cbc)(aes_block *out, aes_key *key, aes_block *_iv, aes_block *in, uint32_t blocks)
+{
+        __m128i *k = (__m128i *) key->data;
+        __m128i iv = _mm_loadu_si128((__m128i *) _iv);
+
+        PRELOAD_DEC(k);
+
+        for (; blocks-- > 0; in += 1, out += 1) {
+                __m128i m = _mm_loadu_si128((__m128i *) in);
+                __m128i ivnext = m;
+
+                DO_DEC_BLOCK(m);
+                m = _mm_xor_si128(m, iv);
+
+                _mm_storeu_si128((__m128i *) out, m);
+                iv = ivnext;
+        }
+}
+
+void SIZED(tmd_aes_ni_encrypt_ctr)(uint8_t *output, aes_key *key, aes_block *_iv, aes_block *newIV, uint8_t *input, uint32_t len)
+{
+        __m128i *k = (__m128i *) key->data;
+        __m128i bswap_mask = _mm_setr_epi8(7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8);
+        __m128i one        = _mm_set_epi32(0,1,0,0);
+        uint32_t nb_blocks = len / 16;
+        uint32_t part_block_len = len % 16;
+
+        /* get the IV in little endian format */
+        __m128i iv = _mm_loadu_si128((__m128i *) _iv);
+        iv = _mm_shuffle_epi8(iv, bswap_mask);
+
+        PRELOAD_ENC(k);
+
+        for (; nb_blocks-- > 0; output += 16, input += 16) {
+                /* put back the iv in big endian mode,
+                 * encrypt it and and xor it the input block
+                 */
+                __m128i tmp = _mm_shuffle_epi8(iv, bswap_mask);
+                DO_ENC_BLOCK(tmp);
+                __m128i m = _mm_loadu_si128((__m128i *) input);
+                m = _mm_xor_si128(m, tmp);
+
+                _mm_storeu_si128((__m128i *) output, m);
+                /* iv += 1 */
+                iv = _mm_add_epi64(iv, one);
+        }
+
+        if (part_block_len != 0) {
+                aes_block block;
+                memset(&block.b, 0, 16);
+                memcpy(&block.b, input, part_block_len);
+
+                __m128i m = _mm_loadu_si128((__m128i *) &block);
+                __m128i tmp = _mm_shuffle_epi8(iv, bswap_mask);
+
+                DO_ENC_BLOCK(tmp);
+                m = _mm_xor_si128(m, tmp);
+                _mm_storeu_si128((__m128i *) &block.b, m);
+                memcpy(output, &block.b, part_block_len);
+                iv = _mm_add_epi64(iv, one);
+        }
+        __m128i tmpRet = _mm_shuffle_epi8(iv, bswap_mask);
+        _mm_storeu_si128((__m128i *) newIV, tmpRet);
+
+        return ;
+}
+
+void SIZED(tmd_aes_ni_encrypt_xts)(aes_block *out, aes_key *key1, aes_key *key2,
+                               aes_block *_tweak, uint32_t spoint, aes_block *in, uint32_t blocks)
+{
+        __m128i tweak = _mm_loadu_si128((__m128i *) _tweak);
+
+        do {
+                __m128i *k2 = (__m128i *) key2->data;
+                PRELOAD_ENC(k2);
+                DO_ENC_BLOCK(tweak);
+
+                while (spoint-- > 0)
+                        tweak = gfmulx(tweak);
+        } while (0) ;
+
+        do {
+                __m128i *k1 = (__m128i *) key1->data;
+                PRELOAD_ENC(k1);
+
+                for ( ; blocks-- > 0; in += 1, out += 1, tweak = gfmulx(tweak)) {
+                        __m128i m = _mm_loadu_si128((__m128i *) in);
+
+                        m = _mm_xor_si128(m, tweak);
+                        DO_ENC_BLOCK(m);
+                        m = _mm_xor_si128(m, tweak);
+
+                        _mm_storeu_si128((__m128i *) out, m);
+                }
+        } while (0);
+}
+
+void SIZED(tmd_aes_ni_gcm_encrypt)(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length, aes_ctx *newCTX)
+{
+        __m128i *k = (__m128i *) key->data;
+        __m128i bswap_mask = _mm_setr_epi8(7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8);
+        __m128i one        = _mm_set_epi32(0,1,0,0);
+        uint32_t nb_blocks = length / 16;
+        uint32_t part_block_len = length % 16;
+
+        ctx->length_input += length;
+
+        __m128i h  = _mm_loadu_si128((__m128i *) &gcm->h);
+        __m128i tag = _mm_loadu_si128((__m128i *) &ctx->tag);
+        __m128i iv = _mm_loadu_si128((__m128i *) &ctx->civ);
+        iv = _mm_shuffle_epi8(iv, bswap_mask);
+
+        PRELOAD_ENC(k);
+
+        for (; nb_blocks-- > 0; output += 16, input += 16) {
+                /* iv += 1 */
+                iv = _mm_add_epi64(iv, one);
+
+                /* put back iv in big endian, encrypt it,
+                 * and xor it to input */
+                __m128i tmp = _mm_shuffle_epi8(iv, bswap_mask);
+                DO_ENC_BLOCK(tmp);
+                __m128i m = _mm_loadu_si128((__m128i *) input);
+                m = _mm_xor_si128(m, tmp);
+
+                tag = ghash_add(tag, h, m);
+
+                /* store it out */
+                _mm_storeu_si128((__m128i *) output, m);
+        }
+        if (part_block_len > 0) {
+                __m128i mask;
+                aes_block block;
+                /* FIXME could do something a bit more clever (slli & sub & and maybe) ... */
+                switch (part_block_len) {
+                case 1: mask = _mm_setr_epi8(0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 2: mask = _mm_setr_epi8(0,1,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 3: mask = _mm_setr_epi8(0,1,2,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 4: mask = _mm_setr_epi8(0,1,2,3,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 5: mask = _mm_setr_epi8(0,1,2,3,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 6: mask = _mm_setr_epi8(0,1,2,3,4,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 7: mask = _mm_setr_epi8(0,1,2,3,4,5,6,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 8: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 9: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 10: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,9,0x80,0x80,0x80,0x80,0x80,0x80); break;
+                case 11: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,9,10,0x80,0x80,0x80,0x80,0x80); break;
+                case 12: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,9,10,11,0x80,0x80,0x80,0x80); break;
+                case 13: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,0x80,0x80,0x80); break;
+                case 14: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,13,0x80,0x80); break;
+                case 15: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,0x80); break;
+                default: mask = _mm_setr_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); break;
+                }
+                memset(&block.b, 0, 16);
+                memcpy(&block.b, input, part_block_len);
+
+                /* iv += 1 */
+                iv = _mm_add_epi64(iv, one);
+
+                /* put back iv in big endian mode, encrypt it and xor it with input */
+                __m128i tmp = _mm_shuffle_epi8(iv, bswap_mask);
+                DO_ENC_BLOCK(tmp);
+
+                __m128i m = _mm_loadu_si128((__m128i *) &block);
+                m = _mm_xor_si128(m, tmp);
+                m = _mm_shuffle_epi8(m, mask);
+
+                tag = ghash_add(tag, h, m);
+
+                /* make output */
+                _mm_storeu_si128((__m128i *) &block.b, m);
+                memcpy(output, &block.b, part_block_len);
+        }
+        /* store back IV & tag */
+        __m128i tmp = _mm_shuffle_epi8(iv, bswap_mask);
+        _mm_storeu_si128((__m128i *) &newCTX->civ, tmp);
+        _mm_storeu_si128((__m128i *) &newCTX->tag, tag);
+}
diff --git a/cbits/bitfn.h b/cbits/bitfn.h
new file mode 100644
--- /dev/null
+++ b/cbits/bitfn.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2006-2009 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef BITFN_H
+#define BITFN_H
+#include <stdint.h>
+
+#ifndef NO_INLINE_ASM
+/**********************************************************/
+# if (defined(__i386__))
+#  define ARCH_HAS_SWAP32
+static inline uint32_t bitfn_swap32(uint32_t a)
+{
+	asm ("bswap %0" : "=r" (a) : "0" (a));
+	return a;
+}
+/**********************************************************/
+# elif (defined(__arm__))
+#  define ARCH_HAS_SWAP32
+static inline uint32_t bitfn_swap32(uint32_t a)
+{
+	uint32_t tmp = a;
+	asm volatile ("eor %1, %0, %0, ror #16\n"
+	              "bic %1, %1, #0xff0000\n"
+	              "mov %0, %0, ror #8\n"
+	              "eor %0, %0, %1, lsr #8\n"
+	             : "=r" (a), "=r" (tmp) : "0" (a), "1" (tmp));
+	return a;
+}
+/**********************************************************/
+# elif defined(__x86_64__)
+#  define ARCH_HAS_SWAP32
+#  define ARCH_HAS_SWAP64
+static inline uint32_t bitfn_swap32(uint32_t a)
+{
+	asm ("bswap %0" : "=r" (a) : "0" (a));
+	return a;
+}
+
+static inline uint64_t bitfn_swap64(uint64_t a)
+{
+	asm ("bswap %0" : "=r" (a) : "0" (a));
+	return a;
+}
+
+# endif
+#endif /* NO_INLINE_ASM */
+/**********************************************************/
+
+#ifndef ARCH_HAS_ROL32
+static inline uint32_t rol32(uint32_t word, uint32_t shift)
+{
+	return (word << shift) | (word >> (32 - shift));
+}
+#endif
+
+#ifndef ARCH_HAS_ROR32
+static inline uint32_t ror32(uint32_t word, uint32_t shift)
+{
+	return (word >> shift) | (word << (32 - shift));
+}
+#endif
+
+#ifndef ARCH_HAS_ROL64
+static inline uint64_t rol64(uint64_t word, uint32_t shift)
+{
+	return (word << shift) | (word >> (64 - shift));
+}
+#endif
+
+#ifndef ARCH_HAS_ROR64
+static inline uint64_t ror64(uint64_t word, uint32_t shift)
+{
+	return (word >> shift) | (word << (64 - shift));
+}
+#endif
+
+#ifndef ARCH_HAS_SWAP32
+static inline uint32_t bitfn_swap32(uint32_t a)
+{
+	return (a << 24) | ((a & 0xff00) << 8) | ((a >> 8) & 0xff00) | (a >> 24);
+}
+#endif
+
+#ifndef ARCH_HAS_ARRAY_SWAP32
+static inline void array_swap32(uint32_t *d, uint32_t *s, uint32_t nb)
+{
+	while (nb--)
+		*d++ = bitfn_swap32(*s++);
+}
+#endif
+
+#ifndef ARCH_HAS_SWAP64
+static inline uint64_t bitfn_swap64(uint64_t a)
+{
+	return ((uint64_t) bitfn_swap32((uint32_t) (a >> 32))) |
+	       (((uint64_t) bitfn_swap32((uint32_t) a)) << 32);
+}
+#endif
+
+#ifndef ARCH_HAS_ARRAY_SWAP64
+static inline void array_swap64(uint64_t *d, uint64_t *s, uint32_t nb)
+{
+	while (nb--)
+		*d++ = bitfn_swap64(*s++);
+}
+#endif
+
+#ifndef ARCH_HAS_MEMORY_ZERO
+static inline void memory_zero(void *ptr, uint32_t len)
+{
+	uint32_t *ptr32 = ptr;
+	uint8_t *ptr8;
+	int i;
+
+	for (i = 0; i < len / 4; i++)
+		*ptr32++ = 0;
+	if (len % 4) {
+		ptr8 = (uint8_t *) ptr32;
+		for (i = len % 4; i >= 0; i--)
+			ptr8[i] = 0;
+	}
+}
+#endif
+
+#ifndef ARCH_HAS_ARRAY_COPY32
+static inline void array_copy32(uint32_t *d, uint32_t *s, uint32_t nb)
+{
+	while (nb--) *d++ = *s++;
+}
+#endif
+
+#ifndef ARCH_HAS_ARRAY_COPY64
+static inline void array_copy64(uint64_t *d, uint64_t *s, uint32_t nb)
+{
+	while (nb--) *d++ = *s++;
+}
+#endif
+
+#ifdef __MINGW32__
+  # define LITTLE_ENDIAN 1234
+  # define BYTE_ORDER    LITTLE_ENDIAN
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
+  # include <sys/endian.h>
+#elif defined(__OpenBSD__) || defined(__SVR4)
+  # include <sys/types.h>
+#elif defined(__APPLE__)
+  # include <machine/endian.h>
+#elif defined( BSD ) && ( BSD >= 199103 )
+  # include <machine/endian.h>
+#elif defined( __QNXNTO__ ) && defined( __LITTLEENDIAN__ )
+  # define LITTLE_ENDIAN 1234
+  # define BYTE_ORDER    LITTLE_ENDIAN
+#elif defined( __QNXNTO__ ) && defined( __BIGENDIAN__ )
+  # define BIG_ENDIAN 1234
+  # define BYTE_ORDER    BIG_ENDIAN
+#else
+  # include <endian.h>
+#endif
+/* big endian to cpu */
+#if LITTLE_ENDIAN == BYTE_ORDER
+
+# define be32_to_cpu(a) bitfn_swap32(a)
+# define cpu_to_be32(a) bitfn_swap32(a)
+# define le32_to_cpu(a) (a)
+# define cpu_to_le32(a) (a)
+# define be64_to_cpu(a) bitfn_swap64(a)
+# define cpu_to_be64(a) bitfn_swap64(a)
+# define le64_to_cpu(a) (a)
+# define cpu_to_le64(a) (a)
+
+# define cpu_to_le32_array(d, s, l) array_copy32(d, s, l)
+# define le32_to_cpu_array(d, s, l) array_copy32(d, s, l)
+# define cpu_to_be32_array(d, s, l) array_swap32(d, s, l)
+# define be32_to_cpu_array(d, s, l) array_swap32(d, s, l)
+
+# define cpu_to_le64_array(d, s, l) array_copy64(d, s, l)
+# define le64_to_cpu_array(d, s, l) array_copy64(d, s, l)
+# define cpu_to_be64_array(d, s, l) array_swap64(d, s, l)
+# define be64_to_cpu_array(d, s, l) array_swap64(d, s, l)
+
+# define ror32_be(a, s) rol32(a, s)
+# define rol32_be(a, s) ror32(a, s)
+
+# define ARCH_IS_LITTLE_ENDIAN
+
+#elif BIG_ENDIAN == BYTE_ORDER
+
+# define be32_to_cpu(a) (a)
+# define cpu_to_be32(a) (a)
+# define be64_to_cpu(a) (a)
+# define cpu_to_be64(a) (a)
+# define le64_to_cpu(a) bitfn_swap64(a)
+# define cpu_to_le64(a) bitfn_swap64(a)
+# define le32_to_cpu(a) bitfn_swap32(a)
+# define cpu_to_le32(a) bitfn_swap32(a)
+
+# define cpu_to_le32_array(d, s, l) array_swap32(d, s, l)
+# define le32_to_cpu_array(d, s, l) array_swap32(d, s, l)
+# define cpu_to_be32_array(d, s, l) array_copy32(d, s, l)
+# define be32_to_cpu_array(d, s, l) array_copy32(d, s, l)
+
+# define cpu_to_le64_array(d, s, l) array_swap64(d, s, l)
+# define le64_to_cpu_array(d, s, l) array_swap64(d, s, l)
+# define cpu_to_be64_array(d, s, l) array_copy64(d, s, l)
+# define be64_to_cpu_array(d, s, l) array_copy64(d, s, l)
+
+# define ror32_be(a, s) ror32(a, s)
+# define rol32_be(a, s) rol32(a, s)
+
+# define ARCH_IS_BIG_ENDIAN
+
+#else
+# error "endian not supported"
+#endif
+
+#endif /* !BITFN_H */
diff --git a/cbits/block128.h b/cbits/block128.h
new file mode 100644
--- /dev/null
+++ b/cbits/block128.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#ifndef BLOCK128_H
+#define BLOCK128_H
+
+#include "bitfn.h"
+
+typedef union {
+       uint64_t q[2];
+       uint32_t d[4];
+       uint16_t w[8];
+       uint8_t  b[16];
+} block128;
+
+static inline void block128_copy_bytes(block128 *block, uint8_t *src, uint32_t len)
+{
+	int i;
+	for (i = 0; i < len; i++) block->b[i] = src[i];
+}
+
+static inline void block128_copy(block128 *d, block128 *s)
+{
+	d->q[0] = s->q[0]; d->q[1] = s->q[1];
+}
+
+static inline void block128_zero(block128 *d)
+{
+	d->q[0] = 0; d->q[1] = 0;
+}
+
+static inline void block128_xor(block128 *d, block128 *s)
+{
+	d->q[0] ^= s->q[0];
+	d->q[1] ^= s->q[1];
+}
+
+static inline void block128_vxor(block128 *d, block128 *s1, block128 *s2)
+{
+	d->q[0] = s1->q[0] ^ s2->q[0];
+	d->q[1] = s1->q[1] ^ s2->q[1];
+}
+
+static inline void block128_xor_bytes(block128 *block, uint8_t *src, uint32_t len)
+{
+	int i;
+	for (i = 0; i < len; i++) block->b[i] ^= src[i];
+}
+
+static inline void block128_inc_be(block128 *b)
+{
+	uint64_t v = be64_to_cpu(b->q[1]);
+	if (++v == 0) {
+		b->q[0] = cpu_to_be64(be64_to_cpu(b->q[0]) + 1);
+		b->q[1] = 0;
+	} else
+		b->q[1] = cpu_to_be64(v);
+}
+
+#ifdef IMPL_DEBUG
+#include <stdio.h>
+static inline void block128_print(block128 *b)
+{
+	int i;
+	for (i = 0; i < 16; i++) {
+		printf("%02x ", b->b[i]);
+	}
+	printf("\n");
+}
+#endif
+
+#endif
diff --git a/cbits/cpu.c b/cbits/cpu.c
new file mode 100644
--- /dev/null
+++ b/cbits/cpu.c
@@ -0,0 +1,75 @@
+/*
+ *	Copyright (C) 2012 Vincent Hanquez <tab@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 "cpu.h"
+#include <stdint.h>
+
+#ifdef ARCH_X86
+static void cpuid(uint32_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
+{
+	*eax = info;
+	asm volatile
+		(
+#ifdef __x86_64__
+		 "mov %%rbx, %%rdi;"
+#else
+		 "mov %%ebx, %%edi;"
+#endif
+		 "cpuid;"
+		 "mov %%ebx, %%esi;"
+#ifdef __x86_64__
+		 "mov %%rdi, %%rbx;"
+#else
+		 "mov %%edi, %%ebx;"
+#endif
+		 :"+a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx)
+		 : :"edi");
+}
+
+#ifdef USE_AESNI
+void initialize_hw(void (*init_table)(int, int))
+{
+	static int inited = 0;
+	if (inited == 0) {
+		uint32_t eax, ebx, ecx, edx;
+		int aesni, pclmul;
+
+		inited = 1;
+		cpuid(1, &eax, &ebx, &ecx, &edx);
+		aesni = (ecx & 0x02000000);
+		pclmul = (ecx & 0x00000001);
+		init_table(aesni, pclmul);
+	}
+}
+#else
+#define initialize_hw(init_table) 	(0)
+#endif
+
+#endif
diff --git a/cbits/cpu.h b/cbits/cpu.h
new file mode 100644
--- /dev/null
+++ b/cbits/cpu.h
@@ -0,0 +1,45 @@
+/*
+ *	Copyright (C) 2012 Vincent Hanquez <tab@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.
+ *
+ */
+#ifndef CPU_H
+#define CPU_H
+
+#if defined(__i386__) || defined(__x86_64__)
+#define ARCH_X86
+#define USE_AESNI
+#endif
+
+#ifdef USE_AESNI
+void initialize_hw(void (*init_table)(int, int));
+#else
+#define initialize_hw(init_table) 	(0)
+#endif
+
+#endif
diff --git a/cbits/gf.c b/cbits/gf.c
new file mode 100644
--- /dev/null
+++ b/cbits/gf.c
@@ -0,0 +1,72 @@
+/*
+ * 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 <stdio.h>
+#include <stdint.h>
+#include "cpu.h"
+#include "gf.h"
+#include "aes_x86ni.h"
+
+/* this is a really inefficient way to GF multiply.
+ * the alternative without hw accel is building small tables
+ * to speed up the multiplication.
+ * TODO: optimise with tables
+ */
+void gf_mul(block128 *a, const block128 *b)
+{
+	uint64_t a0, a1, v0, v1;
+	int i, j;
+
+	a0 = a1 = 0;
+	v0 = cpu_to_be64(a->q[0]);
+	v1 = cpu_to_be64(a->q[1]);
+
+	for (i = 0; i < 16; i++)
+		for (j = 0x80; j != 0; j >>= 1) {
+			uint8_t x = b->b[i] & j;
+			a0 ^= x ? v0 : 0;
+			a1 ^= x ? v1 : 0;
+			x = (uint8_t) v1 & 1;
+			v1 = (v1 >> 1) | (v0 << 63);
+			v0 = (v0 >> 1) ^ (x ? (0xe1ULL << 56) : 0);
+		}
+	a->q[0] = cpu_to_be64(a0);
+	a->q[1] = cpu_to_be64(a1);
+}
+
+/* inplace GFMUL for xts mode */
+void gf_mulx(block128 *a)
+{
+	const uint64_t gf_mask = cpu_to_le64(0x8000000000000000ULL);
+	uint64_t r = ((a->q[1] & gf_mask) ? cpu_to_le64(0x87) : 0);
+	a->q[1] = cpu_to_le64((le64_to_cpu(a->q[1]) << 1) | (a->q[0] & gf_mask ? 1 : 0));
+	a->q[0] = cpu_to_le64(le64_to_cpu(a->q[0]) << 1) ^ r;
+}
+
diff --git a/cbits/gf.h b/cbits/gf.h
new file mode 100644
--- /dev/null
+++ b/cbits/gf.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+#ifndef _GF128MUL_H
+#define _GF128MUL_H
+
+#include "block128.h"
+
+void gf_mul(block128 *a, const block128 *b);
+void gf_mulx(block128 *a);
+
+#endif
diff --git a/cbits/include/aes.h b/cbits/include/aes.h
deleted file mode 100644
--- a/cbits/include/aes.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef AES_AES_H
-#define AES_AES_H
-
-#include <stdint.h>
-#include "aes_types.h"
-
-typedef aes_key AESKey;
-AESKey *allocate_key128();
-void generate_key128(AESKey *k, const uint8_t *bytes);
-void free_key128(AESKey *k);
-void encrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr);
-void decrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr);
-void aes_gcm_full_encrypt( const AESKey *k
-                         , uint8_t *iv, uint32_t ivLen
-                         , uint8_t *aad, uint32_t aadLen
-                         , uint8_t *pt, uint32_t ptLen
-                         , uint8_t *ct, uint8_t *tag);
-void aes_gcm_full_decrypt( const AESKey *k
-                         , uint8_t *iv, uint32_t ivLen
-                         , uint8_t *aad, uint32_t aadLen
-                         , uint8_t *ct, uint32_t ctLen
-                         , uint8_t *pt, uint8_t *tag);
-void free_gcm(aes_gcm *g);
-aes_gcm *allocate_gcm();
-void aes_gcm_init(aes_gcm *gcm, const aes_key *key);
-void aes_gcm_init_ctx(aes_gcm_ctx *ctx, const aes_gcm *gcm, uint8_t *iv, uint32_t len);
-void aes_gcm_encrypt(uint8_t *output, const aes_gcm *gcm, aes_gcm_ctx *ctx, const uint8_t *input, uint32_t length);
-void aes_gcm_decrypt(uint8_t *output, const aes_gcm *gcm, aes_gcm_ctx *ctx, const uint8_t *input, uint32_t length);
-void aes_gcm_aad(const aes_gcm *gcm, aes_gcm_ctx *ctx, uint8_t *input, uint32_t length);
-void aes_gcm_finish(uint8_t *tag, const aes_gcm *gcm, aes_gcm_ctx *ctx);
-void aes_gcm_enc_finish( uint8_t *output, uint8_t *tag
-                       , const aes_gcm *gcm
-                       , uint8_t *iv, uint32_t ivLen
-                       , uint8_t *input, uint32_t inputLen
-                       , uint8_t *aad, uint32_t aadLen);
-void aes_gcm_dec_finish( uint8_t *output, uint8_t *tag
-                       , const aes_gcm *gcm
-                       , uint8_t *iv, uint32_t ivLen
-                       , uint8_t *input, uint32_t inputLen
-                       , uint8_t *aad, uint32_t aadLen);
-
-void encrypt_ctr( const AESKey *key
-                , const uint8_t *iv  /* 16 bytes buffer with the count */
-                , uint8_t *newIV     /* the return buffer for the next IV (or NULL) */
-                , uint8_t *dst       /* 'len' byte buffer for output */
-                , const uint8_t *src /* 'len' bytes of input */
-                , uint32_t len);     /* Length in bytes */
-
-#endif
diff --git a/cbits/include/aes_generic.h b/cbits/include/aes_generic.h
deleted file mode 100644
--- a/cbits/include/aes_generic.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 "aes_types.h"
-#include "block128.h"
-#include <stdint.h>
-
-void aes128_generic_encrypt_block(aes_block *output, const aes_key *key, const aes_block *input);
-void aes128_generic_decrypt_block(aes_block *output, const aes_key *key, const aes_block *input);
-void aes128_generic_init(aes_key *key, const uint8_t *origkey, uint8_t size);
diff --git a/cbits/include/aes_types.h b/cbits/include/aes_types.h
deleted file mode 100644
--- a/cbits/include/aes_types.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef AES_TYPES_H
-#define AES_TYPES_H
-#include <stdint.h>
-#include "block128.h"
-
-typedef block128 aes_block;
-
-typedef struct {
-    uint8_t nbr;
-    uint8_t _padding[7];
-    uint8_t data[16*14*2];
-} aes_key;
-
-/* size = 4*16+2*8+aes_key=456 = 536 */
-typedef struct {
-    aes_block h;        // init
-    aes_key key;
-} aes_gcm;
-
-typedef struct {
-    aes_block iv;
-    aes_block civ;
-    aes_block tag;      // finish, aad, encrypt,decrypt
-    uint64_t length_input;
-    uint64_t length_aad;
-} aes_gcm_ctx;
-
-#endif
diff --git a/cbits/include/aes_x86ni.h b/cbits/include/aes_x86ni.h
deleted file mode 100644
--- a/cbits/include/aes_x86ni.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef AES_X86NI_H
-#define AES_X86NI_H
-
-#if defined(__i386__) || defined(__x86_64__)
-
-#include <wmmintrin.h>
-#include <tmmintrin.h>
-#include "aes_types.h"
-#include "block128.h"
-
-void aes128_ni_init(aes_key *key, const uint8_t *origkey, uint8_t size);
-void aes128_ni_encrypt_ecb(uint8_t *out, aes_key *key, const uint8_t *in, uint32_t blocks);
-void aes128_ni_decrypt_ecb(uint8_t *out, aes_key *key, const uint8_t *in, uint32_t blocks);
-void aes128_ni_encrypt_cbc(uint8_t *out, aes_key *key, uint8_t *_iv, uint8_t *in, uint32_t blocks);
-void aes128_ni_decrypt_cbc(uint8_t *out, aes_key *key, uint8_t *_iv, uint8_t *in, uint32_t blocks);
-void aes128_ni_encrypt_xts(uint8_t *out, aes_key *key1, aes_key *key2,
-                           uint8_t *_tweak, uint32_t spoint, uint8_t *in, uint32_t blocks);
-
-#endif
-
-#endif
diff --git a/cbits/include/bitfn.h b/cbits/include/bitfn.h
deleted file mode 100644
--- a/cbits/include/bitfn.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2006-2009 Vincent Hanquez <vincent@snarc.org>
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
- */
-
-#ifndef BITFN_H
-#define BITFN_H
-#include <stdint.h>
-
-#ifndef NO_INLINE_ASM
-/**********************************************************/
-# if (defined(__i386__))
-#  define ARCH_HAS_SWAP32
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	asm ("bswap %0" : "=r" (a) : "0" (a));
-	return a;
-}
-/**********************************************************/
-# elif (defined(__arm__))
-#  define ARCH_HAS_SWAP32
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	uint32_t tmp = a;
-	asm volatile ("eor %1, %0, %0, ror #16\n"
-	              "bic %1, %1, #0xff0000\n"
-	              "mov %0, %0, ror #8\n"
-	              "eor %0, %0, %1, lsr #8\n"
-	             : "=r" (a), "=r" (tmp) : "0" (a), "1" (tmp));
-	return a;
-}
-/**********************************************************/
-# elif defined(__x86_64__)
-#  define ARCH_HAS_SWAP32
-#  define ARCH_HAS_SWAP64
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	asm ("bswap %0" : "=r" (a) : "0" (a));
-	return a;
-}
-
-static inline uint64_t bitfn_swap64(uint64_t a)
-{
-	asm ("bswap %0" : "=r" (a) : "0" (a));
-	return a;
-}
-
-# endif
-#endif /* NO_INLINE_ASM */
-/**********************************************************/
-
-#ifndef ARCH_HAS_ROL32
-static inline uint32_t rol32(uint32_t word, uint32_t shift)
-{
-	return (word << shift) | (word >> (32 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_ROR32
-static inline uint32_t ror32(uint32_t word, uint32_t shift)
-{
-	return (word >> shift) | (word << (32 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_ROL64
-static inline uint64_t rol64(uint64_t word, uint32_t shift)
-{
-	return (word << shift) | (word >> (64 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_ROR64
-static inline uint64_t ror64(uint64_t word, uint32_t shift)
-{
-	return (word >> shift) | (word << (64 - shift));
-}
-#endif
-
-#ifndef ARCH_HAS_SWAP32
-static inline uint32_t bitfn_swap32(uint32_t a)
-{
-	return (a << 24) | ((a & 0xff00) << 8) | ((a >> 8) & 0xff00) | (a >> 24);
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_SWAP32
-static inline void array_swap32(uint32_t *d, uint32_t *s, uint32_t nb)
-{
-	while (nb--)
-		*d++ = bitfn_swap32(*s++);
-}
-#endif
-
-#ifndef ARCH_HAS_SWAP64
-static inline uint64_t bitfn_swap64(uint64_t a)
-{
-	return ((uint64_t) bitfn_swap32((uint32_t) (a >> 32))) |
-	       (((uint64_t) bitfn_swap32((uint32_t) a)) << 32);
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_SWAP64
-static inline void array_swap64(uint64_t *d, uint64_t *s, uint32_t nb)
-{
-	while (nb--)
-		*d++ = bitfn_swap64(*s++);
-}
-#endif
-
-#ifndef ARCH_HAS_MEMORY_ZERO
-static inline void memory_zero(void *ptr, uint32_t len)
-{
-	uint32_t *ptr32 = ptr;
-	uint8_t *ptr8;
-	int i;
-
-	for (i = 0; i < len / 4; i++)
-		*ptr32++ = 0;
-	if (len % 4) {
-		ptr8 = (uint8_t *) ptr32;
-		for (i = len % 4; i >= 0; i--)
-			ptr8[i] = 0;
-	}
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_COPY32
-static inline void array_copy32(uint32_t *d, uint32_t *s, uint32_t nb)
-{
-	while (nb--) *d++ = *s++;
-}
-#endif
-
-#ifndef ARCH_HAS_ARRAY_COPY64
-static inline void array_copy64(uint64_t *d, uint64_t *s, uint32_t nb)
-{
-	while (nb--) *d++ = *s++;
-}
-#endif
-
-#ifdef __MINGW32__
-  # define LITTLE_ENDIAN 1234
-  # define BYTE_ORDER    LITTLE_ENDIAN
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-  # include <sys/endian.h>
-#elif defined(__OpenBSD__) || defined(__SVR4)
-  # include <sys/types.h>
-#elif defined(__APPLE__)
-  # include <machine/endian.h>
-#elif defined( BSD ) && ( BSD >= 199103 )
-  # include <machine/endian.h>
-#else
-  # include <endian.h>
-#endif
-/* big endian to cpu */
-#if LITTLE_ENDIAN == BYTE_ORDER
-
-# define be32_to_cpu(a) bitfn_swap32(a)
-# define cpu_to_be32(a) bitfn_swap32(a)
-# define le32_to_cpu(a) (a)
-# define cpu_to_le32(a) (a)
-# define be64_to_cpu(a) bitfn_swap64(a)
-# define cpu_to_be64(a) bitfn_swap64(a)
-# define le64_to_cpu(a) (a)
-# define cpu_to_le64(a) (a)
-
-# define cpu_to_le32_array(d, s, l) array_copy32(d, s, l)
-# define le32_to_cpu_array(d, s, l) array_copy32(d, s, l)
-# define cpu_to_be32_array(d, s, l) array_swap32(d, s, l)
-# define be32_to_cpu_array(d, s, l) array_swap32(d, s, l)
-
-# define cpu_to_le64_array(d, s, l) array_copy64(d, s, l)
-# define le64_to_cpu_array(d, s, l) array_copy64(d, s, l)
-# define cpu_to_be64_array(d, s, l) array_swap64(d, s, l)
-# define be64_to_cpu_array(d, s, l) array_swap64(d, s, l)
-
-# define ror32_be(a, s) rol32(a, s)
-# define rol32_be(a, s) ror32(a, s)
-
-# define ARCH_IS_LITTLE_ENDIAN
-
-#elif BIG_ENDIAN == BYTE_ORDER
-
-# define be32_to_cpu(a) (a)
-# define cpu_to_be32(a) (a)
-# define be64_to_cpu(a) (a)
-# define cpu_to_be64(a) (a)
-# define le64_to_cpu(a) bitfn_swap64(a)
-# define cpu_to_le64(a) bitfn_swap64(a)
-# define le32_to_cpu(a) bitfn_swap32(a)
-# define cpu_to_le32(a) bitfn_swap32(a)
-
-# define cpu_to_le32_array(d, s, l) array_swap32(d, s, l)
-# define le32_to_cpu_array(d, s, l) array_swap32(d, s, l)
-# define cpu_to_be32_array(d, s, l) array_copy32(d, s, l)
-# define be32_to_cpu_array(d, s, l) array_copy32(d, s, l)
-
-# define cpu_to_le64_array(d, s, l) array_swap64(d, s, l)
-# define le64_to_cpu_array(d, s, l) array_swap64(d, s, l)
-# define cpu_to_be64_array(d, s, l) array_copy64(d, s, l)
-# define be64_to_cpu_array(d, s, l) array_copy64(d, s, l)
-
-# define ror32_be(a, s) ror32(a, s)
-# define rol32_be(a, s) rol32(a, s)
-
-# define ARCH_IS_BIG_ENDIAN
-
-#else
-# error "endian not supported"
-#endif
-
-#endif /* !BITFN_H */
diff --git a/cbits/include/block128.h b/cbits/include/block128.h
deleted file mode 100644
--- a/cbits/include/block128.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef BLOCK128_H
-#define BLOCK128_H
-
-#include "bitfn.h"
-
-typedef union {
-       uint64_t q[2];
-       uint32_t d[4];
-       uint16_t w[8];
-       uint8_t  b[16];
-} block128;
-
-static inline void block128_copy_bytes(block128 *block, const uint8_t *src, uint32_t len)
-{
-	int i;
-	for (i = 0; i < len; i++) block->b[i] = src[i];
-}
-
-static inline void block128_copy(block128 *d, block128 *s)
-{
-	d->q[0] = s->q[0]; d->q[1] = s->q[1];
-}
-
-static inline void block128_zero(block128 *d)
-{
-	d->q[0] = 0; d->q[1] = 0;
-}
-
-static inline void block128_xor(block128 *d, block128 *s)
-{
-	d->q[0] ^= s->q[0];
-	d->q[1] ^= s->q[1];
-}
-
-static inline void block128_vxor(block128 *d, block128 *s1, block128 *s2)
-{
-	d->q[0] = s1->q[0] ^ s2->q[0];
-	d->q[1] = s1->q[1] ^ s2->q[1];
-}
-
-static inline void block128_xor_bytes(block128 *block, const uint8_t *src, uint32_t len)
-{
-	int i;
-	for (i = 0; i < len; i++) block->b[i] ^= src[i];
-}
-
-static inline void block128_inc_be(block128 *b)
-{
-	uint64_t v = be64_to_cpu(b->q[1]);
-	if (++v == 0) {
-		b->q[0] = cpu_to_be64(be64_to_cpu(b->q[0]) + 1);
-		b->q[1] = 0;
-	} else
-		b->q[1] = cpu_to_be64(v);
-}
-
-#endif
diff --git a/cipher-aes128.cabal b/cipher-aes128.cabal
--- a/cipher-aes128.cabal
+++ b/cipher-aes128.cabal
@@ -2,11 +2,13 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                cipher-aes128
-version:             0.5
-synopsis:            AES128 using AES-NI when available.
-description:         AES128 with crypto-api instances and a trampoline between Vincent Hanquez's
-                     C-based and x86 NI-based AES.  Patches welcome to add additional
-                     high-performance backends (ARM?)
+version:             0.6
+synopsis:            AES and common modes using AES-NI when available.
+description:         Cipher-aes128 is an implementation of AES and common modes of operation.  It borrows Hanquez's C AES code (see 'cipher-aes') but
+                     is unique due to including compile-time detection of
+                     NI compiler support, a slightly more functional interface
+                     for GCM operations, exposure of 'Ptr' based operations via the .Internal module, and build-in crypto-api support.
+                     Cipher-aes128 was originally developed as "'cipher-aes' plus trampolines", which has since been adopted into cipher-aes.
 license:             BSD3
 license-file:        LICENSE
 author:              Thomas M. DuBuisson, Vincent Hanquez (See AUTHORS file)
@@ -18,29 +20,30 @@
 build-type:          Custom
 cabal-version:       >=1.12
 
-extra-source-files:       ./cbits/include/aes.h
-                        , ./cbits/include/aes_generic.h
-                        , ./cbits/include/aes_types.h
-                        , ./cbits/include/aes_x86ni.h
-                        , ./cbits/include/bitfn.h
-                        , ./cbits/include/block128.h
-                        , ./cbits/aes/LICENSE
+extra-source-files:       ./cbits/*.h, ./cbits/*.c
                         , AUTHORS
+                        , cbits/LICENSE
+flag test
+  description: Build a program to test the AES implementation
+  default: False
+
 library
   default-language:    Haskell2010
   exposed-modules:     Crypto.Cipher.AES128, Crypto.Cipher.AES128.Internal
-  build-depends:       base >=4.5 && < 5,
+  build-depends:       base >=4.2 && < 5,
                        bytestring,
-                       crypto-api >= 0.11,
+                       crypto-api >= 0.13,
                        tagged,
                        cereal
   ghc-options:         -Wall -O2
 
-  c-sources:              ./cbits/aes/aes.c
-                        , ./cbits/aes/ni/aes_x86ni.c
-                        , ./cbits/aes/generic/aes_generic.c
-  include-dirs:        cbits/include
-  cc-options:          -O3
+  c-sources:           ./cbits/aes.c
+                     , ./cbits/aes_generic.c
+                     , ./cbits/aes_x86ni.c
+                     , ./cbits/cpu.c
+                     , ./cbits/gf.c
+  include-dirs:        cbits/
+  -- None by default cc-options:          -mssse3 -maes -mpclmul
 
 source-repository head
   type:         git
@@ -50,10 +53,23 @@
   type:         exitcode-stdio-1.0
   hs-source-dirs:       . Benchmark
   main-is:              bench.hs
-  build-depends:        base < 5, criterion, crypto-api, entropy, bytestring, tagged, cereal
+  build-depends:        base < 5, criterion, crypto-api, entropy, bytestring, tagged, cereal, cipher-aes
   ghc-options:          -O2
-  include-dirs:        cbits/include
-  c-sources:              ./cbits/aes/aes.c
-                        , ./cbits/aes/ni/aes_x86ni.c
-                        , ./cbits/aes/generic/aes_generic.c
-  cc-options:          -O3
+  include-dirs:        cbits
+  c-sources:           ./cbits/aes.c
+                     , ./cbits/aes_generic.c
+                     , ./cbits/aes_x86ni.c
+                     , ./cbits/cpu.c
+                     , ./cbits/gf.c
+  cc-options:          -mssse3 -maes -mpclmul
+
+Executable aes128_test
+  if !flag(test)
+    buildable: False
+  main-is: Test/test.hs
+  if flag(test)
+    build-depends:    base
+                    , crypto-api-tests
+                    , test-framework
+                    , cipher-aes128
+                    , bytestring, tagged, cereal, crypto-api
