diff --git a/Crypto/Cipher/AES128.hs b/Crypto/Cipher/AES128.hs
--- a/Crypto/Cipher/AES128.hs
+++ b/Crypto/Cipher/AES128.hs
@@ -1,3 +1,4 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
 module Crypto.Cipher.AES128
   ( AESKey
   ) where
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,11 +1,12 @@
 {-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
 module Crypto.Cipher.AES128.Internal
-        ( AESKey(..), RawKey(..)
-        , generateKey
+        ( AESKey(..), RawKey(..), GCM(..)
+        , generateKey, generateGCM
         , encryptECB
         , decryptECB
         , encryptGCM
         , decryptGCM
+        , finishGCM
         , encryptCTR
         , decryptCTR
         ) where
@@ -13,6 +14,7 @@
 import Foreign.Ptr
 import Foreign.ForeignPtr
 import Foreign.Storable
+import Foreign.Marshal.Alloc
 import Data.Word
 import Data.Bits (shiftL, (.|.))
 
@@ -23,6 +25,10 @@
 data AESKey = AESKey { rawKey      :: !RawKey
                      , expandedKey :: ForeignPtr AESKeyStruct }
 
+type AESGcmPtr = Ptr GCMStruct
+data GCMStruct
+data GCM = GCM { _gcmFP  :: ForeignPtr GCMStruct }
+
 foreign import ccall unsafe "aes/aes.h generate_key128"
         c_generate_key128 :: AESKeyPtr -> Ptr Word8 -> IO ()
 
@@ -32,28 +38,51 @@
 foreign import ccall unsafe "aes/aes.h &free_key128"
         c_free_key128 :: FunPtr (AESKeyPtr -> IO ())
 
+foreign import ccall unsafe "aes/aes.h free_key128"
+        c_key128_free :: 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
+foreign import ccall unsafe "aes/aes.h allocate_gcm"
+    c_gcm_allocate :: IO AESGcmPtr
+
+foreign import ccall unsafe "aes/aes.h aes_gcm_init"
+    c_gcm_init :: AESGcmPtr
+               -> 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
+                  -> AESGcmPtr
+                  -> Ptr Word8 -> Word32 -- IV and length
+                  -> Ptr Word8 -> Word32 -- PT and length
+                  -> Ptr Word8 -> Word32 -- AAD and length
                   -> 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
+foreign import ccall unsafe "aes/aes.h aes_gcm_dec_finish"
+    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
                   -> IO ()
 
+foreign import ccall unsafe "aes/aes.h allocate_gcm"
+    c_gcm_finish :: Ptr Word8 -> AESGcmPtr -> IO ()
+
+foreign import ccall unsafe "aes/aes.h &free_gcm"
+    c_free_gcm :: FunPtr (AESGcmPtr -> IO ())
+
+foreign import ccall unsafe "aes/aes.h free_gcm"
+    _c_gcm_free :: AESGcmPtr -> IO ()
+
 foreign import ccall unsafe "aes/aes.h encrypt_ctr"
     c_encrypt_ctr :: AESKeyPtr
                   -> Ptr Word8 -- ^ 128 bit IV
@@ -105,6 +134,21 @@
         return a
 {-# INLINE generateKey #-}
 
+-- 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)
+{-# INLINE generateGCM #-}
+
 -- An encrypt function that can handle up to blks < maxBound `div` 16 :: Word32
 -- simultaneous blocks.
 encryptECB :: AESKey    -- ^ The key
@@ -125,27 +169,35 @@
   | 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)
+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 (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 #-}
+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)
 
-decryptGCM :: AESKey
-           -> Ptr Word8 -> Int -- IV
-           -> Ptr Word8 -> Int -- AAD
-           -> Ptr Word8 -> Int -- CT
-           -> Ptr Word8        -- PT  (output)
-           -> Ptr Word8        -- Tag (output)
+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 (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 #-}
+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)
+
+finishGCM :: GCM
+          -> Ptr Word8 -- Tag, must point to 16 byte buffer (or larger)
+          -> IO ()
+finishGCM (GCM k) tagPtr = withForeignPtr k $ \g -> c_gcm_finish tagPtr g
 
 encryptCTR :: AESKey
            -> Ptr Word8 -- ^ IV
diff --git a/cbits/aes/aes.c b/cbits/aes/aes.c
--- a/cbits/aes/aes.c
+++ b/cbits/aes/aes.c
@@ -8,7 +8,7 @@
 #include "aes_x86ni.h"
 #endif
 #include "bitfn.h"
-static void gf_mul(block128 *a, block128 *b);
+static void gf_mul(block128 *a, const block128 *b);
 
 #ifdef TRY_NI
 /**
@@ -32,6 +32,13 @@
         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);
@@ -78,6 +85,12 @@
         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);
@@ -168,89 +181,118 @@
 
 
 /* GCM */
-static void gcm_ghash_add(aes_gcm *gcm, block128 *b)
+static void gcm_ghash_add(const aes_gcm *gcm, aes_gcm_ctx *ctx, block128 *b)
 {
-        block128_xor(&gcm->tag, b);
-        gf_mul(&gcm->tag, &gcm->h);
+        block128_xor(&ctx->tag, b);
+        gf_mul(&ctx->tag, &gcm->h);
 }
 
-static void aes_gcm_init(aes_gcm *gcm, const aes_key *key, uint8_t *iv, uint32_t len)
+void aes_gcm_ctx_init(aes_gcm_ctx *ctx, const aes_gcm *gcm, const 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);
+        ctx->length_aad = 0;
+        ctx->length_input = 0;
+        block128_zero(&ctx->tag);
 
+        block128_zero(&ctx->iv);
         if (len == 12) {
-                block128_copy_bytes(&gcm->iv, iv, 12);
-                gcm->iv.b[15] = 0x01;
+                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(&gcm->iv, (block128 *) iv);
-                        gf_mul(&gcm->iv, &gcm->h);
+                        block128_xor(&ctx->iv, (block128 *) iv);
+                        gf_mul(&ctx->iv, &gcm->h);
                 }
                 if (len > 0) {
-                        block128_xor_bytes(&gcm->iv, iv, len);
-                        gf_mul(&gcm->iv, &gcm->h);
+                        block128_xor_bytes(&ctx->iv, iv, len);
+                        gf_mul(&ctx->iv, &gcm->h);
                 }
                 for (i = 15; origlen; --i, origlen >>= 8)
-                        gcm->iv.b[i] ^= (uint8_t) origlen;
-                gf_mul(&gcm->iv, &gcm->h);
+                        ctx->iv.b[i] ^= (uint8_t) origlen;
+                gf_mul(&ctx->iv, &gcm->h);
         }
 
-        block128_copy(&gcm->civ, &gcm->iv);
+        block128_copy(&ctx->civ, &ctx->iv);
 }
 
-static void aes_gcm_aad(aes_gcm *gcm, uint8_t *input, uint32_t length)
+void aes_gcm_init(aes_gcm *gcm, const aes_key *key)
 {
-        gcm->length_aad += length;
+        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, (block128 *) input);
+                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, &tmp);
+                gcm_ghash_add(gcm, ctx, &tmp);
         }
 
 }
 
-static void aes_gcm_encrypt(uint8_t *output, aes_gcm *gcm, uint8_t *input, uint32_t length)
+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;
 
-        gcm->length_input += length;
+        ctx->length_input += length;
         for (; length >= 16; input += 16, output += 16, length -= 16) {
-                block128_inc_be(&gcm->civ);
+                block128_inc_be(&ctx->civ);
 
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->civ, 1);
+                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&ctx->civ, 1);
                 block128_xor(&out, (block128 *) input);
-                gcm_ghash_add(gcm, &out);
+                gcm_ghash_add(gcm, ctx, &out);
                 block128_copy((block128 *) output, &out);
         }
         if (length > 0) {
                 aes_block tmp;
                 int i;
 
-                block128_inc_be(&gcm->civ);
+                block128_inc_be(&ctx->civ);
                 /* create e(civ) in out */
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->civ, 1);
+                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, &tmp);
+                gcm_ghash_add(gcm, ctx, &tmp);
 
                 for (i = 0; i < length; i++) {
                         output[i] = tmp.b[i];
@@ -258,16 +300,16 @@
         }
 }
 
-static void aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, 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)
 {
         aes_block out;
 
-        gcm->length_input += length;
+        ctx->length_input += length;
         for (; length >= 16; input += 16, output += 16, length -= 16) {
-                block128_inc_be(&gcm->civ);
+                block128_inc_be(&ctx->civ);
 
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->civ, 1);
-                gcm_ghash_add(gcm, (block128 *) input);
+                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);
         }
@@ -275,13 +317,13 @@
                 aes_block tmp;
                 int i;
 
-                block128_inc_be(&gcm->civ);
+                block128_inc_be(&ctx->civ);
 
                 block128_zero(&tmp);
                 block128_copy_bytes(&tmp, input, length);
-                gcm_ghash_add(gcm, &tmp);
+                gcm_ghash_add(gcm, ctx, &tmp);
 
-                encrypt_ecb(&gcm->key, (uint8_t *)&out, (const uint8_t *)&gcm->civ, 1);
+                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++) {
@@ -290,21 +332,21 @@
         }
 }
 
-static void aes_gcm_finish(uint8_t *tag, aes_gcm *gcm)
+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(gcm->length_aad << 3);
-        lblock.q[1] = cpu_to_be64(gcm->length_input << 3);
-        gcm_ghash_add(gcm, &lblock);
+        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*)&gcm->iv, 1);
-        block128_xor(&gcm->tag, &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] = gcm->tag.b[i];
+                tag[i] = ctx->tag.b[i];
         }
 }
 
@@ -314,11 +356,13 @@
                          , uint8_t *pt, uint32_t ptLen
                          , uint8_t *ct, uint8_t *tag)
 {
+    aes_gcm_ctx ctx;
     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);
+    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
@@ -328,10 +372,12 @@
                          , 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);
+    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);
 }
 
 
@@ -340,7 +386,7 @@
  * to speed up the multiplication.
  * TODO: optimise with tables
  */
-static void gf_mul(block128 *a, block128 *b)
+static void gf_mul(block128 *a, const block128 *b)
 {
         uint64_t a0, a1, v0, v1;
         int i, j;
diff --git a/cbits/include/aes.h b/cbits/include/aes.h
--- a/cbits/include/aes.h
+++ b/cbits/include/aes.h
@@ -20,6 +20,25 @@
                          , 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) */
diff --git a/cbits/include/aes_types.h b/cbits/include/aes_types.h
--- a/cbits/include/aes_types.h
+++ b/cbits/include/aes_types.h
@@ -13,13 +13,16 @@
 
 /* size = 4*16+2*8+aes_key=456 = 536 */
 typedef struct {
-    aes_block tag;
-    aes_block h;
+    aes_block h;        // init
+    aes_key key;
+} aes_gcm;
+
+typedef struct {
     aes_block iv;
     aes_block civ;
-    uint64_t length_aad;
+    aes_block tag;      // finish, aad, encrypt,decrypt
     uint64_t length_input;
-    aes_key key;
-} aes_gcm;
+    uint64_t length_aad;
+} aes_gcm_ctx;
 
 #endif
diff --git a/cbits/include/block128.h b/cbits/include/block128.h
--- a/cbits/include/block128.h
+++ b/cbits/include/block128.h
@@ -40,7 +40,7 @@
        uint8_t  b[16];
 } block128;
 
-static inline void block128_copy_bytes(block128 *block, uint8_t *src, uint32_t len)
+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];
@@ -68,7 +68,7 @@
 	d->q[1] = s1->q[1] ^ s2->q[1];
 }
 
-static inline void block128_xor_bytes(block128 *block, uint8_t *src, uint32_t len)
+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];
diff --git a/cipher-aes128.cabal b/cipher-aes128.cabal
--- a/cipher-aes128.cabal
+++ b/cipher-aes128.cabal
@@ -2,9 +2,11 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                cipher-aes128
-version:             0.4.1
+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?)
+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)
