diff --git a/Crypto/Hash.hs b/Crypto/Hash.hs
--- a/Crypto/Hash.hs
+++ b/Crypto/Hash.hs
@@ -6,7 +6,16 @@
 -- Stability   : experimental
 -- Portability : unknown
 --
--- Crypto hash main module
+-- Generalized cryptographic hash interface, that you can use with cryptographic hash
+-- algorithm that belong to the HashAlgorithm type class.
+--
+-- > import Crypto.Hash
+-- >
+-- > sha1 :: ByteString -> Digest SHA1
+-- > sha1 = hash
+-- >
+-- > hexSha3_512 :: ByteString -> String
+-- > hexSha3_512 bs = show (hash bs :: Digest SHA3_512)
 --
 module Crypto.Hash
     (
diff --git a/Crypto/Hash/Types.hs b/Crypto/Hash/Types.hs
--- a/Crypto/Hash/Types.hs
+++ b/Crypto/Hash/Types.hs
@@ -26,9 +26,12 @@
 --
 -- The hash algorithm is built over 3 primitives:
 --
---   init     : create a new context
---   updates  : update the context with some strict bytestrings
---   finalize : finalize the context into a digest
+-- * init     : create a new hashing context
+--
+-- * updates  : update the hashing context with some strict bytestrings
+--              and return the new context
+--
+-- * finalize : finalize the context into a digest
 --
 class HashAlgorithm a where
     -- | Block size in bytes the hash algorithm operates on
diff --git a/Crypto/MAC.hs b/Crypto/MAC.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/MAC.hs
@@ -0,0 +1,75 @@
+-- |
+-- Module      : Crypto.MAC
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- Crypto hash generic MAC (Message Authentification Code) module
+--
+{-# LANGUAGE BangPatterns #-}
+module Crypto.MAC
+    (
+    -- * MAC algorithms
+      HMAC(..)
+    , hmac
+    , hmacAlg
+    -- ** Incremental MAC algorithms
+    , HMACContext
+    , hmacInit
+    , hmacInitAlg
+    , hmacUpdate
+    , hmacFinalize
+    ) where
+
+import Crypto.Hash
+import Data.ByteString (ByteString)
+import Data.Byteable
+import Data.Bits (xor)
+import qualified Data.ByteString as B
+
+-- -------------------------------------------------------------------------- --
+-- Incremental HMAC
+
+-- | Represent an ongoing HMAC state, that can be appended with 'hmacUpdate'
+-- and finalize to an HMAC with 'hmacFinalize'
+data HMACContext hashalg = HMACContext !(Context hashalg) !(Context hashalg)
+
+-- | Initialize a new incremental HMAC context
+hmacInit :: HashAlgorithm a
+         => ByteString -- ^ Secret key
+         -> HMACContext a
+hmacInit secret = HMACContext octx ictx
+    where ctxInit = hashInit
+          ictx = hashUpdates ctxInit [ipad]
+          octx = hashUpdates ctxInit [opad]
+          ipad = B.map (xor 0x36) k'
+          opad = B.map (xor 0x5c) k'
+
+          k'  = B.append kt pad
+          kt  = if B.length secret > fromIntegral blockSize then toBytes (hashF secret) else secret
+          pad = B.replicate (fromIntegral blockSize - B.length kt) 0
+          hashF = hashFinalize . hashUpdate ctxInit
+          blockSize = hashBlockSize ctxInit
+
+-- | Initialize a new incremental HMAC context with a given hash algorithm.
+hmacInitAlg :: HashAlgorithm a
+            => a           -- ^ the hash algorithm the actual value is unused.
+            -> ByteString  -- ^ Secret key
+            -> HMACContext a
+hmacInitAlg _ secret = hmacInit secret
+
+-- | Incrementally update a HMAC context
+hmacUpdate :: HashAlgorithm a
+           => HMACContext a
+           -> ByteString -- ^ Message to Mac
+           -> HMACContext a
+hmacUpdate (HMACContext octx ictx) msg =
+    HMACContext octx (hashUpdate ictx msg)
+
+-- | Finalize a HMAC context and return the HMAC.
+hmacFinalize :: HashAlgorithm a
+             => HMACContext a
+             -> HMAC a
+hmacFinalize (HMACContext octx ictx) =
+    HMAC $ hashFinalize $ hashUpdates octx [toBytes $ hashFinalize ictx]
diff --git a/Crypto/MAC/HMAC.hs b/Crypto/MAC/HMAC.hs
--- a/Crypto/MAC/HMAC.hs
+++ b/Crypto/MAC/HMAC.hs
@@ -17,6 +17,9 @@
 import Data.Bits (xor)
 
 -- | compute a MAC using the supplied hashing function
+--
+-- An incremental API can be found in the module "Crypto.Hash".
+--
 hmac :: (ByteString -> ByteString) -- ^ hash function
      -> Int -- ^ block size
      -> ByteString -- ^ secret
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2010-2012 Vincent Hanquez <vincent@snarc.org>
+Copyright (c) 2010-2014 Vincent Hanquez <vincent@snarc.org>
 
 All rights reserved.
 
diff --git a/Tests/KAT.hs b/Tests/KAT.hs
--- a/Tests/KAT.hs
+++ b/Tests/KAT.hs
@@ -5,6 +5,8 @@
 import Data.Word
 import Data.ByteString (ByteString)
 import Data.Byteable
+import Data.Foldable (foldl')
+import Data.Monoid (mconcat)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as BC
 import qualified Crypto.Hash.MD2 as MD2
@@ -23,11 +25,11 @@
 import qualified Crypto.Hash.Skein512 as Skein512
 import qualified Crypto.Hash.Whirlpool as Whirlpool
 import Crypto.Hash
+import Crypto.MAC
 
-import Test.Framework (Test, defaultMain, testGroup)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-import Test.Framework.Providers.HUnit
-import Test.HUnit ((@=?))
+import Test.Tasty
+import Test.Tasty.QuickCheck
+import Test.Tasty.HUnit
 
 v0,v1,v2 :: ByteString
 v0 = ""
@@ -35,6 +37,9 @@
 v2 = "The quick brown fox jumps over the lazy cog"
 vectors = [ v0, v1, v2 ]
 
+instance Arbitrary ByteString where
+    arbitrary = B.pack `fmap` arbitrary
+
 data HashFct = HashFct
     { fctHash   :: (B.ByteString -> B.ByteString)
     , fctInc    :: ([B.ByteString] -> B.ByteString) }
@@ -205,10 +210,10 @@
             , testCase (show i ++ " inc 16") (r @=? runtestinc 16 v)
             ]
 
-katTests :: [Test]
+katTests :: [TestTree]
 katTests = map makeTestAlg results
 
-apiTests :: [Test]
+apiTests :: [TestTree]
 apiTests =
     [ testCase "sha1 api" (runhash sha1Hash B.empty @=? show (hash B.empty :: Digest SHA1))
     , testCase "sha256 api" (runhash sha256Hash B.empty @=? show (hash B.empty :: Digest SHA256))
@@ -258,7 +263,7 @@
     [ MACVector sha3_key1 sha3_data1 "\xc2\x96\x2e\x5b\xbe\x12\x38\x00\x78\x52\xf7\x9d\x81\x4d\xbb\xec\xd4\x68\x2e\x6f\x09\x7d\x37\xa3\x63\x58\x7c\x03\xbf\xa2\xeb\x08\x59\xd8\xd9\xc7\x01\xe0\x4c\xec\xec\xfd\x3d\xd7\xbf\xd4\x38\xf2\x0b\x8b\x64\x8e\x01\xbf\x8c\x11\xd2\x68\x24\xb9\x6c\xeb\xbd\xcb"
     ]
 
-macTests :: [Test]
+macTests :: [TestTree]
 macTests =
     [ testGroup "hmac-md5" $ map (toMACTest MD5) $ zip [0..] md5MACVectors
     , testGroup "hmac-sha1" $ map (toMACTest SHA1) $ zip [0..] sha1MACVectors
@@ -271,8 +276,46 @@
     where toMACTest hashAlg (i, macVector) =
             testCase (show i) (macResult macVector @=? toBytes (hmacAlg hashAlg (macKey macVector) (macSecret macVector)))
 
-main = defaultMain
+macIncrementalTests :: [TestTree]
+macIncrementalTests =
+    [ testGroup "hmac-md5" $ map (toMACTest MD5) $ zip [0..] md5MACVectors
+    , testGroup "hmac-sha1" $ map (toMACTest SHA1) $ zip [0..] sha1MACVectors
+    , testGroup "hmac-sha256" $ map (toMACTest SHA256) $ zip [0..] sha256MACVectors
+    , testGroup "hmac-sha3-224" $ map (toMACTest SHA3_224) $ zip [0..] sha3_224_MAC_Vectors
+    , testGroup "hmac-sha3-256" $ map (toMACTest SHA3_256) $ zip [0..] sha3_256_MAC_Vectors
+    , testGroup "hmac-sha3-384" $ map (toMACTest SHA3_384) $ zip [0..] sha3_384_MAC_Vectors
+    , testGroup "hmac-sha3-512" $ map (toMACTest SHA3_512) $ zip [0..] sha3_512_MAC_Vectors
+
+    , testProperty "hmac-md5" $ prop_inc0 MD5
+    , testProperty "hmac-md5" $ prop_inc1 MD5
+    , testProperty "hmac-sha1" $ prop_inc0 SHA1
+    , testProperty "hmac-sha1" $ prop_inc1 SHA1
+    , testProperty "hmac-sha256" $ prop_inc0 SHA256
+    , testProperty "hmac-sha256" $ prop_inc1 SHA256
+    , testProperty "hmac-sha3-224" $ prop_inc0 SHA3_224
+    , testProperty "hmac-sha3-224" $ prop_inc1 SHA3_224
+    , testProperty "hmac-sha3-256" $ prop_inc0 SHA3_256
+    , testProperty "hmac-sha3-256" $ prop_inc1 SHA3_256
+    , testProperty "hmac-sha3-384" $ prop_inc0 SHA3_384
+    , testProperty "hmac-sha3-384" $ prop_inc1 SHA3_384
+    , testProperty "hmac-sha3-512" $ prop_inc0 SHA3_512
+    , testProperty "hmac-sha3-512" $ prop_inc1 SHA3_512
+    ]
+    where toMACTest hashAlg (i, macVector) =
+            testCase (show i) (macResult macVector @=? toBytes (hmacFinalize $ hmacUpdate initCtx (macSecret macVector)))
+              where initCtx = hmacInitAlg hashAlg (macKey macVector)
+
+          prop_inc0 :: HashAlgorithm a => a -> ByteString -> ByteString -> Bool
+          prop_inc0 hashAlg secret msg = hmacFinalize (hmacUpdate initCtx msg) == hmacAlg hashAlg secret msg
+              where initCtx = hmacInitAlg hashAlg secret
+
+          prop_inc1 :: HashAlgorithm a => a -> ByteString -> [ByteString] -> Bool
+          prop_inc1 hashAlg secret msgs = hmacFinalize (foldl' hmacUpdate initCtx msgs) == hmacAlg hashAlg secret (mconcat msgs)
+              where initCtx = hmacInitAlg hashAlg secret
+
+main = defaultMain $ testGroup "cryptohash"
     [ testGroup "KATs" katTests
     , testGroup "API" apiTests
     , testGroup "MACs" macTests
+    , testGroup "Incremental MACs" macIncrementalTests
     ]
diff --git a/cryptohash.cabal b/cryptohash.cabal
--- a/cryptohash.cabal
+++ b/cryptohash.cabal
@@ -1,5 +1,5 @@
 Name:                cryptohash
-Version:             0.11.4
+Version:             0.11.5
 Description:
     A collection of crypto hashes, with a practical incremental and one-pass, pure APIs,
     with performance close to the fastest implementations available in other languages.
@@ -53,6 +53,7 @@
   Extensions:        ForeignFunctionInterface
   Exposed-modules:   Crypto.Hash
                      Crypto.Hash.Types
+                     Crypto.MAC
                      Crypto.Hash.SHA1
                      Crypto.Hash.SHA224
                      Crypto.Hash.SHA256
@@ -98,9 +99,9 @@
                    , byteable
                    , HUnit
                    , QuickCheck >= 2
-                   , test-framework >= 0.3.3
-                   , test-framework-quickcheck2 >= 0.2.9
-                   , test-framework-hunit
+                   , tasty
+                   , tasty-quickcheck
+                   , tasty-hunit
                    , cryptohash
 
 Benchmark bench-hashes
