diff --git a/Crypto/Cipher/Tests.hs b/Crypto/Cipher/Tests.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Tests.hs
@@ -0,0 +1,33 @@
+-- |
+-- Module      : Crypto.Cipher.Tests
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : Stable
+-- Portability : Excellent
+--
+
+{-# LANGUAGE ViewPatterns #-}
+module Crypto.Cipher.Tests
+    ( testBlockCipher
+    -- * KATs
+    , defaultKATs
+    , KATs(..)
+    , KAT_ECB(..)
+    , KAT_CBC(..)
+    , KAT_CTR(..)
+    , KAT_XTS(..)
+    , KAT_AEAD(..)
+    ) where
+
+import Test.Framework (Test, testGroup)
+
+import Crypto.Cipher.Types
+import Crypto.Cipher.Tests.KATs
+import Crypto.Cipher.Tests.Properties
+
+-- | Return tests for a specific blockcipher and a list of KATs
+testBlockCipher :: BlockCipher a => KATs -> a -> Test
+testBlockCipher kats cipher = testGroup (cipherName cipher)
+    (  (if kats == defaultKATs  then [] else [testKATs kats cipher])
+    ++ testModes cipher
+    )
diff --git a/Crypto/Cipher/Tests/KATs.hs b/Crypto/Cipher/Tests/KATs.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Tests/KATs.hs
@@ -0,0 +1,130 @@
+module Crypto.Cipher.Tests.KATs
+    where
+
+import Data.ByteString (ByteString)
+
+import Test.Framework (Test, testGroup)
+import Test.Framework.Providers.HUnit (testCase)
+import Test.HUnit ((@?=))
+import Crypto.Cipher.Types
+import Data.Maybe
+
+-- | ECB KAT
+data KAT_ECB = KAT_ECB
+    { ecbKey        :: ByteString
+    , ecbPlaintext  :: ByteString
+    , ecbCiphertext :: ByteString
+    } deriving (Show,Eq)
+
+-- | CBC KAT
+data KAT_CBC = KAT_CBC
+    { cbcKey        :: ByteString
+    , cbcIV         :: ByteString
+    , cbcPlaintext  :: ByteString
+    , cbcCiphertext :: ByteString
+    } deriving (Show,Eq)
+
+-- | CTR KAT
+data KAT_CTR = KAT_CTR
+    { ctrKey        :: ByteString
+    , ctrIV         :: ByteString
+    , ctrPlaintext  :: ByteString
+    , ctrCiphertext :: ByteString
+    } deriving (Show,Eq)
+
+-- | XTS KAT
+data KAT_XTS = KAT_XTS
+    { xtsKey1       :: ByteString
+    , xtsKey2       :: ByteString
+    , xtsIV         :: ByteString
+    , xtsPlaintext  :: ByteString
+    , xtsCiphertext :: ByteString
+    } deriving (Show,Eq)
+
+-- | AEAD KAT
+data KAT_AEAD = KAT_AEAD
+    { aeadMode       :: AEADMode   -- ^ AEAD mode to use
+    , aeadKey        :: ByteString -- ^ Key
+    , aeadIV         :: ByteString -- ^ IV for initialization
+    , aeadHeader     :: ByteString -- ^ Authentificated Header
+    , aeadPlaintext  :: ByteString -- ^ Plaintext
+    , aeadCiphertext :: ByteString -- ^ Ciphertext
+    , aeadTaglen     :: Int        -- ^ aead tag len
+    , aeadTag        :: AuthTag    -- ^ expected tag
+    } deriving (Show,Eq)
+
+data KATs = KATs
+    { kat_ECB  :: [KAT_ECB]
+    , kat_CBC  :: [KAT_CBC]
+    , kat_CTR  :: [KAT_CTR]
+    , kat_XTS  :: [KAT_XTS]
+    , kat_AEAD :: [KAT_AEAD]
+    } deriving (Show,Eq)
+
+defaultKATs :: KATs
+defaultKATs = KATs
+    { kat_ECB  = []
+    , kat_CBC  = []
+    , kat_CTR  = []
+    , kat_XTS  = []
+    , kat_AEAD = []
+    }
+
+testKATs :: BlockCipher cipher => KATs -> cipher -> Test
+testKATs kats cipher = testGroup "KAT"
+    (   maybeGroup makeECBTest "ECB" (kat_ECB kats)
+     ++ maybeGroup makeCBCTest "CBC" (kat_CBC kats)
+     ++ maybeGroup makeCTRTest "CTR" (kat_CTR kats)
+     ++ maybeGroup makeXTSTest "XTS" (kat_XTS kats)
+     ++ maybeGroup makeAEADTest "AEAD" (kat_AEAD kats)
+    )
+  where makeECBTest i d =
+            [ testCase ("E" ++ i) (ecbEncrypt ctx (ecbPlaintext d) @?= ecbCiphertext d)
+            , testCase ("D" ++ i) (ecbDecrypt ctx (ecbCiphertext d) @?= ecbPlaintext d)
+            ]
+          where ctx = cipherInit (cipherMakeKey cipher $ ecbKey d)
+        makeCBCTest i d =
+            [ testCase ("E" ++ i) (cbcEncrypt ctx iv (cbcPlaintext d) @?= cbcCiphertext d)
+            , testCase ("D" ++ i) (cbcDecrypt ctx iv (cbcCiphertext d) @?= cbcPlaintext d)
+            ]
+          where ctx = cipherInit (cipherMakeKey cipher $ cbcKey d)
+                iv  = cipherMakeIV cipher $ cbcIV d
+        makeCTRTest i d =
+            [ testCase ("E" ++ i) (ctrCombine ctx iv (ctrPlaintext d) @?= ctrCiphertext d)
+            , testCase ("D" ++ i) (ctrCombine ctx iv (ctrCiphertext d) @?= ctrPlaintext d)
+            ]
+          where ctx = cipherInit (cipherMakeKey cipher $ ctrKey d)
+                iv  = cipherMakeIV cipher $ ctrIV d
+        makeXTSTest i d  =
+            [ testCase ("E" ++ i) (xtsEncrypt ctx iv 0 (xtsPlaintext d) @?= xtsCiphertext d)
+            , testCase ("D" ++ i) (xtsDecrypt ctx iv 0 (xtsCiphertext d) @?= xtsPlaintext d)
+            ]
+          where ctx1 = cipherInit (cipherMakeKey cipher $ xtsKey1 d)
+                ctx2 = cipherInit (cipherMakeKey cipher $ xtsKey2 d)
+                ctx  = (ctx1, ctx2)
+                iv   = cipherMakeIV cipher $ xtsIV d
+        makeAEADTest i d =
+            [ testCase ("AE" ++ i) (etag @?= aeadTag d)
+            , testCase ("AD" ++ i) (dtag @?= aeadTag d)
+            , testCase ("E" ++ i)  (ebs @?= aeadCiphertext d)
+            , testCase ("D" ++ i)  (dbs @?= aeadPlaintext d)
+            ]
+          where ctx  = cipherInit (cipherMakeKey cipher $ aeadKey d)
+                (Just aead) = aeadInit (aeadMode d) ctx (aeadIV d)
+                aeadHeaded     = aeadAppendHeader aead (aeadHeader d)
+                (ebs,aeadEFinal) = aeadEncrypt aeadHeaded (aeadPlaintext d)
+                (dbs,aeadDFinal) = aeadDecrypt aeadHeaded (aeadCiphertext d)
+                etag = aeadFinalize aeadEFinal (aeadTaglen d)
+                dtag = aeadFinalize aeadDFinal (aeadTaglen d)
+               
+        maybeGroup mkTest groupName l
+            | null l    = []
+            | otherwise = [testGroup groupName (concatMap (\(i, d) -> mkTest (show i) d) $ zip nbs l)]
+        nbs :: [Int]
+        nbs = [0..]
+        
+        cipherMakeKey :: BlockCipher cipher => cipher -> ByteString -> Key cipher
+        cipherMakeKey _ bs = fromJust $ makeKey bs
+
+        cipherMakeIV :: BlockCipher cipher => cipher -> ByteString -> IV cipher
+        cipherMakeIV _ bs = fromJust $ makeIV bs
diff --git a/Crypto/Cipher/Tests/Properties.hs b/Crypto/Cipher/Tests/Properties.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Tests/Properties.hs
@@ -0,0 +1,144 @@
+{-# LANGUAGE ViewPatterns #-}
+module Crypto.Cipher.Tests.Properties
+    where
+
+import Control.Applicative
+import Control.Monad
+
+import Test.Framework (Test, testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+import Test.QuickCheck
+
+import Crypto.Cipher.Types
+import qualified Data.ByteString as B
+import Data.Byteable
+import Data.Maybe
+
+-- | a ECB unit test
+data ECBUnit a = ECBUnit (Key a) B.ByteString
+    deriving (Eq)
+
+-- | a CBC unit test
+data CBCUnit a = CBCUnit (Key a) (IV a) B.ByteString
+    deriving (Eq)
+
+-- | a CTR unit test
+data CTRUnit a = CTRUnit (Key a) (IV a) B.ByteString
+    deriving (Eq)
+
+-- | a XTS unit test
+data XTSUnit a = XTSUnit (Key a) (Key a) (IV a) B.ByteString
+    deriving (Eq)
+
+-- | a AEAD unit test
+data AEADUnit a = AEADUnit (Key a) B.ByteString B.ByteString B.ByteString
+    deriving (Eq)
+
+instance Show (ECBUnit a) where
+    show (ECBUnit key b) = "ECB(key=" ++ show (toBytes key) ++ ",input=" ++ show b ++ ")"
+instance Show (CBCUnit a) where
+    show (CBCUnit key iv b) = "CBC(key=" ++ show (toBytes key) ++ ",iv=" ++ show (toBytes iv) ++ ",input=" ++ show b ++ ")"
+instance Show (CTRUnit a) where
+    show (CTRUnit key iv b) = "CTR(key=" ++ show (toBytes key) ++ ",iv=" ++ show (toBytes iv) ++ ",input=" ++ show b ++ ")"
+instance Show (XTSUnit a) where
+    show (XTSUnit key1 key2 iv b) = "XTS(key1=" ++ show (toBytes key1) ++ ",key2=" ++ show (toBytes key2) ++ ",iv=" ++ show (toBytes iv) ++ ",input=" ++ show b ++ ")"
+instance Show (AEADUnit a) where
+    show (AEADUnit key iv aad b) = "AEAD(key=" ++ show (toBytes key) ++ ",iv=" ++ show iv ++ ",aad=" ++ show (toBytes aad) ++ ",input=" ++ show b ++ ")"
+
+generateKey :: BlockCipher a => Gen (Key a)
+generateKey = keyFromCipher undefined
+  where keyFromCipher :: BlockCipher a => a -> Gen (Key a)
+        keyFromCipher cipher = case cipherKeySize cipher of
+                                Just sz -> fromJust . makeKey . B.pack <$> replicateM sz arbitrary
+                                Nothing -> fromJust . makeKey . B.pack <$> (choose (1,66) >>= \sz -> replicateM sz arbitrary)
+
+generateIv :: BlockCipher a => Gen (IV a)
+generateIv = ivFromCipher undefined
+  where ivFromCipher :: BlockCipher a => a -> Gen (IV a)
+        ivFromCipher cipher = fromJust . makeIV . B.pack <$> replicateM (blockSize cipher) arbitrary
+
+generateIvAEAD :: Gen B.ByteString
+generateIvAEAD = choose (12,90) >>= \sz -> (B.pack <$> replicateM sz arbitrary)
+
+generatePlaintextMultiple16 :: Gen B.ByteString
+generatePlaintextMultiple16 = choose (1,128) >>= \size -> replicateM (size*16) arbitrary >>= return . B.pack
+
+generatePlaintext :: Gen B.ByteString
+generatePlaintext = choose (0,324) >>= \size -> replicateM size arbitrary >>= return . B.pack
+
+instance BlockCipher a => Arbitrary (ECBUnit a) where
+    arbitrary = ECBUnit <$> generateKey
+                        <*> generatePlaintextMultiple16
+
+instance BlockCipher a => Arbitrary (CBCUnit a) where
+    arbitrary = CBCUnit <$> generateKey
+                        <*> generateIv
+                        <*> generatePlaintextMultiple16
+
+instance BlockCipher a => Arbitrary (CTRUnit a) where
+    arbitrary = CTRUnit <$> generateKey
+                        <*> generateIv
+                        <*> generatePlaintext
+
+instance BlockCipher a => Arbitrary (XTSUnit a) where
+    arbitrary = XTSUnit <$> generateKey
+                        <*> generateKey
+                        <*> generateIv
+                        <*> generatePlaintextMultiple16
+
+instance BlockCipher a => Arbitrary (AEADUnit a) where
+    arbitrary = AEADUnit <$> generateKey
+                         <*> generateIvAEAD
+                         <*> generatePlaintext
+                         <*> generatePlaintext
+
+testModes :: BlockCipher a => a -> [Test]
+testModes cipher =
+    [ testGroup "decrypt.encrypt==id"
+        [ testProperty "ECB" ecbProp
+        , testProperty "CBC" cbcProp
+        , testProperty "CTR" ctrProp
+        , testProperty "XTS" xtsProp
+        , testProperty "OCB" (aeadProp AEAD_OCB)
+        , testProperty "CCM" (aeadProp AEAD_CCM)
+        , testProperty "EAX" (aeadProp AEAD_EAX)
+        , testProperty "CWC" (aeadProp AEAD_CWC)
+        , testProperty "GCM" (aeadProp AEAD_GCM)
+        ]
+    ]
+  where (ecbProp,cbcProp,ctrProp,xtsProp,aeadProp) = toTests cipher
+        toTests :: BlockCipher a
+                => a
+                -> ((ECBUnit a -> Bool), (CBCUnit a -> Bool), (CTRUnit a -> Bool), (XTSUnit a -> Bool), (AEADMode -> AEADUnit a -> Bool))
+        toTests _ = (testProperty_ECB
+                    ,testProperty_CBC
+                    ,testProperty_CTR
+                    ,testProperty_XTS
+                    ,testProperty_AEAD
+                    )
+        testProperty_ECB (ECBUnit (cipherInit -> ctx) plaintext) =
+            plaintext `assertEq` ecbDecrypt ctx (ecbEncrypt ctx plaintext)
+
+        testProperty_CBC (CBCUnit (cipherInit -> ctx) testIV plaintext) =
+            plaintext `assertEq` cbcDecrypt ctx testIV (cbcEncrypt ctx testIV plaintext)
+
+        testProperty_CTR (CTRUnit (cipherInit -> ctx) testIV plaintext) =
+            plaintext `assertEq` ctrCombine ctx testIV (ctrCombine ctx testIV plaintext)
+
+        testProperty_XTS (XTSUnit (cipherInit -> ctx1) (cipherInit -> ctx2) testIV plaintext) =
+            plaintext `assertEq` xtsDecrypt (ctx1, ctx2) testIV 0 (xtsEncrypt (ctx1, ctx2) testIV 0 plaintext)
+
+        testProperty_AEAD mode (AEADUnit (cipherInit -> ctx) testIV aad plaintext) =
+            case aeadInit mode ctx testIV of
+                Just iniAead ->
+                    let aead           = aeadAppendHeader iniAead aad
+                        (eText, aeadE) = aeadEncrypt aead plaintext
+                        (dText, aeadD) = aeadDecrypt aead eText
+                        eTag           = aeadFinalize aeadE (blockSize ctx)
+                        dTag           = aeadFinalize aeadD (blockSize ctx)
+                     in (plaintext `assertEq` dText) && (toBytes eTag `assertEq` toBytes dTag)
+                Nothing -> True
+
+assertEq :: B.ByteString -> B.ByteString -> Bool
+assertEq b1 b2 | b1 /= b2  = error ("b1: " ++ show b1 ++ " b2: " ++ show b2)
+               | otherwise = True
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013 Vincent Hanquez <vincent@snarc.org>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/crypto-cipher-tests.cabal b/crypto-cipher-tests.cabal
new file mode 100644
--- /dev/null
+++ b/crypto-cipher-tests.cabal
@@ -0,0 +1,36 @@
+Name:                crypto-cipher-tests
+Version:             0.0.1
+Synopsis:            Generic cryptography cipher tests
+Description:         Generic cryptography cipher tests
+License:             BSD3
+License-file:        LICENSE
+Copyright:           Vincent Hanquez <vincent@snarc.org>
+Author:              Vincent Hanquez <vincent@snarc.org>
+Maintainer:          vincent@snarc.org
+Category:            Cryptography
+Stability:           experimental
+Build-Type:          Simple
+Homepage:            http://github.com/vincenthz/hs-crypto-cipher
+Cabal-Version:       >=1.8
+
+Library
+  Exposed-modules:   Crypto.Cipher.Tests
+  Other-modules:     Crypto.Cipher.Tests.KATs
+                     Crypto.Cipher.Tests.Properties
+  Build-depends:     base >= 3 && < 5
+                   , QuickCheck >= 2
+                   , mtl
+                   , HUnit
+                   , test-framework
+                   , test-framework-quickcheck2
+                   , test-framework-hunit
+                   , bytestring
+                   , byteable >= 0.1.1
+                   , securemem >= 0.1.1
+                   , crypto-cipher-types
+  ghc-options:       -Wall -fwarn-tabs
+
+source-repository head
+  type: git
+  location: git://github.com/vincenthz/hs-crypto-cipher
+  subdir: tests
