diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.9
+
+* Quiet down unused module imports
+* Move Curve25519 over to Crypto.Error instead of using Either String.
+* Add documentation for ChaChaPoly1305
+* Add missing documentation for various modules
+* Add a way to create Poly1305 Auth tag.
+* Added support for the BLAKE2 family of hash algorithms
+* Fix endianness of incrementNonce function for ChaChaPoly1305
+
 ## 0.8
 
 * Add support for ChaChaPoly1305 Nonce Increment (John Galt)
diff --git a/Crypto/Cipher/ChaChaPoly1305.hs b/Crypto/Cipher/ChaChaPoly1305.hs
--- a/Crypto/Cipher/ChaChaPoly1305.hs
+++ b/Crypto/Cipher/ChaChaPoly1305.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-
 -- |
 -- Module      : Crypto.Cipher.ChaChaPoly1305
 -- License     : BSD-style
@@ -7,10 +5,23 @@
 -- Stability   : stable
 -- Portability : good
 --
--- A simple AEAD scheme using ChaCha20 and Poly1305.
--- 
--- See RFC7539.
+-- A simple AEAD scheme using ChaCha20 and Poly1305. See RFC7539.
 --
+-- The State is not modified in place, so each function changing the State,
+-- returns a new State.
+--
+-- Authenticated Data need to be added before any call to 'encrypt' or 'decrypt',
+-- and once all the data has been added, then 'finalizeAAD' need to be called.
+--
+-- Once 'finalizeAAD' has been called, no further 'appendAAD' call should be make.
+--
+-- > encrypt nonce key hdr inp =
+-- >    let st1        = ChaChaPoly1305.initialize key nonce
+-- >        st2        = ChaChaPoly1305.finalizeAAD $ ChaChaPoly1305.appendAAD hdr st1
+-- >        (out, st3) = ChaChaPoly1305.encrypt inp st2
+-- >        auth       = ChaChaPoly1305.finalize st3
+-- >     in out `B.append` Data.ByteArray.convert auth
+--
 module Crypto.Cipher.ChaChaPoly1305
     ( State
     , Nonce
@@ -25,6 +36,7 @@
     , finalize
     ) where
 
+import           Control.Monad             (when)
 import           Crypto.Internal.ByteArray (ByteArrayAccess, ByteArray, Bytes, ScrubbedBytes)
 import qualified Crypto.Internal.ByteArray as B
 import           Crypto.Internal.Imports
@@ -36,14 +48,26 @@
 import           Foreign.Ptr
 import           Foreign.Storable
 
+-- | A ChaChaPoly1305 State.
+--
+-- The state is immutable, and only new state can be created
 data State = State !ChaCha.State
                    !Poly1305.State
                    !Word64 -- AAD length
                    !Word64 -- ciphertext length
 
-newtype Nonce = Nonce Bytes
-    deriving (ByteArrayAccess)
+-- | Valid Nonce for ChaChaPoly1305.
+--
+-- It can be created with 'nonce8' or 'nonce12'
+data Nonce = Nonce8 Bytes | Nonce12 Bytes
 
+instance ByteArrayAccess Nonce where
+  length (Nonce8  n) = B.length n
+  length (Nonce12 n) = B.length n
+
+  withByteArray (Nonce8  n) = B.withByteArray n
+  withByteArray (Nonce12 n) = B.withByteArray n
+
 -- Based on the following pseudo code:
 --
 -- chacha20_aead_encrypt(aad, key, iv, constant, plaintext):
@@ -67,42 +91,60 @@
 -- | Nonce smart constructor 12 bytes IV, nonce constructor
 nonce12 :: ByteArrayAccess iv => iv -> CryptoFailable Nonce
 nonce12 iv
-    | B.length iv /= 12 = CryptoFailed  $ CryptoError_IvSizeInvalid
-    | otherwise         = CryptoPassed $ Nonce (B.convert iv)
+    | B.length iv /= 12 = CryptoFailed CryptoError_IvSizeInvalid
+    | otherwise         = CryptoPassed . Nonce12 . B.convert $ iv
 
 -- | 8 bytes IV, nonce constructor
 nonce8 :: ByteArrayAccess ba
-       => ba -- ^ 4 bytes constant 
+       => ba -- ^ 4 bytes constant
        -> ba -- ^ 8 bytes IV
        -> CryptoFailable Nonce
 nonce8 constant iv
-    | B.length constant /= 4 = CryptoFailed $ CryptoError_IvSizeInvalid
-    | B.length iv       /= 8 = CryptoFailed $ CryptoError_IvSizeInvalid
-    | otherwise              = CryptoPassed $ Nonce $ B.concat [constant, iv]
+    | B.length constant /= 4 = CryptoFailed CryptoError_IvSizeInvalid
+    | B.length iv       /= 8 = CryptoFailed CryptoError_IvSizeInvalid
+    | otherwise              = CryptoPassed . Nonce8 . B.concat $ [constant, iv]
 
 -- | Increment a nonce
 incrementNonce :: Nonce -> Nonce
-incrementNonce (Nonce n) = Nonce $ B.copyAndFreeze n $ \s ->
-    loop s $ s `plusPtr` ((B.length n) - 1)
+incrementNonce (Nonce8  n) = Nonce8  $ incrementNonce' n 4
+incrementNonce (Nonce12 n) = Nonce12 $ incrementNonce' n 0
+
+incrementNonce' :: Bytes -> Int -> Bytes
+incrementNonce' b offset = B.copyAndFreeze b $ \s ->
+    loop s (s `plusPtr` offset)
     where
       loop :: Ptr Word8 -> Ptr Word8 -> IO ()
       loop s p
-          | s == p    = peek s >>= poke s . (+) 1
+          | s == (p `plusPtr` (B.length b - offset - 1)) = peek s >>= poke s . (+) 1
           | otherwise = do
               r <- (+) 1 <$> peek p
               poke p r
-              if r == 0 then loop s (p `plusPtr` (-1)) else return ()
+              when (r == 0) $ loop s (p `plusPtr` 1)
 
+-- | Initialize a new ChaChaPoly1305 State
+--
+-- The key length need to be 256 bits, and the nonce
+-- procured using either `nonce8` or `nonce12`
 initialize :: ByteArrayAccess key
            => key -> Nonce -> CryptoFailable State
-initialize key (Nonce nonce)
-    | B.length key /= 32 = CryptoFailed $ CryptoError_KeySizeInvalid
+initialize key (Nonce8  nonce) = initialize' key nonce
+initialize key (Nonce12 nonce) = initialize' key nonce
+
+initialize' :: ByteArrayAccess key
+            => key -> Bytes -> CryptoFailable State
+initialize' key nonce
+    | B.length key /= 32 = CryptoFailed CryptoError_KeySizeInvalid
     | otherwise          = CryptoPassed $ State encState polyState 0 0
   where
     rootState           = ChaCha.initialize 20 key nonce
     (polyKey, encState) = ChaCha.generate rootState 64
     polyState           = throwCryptoError $ Poly1305.initialize (B.take 32 polyKey :: ScrubbedBytes)
 
+-- | Append Authenticated Data to the State and return
+-- the new modified State.
+--
+-- Once no further call to this function need to be make,
+-- the user should call 'finalizeAAD'
 appendAAD :: ByteArrayAccess ba => ba -> State -> State
 appendAAD ba (State encState macState aadLength plainLength) =
     State encState newMacState newLength plainLength
@@ -110,12 +152,15 @@
     newMacState = Poly1305.update macState ba
     newLength   = aadLength + fromIntegral (B.length ba)
 
+-- | Finalize the Authenticated Data and return the finalized State
 finalizeAAD :: State -> State
 finalizeAAD (State encState macState aadLength plainLength) =
     State encState newMacState aadLength plainLength
   where
     newMacState = Poly1305.update macState $ pad16 aadLength
 
+-- | Encrypt a piece of data and returns the encrypted Data and the
+-- updated State.
 encrypt :: ByteArray ba => ba -> State -> (ba, State)
 encrypt input (State encState macState aadLength plainLength) =
     (output, State newEncState newMacState aadLength newPlainLength)
@@ -124,6 +169,8 @@
     newMacState           = Poly1305.update macState output
     newPlainLength        = plainLength + fromIntegral (B.length input)
 
+-- | Decrypt a piece of data and returns the decrypted Data and the
+-- updated State.
 decrypt :: ByteArray ba => ba -> State -> (ba, State)
 decrypt input (State encState macState aadLength plainLength) =
     (output, State newEncState newMacState aadLength newPlainLength)
@@ -132,6 +179,7 @@
     newMacState           = Poly1305.update macState input
     newPlainLength        = plainLength + fromIntegral (B.length input)
 
+-- | Generate an authentication tag from the State.
 finalize :: State -> Poly1305.Auth
 finalize (State _ macState aadLength plainLength) =
     Poly1305.finalize $ Poly1305.updates macState
diff --git a/Crypto/Data/Padding.hs b/Crypto/Data/Padding.hs
--- a/Crypto/Data/Padding.hs
+++ b/Crypto/Data/Padding.hs
@@ -17,6 +17,7 @@
 import           Data.ByteArray (ByteArray, Bytes)
 import qualified Data.ByteArray as B
 
+-- | Format of padding
 data Format =
       PKCS5     -- ^ PKCS5: PKCS7 with hardcoded size of 8
     | PKCS7 Int -- ^ PKCS7 with padding size between 1 and 255
diff --git a/Crypto/Error/Types.hs b/Crypto/Error/Types.hs
--- a/Crypto/Error/Types.hs
+++ b/Crypto/Error/Types.hs
@@ -33,8 +33,10 @@
     | CryptoError_SecretKeySizeInvalid
     | CryptoError_SecretKeyStructureInvalid
     | CryptoError_PublicKeySizeInvalid
+    | CryptoError_SharedSecretSizeInvalid
     -- Message authentification error
     | CryptoError_MacKeyInvalid
+    | CryptoError_AuthenticationTagSizeInvalid
     deriving (Show,Eq,Enum,Data,Typeable)
 
 instance E.Exception CryptoError
diff --git a/Crypto/Hash/Algorithms.hs b/Crypto/Hash/Algorithms.hs
--- a/Crypto/Hash/Algorithms.hs
+++ b/Crypto/Hash/Algorithms.hs
@@ -10,6 +10,10 @@
 module Crypto.Hash.Algorithms
     ( HashAlgorithm
     -- * hash algorithms
+    , Blake2s_256(..)
+    , Blake2sp_256(..)
+    , Blake2b_512(..)
+    , Blake2bp_512(..)
     , MD2(..)
     , MD4(..)
     , MD5(..)
@@ -22,10 +26,10 @@
     , SHA512t_256(..)
     , RIPEMD160(..)
     , Tiger(..)
-    , Kekkak_224(..)
-    , Kekkak_256(..)
-    , Kekkak_384(..)
-    , Kekkak_512(..)
+    , Keccak_224(..)
+    , Keccak_256(..)
+    , Keccak_384(..)
+    , Keccak_512(..)
     , SHA3_224(..)
     , SHA3_256(..)
     , SHA3_384(..)
@@ -40,6 +44,10 @@
     ) where
 
 import           Crypto.Hash.Types (HashAlgorithm)
+import           Crypto.Hash.Blake2s
+import           Crypto.Hash.Blake2sp
+import           Crypto.Hash.Blake2b
+import           Crypto.Hash.Blake2bp
 import           Crypto.Hash.MD2
 import           Crypto.Hash.MD4
 import           Crypto.Hash.MD5
@@ -50,7 +58,7 @@
 import           Crypto.Hash.SHA512
 import           Crypto.Hash.SHA512t
 import           Crypto.Hash.SHA3
-import           Crypto.Hash.Kekkak
+import           Crypto.Hash.Keccak
 import           Crypto.Hash.RIPEMD160
 import           Crypto.Hash.Tiger
 import           Crypto.Hash.Skein256
diff --git a/Crypto/Hash/Blake2b.hs b/Crypto/Hash/Blake2b.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Hash/Blake2b.hs
@@ -0,0 +1,41 @@
+-- |
+-- Module      : Crypto.Hash.Blake2b
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- module containing the binding functions to work with the
+-- Blake2b cryptographic hash.
+--
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Crypto.Hash.Blake2b
+    (  Blake2b_512 (..)
+    ) where
+
+import           Crypto.Hash.Types
+import           Foreign.Ptr (Ptr)
+import           Data.Word (Word8, Word32)
+
+
+-- | Blake2b (512 bits) cryptographic hash algorithm
+data Blake2b_512 = Blake2b_512
+    deriving (Show)
+
+instance HashAlgorithm Blake2b_512 where
+    hashBlockSize  _          = 128
+    hashDigestSize _          = 64
+    hashInternalContextSize _ = 361
+    hashInternalInit p        = c_blake2b_init p 512
+    hashInternalUpdate        = c_blake2b_update
+    hashInternalFinalize p    = c_blake2b_finalize p 512
+
+
+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 ()
diff --git a/Crypto/Hash/Blake2bp.hs b/Crypto/Hash/Blake2bp.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Hash/Blake2bp.hs
@@ -0,0 +1,41 @@
+-- |
+-- Module      : Crypto.Hash.Blake2bp
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- module containing the binding functions to work with the
+-- Blake2bp cryptographic hash.
+--
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Crypto.Hash.Blake2bp
+    (  Blake2bp_512 (..)
+    ) where
+
+import           Crypto.Hash.Types
+import           Foreign.Ptr (Ptr)
+import           Data.Word (Word8, Word32)
+
+
+-- | Blake2bp (512 bits) cryptographic hash algorithm
+data Blake2bp_512 = Blake2bp_512
+    deriving (Show)
+
+instance HashAlgorithm Blake2bp_512 where
+    hashBlockSize  _          = 128
+    hashDigestSize _          = 64
+    hashInternalContextSize _ = 2325
+    hashInternalInit p        = c_blake2sp_init p 512
+    hashInternalUpdate        = c_blake2sp_update
+    hashInternalFinalize p    = c_blake2sp_finalize p 512
+
+
+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 ()
diff --git a/Crypto/Hash/Blake2s.hs b/Crypto/Hash/Blake2s.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Hash/Blake2s.hs
@@ -0,0 +1,41 @@
+-- |
+-- Module      : Crypto.Hash.Blake2s
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- module containing the binding functions to work with the
+-- Blake2s cryptographic hash.
+--
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Crypto.Hash.Blake2s
+    (  Blake2s_256 (..)
+    ) where
+
+import           Crypto.Hash.Types
+import           Foreign.Ptr (Ptr)
+import           Data.Word (Word8, Word32)
+
+
+-- | Blake2s (256 bits) cryptographic hash algorithm
+data Blake2s_256 = Blake2s_256
+    deriving (Show)
+
+instance HashAlgorithm Blake2s_256 where
+    hashBlockSize  _          = 64
+    hashDigestSize _          = 32
+    hashInternalContextSize _ = 185
+    hashInternalInit p        = c_blake2s_init p 256
+    hashInternalUpdate        = c_blake2s_update
+    hashInternalFinalize p    = c_blake2s_finalize p 256
+
+
+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 ()
diff --git a/Crypto/Hash/Blake2sp.hs b/Crypto/Hash/Blake2sp.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Hash/Blake2sp.hs
@@ -0,0 +1,41 @@
+-- |
+-- Module      : Crypto.Hash.Blake2sp
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- module containing the binding functions to work with the
+-- Blake2sp cryptographic hash.
+--
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Crypto.Hash.Blake2sp
+    (  Blake2sp_256 (..)
+    ) where
+
+import           Crypto.Hash.Types
+import           Foreign.Ptr (Ptr)
+import           Data.Word (Word8, Word32)
+
+
+-- | Blake2sp (256 bits) cryptographic hash algorithm
+data Blake2sp_256 = Blake2sp_256
+    deriving (Show)
+
+instance HashAlgorithm Blake2sp_256 where
+    hashBlockSize  _          = 64
+    hashDigestSize _          = 32
+    hashInternalContextSize _ = 2185
+    hashInternalInit p        = c_blake2sp_init p 256
+    hashInternalUpdate        = c_blake2sp_update
+    hashInternalFinalize p    = c_blake2sp_finalize p 256
+
+
+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 ()
diff --git a/Crypto/Hash/Keccak.hs b/Crypto/Hash/Keccak.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Hash/Keccak.hs
@@ -0,0 +1,77 @@
+-- |
+-- Module      : Crypto.Hash.Keccak
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- module containing the binding functions to work with the
+-- Keccak cryptographic hash.
+--
+{-# LANGUAGE ForeignFunctionInterface #-}
+module Crypto.Hash.Keccak
+    (  Keccak_224 (..), Keccak_256 (..), Keccak_384 (..), Keccak_512 (..)
+    ) where
+
+import           Crypto.Hash.Types
+import           Foreign.Ptr (Ptr)
+import           Data.Word (Word8, Word32)
+
+
+-- | Keccak (224 bits) cryptographic hash algorithm
+data Keccak_224 = Keccak_224
+    deriving (Show)
+
+instance HashAlgorithm Keccak_224 where
+    hashBlockSize  _          = 144
+    hashDigestSize _          = 28
+    hashInternalContextSize _ = 360
+    hashInternalInit p        = c_keccak_init p 224
+    hashInternalUpdate        = c_keccak_update
+    hashInternalFinalize p    = c_keccak_finalize p 224
+
+-- | Keccak (256 bits) cryptographic hash algorithm
+data Keccak_256 = Keccak_256
+    deriving (Show)
+
+instance HashAlgorithm Keccak_256 where
+    hashBlockSize  _          = 136
+    hashDigestSize _          = 32
+    hashInternalContextSize _ = 360
+    hashInternalInit p        = c_keccak_init p 256
+    hashInternalUpdate        = c_keccak_update
+    hashInternalFinalize p    = c_keccak_finalize p 256
+
+-- | Keccak (384 bits) cryptographic hash algorithm
+data Keccak_384 = Keccak_384
+    deriving (Show)
+
+instance HashAlgorithm Keccak_384 where
+    hashBlockSize  _          = 104
+    hashDigestSize _          = 48
+    hashInternalContextSize _ = 360
+    hashInternalInit p        = c_keccak_init p 384
+    hashInternalUpdate        = c_keccak_update
+    hashInternalFinalize p    = c_keccak_finalize p 384
+
+-- | Keccak (512 bits) cryptographic hash algorithm
+data Keccak_512 = Keccak_512
+    deriving (Show)
+
+instance HashAlgorithm Keccak_512 where
+    hashBlockSize  _          = 72
+    hashDigestSize _          = 64
+    hashInternalContextSize _ = 360
+    hashInternalInit p        = c_keccak_init p 512
+    hashInternalUpdate        = c_keccak_update
+    hashInternalFinalize p    = c_keccak_finalize p 512
+
+
+foreign import ccall unsafe "cryptonite_keccak_init"
+    c_keccak_init :: Ptr (Context a) -> Word32 -> IO ()
+
+foreign import ccall "cryptonite_keccak_update"
+    c_keccak_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
+
+foreign import ccall unsafe "cryptonite_keccak_finalize"
+    c_keccak_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/Kekkak.hs b/Crypto/Hash/Kekkak.hs
deleted file mode 100644
--- a/Crypto/Hash/Kekkak.hs
+++ /dev/null
@@ -1,77 +0,0 @@
--- |
--- Module      : Crypto.Hash.Kekkak
--- License     : BSD-style
--- Maintainer  : Vincent Hanquez <vincent@snarc.org>
--- Stability   : experimental
--- Portability : unknown
---
--- module containing the binding functions to work with the
--- Kekkak cryptographic hash.
---
-{-# LANGUAGE ForeignFunctionInterface #-}
-module Crypto.Hash.Kekkak
-    (  Kekkak_224 (..), Kekkak_256 (..), Kekkak_384 (..), Kekkak_512 (..)
-    ) where
-
-import           Crypto.Hash.Types
-import           Foreign.Ptr (Ptr)
-import           Data.Word (Word8, Word32)
-
-
--- | Kekkak (224 bits) cryptographic hash algorithm
-data Kekkak_224 = Kekkak_224
-    deriving (Show)
-
-instance HashAlgorithm Kekkak_224 where
-    hashBlockSize  _          = 144
-    hashDigestSize _          = 28
-    hashInternalContextSize _ = 360
-    hashInternalInit p        = c_kekkak_init p 224
-    hashInternalUpdate        = c_kekkak_update
-    hashInternalFinalize      = c_kekkak_finalize
-
--- | Kekkak (256 bits) cryptographic hash algorithm
-data Kekkak_256 = Kekkak_256
-    deriving (Show)
-
-instance HashAlgorithm Kekkak_256 where
-    hashBlockSize  _          = 136
-    hashDigestSize _          = 32
-    hashInternalContextSize _ = 360
-    hashInternalInit p        = c_kekkak_init p 256
-    hashInternalUpdate        = c_kekkak_update
-    hashInternalFinalize      = c_kekkak_finalize
-
--- | Kekkak (384 bits) cryptographic hash algorithm
-data Kekkak_384 = Kekkak_384
-    deriving (Show)
-
-instance HashAlgorithm Kekkak_384 where
-    hashBlockSize  _          = 104
-    hashDigestSize _          = 48
-    hashInternalContextSize _ = 360
-    hashInternalInit p        = c_kekkak_init p 384
-    hashInternalUpdate        = c_kekkak_update
-    hashInternalFinalize      = c_kekkak_finalize
-
--- | Kekkak (512 bits) cryptographic hash algorithm
-data Kekkak_512 = Kekkak_512
-    deriving (Show)
-
-instance HashAlgorithm Kekkak_512 where
-    hashBlockSize  _          = 72
-    hashDigestSize _          = 64
-    hashInternalContextSize _ = 360
-    hashInternalInit p        = c_kekkak_init p 512
-    hashInternalUpdate        = c_kekkak_update
-    hashInternalFinalize      = c_kekkak_finalize
-
-
-foreign import ccall unsafe "cryptonite_kekkak_init"
-    c_kekkak_init :: Ptr (Context a) -> Word32 -> IO ()
-
-foreign import ccall "cryptonite_kekkak_update"
-    c_kekkak_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
-
-foreign import ccall unsafe "cryptonite_kekkak_finalize"
-    c_kekkak_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/SHA3.hs b/Crypto/Hash/SHA3.hs
--- a/Crypto/Hash/SHA3.hs
+++ b/Crypto/Hash/SHA3.hs
@@ -28,7 +28,7 @@
     hashInternalContextSize _ = 360
     hashInternalInit p        = c_sha3_init p 224
     hashInternalUpdate        = c_sha3_update
-    hashInternalFinalize      = c_sha3_finalize
+    hashInternalFinalize p    = c_sha3_finalize p 224
 
 -- | SHA3 (256 bits) cryptographic hash algorithm
 data SHA3_256 = SHA3_256
@@ -40,7 +40,7 @@
     hashInternalContextSize _ = 360
     hashInternalInit p        = c_sha3_init p 256
     hashInternalUpdate        = c_sha3_update
-    hashInternalFinalize      = c_sha3_finalize
+    hashInternalFinalize p    = c_sha3_finalize p 256
 
 -- | SHA3 (384 bits) cryptographic hash algorithm
 data SHA3_384 = SHA3_384
@@ -52,7 +52,7 @@
     hashInternalContextSize _ = 360
     hashInternalInit p        = c_sha3_init p 384
     hashInternalUpdate        = c_sha3_update
-    hashInternalFinalize      = c_sha3_finalize
+    hashInternalFinalize p    = c_sha3_finalize p 384
 
 -- | SHA3 (512 bits) cryptographic hash algorithm
 data SHA3_512 = SHA3_512
@@ -64,7 +64,7 @@
     hashInternalContextSize _ = 360
     hashInternalInit p        = c_sha3_init p 512
     hashInternalUpdate        = c_sha3_update
-    hashInternalFinalize      = c_sha3_finalize
+    hashInternalFinalize p    = c_sha3_finalize p 512
 
 
 foreign import ccall unsafe "cryptonite_sha3_init"
@@ -74,4 +74,4 @@
     c_sha3_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
 
 foreign import ccall unsafe "cryptonite_sha3_finalize"
-    c_sha3_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+    c_sha3_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/SHA512t.hs b/Crypto/Hash/SHA512t.hs
--- a/Crypto/Hash/SHA512t.hs
+++ b/Crypto/Hash/SHA512t.hs
@@ -25,10 +25,10 @@
 instance HashAlgorithm SHA512t_224 where
     hashBlockSize  _          = 128
     hashDigestSize _          = 28
-    hashInternalContextSize _ = 264
+    hashInternalContextSize _ = 256
     hashInternalInit p        = c_sha512t_init p 224
     hashInternalUpdate        = c_sha512t_update
-    hashInternalFinalize      = c_sha512t_finalize
+    hashInternalFinalize p    = c_sha512t_finalize p 224
 
 -- | SHA512t (256 bits) cryptographic hash algorithm
 data SHA512t_256 = SHA512t_256
@@ -37,10 +37,10 @@
 instance HashAlgorithm SHA512t_256 where
     hashBlockSize  _          = 128
     hashDigestSize _          = 32
-    hashInternalContextSize _ = 264
+    hashInternalContextSize _ = 256
     hashInternalInit p        = c_sha512t_init p 256
     hashInternalUpdate        = c_sha512t_update
-    hashInternalFinalize      = c_sha512t_finalize
+    hashInternalFinalize p    = c_sha512t_finalize p 256
 
 
 foreign import ccall unsafe "cryptonite_sha512t_init"
@@ -50,4 +50,4 @@
     c_sha512t_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
 
 foreign import ccall unsafe "cryptonite_sha512t_finalize"
-    c_sha512t_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+    c_sha512t_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/Skein256.hs b/Crypto/Hash/Skein256.hs
--- a/Crypto/Hash/Skein256.hs
+++ b/Crypto/Hash/Skein256.hs
@@ -28,7 +28,7 @@
     hashInternalContextSize _ = 96
     hashInternalInit p        = c_skein256_init p 224
     hashInternalUpdate        = c_skein256_update
-    hashInternalFinalize      = c_skein256_finalize
+    hashInternalFinalize p    = c_skein256_finalize p 224
 
 -- | Skein256 (256 bits) cryptographic hash algorithm
 data Skein256_256 = Skein256_256
@@ -40,7 +40,7 @@
     hashInternalContextSize _ = 96
     hashInternalInit p        = c_skein256_init p 256
     hashInternalUpdate        = c_skein256_update
-    hashInternalFinalize      = c_skein256_finalize
+    hashInternalFinalize p    = c_skein256_finalize p 256
 
 
 foreign import ccall unsafe "cryptonite_skein256_init"
@@ -50,4 +50,4 @@
     c_skein256_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
 
 foreign import ccall unsafe "cryptonite_skein256_finalize"
-    c_skein256_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+    c_skein256_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/Skein512.hs b/Crypto/Hash/Skein512.hs
--- a/Crypto/Hash/Skein512.hs
+++ b/Crypto/Hash/Skein512.hs
@@ -28,7 +28,7 @@
     hashInternalContextSize _ = 160
     hashInternalInit p        = c_skein512_init p 224
     hashInternalUpdate        = c_skein512_update
-    hashInternalFinalize      = c_skein512_finalize
+    hashInternalFinalize p    = c_skein512_finalize p 224
 
 -- | Skein512 (256 bits) cryptographic hash algorithm
 data Skein512_256 = Skein512_256
@@ -40,7 +40,7 @@
     hashInternalContextSize _ = 160
     hashInternalInit p        = c_skein512_init p 256
     hashInternalUpdate        = c_skein512_update
-    hashInternalFinalize      = c_skein512_finalize
+    hashInternalFinalize p    = c_skein512_finalize p 256
 
 -- | Skein512 (384 bits) cryptographic hash algorithm
 data Skein512_384 = Skein512_384
@@ -52,7 +52,7 @@
     hashInternalContextSize _ = 160
     hashInternalInit p        = c_skein512_init p 384
     hashInternalUpdate        = c_skein512_update
-    hashInternalFinalize      = c_skein512_finalize
+    hashInternalFinalize p    = c_skein512_finalize p 384
 
 -- | Skein512 (512 bits) cryptographic hash algorithm
 data Skein512_512 = Skein512_512
@@ -64,7 +64,7 @@
     hashInternalContextSize _ = 160
     hashInternalInit p        = c_skein512_init p 512
     hashInternalUpdate        = c_skein512_update
-    hashInternalFinalize      = c_skein512_finalize
+    hashInternalFinalize p    = c_skein512_finalize p 512
 
 
 foreign import ccall unsafe "cryptonite_skein512_init"
@@ -74,4 +74,4 @@
     c_skein512_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()
 
 foreign import ccall unsafe "cryptonite_skein512_finalize"
-    c_skein512_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+    c_skein512_finalize :: Ptr (Context a) -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/MAC/Poly1305.hs b/Crypto/MAC/Poly1305.hs
--- a/Crypto/MAC/Poly1305.hs
+++ b/Crypto/MAC/Poly1305.hs
@@ -14,7 +14,7 @@
     ( Ctx
     , State
     , Auth(..)
-
+    , authTag
     -- * Incremental MAC Functions
     , initialize -- :: State
     , update     -- :: State -> ByteString -> State
@@ -36,12 +36,18 @@
 newtype State = State ScrubbedBytes
     deriving (ByteArrayAccess)
 
+-- | Poly1305 State. use State instead of Ctx
 type Ctx = State
 {-# DEPRECATED Ctx "use Poly1305 State instead" #-}
 
 -- | Poly1305 Auth
 newtype Auth = Auth Bytes
     deriving (ByteArrayAccess,NFData)
+
+authTag :: ByteArrayAccess b => b -> CryptoFailable Auth
+authTag b
+    | B.length b /= 16 = CryptoFailed $ CryptoError_AuthenticationTagSizeInvalid
+    | otherwise        = CryptoPassed $ Auth $ B.convert b
 
 instance Eq Auth where
     (Auth a1) == (Auth a2) = B.constEq a1 a2
diff --git a/Crypto/Number/Compat.hs b/Crypto/Number/Compat.hs
--- a/Crypto/Number/Compat.hs
+++ b/Crypto/Number/Compat.hs
@@ -116,7 +116,7 @@
 gmpTestPrimeMillerRabin _ _ = GmpUnsupported
 #endif
 
--- | Return the size in bytes of a integer
+-- | Return the size in bytes of an integer
 gmpSizeInBytes :: Integer -> GmpSupported Int
 #if MIN_VERSION_integer_gmp(0,5,1)
 gmpSizeInBytes n = GmpSupported (I# (word2Int# (sizeInBaseInteger n 256#)))
@@ -124,6 +124,7 @@
 gmpSizeInBytes _ = GmpUnsupported
 #endif
 
+-- | Return the size in bits of an integer
 gmpSizeInBits :: Integer -> GmpSupported Int
 #if MIN_VERSION_integer_gmp(0,5,1)
 gmpSizeInBits n = GmpSupported (I# (word2Int# (sizeInBaseInteger n 2#)))
diff --git a/Crypto/Number/Serialize.hs b/Crypto/Number/Serialize.hs
--- a/Crypto/Number/Serialize.hs
+++ b/Crypto/Number/Serialize.hs
@@ -33,8 +33,8 @@
         !sz = numBytes m
 
 -- | just like i2osp, but take an extra parameter for size.
--- if the number is too big to fit in @len bytes, nothing is returned
--- otherwise the number is padded with 0 to fit the @len required.
+-- if the number is too big to fit in @len@ bytes, 'Nothing' is returned
+-- otherwise the number is padded with 0 to fit the @len@ required.
 i2ospOf :: B.ByteArray ba => Int -> Integer -> Maybe ba
 i2ospOf len m
     | len <= 0  = Nothing
diff --git a/Crypto/PubKey/Curve25519.hs b/Crypto/PubKey/Curve25519.hs
--- a/Crypto/PubKey/Curve25519.hs
+++ b/Crypto/PubKey/Curve25519.hs
@@ -26,6 +26,7 @@
 import           Foreign.Ptr
 import           GHC.Ptr
 
+import           Crypto.Error
 import           Crypto.Internal.Compat
 import           Crypto.Internal.Imports
 import           Crypto.Internal.ByteArray (ByteArrayAccess, ScrubbedBytes, Bytes, withByteArray)
@@ -45,21 +46,21 @@
     deriving (Show,Eq,ByteArrayAccess,NFData)
 
 -- | Try to build a public key from a bytearray
-publicKey :: ByteArrayAccess bs => bs -> Either String PublicKey
+publicKey :: ByteArrayAccess bs => bs -> CryptoFailable PublicKey
 publicKey bs
-    | B.length bs == 32 = Right $ PublicKey $ B.copyAndFreeze bs (\_ -> return ())
-    | otherwise               = Left "invalid public key size"
+    | B.length bs == 32 = CryptoPassed $ PublicKey $ B.copyAndFreeze bs (\_ -> return ())
+    | otherwise         = CryptoFailed CryptoError_PublicKeySizeInvalid
 
 -- | Try to build a secret key from a bytearray
-secretKey :: ByteArrayAccess bs => bs -> Either String SecretKey
+secretKey :: ByteArrayAccess bs => bs -> CryptoFailable SecretKey
 secretKey bs
     | B.length bs == 32 = unsafeDoIO $ do
         withByteArray bs $ \inp -> do
             valid <- isValidPtr inp
             if valid
-                then (Right . SecretKey) <$> B.copy bs (\_ -> return ())
-                else return $ Left "invalid secret key"
-    | otherwise = Left "secret key invalid size"
+                then (CryptoPassed . SecretKey) <$> B.copy bs (\_ -> return ())
+                else return $ CryptoFailed CryptoError_SecretKeyStructureInvalid
+    | otherwise = CryptoFailed CryptoError_SecretKeySizeInvalid
   where
         --  e[0] &= 0xf8;
         --  e[31] &= 0x7f;
@@ -80,10 +81,10 @@
 {-# NOINLINE secretKey #-}
 
 -- | Create a DhSecret from a bytearray object
-dhSecret :: ByteArrayAccess b => b -> Either String DhSecret
+dhSecret :: ByteArrayAccess b => b -> CryptoFailable DhSecret
 dhSecret bs
-    | B.length bs == 32 = Right $ DhSecret $ B.copyAndFreeze bs (\_ -> return ())
-    | otherwise         = Left "invalid dh secret size"
+    | B.length bs == 32 = CryptoPassed $ DhSecret $ B.copyAndFreeze bs (\_ -> return ())
+    | otherwise         = CryptoFailed CryptoError_SharedSecretSizeInvalid
 
 -- | Compute the Diffie Hellman secret from a public key and a secret key
 dh :: PublicKey -> SecretKey -> DhSecret
diff --git a/Crypto/PubKey/DH.hs b/Crypto/PubKey/DH.hs
--- a/Crypto/PubKey/DH.hs
+++ b/Crypto/PubKey/DH.hs
@@ -46,7 +46,10 @@
 
 -- | generate params from a specific generator (2 or 5 are common values)
 -- we generate a safe prime (a prime number of the form 2p+1 where p is also prime)
-generateParams :: MonadRandom m => Int -> Integer -> m Params
+generateParams :: MonadRandom m =>
+                  Int                   -- ^ number of bits
+               -> Integer               -- ^ generator
+               -> m Params
 generateParams bits generator =
     (\p -> Params p generator) <$> generateSafePrime bits
 
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
@@ -78,6 +78,7 @@
 -- Point methods
 ------------------------------------------------------------------------
 
+-- | Get the base point for the P256 Curve
 pointBase :: Point
 pointBase =
     case scalarFromInteger 1 of
@@ -127,6 +128,7 @@
     r <- ccryptonite_p256_is_valid_point px py
     return (r /= 0)
 
+-- | Convert a point to (x,y) Integers
 pointToIntegers :: Point -> (Integer, Integer)
 pointToIntegers p = unsafeDoIO $ withPoint p $ \px py ->
     allocTemp 32 (serialize (castPtr px) (castPtr py))
@@ -138,6 +140,7 @@
         y <- os2ip temp scalarSize
         return (x,y)
 
+-- | Convert from (x,y) Integers to a point
 pointFromIntegers :: (Integer, Integer) -> Point
 pointFromIntegers (x,y) = withNewPoint $ \dx dy ->
     allocTemp scalarSize (\temp -> fill temp (castPtr dx) x >> fill temp (castPtr dy) y)
@@ -154,11 +157,13 @@
         -- then fill dest with the P256 scalar from temp
         ccryptonite_p256_from_bin temp dest
 
+-- | Convert a point to a binary representation
 pointToBinary :: ByteArray ba => Point -> ba
 pointToBinary p = B.unsafeCreate pointSize $ \dst -> withPoint p $ \px py -> do
     ccryptonite_p256_to_bin (castPtr px) dst
     ccryptonite_p256_to_bin (castPtr py) (dst `plusPtr` 32)
 
+-- | Convert from binary to a point
 pointFromBinary :: ByteArrayAccess ba => ba -> CryptoFailable Point
 pointFromBinary ba
     | B.length ba /= pointSize = CryptoFailed $ CryptoError_PublicKeySizeInvalid
@@ -184,6 +189,7 @@
 scalarZero :: Scalar
 scalarZero = withNewScalarFreeze $ \d -> ccryptonite_p256_init d
 
+-- | Check if the scalar is 0
 scalarIsZero :: Scalar -> Bool
 scalarIsZero s = unsafeDoIO $ withScalar s $ \d -> do
     result <- ccryptonite_p256_is_zero d
@@ -250,10 +256,12 @@
     ccryptonite_p256_to_bin p b
 {-# NOINLINE scalarToBinary #-}
 
+-- | Convert from an Integer to a P256 Scalar
 scalarFromInteger :: Integer -> CryptoFailable Scalar
 scalarFromInteger i =
     maybe (CryptoFailed CryptoError_SecretKeySizeInvalid) scalarFromBinary (S.i2ospOf 32 i :: Maybe Bytes)
 
+-- | Convert from a P256 Scalar to an Integer
 scalarToInteger :: Scalar -> Integer
 scalarToInteger s = S.os2ip (scalarToBinary s :: Bytes)
 
diff --git a/Crypto/PubKey/ECC/Prim.hs b/Crypto/PubKey/ECC/Prim.hs
--- a/Crypto/PubKey/ECC/Prim.hs
+++ b/Crypto/PubKey/ECC/Prim.hs
@@ -18,6 +18,7 @@
 import Crypto.PubKey.ECC.Types
 import Crypto.Random
 
+-- | Generate a valid scalar for a specific Curve
 scalarGenerate :: MonadRandom randomly => Curve -> randomly PrivateNumber
 scalarGenerate curve = generateBetween 1 (n - 1)
   where
diff --git a/Crypto/PubKey/RSA/PKCS15.hs b/Crypto/PubKey/RSA/PKCS15.hs
--- a/Crypto/PubKey/RSA/PKCS15.hs
+++ b/Crypto/PubKey/RSA/PKCS15.hs
@@ -40,6 +40,7 @@
 -- a ASN1 wrapped description the algorithm plus the content
 -- of the digest.
 class HashAlgorithm hashAlg => HashAlgorithmASN1 hashAlg where
+    -- | Convert a Digest into an ASN1 wrapped descriptive ByteArray
     hashDigestASN1 :: ByteArray out => Digest hashAlg -> out
 
 -- http://uk.emc.com/emc-plus/rsa-labs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf
diff --git a/cbits/blake2/blake2-config.h b/cbits/blake2/blake2-config.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2-config.h
@@ -0,0 +1,72 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2_CONFIG_H__
+#define __BLAKE2_CONFIG_H__
+
+// These don't work everywhere
+#if defined(__SSE2__)
+#define HAVE_SSE2
+#endif
+
+#if defined(__SSSE3__)
+#define HAVE_SSSE3
+#endif
+
+#if defined(__SSE4_1__)
+#define HAVE_SSE41
+#endif
+
+#if defined(__AVX__)
+#define HAVE_AVX
+#endif
+
+#if defined(__XOP__)
+#define HAVE_XOP
+#endif
+
+
+#ifdef HAVE_AVX2
+#ifndef HAVE_AVX
+#define HAVE_AVX
+#endif
+#endif
+
+#ifdef HAVE_XOP
+#ifndef HAVE_AVX
+#define HAVE_AVX
+#endif
+#endif
+
+#ifdef HAVE_AVX
+#ifndef HAVE_SSE41
+#define HAVE_SSE41
+#endif
+#endif
+
+#ifdef HAVE_SSE41
+#ifndef HAVE_SSSE3
+#define HAVE_SSSE3
+#endif
+#endif
+
+#ifdef HAVE_SSSE3
+#define HAVE_SSE2
+#endif
+
+#if !defined(HAVE_SSE2)
+#error "This code requires at least SSE2."
+#endif
+
+#endif
+
diff --git a/cbits/blake2/blake2-impl.h b/cbits/blake2/blake2-impl.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2-impl.h
@@ -0,0 +1,136 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2_IMPL_H__
+#define __BLAKE2_IMPL_H__
+
+#include <stdint.h>
+
+static inline uint32_t load32( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint32_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  uint32_t w = *p++;
+  w |= ( uint32_t )( *p++ ) <<  8;
+  w |= ( uint32_t )( *p++ ) << 16;
+  w |= ( uint32_t )( *p++ ) << 24;
+  return w;
+#endif
+}
+
+static inline uint64_t load64( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint64_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  uint64_t w = *p++;
+  w |= ( uint64_t )( *p++ ) <<  8;
+  w |= ( uint64_t )( *p++ ) << 16;
+  w |= ( uint64_t )( *p++ ) << 24;
+  w |= ( uint64_t )( *p++ ) << 32;
+  w |= ( uint64_t )( *p++ ) << 40;
+  w |= ( uint64_t )( *p++ ) << 48;
+  w |= ( uint64_t )( *p++ ) << 56;
+  return w;
+#endif
+}
+
+static inline void store32( void *dst, uint32_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w;
+#endif
+}
+
+static inline void store64( void *dst, uint64_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w;
+#endif
+}
+
+static inline uint64_t load48( const void *src )
+{
+  const uint8_t *p = ( const uint8_t * )src;
+  uint64_t w = *p++;
+  w |= ( uint64_t )( *p++ ) <<  8;
+  w |= ( uint64_t )( *p++ ) << 16;
+  w |= ( uint64_t )( *p++ ) << 24;
+  w |= ( uint64_t )( *p++ ) << 32;
+  w |= ( uint64_t )( *p++ ) << 40;
+  return w;
+}
+
+static inline void store48( void *dst, uint64_t w )
+{
+  uint8_t *p = ( uint8_t * )dst;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w;
+}
+
+static inline uint32_t rotl32( const uint32_t w, const unsigned c )
+{
+  return ( w << c ) | ( w >> ( 32 - c ) );
+}
+
+static inline uint64_t rotl64( const uint64_t w, const unsigned c )
+{
+  return ( w << c ) | ( w >> ( 64 - c ) );
+}
+
+static inline uint32_t rotr32( const uint32_t w, const unsigned c )
+{
+  return ( w >> c ) | ( w << ( 32 - c ) );
+}
+
+static inline uint64_t rotr64( const uint64_t w, const unsigned c )
+{
+  return ( w >> c ) | ( w << ( 64 - c ) );
+}
+
+/* prevents compiler optimizing out memset() */
+static inline void secure_zero_memory( void *v, size_t n )
+{
+  volatile uint8_t *p = ( volatile uint8_t * )v;
+  while( n-- ) *p++ = 0;
+}
+
+#endif
+
diff --git a/cbits/blake2/blake2.h b/cbits/blake2/blake2.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2.h
@@ -0,0 +1,150 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2_H__
+#define __BLAKE2_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+  enum blake2s_constant
+  {
+    BLAKE2S_BLOCKBYTES = 64,
+    BLAKE2S_OUTBYTES   = 32,
+    BLAKE2S_KEYBYTES   = 32,
+    BLAKE2S_SALTBYTES  = 8,
+    BLAKE2S_PERSONALBYTES = 8
+  };
+
+  enum blake2b_constant
+  {
+    BLAKE2B_BLOCKBYTES = 128,
+    BLAKE2B_OUTBYTES   = 64,
+    BLAKE2B_KEYBYTES   = 64,
+    BLAKE2B_SALTBYTES  = 16,
+    BLAKE2B_PERSONALBYTES = 16
+  };
+
+#pragma pack(push, 1)
+  typedef struct __blake2s_param
+  {
+    uint8_t  digest_length; // 1
+    uint8_t  key_length;    // 2
+    uint8_t  fanout;        // 3
+    uint8_t  depth;         // 4
+    uint32_t leaf_length;   // 8
+    uint8_t  node_offset[6];// 14
+    uint8_t  node_depth;    // 15
+    uint8_t  inner_length;  // 16
+    // uint8_t  reserved[0];
+    uint8_t  salt[BLAKE2S_SALTBYTES]; // 24
+    uint8_t  personal[BLAKE2S_PERSONALBYTES];  // 32
+  } blake2s_param;
+
+  typedef struct __blake2s_state
+  {
+    uint32_t h[8];
+    uint32_t t[2];
+    uint32_t f[2];
+    uint8_t  buf[2 * BLAKE2S_BLOCKBYTES];
+    size_t   buflen;
+    uint8_t  last_node;
+  } blake2s_state;
+
+  typedef struct __blake2b_param
+  {
+    uint8_t  digest_length; // 1
+    uint8_t  key_length;    // 2
+    uint8_t  fanout;        // 3
+    uint8_t  depth;         // 4
+    uint32_t leaf_length;   // 8
+    uint64_t node_offset;   // 16
+    uint8_t  node_depth;    // 17
+    uint8_t  inner_length;  // 18
+    uint8_t  reserved[14];  // 32
+    uint8_t  salt[BLAKE2B_SALTBYTES]; // 48
+    uint8_t  personal[BLAKE2B_PERSONALBYTES];  // 64
+  } blake2b_param;
+
+  typedef struct __blake2b_state
+  {
+    uint64_t h[8];
+    uint64_t t[2];
+    uint64_t f[2];
+    uint8_t  buf[2 * BLAKE2B_BLOCKBYTES];
+    size_t   buflen;
+    uint8_t  last_node;
+  } blake2b_state;
+
+  typedef struct __blake2sp_state
+  {
+    blake2s_state S[8][1];
+    blake2s_state R[1];
+    uint8_t buf[8 * BLAKE2S_BLOCKBYTES];
+    size_t  buflen;
+  } blake2sp_state;
+
+  typedef struct __blake2bp_state
+  {
+    blake2b_state S[4][1];
+    blake2b_state R[1];
+    uint8_t buf[4 * BLAKE2B_BLOCKBYTES];
+    size_t  buflen;
+  } blake2bp_state;
+#pragma pack(pop)
+
+  // Streaming API
+  int blake2s_init( blake2s_state *S, const uint8_t outlen );
+  int blake2s_init_key( blake2s_state *S, const uint8_t outlen, const void *key, const uint8_t keylen );
+  int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+  int blake2s_update( blake2s_state *S, const uint8_t *in, uint64_t inlen );
+  int blake2s_final( blake2s_state *S, uint8_t *out, uint8_t outlen );
+
+  int blake2b_init( blake2b_state *S, const uint8_t outlen );
+  int blake2b_init_key( blake2b_state *S, const uint8_t outlen, const void *key, const uint8_t keylen );
+  int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+  int blake2b_update( blake2b_state *S, const uint8_t *in, uint64_t inlen );
+  int blake2b_final( blake2b_state *S, uint8_t *out, uint8_t outlen );
+
+  int blake2sp_init( blake2sp_state *S, const uint8_t outlen );
+  int blake2sp_init_key( blake2sp_state *S, const uint8_t outlen, const void *key, const uint8_t keylen );
+  int blake2sp_update( blake2sp_state *S, const uint8_t *in, uint64_t inlen );
+  int blake2sp_final( blake2sp_state *S, uint8_t *out, uint8_t outlen );
+
+  int blake2bp_init( blake2bp_state *S, const uint8_t outlen );
+  int blake2bp_init_key( blake2bp_state *S, const uint8_t outlen, const void *key, const uint8_t keylen );
+  int blake2bp_update( blake2bp_state *S, const uint8_t *in, uint64_t inlen );
+  int blake2bp_final( blake2bp_state *S, uint8_t *out, uint8_t outlen );
+
+  // Simple API
+  int blake2s( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen );
+  int blake2b( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen );
+
+  int blake2sp( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen );
+  int blake2bp( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen );
+
+  static inline int blake2( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen )
+  {
+    return blake2b( out, in, key, outlen, inlen, keylen );
+  }
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/cbits/blake2/blake2b-load-sse2.h b/cbits/blake2/blake2b-load-sse2.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2b-load-sse2.h
@@ -0,0 +1,68 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_LOAD_SSE2_H__
+#define __BLAKE2B_LOAD_SSE2_H__
+
+#define LOAD_MSG_0_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4)
+#define LOAD_MSG_0_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5)
+#define LOAD_MSG_0_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_0_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13)
+#define LOAD_MSG_1_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9)
+#define LOAD_MSG_1_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15)
+#define LOAD_MSG_1_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11)
+#define LOAD_MSG_1_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7)
+#define LOAD_MSG_2_1(b0, b1) b0 = _mm_set_epi64x(m12, m11); b1 = _mm_set_epi64x(m15, m5)
+#define LOAD_MSG_2_2(b0, b1) b0 = _mm_set_epi64x(m0, m8); b1 = _mm_set_epi64x(m13, m2)
+#define LOAD_MSG_2_3(b0, b1) b0 = _mm_set_epi64x(m3, m10); b1 = _mm_set_epi64x(m9, m7)
+#define LOAD_MSG_2_4(b0, b1) b0 = _mm_set_epi64x(m6, m14); b1 = _mm_set_epi64x(m4, m1)
+#define LOAD_MSG_3_1(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m13)
+#define LOAD_MSG_3_2(b0, b1) b0 = _mm_set_epi64x(m1, m9); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_3_3(b0, b1) b0 = _mm_set_epi64x(m5, m2); b1 = _mm_set_epi64x(m15, m4)
+#define LOAD_MSG_3_4(b0, b1) b0 = _mm_set_epi64x(m10, m6); b1 = _mm_set_epi64x(m8, m0)
+#define LOAD_MSG_4_1(b0, b1) b0 = _mm_set_epi64x(m5, m9); b1 = _mm_set_epi64x(m10, m2)
+#define LOAD_MSG_4_2(b0, b1) b0 = _mm_set_epi64x(m7, m0); b1 = _mm_set_epi64x(m15, m4)
+#define LOAD_MSG_4_3(b0, b1) b0 = _mm_set_epi64x(m11, m14); b1 = _mm_set_epi64x(m3, m6)
+#define LOAD_MSG_4_4(b0, b1) b0 = _mm_set_epi64x(m12, m1); b1 = _mm_set_epi64x(m13, m8)
+#define LOAD_MSG_5_1(b0, b1) b0 = _mm_set_epi64x(m6, m2); b1 = _mm_set_epi64x(m8, m0)
+#define LOAD_MSG_5_2(b0, b1) b0 = _mm_set_epi64x(m10, m12); b1 = _mm_set_epi64x(m3, m11)
+#define LOAD_MSG_5_3(b0, b1) b0 = _mm_set_epi64x(m7, m4); b1 = _mm_set_epi64x(m1, m15)
+#define LOAD_MSG_5_4(b0, b1) b0 = _mm_set_epi64x(m5, m13); b1 = _mm_set_epi64x(m9, m14)
+#define LOAD_MSG_6_1(b0, b1) b0 = _mm_set_epi64x(m1, m12); b1 = _mm_set_epi64x(m4, m14)
+#define LOAD_MSG_6_2(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m10, m13)
+#define LOAD_MSG_6_3(b0, b1) b0 = _mm_set_epi64x(m6, m0); b1 = _mm_set_epi64x(m8, m9)
+#define LOAD_MSG_6_4(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m2)
+#define LOAD_MSG_7_1(b0, b1) b0 = _mm_set_epi64x(m7, m13); b1 = _mm_set_epi64x(m3, m12)
+#define LOAD_MSG_7_2(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m9, m1)
+#define LOAD_MSG_7_3(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m2, m8)
+#define LOAD_MSG_7_4(b0, b1) b0 = _mm_set_epi64x(m4, m0); b1 = _mm_set_epi64x(m10, m6)
+#define LOAD_MSG_8_1(b0, b1) b0 = _mm_set_epi64x(m14, m6); b1 = _mm_set_epi64x(m0, m11)
+#define LOAD_MSG_8_2(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m8, m3)
+#define LOAD_MSG_8_3(b0, b1) b0 = _mm_set_epi64x(m13, m12); b1 = _mm_set_epi64x(m10, m1)
+#define LOAD_MSG_8_4(b0, b1) b0 = _mm_set_epi64x(m7, m2); b1 = _mm_set_epi64x(m5, m4)
+#define LOAD_MSG_9_1(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m1, m7)
+#define LOAD_MSG_9_2(b0, b1) b0 = _mm_set_epi64x(m4, m2); b1 = _mm_set_epi64x(m5, m6)
+#define LOAD_MSG_9_3(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m13, m3)
+#define LOAD_MSG_9_4(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m0, m12)
+#define LOAD_MSG_10_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4)
+#define LOAD_MSG_10_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5)
+#define LOAD_MSG_10_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_10_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13)
+#define LOAD_MSG_11_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9)
+#define LOAD_MSG_11_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15)
+#define LOAD_MSG_11_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11)
+#define LOAD_MSG_11_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7)
+
+
+#endif
+
diff --git a/cbits/blake2/blake2b-load-sse41.h b/cbits/blake2/blake2b-load-sse41.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2b-load-sse41.h
@@ -0,0 +1,402 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_LOAD_SSE41_H__
+#define __BLAKE2B_LOAD_SSE41_H__
+
+#define LOAD_MSG_0_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m1); \
+b1 = _mm_unpacklo_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_0_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m0, m1); \
+b1 = _mm_unpackhi_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_0_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m5); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_0_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m5); \
+b1 = _mm_unpackhi_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_1_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m2); \
+b1 = _mm_unpackhi_epi64(m4, m6); \
+} while(0)
+
+
+#define LOAD_MSG_1_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_alignr_epi8(m3, m7, 8); \
+} while(0)
+
+
+#define LOAD_MSG_1_3(b0, b1) \
+do \
+{ \
+b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \
+b1 = _mm_unpackhi_epi64(m5, m2); \
+} while(0)
+
+
+#define LOAD_MSG_1_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m1); \
+b1 = _mm_unpackhi_epi64(m3, m1); \
+} while(0)
+
+
+#define LOAD_MSG_2_1(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m6, m5, 8); \
+b1 = _mm_unpackhi_epi64(m2, m7); \
+} while(0)
+
+
+#define LOAD_MSG_2_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m0); \
+b1 = _mm_blend_epi16(m1, m6, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_2_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m5, m1, 0xF0); \
+b1 = _mm_unpackhi_epi64(m3, m4); \
+} while(0)
+
+
+#define LOAD_MSG_2_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m3); \
+b1 = _mm_alignr_epi8(m2, m0, 8); \
+} while(0)
+
+
+#define LOAD_MSG_3_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m3, m1); \
+b1 = _mm_unpackhi_epi64(m6, m5); \
+} while(0)
+
+
+#define LOAD_MSG_3_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m0); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_3_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m1, m2, 0xF0); \
+b1 = _mm_blend_epi16(m2, m7, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_3_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m3, m5); \
+b1 = _mm_unpacklo_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_4_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m2); \
+b1 = _mm_unpacklo_epi64(m1, m5); \
+} while(0)
+
+
+#define LOAD_MSG_4_2(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m0, m3, 0xF0); \
+b1 = _mm_blend_epi16(m2, m7, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_4_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m7, m5, 0xF0); \
+b1 = _mm_blend_epi16(m3, m1, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_4_4(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m6, m0, 8); \
+b1 = _mm_blend_epi16(m4, m6, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_5_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m1, m3); \
+b1 = _mm_unpacklo_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_5_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m5); \
+b1 = _mm_unpackhi_epi64(m5, m1); \
+} while(0)
+
+
+#define LOAD_MSG_5_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m2, m3, 0xF0); \
+b1 = _mm_unpackhi_epi64(m7, m0); \
+} while(0)
+
+
+#define LOAD_MSG_5_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m6, m2); \
+b1 = _mm_blend_epi16(m7, m4, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_6_1(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m6, m0, 0xF0); \
+b1 = _mm_unpacklo_epi64(m7, m2); \
+} while(0)
+
+
+#define LOAD_MSG_6_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m2, m7); \
+b1 = _mm_alignr_epi8(m5, m6, 8); \
+} while(0)
+
+
+#define LOAD_MSG_6_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m3); \
+b1 = _mm_shuffle_epi32(m4, _MM_SHUFFLE(1,0,3,2)); \
+} while(0)
+
+
+#define LOAD_MSG_6_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m3, m1); \
+b1 = _mm_blend_epi16(m1, m5, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_7_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m6, m3); \
+b1 = _mm_blend_epi16(m6, m1, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_7_2(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m7, m5, 8); \
+b1 = _mm_unpackhi_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_7_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m2, m7); \
+b1 = _mm_unpacklo_epi64(m4, m1); \
+} while(0)
+
+
+#define LOAD_MSG_7_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m2); \
+b1 = _mm_unpacklo_epi64(m3, m5); \
+} while(0)
+
+
+#define LOAD_MSG_8_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m3, m7); \
+b1 = _mm_alignr_epi8(m0, m5, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m7, m4); \
+b1 = _mm_alignr_epi8(m4, m1, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_3(b0, b1) \
+do \
+{ \
+b0 = m6; \
+b1 = _mm_alignr_epi8(m5, m0, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_4(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m1, m3, 0xF0); \
+b1 = m2; \
+} while(0)
+
+
+#define LOAD_MSG_9_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_unpackhi_epi64(m3, m0); \
+} while(0)
+
+
+#define LOAD_MSG_9_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m1, m2); \
+b1 = _mm_blend_epi16(m3, m2, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_9_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m7, m4); \
+b1 = _mm_unpackhi_epi64(m1, m6); \
+} while(0)
+
+
+#define LOAD_MSG_9_4(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m7, m5, 8); \
+b1 = _mm_unpacklo_epi64(m6, m0); \
+} while(0)
+
+
+#define LOAD_MSG_10_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m1); \
+b1 = _mm_unpacklo_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_10_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m0, m1); \
+b1 = _mm_unpackhi_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_10_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m5); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_10_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m5); \
+b1 = _mm_unpackhi_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_11_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m2); \
+b1 = _mm_unpackhi_epi64(m4, m6); \
+} while(0)
+
+
+#define LOAD_MSG_11_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_alignr_epi8(m3, m7, 8); \
+} while(0)
+
+
+#define LOAD_MSG_11_3(b0, b1) \
+do \
+{ \
+b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \
+b1 = _mm_unpackhi_epi64(m5, m2); \
+} while(0)
+
+
+#define LOAD_MSG_11_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m1); \
+b1 = _mm_unpackhi_epi64(m3, m1); \
+} while(0)
+
+
+#endif
+
diff --git a/cbits/blake2/blake2b-round.h b/cbits/blake2/blake2b-round.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2b-round.h
@@ -0,0 +1,157 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_ROUND_H__
+#define __BLAKE2B_ROUND_H__
+
+#define LOADU(p)  _mm_loadu_si128( (const __m128i *)(p) )
+#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r)
+
+#define TOF(reg) _mm_castsi128_ps((reg))
+#define TOI(reg) _mm_castps_si128((reg))
+
+#define LIKELY(x) __builtin_expect((x),1)
+
+
+/* Microarchitecture-specific macros */
+#ifndef HAVE_XOP
+#ifdef HAVE_SSSE3
+#define _mm_roti_epi64(x, c) \
+    (-(c) == 32) ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2,3,0,1))  \
+    : (-(c) == 24) ? _mm_shuffle_epi8((x), r24) \
+    : (-(c) == 16) ? _mm_shuffle_epi8((x), r16) \
+    : (-(c) == 63) ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_add_epi64((x), (x)))  \
+    : _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_slli_epi64((x), 64-(-(c))))
+#else
+#define _mm_roti_epi64(r, c) _mm_xor_si128(_mm_srli_epi64( (r), -(c) ),_mm_slli_epi64( (r), 64-(-(c)) ))
+#endif
+#else
+/* ... */
+#endif
+
+
+
+#define G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \
+  row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \
+  row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \
+  \
+  row4l = _mm_xor_si128(row4l, row1l); \
+  row4h = _mm_xor_si128(row4h, row1h); \
+  \
+  row4l = _mm_roti_epi64(row4l, -32); \
+  row4h = _mm_roti_epi64(row4h, -32); \
+  \
+  row3l = _mm_add_epi64(row3l, row4l); \
+  row3h = _mm_add_epi64(row3h, row4h); \
+  \
+  row2l = _mm_xor_si128(row2l, row3l); \
+  row2h = _mm_xor_si128(row2h, row3h); \
+  \
+  row2l = _mm_roti_epi64(row2l, -24); \
+  row2h = _mm_roti_epi64(row2h, -24); \
+ 
+#define G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \
+  row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \
+  row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \
+  \
+  row4l = _mm_xor_si128(row4l, row1l); \
+  row4h = _mm_xor_si128(row4h, row1h); \
+  \
+  row4l = _mm_roti_epi64(row4l, -16); \
+  row4h = _mm_roti_epi64(row4h, -16); \
+  \
+  row3l = _mm_add_epi64(row3l, row4l); \
+  row3h = _mm_add_epi64(row3h, row4h); \
+  \
+  row2l = _mm_xor_si128(row2l, row3l); \
+  row2h = _mm_xor_si128(row2h, row3h); \
+  \
+  row2l = _mm_roti_epi64(row2l, -63); \
+  row2h = _mm_roti_epi64(row2h, -63); \
+ 
+#if defined(HAVE_SSSE3)
+#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+  t0 = _mm_alignr_epi8(row2h, row2l, 8); \
+  t1 = _mm_alignr_epi8(row2l, row2h, 8); \
+  row2l = t0; \
+  row2h = t1; \
+  \
+  t0 = row3l; \
+  row3l = row3h; \
+  row3h = t0;    \
+  \
+  t0 = _mm_alignr_epi8(row4h, row4l, 8); \
+  t1 = _mm_alignr_epi8(row4l, row4h, 8); \
+  row4l = t1; \
+  row4h = t0;
+
+#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+  t0 = _mm_alignr_epi8(row2l, row2h, 8); \
+  t1 = _mm_alignr_epi8(row2h, row2l, 8); \
+  row2l = t0; \
+  row2h = t1; \
+  \
+  t0 = row3l; \
+  row3l = row3h; \
+  row3h = t0; \
+  \
+  t0 = _mm_alignr_epi8(row4l, row4h, 8); \
+  t1 = _mm_alignr_epi8(row4h, row4l, 8); \
+  row4l = t1; \
+  row4h = t0;
+#else
+
+#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+  t0 = row4l;\
+  t1 = row2l;\
+  row4l = row3l;\
+  row3l = row3h;\
+  row3h = row4l;\
+  row4l = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t0, t0)); \
+  row4h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row4h, row4h)); \
+  row2l = _mm_unpackhi_epi64(row2l, _mm_unpacklo_epi64(row2h, row2h)); \
+  row2h = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(t1, t1))
+
+#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+  t0 = row3l;\
+  row3l = row3h;\
+  row3h = t0;\
+  t0 = row2l;\
+  t1 = row4l;\
+  row2l = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(row2l, row2l)); \
+  row2h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row2h, row2h)); \
+  row4l = _mm_unpackhi_epi64(row4l, _mm_unpacklo_epi64(row4h, row4h)); \
+  row4h = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t1, t1))
+
+#endif
+
+#if defined(HAVE_SSE41)
+#include "blake2b-load-sse41.h"
+#else
+#include "blake2b-load-sse2.h"
+#endif
+
+#define ROUND(r) \
+  LOAD_MSG_ ##r ##_1(b0, b1); \
+  G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+  LOAD_MSG_ ##r ##_2(b0, b1); \
+  G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+  DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h); \
+  LOAD_MSG_ ##r ##_3(b0, b1); \
+  G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+  LOAD_MSG_ ##r ##_4(b0, b1); \
+  G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+  UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h);
+
+#endif
+
diff --git a/cbits/blake2/blake2b.c b/cbits/blake2/blake2b.c
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2b.c
@@ -0,0 +1,403 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#include "blake2-config.h"
+
+#ifdef _MSC_VER
+#include <intrin.h> /* for _mm_set_epi64x */
+#endif
+#include <emmintrin.h>
+#if defined(HAVE_SSSE3)
+#include <tmmintrin.h>
+#endif
+#if defined(HAVE_SSE41)
+#include <smmintrin.h>
+#endif
+#if defined(HAVE_AVX)
+#include <immintrin.h>
+#endif
+#if defined(HAVE_XOP)
+#include <x86intrin.h>
+#endif
+
+#include "blake2b-round.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+  0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+  0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+  0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+  0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,
+  { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,
+  {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,
+  {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,
+  {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,
+  { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,
+  { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,
+  {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,
+  { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,
+  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 }
+};
+
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2b_set_lastnode( blake2b_state *S )
+{
+  S->f[1] = -1;
+  return 0;
+}
+
+static inline int blake2b_clear_lastnode( blake2b_state *S )
+{
+  S->f[1] = 0;
+  return 0;
+}
+
+static inline int blake2b_set_lastblock( blake2b_state *S )
+{
+  if( S->last_node ) blake2b_set_lastnode( S );
+
+  S->f[0] = -1;
+  return 0;
+}
+
+static inline int blake2b_clear_lastblock( blake2b_state *S )
+{
+  if( S->last_node ) blake2b_clear_lastnode( S );
+
+  S->f[0] = 0;
+  return 0;
+}
+
+
+static inline int blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
+{
+#if __x86_64__
+  // ADD/ADC chain
+  __uint128_t t = ( ( __uint128_t )S->t[1] << 64 ) | S->t[0];
+  t += inc;
+  S->t[0] = ( uint64_t )( t >>  0 );
+  S->t[1] = ( uint64_t )( t >> 64 );
+#else
+  S->t[0] += inc;
+  S->t[1] += ( S->t[0] < inc );
+#endif
+  return 0;
+}
+
+
+// Parameter-related functions
+static inline int blake2b_param_set_digest_length( blake2b_param *P, const uint8_t digest_length )
+{
+  P->digest_length = digest_length;
+  return 0;
+}
+
+static inline int blake2b_param_set_fanout( blake2b_param *P, const uint8_t fanout )
+{
+  P->fanout = fanout;
+  return 0;
+}
+
+static inline int blake2b_param_set_max_depth( blake2b_param *P, const uint8_t depth )
+{
+  P->depth = depth;
+  return 0;
+}
+
+static inline int blake2b_param_set_leaf_length( blake2b_param *P, const uint32_t leaf_length )
+{
+  P->leaf_length = leaf_length;
+  return 0;
+}
+
+static inline int blake2b_param_set_node_offset( blake2b_param *P, const uint64_t node_offset )
+{
+  P->node_offset = node_offset;
+  return 0;
+}
+
+static inline int blake2b_param_set_node_depth( blake2b_param *P, const uint8_t node_depth )
+{
+  P->node_depth = node_depth;
+  return 0;
+}
+
+static inline int blake2b_param_set_inner_length( blake2b_param *P, const uint8_t inner_length )
+{
+  P->inner_length = inner_length;
+  return 0;
+}
+
+static inline int blake2b_param_set_salt( blake2b_param *P, const uint8_t salt[BLAKE2B_SALTBYTES] )
+{
+  memcpy( P->salt, salt, BLAKE2B_SALTBYTES );
+  return 0;
+}
+
+static inline int blake2b_param_set_personal( blake2b_param *P, const uint8_t personal[BLAKE2B_PERSONALBYTES] )
+{
+  memcpy( P->personal, personal, BLAKE2B_PERSONALBYTES );
+  return 0;
+}
+
+static inline int blake2b_init0( blake2b_state *S )
+{
+  memset( S, 0, sizeof( blake2b_state ) );
+
+  for( int i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
+
+  return 0;
+}
+
+/* init xors IV with input parameter block */
+__attribute__((visibility ("hidden")))
+int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+  //blake2b_init0( S );
+  const uint8_t * v = ( const uint8_t * )( blake2b_IV );
+  const uint8_t * p = ( const uint8_t * )( P );
+  uint8_t * h = ( uint8_t * )( S->h );
+  /* IV XOR ParamBlock */
+  memset( S, 0, sizeof( blake2b_state ) );
+
+  for( int i = 0; i < BLAKE2B_OUTBYTES; ++i ) h[i] = v[i] ^ p[i];
+
+  return 0;
+}
+
+
+/* Some sort of default parameter block initialization, for sequential blake2b */
+__attribute__((visibility ("hidden")))
+int blake2b_init( blake2b_state *S, const uint8_t outlen )
+{
+  if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+  const blake2b_param P =
+  {
+    outlen,
+    0,
+    1,
+    1,
+    0,
+    0,
+    0,
+    0,
+    {0},
+    {0},
+    {0}
+  };
+  return blake2b_init_param( S, &P );
+}
+
+__attribute__((visibility ("hidden")))
+int blake2b_init_key( blake2b_state *S, const uint8_t outlen, const void *key, const uint8_t keylen )
+{
+  if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+  if ( ( !keylen ) || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+  const blake2b_param P =
+  {
+    outlen,
+    keylen,
+    1,
+    1,
+    0,
+    0,
+    0,
+    0,
+    {0},
+    {0},
+    {0}
+  };
+
+  if( blake2b_init_param( S, &P ) < 0 )
+    return 0;
+
+  {
+    uint8_t block[BLAKE2B_BLOCKBYTES];
+    memset( block, 0, BLAKE2B_BLOCKBYTES );
+    memcpy( block, key, keylen );
+    blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
+    secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+  }
+  return 0;
+}
+
+static inline int blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
+{
+  __m128i row1l, row1h;
+  __m128i row2l, row2h;
+  __m128i row3l, row3h;
+  __m128i row4l, row4h;
+  __m128i b0, b1;
+  __m128i t0, t1;
+#if defined(HAVE_SSSE3) && !defined(HAVE_XOP)
+  const __m128i r16 = _mm_setr_epi8( 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9 );
+  const __m128i r24 = _mm_setr_epi8( 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10 );
+#endif
+#if defined(HAVE_SSE41)
+  const __m128i m0 = LOADU( block + 00 );
+  const __m128i m1 = LOADU( block + 16 );
+  const __m128i m2 = LOADU( block + 32 );
+  const __m128i m3 = LOADU( block + 48 );
+  const __m128i m4 = LOADU( block + 64 );
+  const __m128i m5 = LOADU( block + 80 );
+  const __m128i m6 = LOADU( block + 96 );
+  const __m128i m7 = LOADU( block + 112 );
+#else
+  const uint64_t  m0 = ( ( uint64_t * )block )[ 0];
+  const uint64_t  m1 = ( ( uint64_t * )block )[ 1];
+  const uint64_t  m2 = ( ( uint64_t * )block )[ 2];
+  const uint64_t  m3 = ( ( uint64_t * )block )[ 3];
+  const uint64_t  m4 = ( ( uint64_t * )block )[ 4];
+  const uint64_t  m5 = ( ( uint64_t * )block )[ 5];
+  const uint64_t  m6 = ( ( uint64_t * )block )[ 6];
+  const uint64_t  m7 = ( ( uint64_t * )block )[ 7];
+  const uint64_t  m8 = ( ( uint64_t * )block )[ 8];
+  const uint64_t  m9 = ( ( uint64_t * )block )[ 9];
+  const uint64_t m10 = ( ( uint64_t * )block )[10];
+  const uint64_t m11 = ( ( uint64_t * )block )[11];
+  const uint64_t m12 = ( ( uint64_t * )block )[12];
+  const uint64_t m13 = ( ( uint64_t * )block )[13];
+  const uint64_t m14 = ( ( uint64_t * )block )[14];
+  const uint64_t m15 = ( ( uint64_t * )block )[15];
+#endif
+  row1l = LOADU( &S->h[0] );
+  row1h = LOADU( &S->h[2] );
+  row2l = LOADU( &S->h[4] );
+  row2h = LOADU( &S->h[6] );
+  row3l = LOADU( &blake2b_IV[0] );
+  row3h = LOADU( &blake2b_IV[2] );
+  row4l = _mm_xor_si128( LOADU( &blake2b_IV[4] ), LOADU( &S->t[0] ) );
+  row4h = _mm_xor_si128( LOADU( &blake2b_IV[6] ), LOADU( &S->f[0] ) );
+  ROUND( 0 );
+  ROUND( 1 );
+  ROUND( 2 );
+  ROUND( 3 );
+  ROUND( 4 );
+  ROUND( 5 );
+  ROUND( 6 );
+  ROUND( 7 );
+  ROUND( 8 );
+  ROUND( 9 );
+  ROUND( 10 );
+  ROUND( 11 );
+  row1l = _mm_xor_si128( row3l, row1l );
+  row1h = _mm_xor_si128( row3h, row1h );
+  STOREU( &S->h[0], _mm_xor_si128( LOADU( &S->h[0] ), row1l ) );
+  STOREU( &S->h[2], _mm_xor_si128( LOADU( &S->h[2] ), row1h ) );
+  row2l = _mm_xor_si128( row4l, row2l );
+  row2h = _mm_xor_si128( row4h, row2h );
+  STOREU( &S->h[4], _mm_xor_si128( LOADU( &S->h[4] ), row2l ) );
+  STOREU( &S->h[6], _mm_xor_si128( LOADU( &S->h[6] ), row2h ) );
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2b_update( blake2b_state *S, const uint8_t *in, uint64_t inlen )
+{
+  while( inlen > 0 )
+  {
+    size_t left = S->buflen;
+    size_t fill = 2 * BLAKE2B_BLOCKBYTES - left;
+
+    if( inlen > fill )
+    {
+      memcpy( S->buf + left, in, fill ); // Fill buffer
+      S->buflen += fill;
+      blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+      blake2b_compress( S, S->buf ); // Compress
+      memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); // Shift buffer left
+      S->buflen -= BLAKE2B_BLOCKBYTES;
+      in += fill;
+      inlen -= fill;
+    }
+    else // inlen <= fill
+    {
+      memcpy( S->buf + left, in, inlen );
+      S->buflen += inlen; // Be lazy, do not compress
+      in += inlen;
+      inlen -= inlen;
+    }
+  }
+
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2b_final( blake2b_state *S, uint8_t *out, uint8_t outlen )
+{
+  if( outlen > BLAKE2B_OUTBYTES )
+    return -1;
+
+  if( S->buflen > BLAKE2B_BLOCKBYTES )
+  {
+    blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+    blake2b_compress( S, S->buf );
+    S->buflen -= BLAKE2B_BLOCKBYTES;
+    memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, S->buflen );
+  }
+
+  blake2b_increment_counter( S, S->buflen );
+  blake2b_set_lastblock( S );
+  memset( S->buf + S->buflen, 0, 2 * BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
+  blake2b_compress( S, S->buf );
+  memcpy( out, &S->h[0], outlen );
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2b( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen )
+{
+  blake2b_state S[1];
+
+  /* Verify parameters */
+  if ( NULL == in && inlen > 0 ) return -1;
+
+  if ( NULL == out ) return -1;
+
+  if( NULL == key && keylen > 0 ) return -1;
+
+  if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+  if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+  if( keylen )
+  {
+    if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+  }
+  else
+  {
+    if( blake2b_init( S, outlen ) < 0 ) return -1;
+  }
+
+  blake2b_update( S, ( const uint8_t * )in, inlen );
+  blake2b_final( S, out, outlen );
+  return 0;
+}
diff --git a/cbits/blake2/blake2bp.c b/cbits/blake2/blake2bp.c
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2bp.c
@@ -0,0 +1,263 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#define PARALLELISM_DEGREE 4
+
+static inline int blake2bp_init_leaf( blake2b_state *S, uint8_t outlen, uint8_t keylen, uint64_t offset )
+{
+  blake2b_param P[1];
+  P->digest_length = outlen;
+  P->key_length = keylen;
+  P->fanout = PARALLELISM_DEGREE;
+  P->depth = 2;
+  P->leaf_length = 0;
+  P->node_offset = offset;
+  P->node_depth = 0;
+  P->inner_length = BLAKE2B_OUTBYTES;
+  memset( P->reserved, 0, sizeof( P->reserved ) );
+  memset( P->salt, 0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+  return blake2b_init_param( S, P );
+}
+
+static inline int blake2bp_init_root( blake2b_state *S, uint8_t outlen, uint8_t keylen )
+{
+  blake2b_param P[1];
+  P->digest_length = outlen;
+  P->key_length = keylen;
+  P->fanout = PARALLELISM_DEGREE;
+  P->depth = 2;
+  P->leaf_length = 0;
+  P->node_offset = 0;
+  P->node_depth = 1;
+  P->inner_length = BLAKE2B_OUTBYTES;
+  memset( P->reserved, 0, sizeof( P->reserved ) );
+  memset( P->salt, 0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+  return blake2b_init_param( S, P );
+}
+
+__attribute__((visibility ("hidden")))
+int blake2bp_init( blake2bp_state *S, const uint8_t outlen )
+{
+  if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+  memset( S->buf, 0, sizeof( S->buf ) );
+  S->buflen = 0;
+
+  if( blake2bp_init_root( S->R, outlen, 0 ) < 0 )
+    return -1;
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2bp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1;
+
+  S->R->last_node = 1;
+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2bp_init_key( blake2bp_state *S, const uint8_t outlen, const void *key, const uint8_t keylen )
+{
+  if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+  if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+  memset( S->buf, 0, sizeof( S->buf ) );
+  S->buflen = 0;
+
+  if( blake2bp_init_root( S->R, outlen, keylen ) < 0 )
+    return -1;
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2bp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1;
+
+  S->R->last_node = 1;
+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+  {
+    uint8_t block[BLAKE2B_BLOCKBYTES];
+    memset( block, 0, BLAKE2B_BLOCKBYTES );
+    memcpy( block, key, keylen );
+
+    for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
+
+    secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+  }
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2bp_update( blake2bp_state *S, const uint8_t *in, uint64_t inlen )
+{
+  size_t left = S->buflen;
+  size_t fill = sizeof( S->buf ) - left;
+
+  if( left && inlen >= fill )
+  {
+    memcpy( S->buf + left, in, fill );
+
+    for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
+
+    in += fill;
+    inlen -= fill;
+    left = 0;
+  }
+
+#if defined(_OPENMP)
+  #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)
+#else
+
+  for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+  {
+#if defined(_OPENMP)
+    size_t      id__ = omp_get_thread_num();
+#endif
+    uint64_t inlen__ = inlen;
+    const uint8_t *in__ = ( const uint8_t * )in;
+    in__ += id__ * BLAKE2B_BLOCKBYTES;
+
+    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
+    {
+      blake2b_update( S->S[id__], in__, BLAKE2B_BLOCKBYTES );
+      in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+      inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+    }
+  }
+
+  in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES );
+  inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+
+  if( inlen > 0 )
+    memcpy( S->buf + left, in, inlen );
+
+  S->buflen = left + inlen;
+  return 0;
+}
+
+
+__attribute__((visibility ("hidden")))
+int blake2bp_final( blake2bp_state *S, uint8_t *out, const uint8_t outlen )
+{
+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+  {
+    if( S->buflen > i * BLAKE2B_BLOCKBYTES )
+    {
+      size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES;
+
+      if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES;
+
+      blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
+    }
+
+    blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
+  }
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
+
+  return blake2b_final( S->R, out, outlen );
+}
+
+__attribute__((visibility ("hidden")))
+int blake2bp( uint8_t *out, const void *in, const void *key, uint8_t outlen, uint64_t inlen, uint8_t keylen )
+{
+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
+  blake2b_state S[PARALLELISM_DEGREE][1];
+  blake2b_state FS[1];
+
+  /* Verify parameters */
+  if ( NULL == in && inlen > 0 ) return -1;
+
+  if ( NULL == out ) return -1;
+
+  if( NULL == key && keylen > 0 ) return -1;
+
+  if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+  if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2bp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1;
+
+  S[PARALLELISM_DEGREE - 1]->last_node = 1; // mark last node
+
+  if( keylen > 0 )
+  {
+    uint8_t block[BLAKE2B_BLOCKBYTES];
+    memset( block, 0, BLAKE2B_BLOCKBYTES );
+    memcpy( block, key, keylen );
+
+    for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
+
+    secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+  }
+
+#if defined(_OPENMP)
+  #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)
+#else
+
+  for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+  {
+#if defined(_OPENMP)
+    size_t      id__ = omp_get_thread_num();
+#endif
+    uint64_t inlen__ = inlen;
+    const uint8_t *in__ = ( const uint8_t * )in;
+    in__ += id__ * BLAKE2B_BLOCKBYTES;
+
+    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
+    {
+      blake2b_update( S[id__], in__, BLAKE2B_BLOCKBYTES );
+      in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+      inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+    }
+
+    if( inlen__ > id__ * BLAKE2B_BLOCKBYTES )
+    {
+      const size_t left = inlen__ - id__ * BLAKE2B_BLOCKBYTES;
+      const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES;
+      blake2b_update( S[id__], in__, len );
+    }
+
+    blake2b_final( S[id__], hash[id__], BLAKE2B_OUTBYTES );
+  }
+
+  if( blake2bp_init_root( FS, outlen, keylen ) < 0 )
+    return -1;
+
+  FS->last_node = 1; // Mark as last node
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
+
+  return blake2b_final( FS, out, outlen );
+}
diff --git a/cbits/blake2/blake2s-load-sse2.h b/cbits/blake2/blake2s-load-sse2.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2s-load-sse2.h
@@ -0,0 +1,59 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_SSE2_H__
+#define __BLAKE2S_LOAD_SSE2_H__
+
+#define LOAD_MSG_0_1(buf) buf = _mm_set_epi32(m6,m4,m2,m0)
+#define LOAD_MSG_0_2(buf) buf = _mm_set_epi32(m7,m5,m3,m1)
+#define LOAD_MSG_0_3(buf) buf = _mm_set_epi32(m14,m12,m10,m8)
+#define LOAD_MSG_0_4(buf) buf = _mm_set_epi32(m15,m13,m11,m9)
+#define LOAD_MSG_1_1(buf) buf = _mm_set_epi32(m13,m9,m4,m14)
+#define LOAD_MSG_1_2(buf) buf = _mm_set_epi32(m6,m15,m8,m10)
+#define LOAD_MSG_1_3(buf) buf = _mm_set_epi32(m5,m11,m0,m1)
+#define LOAD_MSG_1_4(buf) buf = _mm_set_epi32(m3,m7,m2,m12)
+#define LOAD_MSG_2_1(buf) buf = _mm_set_epi32(m15,m5,m12,m11)
+#define LOAD_MSG_2_2(buf) buf = _mm_set_epi32(m13,m2,m0,m8)
+#define LOAD_MSG_2_3(buf) buf = _mm_set_epi32(m9,m7,m3,m10)
+#define LOAD_MSG_2_4(buf) buf = _mm_set_epi32(m4,m1,m6,m14)
+#define LOAD_MSG_3_1(buf) buf = _mm_set_epi32(m11,m13,m3,m7)
+#define LOAD_MSG_3_2(buf) buf = _mm_set_epi32(m14,m12,m1,m9)
+#define LOAD_MSG_3_3(buf) buf = _mm_set_epi32(m15,m4,m5,m2)
+#define LOAD_MSG_3_4(buf) buf = _mm_set_epi32(m8,m0,m10,m6)
+#define LOAD_MSG_4_1(buf) buf = _mm_set_epi32(m10,m2,m5,m9)
+#define LOAD_MSG_4_2(buf) buf = _mm_set_epi32(m15,m4,m7,m0)
+#define LOAD_MSG_4_3(buf) buf = _mm_set_epi32(m3,m6,m11,m14)
+#define LOAD_MSG_4_4(buf) buf = _mm_set_epi32(m13,m8,m12,m1)
+#define LOAD_MSG_5_1(buf) buf = _mm_set_epi32(m8,m0,m6,m2)
+#define LOAD_MSG_5_2(buf) buf = _mm_set_epi32(m3,m11,m10,m12)
+#define LOAD_MSG_5_3(buf) buf = _mm_set_epi32(m1,m15,m7,m4)
+#define LOAD_MSG_5_4(buf) buf = _mm_set_epi32(m9,m14,m5,m13)
+#define LOAD_MSG_6_1(buf) buf = _mm_set_epi32(m4,m14,m1,m12)
+#define LOAD_MSG_6_2(buf) buf = _mm_set_epi32(m10,m13,m15,m5)
+#define LOAD_MSG_6_3(buf) buf = _mm_set_epi32(m8,m9,m6,m0)
+#define LOAD_MSG_6_4(buf) buf = _mm_set_epi32(m11,m2,m3,m7)
+#define LOAD_MSG_7_1(buf) buf = _mm_set_epi32(m3,m12,m7,m13)
+#define LOAD_MSG_7_2(buf) buf = _mm_set_epi32(m9,m1,m14,m11)
+#define LOAD_MSG_7_3(buf) buf = _mm_set_epi32(m2,m8,m15,m5)
+#define LOAD_MSG_7_4(buf) buf = _mm_set_epi32(m10,m6,m4,m0)
+#define LOAD_MSG_8_1(buf) buf = _mm_set_epi32(m0,m11,m14,m6)
+#define LOAD_MSG_8_2(buf) buf = _mm_set_epi32(m8,m3,m9,m15)
+#define LOAD_MSG_8_3(buf) buf = _mm_set_epi32(m10,m1,m13,m12)
+#define LOAD_MSG_8_4(buf) buf = _mm_set_epi32(m5,m4,m7,m2)
+#define LOAD_MSG_9_1(buf) buf = _mm_set_epi32(m1,m7,m8,m10)
+#define LOAD_MSG_9_2(buf) buf = _mm_set_epi32(m5,m6,m4,m2)
+#define LOAD_MSG_9_3(buf) buf = _mm_set_epi32(m13,m3,m9,m15)
+#define LOAD_MSG_9_4(buf) buf = _mm_set_epi32(m0,m12,m14,m11)
+
+
+#endif
diff --git a/cbits/blake2/blake2s-load-sse41.h b/cbits/blake2/blake2s-load-sse41.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2s-load-sse41.h
@@ -0,0 +1,229 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_SSE41_H__
+#define __BLAKE2S_LOAD_SSE41_H__
+
+#define LOAD_MSG_0_1(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(2,0,2,0)));
+
+#define LOAD_MSG_0_2(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(3,1,3,1)));
+
+#define LOAD_MSG_0_3(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m2), TOF(m3), _MM_SHUFFLE(2,0,2,0)));
+
+#define LOAD_MSG_0_4(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m2), TOF(m3), _MM_SHUFFLE(3,1,3,1)));
+
+#define LOAD_MSG_1_1(buf) \
+t0 = _mm_blend_epi16(m1, m2, 0x0C); \
+t1 = _mm_slli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3));
+
+#define LOAD_MSG_1_2(buf) \
+t0 = _mm_shuffle_epi32(m2,_MM_SHUFFLE(0,0,2,0)); \
+t1 = _mm_blend_epi16(m1,m3,0xC0); \
+t2 = _mm_blend_epi16(t0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_1_3(buf) \
+t0 = _mm_slli_si128(m1, 4); \
+t1 = _mm_blend_epi16(m2, t0, 0x30); \
+t2 = _mm_blend_epi16(m0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_1_4(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_slli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0, t1, 0x0C); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_2_1(buf) \
+t0 = _mm_unpackhi_epi32(m2,m3); \
+t1 = _mm_blend_epi16(m3,m1,0x0C); \
+t2 = _mm_blend_epi16(t0, t1, 0x0F); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2));
+
+#define LOAD_MSG_2_2(buf) \
+t0 = _mm_unpacklo_epi32(m2,m0); \
+t1 = _mm_blend_epi16(t0, m0, 0xF0); \
+t2 = _mm_slli_si128(m3, 8); \
+buf = _mm_blend_epi16(t1, t2, 0xC0);
+
+#define LOAD_MSG_2_3(buf) \
+t0 = _mm_blend_epi16(m0, m2, 0x3C); \
+t1 = _mm_srli_si128(m1, 12); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_2_4(buf) \
+t0 = _mm_slli_si128(m3, 4); \
+t1 = _mm_blend_epi16(m0, m1, 0x33); \
+t2 = _mm_blend_epi16(t1, t0, 0xC0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(0,1,2,3));
+
+#define LOAD_MSG_3_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_unpackhi_epi32(t0, m2); \
+t2 = _mm_blend_epi16(t1, m3, 0x0C); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2));
+
+#define LOAD_MSG_3_2(buf) \
+t0 = _mm_slli_si128(m2, 8); \
+t1 = _mm_blend_epi16(m3,m0,0x0C); \
+t2 = _mm_blend_epi16(t1, t0, 0xC0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3));
+
+#define LOAD_MSG_3_3(buf) \
+t0 = _mm_blend_epi16(m0,m1,0x0F); \
+t1 = _mm_blend_epi16(t0, m3, 0xC0); \
+buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3,0,1,2));
+
+#define LOAD_MSG_3_4(buf) \
+t0 = _mm_unpacklo_epi32(m0,m2); \
+t1 = _mm_unpackhi_epi32(m1,m2); \
+buf = _mm_unpacklo_epi64(t1,t0);
+
+#define LOAD_MSG_4_1(buf) \
+t0 = _mm_unpacklo_epi64(m1,m2); \
+t1 = _mm_unpackhi_epi64(m0,m2); \
+t2 = _mm_blend_epi16(t0,t1,0x33); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3));
+
+#define LOAD_MSG_4_2(buf) \
+t0 = _mm_unpackhi_epi64(m1,m3); \
+t1 = _mm_unpacklo_epi64(m0,m1); \
+buf = _mm_blend_epi16(t0,t1,0x33);
+
+#define LOAD_MSG_4_3(buf) \
+t0 = _mm_unpackhi_epi64(m3,m1); \
+t1 = _mm_unpackhi_epi64(m2,m0); \
+buf = _mm_blend_epi16(t1,t0,0x33);
+
+#define LOAD_MSG_4_4(buf) \
+t0 = _mm_blend_epi16(m0,m2,0x03); \
+t1 = _mm_slli_si128(t0, 8); \
+t2 = _mm_blend_epi16(t1,m3,0x0F); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,0,3));
+
+#define LOAD_MSG_5_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_unpacklo_epi32(m0,m2); \
+buf = _mm_unpacklo_epi64(t0,t1);
+
+#define LOAD_MSG_5_2(buf) \
+t0 = _mm_srli_si128(m2, 4); \
+t1 = _mm_blend_epi16(m0,m3,0x03); \
+buf = _mm_blend_epi16(t1,t0,0x3C);
+
+#define LOAD_MSG_5_3(buf) \
+t0 = _mm_blend_epi16(m1,m0,0x0C); \
+t1 = _mm_srli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0,t1,0x30); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,3,0));
+
+#define LOAD_MSG_5_4(buf) \
+t0 = _mm_unpacklo_epi64(m1,m2); \
+t1= _mm_shuffle_epi32(m3, _MM_SHUFFLE(0,2,0,1)); \
+buf = _mm_blend_epi16(t0,t1,0x33);
+
+#define LOAD_MSG_6_1(buf) \
+t0 = _mm_slli_si128(m1, 12); \
+t1 = _mm_blend_epi16(m0,m3,0x33); \
+buf = _mm_blend_epi16(t1,t0,0xC0);
+
+#define LOAD_MSG_6_2(buf) \
+t0 = _mm_blend_epi16(m3,m2,0x30); \
+t1 = _mm_srli_si128(m1, 4); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,3,0));
+
+#define LOAD_MSG_6_3(buf) \
+t0 = _mm_unpacklo_epi64(m0,m2); \
+t1 = _mm_srli_si128(m1, 4); \
+buf = _mm_shuffle_epi32(_mm_blend_epi16(t0,t1,0x0C), _MM_SHUFFLE(2,3,1,0));
+
+#define LOAD_MSG_6_4(buf) \
+t0 = _mm_unpackhi_epi32(m1,m2); \
+t1 = _mm_unpackhi_epi64(m0,t0); \
+buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3,0,1,2));
+
+#define LOAD_MSG_7_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_blend_epi16(t0,m3,0x0F); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(2,0,3,1));
+
+#define LOAD_MSG_7_2(buf) \
+t0 = _mm_blend_epi16(m2,m3,0x30); \
+t1 = _mm_srli_si128(m0,4); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,2,3));
+
+#define LOAD_MSG_7_3(buf) \
+t0 = _mm_unpackhi_epi64(m0,m3); \
+t1 = _mm_unpacklo_epi64(m1,m2); \
+t2 = _mm_blend_epi16(t0,t1,0x3C); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,2,3,1));
+
+#define LOAD_MSG_7_4(buf) \
+t0 = _mm_unpacklo_epi32(m0,m1); \
+t1 = _mm_unpackhi_epi32(m1,m2); \
+buf = _mm_unpacklo_epi64(t0,t1);
+
+#define LOAD_MSG_8_1(buf) \
+t0 = _mm_unpackhi_epi32(m1,m3); \
+t1 = _mm_unpacklo_epi64(t0,m0); \
+t2 = _mm_blend_epi16(t1,m2,0xC0); \
+buf = _mm_shufflehi_epi16(t2,_MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_8_2(buf) \
+t0 = _mm_unpackhi_epi32(m0,m3); \
+t1 = _mm_blend_epi16(m2,t0,0xF0); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(0,2,1,3));
+
+#define LOAD_MSG_8_3(buf) \
+t0 = _mm_blend_epi16(m2,m0,0x0C); \
+t1 = _mm_slli_si128(t0,4); \
+buf = _mm_blend_epi16(t1,m3,0x0F);
+
+#define LOAD_MSG_8_4(buf) \
+t0 = _mm_blend_epi16(m1,m0,0x30); \
+buf = _mm_shuffle_epi32(t0,_MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_9_1(buf) \
+t0 = _mm_blend_epi16(m0,m2,0x03); \
+t1 = _mm_blend_epi16(m1,m2,0x30); \
+t2 = _mm_blend_epi16(t1,t0,0x0F); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(1,3,0,2));
+
+#define LOAD_MSG_9_2(buf) \
+t0 = _mm_slli_si128(m0,4); \
+t1 = _mm_blend_epi16(m1,t0,0xC0); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(1,2,0,3));
+
+#define LOAD_MSG_9_3(buf) \
+t0 = _mm_unpackhi_epi32(m0,m3); \
+t1 = _mm_unpacklo_epi32(m2,m3); \
+t2 = _mm_unpackhi_epi64(t0,t1); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(3,0,2,1));
+
+#define LOAD_MSG_9_4(buf) \
+t0 = _mm_blend_epi16(m3,m2,0xC0); \
+t1 = _mm_unpacklo_epi32(m0,m3); \
+t2 = _mm_blend_epi16(t0,t1,0x0F); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,1,2,3));
+
+#endif
+
diff --git a/cbits/blake2/blake2s-load-xop.h b/cbits/blake2/blake2s-load-xop.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2s-load-xop.h
@@ -0,0 +1,189 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_XOP_H__
+#define __BLAKE2S_LOAD_XOP_H__
+
+#define TOB(x) ((x)*4*0x01010101 + 0x03020100) // ..or not TOB
+
+/* Basic VPPERM emulation, for testing purposes */
+/*static __m128i _mm_perm_epi8(const __m128i src1, const __m128i src2, const __m128i sel)
+{
+   const __m128i sixteen = _mm_set1_epi8(16);
+   const __m128i t0 = _mm_shuffle_epi8(src1, sel);
+   const __m128i s1 = _mm_shuffle_epi8(src2, _mm_sub_epi8(sel, sixteen));
+   const __m128i mask = _mm_or_si128(_mm_cmpeq_epi8(sel, sixteen),
+                                     _mm_cmpgt_epi8(sel, sixteen)); // (>=16) = 0xff : 00
+   return _mm_blendv_epi8(t0, s1, mask);
+}*/
+
+#define LOAD_MSG_0_1(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) );
+
+#define LOAD_MSG_0_2(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) );
+
+#define LOAD_MSG_0_3(buf) \
+buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) );
+
+#define LOAD_MSG_0_4(buf) \
+buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) );
+
+#define LOAD_MSG_1_1(buf) \
+t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(5),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_1_2(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(2),TOB(0),TOB(4),TOB(6)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_1_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(0),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_1_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(7),TOB(2),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_2_1(buf) \
+t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(1),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(4),TOB(0)) );
+
+#define LOAD_MSG_2_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(2),TOB(0),TOB(4)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_2_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(7),TOB(3),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_2_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(1),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_3_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(3),TOB(7)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(1),TOB(5)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(5),TOB(2)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_4_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_4_2(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_4_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(6),TOB(0),TOB(0)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_4_4(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(4),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(4),TOB(0)) );
+
+#define LOAD_MSG_5_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(2)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_5_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_5_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(0),TOB(7),TOB(4)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_5_4(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(5),TOB(0),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_6_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(0),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_6_2(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(6),TOB(0),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(7),TOB(0)) );
+
+#define LOAD_MSG_6_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(5),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_6_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(3),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_7_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(0),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_7_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(5),TOB(1),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_7_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(2),TOB(0),TOB(0),TOB(5)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) );
+
+#define LOAD_MSG_7_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(6),TOB(4),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_8_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_8_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(4),TOB(3),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(7)) );
+
+#define LOAD_MSG_8_3(buf) \
+t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(6),TOB(1),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(5),TOB(4)) ); \
+ 
+#define LOAD_MSG_8_4(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(4),TOB(7),TOB(2)) );
+
+#define LOAD_MSG_9_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(7),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(4),TOB(6)) );
+
+#define LOAD_MSG_9_2(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(6),TOB(4),TOB(2)) );
+
+#define LOAD_MSG_9_3(buf) \
+t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(3),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(7)) );
+
+#define LOAD_MSG_9_4(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(6),TOB(0)) );
+
+#endif
+
diff --git a/cbits/blake2/blake2s-round.h b/cbits/blake2/blake2s-round.h
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2s-round.h
@@ -0,0 +1,88 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_ROUND_H__
+#define __BLAKE2S_ROUND_H__
+
+#define LOADU(p)  _mm_loadu_si128( (const __m128i *)(p) )
+#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r)
+
+#define TOF(reg) _mm_castsi128_ps((reg))
+#define TOI(reg) _mm_castps_si128((reg))
+
+#define LIKELY(x) __builtin_expect((x),1)
+
+
+/* Microarchitecture-specific macros */
+#ifndef HAVE_XOP
+#ifdef HAVE_SSSE3
+#define _mm_roti_epi32(r, c) ( \
+                (8==-(c)) ? _mm_shuffle_epi8(r,r8) \
+              : (16==-(c)) ? _mm_shuffle_epi8(r,r16) \
+              : _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) )) )
+#else
+#define _mm_roti_epi32(r, c) _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) ))
+#endif
+#else
+/* ... */
+#endif
+
+
+#define G1(row1,row2,row3,row4,buf) \
+  row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+  row4 = _mm_xor_si128( row4, row1 ); \
+  row4 = _mm_roti_epi32(row4, -16); \
+  row3 = _mm_add_epi32( row3, row4 );   \
+  row2 = _mm_xor_si128( row2, row3 ); \
+  row2 = _mm_roti_epi32(row2, -12);
+
+#define G2(row1,row2,row3,row4,buf) \
+  row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+  row4 = _mm_xor_si128( row4, row1 ); \
+  row4 = _mm_roti_epi32(row4, -8); \
+  row3 = _mm_add_epi32( row3, row4 );   \
+  row2 = _mm_xor_si128( row2, row3 ); \
+  row2 = _mm_roti_epi32(row2, -7);
+
+#define DIAGONALIZE(row1,row2,row3,row4) \
+  row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
+  row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+  row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
+
+#define UNDIAGONALIZE(row1,row2,row3,row4) \
+  row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
+  row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+  row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
+
+#if defined(HAVE_XOP)
+#include "blake2s-load-xop.h"
+#elif defined(HAVE_SSE41)
+#include "blake2s-load-sse41.h"
+#else
+#include "blake2s-load-sse2.h"
+#endif
+
+#define ROUND(r)  \
+  LOAD_MSG_ ##r ##_1(buf1); \
+  G1(row1,row2,row3,row4,buf1); \
+  LOAD_MSG_ ##r ##_2(buf2); \
+  G2(row1,row2,row3,row4,buf2); \
+  DIAGONALIZE(row1,row2,row3,row4); \
+  LOAD_MSG_ ##r ##_3(buf3); \
+  G1(row1,row2,row3,row4,buf3); \
+  LOAD_MSG_ ##r ##_4(buf4); \
+  G2(row1,row2,row3,row4,buf4); \
+  UNDIAGONALIZE(row1,row2,row3,row4); \
+ 
+#endif
+
diff --git a/cbits/blake2/blake2s.c b/cbits/blake2/blake2s.c
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2s.c
@@ -0,0 +1,387 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#include "blake2-config.h"
+
+
+#include <emmintrin.h>
+#if defined(HAVE_SSSE3)
+#include <tmmintrin.h>
+#endif
+#if defined(HAVE_SSE41)
+#include <smmintrin.h>
+#endif
+#if defined(HAVE_AVX)
+#include <immintrin.h>
+#endif
+#if defined(HAVE_XOP)
+#include <x86intrin.h>
+#endif
+
+#include "blake2s-round.h"
+
+static const uint32_t blake2s_IV[8] =
+{
+  0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+  0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] =
+{
+  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,
+  { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,
+  {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,
+  {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,
+  {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,
+  { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,
+  { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,
+  {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,
+  { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,
+};
+
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2s_set_lastnode( blake2s_state *S )
+{
+  S->f[1] = -1;
+  return 0;
+}
+
+static inline int blake2s_clear_lastnode( blake2s_state *S )
+{
+  S->f[1] = 0;
+  return 0;
+}
+
+static inline int blake2s_set_lastblock( blake2s_state *S )
+{
+  if( S->last_node ) blake2s_set_lastnode( S );
+
+  S->f[0] = -1;
+  return 0;
+}
+
+static inline int blake2s_clear_lastblock( blake2s_state *S )
+{
+  if( S->last_node ) blake2s_clear_lastnode( S );
+
+  S->f[0] = 0;
+  return 0;
+}
+
+static inline int blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
+{
+  uint64_t t = ( ( uint64_t )S->t[1] << 32 ) | S->t[0];
+  t += inc;
+  S->t[0] = ( uint32_t )( t >>  0 );
+  S->t[1] = ( uint32_t )( t >> 32 );
+  return 0;
+}
+
+
+// Parameter-related functions
+static inline int blake2s_param_set_digest_length( blake2s_param *P, const uint8_t digest_length )
+{
+  P->digest_length = digest_length;
+  return 0;
+}
+
+static inline int blake2s_param_set_fanout( blake2s_param *P, const uint8_t fanout )
+{
+  P->fanout = fanout;
+  return 0;
+}
+
+static inline int blake2s_param_set_max_depth( blake2s_param *P, const uint8_t depth )
+{
+  P->depth = depth;
+  return 0;
+}
+
+static inline int blake2s_param_set_leaf_length( blake2s_param *P, const uint32_t leaf_length )
+{
+  P->leaf_length = leaf_length;
+  return 0;
+}
+
+static inline int blake2s_param_set_node_offset( blake2s_param *P, const uint64_t node_offset )
+{
+  store48( P->node_offset, node_offset );
+  return 0;
+}
+
+static inline int blake2s_param_set_node_depth( blake2s_param *P, const uint8_t node_depth )
+{
+  P->node_depth = node_depth;
+  return 0;
+}
+
+static inline int blake2s_param_set_inner_length( blake2s_param *P, const uint8_t inner_length )
+{
+  P->inner_length = inner_length;
+  return 0;
+}
+
+static inline int blake2s_param_set_salt( blake2s_param *P, const uint8_t salt[BLAKE2S_SALTBYTES] )
+{
+  memcpy( P->salt, salt, BLAKE2S_SALTBYTES );
+  return 0;
+}
+
+static inline int blake2s_param_set_personal( blake2s_param *P, const uint8_t personal[BLAKE2S_PERSONALBYTES] )
+{
+  memcpy( P->personal, personal, BLAKE2S_PERSONALBYTES );
+  return 0;
+}
+
+static inline int blake2s_init0( blake2s_state *S )
+{
+  memset( S, 0, sizeof( blake2s_state ) );
+
+  for( int i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
+
+  return 0;
+}
+
+/* init2 xors IV with input parameter block */
+__attribute__((visibility ("hidden")))
+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+  //blake2s_init0( S );
+  const uint8_t * v = ( const uint8_t * )( blake2s_IV );
+  const uint8_t * p = ( const uint8_t * )( P );
+  uint8_t * h = ( uint8_t * )( S->h );
+  /* IV XOR ParamBlock */
+  memset( S, 0, sizeof( blake2s_state ) );
+
+  for( int i = 0; i < BLAKE2S_OUTBYTES; ++i ) h[i] = v[i] ^ p[i];
+
+  return 0;
+}
+
+
+/* Some sort of default parameter block initialization, for sequential blake2s */
+__attribute__((visibility ("hidden")))
+int blake2s_init( blake2s_state *S, const uint8_t outlen )
+{
+  /* Move interval verification here? */
+  if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+  const blake2s_param P =
+  {
+    outlen,
+    0,
+    1,
+    1,
+    0,
+    {0},
+    0,
+    0,
+    {0},
+    {0}
+  };
+  return blake2s_init_param( S, &P );
+}
+
+__attribute__((visibility ("hidden")))
+int blake2s_init_key( blake2s_state *S, const uint8_t outlen, const void *key, const uint8_t keylen )
+{
+  /* Move interval verification here? */
+  if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+  if ( ( !key ) || ( !keylen ) || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  const blake2s_param P =
+  {
+    outlen,
+    keylen,
+    1,
+    1,
+    0,
+    {0},
+    0,
+    0,
+    {0},
+    {0}
+  };
+
+  if( blake2s_init_param( S, &P ) < 0 )
+    return -1;
+
+  {
+    uint8_t block[BLAKE2S_BLOCKBYTES];
+    memset( block, 0, BLAKE2S_BLOCKBYTES );
+    memcpy( block, key, keylen );
+    blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
+    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+  }
+  return 0;
+}
+
+
+static inline int blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] )
+{
+  __m128i row1, row2, row3, row4;
+  __m128i buf1, buf2, buf3, buf4;
+#if defined(HAVE_SSE41)
+  __m128i t0, t1;
+#if !defined(HAVE_XOP)
+  __m128i t2;
+#endif
+#endif
+  __m128i ff0, ff1;
+#if defined(HAVE_SSSE3) && !defined(HAVE_XOP)
+  const __m128i r8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
+  const __m128i r16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
+#endif
+#if defined(HAVE_SSE41)
+  const __m128i m0 = LOADU( block +  00 );
+  const __m128i m1 = LOADU( block +  16 );
+  const __m128i m2 = LOADU( block +  32 );
+  const __m128i m3 = LOADU( block +  48 );
+#else
+  const uint32_t  m0 = ( ( uint32_t * )block )[ 0];
+  const uint32_t  m1 = ( ( uint32_t * )block )[ 1];
+  const uint32_t  m2 = ( ( uint32_t * )block )[ 2];
+  const uint32_t  m3 = ( ( uint32_t * )block )[ 3];
+  const uint32_t  m4 = ( ( uint32_t * )block )[ 4];
+  const uint32_t  m5 = ( ( uint32_t * )block )[ 5];
+  const uint32_t  m6 = ( ( uint32_t * )block )[ 6];
+  const uint32_t  m7 = ( ( uint32_t * )block )[ 7];
+  const uint32_t  m8 = ( ( uint32_t * )block )[ 8];
+  const uint32_t  m9 = ( ( uint32_t * )block )[ 9];
+  const uint32_t m10 = ( ( uint32_t * )block )[10];
+  const uint32_t m11 = ( ( uint32_t * )block )[11];
+  const uint32_t m12 = ( ( uint32_t * )block )[12];
+  const uint32_t m13 = ( ( uint32_t * )block )[13];
+  const uint32_t m14 = ( ( uint32_t * )block )[14];
+  const uint32_t m15 = ( ( uint32_t * )block )[15];
+#endif
+  row1 = ff0 = LOADU( &S->h[0] );
+  row2 = ff1 = LOADU( &S->h[4] );
+  row3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
+  row4 = _mm_xor_si128( _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ), LOADU( &S->t[0] ) );
+  ROUND( 0 );
+  ROUND( 1 );
+  ROUND( 2 );
+  ROUND( 3 );
+  ROUND( 4 );
+  ROUND( 5 );
+  ROUND( 6 );
+  ROUND( 7 );
+  ROUND( 8 );
+  ROUND( 9 );
+  STOREU( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row1, row3 ) ) );
+  STOREU( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row2, row4 ) ) );
+  return 0;
+}
+
+/* inlen now in bytes */
+__attribute__((visibility ("hidden")))
+int blake2s_update( blake2s_state *S, const uint8_t *in, uint64_t inlen )
+{
+  while( inlen > 0 )
+  {
+    size_t left = S->buflen;
+    size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
+
+    if( inlen > fill )
+    {
+      memcpy( S->buf + left, in, fill ); // Fill buffer
+      S->buflen += fill;
+      blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+      blake2s_compress( S, S->buf ); // Compress
+      memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
+      S->buflen -= BLAKE2S_BLOCKBYTES;
+      in += fill;
+      inlen -= fill;
+    }
+    else // inlen <= fill
+    {
+      memcpy( S->buf + left, in, inlen );
+      S->buflen += inlen; // Be lazy, do not compress
+      in += inlen;
+      inlen -= inlen;
+    }
+  }
+
+  return 0;
+}
+
+/* Is this correct? */
+__attribute__((visibility ("hidden")))
+int blake2s_final( blake2s_state *S, uint8_t *out, uint8_t outlen )
+{
+  uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
+
+  if( outlen > BLAKE2S_OUTBYTES )
+    return -1;
+
+  if( S->buflen > BLAKE2S_BLOCKBYTES )
+  {
+    blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+    blake2s_compress( S, S->buf );
+    S->buflen -= BLAKE2S_BLOCKBYTES;
+    memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
+  }
+
+  blake2s_increment_counter( S, ( uint32_t )S->buflen );
+  blake2s_set_lastblock( S );
+  memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+  blake2s_compress( S, S->buf );
+
+  for( int i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+    store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+  memcpy( out, buffer, outlen );
+  return 0;
+}
+
+/* inlen, at least, should be uint64_t. Others can be size_t. */
+__attribute__((visibility ("hidden")))
+int blake2s( uint8_t *out, const void *in, const void *key, const uint8_t outlen, const uint64_t inlen, uint8_t keylen )
+{
+  blake2s_state S[1];
+
+  /* Verify parameters */
+  if ( NULL == in && inlen > 0 ) return -1;
+
+  if ( NULL == out ) return -1;
+
+  if ( NULL == key && keylen > 0) return -1;
+
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  if( keylen > 0 )
+  {
+    if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+  }
+  else
+  {
+    if( blake2s_init( S, outlen ) < 0 ) return -1;
+  }
+
+  blake2s_update( S, ( const uint8_t * )in, inlen );
+  blake2s_final( S, out, outlen );
+  return 0;
+}
diff --git a/cbits/blake2/blake2sp.c b/cbits/blake2/blake2sp.c
new file mode 100644
--- /dev/null
+++ b/cbits/blake2/blake2sp.c
@@ -0,0 +1,259 @@
+/*
+   BLAKE2 reference source code package - optimized C implementations
+
+   Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+   To the extent possible under law, the author(s) have dedicated all copyright
+   and related and neighboring rights to this software to the public domain
+   worldwide. This software is distributed without any warranty.
+
+   You should have received a copy of the CC0 Public Domain Dedication along with
+   this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#define PARALLELISM_DEGREE 8
+
+static inline int blake2sp_init_leaf( blake2s_state *S, uint8_t outlen, uint8_t keylen, uint64_t offset )
+{
+  blake2s_param P[1];
+  P->digest_length = outlen;
+  P->key_length = keylen;
+  P->fanout = PARALLELISM_DEGREE;
+  P->depth = 2;
+  P->leaf_length = 0;
+  store48( P->node_offset, offset );
+  P->node_depth = 0;
+  P->inner_length = BLAKE2S_OUTBYTES;
+  memset( P->salt, 0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+  return blake2s_init_param( S, P );
+}
+
+static inline int blake2sp_init_root( blake2s_state *S, uint8_t outlen, uint8_t keylen )
+{
+  blake2s_param P[1];
+  P->digest_length = outlen;
+  P->key_length = keylen;
+  P->fanout = PARALLELISM_DEGREE;
+  P->depth = 2;
+  P->leaf_length = 0;
+  store48( P->node_offset, 0ULL );
+  P->node_depth = 1;
+  P->inner_length = BLAKE2S_OUTBYTES;
+  memset( P->salt, 0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+  return blake2s_init_param( S, P );
+}
+
+__attribute__((visibility ("hidden")))
+int blake2sp_init( blake2sp_state *S, const uint8_t outlen )
+{
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  memset( S->buf, 0, sizeof( S->buf ) );
+  S->buflen = 0;
+
+  if( blake2sp_init_root( S->R, outlen, 0 ) < 0 )
+    return -1;
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2sp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1;
+
+  S->R->last_node = 1;
+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2sp_init_key( blake2sp_state *S, const uint8_t outlen, const void *key, const uint8_t keylen )
+{
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  memset( S->buf, 0, sizeof( S->buf ) );
+  S->buflen = 0;
+
+  if( blake2sp_init_root( S->R, outlen, keylen ) < 0 )
+    return -1;
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2sp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1;
+
+  S->R->last_node = 1;
+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+  {
+    uint8_t block[BLAKE2S_BLOCKBYTES];
+    memset( block, 0, BLAKE2S_BLOCKBYTES );
+    memcpy( block, key, keylen );
+
+    for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
+
+    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+  }
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2sp_update( blake2sp_state *S, const uint8_t *in, uint64_t inlen )
+{
+  size_t left = S->buflen;
+  size_t fill = sizeof( S->buf ) - left;
+
+  if( left && inlen >= fill )
+  {
+    memcpy( S->buf + left, in, fill );
+
+    for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+
+    in += fill;
+    inlen -= fill;
+    left = 0;
+  }
+
+#if defined(_OPENMP)
+  #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)
+#else
+
+  for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+  {
+#if defined(_OPENMP)
+    size_t      id__ = omp_get_thread_num();
+#endif
+    uint64_t inlen__ = inlen;
+    const uint8_t *in__ = ( const uint8_t * )in;
+    in__ += id__ * BLAKE2S_BLOCKBYTES;
+
+    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+    {
+      blake2s_update( S->S[id__], in__, BLAKE2S_BLOCKBYTES );
+      in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+      inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+    }
+  }
+
+  in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
+  inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+
+  if( inlen > 0 )
+    memcpy( S->buf + left, in, inlen );
+
+  S->buflen = left + inlen;
+  return 0;
+}
+
+__attribute__((visibility ("hidden")))
+int blake2sp_final( blake2sp_state *S, uint8_t *out, const uint8_t outlen )
+{
+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+  {
+    if( S->buflen > i * BLAKE2S_BLOCKBYTES )
+    {
+      size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
+
+      if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
+
+      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+    }
+
+    blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
+  }
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
+
+  return blake2s_final( S->R, out, outlen );
+}
+
+__attribute__((visibility ("hidden")))
+int blake2sp( uint8_t *out, const void *in, const void *key, uint8_t outlen, uint64_t inlen, uint8_t keylen )
+{
+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+  blake2s_state S[PARALLELISM_DEGREE][1];
+  blake2s_state FS[1];
+
+  /* Verify parameters */
+  if ( NULL == in && inlen > 0 ) return -1;
+
+  if ( NULL == out ) return -1;
+
+  if ( NULL == key && keylen > 0) return -1;
+
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2sp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1;
+
+  S[PARALLELISM_DEGREE - 1]->last_node = 1; // mark last node
+
+  if( keylen > 0 )
+  {
+    uint8_t block[BLAKE2S_BLOCKBYTES];
+    memset( block, 0, BLAKE2S_BLOCKBYTES );
+    memcpy( block, key, keylen );
+
+    for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
+
+    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+  }
+
+#if defined(_OPENMP)
+  #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)
+#else
+
+  for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+  {
+#if defined(_OPENMP)
+    size_t      id__ = omp_get_thread_num();
+#endif
+    uint64_t inlen__ = inlen;
+    const uint8_t *in__ = ( const uint8_t * )in;
+    in__ += id__ * BLAKE2S_BLOCKBYTES;
+
+    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+    {
+      blake2s_update( S[id__], in__, BLAKE2S_BLOCKBYTES );
+      in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+      inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+    }
+
+    if( inlen__ > id__ * BLAKE2S_BLOCKBYTES )
+    {
+      const size_t left = inlen__ - id__ * BLAKE2S_BLOCKBYTES;
+      const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
+      blake2s_update( S[id__], in__, len );
+    }
+
+    blake2s_final( S[id__], hash[id__], BLAKE2S_OUTBYTES );
+  }
+
+  if( blake2sp_init_root( FS, outlen, keylen ) < 0 )
+    return -1;
+
+  FS->last_node = 1;
+
+  for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+    blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
+
+  return blake2s_final( FS, out, outlen );
+}
diff --git a/cbits/cryptonite_blake2b.c b/cbits/cryptonite_blake2b.c
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2b.c
@@ -0,0 +1,16 @@
+#include "cryptonite_blake2b.h"
+
+void cryptonite_blake2b_init(blake2b_ctx *ctx, uint32_t hashlen)
+{
+	blake2b_init(ctx, hashlen / 8);
+}
+
+void cryptonite_blake2b_update(blake2b_ctx *ctx, const uint8_t *data, uint32_t len)
+{
+	blake2b_update(ctx, data, len);
+}
+
+void cryptonite_blake2b_finalize(blake2b_ctx *ctx, uint32_t hashlen, uint8_t *out)
+{
+	blake2b_final(ctx, out, hashlen / 8);
+}
diff --git a/cbits/cryptonite_blake2b.h b/cbits/cryptonite_blake2b.h
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2b.h
@@ -0,0 +1,12 @@
+#ifndef CRYPTOHASH_BLAKE2B_H
+#define CRYPTOHASH_BLAKE2B_H
+
+#include "blake2.h"
+
+typedef blake2b_state blake2b_ctx;
+
+void cryptonite_blake2b_init(blake2b_ctx *ctx, uint32_t hashlen);
+void cryptonite_blake2b_update(blake2b_ctx *ctx, const uint8_t *data, uint32_t len);
+void cryptonite_blake2b_finalize(blake2b_ctx *ctx, uint32_t hashlen, uint8_t *out);
+
+#endif
diff --git a/cbits/cryptonite_blake2bp.c b/cbits/cryptonite_blake2bp.c
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2bp.c
@@ -0,0 +1,16 @@
+#include "cryptonite_blake2bp.h"
+
+void cryptonite_blake2bp_init(blake2bp_ctx *ctx, uint32_t hashlen)
+{
+	blake2bp_init(ctx, hashlen / 8);
+}
+
+void cryptonite_blake2bp_update(blake2bp_ctx *ctx, const uint8_t *data, uint32_t len)
+{
+	blake2bp_update(ctx, data, len);
+}
+
+void cryptonite_blake2bp_finalize(blake2bp_ctx *ctx, uint32_t hashlen, uint8_t *out)
+{
+	blake2bp_final(ctx, out, hashlen / 8);
+}
diff --git a/cbits/cryptonite_blake2bp.h b/cbits/cryptonite_blake2bp.h
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2bp.h
@@ -0,0 +1,12 @@
+#ifndef CRYPTOHASH_BLAKE2BP_H
+#define CRYPTOHASH_BLAKE2BP_H
+
+#include "blake2.h"
+
+typedef blake2bp_state blake2bp_ctx;
+
+void cryptonite_blake2bp_init(blake2bp_ctx *ctx, uint32_t hashlen);
+void cryptonite_blake2bp_update(blake2bp_ctx *ctx, const uint8_t *data, uint32_t len);
+void cryptonite_blake2bp_finalize(blake2bp_ctx *ctx, uint32_t hashlen, uint8_t *out);
+
+#endif
diff --git a/cbits/cryptonite_blake2s.c b/cbits/cryptonite_blake2s.c
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2s.c
@@ -0,0 +1,16 @@
+#include "cryptonite_blake2s.h"
+
+void cryptonite_blake2s_init(blake2s_ctx *ctx, uint32_t hashlen)
+{
+	blake2s_init(ctx, hashlen / 8);
+}
+
+void cryptonite_blake2s_update(blake2s_ctx *ctx, const uint8_t *data, uint32_t len)
+{
+	blake2s_update(ctx, data, len);
+}
+
+void cryptonite_blake2s_finalize(blake2s_ctx *ctx, uint32_t hashlen, uint8_t *out)
+{
+	blake2s_final(ctx, out, hashlen / 8);
+}
diff --git a/cbits/cryptonite_blake2s.h b/cbits/cryptonite_blake2s.h
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2s.h
@@ -0,0 +1,12 @@
+#ifndef CRYPTOHASH_BLAKE2S_H
+#define CRYPTOHASH_BLAKE2S_H
+
+#include "blake2.h"
+
+typedef blake2s_state blake2s_ctx;
+
+void cryptonite_blake2s_init(blake2s_ctx *ctx, uint32_t hashlen);
+void cryptonite_blake2s_update(blake2s_ctx *ctx, const uint8_t *data, uint32_t len);
+void cryptonite_blake2s_finalize(blake2s_ctx *ctx, uint32_t hashlen, uint8_t *out);
+
+#endif
diff --git a/cbits/cryptonite_blake2sp.c b/cbits/cryptonite_blake2sp.c
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2sp.c
@@ -0,0 +1,16 @@
+#include "cryptonite_blake2sp.h"
+
+void cryptonite_blake2sp_init(blake2sp_ctx *ctx, uint32_t hashlen)
+{
+	blake2sp_init(ctx, hashlen / 8);
+}
+
+void cryptonite_blake2sp_update(blake2sp_ctx *ctx, const uint8_t *data, uint32_t len)
+{
+	blake2sp_update(ctx, data, len);
+}
+
+void cryptonite_blake2sp_finalize(blake2sp_ctx *ctx, uint32_t hashlen, uint8_t *out)
+{
+	blake2sp_final(ctx, out, hashlen / 8);
+}
diff --git a/cbits/cryptonite_blake2sp.h b/cbits/cryptonite_blake2sp.h
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_blake2sp.h
@@ -0,0 +1,12 @@
+#ifndef CRYPTOHASH_BLAKE2SP_H
+#define CRYPTOHASH_BLAKE2SP_H
+
+#include "blake2.h"
+
+typedef blake2sp_state blake2sp_ctx;
+
+void cryptonite_blake2sp_init(blake2sp_ctx *ctx, uint32_t hashlen);
+void cryptonite_blake2sp_update(blake2sp_ctx *ctx, const uint8_t *data, uint32_t len);
+void cryptonite_blake2sp_finalize(blake2sp_ctx *ctx, uint32_t hashlen, uint8_t *out);
+
+#endif
diff --git a/cbits/cryptonite_keccak.c b/cbits/cryptonite_keccak.c
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_keccak.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2012 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "cryptonite_bitfn.h"
+#include "cryptonite_keccak.h"
+
+#define KECCAK_NB_ROUNDS 24
+
+/* rounds constants */
+static const uint64_t keccak_rndc[24] =
+{
+	0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL,
+	0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL,
+	0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL,
+	0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
+	0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
+	0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
+	0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL,
+	0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL,
+};
+
+/* triangular numbers constants */
+static const int keccak_rotc[24] =
+	{ 1,3,6,10,15,21,28,36,45,55,2,14,27,41,56,8,25,43,62,18,39,61,20,44 };
+
+static const int keccak_piln[24] =
+	{ 10,7,11,17,18,3,5,16,8,21,24,4,15,23,19,13,12,2,20,14,22,9,6,1 };
+
+static inline void keccak_do_chunk(uint64_t state[25], uint64_t buf[], int bufsz)
+{
+	int i, j, r;
+	uint64_t tmp, bc[5];
+
+	/* merge buf with state */
+	for (i = 0; i < bufsz; i++)
+		state[i] ^= le64_to_cpu(buf[i]);
+
+	/* run keccak rounds */
+	for (r = 0; r < KECCAK_NB_ROUNDS; r++) {
+		/* compute the parity of each columns */
+		for (i = 0; i < 5; i++)
+			bc[i] = state[i] ^ state[i+5] ^ state[i+10] ^ state[i+15] ^ state[i+20];
+
+		for (i = 0; i < 5; i++) {
+			tmp = bc[(i + 4) % 5] ^ rol64(bc[(i + 1) % 5], 1);
+			for (j = 0; j < 25; j += 5)
+				state[j + i] ^= tmp;
+		}
+
+		/* rho pi */
+		tmp = state[1];
+		for (i = 0; i < 24; i++) {
+			j = keccak_piln[i];
+			bc[0] = state[j];
+			state[j] = rol64(tmp, keccak_rotc[i]);
+			tmp = bc[0];
+		}
+
+		/* bitwise combine along rows using a = a xor (not b and c) */
+		for (j = 0; j < 25; j += 5) {
+			for (i = 0; i < 5; i++)
+				bc[i] = state[j + i];
+			#define andn(b,c) (~(b) & (c))
+			state[j + 0] ^= andn(bc[1], bc[2]);
+			state[j + 1] ^= andn(bc[2], bc[3]);
+			state[j + 2] ^= andn(bc[3], bc[4]);
+			state[j + 3] ^= andn(bc[4], bc[0]);
+			state[j + 4] ^= andn(bc[0], bc[1]);
+			#undef andn
+		}
+
+		/* xor the round constant */
+		state[0] ^= keccak_rndc[r];
+	}
+}
+
+void cryptonite_keccak_init(struct keccak_ctx *ctx, uint32_t hashlen)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	ctx->bufsz = 200 - 2 * (hashlen / 8);
+}
+
+void cryptonite_keccak_update(struct keccak_ctx *ctx, uint8_t *data, uint32_t len)
+{
+	uint32_t to_fill;
+
+	to_fill = ctx->bufsz - ctx->bufindex;
+
+	if (ctx->bufindex == ctx->bufsz) {
+		keccak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
+		ctx->bufindex = 0;
+	}
+
+	/* process partial buffer if there's enough data to make a block */
+	if (ctx->bufindex && len >= to_fill) {
+		memcpy(ctx->buf + ctx->bufindex, data, to_fill);
+		keccak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
+		len -= to_fill;
+		data += to_fill;
+		ctx->bufindex = 0;
+	}
+
+	/* process as much ctx->bufsz-block */
+	for (; len >= ctx->bufsz; len -= ctx->bufsz, data += ctx->bufsz)
+		keccak_do_chunk(ctx->state, (uint64_t *) data, ctx->bufsz / 8);
+
+	/* append data into buf */
+	if (len) {
+		memcpy(ctx->buf + ctx->bufindex, data, len);
+		ctx->bufindex += len;
+	}
+}
+
+void cryptonite_keccak_finalize(struct keccak_ctx *ctx, uint32_t hashlen, uint8_t *out)
+{
+	uint64_t w[25];
+
+	/* process full buffer if needed */
+	if (ctx->bufindex == ctx->bufsz) {
+		keccak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
+		ctx->bufindex = 0;
+	}
+
+	/* add the 10*1 padding */
+	ctx->buf[ctx->bufindex++] = 1;
+	memset(ctx->buf + ctx->bufindex, 0, ctx->bufsz - ctx->bufindex);
+	ctx->buf[ctx->bufsz - 1] |= 0x80;
+
+	/* process */
+	keccak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
+
+	/* output */
+	cpu_to_le64_array(w, ctx->state, 25);
+	memcpy(out, w, hashlen / 8);
+}
diff --git a/cbits/cryptonite_keccak.h b/cbits/cryptonite_keccak.h
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_keccak.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+#ifndef CRYPTOHASH_SHA3_H
+#define CRYPTOHASH_SHA3_H
+
+#include <stdint.h>
+
+struct keccak_ctx
+{
+	uint32_t bufindex;
+	uint32_t bufsz;
+	uint64_t state[25];
+	uint8_t  buf[144]; /* minimum SHA3-224, otherwise buffer need increases */
+};
+
+#define SHA3_CTX_SIZE		sizeof(struct keccak_ctx)
+
+void cryptonite_keccak_init(struct keccak_ctx *ctx, uint32_t hashlen);
+void cryptonite_keccak_update(struct keccak_ctx *ctx, uint8_t *data, uint32_t len);
+void cryptonite_keccak_finalize(struct keccak_ctx *ctx, uint32_t hashlen, uint8_t *out);
+
+#endif
diff --git a/cbits/cryptonite_kekkak.c b/cbits/cryptonite_kekkak.c
deleted file mode 100644
--- a/cbits/cryptonite_kekkak.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2012 Vincent Hanquez <vincent@snarc.org>
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
- */
-
-#include <stdint.h>
-#include <string.h>
-#include "cryptonite_bitfn.h"
-#include "cryptonite_kekkak.h"
-
-#define KECCAK_NB_ROUNDS 24
-
-/* rounds constants */
-static const uint64_t keccak_rndc[24] =
-{
-	0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL,
-	0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL,
-	0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL,
-	0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
-	0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
-	0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
-	0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL,
-	0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL,
-};
-
-/* triangular numbers constants */
-static const int keccak_rotc[24] =
-	{ 1,3,6,10,15,21,28,36,45,55,2,14,27,41,56,8,25,43,62,18,39,61,20,44 };
-
-static const int keccak_piln[24] =
-	{ 10,7,11,17,18,3,5,16,8,21,24,4,15,23,19,13,12,2,20,14,22,9,6,1 };
-
-static inline void kekkak_do_chunk(uint64_t state[25], uint64_t buf[], int bufsz)
-{
-	int i, j, r;
-	uint64_t tmp, bc[5];
-
-	/* merge buf with state */
-	for (i = 0; i < bufsz; i++)
-		state[i] ^= le64_to_cpu(buf[i]);
-
-	/* run keccak rounds */
-	for (r = 0; r < KECCAK_NB_ROUNDS; r++) {
-		/* compute the parity of each columns */
-		for (i = 0; i < 5; i++)
-			bc[i] = state[i] ^ state[i+5] ^ state[i+10] ^ state[i+15] ^ state[i+20];
-
-		for (i = 0; i < 5; i++) {
-			tmp = bc[(i + 4) % 5] ^ rol64(bc[(i + 1) % 5], 1);
-			for (j = 0; j < 25; j += 5)
-				state[j + i] ^= tmp;
-		}
-
-		/* rho pi */
-		tmp = state[1];
-		for (i = 0; i < 24; i++) {
-			j = keccak_piln[i];
-			bc[0] = state[j];
-			state[j] = rol64(tmp, keccak_rotc[i]);
-			tmp = bc[0];
-		}
-
-		/* bitwise combine along rows using a = a xor (not b and c) */
-		for (j = 0; j < 25; j += 5) {
-			for (i = 0; i < 5; i++)
-				bc[i] = state[j + i];
-			#define andn(b,c) (~(b) & (c))
-			state[j + 0] ^= andn(bc[1], bc[2]);
-			state[j + 1] ^= andn(bc[2], bc[3]);
-			state[j + 2] ^= andn(bc[3], bc[4]);
-			state[j + 3] ^= andn(bc[4], bc[0]);
-			state[j + 4] ^= andn(bc[0], bc[1]);
-			#undef andn
-		}
-
-		/* xor the round constant */
-		state[0] ^= keccak_rndc[r];
-	}
-}
-
-void cryptonite_kekkak_init(struct kekkak_ctx *ctx, uint32_t hashlen)
-{
-	memset(ctx, 0, sizeof(*ctx));
-	ctx->hashlen = hashlen / 8;
-	ctx->bufsz = 200 - 2 * ctx->hashlen;
-}
-
-void cryptonite_kekkak_update(struct kekkak_ctx *ctx, uint8_t *data, uint32_t len)
-{
-	uint32_t to_fill;
-
-	to_fill = ctx->bufsz - ctx->bufindex;
-
-	if (ctx->bufindex == ctx->bufsz) {
-		kekkak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
-		ctx->bufindex = 0;
-	}
-
-	/* process partial buffer if there's enough data to make a block */
-	if (ctx->bufindex && len >= to_fill) {
-		memcpy(ctx->buf + ctx->bufindex, data, to_fill);
-		kekkak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
-		len -= to_fill;
-		data += to_fill;
-		ctx->bufindex = 0;
-	}
-
-	/* process as much ctx->bufsz-block */
-	for (; len >= ctx->bufsz; len -= ctx->bufsz, data += ctx->bufsz)
-		kekkak_do_chunk(ctx->state, (uint64_t *) data, ctx->bufsz / 8);
-
-	/* append data into buf */
-	if (len) {
-		memcpy(ctx->buf + ctx->bufindex, data, len);
-		ctx->bufindex += len;
-	}
-}
-
-void cryptonite_kekkak_finalize(struct kekkak_ctx *ctx, uint8_t *out)
-{
-	uint64_t w[25];
-
-	/* process full buffer if needed */
-	if (ctx->bufindex == ctx->bufsz) {
-		kekkak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
-		ctx->bufindex = 0;
-	}
-
-	/* add the 10*1 padding */
-	ctx->buf[ctx->bufindex++] = 1;
-	memset(ctx->buf + ctx->bufindex, 0, ctx->bufsz - ctx->bufindex);
-	ctx->buf[ctx->bufsz - 1] |= 0x80;
-
-	/* process */
-	kekkak_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
-
-	/* output */
-	cpu_to_le64_array(w, ctx->state, 25);
-	memcpy(out, w, ctx->hashlen);
-}
diff --git a/cbits/cryptonite_kekkak.h b/cbits/cryptonite_kekkak.h
deleted file mode 100644
--- a/cbits/cryptonite_kekkak.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2012 Vincent Hanquez <vincent@snarc.org>
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
- */
-#ifndef CRYPTOHASH_SHA3_H
-#define CRYPTOHASH_SHA3_H
-
-#include <stdint.h>
-
-struct kekkak_ctx
-{
-	uint32_t hashlen; /* in bytes */
-	uint32_t bufindex;
-	uint64_t state[25];
-	uint32_t bufsz;
-	uint32_t _padding;
-	uint8_t  buf[144]; /* minimum SHA3-224, otherwise buffer need increases */
-};
-
-#define SHA3_CTX_SIZE		sizeof(struct kekkak_ctx)
-
-void cryptonite_kekkak_init(struct kekkak_ctx *ctx, uint32_t hashlen);
-void cryptonite_kekkak_update(struct kekkak_ctx *ctx, uint8_t *data, uint32_t len);
-void cryptonite_kekkak_finalize(struct kekkak_ctx *ctx, uint8_t *out);
-
-#endif
diff --git a/cbits/cryptonite_sha3.c b/cbits/cryptonite_sha3.c
--- a/cbits/cryptonite_sha3.c
+++ b/cbits/cryptonite_sha3.c
@@ -100,8 +100,7 @@
 void cryptonite_sha3_init(struct sha3_ctx *ctx, uint32_t hashlen)
 {
 	memset(ctx, 0, sizeof(*ctx));
-	ctx->hashlen = hashlen / 8;
-	ctx->bufsz = 200 - 2 * ctx->hashlen;
+	ctx->bufsz = 200 - 2 * (hashlen / 8);
 }
 
 void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t len)
@@ -135,7 +134,7 @@
 	}
 }
 
-void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint8_t *out)
+void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
 	uint64_t w[25];
 
@@ -155,5 +154,5 @@
 
 	/* output */
 	cpu_to_le64_array(w, ctx->state, 25);
-	memcpy(out, w, ctx->hashlen);
+	memcpy(out, w, hashlen / 8);
 }
diff --git a/cbits/cryptonite_sha3.h b/cbits/cryptonite_sha3.h
--- a/cbits/cryptonite_sha3.h
+++ b/cbits/cryptonite_sha3.h
@@ -28,11 +28,9 @@
 
 struct sha3_ctx
 {
-	uint32_t hashlen; /* in bytes */
 	uint32_t bufindex;
-	uint64_t state[25];
 	uint32_t bufsz;
-	uint32_t _padding;
+	uint64_t state[25];
 	uint8_t  buf[144]; /* minimum SHA3-224, otherwise buffer need increases */
 };
 
@@ -40,6 +38,6 @@
 
 void cryptonite_sha3_init(struct sha3_ctx *ctx, uint32_t hashlen);
 void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t len);
-void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint8_t *out);
+void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint32_t hashlen, uint8_t *out);
 
 #endif
diff --git a/cbits/cryptonite_sha512.c b/cbits/cryptonite_sha512.c
--- a/cbits/cryptonite_sha512.c
+++ b/cbits/cryptonite_sha512.c
@@ -196,15 +196,12 @@
 
 #include <stdio.h>
 
-void cryptonite_sha512t_init(struct sha512t_ctx *tctx, int t)
+void cryptonite_sha512t_init(struct sha512_ctx *ctx, uint32_t hashlen)
 {
-	struct sha512_ctx *ctx = &tctx->ctx;
 	memset(ctx, 0, sizeof(*ctx));
-	if (t >= 512)
+	if (hashlen >= 512)
 		return;
-	tctx->t = t;
-
-	switch (t) {
+	switch (hashlen) {
 	case 224:
 		ctx->h[0] = 0x8c3d37c819544da2ULL;
 		ctx->h[1] = 0x73e1996689dcd4d6ULL;
@@ -234,7 +231,7 @@
 		for (i = 0; i < 8; i++)
 			ctx->h[i] ^= 0xa5a5a5a5a5a5a5a5ULL;
 
-		i = sprintf(buf, "SHA-512/%d", t);
+		i = sprintf(buf, "SHA-512/%d", hashlen);
 		cryptonite_sha512_update(ctx, (uint8_t *) buf, i);
 		cryptonite_sha512_finalize(ctx, out);
 
@@ -246,16 +243,16 @@
 	}
 }
 
-void cryptonite_sha512t_update(struct sha512t_ctx *ctx, const uint8_t *data, uint32_t len)
+void cryptonite_sha512t_update(struct sha512_ctx *ctx, const uint8_t *data, uint32_t len)
 {
-	return cryptonite_sha512_update(&ctx->ctx, data, len);
+	return cryptonite_sha512_update(ctx, data, len);
 }
 
-void cryptonite_sha512t_finalize(struct sha512t_ctx *ctx, uint8_t *out)
+void cryptonite_sha512t_finalize(struct sha512_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
 	uint8_t intermediate[SHA512_DIGEST_SIZE];
 
-	cryptonite_sha512_finalize(&ctx->ctx, intermediate);
-	memcpy(out, intermediate, ctx->t / 8);
+	cryptonite_sha512_finalize(ctx, intermediate);
+	memcpy(out, intermediate, hashlen / 8);
 }
 
diff --git a/cbits/cryptonite_sha512.h b/cbits/cryptonite_sha512.h
--- a/cbits/cryptonite_sha512.h
+++ b/cbits/cryptonite_sha512.h
@@ -33,12 +33,6 @@
 	uint64_t h[8];
 };
 
-struct sha512t_ctx
-{
-	struct sha512_ctx ctx;
-	uint64_t t; /* the custom t (e.g. 224 for SHA512/224) */
-};
-
 #define sha384_ctx sha512_ctx
 
 #define SHA384_DIGEST_SIZE	64
@@ -47,8 +41,6 @@
 #define SHA512_DIGEST_SIZE	64
 #define SHA512_CTX_SIZE		sizeof(struct sha512_ctx)
 
-#define SHA512t_CTX_SIZE	sizeof(struct sha512t_ctx)
-
 void cryptonite_sha384_init(struct sha384_ctx *ctx);
 void cryptonite_sha384_update(struct sha384_ctx *ctx, const uint8_t *data, uint32_t len);
 void cryptonite_sha384_finalize(struct sha384_ctx *ctx, uint8_t *out);
@@ -58,8 +50,8 @@
 void cryptonite_sha512_finalize(struct sha512_ctx *ctx, uint8_t *out);
 
 /* only multiples of 8 are supported as valid t values */
-void cryptonite_sha512t_init(struct sha512t_ctx *ctx, int t);
-void cryptonite_sha512t_update(struct sha512t_ctx *ctx, const uint8_t *data, uint32_t len);
-void cryptonite_sha512t_finalize(struct sha512t_ctx *ctx, uint8_t *out);
+void cryptonite_sha512t_init(struct sha512_ctx *ctx, uint32_t hashlen);
+void cryptonite_sha512t_update(struct sha512_ctx *ctx, const uint8_t *data, uint32_t len);
+void cryptonite_sha512t_finalize(struct sha512_ctx *ctx, uint32_t hashlen, uint8_t *out);
 
 #endif
diff --git a/cbits/cryptonite_skein256.c b/cbits/cryptonite_skein256.c
--- a/cbits/cryptonite_skein256.c
+++ b/cbits/cryptonite_skein256.c
@@ -108,7 +108,6 @@
 	uint64_t buf[4];
 	memset(ctx, 0, sizeof(*ctx));
 
-	ctx->hashlen = (hashlen + 7) >> 3;
 	SET_TYPE(ctx, FLAG_FIRST | FLAG_FINAL | FLAG_TYPE(TYPE_CFG));
 	
 	memset(buf, '\0', sizeof(buf));
@@ -155,7 +154,7 @@
 	}
 }
 
-void cryptonite_skein256_finalize(struct skein256_ctx *ctx, uint8_t *out)
+void cryptonite_skein256_finalize(struct skein256_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
 	uint32_t outsize;
 	uint64_t *p = (uint64_t *) out;
@@ -170,8 +169,8 @@
 
 	memset(ctx->buf, '\0', 32);
 
-	/* make sure we have a 8 bit rounded value */
-	outsize = ctx->hashlen;
+	/* make sure we have a 8 bit up rounded value */
+	outsize = (hashlen + 7) >> 3;
 
 	/* backup h[0--4] */
 	for (j = 0; j < 4; j++)
diff --git a/cbits/cryptonite_skein256.h b/cbits/cryptonite_skein256.h
--- a/cbits/cryptonite_skein256.h
+++ b/cbits/cryptonite_skein256.h
@@ -28,18 +28,17 @@
 
 struct skein256_ctx
 {
-	uint32_t hashlen;
-	uint32_t bufindex;
 	uint8_t  buf[32];
 	uint64_t h[4];
 	uint64_t t0;
 	uint64_t t1;
+	uint32_t bufindex;
 };
 
 #define SKEIN256_CTX_SIZE		sizeof(struct skein256_ctx)
 
 void cryponite_skein256_init(struct skein256_ctx *ctx, uint32_t hashlen);
 void cryponite_skein256_update(struct skein256_ctx *ctx, const uint8_t *data, uint32_t len);
-void cryponite_skein256_finalize(struct skein256_ctx *ctx, uint8_t *out);
+void cryponite_skein256_finalize(struct skein256_ctx *ctx, uint32_t hashlen, uint8_t *out);
 
 #endif
diff --git a/cbits/cryptonite_skein512.c b/cbits/cryptonite_skein512.c
--- a/cbits/cryptonite_skein512.c
+++ b/cbits/cryptonite_skein512.c
@@ -126,7 +126,6 @@
 	uint64_t buf[8];
 	memset(ctx, 0, sizeof(*ctx));
 
-	ctx->hashlen = (hashlen + 7) >> 3;
 	SET_TYPE(ctx, FLAG_FIRST | FLAG_FINAL | FLAG_TYPE(TYPE_CFG));
 	
 	memset(buf, '\0', sizeof(buf));
@@ -173,7 +172,7 @@
 	}
 }
 
-void cryptonite_skein512_finalize(struct skein512_ctx *ctx, uint8_t *out)
+void cryptonite_skein512_finalize(struct skein512_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
 	uint32_t outsize;
 	uint64_t *p = (uint64_t *) out;
@@ -189,7 +188,7 @@
 	memset(ctx->buf, '\0', 64);
 
 	/* make sure we have a 8 bit rounded value */
-	outsize = ctx->hashlen;
+	outsize = (hashlen + 7) >> 3;
 
 	/* backup h[0--7] */
 	for (j = 0; j < 8; j++)
diff --git a/cbits/cryptonite_skein512.h b/cbits/cryptonite_skein512.h
--- a/cbits/cryptonite_skein512.h
+++ b/cbits/cryptonite_skein512.h
@@ -28,18 +28,17 @@
 
 struct skein512_ctx
 {
-	uint32_t hashlen; /* in bytes, typically 384/8, 512/8 */
-	uint32_t bufindex;
 	uint8_t  buf[64];
 	uint64_t h[8];
 	uint64_t t0;
 	uint64_t t1;
+	uint32_t bufindex;
 };
 
 #define SKEIN512_CTX_SIZE		sizeof(struct skein512_ctx)
 
 void cryponite_skein512_init(struct skein512_ctx *ctx, uint32_t hashlen);
 void cryponite_skein512_update(struct skein512_ctx *ctx, const uint8_t *data, uint32_t len);
-void cryponite_skein512_finalize(struct skein512_ctx *ctx, uint8_t *out);
+void cryponite_skein512_finalize(struct skein512_ctx *ctx, uint32_t hashlen, uint8_t *out);
 
 #endif
diff --git a/cryptonite.cabal b/cryptonite.cabal
--- a/cryptonite.cabal
+++ b/cryptonite.cabal
@@ -1,12 +1,12 @@
 Name:                cryptonite
-Version:             0.8
+Version:             0.9
 Synopsis:            Cryptography Primitives sink
 Description:
     A repository of cryptographic primitives.
     .
     * Symmetric ciphers: AES, DES, 3DES, Blowfish, Camellia, RC4, Salsa, ChaCha.
     .
-    * Hash: SHA1, SHA2, SHA3, MD2, MD4, MD5, Kekkak, Skein, Ripemd, Tiger, Whirlpool
+    * Hash: SHA1, SHA2, SHA3, MD2, MD4, MD5, Keccak, Skein, Ripemd, Tiger, Whirlpool, Blake2
     .
     * MAC: HMAC, Poly1305
     .
@@ -39,6 +39,8 @@
                      cbits/aes/*.h
                      cbits/ed25519/*.h
                      cbits/p256/*.h
+                     cbits/blake2/*.h
+                     cbits/blake2/*.c
                      cbits/aes/x86ni_impl.c
                      tests/*.hs
 
@@ -143,10 +145,14 @@
                      Crypto.Hash.SHA512
                      Crypto.Hash.SHA512t
                      Crypto.Hash.SHA3
-                     Crypto.Hash.Kekkak
+                     Crypto.Hash.Keccak
                      Crypto.Hash.MD2
                      Crypto.Hash.MD4
                      Crypto.Hash.MD5
+                     Crypto.Hash.Blake2s
+                     Crypto.Hash.Blake2sp
+                     Crypto.Hash.Blake2b
+                     Crypto.Hash.Blake2bp
                      Crypto.Hash.RIPEMD160
                      Crypto.Hash.Skein256
                      Crypto.Hash.Skein512
@@ -170,8 +176,9 @@
                    , bytestring
                    , memory >= 0.8
                    , ghc-prim
-  ghc-options:       -Wall -fwarn-tabs -optc-O3
+  ghc-options:       -Wall -fwarn-tabs -optc-O3 -fno-warn-unused-imports
   default-language:  Haskell2010
+  cc-options:        -std=gnu99
   C-sources:         cbits/cryptonite_chacha.c
                    , cbits/cryptonite_salsa.c
                    , cbits/cryptonite_rc4.c
@@ -188,10 +195,14 @@
                    , cbits/cryptonite_sha256.c
                    , cbits/cryptonite_sha512.c
                    , cbits/cryptonite_sha3.c
-                   , cbits/cryptonite_kekkak.c
+                   , cbits/cryptonite_keccak.c
                    , cbits/cryptonite_md2.c
                    , cbits/cryptonite_md4.c
                    , cbits/cryptonite_md5.c
+                   , cbits/cryptonite_blake2s.c
+                   , cbits/cryptonite_blake2sp.c
+                   , cbits/cryptonite_blake2b.c
+                   , cbits/cryptonite_blake2bp.c
                    , cbits/cryptonite_ripemd.c
                    , cbits/cryptonite_skein256.c
                    , cbits/cryptonite_skein512.c
@@ -199,7 +210,11 @@
                    , cbits/cryptonite_whirlpool.c
                    , cbits/cryptonite_scrypt.c
                    , cbits/cryptonite_sysrand.c
-  include-dirs:  cbits cbits/ed25519
+                   , cbits/blake2/blake2s.c
+                   , cbits/blake2/blake2sp.c
+                   , cbits/blake2/blake2b.c
+                   , cbits/blake2/blake2bp.c
+  include-dirs:  cbits cbits/ed25519 cbits/blake2
 
   -- FIXME armel or mispel is also little endian.
   -- might be a good idea to also add a runtime autodetect mode.
@@ -282,5 +297,5 @@
                    , tasty-hunit
                    , tasty-kat
                    , cryptonite
-  ghc-options:       -Wall -fno-warn-orphans -fno-warn-missing-signatures -rtsopts
+  ghc-options:       -Wall -fno-warn-orphans -fno-warn-missing-signatures -fno-warn-unused-imports -rtsopts
   default-language:  Haskell2010
diff --git a/tests/BCrypt.hs b/tests/BCrypt.hs
--- a/tests/BCrypt.hs
+++ b/tests/BCrypt.hs
@@ -60,7 +60,7 @@
     , ("$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC", "abcdefghijklmnopqrstuvwxyz")
     ]
 
-makeKATs expected = concatMap maketest (zip3 is passwords hashes)
+makeKATs = concatMap maketest (zip3 is passwords hashes)
   where
     is :: [Int]
     is = [1..]
@@ -73,5 +73,5 @@
         ]
 
 tests = testGroup "bcrypt"
-    [ testGroup "KATs" (makeKATs expected)
+    [ testGroup "KATs" makeKATs
     ]
diff --git a/tests/ChaChaPoly1305.hs b/tests/ChaChaPoly1305.hs
--- a/tests/ChaChaPoly1305.hs
+++ b/tests/ChaChaPoly1305.hs
@@ -9,7 +9,7 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteArray as B (convert)
 
-plaintext, aad, key, iv, ciphertext, tag, nonce1, nonce2, nonce3 :: B.ByteString
+plaintext, aad, key, iv, ciphertext, tag, nonce1, nonce2, nonce3, nonce4, nonce5, nonce6, nonce7, nonce8, nonce9, nonce10 :: B.ByteString
 plaintext = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
 aad = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
 key = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
@@ -17,9 +17,16 @@
 constant = "\x07\x00\x00\x00"
 ciphertext = "\xd3\x1a\x8d\x34\x64\x8e\x60\xdb\x7b\x86\xaf\xbc\x53\xef\x7e\xc2\xa4\xad\xed\x51\x29\x6e\x08\xfe\xa9\xe2\xb5\xa7\x36\xee\x62\xd6\x3d\xbe\xa4\x5e\x8c\xa9\x67\x12\x82\xfa\xfb\x69\xda\x92\x72\x8b\x1a\x71\xde\x0a\x9e\x06\x0b\x29\x05\xd6\xa5\xb6\x7e\xcd\x3b\x36\x92\xdd\xbd\x7f\x2d\x77\x8b\x8c\x98\x03\xae\xe3\x28\x09\x1b\x58\xfa\xb3\x24\xe4\xfa\xd6\x75\x94\x55\x85\x80\x8b\x48\x31\xd7\xbc\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d\xe5\x76\xd2\x65\x86\xce\xc6\x4b\x61\x16"
 tag = "\x1a\xe1\x0b\x59\x4f\x09\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60\x06\x91"
-nonce1 = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-nonce2 = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
-nonce3 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+nonce1  = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+nonce2  = "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+nonce3  = "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+nonce4  = "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+nonce5  = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+nonce6  = "\x00\x00\x00\x00\x00\x00\x00\x00"
+nonce7  = "\x01\x00\x00\x00\x00\x00\x00\x00"
+nonce8  = "\xff\x00\x00\x00\x00\x00\x00\x00"
+nonce9  = "\x00\x01\x00\x00\x00\x00\x00\x00"
+nonce10 = "\xff\xff\xff\xff\xff\xff\xff\xff"
 
 tests = testGroup "ChaChaPoly1305"
     [ testCase "V1" runEncrypt
@@ -45,8 +52,16 @@
                                  ]
 
         runNonceInc =
-            let n1 = throwCryptoError $ AEAD.nonce12 nonce1
-                n3 = throwCryptoError $ AEAD.nonce12 nonce3
-            in propertyHoldCase [ eqTest "nonce" nonce2 $ B.convert . AEAD.incrementNonce $ n1
-                                , eqTest "nonce" nonce1 $ B.convert . AEAD.incrementNonce $ n3
+            let n1  = throwCryptoError . AEAD.nonce12 $ nonce1
+                n3  = throwCryptoError . AEAD.nonce12 $ nonce3
+                n5  = throwCryptoError . AEAD.nonce12 $ nonce5
+                n6  = throwCryptoError . AEAD.nonce8 constant $ nonce6
+                n8  = throwCryptoError . AEAD.nonce8 constant $ nonce8
+                n10 = throwCryptoError . AEAD.nonce8 constant $ nonce10
+            in propertyHoldCase [ eqTest "nonce12a" nonce2 $ B.convert . AEAD.incrementNonce $ n1
+                                , eqTest "nonce12b" nonce4 $ B.convert . AEAD.incrementNonce $ n3
+                                , eqTest "nonce12c" nonce1 $ B.convert . AEAD.incrementNonce $ n5
+                                , eqTest "nonce8a" (B.concat [constant, nonce7]) $ B.convert . AEAD.incrementNonce $ n6
+                                , eqTest "nonce8b" (B.concat [constant, nonce9]) $ B.convert . AEAD.incrementNonce $ n8
+                                , eqTest "nonce8c" (B.concat [constant, nonce6]) $ B.convert . AEAD.incrementNonce $ n10
                                 ]
diff --git a/tests/Hash.hs b/tests/Hash.hs
--- a/tests/Hash.hs
+++ b/tests/Hash.hs
@@ -106,19 +106,19 @@
         "19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3",
         "b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35",
         "dce81fc695cfea3d7e1446509238daf89f24cc61896f2d265927daa70f2108f8902f0dfd68be085d5abb9fcd2e482c1dc24f2fabf81f40b73495cad44d7360d3"])
-    , ("Kekkak-224", HashAlg Kekkak_224, [
+    , ("Keccak-224", HashAlg Keccak_224, [
         "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd",
         "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe",
         "0b27ff3b732133287f6831e2af47cf342b7ef1f3fcdee248811090cd" ])
-    , ("Kekkak-256", HashAlg Kekkak_256, [
+    , ("Keccak-256", HashAlg Keccak_256, [
         "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
         "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
         "ed6c07f044d7573cc53bf1276f8cba3dac497919597a45b4599c8f73e22aa334" ])
-    , ("Kekkak-384", HashAlg Kekkak_384, [
+    , ("Keccak-384", HashAlg Keccak_384, [
         "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff",
         "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3",
         "1cc515e1812491058d8b8b226fd85045e746b4937a58b0111b6b7a39dd431b6295bd6b6d05e01e225586b4dab3cbb87a" ])
-    , ("Kekkak-512", HashAlg Kekkak_512, [
+    , ("Keccak-512", HashAlg Keccak_512, [
         "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e",
         "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609",
         "10f8caabb5b179861da5e447d34b84d604e3eb81830880e1c2135ffc94580a47cb21f6243ec0053d58b1124d13af2090033659075ee718e0f111bb3f69fb24cf" ])
@@ -138,6 +138,10 @@
         "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26",
         "01dedd5de4ef14642445ba5f5b97c15e47b9ad931326e4b0727cd94cefc44fff23f07bf543139939b49128caf436dc1bdee54fcb24023a08d9403f9b4bf0d450",
         "28e361fe8c56e617caa56c28c7c36e5c13be552b77081be82b642f08bb7ef085b9a81910fe98269386b9aacfd2349076c9506126e198f6f6ad44c12017ca77b1" ])
+    , ("Blake2b-512", HashAlg Blake2b_512, [
+        "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
+        "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918",
+        "af438eea5d8cdb209336a7e85bf58090dc21b49d823f89a7d064c119f127bd361af9c7d109edda0f0e91bdce078d1d86b8e6f25727c98f6d3bb6f50acb2dd376" ])
     ]
 
 runhash :: HashAlg -> ByteString -> ByteString
diff --git a/tests/KAT_Curve25519.hs b/tests/KAT_Curve25519.hs
--- a/tests/KAT_Curve25519.hs
+++ b/tests/KAT_Curve25519.hs
@@ -1,14 +1,15 @@
 {-# LANGUAGE OverloadedStrings #-}
 module KAT_Curve25519 ( tests ) where
 
+import           Crypto.Error
 import qualified Crypto.PubKey.Curve25519 as Curve25519
 import           Data.ByteArray as B
 import           Imports
 
-alicePrivate = either error id $ Curve25519.secretKey ("\x77\x07\x6d\x0a\x73\x18\xa5\x7d\x3c\x16\xc1\x72\x51\xb2\x66\x45\xdf\x4c\x2f\x87\xeb\xc0\x99\x2a\xb1\x77\xfb\xa5\x1d\xb9\x2c\x2a" :: ByteString)
-alicePublic  = either error id $ Curve25519.publicKey ("\x85\x20\xf0\x09\x89\x30\xa7\x54\x74\x8b\x7d\xdc\xb4\x3e\xf7\x5a\x0d\xbf\x3a\x0d\x26\x38\x1a\xf4\xeb\xa4\xa9\x8e\xaa\x9b\x4e\x6a" :: ByteString)
-bobPrivate   = either error id $ Curve25519.secretKey ("\x5d\xab\x08\x7e\x62\x4a\x8a\x4b\x79\xe1\x7f\x8b\x83\x80\x0e\xe6\x6f\x3b\xb1\x29\x26\x18\xb6\xfd\x1c\x2f\x8b\x27\xff\x88\xe0\xeb" :: ByteString)
-bobPublic    = either error id $ Curve25519.publicKey ("\xde\x9e\xdb\x7d\x7b\x7d\xc1\xb4\xd3\x5b\x61\xc2\xec\xe4\x35\x37\x3f\x83\x43\xc8\x5b\x78\x67\x4d\xad\xfc\x7e\x14\x6f\x88\x2b\x4f" :: ByteString)
+alicePrivate = throwCryptoError $ Curve25519.secretKey ("\x77\x07\x6d\x0a\x73\x18\xa5\x7d\x3c\x16\xc1\x72\x51\xb2\x66\x45\xdf\x4c\x2f\x87\xeb\xc0\x99\x2a\xb1\x77\xfb\xa5\x1d\xb9\x2c\x2a" :: ByteString)
+alicePublic  = throwCryptoError $ Curve25519.publicKey ("\x85\x20\xf0\x09\x89\x30\xa7\x54\x74\x8b\x7d\xdc\xb4\x3e\xf7\x5a\x0d\xbf\x3a\x0d\x26\x38\x1a\xf4\xeb\xa4\xa9\x8e\xaa\x9b\x4e\x6a" :: ByteString)
+bobPrivate   = throwCryptoError $ Curve25519.secretKey ("\x5d\xab\x08\x7e\x62\x4a\x8a\x4b\x79\xe1\x7f\x8b\x83\x80\x0e\xe6\x6f\x3b\xb1\x29\x26\x18\xb6\xfd\x1c\x2f\x8b\x27\xff\x88\xe0\xeb" :: ByteString)
+bobPublic    = throwCryptoError $ Curve25519.publicKey ("\xde\x9e\xdb\x7d\x7b\x7d\xc1\xb4\xd3\x5b\x61\xc2\xec\xe4\x35\x37\x3f\x83\x43\xc8\x5b\x78\x67\x4d\xad\xfc\x7e\x14\x6f\x88\x2b\x4f" :: ByteString)
 aliceMultBob = "\x4a\x5d\x9d\x5b\xa4\xce\x2d\xe1\x72\x8e\x3b\xf4\x80\x35\x0f\x25\xe0\x7e\x21\xc9\x47\xd1\x9e\x33\x76\xf0\x9b\x3c\x1e\x16\x17\x42" :: ByteString
 
 katTests :: [TestTree]
diff --git a/tests/KAT_HMAC.hs b/tests/KAT_HMAC.hs
--- a/tests/KAT_HMAC.hs
+++ b/tests/KAT_HMAC.hs
@@ -3,7 +3,7 @@
 
 import qualified Crypto.MAC.HMAC as HMAC
 import Crypto.Hash (MD5(..), SHA1(..), SHA256(..)
-                   , Kekkak_224(..), Kekkak_256(..), Kekkak_384(..), Kekkak_512(..)
+                   , Keccak_224(..), Keccak_256(..), Keccak_384(..), Keccak_512(..)
                    , SHA3_224(..), SHA3_256(..), SHA3_384(..), SHA3_512(..)
                    , HashAlgorithm, digestFromByteString)
 import qualified Data.ByteString as B
@@ -43,27 +43,27 @@
     , MACVector "key"   v1      $ digest "\xf7\xbc\x83\xf4\x30\x53\x84\x24\xb1\x32\x98\xe6\xaa\x6f\xb1\x43\xef\x4d\x59\xa1\x49\x46\x17\x59\x97\x47\x9d\xbc\x2d\x1a\x3c\xd8"
     ]
 
-kekkak_key1 = "\x4a\x65\x66\x65"
-kekkak_data1 = "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f"
+keccak_key1 = "\x4a\x65\x66\x65"
+keccak_data1 = "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f"
 
-kekkak_224_MAC_Vectors :: [MACVector Kekkak_224]
-kekkak_224_MAC_Vectors =
-    [ MACVector kekkak_key1 kekkak_data1 $ digest "\xe8\x24\xfe\xc9\x6c\x07\x4f\x22\xf9\x92\x35\xbb\x94\x2d\xa1\x98\x26\x64\xab\x69\x2c\xa8\x50\x10\x53\xcb\xd4\x14"
+keccak_224_MAC_Vectors :: [MACVector Keccak_224]
+keccak_224_MAC_Vectors =
+    [ MACVector keccak_key1 keccak_data1 $ digest "\xe8\x24\xfe\xc9\x6c\x07\x4f\x22\xf9\x92\x35\xbb\x94\x2d\xa1\x98\x26\x64\xab\x69\x2c\xa8\x50\x10\x53\xcb\xd4\x14"
     ]
 
-kekkak_256_MAC_Vectors :: [MACVector Kekkak_256]
-kekkak_256_MAC_Vectors =
-    [  MACVector kekkak_key1 kekkak_data1 $ digest "\xaa\x9a\xed\x44\x8c\x7a\xbc\x8b\x5e\x32\x6f\xfa\x6a\x01\xcd\xed\xf7\xb4\xb8\x31\x88\x14\x68\xc0\x44\xba\x8d\xd4\x56\x63\x69\xa1"
+keccak_256_MAC_Vectors :: [MACVector Keccak_256]
+keccak_256_MAC_Vectors =
+    [  MACVector keccak_key1 keccak_data1 $ digest "\xaa\x9a\xed\x44\x8c\x7a\xbc\x8b\x5e\x32\x6f\xfa\x6a\x01\xcd\xed\xf7\xb4\xb8\x31\x88\x14\x68\xc0\x44\xba\x8d\xd4\x56\x63\x69\xa1"
     ]
 
-kekkak_384_MAC_Vectors :: [MACVector Kekkak_384]
-kekkak_384_MAC_Vectors =
-    [ MACVector kekkak_key1 kekkak_data1 $ digest "\x5a\xf5\xc9\xa7\x7a\x23\xa6\xa9\x3d\x80\x64\x9e\x56\x2a\xb7\x7f\x4f\x35\x52\xe3\xc5\xca\xff\xd9\x3b\xdf\x8b\x3c\xfc\x69\x20\xe3\x02\x3f\xc2\x67\x75\xd9\xdf\x1f\x3c\x94\x61\x31\x46\xad\x2c\x9d"
+keccak_384_MAC_Vectors :: [MACVector Keccak_384]
+keccak_384_MAC_Vectors =
+    [ MACVector keccak_key1 keccak_data1 $ digest "\x5a\xf5\xc9\xa7\x7a\x23\xa6\xa9\x3d\x80\x64\x9e\x56\x2a\xb7\x7f\x4f\x35\x52\xe3\xc5\xca\xff\xd9\x3b\xdf\x8b\x3c\xfc\x69\x20\xe3\x02\x3f\xc2\x67\x75\xd9\xdf\x1f\x3c\x94\x61\x31\x46\xad\x2c\x9d"
     ]
 
-kekkak_512_MAC_Vectors :: [MACVector Kekkak_512]
-kekkak_512_MAC_Vectors =
-    [ MACVector kekkak_key1 kekkak_data1 $ digest "\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"
+keccak_512_MAC_Vectors :: [MACVector Keccak_512]
+keccak_512_MAC_Vectors =
+    [ MACVector keccak_key1 keccak_data1 $ digest "\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"
     ]
 
 sha3_key1 = "\x4a\x65\x66\x65"
@@ -95,10 +95,10 @@
     [ testGroup "md5" $ concatMap toMACTest $ zip is md5MACVectors
     , testGroup "sha1" $ concatMap toMACTest $ zip is sha1MACVectors
     , testGroup "sha256" $ concatMap toMACTest $ zip is sha256MACVectors
-    , testGroup "kekkak-224" $ concatMap toMACTest $ zip is kekkak_224_MAC_Vectors
-    , testGroup "kekkak-256" $ concatMap toMACTest $ zip is kekkak_256_MAC_Vectors
-    , testGroup "kekkak-384" $ concatMap toMACTest $ zip is kekkak_384_MAC_Vectors
-    , testGroup "kekkak-512" $ concatMap toMACTest $ zip is kekkak_512_MAC_Vectors
+    , testGroup "keccak-224" $ concatMap toMACTest $ zip is keccak_224_MAC_Vectors
+    , testGroup "keccak-256" $ concatMap toMACTest $ zip is keccak_256_MAC_Vectors
+    , testGroup "keccak-384" $ concatMap toMACTest $ zip is keccak_384_MAC_Vectors
+    , testGroup "keccak-512" $ concatMap toMACTest $ zip is keccak_512_MAC_Vectors
     , testGroup "sha3-224" $ concatMap toMACTest $ zip is sha3_224_MAC_Vectors
     , testGroup "sha3-256" $ concatMap toMACTest $ zip is sha3_256_MAC_Vectors
     , testGroup "sha3-384" $ concatMap toMACTest $ zip is sha3_384_MAC_Vectors
