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
@@ -347,7 +347,7 @@
     c_gcm_decrypt pt g c k ct (fromIntegral ctlen)
 
 finishGCM :: GetExpanded k
-          => GCM k
+          => GCM k     -- GCM context (which is mutated!)
           -> Ptr Word8 -- Tag, must point to 16 byte buffer (or larger)
           -> IO ()
 finishGCM gcm tagPtr =
diff --git a/cbits/aes.c b/cbits/aes.c
--- a/cbits/aes.c
+++ b/cbits/aes.c
@@ -38,17 +38,17 @@
 #include "gf.h"
 #include "aes_x86ni.h"
 
-void tmd_aes_generic_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
-void tmd_aes_generic_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
-void tmd_aes_generic_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
-void tmd_aes_generic_decrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
-void tmd_aes_generic_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t length);
+void tmd_aes_generic_encrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_decrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_encrypt_cbc(aes_block *output, const aes_key *key, const aes_block *iv, aes_block *newIV, const aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_decrypt_cbc(aes_block *output, const aes_key *key, const aes_block *ivini, aes_block *newIV, const aes_block *input, uint32_t nb_blocks);
+void tmd_aes_generic_encrypt_ctr(uint8_t *output, const aes_key *key, const aes_block *iv, aes_block *newIV, const uint8_t *input, uint32_t length);
 void tmd_aes_generic_encrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
                              uint32_t spoint, aes_block *input, uint32_t nb_blocks);
 void tmd_aes_generic_decrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
                              uint32_t spoint, aes_block *input, uint32_t nb_blocks);
-void tmd_aes_generic_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
-void tmd_aes_generic_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
+void tmd_aes_generic_gcm_encrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX);
+void tmd_aes_generic_gcm_decrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX);
 
 enum {
         /* init */
@@ -118,13 +118,13 @@
         [DECRYPT_GCM_256]   = tmd_aes_generic_gcm_decrypt,
 };
 
-typedef void (*init_f)(aes_key *, uint8_t *, uint8_t);
-typedef void (*ecb_f)(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
-typedef void (*cbc_f)(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
-typedef void (*ctr_f)(uint8_t *output, aes_key *key, aes_block *iv, aes_block *niv, uint8_t *input, uint32_t length);
-typedef void (*xts_f)(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit, uint32_t spoint, aes_block *input, uint32_t nb_blocks);
-typedef void (*gcm_crypt_f)(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
-typedef void (*block_f)(aes_block *output, aes_key *key, aes_block *input);
+typedef void (*init_f)(const aes_key *, uint8_t *, uint8_t);
+typedef void (*ecb_f)(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks);
+typedef void (*cbc_f)(aes_block *output, const aes_key *key, const aes_block *iv, aes_block *niv, const aes_block *input, uint32_t nb_blocks);
+typedef void (*ctr_f)(uint8_t *output, const aes_key *key, const aes_block *iv, aes_block *niv, const uint8_t *input, uint32_t length);
+typedef void (*xts_f)(aes_block *output, const aes_key *k1, aes_key *k2, aes_block *dataunit, uint32_t spoint, aes_block *input, uint32_t nb_blocks);
+typedef void (*gcm_crypt_f)(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX);
+typedef void (*block_f)(aes_block *output, const aes_key *key, const aes_block *input);
 
 #ifdef WITH_AESNI
 #define GET_INIT(strength) \
@@ -197,6 +197,8 @@
         /* GCM */
         tmd_branch_table[ENCRYPT_GCM_128] = tmd_aes_ni_gcm_encrypt128;
         tmd_branch_table[ENCRYPT_GCM_256] = tmd_aes_ni_gcm_encrypt256;
+        // tmd_branch_table[DECRYPT_GCM_128] = tmd_aes_ni_gcm_decrypt128;
+        // tmd_branch_table[DECRYPT_GCM_256] = tmd_aes_ni_gcm_decrypt256;
 }
 #endif
 
@@ -247,31 +249,31 @@
         _init(key, origkey, size);
 }
 
-void tmd_aes_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_encrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks)
 {
         ecb_f e = GET_ECB_ENCRYPT(key->strength);
         e(output, key, input, nb_blocks);
 }
 
-void tmd_aes_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_decrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks)
 {
         ecb_f d = GET_ECB_DECRYPT(key->strength);
         d(output, key, input, nb_blocks);
 }
 
-void tmd_aes_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_encrypt_cbc(aes_block *output, const aes_key *key, const aes_block *iv, aes_block *niv, const aes_block *input, uint32_t nb_blocks)
 {
         cbc_f e = GET_CBC_ENCRYPT(key->strength);
-        e(output, key, iv, input, nb_blocks);
+        e(output, key, iv, niv, input, nb_blocks);
 }
 
-void tmd_aes_decrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_decrypt_cbc(aes_block *output, const aes_key *key, const aes_block *iv, aes_block *niv, const aes_block *input, uint32_t nb_blocks)
 {
         cbc_f d = GET_CBC_DECRYPT(key->strength);
-        d(output, key, iv, input, nb_blocks);
+        d(output, key, iv, niv, input, nb_blocks);
 }
 
-void tmd_aes_gen_ctr(aes_block *output, aes_key *key, aes_block *iv, uint32_t nb_blocks)
+void tmd_aes_gen_ctr(aes_block *output, const aes_key *key, aes_block *iv, uint32_t nb_blocks)
 {
         aes_block block;
 
@@ -283,7 +285,7 @@
         }
 }
 
-void tmd_aes_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t len)
+void tmd_aes_encrypt_ctr(uint8_t *output, const aes_key *key, const aes_block *iv, aes_block *newIV, const uint8_t *input, uint32_t len)
 {
         ctr_f e = GET_CTR_ENCRYPT(key->strength);
         e(output, key, iv, newIV, input, len);
@@ -302,32 +304,32 @@
         tmd_aes_generic_decrypt_xts(output, k1, k2, dataunit, spoint, input, nb_blocks);
 }
 
-void tmd_aes_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+void tmd_aes_gcm_encrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX)
 {
         gcm_crypt_f e = GET_GCM_ENCRYPT(key->strength);
-        e(output, gcm, ctx, key, input, length);
+        e(output, gcm, ctx, key, input, length, newCTX);
 }
 
-void tmd_aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+void tmd_aes_gcm_decrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX)
 {
         gcm_crypt_f d = GET_GCM_DECRYPT(key->strength);
-        d(output, gcm, ctx, key, input, length);
+        d(output, gcm, ctx, key, input, length, newCTX);
 }
 
-static void gcm_ghash_add(aes_gcm *gcm, aes_ctx *ctx, block128 *b)
+static void gcm_ghash_add(const aes_gcm *gcm, aes_ctx *ctx, const block128 *b)
 {
         block128_xor(&ctx->tag, b);
         gf_mul(&ctx->tag, &gcm->h);
 }
 
-void tmd_aes_gcm_init(aes_gcm *gcm, aes_key *key)
+void tmd_aes_gcm_init(aes_gcm *gcm, const aes_key *key)
 {
         block128_zero(&gcm->h);
         /* prepare H : encrypt_K(0^128) */
         aes_encrypt_block(&gcm->h, key, &gcm->h);
 }
 
-void tmd_aes_ctx_init(const aes_gcm *gcm, aes_ctx *ctx, const aes_key *key, uint8_t *iv, uint32_t len)
+void tmd_aes_ctx_init(const aes_gcm *gcm, aes_ctx *ctx, const aes_key *key, const uint8_t *iv, uint32_t len)
 {
         ctx->length_aad = 0;
         ctx->length_input = 0;
@@ -357,7 +359,7 @@
         block128_copy(&ctx->civ, &ctx->iv);
 }
 
-void tmd_aes_gcm_aad(aes_gcm *gcm, aes_ctx *ctx, uint8_t *input, uint32_t length)
+void tmd_aes_gcm_aad(const aes_gcm *gcm, aes_ctx *ctx, const uint8_t *input, uint32_t length)
 {
         ctx->length_aad += length;
         for (; length >= 16; input += 16, length -= 16) {
@@ -372,7 +374,7 @@
 
 }
 
-void tmd_aes_gcm_finish(uint8_t *tag, aes_gcm *gcm, aes_key *key, aes_ctx *ctx)
+void tmd_aes_gcm_finish(uint8_t *tag, const aes_gcm *gcm, const aes_key *key, aes_ctx *ctx)
 {
         aes_block lblock;
         int i;
@@ -390,75 +392,74 @@
         }
 }
 
-void tmd_aes_gcm_full_encrypt(aes_key *key, aes_gcm *gcm
-                             , uint8_t *iv, uint32_t ivLen
-                             , uint8_t *aad, uint32_t aadLen
-                             , uint8_t *pt, uint32_t ptLen
+void tmd_aes_gcm_full_encrypt(const aes_key *key, const aes_gcm *gcm
+                             , const uint8_t *iv, uint32_t ivLen
+                             , const uint8_t *aad, uint32_t aadLen
+                             , const uint8_t *pt, uint32_t ptLen
                              , uint8_t *ct, uint8_t *tag)
 {
-    aes_ctx ctx;
+    aes_ctx ctx, newCTX;
     tmd_aes_ctx_init(gcm, &ctx, key, iv, ivLen);
-    tmd_aes_gcm_encrypt(ct, gcm, &ctx, key, pt, ptLen);
-    tmd_aes_gcm_aad(gcm, &ctx, aad, aadLen);
-    tmd_aes_gcm_finish(tag, gcm, key, &ctx);
+    tmd_aes_gcm_encrypt(ct, gcm, &ctx, key, pt, ptLen, &newCTX);
+    tmd_aes_gcm_aad(gcm, &newCTX, aad, aadLen);
+    tmd_aes_gcm_finish(tag, gcm, key, &newCTX);
 }
 
-void tmd_aes_gcm_full_decrypt(aes_key *key, aes_gcm *gcm
-                             , uint8_t *iv, uint32_t ivLen
-                             , uint8_t *aad, uint32_t aadLen
-                             , uint8_t *ct, uint32_t ctLen
+void tmd_aes_gcm_full_decrypt( const aes_key *key, const aes_gcm *gcm
+                             , const uint8_t *iv, uint32_t ivLen
+                             , const uint8_t *aad, uint32_t aadLen
+                             , const uint8_t *ct, uint32_t ctLen
                              , uint8_t *pt, uint8_t *tag)
 {
-    aes_ctx ctx;
+    aes_ctx ctx, newCTX;
     tmd_aes_ctx_init(gcm, &ctx, key, iv, ivLen);
-    tmd_aes_gcm_decrypt(pt, gcm, &ctx, key, ct, ctLen);
-    tmd_aes_gcm_aad(gcm, &ctx, aad, aadLen);
-    tmd_aes_gcm_finish(tag, gcm, key, &ctx);
+    tmd_aes_gcm_decrypt(pt, gcm, &ctx, key, ct, ctLen, &newCTX);
+    tmd_aes_gcm_aad(gcm, &newCTX, aad, aadLen);
+    tmd_aes_gcm_finish(tag, gcm, key, &newCTX);
 }
 
-void tmd_aes_generic_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_generic_encrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks)
 {
         for ( ; nb_blocks-- > 0; input++, output++) {
                 tmd_aes_generic_encrypt_block(output, key, input);
         }
 }
 
-void tmd_aes_generic_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_generic_decrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks)
 {
         for ( ; nb_blocks-- > 0; input++, output++) {
                 tmd_aes_generic_decrypt_block(output, key, input);
         }
 }
 
-void tmd_aes_generic_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_generic_encrypt_cbc(aes_block *output, const aes_key *key, const aes_block *iv, aes_block *newIV, const aes_block *input, uint32_t nb_blocks)
 {
-        aes_block block;
-
         /* preload IV in block */
-        block128_copy(&block, iv);
+        block128_copy(newIV, iv);
         for ( ; nb_blocks-- > 0; input++, output++) {
-                block128_xor(&block, (block128 *) input);
-                tmd_aes_generic_encrypt_block(&block, key, &block);
-                block128_copy((block128 *) output, &block);
+                block128_xor(newIV, (block128 *) input);
+                tmd_aes_generic_encrypt_block(newIV, key, newIV);
+                block128_copy((block128 *) output, newIV);
         }
 }
 
-void tmd_aes_generic_decrypt_cbc(aes_block *output, aes_key *key, aes_block *ivini, aes_block *input, uint32_t nb_blocks)
+void tmd_aes_generic_decrypt_cbc(aes_block *output, const aes_key *key, const aes_block *ivini, aes_block *newIV, const aes_block *input, uint32_t nb_blocks)
 {
         aes_block block, blocko;
-        aes_block iv;
+        aes_block *iv;
+        iv = newIV;
 
         /* preload IV in block */
-        block128_copy(&iv, ivini);
+        block128_copy(iv, ivini);
         for ( ; nb_blocks-- > 0; input++, output++) {
                 block128_copy(&block, (block128 *) input);
                 tmd_aes_generic_decrypt_block(&blocko, key, &block);
-                block128_vxor((block128 *) output, &blocko, &iv);
-                block128_copy(&iv, &block);
+                block128_vxor((block128 *) output, &blocko, iv);
+                block128_copy(iv, &block);
         }
 }
 
-void tmd_aes_generic_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t len)
+void tmd_aes_generic_encrypt_ctr(uint8_t *output, const aes_key *key, const aes_block *iv, aes_block *newIV, const uint8_t *input, uint32_t len)
 {
         aes_block block, o;
         uint32_t nb_blocks = len / 16;
@@ -524,32 +525,33 @@
         }
 }
 
-void tmd_aes_generic_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+void tmd_aes_generic_gcm_encrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX)
 {
         aes_block out;
+        memcpy(newCTX, ctx, sizeof(aes_ctx));
 
-        ctx->length_input += length;
+        newCTX->length_input += length;
         for (; length >= 16; input += 16, output += 16, length -= 16) {
-                block128_inc_be(&ctx->civ);
+                block128_inc_be(&newCTX->civ);
 
-                aes_encrypt_block(&out, key, &ctx->civ);
+                aes_encrypt_block(&out, key, &newCTX->civ);
                 block128_xor(&out, (block128 *) input);
-                gcm_ghash_add(gcm, ctx, &out);
+                gcm_ghash_add(gcm, newCTX, &out);
                 block128_copy((block128 *) output, &out);
         }
         if (length > 0) {
                 aes_block tmp;
                 int i;
 
-                block128_inc_be(&ctx->civ);
+                block128_inc_be(&newCTX->civ);
                 /* create e(civ) in out */
-                aes_encrypt_block(&out, key, &ctx->civ);
+                aes_encrypt_block(&out, key, &newCTX->civ);
                 /* initialize a tmp as input and xor it to e(civ) */
                 block128_zero(&tmp);
                 block128_copy_bytes(&tmp, input, length);
                 block128_xor_bytes(&tmp, out.b, length);
 
-                gcm_ghash_add(gcm, ctx, &tmp);
+                gcm_ghash_add(gcm, newCTX, &tmp);
 
                 for (i = 0; i < length; i++) {
                         output[i] = tmp.b[i];
@@ -557,16 +559,17 @@
         }
 }
 
-void tmd_aes_generic_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length)
+void tmd_aes_generic_gcm_decrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX)
 {
         aes_block out;
 
-        ctx->length_input += length;
+        memcpy(newCTX, ctx, sizeof(aes_ctx));
+        newCTX->length_input += length;
         for (; length >= 16; input += 16, output += 16, length -= 16) {
-                block128_inc_be(&ctx->civ);
+                block128_inc_be(&newCTX->civ);
 
-                aes_encrypt_block(&out, key, &ctx->civ);
-                gcm_ghash_add(gcm, ctx, (block128 *) input);
+                aes_encrypt_block(&out, key, &newCTX->civ);
+                gcm_ghash_add(gcm, newCTX, (block128 *) input);
                 block128_xor(&out, (block128 *) input);
                 block128_copy((block128 *) output, &out);
         }
@@ -574,13 +577,13 @@
                 aes_block tmp;
                 int i;
 
-                block128_inc_be(&ctx->civ);
+                block128_inc_be(&newCTX->civ);
 
                 block128_zero(&tmp);
                 block128_copy_bytes(&tmp, input, length);
-                gcm_ghash_add(gcm, ctx, &tmp);
+                gcm_ghash_add(gcm, newCTX, &tmp);
 
-                aes_encrypt_block(&out, key, &ctx->civ);
+                aes_encrypt_block(&out, key, &newCTX->civ);
                 block128_xor_bytes(&tmp, out.b, length);
 
                 for (i = 0; i < length; i++) {
diff --git a/cbits/aes.h b/cbits/aes.h
--- a/cbits/aes.h
+++ b/cbits/aes.h
@@ -68,28 +68,37 @@
 /* in bytes: either 16,24,32 */
 void tmd_aes_initkey(aes_key *ctx, uint8_t *key, uint8_t size);
 
-void tmd_aes_encrypt(aes_block *output, aes_key *key, aes_block *input);
-void tmd_aes_decrypt(aes_block *output, aes_key *key, aes_block *input);
+void tmd_aes_encrypt(aes_block *output, const aes_key *key, const aes_block *input);
+void tmd_aes_decrypt(aes_block *output, const aes_key *key, const aes_block *input);
 
-void tmd_aes_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
-void tmd_aes_decrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_encrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks);
+void tmd_aes_decrypt_ecb(aes_block *output, const aes_key *key, const aes_block *input, uint32_t nb_blocks);
 
-void tmd_aes_encrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
-void tmd_aes_decrypt_cbc(aes_block *output, aes_key *key, aes_block *iv, aes_block *input, uint32_t nb_blocks);
+void tmd_aes_encrypt_cbc(aes_block *output, const aes_key *key, const aes_block *iv, aes_block *niv, const aes_block *input, uint32_t nb_blocks);
+void tmd_aes_decrypt_cbc(aes_block *output, const aes_key *key, const aes_block *iv, aes_block *niv, const aes_block *input, uint32_t nb_blocks);
 
-void tmd_aes_encrypt_ctr(uint8_t *output, aes_key *key, aes_block *iv, aes_block *newIV, uint8_t *input, uint32_t len);
-void tmd_aes_gen_ctr(aes_block *output, aes_key *key, aes_block *iv, uint32_t nb_blocks);
+void tmd_aes_encrypt_ctr(uint8_t *output, const aes_key *key, const aes_block *iv, aes_block *newIV, const uint8_t *input, uint32_t len);
+void tmd_aes_gen_ctr(aes_block *output, const aes_key *key, aes_block *iv, uint32_t nb_blocks);
 
 void tmd_aes_encrypt_xts(aes_block *output, aes_key *key, aes_key *key2, aes_block *sector,
                      uint32_t spoint, aes_block *input, uint32_t nb_blocks);
 void tmd_aes_decrypt_xts(aes_block *output, aes_key *key, aes_key *key2, aes_block *sector,
                      uint32_t spoint, aes_block *input, uint32_t nb_blocks);
 
-void tmd_aes_gcm_init(aes_gcm *gcm, aes_key *key);
-void tmd_aes_ctx_init(const aes_gcm *gcm, aes_ctx *ctx, const aes_key *key, uint8_t *iv, uint32_t len);
-void tmd_aes_gcm_aad(aes_gcm *gcm, aes_ctx *ctx, uint8_t *input, uint32_t length);
-void tmd_aes_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
-void tmd_aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length);
-void tmd_aes_gcm_finish(uint8_t *tag, aes_gcm *gcm, aes_key *key, aes_ctx *ctx);
-
+void tmd_aes_gcm_init(aes_gcm *gcm, const aes_key *key);
+void tmd_aes_ctx_init(const aes_gcm *gcm, aes_ctx *ctx, const aes_key *key, const uint8_t *iv, uint32_t len);
+void tmd_aes_gcm_aad(const aes_gcm *gcm, aes_ctx *ctx, const uint8_t *input, uint32_t length);
+void tmd_aes_gcm_encrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX);
+void tmd_aes_gcm_decrypt(uint8_t *output, const aes_gcm *gcm, const aes_ctx *ctx, const aes_key *key, const uint8_t *input, uint32_t length, aes_ctx *newCTX);
+void tmd_aes_gcm_finish(uint8_t *tag, const aes_gcm *gcm, const aes_key *key, aes_ctx *ctx);
+void tmd_aes_gcm_full_encrypt( const aes_key *key, const aes_gcm *gcm
+                             , const uint8_t *iv, uint32_t ivLen
+                             , const uint8_t *aad, uint32_t aadLen
+                             , const uint8_t *pt, uint32_t ptLen
+                             , uint8_t *ct, uint8_t *tag);
+void tmd_aes_gcm_full_decrypt( const aes_key *key, const aes_gcm *gcm
+                             , const uint8_t *iv, uint32_t ivLen
+                             , const uint8_t *aad, uint32_t aadLen
+                             , const uint8_t *ct, uint32_t ctLen
+                             , uint8_t *pt, uint8_t *tag);
 #endif
diff --git a/cbits/aes_generic.c b/cbits/aes_generic.c
--- a/cbits/aes_generic.c
+++ b/cbits/aes_generic.c
@@ -222,7 +222,7 @@
 };
 #undef G
 
-static void expand_key(uint8_t *expandedKey, uint8_t *key, int size, size_t expandedKeySize)
+static void expand_key(uint8_t *expandedKey, const uint8_t *key, int size, size_t expandedKeySize)
 {
 	int csz;
 	int i;
@@ -313,7 +313,7 @@
 	}
 }
 
-static void create_round_key(uint8_t *expandedKey, uint8_t *rk)
+static void create_round_key(const uint8_t *expandedKey, uint8_t *rk)
 {
 	int i,j;
 	for (i = 0; i < 4; i++)
@@ -321,7 +321,7 @@
 			rk[i + j * 4] = expandedKey[i * 4 + j];
 }
 
-static void aes_main(aes_key *key, uint8_t *state)
+static void aes_main(const aes_key *key, uint8_t *state)
 {
 	int i = 0;
 	uint8_t rk[16];
@@ -371,7 +371,7 @@
 	}
 }
 
-static void aes_main_inv(aes_key *key, uint8_t *state)
+static void aes_main_inv(const aes_key *key, uint8_t *state)
 {
 	int i = 0;
 	uint8_t rk[16];
@@ -403,7 +403,7 @@
 	t[2] = f[8]; t[6] = f[9]; t[10] = f[10]; t[14] = f[11]; \
 	t[3] = f[12]; t[7] = f[13]; t[11] = f[14]; t[15] = f[15]
 
-void tmd_aes_generic_encrypt_block(aes_block *output, aes_key *key, aes_block *input)
+void tmd_aes_generic_encrypt_block(aes_block *output, const aes_key *key, const aes_block *input)
 {
 	uint8_t block[16];
 	uint8_t *iptr, *optr;
@@ -415,7 +415,7 @@
 	swap_block(optr, block);
 }
 
-void tmd_aes_generic_decrypt_block(aes_block *output, aes_key *key, aes_block *input)
+void tmd_aes_generic_decrypt_block(aes_block *output, const aes_key *key, const aes_block *input)
 {
 	uint8_t block[16];
 	uint8_t *iptr, *optr;
@@ -427,7 +427,7 @@
 	swap_block(optr, block);
 }
 
-void tmd_aes_generic_init(aes_key *key, uint8_t *origkey, uint8_t size)
+void tmd_aes_generic_init(aes_key *key, const uint8_t *origkey, uint8_t size)
 {
 	int esz;
 
diff --git a/cbits/aes_generic.h b/cbits/aes_generic.h
--- a/cbits/aes_generic.h
+++ b/cbits/aes_generic.h
@@ -29,6 +29,6 @@
  */
 #include "aes.h"
 
-void tmd_aes_generic_encrypt_block(aes_block *output, aes_key *key, aes_block *input);
-void tmd_aes_generic_decrypt_block(aes_block *output, aes_key *key, aes_block *input);
-void tmd_aes_generic_init(aes_key *key, uint8_t *origkey, uint8_t size);
+void tmd_aes_generic_encrypt_block(aes_block *output, const aes_key *key, const aes_block *input);
+void tmd_aes_generic_decrypt_block(aes_block *output, const aes_key *key, const aes_block *input);
+void tmd_aes_generic_init(aes_key *key, const uint8_t *origkey, uint8_t size);
diff --git a/cbits/aes_x86ni_impl.c b/cbits/aes_x86ni_impl.c
--- a/cbits/aes_x86ni_impl.c
+++ b/cbits/aes_x86ni_impl.c
@@ -186,17 +186,18 @@
 
 void SIZED(tmd_aes_ni_gcm_encrypt)(uint8_t *output, aes_gcm *gcm, aes_ctx *ctx, aes_key *key, uint8_t *input, uint32_t length, aes_ctx *newCTX)
 {
+        memcpy(newCTX, ctx, sizeof(aes_ctx));
         __m128i *k = (__m128i *) key->data;
         __m128i bswap_mask = _mm_setr_epi8(7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8);
         __m128i one        = _mm_set_epi32(0,1,0,0);
         uint32_t nb_blocks = length / 16;
         uint32_t part_block_len = length % 16;
 
-        ctx->length_input += length;
+        newCTX->length_input += length;
 
         __m128i h  = _mm_loadu_si128((__m128i *) &gcm->h);
-        __m128i tag = _mm_loadu_si128((__m128i *) &ctx->tag);
-        __m128i iv = _mm_loadu_si128((__m128i *) &ctx->civ);
+        __m128i tag = _mm_loadu_si128((__m128i *) &newCTX->tag);
+        __m128i iv = _mm_loadu_si128((__m128i *) &newCTX->civ);
         iv = _mm_shuffle_epi8(iv, bswap_mask);
 
         PRELOAD_ENC(k);
diff --git a/cbits/block128.h b/cbits/block128.h
--- a/cbits/block128.h
+++ b/cbits/block128.h
@@ -40,13 +40,13 @@
        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];
 }
 
-static inline void block128_copy(block128 *d, block128 *s)
+static inline void block128_copy(block128 *d, const block128 *s)
 {
 	d->q[0] = s->q[0]; d->q[1] = s->q[1];
 }
@@ -56,7 +56,7 @@
 	d->q[0] = 0; d->q[1] = 0;
 }
 
-static inline void block128_xor(block128 *d, block128 *s)
+static inline void block128_xor(block128 *d, const block128 *s)
 {
 	d->q[0] ^= s->q[0];
 	d->q[1] ^= s->q[1];
@@ -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,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                cipher-aes128
-version:             0.6.1
+version:             0.6.2
 synopsis:            AES and common modes using AES-NI when available.
 description:         Cipher-aes128 is an implementation of AES and common modes of operation.  It borrows Hanquez's C AES code (see 'cipher-aes') but
                      is unique due to including compile-time detection of
@@ -43,6 +43,7 @@
                      , ./cbits/cpu.c
                      , ./cbits/gf.c
   include-dirs:        cbits/
+  cc-options: -g
   -- None by default cc-options:          -mssse3 -maes -mpclmul
 
 source-repository head
