diff --git a/AUTHORS b/AUTHORS
new file mode 100644
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+Vincent Hanquez implemented, in C, the AES routines.
+
+Thomas M. DuBuisson is the original author of the implementation selection
+routines in cbits/aes/aes.c and Haskell bindings.
diff --git a/Crypto/Cipher/AES128.hs b/Crypto/Cipher/AES128.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/AES128.hs
@@ -0,0 +1,46 @@
+module Crypto.Cipher.AES128
+  ( AESKey
+  ) where
+
+import Crypto.Cipher.AES128.Internal
+import Crypto.Classes
+import Data.Serialize
+import Data.Tagged
+import Foreign.Ptr
+import System.IO.Unsafe
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Internal as B
+import qualified Data.ByteString.Unsafe as B
+
+instance Serialize AESKey where
+    put k = do
+        let RKey l h = (rawKey k)
+        putWord64be h
+        putWord64be l
+
+    get = do
+        b <- getByteString 16
+        case buildKey b of
+            Nothing -> fail "Invalid key on 'get'"
+            Just k  -> return k
+
+instance BlockCipher AESKey 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)
+      | 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)
+
+blkSize :: Int
+blkSize = 16
diff --git a/Crypto/Cipher/AES128/Internal.hs b/Crypto/Cipher/AES128/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/AES128/Internal.hs
@@ -0,0 +1,126 @@
+{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
+module Crypto.Cipher.AES128.Internal
+        ( AESKey(..), RawKey(..)
+        , generateKey
+        , encryptECB
+        , decryptECB
+        , encryptGCM
+        , decryptGCM
+        ) where
+
+import Foreign.Ptr
+import Foreign.ForeignPtr
+import Foreign.Storable
+import Data.Word
+import Data.Bits (shiftL, (.|.))
+
+-- AES Bindings
+data AESKeyStruct
+type AESKeyPtr = Ptr AESKeyStruct
+data RawKey = RKey { lowK,highK :: {-# UNPACK #-} !Word64 }
+data AESKey = AESKey { rawKey      :: !RawKey
+                     , expandedKey :: ForeignPtr AESKeyStruct }
+
+foreign import ccall unsafe "aes/aes.h generate_key128"
+        c_generate_key128 :: AESKeyPtr -> Ptr Word8 -> IO ()
+
+foreign import ccall unsafe "aes/aes.h allocate_key128"
+        c_allocate_key128 :: IO AESKeyPtr
+
+foreign import ccall unsafe "aes/aes.h &free_key128"
+        c_free_key128 :: FunPtr (AESKeyPtr -> IO ())
+
+foreign import ccall unsafe "aes/aes.h encrypt_ecb"
+        c_encrypt_ecb :: AESKeyPtr -> Ptr Word8 -> Ptr Word8 -> Word32 -> 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/aes.h aes_gcm_full_encrypt"
+    c_encrypt_gcm :: AESKeyPtr
+                  -> Ptr Word8 -> Word32 -- ^ IV and length
+                  -> Ptr Word8 -> Word32 -- ^ AAD and length
+                  -> Ptr Word8 -> Word32 -- ^ PT and length
+                  -> Ptr Word8 -> Ptr Word8 -- ^ Result CT and TAG
+                  -> IO ()
+
+foreign import ccall unsafe "aes/aes.h aes_gcm_full_decrypt"
+    c_decrypt_gcm :: AESKeyPtr
+                  -> Ptr Word8 -> Word32 -- ^ IV and length
+                  -> Ptr Word8 -> Word32 -- ^ AAD and length
+                  -> Ptr Word8 -> Word32 -- ^ CT and length
+                  -> Ptr Word8 -> Ptr Word8 -- ^ Result PT and TAG
+                  -> IO ()
+
+blkSzC :: Word32
+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
+    k <- c_allocate_key128
+    c_generate_key128 k (castPtr keyPtr)
+    raw <- do
+            a <- peekLE (castPtr keyPtr)
+            let keyPtr2 = (castPtr keyPtr) `plusPtr` sizeOf a
+            b <- peekLE keyPtr2
+            return (RKey a b)
+    fmap (AESKey raw) (newForeignPtr c_free_key128 k)
+ where
+     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 a = (a1 `shiftL` 56) .|. (a2 `shiftL` 48) .|. (a3 `shiftL` 40) .|.
+                (a4 `shiftL` 32) .|. (a5 `shiftL` 24) .|. (a6 `shiftL` 16) .|.
+                (a7 `shiftL` 8)  .|. a8
+        return a
+{-# INLINE generateKey #-}
+
+-- An encrypt function that can handle up to blks < maxBound `div` 16 :: Word32
+-- simultaneous blocks.
+encryptECB :: AESKey    -- ^ 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)
+{-# INLINE encryptECB #-}
+
+decryptECB :: AESKey    -- ^ 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
+  | 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)
+{-# INLINE decryptECB #-}
+
+encryptGCM :: AESKey
+           -> Ptr Word8 -> Int -- IV
+           -> Ptr Word8 -> Int -- AAD
+           -> Ptr Word8 -> Int -- PT
+           -> Ptr Word8        -- CT  (output)
+           -> Ptr Word8        -- Tag (output)
+           -> IO ()
+encryptGCM (AESKey _ k) iv ivLen aad aadLen pt ptLen ct tag = withForeignPtr k $ \p -> do
+        c_encrypt_gcm p iv (fromIntegral ivLen) aad (fromIntegral aadLen) pt (fromIntegral ptLen) ct tag
+{-# INLINE encryptGCM #-}
+
+decryptGCM :: AESKey
+           -> Ptr Word8 -> Int -- IV
+           -> Ptr Word8 -> Int -- AAD
+           -> Ptr Word8 -> Int -- CT
+           -> Ptr Word8        -- PT  (output)
+           -> Ptr Word8        -- Tag (output)
+           -> IO ()
+decryptGCM (AESKey _ k) iv ivLen aad aadLen ct ctLen pt tag = withForeignPtr k $ \p -> do
+        c_decrypt_gcm p iv (fromIntegral ivLen) aad (fromIntegral aadLen) ct (fromIntegral ctLen) pt tag
+{-# INLINE decryptGCM #-}
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) Thomas DuBuisson
+
+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 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/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,74 @@
+import Distribution.Simple
+import Distribution.Simple.Setup
+import Distribution.PackageDescription
+import Distribution.Simple.Utils
+import Distribution.Simple.Program
+import Distribution.Verbosity
+import System.Process
+import System.Directory
+import System.Exit
+
+main = defaultMainWithHooks hk
+ where
+ hk = simpleUserHooks { preBuild = \as fs -> do
+                                        b <- (preBuild simpleUserHooks) as fs
+                                        checkAndAddAES fs b }
+
+checkAndAddAES :: BuildFlags -> HookedBuildInfo -> IO HookedBuildInfo
+checkAndAddAES fs x@(m,as) = do
+        b  <- canUseAesIntrinsicsFlag fs
+        b2 <- haveNIInstrs fs
+        let bi = maybe emptyBuildInfo id m
+            op = if b && b2 then addNIintrinsicOpt else id
+        return (Just (op emptyBuildInfo), as)
+
+addNIintrinsicOpt :: BuildInfo -> BuildInfo
+addNIintrinsicOpt bi = bi { ccOptions = ccOptions bi ++ ["-maes", "-mssse3", "-DHAVE_AES_INTRINSICS", "-DHAVE_NI"] }
+
+-- Detect if we have AES NI instructions for acceleration on x86 CPUs
+haveNIInstrs :: BuildFlags -> IO Bool
+haveNIInstrs cf = do
+        -- FIXME cf is always empty, how do we ensure we're testing the right C compiler?
+        -- TODO: I am hooking Cabal at the wrong spot, need to hook one stage
+        -- later!
+        let prog = lookup "cc" (buildProgramPaths cf)
+            cc = maybe "gcc" id prog
+        withTempDirectory normal "" "testNI" $ \tmpDir -> do
+         writeFile (tmpDir ++ "/testNI.c")
+               (unlines ["#include <stdint.h>",
+                        "/**",
+                        " * Returns zero if false, non-zero otherwise",
+                        " */",
+                        "int cpu_has_ni()",
+                        "{",
+                        "uint32_t ax,bx,cx,dx,func=1;",
+                        "uint32_t regs[4];",
+                        "asm volatile (\"cpuid\":",
+                        "\"=a\" (ax), \"=b\" (bx), \"=c\" (cx), \"=d\" (dx) : \"a\" (func));",
+                        "return (cx & 0x2000000);",
+                        "}",
+                        "int main()",
+                        "{",
+                        "    if(cpu_has_ni()) return 0; else return -1;",
+                        "}"]
+                                )
+         ec <- rawSystemExitCode normal cc ["-maes",tmpDir ++ "/testNI.c"]
+         notice normal $ "Result of AES NI Test: " ++ show (ec == ExitSuccess)
+         return (ec == ExitSuccess)
+
+canUseAesIntrinsicsFlag :: BuildFlags -> IO Bool
+canUseAesIntrinsicsFlag cf = do
+        -- FIXME cf is always empty, how do we ensure we're testing the right C compiler?
+        -- TODO: I am hooking Cabal at the wrong spot, need to hook one stage
+        -- later!
+        let prog = lookup "cc" (buildProgramPaths cf)
+            cc = maybe "gcc" id prog
+        withTempDirectory normal "" "testIntrinsic" $ \tmpDir -> do
+        writeFile (tmpDir ++ "/testIntrinsic.c")
+                (unlines        [ "#include <wmmintrin.h>"
+                                , "int main() {"
+                                , "return 0; }"
+                                ])
+        ec <- rawSystemExitCode normal cc ["-maes",tmpDir ++ "/testIntrinsic.c"]
+        notice normal $ "Result of NI Intrinsics Test: " ++ show (ec == ExitSuccess)
+        return (ec == ExitSuccess)
diff --git a/cbits/aes/LICENSE b/cbits/aes/LICENSE
new file mode 100644
--- /dev/null
+++ b/cbits/aes/LICENSE
@@ -0,0 +1,29 @@
+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
new file mode 100644
--- /dev/null
+++ b/cbits/aes/aes.c
@@ -0,0 +1,365 @@
+#include "aes_generic.h"
+#include "aes.h"
+#include <string.h>
+#include <stdlib.h>
+
+/* The "HAVE_NI" macro is also defined (or not) but we no longer need that
+ * since we're doing a runtime detection... again. */
+#if (defined(__i386__) || defined(__x86_64__)) && defined(HAVE_AES_INTRINSICS) && !defined(AVOID_NI)
+#define TRY_NI
+#include "aes_x86ni.h"
+#endif
+#include "bitfn.h"
+void gf_mul(block128 *a, block128 *b);
+
+#ifdef TRY_NI
+/**
+ * Returns zero if false, non-zero otherwise
+ */
+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;
+}
+
+/* Generate Key */
+#ifdef TRY_NI
+void detect_and_generate_key128(AESKey *k, const uint8_t *bytes);
+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);
+}
+
+void generate_key128_generic(AESKey *k, const uint8_t *bytes)
+{
+    aes_generic_init(k, bytes, 16);
+}
+
+void generate_key128_ni(AESKey *k, const uint8_t *bytes)
+{
+    aes_ni_init((aes_key *)k, bytes, 16);
+}
+
+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)
+{
+    aes_generic_init(k, bytes, 16);
+}
+#endif
+
+void free_key128(AESKey *k)
+{
+        memset(k, 0, sizeof(AESKey));
+        free(k);
+}
+
+/* ECB Encrypt */
+#ifdef TRY_NI
+void detect_and_encrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr);
+void (*encrypt_ecb_ptr)(const AESKey *, uint8_t *, const uint8_t *, const uint32_t) = &detect_and_encrypt_ecb;
+
+void encrypt_ecb_ni(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
+{
+        aes_ni_encrypt_ecb(dst, (aes_key *)k, src, nr);
+}
+
+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) {
+        aes_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);
+}
+
+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) {
+        aes_generic_encrypt_block
+                       ( (aes_block*) dst+i
+                       , k
+                       , (const aes_block*)src+i);
+    }
+}
+#endif
+
+/* ECB Decrypt */
+#ifdef TRY_NI
+void detect_and_decrypt_ecb(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr);
+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);
+}
+
+void decrypt_ecb_ni(const AESKey *k, uint8_t *dst, const uint8_t *src, const uint32_t nr)
+{
+        aes_ni_decrypt_ecb(dst, (aes_key *)k, src, nr);
+}
+
+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) {
+        aes_generic_decrypt_block ( (aes_block*) dst+i
+                                  , k
+                                  , (const aes_block*)src+i);
+    }
+}
+
+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) {
+        aes_generic_decrypt_block ( (aes_block*) dst+i
+                                  , k
+                                  , (const aes_block*)src+i);
+    }
+}
+#endif
+
+
+/* GCM */
+static void gcm_ghash_add(aes_gcm *gcm, block128 *b)
+{
+        block128_xor(&gcm->tag, b);
+        gf_mul(&gcm->tag, &gcm->h);
+}
+
+void aes_gcm_init(aes_gcm *gcm, const aes_key *key, uint8_t *iv, uint32_t len)
+{
+        gcm->length_aad = 0;
+        gcm->length_input = 0;
+
+        block128_zero(&gcm->h);
+        block128_zero(&gcm->tag);
+        block128_zero(&gcm->iv);
+
+        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);
+
+        if (len == 12) {
+                block128_copy_bytes(&gcm->iv, iv, 12);
+                gcm->iv.b[15] = 0x01;
+        } else {
+                uint32_t origlen = len << 3;
+                int i;
+                for (; len >= 16; len -= 16, iv += 16) {
+                        block128_xor(&gcm->iv, (block128 *) iv);
+                        gf_mul(&gcm->iv, &gcm->h);
+                }
+                if (len > 0) {
+                        block128_xor_bytes(&gcm->iv, iv, len);
+                        gf_mul(&gcm->iv, &gcm->h);
+                }
+                for (i = 15; origlen; --i, origlen >>= 8)
+                        gcm->iv.b[i] ^= (uint8_t) origlen;
+                gf_mul(&gcm->iv, &gcm->h);
+        }
+
+        block128_copy(&gcm->civ, &gcm->iv);
+}
+
+void aes_gcm_aad(aes_gcm *gcm, uint8_t *input, uint32_t length)
+{
+        gcm->length_aad += length;
+        for (; length >= 16; input += 16, length -= 16) {
+                gcm_ghash_add(gcm, (block128 *) input);
+        }
+        if (length > 0) {
+                aes_block tmp;
+                block128_zero(&tmp);
+                block128_copy_bytes(&tmp, input, length);
+                gcm_ghash_add(gcm, &tmp);
+        }
+
+}
+
+void aes_gcm_encrypt(uint8_t *output, aes_gcm *gcm, uint8_t *input, uint32_t length)
+{
+        aes_block out;
+
+        gcm->length_input += length;
+        for (; length >= 16; input += 16, output += 16, length -= 16) {
+                block128_inc_be(&gcm->civ);
+
+                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->civ, 1);
+                block128_xor(&out, (block128 *) input);
+                gcm_ghash_add(gcm, &out);
+                block128_copy((block128 *) output, &out);
+        }
+        if (length > 0) {
+                aes_block tmp;
+                int i;
+
+                block128_inc_be(&gcm->civ);
+                /* create e(civ) in out */
+                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->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, &tmp);
+
+                for (i = 0; i < length; i++) {
+                        output[i] = tmp.b[i];
+                }
+        }
+}
+
+void aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, uint8_t *input, uint32_t length)
+{
+        aes_block out;
+
+        gcm->length_input += length;
+        for (; length >= 16; input += 16, output += 16, length -= 16) {
+                block128_inc_be(&gcm->civ);
+
+                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->civ, 1);
+                gcm_ghash_add(gcm, (block128 *) input);
+                block128_xor(&out, (block128 *) input);
+                block128_copy((block128 *) output, &out);
+        }
+        if (length > 0) {
+                aes_block tmp;
+                int i;
+
+                block128_inc_be(&gcm->civ);
+
+                block128_zero(&tmp);
+                block128_copy_bytes(&tmp, input, length);
+                gcm_ghash_add(gcm, &tmp);
+
+                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->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, aes_gcm *gcm)
+{
+        aes_block lblock;
+        int i;
+
+        /* tag = (tag-1 xor (lenbits(a) | lenbits(c)) ) . H */
+        lblock.q[0] = cpu_to_be64(gcm->length_aad << 3);
+        lblock.q[1] = cpu_to_be64(gcm->length_input << 3);
+        gcm_ghash_add(gcm, &lblock);
+
+        encrypt_ecb(&gcm->key, (uint8_t *)&lblock, (const uint8_t*)&gcm->iv, 1);
+        block128_xor(&gcm->tag, &lblock);
+
+        for (i = 0; i < 16; i++) {
+                tag[i] = gcm->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 gcm;
+    aes_gcm_init(&gcm, k, iv, ivLen);
+    aes_gcm_aad(&gcm, aad, aadLen);
+    aes_gcm_encrypt(ct, &gcm,  pt, ptLen);
+    aes_gcm_finish(tag, &gcm);
+}
+
+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_init(&gcm, k, iv, ivLen);
+    aes_gcm_aad(&gcm, aad, aadLen);
+    aes_gcm_decrypt(pt, &gcm,  ct, ctLen);
+    aes_gcm_finish(tag, &gcm);
+}
+
+
+/* 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, 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);
+}
diff --git a/cbits/aes/generic/aes_generic.c b/cbits/aes/generic/aes_generic.c
new file mode 100644
--- /dev/null
+++ b/cbits/aes/generic/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_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 }
+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 aes_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 aes_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 aes_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
new file mode 100644
--- /dev/null
+++ b/cbits/aes/ni/aes_x86ni.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2012 Vincent Hanquez <vincent@snarc.org>
+ * 
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of his contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <wmmintrin.h>
+#include <tmmintrin.h>
+#include "aes.h"
+#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 aes_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 aes_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 aes_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 aes_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 aes_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 aes_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 */
diff --git a/cbits/include/aes.h b/cbits/include/aes.h
new file mode 100644
--- /dev/null
+++ b/cbits/include/aes.h
@@ -0,0 +1,24 @@
+#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);
+
+#endif
diff --git a/cbits/include/aes_generic.h b/cbits/include/aes_generic.h
new file mode 100644
--- /dev/null
+++ b/cbits/include/aes_generic.h
@@ -0,0 +1,36 @@
+/*
+ * 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 aes_generic_encrypt_block(aes_block *output, const aes_key *key, const aes_block *input);
+void aes_generic_decrypt_block(aes_block *output, const aes_key *key, const aes_block *input);
+void aes_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
new file mode 100644
--- /dev/null
+++ b/cbits/include/aes_types.h
@@ -0,0 +1,25 @@
+#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 tag;
+    aes_block h;
+    aes_block iv;
+    aes_block civ;
+    uint64_t length_aad;
+    uint64_t length_input;
+    aes_key key;
+} aes_gcm;
+
+#endif
diff --git a/cbits/include/aes_x86ni.h b/cbits/include/aes_x86ni.h
new file mode 100644
--- /dev/null
+++ b/cbits/include/aes_x86ni.h
@@ -0,0 +1,53 @@
+/*
+ * 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 aes_ni_init(aes_key *key, const uint8_t *origkey, uint8_t size);
+void aes_ni_encrypt_ecb(uint8_t *out, aes_key *key, const uint8_t *in, uint32_t blocks);
+void aes_ni_decrypt_ecb(uint8_t *out, aes_key *key, const uint8_t *in, uint32_t blocks);
+void aes_ni_encrypt_cbc(uint8_t *out, aes_key *key, uint8_t *_iv, uint8_t *in, uint32_t blocks);
+void aes_ni_decrypt_cbc(uint8_t *out, aes_key *key, uint8_t *_iv, uint8_t *in, uint32_t blocks);
+void aes_ni_encrypt_xts(uint8_t *out, aes_key *key1, aes_key *key2,
+                        uint8_t *_tweak, uint32_t spoint, uint8_t *in, uint32_t blocks);
+
+void gf_mul_x86ni(block128 *res, block128 *a_, block128 *b_);
+
+#endif
+
+#endif
diff --git a/cbits/include/bitfn.h b/cbits/include/bitfn.h
new file mode 100644
--- /dev/null
+++ b/cbits/include/bitfn.h
@@ -0,0 +1,232 @@
+/*
+ * 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
new file mode 100644
--- /dev/null
+++ b/cbits/include/block128.h
@@ -0,0 +1,87 @@
+/*
+ * 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);
+}
+
+#endif
diff --git a/cipher-aes128.cabal b/cipher-aes128.cabal
new file mode 100644
--- /dev/null
+++ b/cipher-aes128.cabal
@@ -0,0 +1,39 @@
+-- Initial cipher.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                cipher-aes128
+version:             0.1
+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?)
+license:             BSD3
+license-file:        LICENSE
+author:              Thomas M. DuBuisson, Vincent Hanquez (See AUTHORS file)
+maintainer:          thomas.dubuisson@gmail.com
+copyright:           Thomas M. DuBuisson
+category:            Cryptography
+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
+                        , AUTHORS
+library
+  default-language:    Haskell2010
+  exposed-modules:     Crypto.Cipher.AES128, Crypto.Cipher.AES128.Internal
+  build-depends:       base >=4.5 && < 5,
+                       bytestring,
+                       crypto-api,
+                       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
