diff --git a/Crypto/Cipher/Types.hs b/Crypto/Cipher/Types.hs
--- a/Crypto/Cipher/Types.hs
+++ b/Crypto/Cipher/Types.hs
@@ -8,8 +8,6 @@
 -- symmetric cipher basic types
 --
 {-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE ExistentialQuantification #-}
 module Crypto.Cipher.Types
     (
     -- * Cipher classes
@@ -23,16 +21,10 @@
     , AEADState(..)
     , AEADMode(..)
     , AEADModeImpl(..)
-    -- * CFB8 mode
     , cfb8Encrypt
     , cfb8Decrypt
-    -- * AEAD
-    , aeadAppendHeader
-    , aeadEncrypt
-    , aeadDecrypt
-    , aeadFinalize
-    , aeadSimpleEncrypt
-    , aeadSimpleDecrypt
+    -- * AEAD functions
+    , module Crypto.Cipher.Types.AEAD
     -- * Key type and constructor
     , Key
     , makeKey
@@ -46,229 +38,11 @@
     ) where
 
 import Data.SecureMem
-import Data.ByteString (ByteString)
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Internal as B (unsafeCreate)
 import Data.Byteable
-import Data.Word
-import Data.Bits (shiftR, xor)
-import Crypto.Cipher.Types.GF
-import Foreign.Storable (poke)
-import Foreign.Ptr (plusPtr)
-
--- | Offset inside an XTS data unit, measured in block size.
-type DataUnitOffset = Word32
-
--- | Possible Error that can be reported when initializating a key
-data KeyError =
-      KeyErrorTooSmall
-    | KeyErrorTooBig
-    | KeyErrorInvalid String
-    deriving (Show,Eq)
-
--- | Different specifier for key size in bytes
-data KeySizeSpecifier =
-      KeySizeRange Int Int -- ^ in the range [min,max]
-    | KeySizeEnum  [Int]   -- ^ one of the specified values
-    | KeySizeFixed Int     -- ^ a specific size
-    deriving (Show,Eq)
-
--- | Symmetric cipher class.
-class Cipher cipher where
-    -- | Initialize a cipher context from a key
-    cipherInit    :: Key cipher -> cipher
-    -- | Cipher name
-    cipherName    :: cipher -> String
-    -- | return the size of the key required for this cipher.
-    -- Some cipher accept any size for key
-    cipherKeySize :: cipher -> KeySizeSpecifier
-
--- | Symmetric stream cipher class
-class Cipher cipher => StreamCipher cipher where
-    -- | Combine using the stream cipher
-    streamCombine :: cipher -> ByteString -> (ByteString, cipher)
-
--- | Symmetric block cipher class
-class Cipher cipher => BlockCipher cipher where
-    -- | Return the size of block required for this block cipher
-    blockSize    :: cipher -> Int
-
-    -- | Encrypt using the ECB mode.
-    --
-    -- input need to be a multiple of the blocksize
-    ecbEncrypt :: cipher -> ByteString -> ByteString
-    -- | Decrypt using the ECB mode.
-    --
-    -- input need to be a multiple of the blocksize
-    ecbDecrypt :: cipher -> ByteString -> ByteString
-
-    -- | encrypt using the CBC mode.
-    --
-    -- input need to be a multiple of the blocksize
-    cbcEncrypt :: cipher -> IV cipher -> ByteString -> ByteString
-    cbcEncrypt = cbcEncryptGeneric
-    -- | decrypt using the CBC mode.
-    --
-    -- input need to be a multiple of the blocksize
-    cbcDecrypt :: cipher -> IV cipher -> ByteString -> ByteString
-    cbcDecrypt = cbcDecryptGeneric
-
-    -- | encrypt using the CFB mode.
-    --
-    -- input need to be a multiple of the blocksize
-    cfbEncrypt :: cipher -> IV cipher -> ByteString -> ByteString
-    cfbEncrypt = cfbEncryptGeneric
-    -- | decrypt using the CFB mode.
-    --
-    -- input need to be a multiple of the blocksize
-    cfbDecrypt :: cipher -> IV cipher -> ByteString -> ByteString
-    cfbDecrypt = cfbDecryptGeneric
-
-    -- | combine using the CTR mode.
-    --
-    -- CTR mode produce a stream of randomized data that is combined
-    -- (by XOR operation) with the input stream.
-    --
-    -- encryption and decryption are the same operation.
-    --
-    -- input can be of any size
-    ctrCombine :: cipher -> IV cipher -> ByteString -> ByteString
-    ctrCombine = ctrCombineGeneric
-
-    -- | encrypt using the XTS mode.
-    --
-    -- input need to be a multiple of the blocksize
-    xtsEncrypt :: (cipher, cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString
-    xtsEncrypt = xtsEncryptGeneric
-    -- | decrypt using the XTS mode.
-    --
-    -- input need to be a multiple of the blocksize
-    xtsDecrypt :: (cipher, cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString
-    xtsDecrypt = xtsDecryptGeneric
-
-    -- | Initialize a new AEAD State
-    --
-    -- When Nothing is returns, it means the mode is not handled.
-    aeadInit :: Byteable iv => AEADMode -> cipher -> iv -> Maybe (AEAD cipher)
-    aeadInit _ _ _ = Nothing
-
--- | AEAD Mode
-data AEADMode =
-      AEAD_OCB
-    | AEAD_CCM
-    | AEAD_EAX
-    | AEAD_CWC
-    | AEAD_GCM
-    deriving (Show,Eq)
-
--- | Authenticated Encryption with Associated Data algorithms
-data AEAD cipher = AEAD cipher (AEADState cipher)
-
--- | Wrapper for any AEADState
-data AEADState cipher = forall st . AEADModeImpl cipher st => AEADState st
-
--- | Class of AEAD Mode implementation
-class BlockCipher cipher => AEADModeImpl cipher state where
-    aeadStateAppendHeader :: cipher -> state -> ByteString -> state
-    aeadStateEncrypt      :: cipher -> state -> ByteString -> (ByteString, state)
-    aeadStateDecrypt      :: cipher -> state -> ByteString -> (ByteString, state)
-    aeadStateFinalize     :: cipher -> state -> Int -> AuthTag
-
--- | Append associated data into the AEAD state
-aeadAppendHeader :: BlockCipher a => AEAD a -> ByteString -> AEAD a
-aeadAppendHeader (AEAD cipher (AEADState state)) bs =
-    AEAD cipher $ AEADState (aeadStateAppendHeader cipher state bs)
-
--- | Encrypt input and append into the AEAD state
-aeadEncrypt :: BlockCipher a => AEAD a -> ByteString -> (ByteString, AEAD a)
-aeadEncrypt (AEAD cipher (AEADState state)) input = (output, AEAD cipher (AEADState nst))
-  where (output, nst) = aeadStateEncrypt cipher state input
-
--- | Decrypt input and append into the AEAD state
-aeadDecrypt :: BlockCipher a => AEAD a -> ByteString -> (ByteString, AEAD a)
-aeadDecrypt (AEAD cipher (AEADState state)) input = (output, AEAD cipher (AEADState nst))
-  where (output, nst) = aeadStateDecrypt cipher state input
-
--- | Finalize the AEAD state and create an authentification tag
-aeadFinalize :: BlockCipher a => AEAD a -> Int -> AuthTag
-aeadFinalize (AEAD cipher (AEADState state)) len =
-    aeadStateFinalize cipher state len
-
--- | Simple AEAD encryption
-aeadSimpleEncrypt :: BlockCipher a
-                  => AEAD a        -- ^ A new AEAD Context
-                  -> B.ByteString  -- ^ Optional Authentified Header
-                  -> B.ByteString  -- ^ Optional Plaintext
-                  -> Int           -- ^ Tag length
-                  -> (AuthTag, B.ByteString) -- ^ Authentification tag and ciphertext
-aeadSimpleEncrypt aeadIni header input taglen = (tag, output)
-  where aead                = aeadAppendHeader aeadIni header
-        (output, aeadFinal) = aeadEncrypt aead input
-        tag                 = aeadFinalize aeadFinal taglen
-
--- | Simple AEAD decryption
-aeadSimpleDecrypt :: BlockCipher a
-                  => AEAD a        -- ^ A new AEAD Context
-                  -> B.ByteString  -- ^ Optional Authentified Header
-                  -> B.ByteString  -- ^ Optional Plaintext
-                  -> AuthTag       -- ^ Tag length
-                  -> Maybe B.ByteString -- ^ Plaintext
-aeadSimpleDecrypt aeadIni header input authTag
-    | tag == authTag = Just output
-    | otherwise      = Nothing
-  where aead                = aeadAppendHeader aeadIni header
-        (output, aeadFinal) = aeadDecrypt aead input
-        tag                 = aeadFinalize aeadFinal (byteableLength authTag)
-
--- | a Key parametrized by the cipher
-newtype Key c = Key SecureMem deriving (Eq)
-
-instance ToSecureMem (Key c) where
-    toSecureMem (Key sm) = sm
-instance Byteable (Key c) where
-    toBytes (Key sm) = toBytes sm
-
--- | an IV parametrized by the cipher
-newtype IV c = IV ByteString deriving (Eq)
-
-instance Byteable (IV c) where
-    toBytes (IV sm) = sm
-
--- | Authentification Tag for AE cipher mode
-newtype AuthTag = AuthTag ByteString
-    deriving (Show)
-
-instance Eq AuthTag where
-    (AuthTag a) == (AuthTag b) = constEqBytes a b
-instance Byteable AuthTag where
-    toBytes (AuthTag bs) = bs
-
--- | Create an IV for a specified block cipher
-makeIV :: (Byteable b, BlockCipher c) => b -> Maybe (IV c)
-makeIV b = toIV undefined
-  where toIV :: BlockCipher c => c -> Maybe (IV c)
-        toIV cipher
-          | byteableLength b == sz = Just (IV $ toBytes b)
-          | otherwise              = Nothing
-          where sz = blockSize cipher
-
--- | Create an IV that is effectively representing the number 0
-nullIV :: BlockCipher c => IV c
-nullIV = toIV undefined
-  where toIV :: BlockCipher c => c -> IV c
-        toIV cipher = IV $ B.replicate (blockSize cipher) 0
-
--- | Increment an IV by a number.
---
--- Assume the IV is in Big Endian format.
-ivAdd :: BlockCipher c => IV c -> Int -> IV c
-ivAdd (IV b) i = IV $ snd $ B.mapAccumR addCarry i b
-  where addCarry :: Int -> Word8 -> (Int, Word8)
-        addCarry acc w
-            | acc == 0  = (0, w)
-            | otherwise = let (hi,lo) = acc `divMod` 256
-                              nw      = lo + (fromIntegral w)
-                           in (hi + (nw `shiftR` 8), fromIntegral nw)
+import Crypto.Cipher.Types.Base
+import Crypto.Cipher.Types.Block
+import Crypto.Cipher.Types.Stream
+import Crypto.Cipher.Types.AEAD
 
 -- | Create a Key for a specified cipher
 makeKey :: (ToSecureMem b, Cipher c) => b -> Either KeyError (Key c)
@@ -284,99 +58,3 @@
                           | otherwise       -> Left $ KeyErrorInvalid ("valid size: " ++ show l)
             KeySizeFixed v | smLen == v     -> Right $ Key sm
                            | otherwise      -> Left $ KeyErrorInvalid ("valid size: " ++ show v)
-
-cbcEncryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
-cbcEncryptGeneric cipher (IV ivini) input = B.concat $ doEnc ivini $ chunk (blockSize cipher) input
-  where doEnc _  []     = []
-        doEnc iv (i:is) =
-            let o = ecbEncrypt cipher $ bxor iv i
-             in o : doEnc o is
-
-cbcDecryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
-cbcDecryptGeneric cipher (IV ivini) input = B.concat $ doDec ivini $ chunk (blockSize cipher) input
-  where doDec _  []     = []
-        doDec iv (i:is) =
-            let o = bxor iv $ ecbDecrypt cipher i
-             in o : doDec i is
-
-cfbEncryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
-cfbEncryptGeneric cipher (IV ivini) input = B.concat $ doEnc ivini $ chunk (blockSize cipher) input
-  where doEnc _  []     = []
-        doEnc iv (i:is) =
-            let o = bxor i $ ecbEncrypt cipher iv
-             in o : doEnc o is
-
-cfbDecryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
-cfbDecryptGeneric cipher (IV ivini) input = B.concat $ doDec ivini $ chunk (blockSize cipher) input
-  where doDec _  []     = []
-        doDec iv (i:is) =
-            let o = bxor i $ ecbEncrypt cipher iv
-             in o : doDec i is
-
-ctrCombineGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
-ctrCombineGeneric cipher ivini input = B.concat $ doCnt ivini $ chunk (blockSize cipher) input
-  where doCnt _  [] = []
-        doCnt iv (i:is) =
-            let ivEnc = ecbEncrypt cipher (toBytes iv)
-             in bxor i ivEnc : doCnt (ivAdd iv 1) is
-
-xtsEncryptGeneric :: BlockCipher cipher => (cipher,cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString
-xtsEncryptGeneric = xtsGeneric ecbEncrypt
-
-xtsDecryptGeneric :: BlockCipher cipher => (cipher,cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString
-xtsDecryptGeneric = xtsGeneric ecbDecrypt
-
-xtsGeneric :: BlockCipher cipher
-           => (cipher -> B.ByteString -> B.ByteString)
-           -> (cipher,cipher)
-           -> IV cipher
-           -> DataUnitOffset
-           -> ByteString
-           -> ByteString
-xtsGeneric f (cipher, tweakCipher) iv sPoint input = B.concat $ doXts iniTweak $ chunk (blockSize cipher) input
-  where encTweak = ecbEncrypt tweakCipher (toBytes iv)
-        iniTweak = iterate xtsGFMul encTweak !! fromIntegral sPoint
-        doXts _     []     = []
-        doXts tweak (i:is) =
-            let o = bxor (f cipher $ bxor i tweak) tweak
-             in o : doXts (xtsGFMul tweak) is
-
--- | Encrypt using CFB mode in 8 bit output
---
--- Effectively turn a Block cipher in CFB mode into a Stream cipher
-cfb8Encrypt :: BlockCipher a => a -> IV a -> B.ByteString -> B.ByteString
-cfb8Encrypt ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
-  where loop d iv@(IV i) m
-            | B.null m  = return ()
-            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
-          where m'  = if B.length m < blockSize ctx
-                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
-                            else B.take (blockSize ctx) m
-                r   = cfbEncrypt ctx iv m'
-                out = B.head r
-                ni  = IV (B.drop 1 i `B.snoc` out)
-
--- | Decrypt using CFB mode in 8 bit output
---
--- Effectively turn a Block cipher in CFB mode into a Stream cipher
-cfb8Decrypt :: BlockCipher a => a -> IV a -> B.ByteString -> B.ByteString
-cfb8Decrypt ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
-  where loop d iv@(IV i) m
-            | B.null m  = return ()
-            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
-          where m'  = if B.length m < blockSize ctx
-                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
-                            else B.take (blockSize ctx) m
-                r   = cfbDecrypt ctx iv m'
-                out = B.head r
-                ni  = IV (B.drop 1 i `B.snoc` B.head m')
-
-chunk :: Int -> ByteString -> [ByteString]
-chunk sz bs = split bs
-  where split b | B.length b <= sz = [b]
-                | otherwise        =
-                        let (b1, b2) = B.splitAt sz b
-                         in b1 : split b2
-
-bxor :: ByteString -> ByteString -> ByteString
-bxor src dst = B.pack $ B.zipWith xor src dst
diff --git a/Crypto/Cipher/Types/AEAD.hs b/Crypto/Cipher/Types/AEAD.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/AEAD.hs
@@ -0,0 +1,63 @@
+-- |
+-- Module      : Crypto.Cipher.Types.AEAD
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- AEAD cipher basic types
+--
+module Crypto.Cipher.Types.AEAD where
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Data.Byteable
+import Crypto.Cipher.Types.Base
+import Crypto.Cipher.Types.Block
+
+-- | Append associated data into the AEAD state
+aeadAppendHeader :: BlockCipher a => AEAD a -> ByteString -> AEAD a
+aeadAppendHeader (AEAD cipher (AEADState state)) bs =
+    AEAD cipher $ AEADState (aeadStateAppendHeader cipher state bs)
+
+-- | Encrypt input and append into the AEAD state
+aeadEncrypt :: BlockCipher a => AEAD a -> ByteString -> (ByteString, AEAD a)
+aeadEncrypt (AEAD cipher (AEADState state)) input = (output, AEAD cipher (AEADState nst))
+  where (output, nst) = aeadStateEncrypt cipher state input
+
+-- | Decrypt input and append into the AEAD state
+aeadDecrypt :: BlockCipher a => AEAD a -> ByteString -> (ByteString, AEAD a)
+aeadDecrypt (AEAD cipher (AEADState state)) input = (output, AEAD cipher (AEADState nst))
+  where (output, nst) = aeadStateDecrypt cipher state input
+
+-- | Finalize the AEAD state and create an authentification tag
+aeadFinalize :: BlockCipher a => AEAD a -> Int -> AuthTag
+aeadFinalize (AEAD cipher (AEADState state)) len =
+    aeadStateFinalize cipher state len
+
+-- | Simple AEAD encryption
+aeadSimpleEncrypt :: BlockCipher a
+                  => AEAD a        -- ^ A new AEAD Context
+                  -> B.ByteString  -- ^ Optional Authentified Header
+                  -> B.ByteString  -- ^ Optional Plaintext
+                  -> Int           -- ^ Tag length
+                  -> (AuthTag, B.ByteString) -- ^ Authentification tag and ciphertext
+aeadSimpleEncrypt aeadIni header input taglen = (tag, output)
+  where aead                = aeadAppendHeader aeadIni header
+        (output, aeadFinal) = aeadEncrypt aead input
+        tag                 = aeadFinalize aeadFinal taglen
+
+-- | Simple AEAD decryption
+aeadSimpleDecrypt :: BlockCipher a
+                  => AEAD a        -- ^ A new AEAD Context
+                  -> B.ByteString  -- ^ Optional Authentified Header
+                  -> B.ByteString  -- ^ Optional Plaintext
+                  -> AuthTag       -- ^ Tag length
+                  -> Maybe B.ByteString -- ^ Plaintext
+aeadSimpleDecrypt aeadIni header input authTag
+    | tag == authTag = Just output
+    | otherwise      = Nothing
+  where aead                = aeadAppendHeader aeadIni header
+        (output, aeadFinal) = aeadDecrypt aead input
+        tag                 = aeadFinalize aeadFinal (byteableLength authTag)
+
diff --git a/Crypto/Cipher/Types/Base.hs b/Crypto/Cipher/Types/Base.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/Base.hs
@@ -0,0 +1,83 @@
+-- |
+-- Module      : Crypto.Cipher.Types.Base
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- symmetric cipher basic types
+--
+module Crypto.Cipher.Types.Base
+    ( KeyError(..)
+    , KeySizeSpecifier(..)
+    , Key(..)
+    , IV(..)
+    , Cipher(..)
+    , AuthTag(..)
+    , AEADMode(..)
+    , DataUnitOffset
+    ) where
+
+import Data.Byteable
+import Data.SecureMem
+import Data.Word
+import Data.ByteString (ByteString)
+
+-- | Possible Error that can be reported when initializating a key
+data KeyError =
+      KeyErrorTooSmall
+    | KeyErrorTooBig
+    | KeyErrorInvalid String
+    deriving (Show,Eq)
+
+-- | Different specifier for key size in bytes
+data KeySizeSpecifier =
+      KeySizeRange Int Int -- ^ in the range [min,max]
+    | KeySizeEnum  [Int]   -- ^ one of the specified values
+    | KeySizeFixed Int     -- ^ a specific size
+    deriving (Show,Eq)
+
+-- | Offset inside an XTS data unit, measured in block size.
+type DataUnitOffset = Word32
+
+-- | a Key parametrized by the cipher
+newtype Key c = Key SecureMem deriving (Eq)
+
+instance ToSecureMem (Key c) where
+    toSecureMem (Key sm) = sm
+instance Byteable (Key c) where
+    toBytes (Key sm) = toBytes sm
+
+-- | an IV parametrized by the cipher
+newtype IV c = IV ByteString deriving (Eq)
+
+instance Byteable (IV c) where
+    toBytes (IV sm) = sm
+
+-- | Authentification Tag for AE cipher mode
+newtype AuthTag = AuthTag ByteString
+    deriving (Show)
+
+instance Eq AuthTag where
+    (AuthTag a) == (AuthTag b) = constEqBytes a b
+instance Byteable AuthTag where
+    toBytes (AuthTag bs) = bs
+
+-- | AEAD Mode
+data AEADMode =
+      AEAD_OCB
+    | AEAD_CCM
+    | AEAD_EAX
+    | AEAD_CWC
+    | AEAD_GCM
+    deriving (Show,Eq)
+
+-- | Symmetric cipher class.
+class Cipher cipher where
+    -- | Initialize a cipher context from a key
+    cipherInit    :: Key cipher -> cipher
+    -- | Cipher name
+    cipherName    :: cipher -> String
+    -- | return the size of the key required for this cipher.
+    -- Some cipher accept any size for key
+    cipherKeySize :: cipher -> KeySizeSpecifier
diff --git a/Crypto/Cipher/Types/Block.hs b/Crypto/Cipher/Types/Block.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/Block.hs
@@ -0,0 +1,253 @@
+-- |
+-- Module      : Crypto.Cipher.Types.Block
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- block cipher basic types
+--
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE ViewPatterns #-}
+module Crypto.Cipher.Types.Block
+    (
+    -- * BlockCipher
+      BlockCipher(..)
+    -- * initialization vector (IV)
+    , IV
+    , makeIV
+    , nullIV
+    , ivAdd
+    -- * XTS
+    , XTS
+    -- * AEAD
+    , AEAD(..)
+    , AEADState(..)
+    , AEADModeImpl(..)
+    -- * CFB 8 bits
+    , cfb8Encrypt
+    , cfb8Decrypt
+    ) where
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Internal as B (unsafeCreate)
+import Data.Byteable
+import Data.Word
+import Data.Bits (shiftR, Bits)
+import Crypto.Cipher.Types.Base
+import Crypto.Cipher.Types.GF
+import Crypto.Cipher.Types.Utils
+import Foreign.Ptr
+import Foreign.Storable
+
+type XTS cipher = (cipher, cipher)
+               -> IV cipher        -- ^ Usually represent the Data Unit (e.g. disk sector)
+               -> DataUnitOffset   -- ^ Offset in the data unit in number of blocks
+               -> ByteString       -- ^ Data
+               -> ByteString       -- ^ Processed Data
+
+-- | Symmetric block cipher class
+class Cipher cipher => BlockCipher cipher where
+    -- | Return the size of block required for this block cipher
+    blockSize    :: cipher -> Int
+
+    -- | Encrypt blocks
+    --
+    -- the input string need to be multiple of the block size
+    ecbEncrypt :: cipher -> ByteString -> ByteString
+
+    -- | Decrypt blocks
+    --
+    -- the input string need to be multiple of the block size
+    ecbDecrypt :: cipher -> ByteString -> ByteString
+
+    -- | encrypt using the CBC mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cbcEncrypt :: cipher -> IV cipher -> ByteString -> ByteString
+    cbcEncrypt = cbcEncryptGeneric
+    -- | decrypt using the CBC mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cbcDecrypt :: cipher -> IV cipher -> ByteString -> ByteString
+    cbcDecrypt = cbcDecryptGeneric
+
+    -- | encrypt using the CFB mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cfbEncrypt :: cipher -> IV cipher -> ByteString -> ByteString
+    cfbEncrypt = cfbEncryptGeneric
+    -- | decrypt using the CFB mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cfbDecrypt :: cipher -> IV cipher -> ByteString -> ByteString
+    cfbDecrypt = cfbDecryptGeneric
+
+    -- | combine using the CTR mode.
+    --
+    -- CTR mode produce a stream of randomized data that is combined
+    -- (by XOR operation) with the input stream.
+    --
+    -- encryption and decryption are the same operation.
+    --
+    -- input can be of any size
+    ctrCombine :: cipher -> IV cipher -> ByteString -> ByteString
+    ctrCombine = ctrCombineGeneric
+
+    -- | encrypt using the XTS mode.
+    --
+    -- input need to be a multiple of the blocksize, and the cipher
+    -- need to process 128 bits block only
+    xtsEncrypt :: (cipher, cipher)
+               -> IV cipher        -- ^ Usually represent the Data Unit (e.g. disk sector)
+               -> DataUnitOffset   -- ^ Offset in the data unit in number of blocks
+               -> ByteString       -- ^ Plaintext
+               -> ByteString       -- ^ Ciphertext
+    xtsEncrypt = xtsEncryptGeneric
+
+    -- | decrypt using the XTS mode.
+    --
+    -- input need to be a multiple of the blocksize, and the cipher
+    -- need to process 128 bits block only
+    xtsDecrypt :: (cipher, cipher)
+               -> IV cipher        -- ^ Usually represent the Data Unit (e.g. disk sector)
+               -> DataUnitOffset   -- ^ Offset in the data unit in number of blocks
+               -> ByteString       -- ^ Ciphertext
+               -> ByteString       -- ^ Plaintext
+    xtsDecrypt = xtsDecryptGeneric
+
+    -- | Initialize a new AEAD State
+    --
+    -- When Nothing is returns, it means the mode is not handled.
+    aeadInit :: Byteable iv => AEADMode -> cipher -> iv -> Maybe (AEAD cipher)
+    aeadInit _ _ _ = Nothing
+
+-- | Authenticated Encryption with Associated Data algorithms
+data AEAD cipher = AEAD cipher (AEADState cipher)
+
+-- | Wrapper for any AEADState
+data AEADState cipher = forall st . AEADModeImpl cipher st => AEADState st
+
+-- | Class of AEAD Mode implementation
+class BlockCipher cipher => AEADModeImpl cipher state where
+    aeadStateAppendHeader :: cipher -> state -> ByteString -> state
+    aeadStateEncrypt      :: cipher -> state -> ByteString -> (ByteString, state)
+    aeadStateDecrypt      :: cipher -> state -> ByteString -> (ByteString, state)
+    aeadStateFinalize     :: cipher -> state -> Int -> AuthTag
+
+-- | Create an IV for a specified block cipher
+makeIV :: (Byteable b, BlockCipher c) => b -> Maybe (IV c)
+makeIV b = toIV undefined
+  where toIV :: BlockCipher c => c -> Maybe (IV c)
+        toIV cipher
+          | byteableLength b == sz = Just (IV $ toBytes b)
+          | otherwise              = Nothing
+          where sz = blockSize cipher
+
+-- | Create an IV that is effectively representing the number 0
+nullIV :: BlockCipher c => IV c
+nullIV = toIV undefined
+  where toIV :: BlockCipher c => c -> IV c
+        toIV cipher = IV $ B.replicate (blockSize cipher) 0
+
+-- | Increment an IV by a number.
+--
+-- Assume the IV is in Big Endian format.
+ivAdd :: BlockCipher c => IV c -> Int -> IV c
+ivAdd (IV b) i = IV $ snd $ B.mapAccumR addCarry i b
+  where addCarry :: Int -> Word8 -> (Int, Word8)
+        addCarry acc w
+            | acc == 0  = (0, w)
+            | otherwise = let (hi,lo) = acc `divMod` 256
+                              nw      = lo + (fromIntegral w)
+                           in (hi + (nw `shiftR` 8), fromIntegral nw)
+
+cbcEncryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
+cbcEncryptGeneric cipher (IV ivini) input = B.concat $ doEnc ivini $ chunk (blockSize cipher) input
+  where doEnc _  []     = []
+        doEnc iv (i:is) =
+            let o = ecbEncrypt cipher $ bxor iv i
+             in o : doEnc o is
+
+cbcDecryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
+cbcDecryptGeneric cipher (IV ivini) input = B.concat $ doDec ivini $ chunk (blockSize cipher) input
+  where doDec _  []     = []
+        doDec iv (i:is) =
+            let o = bxor iv $ ecbDecrypt cipher i
+             in o : doDec i is
+
+cfbEncryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
+cfbEncryptGeneric cipher (IV ivini) input = B.concat $ doEnc ivini $ chunk (blockSize cipher) input
+  where doEnc _  []     = []
+        doEnc iv (i:is) =
+            let o = bxor i $ ecbEncrypt cipher iv
+             in o : doEnc o is
+
+cfbDecryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
+cfbDecryptGeneric cipher (IV ivini) input = B.concat $ doDec ivini $ chunk (blockSize cipher) input
+  where doDec _  []     = []
+        doDec iv (i:is) =
+            let o = bxor i $ ecbEncrypt cipher iv
+             in o : doDec i is
+
+ctrCombineGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString
+ctrCombineGeneric cipher ivini input = B.concat $ doCnt ivini $ chunk (blockSize cipher) input
+  where doCnt _  [] = []
+        doCnt iv (i:is) =
+            let ivEnc = ecbEncrypt cipher (toBytes iv)
+             in bxor i ivEnc : doCnt (ivAdd iv 1) is
+
+xtsEncryptGeneric :: BlockCipher cipher => XTS cipher
+xtsEncryptGeneric = xtsGeneric ecbEncrypt
+
+xtsDecryptGeneric :: BlockCipher cipher => XTS cipher
+xtsDecryptGeneric = xtsGeneric ecbDecrypt
+
+xtsGeneric :: BlockCipher cipher
+           => (cipher -> B.ByteString -> B.ByteString)
+           -> (cipher, cipher)
+           -> IV cipher
+           -> DataUnitOffset
+           -> ByteString
+           -> ByteString
+xtsGeneric f (cipher, tweakCipher) iv sPoint input
+    | blockSize cipher /= 16 = error "XTS mode is only available with cipher that have a block size of 128 bits"
+    | otherwise = B.concat $ doXts iniTweak $ chunk (blockSize cipher) input
+  where encTweak = ecbEncrypt tweakCipher (toBytes iv)
+        iniTweak = iterate xtsGFMul encTweak !! fromIntegral sPoint
+        doXts _     []     = []
+        doXts tweak (i:is) =
+            let o = bxor (f cipher $ bxor i tweak) tweak
+             in o : doXts (xtsGFMul tweak) is
+
+-- | Encrypt using CFB mode in 8 bit output
+--
+-- Effectively turn a Block cipher in CFB mode into a Stream cipher
+cfb8Encrypt :: BlockCipher a => a -> IV a -> B.ByteString -> B.ByteString
+cfb8Encrypt ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
+  where loop d iv@(IV i) m
+            | B.null m  = return ()
+            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
+          where m'  = if B.length m < blockSize ctx
+                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
+                            else B.take (blockSize ctx) m
+                r   = cfbEncrypt ctx iv m'
+                out = B.head r
+                ni  = IV (B.drop 1 i `B.snoc` out)
+
+-- | Decrypt using CFB mode in 8 bit output
+--
+-- Effectively turn a Block cipher in CFB mode into a Stream cipher
+cfb8Decrypt :: BlockCipher a => a -> IV a -> B.ByteString -> B.ByteString
+cfb8Decrypt ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
+  where loop d iv@(IV i) m
+            | B.null m  = return ()
+            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
+          where m'  = if B.length m < blockSize ctx
+                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
+                            else B.take (blockSize ctx) m
+                r   = cfbDecrypt ctx iv m'
+                out = B.head r
+                ni  = IV (B.drop 1 i `B.snoc` B.head m')
diff --git a/Crypto/Cipher/Types/BlockIO.hs b/Crypto/Cipher/Types/BlockIO.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/BlockIO.hs
@@ -0,0 +1,186 @@
+-- |
+-- Module      : Crypto.Cipher.Types.Block
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- block cipher basic types
+--
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE ViewPatterns #-}
+module Crypto.Cipher.Types.BlockIO
+    ( BlockCipherIO(..)
+    , PtrDest
+    , PtrSource
+    , PtrIV
+    , BufferLength
+    , onBlock
+    ) where
+
+import Control.Applicative
+import Data.Word
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Internal as B (fromForeignPtr, memcpy)
+import Data.Byteable
+import Data.Bits (xor, Bits)
+import Foreign.Storable (poke, peek, Storable)
+--import Foreign.Ptr (plusPtr, Ptr, castPtr, nullPtr)
+import Crypto.Cipher.Types.Block
+import Foreign.Ptr
+import Foreign.ForeignPtr (newForeignPtr_)
+
+-- | pointer to the destination data
+type PtrDest   = Ptr Word8
+
+-- | pointer to the source data
+type PtrSource = Ptr Word8
+
+-- | pointer to the IV data
+type PtrIV     = Ptr Word8
+
+-- | Length of the pointed data
+type BufferLength = Word32
+
+-- | Symmetric block cipher class, mutable API
+class BlockCipher cipher => BlockCipherIO cipher where
+    -- | Encrypt using the ECB mode.
+    --
+    -- input need to be a multiple of the blocksize
+    ecbEncryptMutable :: cipher -> PtrDest -> PtrSource -> BufferLength -> IO ()
+
+    -- | Decrypt using the ECB mode.
+    --
+    -- input need to be a multiple of the blocksize
+    ecbDecryptMutable :: cipher -> PtrDest -> PtrSource -> BufferLength -> IO ()
+
+    -- | encrypt using the CBC mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cbcEncryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+    cbcEncryptMutable = cbcEncryptGeneric
+
+    -- | decrypt using the CBC mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cbcDecryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+    cbcDecryptMutable = cbcDecryptGeneric
+
+{-
+    -- | encrypt using the CFB mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cfbEncryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+    cfbEncryptMutable = cfbEncryptGeneric
+
+    -- | decrypt using the CFB mode.
+    --
+    -- input need to be a multiple of the blocksize
+    cfbDecryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+    cfbDecryptMutable = cfbDecryptGeneric
+
+    -- | combine using the CTR mode.
+    --
+    -- CTR mode produce a stream of randomized data that is combined
+    -- (by XOR operation) with the input stream.
+    --
+    -- encryption and decryption are the same operation.
+    --
+    -- input can be of any size
+    ctrCombineMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+    ctrCombineMutable = ctrCombineGeneric
+
+    -- | encrypt using the XTS mode.
+    --
+    -- input need to be a multiple of the blocksize
+    xtsEncryptMutable :: (cipher, cipher) -> PtrIV -> DataUnitOffset -> PtrDest -> PtrSource -> BufferLength -> IO ()
+    xtsEncryptMutable = xtsEncryptGeneric
+    -- | decrypt using the XTS mode.
+    --
+    -- input need to be a multiple of the blocksize
+    xtsDecryptMutable :: (cipher, cipher) -> PtrIV -> DataUnitOffset -> PtrDest -> PtrSource -> BufferLength -> IO ()
+    xtsDecryptMutable = xtsDecryptGeneric
+-}
+
+cbcEncryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+cbcEncryptGeneric cipher = loopBS cipher encrypt
+  where encrypt bs iv d s = do
+            mutableXor d iv s bs
+            ecbEncryptMutable cipher d d (fromIntegral bs)
+            return s
+
+cbcDecryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+cbcDecryptGeneric cipher = loopBS cipher decrypt
+  where decrypt bs iv d s = do
+            ecbEncryptMutable cipher d s (fromIntegral bs)
+            -- FIXME only work if s != d
+            mutableXor d iv d bs
+            return d
+
+{-
+cfbEncryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+cfbEncryptGeneric cipher = loopBS cipher encrypt
+  where encrypt bs iv d s = do
+            ecbEncryptMutable cipher d iv (fromIntegral bs)
+            mutableXor d d s bs
+            return d
+
+
+cfbDecryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+cfbDecryptGeneric cipher = loopBS cipher decrypt
+  where decrypt bs iv d s = do
+            ecbEncryptMutable cipher d iv (fromIntegral bs)
+            mutableXor d d s bs
+            return s
+
+ctrCombineGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
+ctrCombineGeneric cipher ivini dst src len = return () {-B.concat $ doCnt ivini $ chunk (blockSize cipher) input
+  where doCnt _  [] = []
+        doCnt iv (i:is) =
+            let ivEnc = ecbEncrypt cipher (toBytes iv)
+             in bxor i ivEnc : doCnt (ivAdd iv 1) is-}
+-}
+
+-- | Helper to use a purer interface
+onBlock :: BlockCipherIO cipher
+        => cipher
+        -> (ByteString -> ByteString)
+        -> PtrDest
+        -> PtrSource
+        -> BufferLength
+        -> IO ()
+onBlock cipher f dst src len = loopBS cipher wrap nullPtr dst src len
+  where wrap bs fakeIv d s = do
+            fSrc <- newForeignPtr_ s
+            let res = f (B.fromForeignPtr fSrc 0 bs)
+            withBytePtr res $ \r -> B.memcpy d r bs
+            return fakeIv
+
+loopBS :: BlockCipherIO cipher
+       => cipher
+       -> (Int -> PtrIV -> PtrDest -> PtrSource -> IO PtrIV)
+       -> PtrIV -> PtrDest -> PtrSource -> BufferLength
+       -> IO ()
+loopBS cipher f iv dst src len = loop iv dst src len
+  where bs = blockSize cipher
+        loop _ _ _ 0 = return ()
+        loop i d s n = do
+            newIV <- f bs i d s
+            loop newIV (d `plusPtr` bs) (s `plusPtr` bs) (n - fromIntegral bs)
+
+mutableXor :: PtrDest -> PtrSource -> PtrIV -> Int -> IO ()
+mutableXor (to64 -> dst) (to64 -> src) (to64 -> iv) 16 = do
+    peeksAndPoke dst src iv
+    peeksAndPoke (dst `plusPtr` 8) (src `plusPtr` 8) ((iv `plusPtr` 8) :: Ptr Word64)
+mutableXor (to64 -> dst) (to64 -> src) (to64 -> iv) 8 = do
+    peeksAndPoke dst src iv
+mutableXor dst src iv len = loop dst src iv len
+  where loop _ _ _ 0 = return ()
+        loop d s i n = peeksAndPoke d s i >> loop (d `plusPtr` 1) (s `plusPtr` 1) (i `plusPtr` 1) (n-1)
+
+to64 :: Ptr Word8 -> Ptr Word64
+to64 = castPtr
+
+peeksAndPoke :: (Bits a, Storable a) => Ptr a -> Ptr a -> Ptr a -> IO ()
+peeksAndPoke dst a b = (xor <$> peek a <*> peek b) >>= poke dst
diff --git a/Crypto/Cipher/Types/OfIO.hs b/Crypto/Cipher/Types/OfIO.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/OfIO.hs
@@ -0,0 +1,183 @@
+-- |
+-- Module      : Crypto.Cipher.Types.Modes
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- block cipher modes immutable interfaces
+--
+module Crypto.Cipher.Types.OfIO
+    (
+    -- * ECB
+      ecbEncryptOfIO
+    , ecbDecryptOfIO
+{-
+    -- * CBC
+    , cbcEncryptOfIO
+    , cbcDecryptOfIO
+    -- * CFB
+    , cfbEncryptOfIO
+    , cfbDecryptOfIO
+    , cfb8EncryptOfIO
+    , cfb8DecryptOfIO
+    -- * CTR
+    , ctrCombineOfIO
+    -- * XTS
+    , xtsEncryptOfIO
+    , xtsDecryptOfIO
+-}
+    ) where
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Internal as B
+import Data.Byteable
+--import Crypto.Cipher.Types.Base
+import Crypto.Cipher.Types.Block
+import Crypto.Cipher.Types.BlockIO
+--import Foreign.Storable (poke)
+--import Foreign.Ptr
+
+isBlockSized :: (BlockCipher cipher, BlockCipherIO cipher) => cipher -> Int -> Bool
+isBlockSized cipher bsLen = (bsLen `mod` blockSize cipher) == 0
+
+notBlockSized :: (BlockCipher cipher, BlockCipherIO cipher) => cipher -> a
+notBlockSized = undefined
+
+withDest :: BlockCipherIO cipher
+         => cipher
+         -> ByteString
+         -> (PtrDest -> PtrSource -> BufferLength -> IO ())
+         -> ByteString
+withDest cipher bs f
+    | B.null bs                     = B.empty
+    | not (isBlockSized cipher len) = notBlockSized cipher
+    | otherwise                     =
+        B.unsafeCreate len $ \dst ->
+        withBytePtr bs     $ \src ->
+        f dst src (fromIntegral len)
+  where len = B.length bs
+
+{-
+withDestIV :: BlockCipherIO cipher
+           => cipher
+           -> IV cipher
+           -> ByteString
+           -> (PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ())
+           -> ByteString
+withDestIV cipher (IV iv) bs f
+    | B.null bs                     = B.empty
+    | not (isBlockSized cipher len) = notBlockSized cipher
+    | otherwise                     =
+        B.unsafeCreate len $ \dst   ->
+        withBytePtr iv     $ \ivPtr ->
+        withBytePtr bs     $ \src   ->
+        f ivPtr dst src (fromIntegral len)
+  where len = B.length bs
+
+withDestIVAnySize :: BlockCipherIO cipher
+                  => IV cipher
+                  -> ByteString
+                  -> (PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ())
+                  -> ByteString
+withDestIVAnySize (IV iv) bs f
+    | B.null bs = B.empty
+    | otherwise =
+        B.unsafeCreate len $ \dst   ->
+        withBytePtr iv     $ \ivPtr ->
+        withBytePtr bs     $ \src   ->
+        f ivPtr dst src (fromIntegral len)
+  where len = B.length bs
+-}
+
+-- | Encrypt using the ECB mode.
+--
+-- input need to be a multiple of the blocksize
+ecbEncryptOfIO :: BlockCipherIO cipher => cipher -> ByteString -> ByteString
+ecbEncryptOfIO cipher bs = withDest cipher bs $ ecbEncryptMutable cipher
+
+-- | Decrypt using the ECB mode.
+--
+-- input need to be a multiple of the blocksize
+ecbDecryptOfIO :: BlockCipherIO cipher => cipher -> ByteString -> ByteString
+ecbDecryptOfIO cipher bs = withDest cipher bs $ ecbEncryptMutable cipher
+
+{-
+-- | encrypt using the CBC mode.
+--
+-- input need to be a multiple of the blocksize
+cbcEncryptOfIO :: BlockCipherIO cipher => cipher -> IV cipher -> ByteString -> ByteString
+cbcEncryptOfIO cipher iv bs = withDestIV cipher iv bs $ cbcEncryptMutable cipher
+
+-- | decrypt using the CBC mode.
+--
+-- input need to be a multiple of the blocksize
+cbcDecryptOfIO :: BlockCipherIO cipher => cipher -> IV cipher -> ByteString -> ByteString
+cbcDecryptOfIO cipher iv bs = withDestIV cipher iv bs $ cbcDecryptMutable cipher
+
+-- | encrypt using the CFB mode.
+--
+-- input need to be a multiple of the blocksize
+cfbEncryptOfIO :: BlockCipherIO cipher => cipher -> IV cipher -> ByteString -> ByteString
+cfbEncryptOfIO cipher iv bs = withDestIV cipher iv bs $ cfbEncryptMutable cipher
+
+-- | decrypt using the CFB mode.
+--
+-- input need to be a multiple of the blocksize
+cfbDecryptOfIO :: BlockCipherIO cipher => cipher -> IV cipher -> ByteString -> ByteString
+cfbDecryptOfIO cipher iv bs = withDestIV cipher iv bs $ cfbDecryptMutable cipher
+
+-- | combine using the CTR mode.
+--
+-- CTR mode produce a stream of randomized data that is combined
+-- (by XOR operation) with the input stream.
+--
+-- encryption and decryption are the same operation.
+--
+-- input can be of any size
+ctrCombineOfIO :: BlockCipherIO cipher => cipher -> IV cipher -> ByteString -> ByteString
+ctrCombineOfIO cipher iv bs = withDestIVAnySize iv bs $ cfbDecryptMutable cipher
+ 
+-- | encrypt using the XTS mode.
+--
+-- input need to be a multiple of the blocksize
+xtsEncryptOfIO :: BlockCipherIO cipher => (cipher, cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString
+xtsEncryptOfIO ciphers@(c1,_) iv ofs bs = withDestIV c1 iv bs $ \ivPtr -> xtsEncryptMutable ciphers ivPtr ofs
+
+-- | decrypt using the XTS mode.
+--
+-- input need to be a multiple of the blocksize
+xtsDecryptOfIO :: BlockCipherIO cipher => (cipher, cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString
+xtsDecryptOfIO ciphers@(c1,_) iv ofs bs = withDestIV c1 iv bs $ \ivPtr -> xtsDecryptMutable ciphers ivPtr ofs
+
+-- | Encrypt using CFB mode in 8 bit output
+--
+-- Effectively turn a Block cipher in CFB mode into a Stream cipher
+cfb8EncryptOfIO :: BlockCipherIO a => a -> IV a -> B.ByteString -> B.ByteString
+cfb8EncryptOfIO ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
+  where loop d iv@(IV i) m
+            | B.null m  = return ()
+            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
+          where m'  = if B.length m < blockSize ctx
+                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
+                            else B.take (blockSize ctx) m
+                r   = cfbEncryptOfIO ctx iv m'
+                out = B.head r
+                ni  = IV (B.drop 1 i `B.snoc` out)
+
+-- | Decrypt using CFB mode in 8 bit output
+--
+-- Effectively turn a Block cipher in CFB mode into a Stream cipher
+cfb8DecryptOfIO :: BlockCipherIO a => a -> IV a -> B.ByteString -> B.ByteString
+cfb8DecryptOfIO ctx origIv msg = B.unsafeCreate (B.length msg) $ \dst -> loop dst origIv msg
+  where loop d iv@(IV i) m
+            | B.null m  = return ()
+            | otherwise = poke d out >> loop (d `plusPtr` 1) ni (B.drop 1 m)
+          where m'  = if B.length m < blockSize ctx
+                            then m `B.append` B.replicate (blockSize ctx - B.length m) 0
+                            else B.take (blockSize ctx) m
+                r   = cfbDecryptOfIO ctx iv m'
+                out = B.head r
+                ni  = IV (B.drop 1 i `B.snoc` B.head m')
+-}
diff --git a/Crypto/Cipher/Types/Stream.hs b/Crypto/Cipher/Types/Stream.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/Stream.hs
@@ -0,0 +1,20 @@
+-- |
+-- Module      : Crypto.Cipher.Types.Stream
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- stream cipher basic types
+--
+module Crypto.Cipher.Types.Stream
+    ( StreamCipher(..)
+    ) where
+
+import Crypto.Cipher.Types.Base
+import Data.ByteString (ByteString)
+
+-- | Symmetric stream cipher class
+class Cipher cipher => StreamCipher cipher where
+    -- | Combine using the stream cipher
+    streamCombine :: cipher -> ByteString -> (ByteString, cipher)
diff --git a/Crypto/Cipher/Types/Unsafe.hs b/Crypto/Cipher/Types/Unsafe.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/Unsafe.hs
@@ -0,0 +1,20 @@
+-- |
+-- Module      : Crypto.Cipher.Types.Unsafe
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- mutable and unsafe interface for Block ciphers.
+-- export a BlockCipherIO class
+--
+module Crypto.Cipher.Types.Unsafe
+    ( BlockCipherIO(..)
+    , BufferLength
+    , PtrDest
+    , PtrSource
+    , PtrIV
+    , onBlock
+    ) where
+
+import Crypto.Cipher.Types.BlockIO
diff --git a/Crypto/Cipher/Types/Utils.hs b/Crypto/Cipher/Types/Utils.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Types/Utils.hs
@@ -0,0 +1,24 @@
+-- |
+-- Module      : Crypto.Cipher.Types.Utils
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+-- basic utility for cipher related stuff
+--
+module Crypto.Cipher.Types.Utils where
+
+import Data.Bits (xor)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+
+chunk :: Int -> ByteString -> [ByteString]
+chunk sz bs = split bs
+  where split b | B.length b <= sz = [b]
+                | otherwise        =
+                        let (b1, b2) = B.splitAt sz b
+                         in b1 : split b2
+
+bxor :: ByteString -> ByteString -> ByteString
+bxor src dst = B.pack $ B.zipWith xor src dst
diff --git a/crypto-cipher-types.cabal b/crypto-cipher-types.cabal
--- a/crypto-cipher-types.cabal
+++ b/crypto-cipher-types.cabal
@@ -1,5 +1,5 @@
 Name:                crypto-cipher-types
-Version:             0.0.7
+Version:             0.0.8
 Synopsis:            Generic cryptography cipher types
 Description:         Generic cryptography cipher types
 License:             BSD3
@@ -16,7 +16,15 @@
 
 Library
   Exposed-modules:   Crypto.Cipher.Types
+                   , Crypto.Cipher.Types.Unsafe
   Other-modules:     Crypto.Cipher.Types.GF
+                   , Crypto.Cipher.Types.Block
+                   , Crypto.Cipher.Types.BlockIO
+                   , Crypto.Cipher.Types.Stream
+                   , Crypto.Cipher.Types.OfIO
+                   , Crypto.Cipher.Types.AEAD
+                   , Crypto.Cipher.Types.Base
+                   , Crypto.Cipher.Types.Utils
   Build-depends:     base >= 4 && < 5
                    , bytestring
                    , byteable >= 0.1.1
