diff --git a/Crypto/Cipher/AES.hs b/Crypto/Cipher/AES.hs
--- a/Crypto/Cipher/AES.hs
+++ b/Crypto/Cipher/AES.hs
@@ -33,6 +33,7 @@
     , encryptCTR
     , encryptXTS
     , encryptGCM
+    , encryptOCB
 
     -- * decryption
     , decryptECB
@@ -40,6 +41,7 @@
     , decryptCTR
     , decryptXTS
     , decryptGCM
+    , decryptOCB
     ) where
 
 import Data.Word
@@ -97,6 +99,7 @@
     xtsEncrypt = encryptXTS
     xtsDecrypt = decryptXTS
     aeadInit AEAD_GCM aes iv = Just $ AEAD aes $ AEADState $ gcmInit aes iv
+    aeadInit AEAD_OCB aes iv = Just $ AEAD aes $ AEADState $ ocbInit aes iv
     aeadInit _        _    _ = Nothing
 
 instance AEADModeImpl AES AESGCM where
@@ -105,6 +108,12 @@
     aeadStateDecrypt = gcmAppendDecrypt
     aeadStateFinalize = gcmFinish
 
+instance AEADModeImpl AES AESOCB where
+    aeadStateAppendHeader = ocbAppendAAD
+    aeadStateEncrypt = ocbAppendEncrypt
+    aeadStateDecrypt = ocbAppendDecrypt
+    aeadStateFinalize = ocbFinish
+
 #define INSTANCE_BLOCKCIPHER(CSTR) \
 instance BlockCipher CSTR where \
     { blockSize _ = 16 \
@@ -116,6 +125,7 @@
     ; xtsEncrypt (CSTR aes1, CSTR aes2) = encryptXTS (aes1,aes2) \
     ; xtsDecrypt (CSTR aes1, CSTR aes2) = decryptXTS (aes1,aes2) \
     ; aeadInit AEAD_GCM cipher@(CSTR aes) iv = Just $ AEAD cipher $ AEADState $ gcmInit aes iv \
+    ; aeadInit AEAD_OCB cipher@(CSTR aes) iv = Just $ AEAD cipher $ AEADState $ ocbInit aes iv \
     ; aeadInit _        _                  _ = Nothing \
     }; \
 \
@@ -124,6 +134,13 @@
     ; aeadStateEncrypt (CSTR aes) gcmState input = gcmAppendEncrypt aes gcmState input \
     ; aeadStateDecrypt (CSTR aes) gcmState input = gcmAppendDecrypt aes gcmState input \
     ; aeadStateFinalize (CSTR aes) gcmState len  = gcmFinish aes gcmState len \
+    }; \
+\
+instance AEADModeImpl CSTR AESOCB where \
+    { aeadStateAppendHeader (CSTR aes) ocbState bs = ocbAppendAAD aes ocbState bs \
+    ; aeadStateEncrypt (CSTR aes) ocbState input = ocbAppendEncrypt aes ocbState input \
+    ; aeadStateDecrypt (CSTR aes) ocbState input = ocbAppendDecrypt aes ocbState input \
+    ; aeadStateFinalize (CSTR aes) ocbState len  = ocbFinish aes ocbState len \
     }
 
 INSTANCE_BLOCKCIPHER(AES128)
@@ -133,9 +150,15 @@
 -- | AESGCM State
 newtype AESGCM = AESGCM SecureMem
 
+-- | AESOCB State
+newtype AESOCB = AESOCB SecureMem
+
 sizeGCM :: Int
 sizeGCM = 80
 
+sizeOCB :: Int
+sizeOCB = 96
+
 keyToPtr :: AES -> (Ptr AES -> IO a) -> IO a
 keyToPtr (AES b) f = withSecureMemPtr b (f . castPtr)
 
@@ -159,6 +182,13 @@
 withNewGCMSt :: AESGCM -> (Ptr AESGCM -> IO ()) -> IO AESGCM
 withNewGCMSt (AESGCM gcmSt) f = withSecureMemCopy gcmSt (f . castPtr) >>= \sm2 -> return (AESGCM sm2)
 
+withOCBKeyAndCopySt :: AES -> AESOCB -> (Ptr AESOCB -> Ptr AES -> IO a) -> IO (a, AESOCB)
+withOCBKeyAndCopySt aes (AESOCB gcmSt) f =
+    keyToPtr aes $ \aesPtr -> do
+        newSt <- secureMemCopy gcmSt
+        a     <- withSecureMemPtr newSt $ \gcmStPtr -> f (castPtr gcmStPtr) aesPtr
+        return (a, AESOCB newSt)
+
 -- | Initialize a new context with a key
 --
 -- Key need to be of length 16, 24 or 32 bytes. any other values will cause undefined behavior
@@ -240,6 +270,17 @@
            -> (ByteString, AuthTag) -- ^ ciphertext and tag
 encryptGCM = doGCM gcmAppendEncrypt
 
+-- | encrypt using OCB v3
+-- return the encrypted bytestring and the tag associated
+{-# NOINLINE encryptOCB #-}
+encryptOCB :: Byteable iv
+           => AES        -- ^ AES Context
+           -> iv         -- ^ IV initial vector of any size
+           -> ByteString -- ^ data to authenticate (AAD)
+           -> ByteString -- ^ data to encrypt
+           -> (ByteString, AuthTag) -- ^ ciphertext and tag
+encryptOCB = doOCB ocbAppendEncrypt
+
 -- | encrypt using XTS
 --
 -- the first key is the normal block encryption key
@@ -293,6 +334,16 @@
            -> (ByteString, AuthTag) -- ^ plaintext and tag
 decryptGCM = doGCM gcmAppendDecrypt
 
+-- | decrypt using Offset Codebook Mode (OCB)
+{-# NOINLINE decryptOCB #-}
+decryptOCB :: Byteable iv
+           => AES        -- ^ Key
+           -> iv         -- ^ IV initial vector of any size
+           -> ByteString -- ^ data to authenticate (AAD)
+           -> ByteString -- ^ data to decrypt
+           -> (ByteString, AuthTag) -- ^ plaintext and tag
+decryptOCB = doOCB ocbAppendDecrypt
+
 {-# INLINE doECB #-}
 doECB :: (Ptr b -> Ptr AES -> CString -> CUInt -> IO ())
       -> AES -> ByteString -> ByteString
@@ -335,6 +386,10 @@
   where (nbBlocks, r) = len `quotRem` 16
         len           = B.length input
 
+------------------------------------------------------------------------
+-- GCM
+------------------------------------------------------------------------
+
 {-# INLINE doGCM #-}
 doGCM :: Byteable iv
       => (AES -> AESGCM -> ByteString -> (ByteString, AESGCM))
@@ -402,33 +457,109 @@
   where computeTag = unsafeCreate 16 $ \t ->
                         withGCMKeyAndCopySt ctx gcm (c_aes_gcm_finish (castPtr t)) >> return ()
 
+------------------------------------------------------------------------
+-- OCB v3
+------------------------------------------------------------------------
+
+{-# INLINE doOCB #-}
+doOCB :: Byteable iv
+      => (AES -> AESOCB -> ByteString -> (ByteString, AESOCB))
+      -> AES
+      -> iv
+      -> ByteString
+      -> ByteString
+      -> (ByteString, AuthTag)
+doOCB f ctx iv aad input = (output, tag)
+  where tag             = ocbFinish ctx after 16
+        (output, after) = f ctx afterAAD input
+        afterAAD        = ocbAppendAAD ctx ini aad
+        ini             = ocbInit ctx iv
+
+-- | initialize an ocb context
+{-# NOINLINE ocbInit #-}
+ocbInit :: Byteable iv => AES -> iv -> AESOCB
+ocbInit ctx iv = unsafePerformIO $ do
+    sm <- createSecureMem sizeOCB $ \ocbStPtr ->
+            withKeyAndIV ctx iv $ \k v ->
+            c_aes_ocb_init (castPtr ocbStPtr) k v (fromIntegral $ byteableLength iv)
+    return $ AESOCB sm
+
+-- | append data which is going to just be authentified to the OCB context.
+--
+-- need to happen after initialization and before appending encryption/decryption data.
+{-# NOINLINE ocbAppendAAD #-}
+ocbAppendAAD :: AES -> AESOCB -> ByteString -> AESOCB
+ocbAppendAAD ctx ocb input = unsafePerformIO (snd `fmap` withOCBKeyAndCopySt ctx ocb doAppend)
+  where doAppend ocbStPtr aesPtr =
+            unsafeUseAsCString input $ \i ->
+            c_aes_ocb_aad ocbStPtr aesPtr i (fromIntegral $ B.length input)
+
+-- | append data to encrypt and append to the OCB context
+--
+-- bytestring need to be multiple of AES block size, unless it's the last call to this function.
+-- need to happen after AAD appending, or after initialization if no AAD data.
+{-# NOINLINE ocbAppendEncrypt #-}
+ocbAppendEncrypt :: AES -> AESOCB -> ByteString -> (ByteString, AESOCB)
+ocbAppendEncrypt ctx ocb input = unsafePerformIO $ withOCBKeyAndCopySt ctx ocb doEnc
+  where len = B.length input
+        doEnc ocbStPtr aesPtr =
+            create len $ \o ->
+            unsafeUseAsCString input $ \i ->
+            c_aes_ocb_encrypt (castPtr o) ocbStPtr aesPtr i (fromIntegral len)
+
+-- | append data to decrypt and append to the OCB context
+--
+-- bytestring need to be multiple of AES block size, unless it's the last call to this function.
+-- need to happen after AAD appending, or after initialization if no AAD data.
+{-# NOINLINE ocbAppendDecrypt #-}
+ocbAppendDecrypt :: AES -> AESOCB -> ByteString -> (ByteString, AESOCB)
+ocbAppendDecrypt ctx ocb input = unsafePerformIO $ withOCBKeyAndCopySt ctx ocb doDec
+  where len = B.length input
+        doDec ocbStPtr aesPtr =
+            create len $ \o ->
+            unsafeUseAsCString input $ \i ->
+            c_aes_ocb_decrypt (castPtr o) ocbStPtr aesPtr i (fromIntegral len)
+
+-- | Generate the Tag from OCB context
+{-# NOINLINE ocbFinish #-}
+ocbFinish :: AES -> AESOCB -> Int -> AuthTag
+ocbFinish ctx ocb taglen = AuthTag $ B.take taglen computeTag
+  where computeTag = unsafeCreate 16 $ \t ->
+                        withOCBKeyAndCopySt ctx ocb (c_aes_ocb_finish (castPtr t)) >> return ()
+
+------------------------------------------------------------------------
 foreign import ccall "aes.h aes_initkey"
     c_aes_init :: Ptr AES -> CString -> CUInt -> IO ()
 
+------------------------------------------------------------------------
 foreign import ccall "aes.h aes_encrypt_ecb"
     c_aes_encrypt_ecb :: CString -> Ptr AES -> CString -> CUInt -> IO ()
 
 foreign import ccall "aes.h aes_decrypt_ecb"
     c_aes_decrypt_ecb :: CString -> Ptr AES -> CString -> CUInt -> IO ()
 
+------------------------------------------------------------------------
 foreign import ccall "aes.h aes_encrypt_cbc"
     c_aes_encrypt_cbc :: CString -> Ptr AES -> Ptr Word8 -> CString -> CUInt -> IO ()
 
 foreign import ccall "aes.h aes_decrypt_cbc"
     c_aes_decrypt_cbc :: CString -> Ptr AES -> Ptr Word8 -> CString -> CUInt -> IO ()
 
+------------------------------------------------------------------------
 foreign import ccall "aes.h aes_encrypt_xts"
     c_aes_encrypt_xts :: CString -> Ptr AES -> Ptr AES -> Ptr Word8 -> CUInt -> CString -> CUInt -> IO ()
 
 foreign import ccall "aes.h aes_decrypt_xts"
     c_aes_decrypt_xts :: CString -> Ptr AES -> Ptr AES -> Ptr Word8 -> CUInt -> CString -> CUInt -> IO ()
 
+------------------------------------------------------------------------
 foreign import ccall "aes.h aes_gen_ctr"
     c_aes_gen_ctr :: CString -> Ptr AES -> Ptr Word8 -> CUInt -> IO ()
 
 foreign import ccall "aes.h aes_encrypt_ctr"
     c_aes_encrypt_ctr :: CString -> Ptr AES -> Ptr Word8 -> CString -> CUInt -> IO ()
 
+------------------------------------------------------------------------
 foreign import ccall "aes.h aes_gcm_init"
     c_aes_gcm_init :: Ptr AESGCM -> Ptr AES -> Ptr Word8 -> CUInt -> IO ()
 
@@ -443,3 +574,19 @@
 
 foreign import ccall "aes.h aes_gcm_finish"
     c_aes_gcm_finish :: CString -> Ptr AESGCM -> Ptr AES -> IO ()
+
+------------------------------------------------------------------------
+foreign import ccall "aes.h aes_ocb_init"
+    c_aes_ocb_init :: Ptr AESOCB -> Ptr AES -> Ptr Word8 -> CUInt -> IO ()
+
+foreign import ccall "aes.h aes_ocb_aad"
+    c_aes_ocb_aad :: Ptr AESOCB -> Ptr AES -> CString -> CUInt -> IO ()
+
+foreign import ccall "aes.h aes_ocb_encrypt"
+    c_aes_ocb_encrypt :: CString -> Ptr AESOCB -> Ptr AES -> CString -> CUInt -> IO ()
+
+foreign import ccall "aes.h aes_ocb_decrypt"
+    c_aes_ocb_decrypt :: CString -> Ptr AESOCB -> Ptr AES -> CString -> CUInt -> IO ()
+
+foreign import ccall "aes.h aes_ocb_finish"
+    c_aes_ocb_finish :: CString -> Ptr AESOCB -> Ptr AES -> IO ()
diff --git a/Tests/KATOCB3.hs b/Tests/KATOCB3.hs
new file mode 100644
--- /dev/null
+++ b/Tests/KATOCB3.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE OverloadedStrings #-}
+module KATOCB3 where
+
+import qualified Data.ByteString as B
+import Data.ByteString.Char8 ()
+
+-- (key, iv, aad, input, out, taglen, tag)
+type KATOCB3 = (B.ByteString, B.ByteString, B.ByteString, B.ByteString, B.ByteString, Int, B.ByteString)
+
+key1   = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+nonce1 = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b"
+
+vectors_aes128_enc :: [KATOCB3]
+vectors_aes128_enc =
+    [ ( {-key = -} key1
+      , {-iv = -} nonce1
+      , {-aad = -}""
+      , {-input = -}""
+      , {-out = -}""
+      , {-taglen = -} 16
+      , {-tag = -} "\x19\x7b\x9c\x3c\x44\x1d\x3c\x83\xea\xfb\x2b\xef\x63\x3b\x91\x82")
+    , ( key1, nonce1
+      , "\x00\x01\x02\x03\x04\x05\x06\x07"
+      , "\x00\x01\x02\x03\x04\x05\x06\x07"
+      , "\x92\xb6\x57\x13\x0a\x74\xb8\x5a"
+      , 16
+      , "\x16\xdc\x76\xa4\x6d\x47\xe1\xea\xd5\x37\x20\x9e\x8a\x96\xd1\x4e")
+    , ( key1, nonce1
+      , "\x00\x01\x02\x03\x04\x05\x06\x07"
+      , ""
+      , ""
+      , 16
+      , "\x98\xb9\x15\x52\xc8\xc0\x09\x18\x50\x44\xe3\x0a\x6e\xb2\xfe\x21")
+    , ( key1, nonce1
+      , ""
+      , "\x00\x01\x02\x03\x04\x05\x06\x07"
+      , "\x92\xb6\x57\x13\x0a\x74\xb8\x5a"
+      , 16
+      , "\x97\x1e\xff\xca\xe1\x9a\xd4\x71\x6f\x88\xe8\x7b\x87\x1f\xbe\xed")
+    , ( key1, nonce1
+      , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+      , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+      , "\xbe\xa5\xe8\x79\x8d\xbe\x71\x10\x03\x1c\x14\x4d\xa0\xb2\x61\x22"
+      , 16
+      , "\x77\x6c\x99\x24\xd6\x72\x3a\x1f\xc4\x52\x45\x32\xac\x3e\x5b\xeb")
+    ]
+
+vectors_encrypt =
+    [ ("AES128 Enc", vectors_aes128_enc)
+    ]
+
+vectors_decrypt = []
diff --git a/Tests/Tests.hs b/Tests/Tests.hs
--- a/Tests/Tests.hs
+++ b/Tests/Tests.hs
@@ -21,12 +21,13 @@
 import qualified KATCBC
 import qualified KATXTS
 import qualified KATGCM
+import qualified KATOCB3
 
 toKatECB (k,p,c) = KAT_ECB { ecbKey = k, ecbPlaintext = p, ecbCiphertext = c }
 toKatCBC (k,iv,p,c) = KAT_CBC { cbcKey = k, cbcIV = iv, cbcPlaintext = p, cbcCiphertext = c }
 toKatXTS (k1,k2,iv,p,_,c) = KAT_XTS { xtsKey1 = k1, xtsKey2 = k2, xtsIV = iv, xtsPlaintext = p, xtsCiphertext = c }
-toKatGCM (k,iv,h,p,c,taglen,tag) =
-    KAT_AEAD { aeadMode       = AEAD_GCM
+toKatAEAD mode (k,iv,h,p,c,taglen,tag) =
+    KAT_AEAD { aeadMode       = mode
              , aeadKey        = k
              , aeadIV         = iv
              , aeadHeader     = h
@@ -35,6 +36,8 @@
              , aeadTaglen     = taglen
              , aeadTag        = AuthTag tag
              }
+toKatGCM = toKatAEAD AEAD_GCM
+toKatOCB = toKatAEAD AEAD_OCB
 
 kats128 = defaultKATs
     { kat_ECB  = map toKatECB KATECB.vectors_aes128_enc
@@ -46,7 +49,8 @@
                            }
                  ]
     , kat_XTS  = map toKatXTS KATXTS.vectors_aes128_enc
-    , kat_AEAD = map toKatGCM KATGCM.vectors_aes128_enc
+    , kat_AEAD = map toKatGCM KATGCM.vectors_aes128_enc ++
+                 map toKatOCB KATOCB3.vectors_aes128_enc
     }
 
 kats192 = defaultKATs
diff --git a/cbits/aes.c b/cbits/aes.c
--- a/cbits/aes.c
+++ b/cbits/aes.c
@@ -49,6 +49,8 @@
                              uint32_t spoint, aes_block *input, uint32_t nb_blocks);
 void aes_generic_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_key *key, uint8_t *input, uint32_t length);
 void aes_generic_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_key *key, uint8_t *input, uint32_t length);
+void aes_generic_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
+void aes_generic_ocb_decrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
 
 enum {
 	/* init */
@@ -67,9 +69,12 @@
 	/* xts */
 	ENCRYPT_XTS_128, ENCRYPT_XTS_192, ENCRYPT_XTS_256,
 	DECRYPT_XTS_128, DECRYPT_XTS_192, DECRYPT_XTS_256,
-	/* xts */
+	/* gcm */
 	ENCRYPT_GCM_128, ENCRYPT_GCM_192, ENCRYPT_GCM_256,
 	DECRYPT_GCM_128, DECRYPT_GCM_192, DECRYPT_GCM_256,
+	/* ocb */
+	ENCRYPT_OCB_128, ENCRYPT_OCB_192, ENCRYPT_OCB_256,
+	DECRYPT_OCB_128, DECRYPT_OCB_192, DECRYPT_OCB_256,
 };
 
 void *branch_table[] = {
@@ -116,6 +121,13 @@
 	[DECRYPT_GCM_128]   = aes_generic_gcm_decrypt,
 	[DECRYPT_GCM_192]   = aes_generic_gcm_decrypt,
 	[DECRYPT_GCM_256]   = aes_generic_gcm_decrypt,
+	/* OCB */
+	[ENCRYPT_OCB_128]   = aes_generic_ocb_encrypt,
+	[ENCRYPT_OCB_192]   = aes_generic_ocb_encrypt,
+	[ENCRYPT_OCB_256]   = aes_generic_ocb_encrypt,
+	[DECRYPT_OCB_128]   = aes_generic_ocb_decrypt,
+	[DECRYPT_OCB_192]   = aes_generic_ocb_decrypt,
+	[DECRYPT_OCB_256]   = aes_generic_ocb_decrypt,
 };
 
 typedef void (*init_f)(aes_key *, uint8_t *, uint8_t);
@@ -124,6 +136,7 @@
 typedef void (*ctr_f)(uint8_t *output, aes_key *key, aes_block *iv, 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_key *key, uint8_t *input, uint32_t length);
+typedef void (*ocb_crypt_f)(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
 typedef void (*block_f)(aes_block *output, aes_key *key, aes_block *input);
 
 #ifdef WITH_AESNI
@@ -147,6 +160,10 @@
 	((gcm_crypt_f) (branch_table[ENCRYPT_GCM_128 + strength]))
 #define GET_GCM_DECRYPT(strength) \
 	((gcm_crypt_f) (branch_table[DECRYPT_GCM_128 + strength]))
+#define GET_OCB_ENCRYPT(strength) \
+	((ocb_crypt_f) (branch_table[ENCRYPT_OCB_128 + strength]))
+#define GET_OCB_DECRYPT(strength) \
+	((ocb_crypt_f) (branch_table[DECRYPT_OCB_128 + strength]))
 #define aes_encrypt_block(o,k,i) \
 	(((block_f) (branch_table[ENCRYPT_BLOCK_128 + k->strength]))(o,k,i))
 #define aes_decrypt_block(o,k,i) \
@@ -162,6 +179,8 @@
 #define GET_XTS_DECRYPT(strength) aes_generic_decrypt_xts
 #define GET_GCM_ENCRYPT(strength) aes_generic_gcm_encrypt
 #define GET_GCM_DECRYPT(strength) aes_generic_gcm_decrypt
+#define GET_OCB_ENCRYPT(strength) aes_generic_ocb_encrypt
+#define GET_OCB_DECRYPT(strength) aes_generic_ocb_decrypt
 #define aes_encrypt_block(o,k,i) aes_generic_encrypt_block(o,k,i)
 #define aes_decrypt_block(o,k,i) aes_generic_decrypt_block(o,k,i)
 #endif
@@ -197,6 +216,11 @@
 	/* GCM */
 	branch_table[ENCRYPT_GCM_128] = aes_ni_gcm_encrypt128;
 	branch_table[ENCRYPT_GCM_256] = aes_ni_gcm_encrypt256;
+	/* OCB */
+	/*
+	branch_table[ENCRYPT_OCB_128] = aes_ni_ocb_encrypt128;
+	branch_table[ENCRYPT_OCB_256] = aes_ni_ocb_encrypt256;
+	*/
 }
 #endif
 
@@ -281,6 +305,18 @@
 	d(output, gcm, key, input, length);
 }
 
+void aes_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length)
+{
+	ocb_crypt_f e = GET_OCB_ENCRYPT(key->strength);
+	e(output, ocb, key, input, length);
+}
+
+void aes_ocb_decrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length)
+{
+	ocb_crypt_f d = GET_OCB_DECRYPT(key->strength);
+	d(output, ocb, key, input, length);
+}
+
 static void gcm_ghash_add(aes_gcm *gcm, block128 *b)
 {
 	block128_xor(&gcm->tag, b);
@@ -354,6 +390,106 @@
 	}
 }
 
+static void ocb_block_double(block128 *output, block128 *input)
+{
+	unsigned int i;
+	uint8_t tmp = input->b[0];
+
+	if (output != input) block128_copy(output, input);
+	for (i=0; i<15; i++)
+		output->b[i] = (input->b[i] << 1) | (input->b[i+1] >> 7);
+	output->b[15] = (input->b[15] << 1) ^ ((tmp >> 7) * 0x87);
+}
+
+static void ocb_get_L_i(block128 *l, block128 *ldollar, unsigned int i)
+{
+	/* TODO optimise with a table. */
+	ocb_block_double(l, ldollar);
+	for ( ; (i&1) == 0; i >>= 1) /* double for each trailing 0 */
+		ocb_block_double(l, l);
+}
+
+void aes_ocb_init(aes_ocb *ocb, aes_key *key, uint8_t *iv, uint32_t len)
+{
+	block128 tmp, nonce, ktop;
+	unsigned char stretch[24];
+	unsigned bottom, byteshift, bitshift, i;
+
+	/* we don't accept more than 15 bytes, any bytes higher will be ignored. */
+	if (len > 15) {
+		len = 15;
+	}
+
+	/* create L*, and L$ */
+	block128_zero(&tmp);
+	aes_encrypt_block(&ocb->lstar, key, &tmp);
+	ocb_block_double(&ocb->ldollar, &ocb->lstar);
+
+	/* create strech from the nonce */
+	block128_zero(&nonce);
+	memcpy(nonce.b + 4, iv, 12);
+	nonce.b[0] = (unsigned char)(((16 * 8) % 128) << 1);
+	nonce.b[16-12-1] |= 0x01;
+	bottom = nonce.b[15] & 0x3F;
+	nonce.b[15] &= 0xC0;
+	aes_encrypt_block(&ktop, key, &nonce);
+	memcpy(stretch, ktop.b, 16);
+	memcpy(tmp.b, ktop.b + 1, 8);
+	block128_xor(&tmp, &ktop);
+	memcpy(stretch + 16, tmp.b, 8);
+
+	/* initialize the encryption offset from stretch */
+	byteshift = bottom / 8;
+	bitshift = bottom % 8;
+	if (bitshift != 0)
+		for (i = 0; i < 16; i++)
+			ocb->offset_enc.b[i] = (stretch[i+byteshift] << bitshift)
+			                     | (stretch[i+byteshift+1] >> (8-bitshift));
+	else
+		for (i = 0; i < 16; i++)
+			ocb->offset_enc.b[i] = stretch[i+byteshift];
+	/* initialize checksum for aad and encryption, and the aad offset */
+	block128_zero(&ocb->sum_aad);
+	block128_zero(&ocb->sum_enc);
+	block128_zero(&ocb->offset_aad);
+}
+
+void aes_ocb_aad(aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length)
+{
+	block128 tmp;
+	unsigned int i;
+
+	for (i=1; i<= length/16; i++, input=input+16) {
+		ocb_get_L_i(&tmp, &ocb->ldollar, i);
+		block128_xor(&ocb->offset_aad, &tmp);
+
+		block128_vxor(&tmp, &ocb->offset_aad, (block128 *) input);
+		aes_encrypt_block(&tmp, key, &tmp);
+		block128_xor(&ocb->sum_aad, &tmp);
+	}
+
+	length = length % 16; /* Bytes in final block */
+	if (length > 0) {
+		block128_xor(&ocb->offset_aad, &ocb->lstar);
+		block128_zero(&tmp);
+		block128_copy_bytes(&tmp, input, length);
+		tmp.b[length] = 0x80;
+		block128_xor(&tmp, &ocb->offset_aad);
+		aes_encrypt_block(&tmp, key, &tmp);
+		block128_xor(&ocb->sum_aad, &tmp);
+	}
+}
+
+void aes_ocb_finish(uint8_t *tag, aes_ocb *ocb, aes_key *key)
+{
+	block128 tmp;
+
+	block128_vxor(&tmp, &ocb->sum_enc, &ocb->offset_enc);
+	block128_xor(&tmp, &ocb->ldollar);
+	aes_encrypt_block((block128 *) tag, key, &tmp);
+	block128_xor((block128 *) tag, &ocb->sum_aad);
+}
+
 void aes_generic_encrypt_ecb(aes_block *output, aes_key *key, aes_block *input, uint32_t nb_blocks)
 {
 	for ( ; nb_blocks-- > 0; input++, output++) {
@@ -525,3 +661,61 @@
 	}
 }
 
+static int ocb_generic_crypt(uint8_t *output, aes_ocb *ocb, aes_key *key,
+                             uint8_t *input, uint32_t length, int encrypt)
+{
+	block128 tmp, pad;
+	unsigned int i;
+
+	for (i = 1; i <= length/16; i++, input += 16, output += 16) {
+		/* Offset_i = Offset_{i-1} xor L_{ntz(i)} */
+		ocb_get_L_i(&tmp, &ocb->ldollar, i);
+		block128_xor(&ocb->offset_enc, &tmp);
+
+		block128_vxor(&tmp, &ocb->offset_enc, (block128 *) input);
+		if (encrypt) {
+			aes_encrypt_block(&tmp, key, &tmp);
+			block128_vxor((block128 *) output, &ocb->offset_enc, &tmp);
+			block128_xor(&ocb->sum_enc, (block128 *) input);
+		} else {
+			aes_decrypt_block(&tmp, key, &tmp);
+			block128_vxor((block128 *) output, &ocb->offset_enc, &tmp);
+			block128_xor(&ocb->sum_enc, (block128 *) output);
+		}
+	}
+
+	/* process the last partial block if any */
+	length = length % 16;
+	if (length > 0) {
+		block128_xor(&ocb->offset_enc, &ocb->lstar);
+		aes_encrypt_block(&pad, key, &ocb->offset_enc);
+
+		if (encrypt) {
+			block128_zero(&tmp);
+			block128_copy_bytes(&tmp, input, length);
+			tmp.b[length] = 0x80;
+			block128_xor(&ocb->sum_enc, &tmp);
+			block128_xor(&pad, &tmp);
+			memcpy(output, pad.b, length);
+			output += length;
+		} else {
+			block128_copy(&tmp, &pad);
+			block128_copy_bytes(&tmp, input, length);
+			block128_xor(&tmp, &pad);
+			tmp.b[length] = 0x80;
+			memcpy(output, tmp.b, length);
+			block128_xor(&ocb->sum_enc, &tmp);
+			input += length;
+		}
+	}
+}
+
+void aes_generic_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length)
+{
+	ocb_generic_crypt(output, ocb, key, input, length, 1);
+}
+
+void aes_generic_ocb_decrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length)
+{
+	ocb_generic_crypt(output, ocb, key, input, length, 0);
+}
diff --git a/cbits/aes.h b/cbits/aes.h
--- a/cbits/aes.h
+++ b/cbits/aes.h
@@ -55,6 +55,15 @@
 	uint64_t length_input;
 } aes_gcm;
 
+typedef struct {
+	block128 lstar;
+	block128 ldollar;
+	block128 offset_aad;
+	block128 offset_enc;
+	block128 sum_aad;
+	block128 sum_enc;
+} aes_ocb;
+
 /* in bytes: either 16,24,32 */
 void aes_initkey(aes_key *ctx, uint8_t *key, uint8_t size);
 
@@ -79,5 +88,11 @@
 void aes_gcm_encrypt(uint8_t *output, aes_gcm *gcm, aes_key *key, uint8_t *input, uint32_t length);
 void aes_gcm_decrypt(uint8_t *output, aes_gcm *gcm, aes_key *key, uint8_t *input, uint32_t length);
 void aes_gcm_finish(uint8_t *tag, aes_gcm *gcm, aes_key *key);
+
+void aes_ocb_init(aes_ocb *ocb, aes_key *key, uint8_t *iv, uint32_t len);
+void aes_ocb_aad(aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
+void aes_ocb_encrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
+void aes_ocb_decrypt(uint8_t *output, aes_ocb *ocb, aes_key *key, uint8_t *input, uint32_t length);
+void aes_ocb_finish(uint8_t *tag, aes_ocb *ocb, aes_key *key);
 
 #endif
diff --git a/cipher-aes.cabal b/cipher-aes.cabal
--- a/cipher-aes.cabal
+++ b/cipher-aes.cabal
@@ -1,5 +1,5 @@
 Name:                cipher-aes
-Version:             0.2.6
+Version:             0.2.7
 Description:
     Fast AES cipher implementation with advanced mode of operations.
     .
