diff --git a/Crypto/Cipher/AES128.hs b/Crypto/Cipher/AES128.hs
--- a/Crypto/Cipher/AES128.hs
+++ b/Crypto/Cipher/AES128.hs
@@ -1,17 +1,27 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE RecordWildCards #-}
 module Crypto.Cipher.AES128
-  ( AESKey128, AESKey192, AESKey256
+  ( -- * Key types with crypto-api instances
+    AESKey128, AESKey192, AESKey256
   , BlockCipher(..), buildKeyIO, zeroIV
+    -- * GCM Operations
+  , makeGCMCtx, aesKeyToGCM, GCMCtx, AuthTag(..), AES_GCM
+  , Crypto.Cipher.AES128.encryptGCM
+  , Crypto.Cipher.AES128.decryptGCM
   ) where
 
-import Crypto.Cipher.AES128.Internal
+import Crypto.Cipher.AES128.Internal as I
 import Crypto.Classes
+import Data.Function (on)
 import Control.Monad (when)
 import Data.Serialize
 import Data.Tagged
+import Data.Word (Word8)
 import Foreign.Ptr
 import Foreign.ForeignPtr
+import Foreign.Marshal.Alloc as F
 import System.IO.Unsafe
+import Data.ByteString (ByteString)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Internal as B
 import qualified Data.ByteString.Unsafe as B
@@ -147,3 +157,128 @@
             return (ct,IV newIV)
     {-# INLINE ctr #-}
     unCtr = ctr
+
+-- GCM Routines
+maxTagLen :: Int
+maxTagLen = 16
+
+data AuthTag = AuthTag { unAuthTag :: ByteString }
+
+-- | A tuple of key and precomputed data for use by GCM
+data GCMCtx k = GCMCtx { gcmkey :: k
+                       , gcmpc  :: GCMpc
+                       }
+
+instance Eq AuthTag where
+    (==)  = constTimeEq `on` unAuthTag
+
+-- A super-class indicating which keys can be used with GCMCtx.
+class (BlockCipher k, GetExpanded k) => AES_GCM k where
+instance AES_GCM AESKey128
+instance AES_GCM AESKey192
+instance AES_GCM AESKey256
+
+-- | Given key material produce a context useful for GCM operations
+makeGCMCtx :: AES_GCM k => ByteString -> Maybe (GCMCtx k)
+makeGCMCtx = fmap aesKeyToGCM . buildKey
+
+-- | Given an AESKey produce a GCM Context.
+aesKeyToGCM :: AES_GCM k => k -> GCMCtx k
+aesKeyToGCM k = GCMCtx k (I.precomputeGCMdata k)
+
+-- |Encrypts multiple-of-block-sized input, returning a bytestring and tag.
+encryptGCM :: AES_GCM k
+           => GCMCtx k
+           -> ByteString -- ^ IV
+           -> ByteString -- ^ Plaintext
+           -> ByteString -- ^ AAD
+           -> (ByteString, AuthTag)
+encryptGCM key iv pt aad = unsafePerformIO $ do
+ B.unsafeUseAsCString pt  $ \ptPtr  -> do
+  B.unsafeUseAsCString iv  $ \ivPtr  -> do
+   B.unsafeUseAsCString aad $ \aadPtr -> do
+    ctPtr  <- F.mallocBytes (B.length pt)
+    tagPtr <- F.mallocBytes maxTagLen
+    encryptGCMPtr key
+                  (castPtr ivPtr) (B.length iv)
+                  (castPtr ptPtr) (B.length pt)
+                  (castPtr aadPtr) (B.length aad)
+                  (castPtr ctPtr)
+                  (castPtr tagPtr)
+    ctBS  <- B.unsafePackMallocCStringLen (castPtr ctPtr, B.length pt)
+    tagBS <- B.unsafePackMallocCStringLen (castPtr tagPtr, maxTagLen)
+    return (ctBS, AuthTag tagBS)
+
+-- Encrypts multiple-of-block-sized input, filling a pointer with the
+-- result of [ctr, ct, tag].
+encryptGCMPtr :: AES_GCM k
+           => GCMCtx k
+           -> Ptr Word8 -- ^ IV
+           -> Int       -- ^ IV Length
+           -> Ptr Word8 -- ^ Plaintext buffer
+           -> Int       -- ^ Plaintext length
+           -> Ptr Word8 -- ^ AAD buffer
+           -> Int       -- ^ AAD Length
+           -> Ptr Word8 -- ^ ciphertext buffer (at least encBytes large)
+           -> Ptr Word8 -- ^ Tag buffer (always allocated to max length)
+           -> IO ()
+encryptGCMPtr (GCMCtx {..}) ivPtr ivLen
+                             ptPtr ptLen
+                             aadPtr aadLen
+                             ctPtr
+                             tagPtr =
+ do I.encryptGCM gcmkey gcmpc
+                   (castPtr ivPtr)  (fromIntegral ivLen)
+                   (castPtr aadPtr) (fromIntegral aadLen)
+                   (castPtr ptPtr)  (fromIntegral ptLen)
+                   (castPtr ctPtr)
+                   (castPtr tagPtr)
+
+-- | Decrypts multiple-of-block-sized input, returing a bytestring of the
+-- [ctr, ct, tag].
+decryptGCM :: AES_GCM k
+           => GCMCtx k
+           -> ByteString -- ^ IV
+           -> ByteString -- ^ Ciphertext
+           -> ByteString -- ^ AAD
+           -> (ByteString, AuthTag)
+           -- ^ Plaintext and incremented context (or an error)
+decryptGCM gcmdata iv ct aad = unsafePerformIO $ do
+ let ivLen     = B.length iv
+     tagLen    = maxTagLen
+     ctLen     = B.length ct
+ B.unsafeUseAsCString iv  $ \ivPtr  -> do
+  B.unsafeUseAsCString ct  $ \ctPtr  -> do
+   B.unsafeUseAsCString aad $ \aadPtr -> do
+    tagPtr     <- F.mallocBytes tagLen
+    ptPtr      <- F.mallocBytes ctLen
+    decryptGCM_ptr gcmdata
+                   (castPtr ivPtr)   ivLen
+                   (castPtr ctPtr)   ctLen
+                   (castPtr aadPtr) (B.length aad)
+                   (castPtr ptPtr)
+                   (castPtr tagPtr)
+    tagBS      <- B.unsafePackMallocCStringLen (castPtr tagPtr,tagLen)
+    ptBS       <- B.unsafePackMallocCStringLen (castPtr ptPtr, ctLen)
+    return (ptBS, AuthTag tagBS)
+
+decryptGCM_ptr :: AES_GCM k
+               => GCMCtx k
+               -> Ptr Word8 -> Int -- IV
+               -> Ptr Word8 -> Int -- CT
+               -> Ptr Word8 -> Int -- AAD
+               -> Ptr Word8        -- Plaintext
+               -> Ptr Word8        -- Tag
+               -> IO ()
+decryptGCM_ptr (GCMCtx {..})
+               ivPtr ivLen
+               ctPtr ctLen
+               aadPtr aadLen
+               ptPtr
+               tagPtr =
+    I.decryptGCM gcmkey gcmpc
+                   ivPtr  (fromIntegral ivLen)
+                   aadPtr (fromIntegral aadLen)
+                   ctPtr  (fromIntegral ctLen)
+                   ptPtr
+                   tagPtr
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
@@ -12,6 +12,8 @@
         , cipherOnlyGCM
         , decipherOnlyGCM
         , finishGCM, aadGCM
+        -- * Internal, will not be exported in a near-future release.
+        , GetExpanded
         ) where
 
 import Foreign.Ptr
@@ -20,6 +22,7 @@
 import Foreign.Marshal.Alloc
 import Data.Word
 import Data.Bits (shiftL, (.|.))
+import System.IO.Unsafe
 
 -- AES Bindings
 data AESKeyStruct
@@ -49,6 +52,9 @@
 
 type AESGcmPtr = Ptr GCMStruct
 data GCMStruct
+
+-- Store the key, the precomputed GCM data, and the current IV by way of
+-- a foreign pointer
 data GCM k = GCM { _gcmFP   :: GCMpc
                  , _keyFP   :: k
                  , _ctxFP2  :: ForeignPtr CTXStruct
@@ -283,7 +289,7 @@
             => k
             -> IO (GCM k)
 generateGCM keyStruct = do
-    gcmPC <- precomputeGCMdata keyStruct
+    let gcmPC = precomputeGCMdata keyStruct
     withForeignPtr (expandedKey keyStruct) $ \k -> do
       c <- c_allocate_ctx
       allocaBytes 12 $ \ivPtr -> withGCMpc gcmPC $ \g -> do
@@ -293,8 +299,8 @@
       return (GCM gcmPC keyStruct cFP)
 {-# INLINE generateGCM #-}
 
-precomputeGCMdata :: GetExpanded k => k -> IO GCMpc
-precomputeGCMdata k = do
+precomputeGCMdata :: GetExpanded k => k -> GCMpc
+precomputeGCMdata k = unsafePerformIO $ do
     withForeignPtr (expandedKey k) $ \kp -> do
         g <- c_allocate_gcm
         c_gcm_init g kp
@@ -381,13 +387,13 @@
     c_decrypt_ctr pt p iv niv ct (fromIntegral len)
 
 encryptGCM :: GetExpanded k
-           => k
-           -> GCMpc
-           -> Ptr Word8 -> Word32 -- IV
-           -> Ptr Word8 -> Word32 -- AAD
-           -> Ptr Word8 -> Word32 -- PT
-           -> Ptr Word8 -- CT
-           -> Ptr Word8 -- Tag
+           => k                   -- AES{128,192,256}
+           -> GCMpc               -- Precomputed GCM Data
+           -> Ptr Word8 -> Word32 -- IV, len
+           -> Ptr Word8 -> Word32 -- AAD, len
+           -> Ptr Word8 -> Word32 -- PT, len
+           -> Ptr Word8 -- CT  (out)
+           -> Ptr Word8 -- Tag (128 bits out)
            -> IO ()
 encryptGCM (expandedKey -> k) (GCMpc g) iv ivLen aad aadLen pt ptLen ct tag =
     withForeignPtr k $ \kp ->
@@ -397,11 +403,11 @@
 decryptGCM :: GetExpanded k
            => k
            -> GCMpc
-           -> Ptr Word8 -> Word32 -- IV
-           -> Ptr Word8 -> Word32 -- AAD
-           -> Ptr Word8 -> Word32 -- CT
-           -> Ptr Word8 -- PT
-           -> Ptr Word8 -- Tag
+           -> Ptr Word8 -> Word32 -- IV, len
+           -> Ptr Word8 -> Word32 -- AAD, len
+           -> Ptr Word8 -> Word32 -- CT, len
+           -> Ptr Word8           -- PT (out)
+           -> Ptr Word8           -- Tag (out)
            -> IO ()
 decryptGCM (expandedKey -> k) (GCMpc g) iv ivLen aad aadLen ct ctLen pt tag =
     withForeignPtr k $ \kp ->
diff --git a/cbits/aes.c b/cbits/aes.c
--- a/cbits/aes.c
+++ b/cbits/aes.c
@@ -34,6 +34,7 @@
 #include "bitfn.h"
 #include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #include "gf.h"
 #include "aes_x86ni.h"
@@ -43,9 +44,9 @@
 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,
+void tmd_aes_generic_encrypt_xts(aes_block *output, const 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,
+void tmd_aes_generic_decrypt_xts(aes_block *output, const 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, 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);
@@ -118,7 +119,7 @@
         [DECRYPT_GCM_256]   = tmd_aes_generic_gcm_decrypt,
 };
 
-typedef void (*init_f)(const aes_key *, uint8_t *, uint8_t);
+typedef void (*init_f)(aes_key *, const 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);
@@ -152,7 +153,7 @@
 #define aes_decrypt_block(o,k,i) \
         (((block_f) (tmd_branch_table[DECRYPT_BLOCK_128 + k->strength]))(o,k,i))
 #else
-#define GET_INIT(strenght) tmd_aes_generic_init
+#define GET_INIT(strength) tmd_aes_generic_init
 #define GET_ECB_ENCRYPT(strength) tmd_aes_generic_encrypt_ecb
 #define GET_ECB_DECRYPT(strength) tmd_aes_generic_decrypt_ecb
 #define GET_CBC_ENCRYPT(strength) tmd_aes_generic_encrypt_cbc
@@ -291,14 +292,14 @@
         e(output, key, iv, newIV, input, len);
 }
 
-void tmd_aes_encrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+void tmd_aes_encrypt_xts(aes_block *output, const aes_key *k1, aes_key *k2, aes_block *dataunit,
                      uint32_t spoint, aes_block *input, uint32_t nb_blocks)
 {
         xts_f e = GET_XTS_ENCRYPT(k1->strength);
         e(output, k1, k2, dataunit, spoint, input, nb_blocks);
 }
 
-void tmd_aes_decrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+void tmd_aes_decrypt_xts(aes_block *output, const aes_key *k1, aes_key *k2, aes_block *dataunit,
                      uint32_t spoint, aes_block *input, uint32_t nb_blocks)
 {
         tmd_aes_generic_decrypt_xts(output, k1, k2, dataunit, spoint, input, nb_blocks);
@@ -329,7 +330,8 @@
         aes_encrypt_block(&gcm->h, key, &gcm->h);
 }
 
-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_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;
@@ -485,7 +487,7 @@
             block128_copy(newIV, &block);
 }
 
-void tmd_aes_generic_encrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+void tmd_aes_generic_encrypt_xts(aes_block *output, const aes_key *k1, aes_key *k2, aes_block *dataunit,
                              uint32_t spoint, aes_block *input, uint32_t nb_blocks)
 {
         aes_block block, tweak;
@@ -505,7 +507,7 @@
         }
 }
 
-void tmd_aes_generic_decrypt_xts(aes_block *output, aes_key *k1, aes_key *k2, aes_block *dataunit,
+void tmd_aes_generic_decrypt_xts(aes_block *output, const aes_key *k1, aes_key *k2, aes_block *dataunit,
                              uint32_t spoint, aes_block *input, uint32_t nb_blocks)
 {
         aes_block block, tweak;
diff --git a/cbits/aes.h b/cbits/aes.h
--- a/cbits/aes.h
+++ b/cbits/aes.h
@@ -80,9 +80,9 @@
 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,
+void tmd_aes_encrypt_xts(aes_block *output, const 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,
+void tmd_aes_decrypt_xts(aes_block *output, const 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, const aes_key *key);
diff --git a/cbits/aes_x86ni.c b/cbits/aes_x86ni.c
--- a/cbits/aes_x86ni.c
+++ b/cbits/aes_x86ni.c
@@ -63,7 +63,7 @@
 	return _mm_xor_si128(key, keygened);
 }
 
-void tmd_aes_ni_init(aes_key *key, uint8_t *ikey, uint8_t size)
+void tmd_aes_ni_init(aes_key *key, const uint8_t *ikey, uint8_t size)
 {
 	__m128i k[28];
 	uint64_t *out = (uint64_t *) key->data;
diff --git a/cbits/aes_x86ni.h b/cbits/aes_x86ni.h
--- a/cbits/aes_x86ni.h
+++ b/cbits/aes_x86ni.h
@@ -49,7 +49,7 @@
 }
 #endif
 
-void tmd_aes_ni_init(aes_key *key, uint8_t *origkey, uint8_t size);
+void tmd_aes_ni_init(aes_key *key, const uint8_t *origkey, uint8_t size);
 void tmd_aes_ni_encrypt_block128(aes_block *out, aes_key *key, aes_block *in);
 void tmd_aes_ni_encrypt_block256(aes_block *out, aes_key *key, aes_block *in);
 void tmd_aes_ni_decrypt_block128(aes_block *out, aes_key *key, aes_block *in);
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.4
+version:             0.7
 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
@@ -18,6 +18,8 @@
 homepage:            https://github.com/TomMD/cipher-aes128
 bug-reports:         https://github.com/TomMD/cipher-aes128/issues
 build-type:          Custom
+-- build-type:          Simple
+-- ^^^ For HaLVM
 cabal-version:       >=1.12
 
 extra-source-files:       ./cbits/*.h, ./cbits/*.c
@@ -27,6 +29,10 @@
   description: Build a program to test the AES implementation
   default: False
 
+flag halvm
+    description: The HaLVM target
+    default: False
+
 library
   default-language:    Haskell2010
   exposed-modules:     Crypto.Cipher.AES128, Crypto.Cipher.AES128.Internal
@@ -43,7 +49,10 @@
                      , ./cbits/cpu.c
                      , ./cbits/gf.c
   include-dirs:        cbits/
-  cc-options: -g
+  cc-options:
+  if flag(halvm)
+    cc-options: -maes -mpclmul -mssse3 -DWITH_AESNI
+    cpp-options: -DWITH_AESNI -maes -mpclmul -mssse3
   -- None by default cc-options:          -mssse3 -maes -mpclmul
 
 source-repository head
