diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.23
+
+* Digest memory usage improvement by using unpinned memory
+* Fix generateBetween to generate within the right bounds
+* Add pure Twofish implementation
+* Fix memory allocation in P256 when using a temp point
+* Consolidate hash benchmark code
+* Add Nat-length Blake2 support (GHC > 8.0)
+* Update tutorial
+
 ## 0.22
 
 * Add Argon2 (Password Hashing Competition winner) hash function
diff --git a/Crypto/Cipher/AES.hs b/Crypto/Cipher/AES.hs
--- a/Crypto/Cipher/AES.hs
+++ b/Crypto/Cipher/AES.hs
@@ -14,6 +14,7 @@
 
 import Crypto.Error
 import Crypto.Cipher.Types
+import Crypto.Cipher.Utils
 import Crypto.Cipher.Types.Block
 import Crypto.Cipher.AES.Primitive
 import Crypto.Internal.Imports
@@ -47,15 +48,6 @@
     cipherKeySize _ = KeySizeFixed 32
     cipherInit k    = AES256 <$> (initAES =<< validateKeySize (undefined :: AES256) k)
 
-validateKeySize :: (ByteArrayAccess key, Cipher cipher) => cipher -> key -> CryptoFailable key
-validateKeySize c k = if validKeyLength
-                      then CryptoPassed k
-                      else CryptoFailed CryptoError_KeySizeInvalid
-  where keyLength = BA.length k
-        validKeyLength = case cipherKeySize c of
-          KeySizeRange low high -> keyLength >= low && keyLength <= high
-          KeySizeEnum lengths -> keyLength `elem` lengths
-          KeySizeFixed s -> keyLength == s
 
 #define INSTANCE_BLOCKCIPHER(CSTR) \
 instance BlockCipher CSTR where \
diff --git a/Crypto/Cipher/Twofish.hs b/Crypto/Cipher/Twofish.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Twofish.hs
@@ -0,0 +1,46 @@
+module Crypto.Cipher.Twofish
+    ( Twofish128
+    , Twofish192
+    , Twofish256
+    ) where
+
+import Crypto.Cipher.Twofish.Primitive
+import Crypto.Cipher.Types
+import Crypto.Cipher.Utils
+import Crypto.Internal.Imports
+
+newtype Twofish128 = Twofish128 Twofish
+
+instance Cipher Twofish128 where
+    cipherName    _ = "Twofish128"
+    cipherKeySize _ = KeySizeFixed 16
+    cipherInit key  = Twofish128 <$> (initTwofish =<< validateKeySize (undefined :: Twofish128) key)
+
+instance BlockCipher Twofish128 where
+    blockSize                 _ = 16
+    ecbEncrypt (Twofish128 key) = encrypt key
+    ecbDecrypt (Twofish128 key) = decrypt key
+
+newtype Twofish192 = Twofish192 Twofish
+
+instance Cipher Twofish192 where
+    cipherName    _ = "Twofish192"
+    cipherKeySize _ = KeySizeFixed 24
+    cipherInit key  = Twofish192 <$> (initTwofish =<< validateKeySize (undefined :: Twofish192) key)
+
+instance BlockCipher Twofish192 where
+    blockSize                 _ = 16
+    ecbEncrypt (Twofish192 key) = encrypt key
+    ecbDecrypt (Twofish192 key) = decrypt key
+
+newtype Twofish256 = Twofish256 Twofish
+
+instance Cipher Twofish256 where
+    cipherName    _ = "Twofish256"
+    cipherKeySize _ = KeySizeFixed 32
+    cipherInit key  = Twofish256 <$> (initTwofish =<< validateKeySize (undefined :: Twofish256) key)
+
+instance BlockCipher Twofish256 where
+    blockSize                 _ = 16
+    ecbEncrypt (Twofish256 key) = encrypt key
+    ecbDecrypt (Twofish256 key) = decrypt key
diff --git a/Crypto/Cipher/Twofish/Primitive.hs b/Crypto/Cipher/Twofish/Primitive.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Twofish/Primitive.hs
@@ -0,0 +1,314 @@
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE BangPatterns #-}
+module Crypto.Cipher.Twofish.Primitive
+    ( Twofish
+    , initTwofish
+    , encrypt
+    , decrypt
+    ) where
+
+import           Crypto.Error
+import           Crypto.Internal.ByteArray (ByteArrayAccess, ByteArray, Bytes)
+import qualified Crypto.Internal.ByteArray as B
+import           Crypto.Internal.WordArray
+import           Crypto.Internal.Words
+import           Data.Word
+import           Data.Int
+import           Data.Bits
+import           Data.List
+import           Control.Monad
+
+-- Based on the Golang referance implementation
+-- https://github.com/golang/crypto/blob/master/twofish/twofish.go
+
+
+-- BlockSize is the constant block size of Twofish.
+blockSize :: Int
+blockSize = 16
+
+mdsPolynomial, rsPolynomial :: Word32
+mdsPolynomial = 0x169 -- x^8 + x^6 + x^5 + x^3 + 1, see [TWOFISH] 4.2
+rsPolynomial = 0x14d  -- x^8 + x^6 + x^3 + x^2 + 1, see [TWOFISH] 4.3
+
+data Twofish = Twofish { s :: (Array32, Array32, Array32, Array32)
+                       , k :: Array32 }
+
+data ByteSize = Bytes16 | Bytes24 | Bytes32 deriving (Eq)
+
+data KeyPackage ba = KeyPackage { rawKeyBytes :: ba
+                                , byteSize :: ByteSize }
+
+buildPackage :: ByteArray ba => ba -> Maybe (KeyPackage ba)
+buildPackage key
+    | B.length key == 16 = return $ KeyPackage key Bytes16
+    | B.length key == 24 = return $ KeyPackage key Bytes24
+    | B.length key == 32 = return $ KeyPackage key Bytes32
+    | otherwise = Nothing
+
+-- | Initialize a 128-bit, 192-bit, or 256-bit key
+--
+-- Return the initialized key or a error message if the given
+-- keyseed was not 16-bytes in length.
+initTwofish :: ByteArray key
+            => key -- ^ The key to create the twofish context
+            -> CryptoFailable Twofish
+initTwofish key =
+    case buildPackage key of Nothing -> CryptoFailed CryptoError_KeySizeInvalid
+                             Just keyPackage -> CryptoPassed Twofish { k = generatedK, s = generatedS }
+                                  where generatedK = array32 40 $ genK keyPackage
+                                        generatedS = genSboxes keyPackage $ sWords key
+
+mapBlocks :: ByteArray ba => (ba -> ba) -> ba -> ba
+mapBlocks operation input
+    | B.null rest = blockOutput
+    | otherwise = blockOutput `B.append` mapBlocks operation rest
+        where (block, rest) = B.splitAt blockSize input
+              blockOutput = operation block
+
+-- | Encrypts the given ByteString using the given Key
+encrypt :: ByteArray ba
+        => Twofish     -- ^ The key to use
+        -> ba           -- ^ The data to encrypt
+        -> ba
+encrypt cipher = mapBlocks (encryptBlock cipher)
+
+encryptBlock :: ByteArray ba => Twofish -> ba -> ba
+encryptBlock Twofish { s = (s1, s2, s3, s4), k = ks } message = store32ls ts
+    where (a, b, c, d) = load32ls message
+          a' = a `xor` arrayRead32 ks 0
+          b' = b `xor` arrayRead32 ks 1
+          c' = c `xor` arrayRead32 ks 2
+          d' = d `xor` arrayRead32 ks 3
+          (!a'', !b'', !c'', !d'') = foldl' shuffle (a', b', c', d') [0..7]
+          ts = (c'' `xor` arrayRead32 ks 4, d'' `xor` arrayRead32 ks 5, a'' `xor` arrayRead32 ks 6, b'' `xor` arrayRead32 ks 7)
+
+          shuffle :: (Word32, Word32, Word32, Word32) -> Int -> (Word32, Word32, Word32, Word32)
+          shuffle (!retA, !retB, !retC, !retD) ind = (retA', retB', retC', retD')
+            where [k0, k1, k2, k3] = fmap (\offset -> arrayRead32 ks $ (8 + 4 * ind) + offset) [0..3]
+                  t2 = byteIndex s2 retB `xor` byteIndex s3 (shiftR retB 8) `xor` byteIndex s4 (shiftR retB 16) `xor` byteIndex s1 (shiftR retB 24)
+                  t1 = (byteIndex s1 retA `xor` byteIndex s2 (shiftR retA 8) `xor` byteIndex s3 (shiftR retA 16) `xor` byteIndex s4 (shiftR retA 24)) + t2
+                  retC' = rotateR (retC `xor` (t1 + k0)) 1
+                  retD' = rotateL retD 1 `xor` (t1 + t2 + k1)
+                  t2' = byteIndex s2 retD' `xor` byteIndex s3 (shiftR retD' 8) `xor` byteIndex s4 (shiftR retD' 16) `xor` byteIndex s1 (shiftR retD' 24)
+                  t1' = (byteIndex s1 retC' `xor` byteIndex s2 (shiftR retC' 8) `xor` byteIndex s3 (shiftR retC' 16) `xor` byteIndex s4 (shiftR retC' 24)) + t2'
+                  retA' = rotateR (retA `xor` (t1' + k2)) 1
+                  retB' = rotateL retB 1 `xor` (t1' + t2' + k3)
+
+-- Unsafe, no bounds checking
+byteIndex :: Array32 -> Word32 -> Word32
+byteIndex xs ind  = arrayRead32 xs $ fromIntegral byte
+    where byte = ind `mod` 256
+
+-- | Decrypts the given ByteString using the given Key
+decrypt :: ByteArray ba
+        => Twofish     -- ^ The key to use
+        -> ba           -- ^ The data to decrypt
+        -> ba
+decrypt cipher = mapBlocks (decryptBlock cipher)
+
+{- decryption for 128 bits blocks -}
+decryptBlock :: ByteArray ba => Twofish -> ba -> ba
+decryptBlock Twofish { s = (s1, s2, s3, s4), k = ks } message = store32ls ixs
+    where (a, b, c, d) = load32ls message
+          a' = c `xor` arrayRead32 ks 6
+          b' = d `xor` arrayRead32 ks 7
+          c' = a `xor` arrayRead32 ks 4
+          d' = b `xor` arrayRead32 ks 5
+          (!a'', !b'', !c'', !d'') = foldl' unshuffle (a', b', c', d') [8, 7..1]
+          ixs = (a'' `xor` arrayRead32 ks 0, b'' `xor` arrayRead32 ks 1, c'' `xor` arrayRead32 ks 2, d'' `xor` arrayRead32 ks 3)
+
+          unshuffle :: (Word32, Word32, Word32, Word32) -> Int -> (Word32, Word32, Word32, Word32)
+          unshuffle (!retA, !retB, !retC, !retD) ind = (retA', retB', retC', retD')
+            where [k0, k1, k2, k3] = fmap (\offset -> arrayRead32 ks $ (4 + 4 * ind) + offset) [0..3]
+                  t2 = byteIndex s2 retD `xor` byteIndex s3 (shiftR retD 8) `xor` byteIndex s4 (shiftR retD 16) `xor` byteIndex s1 (shiftR retD 24)
+                  t1 = (byteIndex s1 retC `xor` byteIndex s2 (shiftR retC 8) `xor` byteIndex s3 (shiftR retC 16) `xor` byteIndex s4 (shiftR retC 24)) + t2
+                  retA' = rotateL retA 1 `xor` (t1 + k2)
+                  retB' = rotateR (retB `xor` (t2 + t1 + k3)) 1
+                  t2' = byteIndex s2 retB' `xor` byteIndex s3 (shiftR retB' 8) `xor` byteIndex s4 (shiftR retB' 16) `xor` byteIndex s1 (shiftR retB' 24)
+                  t1' = (byteIndex s1 retA' `xor` byteIndex s2 (shiftR retA' 8) `xor` byteIndex s3 (shiftR retA' 16) `xor` byteIndex s4 (shiftR retA' 24)) + t2'
+                  retC' = rotateL retC 1 `xor` (t1' + k0)
+                  retD' = rotateR (retD `xor` (t2' + t1' + k1)) 1
+
+sbox0 :: Int -> Word8
+sbox0 = arrayRead8 t
+    where t = array8
+            "\xa9\x67\xb3\xe8\x04\xfd\xa3\x76\x9a\x92\x80\x78\xe4\xdd\xd1\x38\
+            \\x0d\xc6\x35\x98\x18\xf7\xec\x6c\x43\x75\x37\x26\xfa\x13\x94\x48\
+            \\xf2\xd0\x8b\x30\x84\x54\xdf\x23\x19\x5b\x3d\x59\xf3\xae\xa2\x82\
+            \\x63\x01\x83\x2e\xd9\x51\x9b\x7c\xa6\xeb\xa5\xbe\x16\x0c\xe3\x61\
+            \\xc0\x8c\x3a\xf5\x73\x2c\x25\x0b\xbb\x4e\x89\x6b\x53\x6a\xb4\xf1\
+            \\xe1\xe6\xbd\x45\xe2\xf4\xb6\x66\xcc\x95\x03\x56\xd4\x1c\x1e\xd7\
+            \\xfb\xc3\x8e\xb5\xe9\xcf\xbf\xba\xea\x77\x39\xaf\x33\xc9\x62\x71\
+            \\x81\x79\x09\xad\x24\xcd\xf9\xd8\xe5\xc5\xb9\x4d\x44\x08\x86\xe7\
+            \\xa1\x1d\xaa\xed\x06\x70\xb2\xd2\x41\x7b\xa0\x11\x31\xc2\x27\x90\
+            \\x20\xf6\x60\xff\x96\x5c\xb1\xab\x9e\x9c\x52\x1b\x5f\x93\x0a\xef\
+            \\x91\x85\x49\xee\x2d\x4f\x8f\x3b\x47\x87\x6d\x46\xd6\x3e\x69\x64\
+            \\x2a\xce\xcb\x2f\xfc\x97\x05\x7a\xac\x7f\xd5\x1a\x4b\x0e\xa7\x5a\
+            \\x28\x14\x3f\x29\x88\x3c\x4c\x02\xb8\xda\xb0\x17\x55\x1f\x8a\x7d\
+            \\x57\xc7\x8d\x74\xb7\xc4\x9f\x72\x7e\x15\x22\x12\x58\x07\x99\x34\
+            \\x6e\x50\xde\x68\x65\xbc\xdb\xf8\xc8\xa8\x2b\x40\xdc\xfe\x32\xa4\
+            \\xca\x10\x21\xf0\xd3\x5d\x0f\x00\x6f\x9d\x36\x42\x4a\x5e\xc1\xe0"#
+
+sbox1 :: Int -> Word8
+sbox1 = arrayRead8 t
+    where t = array8
+            "\x75\xf3\xc6\xf4\xdb\x7b\xfb\xc8\x4a\xd3\xe6\x6b\x45\x7d\xe8\x4b\
+            \\xd6\x32\xd8\xfd\x37\x71\xf1\xe1\x30\x0f\xf8\x1b\x87\xfa\x06\x3f\
+            \\x5e\xba\xae\x5b\x8a\x00\xbc\x9d\x6d\xc1\xb1\x0e\x80\x5d\xd2\xd5\
+            \\xa0\x84\x07\x14\xb5\x90\x2c\xa3\xb2\x73\x4c\x54\x92\x74\x36\x51\
+            \\x38\xb0\xbd\x5a\xfc\x60\x62\x96\x6c\x42\xf7\x10\x7c\x28\x27\x8c\
+            \\x13\x95\x9c\xc7\x24\x46\x3b\x70\xca\xe3\x85\xcb\x11\xd0\x93\xb8\
+            \\xa6\x83\x20\xff\x9f\x77\xc3\xcc\x03\x6f\x08\xbf\x40\xe7\x2b\xe2\
+            \\x79\x0c\xaa\x82\x41\x3a\xea\xb9\xe4\x9a\xa4\x97\x7e\xda\x7a\x17\
+            \\x66\x94\xa1\x1d\x3d\xf0\xde\xb3\x0b\x72\xa7\x1c\xef\xd1\x53\x3e\
+            \\x8f\x33\x26\x5f\xec\x76\x2a\x49\x81\x88\xee\x21\xc4\x1a\xeb\xd9\
+            \\xc5\x39\x99\xcd\xad\x31\x8b\x01\x18\x23\xdd\x1f\x4e\x2d\xf9\x48\
+            \\x4f\xf2\x65\x8e\x78\x5c\x58\x19\x8d\xe5\x98\x57\x67\x7f\x05\x64\
+            \\xaf\x63\xb6\xfe\xf5\xb7\x3c\xa5\xce\xe9\x68\x44\xe0\x4d\x43\x69\
+            \\x29\x2e\xac\x15\x59\xa8\x0a\x9e\x6e\x47\xdf\x34\x35\x6a\xcf\xdc\
+            \\x22\xc9\xc0\x9b\x89\xd4\xed\xab\x12\xa2\x0d\x52\xbb\x02\x2f\xa9\
+            \\xd7\x61\x1e\xb4\x50\x04\xf6\xc2\x16\x25\x86\x56\x55\x09\xbe\x91"#
+
+rs :: [[Word8]]
+rs = [ [0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E]
+     , [0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5]
+     , [0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19]
+     , [0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03] ]
+
+
+
+load32ls :: ByteArray ba => ba -> (Word32, Word32, Word32, Word32)
+load32ls message = (intify q1, intify q2, intify q3, intify q4)
+    where (half1, half2) = B.splitAt 8 message
+          (q1, q2) = B.splitAt 4 half1
+          (q3, q4) = B.splitAt 4 half2
+
+          intify :: ByteArray ba => ba -> Word32
+          intify bytes = foldl' (\int (!word, !ind) -> int .|. shiftL (fromIntegral word) (ind * 8) ) 0 (zip (B.unpack bytes) [0..])
+
+store32ls :: ByteArray ba => (Word32, Word32, Word32, Word32) -> ba
+store32ls (a, b, c, d) = B.pack $ concatMap splitWordl [a, b, c, d]
+    where splitWordl :: Word32 -> [Word8]
+          splitWordl w = fmap (\ind -> fromIntegral $ shiftR w (8 * ind)) [0..3]
+
+
+-- Create S words
+sWords :: ByteArray ba => ba -> [Word8]
+sWords key = sWord
+    where word64Count = B.length key `div` 2
+          sWord = concatMap (\wordIndex ->
+                        map (\rsRow ->
+                            foldl' (\acc (!rsVal, !colIndex) ->
+                                acc `xor` gfMult rsPolynomial (B.index key $ 8 * wordIndex + colIndex) rsVal
+                                ) 0 (zip rsRow [0..])
+                            ) rs
+                    ) [0..word64Count - 1]
+
+data Column = Zero | One | Two | Three deriving (Show, Eq, Enum, Bounded)
+
+genSboxes :: ByteArray ba => KeyPackage ba -> [Word8] -> (Array32, Array32, Array32, Array32)
+genSboxes keyPackage ws = (mkArray b0', mkArray b1', mkArray b2', mkArray b3')
+    where range = [0..255]
+          mkArray = array32 256
+          [w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15] = take 16 ws
+          (b0', b1', b2', b3') = sboxBySize $ byteSize keyPackage
+
+          sboxBySize :: ByteSize -> ([Word32], [Word32], [Word32], [Word32])
+          sboxBySize Bytes16 = (b0, b1, b2, b3)
+            where !b0 = fmap mapper range
+                    where mapper :: Int -> Word32
+                          mapper byte = mdsColumnMult ((sbox1 . fromIntegral) ((sbox0 . fromIntegral $ sbox0 byte `xor` w0) `xor` w4)) Zero
+                  !b1 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox0 . fromIntegral) ((sbox0 . fromIntegral $ sbox1 byte `xor` w1) `xor` w5)) One
+                  !b2 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox1 . fromIntegral) ((sbox1 . fromIntegral $ sbox0 byte `xor` w2) `xor` w6)) Two
+                  !b3 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox0 . fromIntegral) ((sbox1 . fromIntegral $ sbox1 byte `xor` w3) `xor` w7)) Three
+
+          sboxBySize Bytes24 = (b0, b1, b2, b3)
+            where !b0 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox1 . fromIntegral) ((sbox0 . fromIntegral) ((sbox0 . fromIntegral $ sbox1 byte `xor` w0) `xor` w4) `xor` w8)) Zero
+                  !b1 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox0 . fromIntegral) ((sbox0 . fromIntegral) ((sbox1 . fromIntegral $ sbox1 byte `xor` w1) `xor` w5) `xor` w9)) One
+                  !b2 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox1 . fromIntegral) ((sbox1 . fromIntegral) ((sbox0 . fromIntegral $ sbox0 byte `xor` w2) `xor` w6) `xor` w10)) Two
+                  !b3 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox0 . fromIntegral) ((sbox1 . fromIntegral) ((sbox1 . fromIntegral $ sbox0 byte `xor` w3) `xor` w7) `xor` w11)) Three
+
+          sboxBySize Bytes32 = (b0, b1, b2, b3)
+            where !b0 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox1 . fromIntegral) ((sbox0 . fromIntegral) ((sbox0 . fromIntegral) ((sbox1 . fromIntegral $ sbox1 byte `xor` w0) `xor` w4) `xor` w8) `xor` w12)) Zero
+                  !b1 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox0 . fromIntegral) ((sbox0 . fromIntegral) ((sbox1 . fromIntegral) ((sbox1 . fromIntegral $ sbox0 byte `xor` w1) `xor` w5) `xor` w9) `xor` w13)) One
+                  !b2 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox1 . fromIntegral) ((sbox1 . fromIntegral) ((sbox0 . fromIntegral) ((sbox0 . fromIntegral $ sbox0 byte `xor` w2) `xor` w6) `xor` w10) `xor` w14)) Two
+                  !b3 = fmap mapper range
+                    where mapper byte = mdsColumnMult ((sbox0 . fromIntegral) ((sbox1 . fromIntegral) ((sbox1 . fromIntegral) ((sbox0 . fromIntegral $ sbox1 byte `xor` w3) `xor` w7) `xor` w11) `xor` w15)) Three
+
+genK :: (ByteArray ba) => KeyPackage ba -> [Word32]
+genK keyPackage = concatMap makeTuple [0..19]
+    where makeTuple :: Word8 -> [Word32]
+          makeTuple idx = [a + b', rotateL (2 * b' + a) 9]
+            where tmp1 = replicate 4 $ 2 * idx
+                  tmp2 = fmap (+1) tmp1
+                  a = h tmp1 keyPackage 0
+                  b = h tmp2 keyPackage 1
+                  b' = rotateL b 8
+
+h :: (ByteArray ba) => [Word8] -> KeyPackage ba -> Int -> Word32
+h input keyPackage offset =  foldl' xorMdsColMult 0 $ zip [y0f, y1f, y2f, y3f] $ enumFrom Zero
+    where key = rawKeyBytes keyPackage
+          [y0, y1, y2, y3] = take 4 input
+          (!y0f, !y1f, !y2f, !y3f) = run (y0, y1, y2, y3) $ byteSize keyPackage
+
+          run :: (Word8, Word8, Word8, Word8) -> ByteSize -> (Word8, Word8, Word8, Word8)
+          run (!y0'', !y1'', !y2'', !y3'') Bytes32 = run (y0', y1', y2', y3') Bytes24
+            where y0' = sbox1 (fromIntegral y0'') `xor` B.index key (4 * (6 + offset) + 0)
+                  y1' = sbox0 (fromIntegral y1'') `xor` B.index key (4 * (6 + offset) + 1)
+                  y2' = sbox0 (fromIntegral y2'') `xor` B.index key (4 * (6 + offset) + 2)
+                  y3' = sbox1 (fromIntegral y3'') `xor` B.index key (4 * (6 + offset) + 3)
+
+          run (!y0'', !y1'', !y2'', !y3'') Bytes24 = run (y0', y1', y2', y3') Bytes16
+            where y0' = sbox1 (fromIntegral y0'') `xor` B.index key (4 * (4 + offset) + 0)
+                  y1' = sbox1 (fromIntegral y1'') `xor` B.index key (4 * (4 + offset) + 1)
+                  y2' = sbox0 (fromIntegral y2'') `xor` B.index key (4 * (4 + offset) + 2)
+                  y3' = sbox0 (fromIntegral y3'') `xor` B.index key (4 * (4 + offset) + 3)
+
+          run (!y0'', !y1'', !y2'', !y3'') Bytes16 = (y0', y1', y2', y3')
+            where y0' = sbox1 . fromIntegral $ (sbox0 . fromIntegral $ (sbox0 (fromIntegral y0'') `xor` B.index key (4 * (2 + offset) + 0))) `xor` B.index key (4 * (0 + offset) + 0)
+                  y1' = sbox0 . fromIntegral $ (sbox0 . fromIntegral $ (sbox1 (fromIntegral y1'') `xor` B.index key (4 * (2 + offset) + 1))) `xor` B.index key (4 * (0 + offset) + 1)
+                  y2' = sbox1 . fromIntegral $ (sbox1 . fromIntegral $ (sbox0 (fromIntegral y2'') `xor` B.index key (4 * (2 + offset) + 2))) `xor` B.index key (4 * (0 + offset) + 2)
+                  y3' = sbox0 . fromIntegral $ (sbox1 . fromIntegral $ (sbox1 (fromIntegral y3'') `xor` B.index key (4 * (2 + offset) + 3))) `xor` B.index key (4 * (0 + offset) + 3)
+
+          xorMdsColMult :: Word32 -> (Word8, Column) -> Word32
+          xorMdsColMult acc wordAndIndex = acc `xor` uncurry mdsColumnMult wordAndIndex
+
+mdsColumnMult :: Word8 -> Column -> Word32
+mdsColumnMult !byte !col =
+    case col of Zero  -> input .|. rotateL mul5B 8 .|. rotateL mulEF 16 .|. rotateL mulEF 24
+                One   -> mulEF .|. rotateL mulEF 8 .|. rotateL mul5B 16 .|. rotateL input 24
+                Two   -> mul5B .|. rotateL mulEF 8 .|. rotateL input 16 .|. rotateL mulEF 24
+                Three -> mul5B .|. rotateL input 8 .|. rotateL mulEF 16 .|. rotateL mul5B 24
+        where input = fromIntegral byte
+              mul5B = fromIntegral $ gfMult mdsPolynomial byte 0x5B
+              mulEF = fromIntegral $ gfMult mdsPolynomial byte 0xEF
+
+tupInd :: (Bits b) => b -> (a, a) -> a
+tupInd b
+    | testBit b 0 = snd
+    | otherwise = fst
+
+gfMult :: Word32 -> Word8 -> Word8 -> Word8
+gfMult p a b = fromIntegral $ run a b' p' result 0
+    where b' = (0, fromIntegral b)
+          p' = (0, p)
+          result = 0
+
+          run :: Word8 -> (Word32, Word32) -> (Word32, Word32) -> Word32 -> Int -> Word32
+          run a' b'' p'' result' count =
+            if count == 7
+            then result''
+            else run a'' b''' p'' result'' (count + 1)
+                where result'' = result' `xor` tupInd (a' .&. 1) b''
+                      a'' = shiftR a' 1
+                      b''' = (fst b'', tupInd (shiftR (snd b'') 7) p'' `xor` shiftL (snd b'') 1)
diff --git a/Crypto/Cipher/Utils.hs b/Crypto/Cipher/Utils.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/Utils.hs
@@ -0,0 +1,19 @@
+module Crypto.Cipher.Utils
+    ( validateKeySize
+    ) where
+
+import Crypto.Error
+import Crypto.Cipher.Types
+import Crypto.Internal.Imports
+
+import Data.ByteArray as BA
+
+validateKeySize :: (ByteArrayAccess key, Cipher cipher) => cipher -> key -> CryptoFailable key
+validateKeySize c k = if validKeyLength
+                      then CryptoPassed k
+                      else CryptoFailed CryptoError_KeySizeInvalid
+  where keyLength = BA.length k
+        validKeyLength = case cipherKeySize c of
+          KeySizeRange low high -> keyLength >= low && keyLength <= high
+          KeySizeEnum lengths -> keyLength `elem` lengths
+          KeySizeFixed s -> keyLength == s
diff --git a/Crypto/Hash.hs b/Crypto/Hash.hs
--- a/Crypto/Hash.hs
+++ b/Crypto/Hash.hs
@@ -16,6 +16,8 @@
 -- > hexSha3_512 :: ByteString -> String
 -- > hexSha3_512 bs = show (hash bs :: Digest SHA3_512)
 --
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE BangPatterns        #-}
 module Crypto.Hash
     (
     -- * Types
@@ -56,44 +58,37 @@
 hashlazy lbs = hashFinalize $ hashUpdates hashInit (L.toChunks lbs)
 
 -- | Initialize a new context for this hash algorithm
-hashInit :: HashAlgorithm a
-         => Context a
-hashInit = doInit undefined B.allocAndFreeze
-  where
-        doInit :: HashAlgorithm a => a -> (Int -> (Ptr (Context a) -> IO ()) -> B.Bytes) -> Context a
-        doInit alg alloc = Context $ alloc (hashInternalContextSize alg) hashInternalInit
-{-# NOINLINE hashInit #-}
+hashInit :: forall a . HashAlgorithm a => Context a
+hashInit = Context $ B.allocAndFreeze (hashInternalContextSize (undefined :: a)) $ \(ptr :: Ptr (Context a)) ->
+    hashInternalInit ptr
 
 -- | run hashUpdates on one single bytestring and return the updated context.
 hashUpdate :: (ByteArrayAccess ba, HashAlgorithm a) => Context a -> ba -> Context a
-hashUpdate ctx b = hashUpdates ctx [b]
+hashUpdate ctx b
+    | B.null b  = ctx
+    | otherwise = hashUpdates ctx [b]
 
 -- | Update the context with a list of strict bytestring,
 -- and return a new context with the updates.
-hashUpdates :: (HashAlgorithm a, ByteArrayAccess ba)
+hashUpdates :: forall a ba . (HashAlgorithm a, ByteArrayAccess ba)
             => Context a
             -> [ba]
             -> Context a
-hashUpdates c l = doUpdates (B.copyAndFreeze c)
-  where doUpdates :: HashAlgorithm a => ((Ptr (Context a) -> IO ()) -> B.Bytes) -> Context a
-        doUpdates copy = Context $ copy $ \ctx ->
-            mapM_ (\b -> B.withByteArray b $ \d -> hashInternalUpdate ctx d (fromIntegral $ B.length b)) l
-{-# NOINLINE hashUpdates #-}
+hashUpdates c l
+    | null ls   = c
+    | otherwise = Context $ B.copyAndFreeze c $ \(ctx :: Ptr (Context a)) ->
+        mapM_ (\b -> B.withByteArray b $ \d -> hashInternalUpdate ctx d (fromIntegral $ B.length b)) ls
+  where
+    ls = filter (not . B.null) l
 
 -- | Finalize a context and return a digest.
-hashFinalize :: HashAlgorithm a
+hashFinalize :: forall a . HashAlgorithm a
              => Context a
              -> Digest a
-hashFinalize c = doFinalize undefined (B.copy c) (B.allocAndFreeze)
-  where doFinalize :: HashAlgorithm alg
-                   => alg
-                   -> ((Ptr (Context alg) -> IO ()) -> IO B.Bytes)
-                   -> (Int -> (Ptr (Digest alg)  -> IO ()) -> B.Bytes)
-                   -> Digest alg
-        doFinalize alg copy allocDigest =
-            Digest $ allocDigest (hashDigestSize alg) $ \dig ->
-                (void $ copy $ \ctx -> hashInternalFinalize ctx dig)
-{-# NOINLINE hashFinalize #-}
+hashFinalize !c =
+    Digest $ B.allocAndFreeze (hashDigestSize (undefined :: a)) $ \(dig :: Ptr (Digest a)) -> do
+        ((!_) :: B.Bytes) <- B.copy c $ \(ctx :: Ptr (Context a)) -> hashInternalFinalize ctx dig
+        return ()
 
 -- | Initialize a new context for a specified hash algorithm
 hashInitWith :: HashAlgorithm alg => alg -> Context alg
diff --git a/Crypto/Hash/Algorithms.hs b/Crypto/Hash/Algorithms.hs
--- a/Crypto/Hash/Algorithms.hs
+++ b/Crypto/Hash/Algorithms.hs
@@ -45,6 +45,8 @@
 #if MIN_VERSION_base(4,7,0)
     , SHAKE128(..)
     , SHAKE256(..)
+    , Blake2b(..), Blake2bp(..)
+    , Blake2s(..), Blake2sp(..)
 #endif
     , Skein256_224(..)
     , Skein256_256(..)
@@ -78,4 +80,5 @@
 import           Crypto.Hash.Whirlpool
 #if MIN_VERSION_base(4,7,0)
 import           Crypto.Hash.SHAKE
+import           Crypto.Hash.Blake2
 #endif
diff --git a/Crypto/Hash/Blake2.hs b/Crypto/Hash/Blake2.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Hash/Blake2.hs
@@ -0,0 +1,151 @@
+-- |
+-- Module      : Crypto.Hash.Blake2
+-- License     : BSD-style
+-- Maintainer  : Nicolas Di Prima <nicolas@primetype.co.uk>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- module containing the binding functions to work with the
+-- Blake2
+--
+-- Implementation based from [RFC7693](https://tools.ietf.org/html/rfc7693)
+--
+-- Please consider the following when chosing a hash:
+--
+--      Algorithm     | Target | Collision | Digest Size |
+--         Identifier |  Arch  |  Security |   in bytes  |
+--     ---------------+--------+-----------+-------------+
+--      id-blake2b160 | 64-bit |   2**80   |         20  |
+--      id-blake2b256 | 64-bit |   2**128  |         32  |
+--      id-blake2b384 | 64-bit |   2**192  |         48  |
+--      id-blake2b512 | 64-bit |   2**256  |         64  |
+--     ---------------+--------+-----------+-------------+
+--      id-blake2s128 | 32-bit |   2**64   |         16  |
+--      id-blake2s160 | 32-bit |   2**80   |         20  |
+--      id-blake2s224 | 32-bit |   2**112  |         28  |
+--      id-blake2s256 | 32-bit |   2**128  |         32  |
+--     ---------------+--------+-----------+-------------+
+--
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeFamilies #-}
+module Crypto.Hash.Blake2
+    ( Blake2s(..)
+    , Blake2sp(..)
+    , Blake2b(..)
+    , Blake2bp(..)
+    ) where
+
+import           Crypto.Hash.Types
+import           Foreign.Ptr (Ptr)
+import           Data.Data
+import           Data.Typeable
+import           Data.Word (Word8, Word32)
+import           GHC.TypeLits (Nat, KnownNat, natVal)
+import           Crypto.Internal.Nat
+
+-- | Fast and secure alternative to SHA1 and HMAC-SHA1
+--
+-- It is espacially known to target 32bits architectures.
+--
+-- known supported digest sizes:
+--
+-- * Blake2s 160
+-- * Blake2s 224
+-- * Blake2s 256
+--
+data Blake2s (bitlen :: Nat) = Blake2s
+  deriving (Show, Typeable)
+
+instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 256)
+      => HashAlgorithm (Blake2s bitlen)
+      where
+    hashBlockSize  _          = 64
+    hashDigestSize _          = byteLen (Proxy :: Proxy bitlen)
+    hashInternalContextSize _ = 185
+    hashInternalInit p        = c_blake2s_init p (integralNatVal (Proxy :: Proxy bitlen))
+    hashInternalUpdate        = c_blake2s_update
+    hashInternalFinalize p    = c_blake2s_finalize p (integralNatVal (Proxy :: Proxy bitlen))
+
+foreign import ccall unsafe "cryptonite_blake2s_init"
+    c_blake2s_init :: Ptr (Context a) -> Word32 -> IO ()
+foreign import ccall "cryptonite_blake2s_update"
+    c_blake2s_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
+foreign import ccall unsafe "cryptonite_blake2s_finalize"
+    c_blake2s_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
+
+-- | Fast cryptographic hash.
+--
+-- It is especially known to target 64bits architectures.
+--
+-- Known supported digest sizes:
+--
+-- * Blake2b 160
+-- * Blake2b 224
+-- * Blake2b 256
+-- * Blake2b 384
+-- * Blake2b 512
+--
+data Blake2b (bitlen :: Nat) = Blake2b
+  deriving (Show, Typeable)
+
+instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 512)
+      => HashAlgorithm (Blake2b bitlen)
+      where
+    hashBlockSize  _          = 128
+    hashDigestSize _          = byteLen (Proxy :: Proxy bitlen)
+    hashInternalContextSize _ = 361
+    hashInternalInit p        = c_blake2b_init p (integralNatVal (Proxy :: Proxy bitlen))
+    hashInternalUpdate        = c_blake2b_update
+    hashInternalFinalize p    = c_blake2b_finalize p (integralNatVal (Proxy :: Proxy bitlen))
+
+foreign import ccall unsafe "cryptonite_blake2b_init"
+    c_blake2b_init :: Ptr (Context a) -> Word32 -> IO ()
+foreign import ccall "cryptonite_blake2b_update"
+    c_blake2b_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
+foreign import ccall unsafe "cryptonite_blake2b_finalize"
+    c_blake2b_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
+
+data Blake2sp (bitlen :: Nat) = Blake2sp
+  deriving (Show, Typeable)
+
+instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 256)
+      => HashAlgorithm (Blake2sp bitlen)
+      where
+    hashBlockSize  _          = 64
+    hashDigestSize _          = byteLen (Proxy :: Proxy bitlen)
+    hashInternalContextSize _ = 2185
+    hashInternalInit p        = c_blake2sp_init p (integralNatVal (Proxy :: Proxy bitlen))
+    hashInternalUpdate        = c_blake2sp_update
+    hashInternalFinalize p    = c_blake2sp_finalize p (integralNatVal (Proxy :: Proxy bitlen))
+
+foreign import ccall unsafe "cryptonite_blake2sp_init"
+    c_blake2sp_init :: Ptr (Context a) -> Word32 -> IO ()
+foreign import ccall "cryptonite_blake2sp_update"
+    c_blake2sp_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
+foreign import ccall unsafe "cryptonite_blake2sp_finalize"
+    c_blake2sp_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
+
+data Blake2bp (bitlen :: Nat) = Blake2bp
+  deriving (Show, Typeable)
+
+instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 512)
+      => HashAlgorithm (Blake2bp bitlen)
+      where
+    hashBlockSize  _          = 128
+    hashDigestSize _          = byteLen (Proxy :: Proxy bitlen)
+    hashInternalContextSize _ = 2325
+    hashInternalInit p        = c_blake2bp_init p (integralNatVal (Proxy :: Proxy bitlen))
+    hashInternalUpdate        = c_blake2bp_update
+    hashInternalFinalize p    = c_blake2bp_finalize p (integralNatVal (Proxy :: Proxy bitlen))
+
+
+foreign import ccall unsafe "cryptonite_blake2bp_init"
+    c_blake2bp_init :: Ptr (Context a) -> Word32 -> IO ()
+foreign import ccall "cryptonite_blake2bp_update"
+    c_blake2bp_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
+foreign import ccall unsafe "cryptonite_blake2bp_finalize"
+    c_blake2bp_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/IO.hs b/Crypto/Hash/IO.hs
--- a/Crypto/Hash/IO.hs
+++ b/Crypto/Hash/IO.hs
@@ -8,6 +8,7 @@
 -- Generalized impure cryptographic hash interface
 --
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
 module Crypto.Hash.IO
     ( HashAlgorithm(..)
     , MutableContext
@@ -51,18 +52,10 @@
                 hashInternalUpdate ctx d (fromIntegral $ B.length dat)
 
 -- | Finalize a mutable hash context and compute a digest
-hashMutableFinalize :: HashAlgorithm a => MutableContext a -> IO (Digest a)
-hashMutableFinalize mc = doFinalize undefined (B.withByteArray mc) B.alloc
-  where doFinalize :: HashAlgorithm alg
-                   => alg
-                   -> ((Ptr (Context alg) -> IO ()) -> IO ())
-                   -> (Int -> (Ptr (Digest alg)  -> IO ()) -> IO B.Bytes)
-                   -> IO (Digest alg)
-        doFinalize alg withCtx allocDigest = do
-            b <- allocDigest (hashDigestSize alg) $ \dig ->
-                    withCtx $ \ctx ->
-                        hashInternalFinalize ctx dig
-            return $ Digest b
+hashMutableFinalize :: forall a . HashAlgorithm a => MutableContext a -> IO (Digest a)
+hashMutableFinalize mc = do
+    b <- B.alloc (hashDigestSize (undefined :: a)) $ \dig -> B.withByteArray mc $ \(ctx :: Ptr (Context a)) -> hashInternalFinalize ctx dig
+    return $ Digest b
 
 -- | Reset the mutable context to the initial state of the hash
 hashMutableReset :: HashAlgorithm a => MutableContext a -> IO ()
diff --git a/Crypto/Hash/SHAKE.hs b/Crypto/Hash/SHAKE.hs
--- a/Crypto/Hash/SHAKE.hs
+++ b/Crypto/Hash/SHAKE.hs
@@ -73,10 +73,7 @@
                     -> IO ()
 shakeFinalizeOutput d ctx dig = do
     c_sha3_finalize_shake ctx
-    c_sha3_output ctx dig (fromInteger (natVal d `div` 8))
-
-byteLen :: (KnownNat bitlen, IsDivisibleBy8 bitlen, Num a) => proxy bitlen -> a
-byteLen d = fromInteger (natVal d `div` 8)
+    c_sha3_output ctx dig (byteLen d)
 
 foreign import ccall unsafe "cryptonite_sha3_init"
     c_sha3_init :: Ptr (Context a) -> Word32 -> IO ()
diff --git a/Crypto/Hash/Types.hs b/Crypto/Hash/Types.hs
--- a/Crypto/Hash/Types.hs
+++ b/Crypto/Hash/Types.hs
@@ -18,6 +18,8 @@
 import           Crypto.Internal.ByteArray (ByteArrayAccess, Bytes)
 import qualified Crypto.Internal.ByteArray as B
 import           Foreign.Ptr (Ptr)
+import qualified Foundation.Array as F
+import qualified Foundation       as F
 
 -- | Class representing hashing algorithms.
 --
@@ -50,8 +52,11 @@
     deriving (ByteArrayAccess,NFData)
 
 -- | Represent a digest for a given hash algorithm.
-newtype Digest a = Digest Bytes
-    deriving (Eq,Ord,ByteArrayAccess,NFData)
+newtype Digest a = Digest (F.UArray Word8)
+    deriving (Eq,Ord,ByteArrayAccess)
+
+instance NFData (Digest a) where
+    rnf (Digest u) = u `F.deepseq` ()
 
 instance Show (Digest a) where
     show (Digest bs) = map (toEnum . fromIntegral)
diff --git a/Crypto/Internal/Nat.hs b/Crypto/Internal/Nat.hs
--- a/Crypto/Internal/Nat.hs
+++ b/Crypto/Internal/Nat.hs
@@ -6,9 +6,48 @@
 {-# LANGUAGE UndecidableInstances #-}
 module Crypto.Internal.Nat
     ( type IsDivisibleBy8
+    , type IsAtMost, type IsAtLeast
+    , byteLen
+    , integralNatVal
     ) where
 
 import           GHC.TypeLits
+
+byteLen :: (KnownNat bitlen, IsDivisibleBy8 bitlen, Num a) => proxy bitlen -> a
+byteLen d = fromInteger (natVal d `div` 8)
+
+integralNatVal :: (KnownNat bitlen, Num a) => proxy bitlen -> a
+integralNatVal = fromInteger . natVal
+
+type family IsLE (bitlen :: Nat) (n :: Nat) (c :: Bool) where
+    IsLE bitlen n 'True  = 'True
+#if MIN_VERSION_base(4,9,0)
+    IsLE bitlen n 'False = TypeError
+      (     ('Text "bitlen " ':<>: 'ShowType bitlen ':<>: 'Text " is greater than " ':<>: 'ShowType n)
+      ':$$: ('Text "You have tried to use an invalid Digest size. Please, refer to the documentation.")
+      )
+#else
+    IsLE bitlen n 'False = 'False
+#endif
+
+-- | ensure the given `bitlen` is lesser or equal to `n`
+--
+type IsAtMost  (bitlen :: Nat) (n :: Nat) = IsLE bitlen n (bitlen <=? n) ~ 'True
+
+type family IsGE (bitlen :: Nat) (n :: Nat) (c :: Bool) where
+    IsGE bitlen n 'True  = 'True
+#if MIN_VERSION_base(4,9,0)
+    IsGE bitlen n 'False = TypeError
+      (     ('Text "bitlen " ':<>: 'ShowType bitlen ':<>: 'Text " is lesser than " ':<>: 'ShowType n)
+      ':$$: ('Text "You have tried to use an invalid Digest size. Please, refer to the documentation.")
+      )
+#else
+    IsGE bitlen n 'False = 'False
+#endif
+
+-- | ensure the given `bitlen` is greater or equal to `n`
+--
+type IsAtLeast (bitlen :: Nat) (n :: Nat) = IsGE bitlen n (n <=? bitlen) ~ 'True
 
 type family IsDiv8 (bitLen :: Nat) (n :: Nat) where
     IsDiv8 bitLen 0 = 'True
diff --git a/Crypto/KDF/Argon2.hs b/Crypto/KDF/Argon2.hs
--- a/Crypto/KDF/Argon2.hs
+++ b/Crypto/KDF/Argon2.hs
@@ -85,8 +85,10 @@
 outputMinLength :: Int
 outputMinLength = 4
 
+-- specification allows up to 2^32-1 but this is too big for a signed Int
+-- on a 32-bit architecture, so we limit tag length to 2^31-1 bytes
 outputMaxLength :: Int
-outputMaxLength = 0xffffffff
+outputMaxLength = 0x7fffffff
 
 defaultOptions :: Options
 defaultOptions =
diff --git a/Crypto/Number/Generate.hs b/Crypto/Number/Generate.hs
--- a/Crypto/Number/Generate.hs
+++ b/Crypto/Number/Generate.hs
@@ -120,6 +120,4 @@
 
 -- | generate a number between the inclusive bound [low,high].
 generateBetween :: MonadRandom m => Integer -> Integer -> m Integer
-generateBetween low high
-    | low == 1  = generateMax high >>= \r -> if r == 0 then generateBetween low high else return r
-    | otherwise = (low +) <$> generateMax (high - low + 1)
+generateBetween low high = (low +) <$> generateMax (high - low + 1)
diff --git a/Crypto/PubKey/ECC/P256.hs b/Crypto/PubKey/ECC/P256.hs
--- a/Crypto/PubKey/ECC/P256.hs
+++ b/Crypto/PubKey/ECC/P256.hs
@@ -292,7 +292,7 @@
 {-# NOINLINE withNewScalarFreeze #-}
 
 withTempPoint :: (Ptr P256X -> Ptr P256Y -> IO a) -> IO a
-withTempPoint f = allocTempScrubbed scalarSize (\p -> let px = castPtr p in f px (pxToPy px))
+withTempPoint f = allocTempScrubbed pointSize (\p -> let px = castPtr p in f px (pxToPy px))
 
 withTempScalar :: (Ptr P256Scalar -> IO a) -> IO a
 withTempScalar f = allocTempScrubbed scalarSize (f . castPtr)
diff --git a/Crypto/Tutorial.hs b/Crypto/Tutorial.hs
--- a/Crypto/Tutorial.hs
+++ b/Crypto/Tutorial.hs
@@ -1,34 +1,65 @@
-{-# OPTIONS_GHC -fno-warn-unused-imports #-}
-{-# LANGUAGE OverloadedStrings #-}
 
-{-| How to use @cryptonite@
+{- How to use @cryptonite@ with symmetric block ciphers 
 
-> -- | Beware MUST BE 256bits as we use AES256
-> import Data.ByteString (ByteString)
-> import Crypto.Cipher.AES (AES256)
-> import Crypto.Cipher.Types (BlockCipher(..), Cipher(..),nullIV)
-> import Crypto.Error (CryptoFailable(..))
->
-> secretKey :: ByteString
-> secretKey = "012-456-89A-CDE-012-456-89A-CDE-"
->
-> encrypt :: ByteString -> ByteString -> ByteString
-> encrypt secret = ctrCombine ctx nullIV
->   where
->     ctx = cipherInitNoErr (cipherMakeKey (undefined :: AES256) secret)
->     cipherInitNoErr :: BlockCipher c => Key c -> c
->     cipherInitNoErr (Key k) = case cipherInit k of
->       CryptoPassed a -> a
->       CryptoFailed e -> error (show e)
->     cipherMakeKey :: Cipher cipher => cipher -> ByteString -> Key cipher
->     cipherMakeKey _ = Key -- Yeah Lazyness!!!!!!
->
->
-> decrypt :: ByteString -> ByteString -> ByteString
+> {-# LANGUAGE OverloadedStrings #-}
+> {-# LANGUAGE ScopedTypeVariables #-}
+> {-# LANGUAGE GADTs #-}
+> 
+> import           Crypto.Cipher.AES (AES256)
+> import           Crypto.Cipher.Types (BlockCipher(..), Cipher(..), nullIV, KeySizeSpecifier(..), IV, makeIV)
+> import           Crypto.Error (CryptoFailable(..), CryptoError(..))
+> 
+> import qualified Crypto.Random.Types as CRT
+> 
+> import           Data.ByteArray (ByteArray)
+> import           Data.ByteString (ByteString)
+> 
+> -- | Not required, but most general implementation
+> data Key c a where
+>   Key :: (BlockCipher c, ByteArray a) => a -> Key c a 
+> 
+> -- | Generates a string of bytes (key) of a specific length for a given block cipher 
+> genSecretKey :: forall m c a. (CRT.MonadRandom m, BlockCipher c, ByteArray a) => c -> Int -> m (Key c a) 
+> genSecretKey _ = fmap Key . CRT.getRandomBytes 
+>   
+> -- | Generate a random initialization vector for a given block cipher
+> genRandomIV :: forall m c. (CRT.MonadRandom m, BlockCipher c) => c -> m (Maybe (IV c)) 
+> genRandomIV _ = do
+>   bytes :: ByteString <- CRT.getRandomBytes $ blockSize (undefined :: c) 
+>   return $ makeIV bytes 
+> 
+> -- | Initialize a block cipher
+> initCipher :: (BlockCipher c, ByteArray a) => Key c a -> Either CryptoError c
+> initCipher (Key k) = case cipherInit k of
+>   CryptoFailed e -> Left e
+>   CryptoPassed a -> Right a
+> 
+> encrypt :: (BlockCipher c, ByteArray a) => Key c a -> IV c -> a -> Either CryptoError a
+> encrypt secretKey initIV msg = 
+>   case initCipher secretKey of
+>     Left e -> Left e
+>     Right c -> Right $ ctrCombine c initIV msg
+>     
+> decrypt :: (BlockCipher c, ByteArray a) => Key c a -> IV c -> a -> Either CryptoError a 
 > decrypt = encrypt
+> 
+> exampleAES256 :: ByteString -> IO ()
+> exampleAES256 msg = do
+>   -- secret key needs 256 bits (32 * 8) 
+>   secretKey <- genSecretKey (undefined :: AES256) 32
+>   mInitIV <- genRandomIV (undefined :: AES256) 
+>   case mInitIV of
+>     Nothing -> error "Failed to generate and initialization vector."
+>     Just initIV -> do
+>       let encryptedMsg = encrypt secretKey initIV msg
+>           decryptedMsg = decrypt secretKey initIV =<< encryptedMsg
+>       case (,) <$> encryptedMsg <*> decryptedMsg of
+>         Left err -> error $ show err
+>         Right (eMsg, dMsg) -> do
+>           putStrLn $ "Original Message: " ++ show msg
+>           putStrLn $ "Message after encryption: " ++ show eMsg 
+>           putStrLn $ "Message after decryption: " ++ show dMsg
 
 |-}
 
-module Crypto.Tutorial () where
-
-import Crypto.Cipher.Types
+module Crypto.Tutorial where
diff --git a/benchs/Bench.hs b/benchs/Bench.hs
new file mode 100644
--- /dev/null
+++ b/benchs/Bench.hs
@@ -0,0 +1,185 @@
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ExistentialQuantification #-}
+module Main where
+
+import Criterion.Main
+
+import           Crypto.Cipher.AES
+import           Crypto.Cipher.Blowfish
+import qualified Crypto.Cipher.ChaChaPoly1305 as CP
+import           Crypto.Cipher.DES
+import           Crypto.Cipher.Types
+import           Crypto.Error
+import           Crypto.Hash
+import qualified Crypto.KDF.PBKDF2 as PBKDF2
+import qualified Crypto.PubKey.ECC.Types as ECC
+import qualified Crypto.PubKey.ECC.Prim as ECC
+import           Crypto.Random
+
+import           Data.ByteArray (ByteArray, Bytes)
+import qualified Data.ByteString as B
+
+import           System.IO.Unsafe (unsafePerformIO)
+
+import Number.F2m
+
+data HashAlg = forall alg . HashAlgorithm alg => HashAlg alg
+
+benchHash =
+    [ bgroup "1KB" $ map (doHashBench oneKB) hashAlgs
+    , bgroup "1MB" $ map (doHashBench oneMB) hashAlgs
+    ]
+  where
+    doHashBench b (name, HashAlg alg) = bench name $ nf (hashWith alg) b
+
+    oneKB :: Bytes
+    oneKB = unsafePerformIO (getRandomBytes 1024)
+    {-# NOINLINE oneKB #-}
+
+    oneMB :: Bytes
+    oneMB = unsafePerformIO (getRandomBytes $ 1024 * 1024)
+    {-# NOINLINE oneMB #-}
+
+    hashAlgs =
+        [ ("MD2", HashAlg MD2)
+        , ("MD4", HashAlg MD4)
+        , ("MD5", HashAlg MD5)
+        , ("SHA1", HashAlg SHA1)
+        , ("SHA224", HashAlg SHA224)
+        , ("SHA256", HashAlg SHA256)
+        , ("SHA384", HashAlg SHA384)
+        , ("SHA512", HashAlg SHA512)
+        , ("SHA512t_224", HashAlg SHA512t_224)
+        , ("SHA512t_256", HashAlg SHA512t_256)
+        , ("RIPEMD160", HashAlg RIPEMD160)
+        , ("Tiger", HashAlg Tiger)
+        --, ("Skein256-160", HashAlg Skein256_160)
+        , ("Skein256-256", HashAlg Skein256_256)
+        --, ("Skein512-160", HashAlg Skein512_160)
+        , ("Skein512-384", HashAlg Skein512_384)
+        , ("Skein512-512", HashAlg Skein512_512)
+        --, ("Skein512-896", HashAlg Skein512_896)
+        , ("Whirlpool", HashAlg Whirlpool)
+        , ("Keccak-224", HashAlg Keccak_224)
+        , ("Keccak-256", HashAlg Keccak_256)
+        , ("Keccak-384", HashAlg Keccak_384)
+        , ("Keccak-512", HashAlg Keccak_512)
+        , ("SHA3-224", HashAlg SHA3_224)
+        , ("SHA3-256", HashAlg SHA3_256)
+        , ("SHA3-384", HashAlg SHA3_384)
+        , ("SHA3-512", HashAlg SHA3_512)
+        , ("Blake2b-160", HashAlg Blake2b_160)
+        , ("Blake2b-224", HashAlg Blake2b_224)
+        , ("Blake2b-256", HashAlg Blake2b_256)
+        , ("Blake2b-384", HashAlg Blake2b_384)
+        , ("Blake2b-512", HashAlg Blake2b_512)
+        , ("Blake2s-160", HashAlg Blake2s_160)
+        , ("Blake2s-224", HashAlg Blake2s_224)
+        , ("Blake2s-256", HashAlg Blake2s_256)
+        ]
+
+benchPBKDF2 =
+    [ bgroup "64"
+        [ bench "cryptonite-PBKDF2-100-64" $ nf (pbkdf2 64) 100
+        , bench "cryptonite-PBKDF2-1000-64" $ nf (pbkdf2 64) 1000
+        , bench "cryptonite-PBKDF2-10000-64" $ nf (pbkdf2 64) 10000
+        ]
+    , bgroup "128"
+        [ bench "cryptonite-PBKDF2-100-128" $ nf (pbkdf2 128) 100
+        , bench "cryptonite-PBKDF2-1000-128" $ nf (pbkdf2 128) 1000
+        , bench "cryptonite-PBKDF2-10000-128" $ nf (pbkdf2 128) 10000
+        ]
+    ]
+  where
+        pbkdf2 :: Int -> Int -> B.ByteString
+        pbkdf2 n iter = PBKDF2.generate (PBKDF2.prfHMAC SHA512) (params n iter) mypass mysalt
+
+        mypass, mysalt :: B.ByteString
+        mypass = "password"
+        mysalt = "salt"
+
+        params n iter = PBKDF2.Parameters iter n
+
+
+benchBlockCipher =
+    [ bgroup "ECB" benchECB
+    , bgroup "CBC" benchCBC
+    ]
+  where 
+        benchECB =
+            [ bench "DES-input=1024" $ nf (run (undefined :: DES) cipherInit key8) input1024
+            , bench "Blowfish128-input=1024" $ nf (run (undefined :: Blowfish128) cipherInit key16) input1024
+            , bench "AES128-input=1024" $ nf (run (undefined :: AES128) cipherInit key16) input1024
+            , bench "AES256-input=1024" $ nf (run (undefined :: AES256) cipherInit key32) input1024
+            ]
+          where run :: (ByteArray ba, ByteArray key, BlockCipher c)
+                    => c -> (key -> CryptoFailable c) -> key -> ba -> ba
+                run witness initF key input =
+                    (ecbEncrypt (throwCryptoError (initF key))) input
+
+        benchCBC =
+            [ bench "DES-input=1024" $ nf (run (undefined :: DES) cipherInit key8 iv8) input1024
+            , bench "Blowfish128-input=1024" $ nf (run (undefined :: Blowfish128) cipherInit key16 iv8) input1024
+            , bench "AES128-input=1024" $ nf (run (undefined :: AES128) cipherInit key16 iv16) input1024
+            , bench "AES256-input=1024" $ nf (run (undefined :: AES256) cipherInit key32 iv16) input1024
+            ]
+          where run :: (ByteArray ba, ByteArray key, BlockCipher c)
+                    => c -> (key -> CryptoFailable c) -> key -> IV c -> ba -> ba
+                run witness initF key iv input =
+                    (cbcEncrypt (throwCryptoError (initF key))) iv input
+
+        key8  = B.replicate 8 0
+        key16 = B.replicate 16 0
+        key32 = B.replicate 32 0
+        input1024 = B.replicate 1024 0
+
+        iv8 :: BlockCipher c => IV c
+        iv8  = maybe (error "iv size 8") id  $ makeIV key8
+
+        iv16 :: BlockCipher c => IV c
+        iv16 = maybe (error "iv size 16") id $ makeIV key16
+
+benchAE =
+    [ bench "ChaChaPoly1305" $ nf (run key32) (input64, input1024)
+    ]
+  where run k (ini, plain) =
+            let iniState            = throwCryptoError $ CP.initialize k (throwCryptoError $ CP.nonce12 nonce12)
+                afterAAD            = CP.finalizeAAD (CP.appendAAD ini iniState)
+                (out, afterEncrypt) = CP.encrypt plain afterAAD
+                outtag              = CP.finalize afterEncrypt
+             in (out, outtag)
+
+        input64 = B.replicate 64 0
+        input1024 = B.replicate 1024 0
+
+        nonce12 :: B.ByteString
+        nonce12 = B.replicate 12 0
+
+        key32 = B.replicate 32 0
+
+benchECC =
+    [ bench "pointAddTwoMuls-baseline"  $ nf run_b (n1, p1, n2, p2)
+    , bench "pointAddTwoMuls-optimized" $ nf run_o (n1, p1, n2, p2)
+    ]
+  where run_b (n, p, k, q) = ECC.pointAdd c (ECC.pointMul c n p)
+                                            (ECC.pointMul c k q)
+
+        run_o (n, p, k, q) = ECC.pointAddTwoMuls c n p k q
+
+        c  = ECC.getCurveByName ECC.SEC_p256r1
+        r1 = 7
+        r2 = 11
+        p1 = ECC.pointBaseMul c r1
+        p2 = ECC.pointBaseMul c r2
+        n1 = 0x2ba9daf2363b2819e69b34a39cf496c2458a9b2a21505ea9e7b7cbca42dc7435
+        n2 = 0xf054a7f60d10b8c2cf847ee90e9e029f8b0e971b09ca5f55c4d49921a11fadc1
+
+main = defaultMain
+    [ bgroup "hash" benchHash
+    , bgroup "block-cipher" benchBlockCipher
+    , bgroup "AE" benchAE
+    , bgroup "pbkdf2" benchPBKDF2
+    , bgroup "ECC" benchECC
+    , bgroup "F2m" benchF2m
+    ]
diff --git a/benchs/Number/F2m.hs b/benchs/Number/F2m.hs
new file mode 100644
--- /dev/null
+++ b/benchs/Number/F2m.hs
@@ -0,0 +1,53 @@
+{-# LANGUAGE PackageImports #-}
+
+module Number.F2m (benchF2m) where
+
+import Criterion.Main
+import System.Random
+
+import Crypto.Number.Basic (log2)
+import Crypto.Number.F2m
+
+genInteger :: Int -> Int -> Integer
+genInteger salt bits
+    = head
+    . dropWhile ((< bits) . log2)
+    . scanl (\a r -> a * 2^(31 :: Int) + abs r) 0
+    . randoms
+    . mkStdGen
+    $ salt + bits
+
+benchMod :: Int -> Benchmark
+benchMod bits = bench (show bits) $ nf (modF2m m) a
+  where
+    m = genInteger 0 bits
+    a = genInteger 1 (2 * bits)
+
+benchMul :: Int -> Benchmark
+benchMul bits = bench (show bits) $ nf (mulF2m m a) b
+  where
+    m = genInteger 0 bits
+    a = genInteger 1 bits
+    b = genInteger 2 bits
+
+benchSquare :: Int -> Benchmark
+benchSquare bits = bench (show bits) $ nf (squareF2m m) a
+  where
+    m = genInteger 0 bits
+    a = genInteger 1 bits
+
+benchInv :: Int -> Benchmark
+benchInv bits = bench (show bits) $ nf (invF2m m) a
+  where
+    m = genInteger 0 bits
+    a = genInteger 1 bits
+
+bitsList :: [Int]
+bitsList = [64, 128, 256, 512, 1024, 2048]
+
+benchF2m =
+    [ bgroup    "modF2m" $ map benchMod    bitsList
+    , bgroup    "mulF2m" $ map benchMul    bitsList
+    , bgroup "squareF2m" $ map benchSquare bitsList
+    , bgroup    "invF2m" $ map benchInv    bitsList
+    ]
diff --git a/cryptonite.cabal b/cryptonite.cabal
--- a/cryptonite.cabal
+++ b/cryptonite.cabal
@@ -1,5 +1,5 @@
 Name:                cryptonite
-Version:             0.22
+Version:             0.23
 Synopsis:            Cryptography Primitives sink
 Description:
     A repository of cryptographic primitives.
@@ -108,7 +108,9 @@
                      Crypto.Cipher.RC4
                      Crypto.Cipher.Salsa
                      Crypto.Cipher.TripleDES
+                     Crypto.Cipher.Twofish
                      Crypto.Cipher.Types
+                     Crypto.Cipher.Utils
                      Crypto.Cipher.XSalsa
                      Crypto.ConstructHash.MiyaguchiPreneel
                      Crypto.Data.AFIS
@@ -165,6 +167,7 @@
                      Crypto.Cipher.Blowfish.Primitive
                      Crypto.Cipher.Camellia.Primitive
                      Crypto.Cipher.DES.Primitive
+                     Crypto.Cipher.Twofish.Primitive
                      Crypto.Cipher.Types.AEAD
                      Crypto.Cipher.Types.Base
                      Crypto.Cipher.Types.Block
@@ -213,10 +216,12 @@
                      Crypto.Internal.WordArray
   if impl(ghc >= 7.8)
     Other-modules:   Crypto.Hash.SHAKE
+                     Crypto.Hash.Blake2
                      Crypto.Internal.Nat
   Build-depends:     base >= 4.3 && < 5
                    , bytestring
-                   , memory >= 0.12
+                   , memory >= 0.14.5
+                   , foundation >= 0.0.8
                    , ghc-prim
   ghc-options:       -Wall -fwarn-tabs -optc-O3 -fno-warn-unused-imports
   default-language:  Haskell2010
@@ -363,6 +368,7 @@
                      KAT_RC4
                      KAT_Scrypt
                      KAT_TripleDES
+                     KAT_Twofish
                      ChaChaPoly1305
                      Number
                      Number.F2m
@@ -380,4 +386,18 @@
                    , tasty-kat
                    , cryptonite
   ghc-options:       -Wall -fno-warn-orphans -fno-warn-missing-signatures -fno-warn-unused-imports -rtsopts
+  default-language:  Haskell2010
+
+Benchmark bench-cryptonite
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    benchs
+  Main-is:           Bench.hs
+  Other-modules:     Number.F2m
+  Build-Depends:     base >= 3 && < 5
+                   , bytestring
+                   , memory
+                   , criterion
+                   , random
+                   , cryptonite
+  ghc-options:       -Wall -fno-warn-missing-signatures
   default-language:  Haskell2010
diff --git a/tests/Hash.hs b/tests/Hash.hs
--- a/tests/Hash.hs
+++ b/tests/Hash.hs
@@ -1,8 +1,8 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ViewPatterns #-}
-#if MIN_VERSION_base(4,7,0)
 {-# LANGUAGE ExistentialQuantification #-}
+#if MIN_VERSION_base(4,7,0)
 {-# LANGUAGE DataKinds #-}
 #endif
 module Hash
@@ -183,6 +183,38 @@
         "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be141e96616fb13957692cc7edd0b45ae3dc07223c8e92937bef84bc0eab862853349ec75546f58fb7c2775c38462c5010d846c185c15111e595522a6bcd16cf86f3d122109e3b1fdd943b6aec468a2d621a7c06c6a957c62b54dafc3be87567d677231395f6147293b68ceab7a9e0c58d864e8efde4e1b9a46cbe854713672f5caaae314ed9083dab4b099f8e300f01b8650f1f4b1d8fcf3f3cb53fb8e9eb2ea203bdc970f50ae55428a91f7f53ac266b28419c3778a15fd248d339ede785fb7f5a1aaa96d313eacc890936c173cdcd0fab882c45755feb3aed96d477ff96390bf9a66d1368b208e21f7c10d04a3dbd4e360633e5db4b602601c14cea737db3dcf722632cc77851cbdde2aaf0a33a07b373445df490cc8fc1e4160ff118378f11f0477de055a81a9eda57a4a2cfb0c83929d310912f729ec6cfa36c6ac6a75837143045d791cc85eff5b21932f23861bcf23a52b5da67eaf7baae0f5fb1369db78f3ac45f8c4ac5671d85735cdddb09d2b1e34a1fc066ff4a162cb263d6541274ae2fcc865f618abe27c124cd8b074ccd516301b91875824d09958f341ef274bdab0bae316339894304e35877b0c28a9b1fd166c796b9cc258a064a8f57e27f2a",
         "2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca1d01d1369a23539cd80f7c054b6e5daf9c962cad5b8ed5bd11998b40d5734442bed798f6e5c915bd8bb07e0188d0a55c1290074f1c287af06352299184492cbdec9acba737ee292e5adaa445547355e72a03a3bac3aac770fe5d6b66600ff15d37d5b4789994ea2aeb097f550aa5e88e4d8ff0ba07b88c1c88573063f5d96df820abc2abd177ab037f351c375e553af917132cf2f563c79a619e1bb76e8e2266b0c5617d695f2c496a25f4073b6840c1833757ebb386f16757a8e16a21e9355e9b248f3b33be672da700266be99b8f8725e8ab06075f0219e655ebc188976364b7db139390d34a6ea67b4b223229183a94cf455ece91fdaf5b9c707fa4b40ec39816c1120c7aaaf47920977be900e6b9ca4b8940e192b927c475bd58e836f512ae3e52924e36ff8e9b1d0251047770a5e465905622b1f159be121ab93819c5e5c6dae299ac73bf1c4ed4a1e2c7fa3caa1039b05e94c9f993d04feb272b6e00bb0276939cf746c42936831fc8f2b4cb0cf94808ae0af405ce4bc67d1e7acfc6fd6590d3de91f795df5aaf57e2cee1845a303d0ea564be3f1299acdce67efe0d62cfc6d6829ff4ecc0a05153c24696c4d34c076453827e796f3062f94f62f4528b7cfc870f0dcd615b7c97b95da4b9be5830e8b3f66cce71e0f622c771994443e2",
         "fffcaac0606c0edb7bc0d15f033accb68538159016e5ae8470bf9ebea89fa6c9fcc3e027d94f7f967b7246346bd9f6b8084e45a057b976847c4db03bf383c834054866f6a8282a497368c46e1852fc09e20f22c45607a27c8b2a4798ebefada54f8d3795b9f07606b1cd6e41f90d765480ef5c0d5790659cf1d210adfd412378b92e1dd9bd7fd95a1a66677fc6baa0e3a53c9031c1fb59cbad9f5dc5881a3c8e25c80ecb1abf0971488ada1f533dcbf8d37031335378574b8d3fad61159c9fae28caa543b3072ce308d369be340e78c6edc664cc6dde9b2f0a4ad2e60ce9c8b1e5722b8d5b73d0962b74fb9ed86307a180f53933339f9d56d3b345c2a0e98fcf5de7754f3845f6be30089f0e142ad4602f18abdc750bda7c91c3f32872e66640db46045ab4c276b379f1b834c2cbb1bd8601305649ec6b3bf20618695136dee6541492d1d985ea1fb765fd7a559e810eba30f2f710233ae5a411b94ddcaa01a08f1c31320d111c0714422cd5e987c9a76fc865de34003ab12664081be8017d23d977f2bf4ed9e3ce09ea3d64bb4ae8ebfa9d0721f57841008c297e2f455a0441a2bd618ca379dbd239a21e410defb4001b1e11f87e36bf894c222f76f12ddcc3771bbb17d5c0dfd86d89a3e13e084f6dc1c4762bcd393c1757db7afb1434221569e7ddaaffd6318253ec3df8cf5f826b81896d6474ee06a2e30ccc8c6a96bdd5" ])
+    , ("Blake2b 160", HashAlg (Blake2b :: Blake2b 160), [
+        "3345524abf6bbe1809449224b5972c41790b6cf2",
+        "3c523ed102ab45a37d54f5610d5a983162fde84f",
+        "a3d365b5fba5d36fbb19c03b7fde496058969c5a" ])
+    , ("Blake2b 224", HashAlg (Blake2b :: Blake2b 224), [
+        "836cc68931c2e4e3e838602eca1902591d216837bafddfe6f0c8cb07",
+        "477c3985751dd4d1b8c93827ea5310b33bb02a26463a050dffd3e857",
+        "a4a1b6851be66891a3deff406c4d7556879ebf952407450755f90eb6" ])
+    , ("Blake2b 256", HashAlg (Blake2b :: Blake2b 256), [
+        "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8",
+        "01718cec35cd3d796dd00020e0bfecb473ad23457d063b75eff29c0ffa2e58a9",
+        "036c13096926b3dfccfe3f233bd1b2f583b818b8b15c01be65af69238e900b2c" ])
+    , ("Blake2b 384", HashAlg (Blake2b :: Blake2b 384), [
+        "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100",
+        "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d",
+        "927a1f297873cbe887a93b2183c4e2eba53966ba92c6db8b87029a1d8c673471d09740676cced79c5016838973f630c3" ])
+    , ("Blake2b 512", HashAlg (Blake2b :: Blake2b 512), [
+        "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
+        "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
+        "af438eea5d8cdb209336a7e85bf58090dc21b49d823f89a7d064c119f127bd361af9c7d109edda0f0e91bdce078d1d86b8e6f25727c98f6d3bb6f50acb2dd376" ])
+    , ("Blake2s 160", HashAlg (Blake2s :: Blake2s 160), [
+        "354c9c33f735962418bdacb9479873429c34916f",
+        "5a604fec9713c369e84b0ed68daed7d7504ef240",
+        "759bef6d041bcbd861b8b51baaece6c8fffd0acf" ])
+    , ("Blake2s 224", HashAlg (Blake2s :: Blake2s 224), [
+        "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4",
+        "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912",
+        "e220025fd46a9a635c3f7f60bb96a84c01019ac0817f5901e7eeaa2c" ])
+    , ("Blake2s 256", HashAlg (Blake2s ::Blake2s 256), [
+        "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9",
+        "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812",
+        "94662583a600a12dff357c0a6f1b514a710ef0f587a38e8d2e4d7f67e9c81667" ])
 #endif
     ]
 
diff --git a/tests/KAT_Twofish.hs b/tests/KAT_Twofish.hs
new file mode 100644
--- /dev/null
+++ b/tests/KAT_Twofish.hs
@@ -0,0 +1,45 @@
+module KAT_Twofish (tests) where
+
+import Imports
+import BlockCipher
+
+import qualified Data.ByteString as B
+import Crypto.Cipher.Twofish
+
+
+vectors_twofish128 =
+    [ KAT_ECB (B.replicate 16 0x00) (B.replicate 16 0x00) (B.pack [0x9F,0x58,0x9F,0x5C,0xF6,0x12,0x2C,0x32,0xB6,0xBF,0xEC,0x2F,0x2A,0xE8,0xC3,0x5A])
+    , KAT_ECB (B.pack [0x9F,0x58,0x9F,0x5C,0xF6,0x12,0x2C,0x32,0xB6,0xBF,0xEC,0x2F,0x2A,0xE8,0xC3,0x5A])
+              (B.pack [0xD4, 0x91, 0xDB, 0x16, 0xE7, 0xB1, 0xC3, 0x9E, 0x86, 0xCB, 0x08, 0x6B, 0x78, 0x9F, 0x54, 0x19])
+              (B.pack [0x01, 0x9F, 0x98, 0x09, 0xDE, 0x17, 0x11, 0x85, 0x8F, 0xAA, 0xC3, 0xA3, 0xBA, 0x20, 0xFB, 0xC3])
+    ]
+
+vectors_twofish192 =
+    [ KAT_ECB (B.pack [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+                       0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77])
+              (B.pack [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+              (B.pack [0xCF, 0xD1, 0xD2, 0xE5, 0xA9, 0xBE, 0x9C, 0xDF, 0x50, 0x1F, 0x13, 0xB8, 0x92, 0xBD, 0x22, 0x48])
+    , KAT_ECB (B.pack [0x88, 0xB2, 0xB2, 0x70, 0x6B, 0x10, 0x5E, 0x36, 0xB4, 0x46, 0xBB, 0x6D, 0x73, 0x1A, 0x1E, 0x88,
+                       0xEF, 0xA7, 0x1F, 0x78, 0x89, 0x65, 0xBD, 0x44])
+              (B.pack [0x39, 0xDA, 0x69, 0xD6, 0xBA, 0x49, 0x97, 0xD5, 0x85, 0xB6, 0xDC, 0x07, 0x3C, 0xA3, 0x41, 0xB2])
+              (B.pack [0x18, 0x2B, 0x02, 0xD8, 0x14, 0x97, 0xEA, 0x45, 0xF9, 0xDA, 0xAC, 0xDC, 0x29, 0x19, 0x3A, 0x65])]
+
+vectors_twofish256 =
+    [ KAT_ECB (B.pack [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+                       0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF])
+              (B.pack [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+              (B.pack [0x37, 0x52, 0x7B, 0xE0, 0x05, 0x23, 0x34, 0xB8, 0x9F, 0x0C, 0xFC, 0xCA, 0xE8, 0x7C, 0xFA, 0x20])
+    , KAT_ECB (B.pack [0xD4, 0x3B, 0xB7, 0x55, 0x6E, 0xA3, 0x2E, 0x46, 0xF2, 0xA2, 0x82, 0xB7, 0xD4, 0x5B, 0x4E, 0x0D,
+                       0x57, 0xFF, 0x73, 0x9D, 0x4D, 0xC9, 0x2C, 0x1B, 0xD7, 0xFC, 0x01, 0x70, 0x0C, 0xC8, 0x21, 0x6F])
+              (B.pack [0x90, 0xAF, 0xE9, 0x1B, 0xB2, 0x88, 0x54, 0x4F, 0x2C, 0x32, 0xDC, 0x23, 0x9B, 0x26, 0x35, 0xE6])
+              (B.pack [0x6C, 0xB4, 0x56, 0x1C, 0x40, 0xBF, 0x0A, 0x97, 0x05, 0x93, 0x1C, 0xB6, 0xD4, 0x08, 0xE7, 0xFA])]
+
+kats128 = defaultKATs { kat_ECB = vectors_twofish128 }
+kats192 = defaultKATs { kat_ECB = vectors_twofish192 }
+kats256 = defaultKATs { kat_ECB = vectors_twofish256 }
+
+tests = testGroup "Twofish"
+            [ testBlockCipher kats128 (undefined :: Twofish128)
+            , testBlockCipher kats192 (undefined :: Twofish192)
+            , testBlockCipher kats256 (undefined :: Twofish256) ]
+
diff --git a/tests/Tests.hs b/tests/Tests.hs
--- a/tests/Tests.hs
+++ b/tests/Tests.hs
@@ -31,6 +31,7 @@
 import qualified KAT_DES
 import qualified KAT_RC4
 import qualified KAT_TripleDES
+import qualified KAT_Twofish
 -- misc --------------------------------
 import qualified KAT_AFIS
 import qualified Padding
@@ -66,6 +67,7 @@
         , KAT_Camellia.tests
         , KAT_DES.tests
         , KAT_TripleDES.tests
+        , KAT_Twofish.tests
         ]
     , testGroup "stream-cipher"
         [ KAT_RC4.tests
