diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,20 @@
+## 0.30
+
+* Fix some C symbol blake2b prefix to be cryptonite_ prefix (fix mixing with other C library)
+* add hmac-lazy
+* Fix compilation with GHC 9.2
+* Drop support for GHC8.0, GHC8.2, GHC8.4, GHC8.6
+
+## 0.29
+
+* advance compilation with gmp breakage due to change upstream
+* Add native EdDSA support
+
+## 0.28
+
+* Add hash constant time capability
+* Prevent possible overflow during hashing by hashing in 4GB chunks
+
 ## 0.27
 
 * Optimise AES GCM and CCM
diff --git a/Crypto/Cipher/RC4.hs b/Crypto/Cipher/RC4.hs
--- a/Crypto/Cipher/RC4.hs
+++ b/Crypto/Cipher/RC4.hs
@@ -30,6 +30,11 @@
 import           Crypto.Internal.Imports
 
 -- | The encryption state for RC4
+--
+-- This type is an instance of 'ByteArrayAccess' for debugging purpose. Internal
+-- layout is architecture dependent, may contain uninitialized data fragments,
+-- and change in future versions.  The bytearray should not be used as input to
+-- cryptographic algorithms.
 newtype State = State ScrubbedBytes
     deriving (ByteArrayAccess,NFData)
 
diff --git a/Crypto/Cipher/Types/AEAD.hs b/Crypto/Cipher/Types/AEAD.hs
--- a/Crypto/Cipher/Types/AEAD.hs
+++ b/Crypto/Cipher/Types/AEAD.hs
@@ -27,24 +27,24 @@
 -- | Authenticated Encryption with Associated Data algorithms
 data AEAD cipher = forall st . AEAD
     { aeadModeImpl :: AEADModeImpl st
-    , aeadState    :: st
+    , aeadState    :: !st
     }
 
 -- | Append some header information to an AEAD context
 aeadAppendHeader :: ByteArrayAccess aad => AEAD cipher -> aad -> AEAD cipher
-aeadAppendHeader (AEAD impl st) aad = AEAD impl $ (aeadImplAppendHeader impl) st aad
+aeadAppendHeader (AEAD impl st) aad = AEAD impl $ aeadImplAppendHeader impl st aad
 
 -- | Encrypt some data and update the AEAD context
 aeadEncrypt :: ByteArray ba => AEAD cipher -> ba -> (ba, AEAD cipher)
-aeadEncrypt (AEAD impl st) ba = second (AEAD impl) $ (aeadImplEncrypt impl) st ba
+aeadEncrypt (AEAD impl st) ba = second (AEAD impl) $ aeadImplEncrypt impl st ba
 
 -- | Decrypt some data and update the AEAD context
 aeadDecrypt :: ByteArray ba => AEAD cipher -> ba -> (ba, AEAD cipher)
-aeadDecrypt (AEAD impl st) ba = second (AEAD impl) $ (aeadImplDecrypt impl) st ba
+aeadDecrypt (AEAD impl st) ba = second (AEAD impl) $ aeadImplDecrypt impl st ba
 
 -- | Finalize the AEAD context and return the authentication tag
 aeadFinalize :: AEAD cipher -> Int -> AuthTag
-aeadFinalize (AEAD impl st) n = (aeadImplFinalize impl) st n
+aeadFinalize (AEAD impl st) = aeadImplFinalize impl st
 
 -- | Simple AEAD encryption
 aeadSimpleEncrypt :: (ByteArrayAccess aad, ByteArray ba)
diff --git a/Crypto/ECC/Edwards25519.hs b/Crypto/ECC/Edwards25519.hs
--- a/Crypto/ECC/Edwards25519.hs
+++ b/Crypto/ECC/Edwards25519.hs
@@ -283,45 +283,45 @@
         withByteArray p  $ \pp  ->
              ed25519_base_double_scalarmul_vartime out ps1 pp ps2
 
-foreign import ccall "cryptonite_ed25519_scalar_eq"
+foreign import ccall unsafe "cryptonite_ed25519_scalar_eq"
     ed25519_scalar_eq :: Ptr Scalar
                       -> Ptr Scalar
                       -> IO CInt
 
-foreign import ccall "cryptonite_ed25519_scalar_encode"
+foreign import ccall unsafe "cryptonite_ed25519_scalar_encode"
     ed25519_scalar_encode :: Ptr Word8
                           -> Ptr Scalar
                           -> IO ()
 
-foreign import ccall "cryptonite_ed25519_scalar_decode_long"
+foreign import ccall unsafe "cryptonite_ed25519_scalar_decode_long"
     ed25519_scalar_decode_long :: Ptr Scalar
                                -> Ptr Word8
                                -> CSize
                                -> IO ()
 
-foreign import ccall "cryptonite_ed25519_scalar_add"
+foreign import ccall unsafe "cryptonite_ed25519_scalar_add"
     ed25519_scalar_add :: Ptr Scalar -- sum
                        -> Ptr Scalar -- a
                        -> Ptr Scalar -- b
                        -> IO ()
 
-foreign import ccall "cryptonite_ed25519_scalar_mul"
+foreign import ccall unsafe "cryptonite_ed25519_scalar_mul"
     ed25519_scalar_mul :: Ptr Scalar -- out
                        -> Ptr Scalar -- a
                        -> Ptr Scalar -- b
                        -> IO ()
 
-foreign import ccall "cryptonite_ed25519_point_encode"
+foreign import ccall unsafe "cryptonite_ed25519_point_encode"
     ed25519_point_encode :: Ptr Word8
                          -> Ptr Point
                          -> IO ()
 
-foreign import ccall "cryptonite_ed25519_point_decode_vartime"
+foreign import ccall unsafe "cryptonite_ed25519_point_decode_vartime"
     ed25519_point_decode_vartime :: Ptr Point
                                  -> Ptr Word8
                                  -> IO CInt
 
-foreign import ccall "cryptonite_ed25519_point_eq"
+foreign import ccall unsafe "cryptonite_ed25519_point_eq"
     ed25519_point_eq :: Ptr Point
                      -> Ptr Point
                      -> IO CInt
@@ -330,23 +330,23 @@
     ed25519_point_has_prime_order :: Ptr Point
                                   -> IO CInt
 
-foreign import ccall "cryptonite_ed25519_point_negate"
+foreign import ccall unsafe "cryptonite_ed25519_point_negate"
     ed25519_point_negate :: Ptr Point -- minus_a
                          -> Ptr Point -- a
                          -> IO ()
 
-foreign import ccall "cryptonite_ed25519_point_add"
+foreign import ccall unsafe "cryptonite_ed25519_point_add"
     ed25519_point_add :: Ptr Point -- sum
                       -> Ptr Point -- a
                       -> Ptr Point -- b
                       -> IO ()
 
-foreign import ccall "cryptonite_ed25519_point_double"
+foreign import ccall unsafe "cryptonite_ed25519_point_double"
     ed25519_point_double :: Ptr Point -- two_a
                          -> Ptr Point -- a
                          -> IO ()
 
-foreign import ccall "cryptonite_ed25519_point_mul_by_cofactor"
+foreign import ccall unsafe "cryptonite_ed25519_point_mul_by_cofactor"
     ed25519_point_mul_by_cofactor :: Ptr Point -- eight_a
                                   -> Ptr Point -- a
                                   -> IO ()
diff --git a/Crypto/Hash.hs b/Crypto/Hash.hs
--- a/Crypto/Hash.hs
+++ b/Crypto/Hash.hs
@@ -28,14 +28,17 @@
     -- * Hash methods parametrized by algorithm
     , hashInitWith
     , hashWith
+    , hashPrefixWith
     -- * Hash methods
     , hashInit
     , hashUpdates
     , hashUpdate
     , hashFinalize
+    , hashFinalizePrefix
     , hashBlockSize
     , hashDigestSize
     , hash
+    , hashPrefix
     , hashlazy
     -- * Hash algorithms
     , module Crypto.Hash.Algorithms
@@ -47,16 +50,21 @@
 import           Crypto.Internal.Compat (unsafeDoIO)
 import           Crypto.Hash.Types
 import           Crypto.Hash.Algorithms
-import           Foreign.Ptr (Ptr)
+import           Foreign.Ptr (Ptr, plusPtr)
 import           Crypto.Internal.ByteArray (ByteArrayAccess)
 import qualified Crypto.Internal.ByteArray as B
 import qualified Data.ByteString.Lazy as L
 import           Data.Word (Word8)
+import           Data.Int (Int32)
 
 -- | Hash a strict bytestring into a digest.
 hash :: (ByteArrayAccess ba, HashAlgorithm a) => ba -> Digest a
 hash bs = hashFinalize $ hashUpdate hashInit bs
 
+-- | Hash the first N bytes of a bytestring, with code path independent from N.
+hashPrefix :: (ByteArrayAccess ba, HashAlgorithmPrefix a) => ba -> Int -> Digest a
+hashPrefix = hashFinalizePrefix hashInit
+
 -- | Hash a lazy bytestring into a digest.
 hashlazy :: HashAlgorithm a => L.ByteString -> Digest a
 hashlazy lbs = hashFinalize $ hashUpdates hashInit (L.toChunks lbs)
@@ -81,9 +89,17 @@
 hashUpdates c l
     | null ls   = c
     | otherwise = Context $ B.copyAndFreeze c $ \(ctx :: Ptr (Context a)) ->
-        mapM_ (\b -> B.withByteArray b $ \d -> hashInternalUpdate ctx d (fromIntegral $ B.length b)) ls
+        mapM_ (\b -> B.withByteArray b (processBlocks ctx (B.length b))) ls
   where
     ls = filter (not . B.null) l
+    -- process the data in 2GB chunks to fit in uint32_t and Int on 32 bit systems
+    processBlocks ctx bytesLeft dataPtr
+        | bytesLeft == 0 = return ()
+        | otherwise = do
+            hashInternalUpdate ctx dataPtr (fromIntegral actuallyProcessed)
+            processBlocks ctx (bytesLeft - actuallyProcessed) (dataPtr `plusPtr` actuallyProcessed)
+        where
+            actuallyProcessed = min bytesLeft (fromIntegral (maxBound :: Int32))
 
 -- | Finalize a context and return a digest.
 hashFinalize :: forall a . HashAlgorithm a
@@ -94,6 +110,24 @@
         ((!_) :: B.Bytes) <- B.copy c $ \(ctx :: Ptr (Context a)) -> hashInternalFinalize ctx dig
         return ()
 
+-- | Update the context with the first N bytes of a bytestring and return the
+-- digest.  The code path is independent from N but much slower than a normal
+-- 'hashUpdate'.  The function can be called for the last bytes of a message, in
+-- order to exclude a variable padding, without leaking the padding length.  The
+-- begining of the message, never impacted by the padding, should preferably go
+-- through 'hashUpdate' for better performance.
+hashFinalizePrefix :: forall a ba . (HashAlgorithmPrefix a, ByteArrayAccess ba)
+                   => Context a
+                   -> ba
+                   -> Int
+                   -> Digest a
+hashFinalizePrefix !c b len =
+    Digest $ B.allocAndFreeze (hashDigestSize (undefined :: a)) $ \(dig :: Ptr (Digest a)) -> do
+        ((!_) :: B.Bytes) <- B.copy c $ \(ctx :: Ptr (Context a)) ->
+            B.withByteArray b $ \d ->
+                hashInternalFinalizePrefix ctx d (fromIntegral $ B.length b) (fromIntegral len) dig
+        return ()
+
 -- | Initialize a new context for a specified hash algorithm
 hashInitWith :: HashAlgorithm alg => alg -> Context alg
 hashInitWith _ = hashInit
@@ -101,6 +135,10 @@
 -- | Run the 'hash' function but takes an explicit hash algorithm parameter
 hashWith :: (ByteArrayAccess ba, HashAlgorithm alg) => alg -> ba -> Digest alg
 hashWith _ = hash
+
+-- | Run the 'hashPrefix' function but takes an explicit hash algorithm parameter
+hashPrefixWith :: (ByteArrayAccess ba, HashAlgorithmPrefix alg) => alg -> ba -> Int -> Digest alg
+hashPrefixWith _ = hashPrefix
 
 -- | Try to transform a bytearray into a Digest of specific algorithm.
 --
diff --git a/Crypto/Hash/Algorithms.hs b/Crypto/Hash/Algorithms.hs
--- a/Crypto/Hash/Algorithms.hs
+++ b/Crypto/Hash/Algorithms.hs
@@ -9,6 +9,7 @@
 --
 module Crypto.Hash.Algorithms
     ( HashAlgorithm
+    , HashAlgorithmPrefix
     -- * Hash algorithms
     , Blake2s_160(..)
     , Blake2s_224(..)
@@ -54,7 +55,7 @@
     , Whirlpool(..)
     ) where
 
-import           Crypto.Hash.Types (HashAlgorithm)
+import           Crypto.Hash.Types (HashAlgorithm, HashAlgorithmPrefix)
 import           Crypto.Hash.Blake2s
 import           Crypto.Hash.Blake2sp
 import           Crypto.Hash.Blake2b
diff --git a/Crypto/Hash/IO.hs b/Crypto/Hash/IO.hs
--- a/Crypto/Hash/IO.hs
+++ b/Crypto/Hash/IO.hs
@@ -24,6 +24,11 @@
 import           Foreign.Ptr
 
 -- | A Mutable hash context
+--
+-- This type is an instance of 'B.ByteArrayAccess' for debugging purpose.
+-- Internal layout is architecture dependent, may contain uninitialized data
+-- fragments, and change in future versions.  The bytearray should not be used
+-- as input to cryptographic algorithms.
 newtype MutableContext a = MutableContext B.Bytes
     deriving (B.ByteArrayAccess)
 
diff --git a/Crypto/Hash/MD5.hs b/Crypto/Hash/MD5.hs
--- a/Crypto/Hash/MD5.hs
+++ b/Crypto/Hash/MD5.hs
@@ -34,6 +34,9 @@
     hashInternalUpdate        = c_md5_update
     hashInternalFinalize      = c_md5_finalize
 
+instance HashAlgorithmPrefix MD5 where
+    hashInternalFinalizePrefix = c_md5_finalize_prefix
+
 foreign import ccall unsafe "cryptonite_md5_init"
     c_md5_init :: Ptr (Context a)-> IO ()
 
@@ -42,3 +45,6 @@
 
 foreign import ccall unsafe "cryptonite_md5_finalize"
     c_md5_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+
+foreign import ccall "cryptonite_md5_finalize_prefix"
+    c_md5_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/SHA1.hs b/Crypto/Hash/SHA1.hs
--- a/Crypto/Hash/SHA1.hs
+++ b/Crypto/Hash/SHA1.hs
@@ -34,6 +34,9 @@
     hashInternalUpdate        = c_sha1_update
     hashInternalFinalize      = c_sha1_finalize
 
+instance HashAlgorithmPrefix SHA1 where
+    hashInternalFinalizePrefix = c_sha1_finalize_prefix
+
 foreign import ccall unsafe "cryptonite_sha1_init"
     c_sha1_init :: Ptr (Context a)-> IO ()
 
@@ -42,3 +45,6 @@
 
 foreign import ccall unsafe "cryptonite_sha1_finalize"
     c_sha1_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+
+foreign import ccall "cryptonite_sha1_finalize_prefix"
+    c_sha1_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/SHA224.hs b/Crypto/Hash/SHA224.hs
--- a/Crypto/Hash/SHA224.hs
+++ b/Crypto/Hash/SHA224.hs
@@ -34,6 +34,9 @@
     hashInternalUpdate        = c_sha224_update
     hashInternalFinalize      = c_sha224_finalize
 
+instance HashAlgorithmPrefix SHA224 where
+    hashInternalFinalizePrefix = c_sha224_finalize_prefix
+
 foreign import ccall unsafe "cryptonite_sha224_init"
     c_sha224_init :: Ptr (Context a)-> IO ()
 
@@ -42,3 +45,6 @@
 
 foreign import ccall unsafe "cryptonite_sha224_finalize"
     c_sha224_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+
+foreign import ccall "cryptonite_sha224_finalize_prefix"
+    c_sha224_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/SHA256.hs b/Crypto/Hash/SHA256.hs
--- a/Crypto/Hash/SHA256.hs
+++ b/Crypto/Hash/SHA256.hs
@@ -34,6 +34,9 @@
     hashInternalUpdate        = c_sha256_update
     hashInternalFinalize      = c_sha256_finalize
 
+instance HashAlgorithmPrefix SHA256 where
+    hashInternalFinalizePrefix = c_sha256_finalize_prefix
+
 foreign import ccall unsafe "cryptonite_sha256_init"
     c_sha256_init :: Ptr (Context a)-> IO ()
 
@@ -42,3 +45,6 @@
 
 foreign import ccall unsafe "cryptonite_sha256_finalize"
     c_sha256_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+
+foreign import ccall "cryptonite_sha256_finalize_prefix"
+    c_sha256_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/SHA384.hs b/Crypto/Hash/SHA384.hs
--- a/Crypto/Hash/SHA384.hs
+++ b/Crypto/Hash/SHA384.hs
@@ -34,6 +34,9 @@
     hashInternalUpdate        = c_sha384_update
     hashInternalFinalize      = c_sha384_finalize
 
+instance HashAlgorithmPrefix SHA384 where
+    hashInternalFinalizePrefix = c_sha384_finalize_prefix
+
 foreign import ccall unsafe "cryptonite_sha384_init"
     c_sha384_init :: Ptr (Context a)-> IO ()
 
@@ -42,3 +45,6 @@
 
 foreign import ccall unsafe "cryptonite_sha384_finalize"
     c_sha384_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+
+foreign import ccall "cryptonite_sha384_finalize_prefix"
+    c_sha384_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/SHA512.hs b/Crypto/Hash/SHA512.hs
--- a/Crypto/Hash/SHA512.hs
+++ b/Crypto/Hash/SHA512.hs
@@ -34,6 +34,9 @@
     hashInternalUpdate        = c_sha512_update
     hashInternalFinalize      = c_sha512_finalize
 
+instance HashAlgorithmPrefix SHA512 where
+    hashInternalFinalizePrefix = c_sha512_finalize_prefix
+
 foreign import ccall unsafe "cryptonite_sha512_init"
     c_sha512_init :: Ptr (Context a)-> IO ()
 
@@ -42,3 +45,6 @@
 
 foreign import ccall unsafe "cryptonite_sha512_finalize"
     c_sha512_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
+
+foreign import ccall "cryptonite_sha512_finalize_prefix"
+    c_sha512_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
diff --git a/Crypto/Hash/Types.hs b/Crypto/Hash/Types.hs
--- a/Crypto/Hash/Types.hs
+++ b/Crypto/Hash/Types.hs
@@ -14,6 +14,7 @@
 {-# LANGUAGE TypeFamilies #-}
 module Crypto.Hash.Types
     ( HashAlgorithm(..)
+    , HashAlgorithmPrefix(..)
     , Context(..)
     , Digest(..)
     ) where
@@ -59,12 +60,28 @@
     -- | Finalize the context and set the digest raw memory to the right value
     hashInternalFinalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
 
+-- | Hashing algorithms with a constant-time implementation.
+class HashAlgorithm a => HashAlgorithmPrefix a where
+    -- | Update the context with the first N bytes of a buffer and finalize this
+    -- context.  The code path executed is independent from N and depends only
+    -- on the complete buffer length.
+    hashInternalFinalizePrefix :: Ptr (Context a)
+                               -> Ptr Word8 -> Word32
+                               -> Word32
+                               -> Ptr (Digest a)
+                               -> IO ()
+
 {-
 hashContextGetAlgorithm :: HashAlgorithm a => Context a -> a
 hashContextGetAlgorithm = undefined
 -}
 
 -- | Represent a context for a given hash algorithm.
+--
+-- This type is an instance of 'ByteArrayAccess' for debugging purpose. Internal
+-- layout is architecture dependent, may contain uninitialized data fragments,
+-- and change in future versions.  The bytearray should not be used as input to
+-- cryptographic algorithms.
 newtype Context a = Context Bytes
     deriving (ByteArrayAccess,NFData)
 
diff --git a/Crypto/Internal/Builder.hs b/Crypto/Internal/Builder.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Internal/Builder.hs
@@ -0,0 +1,53 @@
+-- |
+-- Module      : Crypto.Internal.Builder
+-- License     : BSD-style
+-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>
+-- Stability   : stable
+-- Portability : Good
+--
+-- Delaying and merging ByteArray allocations.  This is similar to module
+-- "Data.ByteArray.Pack" except the total length is computed automatically based
+-- on what is appended.
+--
+{-# LANGUAGE BangPatterns #-}
+module Crypto.Internal.Builder
+    ( Builder
+    , buildAndFreeze
+    , builderLength
+    , byte
+    , bytes
+    , zero
+    ) where
+
+import           Data.ByteArray (ByteArray, ByteArrayAccess)
+import qualified Data.ByteArray as B
+import           Data.Memory.PtrMethods (memSet)
+
+import           Foreign.Ptr (Ptr, plusPtr)
+import           Foreign.Storable (poke)
+
+import           Crypto.Internal.Imports hiding (empty)
+
+data Builder =  Builder !Int (Ptr Word8 -> IO ())  -- size and initializer
+
+instance Semigroup Builder where
+    (Builder s1 f1) <> (Builder s2 f2) = Builder (s1 + s2) f
+      where f p = f1 p >> f2 (p `plusPtr` s1)
+
+builderLength :: Builder -> Int
+builderLength (Builder s _) = s
+
+buildAndFreeze :: ByteArray ba => Builder -> ba
+buildAndFreeze (Builder s f) = B.allocAndFreeze s f
+
+byte :: Word8 -> Builder
+byte !b = Builder 1 (`poke` b)
+
+bytes :: ByteArrayAccess ba => ba -> Builder
+bytes bs = Builder (B.length bs) (B.copyByteArrayToPtr bs)
+
+zero :: Int -> Builder
+zero s = if s > 0 then Builder s (\p -> memSet p 0 s) else empty
+
+empty :: Builder
+empty = Builder 0 (const $ return ())
diff --git a/Crypto/Internal/CompatPrim.hs b/Crypto/Internal/CompatPrim.hs
--- a/Crypto/Internal/CompatPrim.hs
+++ b/Crypto/Internal/CompatPrim.hs
@@ -23,15 +23,21 @@
     , convert4To32
     ) where
 
-import GHC.Prim
 #if !defined(ARCH_IS_LITTLE_ENDIAN) && !defined(ARCH_IS_BIG_ENDIAN)
 import Data.Memory.Endian (getSystemEndianness, Endianness(..))
 #endif
 
+#if __GLASGOW_HASKELL__ >= 902
+import GHC.Prim
+#else
+import GHC.Prim hiding (Word32#)
+type Word32# = Word#
+#endif
+
 -- | Byteswap Word# to or from Big Endian
 --
 -- On a big endian machine, this function is a nop.
-be32Prim :: Word# -> Word#
+be32Prim :: Word32# -> Word32#
 #ifdef ARCH_IS_LITTLE_ENDIAN
 be32Prim = byteswap32Prim
 #elif defined(ARCH_IS_BIG_ENDIAN)
@@ -43,7 +49,7 @@
 -- | Byteswap Word# to or from Little Endian
 --
 -- On a little endian machine, this function is a nop.
-le32Prim :: Word# -> Word#
+le32Prim :: Word32# -> Word32#
 #ifdef ARCH_IS_LITTLE_ENDIAN
 le32Prim w = w
 #elif defined(ARCH_IS_BIG_ENDIAN)
@@ -54,16 +60,11 @@
 
 -- | Simple compatibility for byteswap the lower 32 bits of a Word#
 -- at the primitive level
-byteswap32Prim :: Word# -> Word#
-#if __GLASGOW_HASKELL__ >= 708
-byteswap32Prim w = byteSwap32# w
+byteswap32Prim :: Word32# -> Word32#
+#if __GLASGOW_HASKELL__ >= 902
+byteswap32Prim w = wordToWord32# (byteSwap32# (word32ToWord# w))
 #else
-byteswap32Prim w =
-    let !a =       uncheckedShiftL# w 24#
-        !b = and# (uncheckedShiftL# w 8#) 0x00ff0000##
-        !c = and# (uncheckedShiftRL# w 8#) 0x0000ff00##
-        !d = and# (uncheckedShiftRL# w 24#) 0x000000ff##
-     in or# a (or# b (or# c d))
+byteswap32Prim w = byteSwap32# w
 #endif
 
 -- | Combine 4 word8 [a,b,c,d] to a word32 representing [a,b,c,d]
diff --git a/Crypto/Internal/Imports.hs b/Crypto/Internal/Imports.hs
--- a/Crypto/Internal/Imports.hs
+++ b/Crypto/Internal/Imports.hs
@@ -5,11 +5,15 @@
 -- Stability   : experimental
 -- Portability : unknown
 --
+{-# LANGUAGE CPP #-}
 module Crypto.Internal.Imports
     ( module X
     ) where
 
 import Data.Word               as X
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Semigroup          as X (Semigroup(..))
+#endif
 import Control.Applicative     as X
 import Control.Monad           as X (forM, forM_, void)
 import Control.Arrow           as X (first, second)
diff --git a/Crypto/MAC/HMAC.hs b/Crypto/MAC/HMAC.hs
--- a/Crypto/MAC/HMAC.hs
+++ b/Crypto/MAC/HMAC.hs
@@ -12,6 +12,7 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 module Crypto.MAC.HMAC
     ( hmac
+    , hmacLazy
     , HMAC(..)
     -- * Incremental
     , Context(..)
@@ -28,6 +29,7 @@
 import qualified Crypto.Internal.ByteArray as B
 import           Data.Memory.PtrMethods
 import           Crypto.Internal.Compat
+import qualified Data.ByteString.Lazy as L
 
 -- | Represent an HMAC that is a phantom type with the hash used to produce the mac.
 --
@@ -39,12 +41,19 @@
 instance Eq (HMAC a) where
     (HMAC b1) == (HMAC b2) = B.constEq b1 b2
 
--- | compute a MAC using the supplied hashing function
+-- | Compute a MAC using the supplied hashing function
 hmac :: (ByteArrayAccess key, ByteArrayAccess message, HashAlgorithm a)
      => key     -- ^ Secret key
      -> message -- ^ Message to MAC
      -> HMAC a
 hmac secret msg = finalize $ updates (initialize secret) [msg]
+
+-- | Compute a MAC using the supplied hashing function, for a lazy input
+hmacLazy :: (ByteArrayAccess key, HashAlgorithm a)
+     => key     -- ^ Secret key
+     -> L.ByteString -- ^ Message to MAC
+     -> HMAC a
+hmacLazy secret msg = finalize $ updates (initialize secret) (L.toChunks msg)
 
 -- | Represent an ongoing HMAC state, that can be appended with 'update'
 -- and finalize to an HMAC with 'hmacFinalize'
diff --git a/Crypto/MAC/KMAC.hs b/Crypto/MAC/KMAC.hs
--- a/Crypto/MAC/KMAC.hs
+++ b/Crypto/MAC/KMAC.hs
@@ -27,13 +27,12 @@
 import           Crypto.Hash.SHAKE (HashSHAKE(..))
 import           Crypto.Hash.Types (HashAlgorithm(..), Digest(..))
 import qualified Crypto.Hash.Types as H
-import           Foreign.Ptr (Ptr, plusPtr)
-import           Foreign.Storable (poke)
+import           Crypto.Internal.Builder
+import           Crypto.Internal.Imports
+import           Foreign.Ptr (Ptr)
 import           Data.Bits (shiftR)
-import           Data.ByteArray (ByteArray, ByteArrayAccess)
+import           Data.ByteArray (ByteArrayAccess)
 import qualified Data.ByteArray as B
-import           Data.Word (Word8)
-import           Data.Memory.PtrMethods (memSet)
 
 
 -- cSHAKE
@@ -47,8 +46,8 @@
   where
     c = hashInternalContextSize (undefined :: a)
     w = hashBlockSize (undefined :: a)
-    x = encodeString n <+> encodeString s
-    b = builderAllocAndFreeze (bytepad x w) :: B.Bytes
+    x = encodeString n <> encodeString s
+    b = buildAndFreeze (bytepad x w) :: B.Bytes
 
 cshakeUpdate :: (HashSHAKE a, ByteArrayAccess ba)
              => H.Context a -> ba -> H.Context a
@@ -77,7 +76,7 @@
 -- The Eq instance is constant time.  No Show instance is provided, to avoid
 -- printing by mistake.
 newtype KMAC a = KMAC { kmacGetDigest :: Digest a }
-    deriving ByteArrayAccess
+    deriving (ByteArrayAccess,NFData)
 
 instance Eq (KMAC a) where
     (KMAC b1) == (KMAC b2) = B.constEq b1 b2
@@ -99,7 +98,7 @@
   where
     n = B.pack [75,77,65,67] :: B.Bytes  -- "KMAC"
     w = hashBlockSize (undefined :: a)
-    p = builderAllocAndFreeze (bytepad (encodeString key) w) :: B.ScrubbedBytes
+    p = buildAndFreeze (bytepad (encodeString key) w) :: B.ScrubbedBytes
 
 -- | Incrementally update a KMAC context.
 update :: (HashSHAKE a, ByteArrayAccess ba) => Context a -> ba -> Context a
@@ -114,56 +113,32 @@
 finalize (Context ctx) = KMAC $ cshakeFinalize ctx suffix
   where
     l = cshakeOutputLength (undefined :: a)
-    suffix = builderAllocAndFreeze (rightEncode l) :: B.Bytes
+    suffix = buildAndFreeze (rightEncode l) :: B.Bytes
 
 
 -- Utilities
 
 bytepad :: Builder -> Int -> Builder
-bytepad x w = prefix <+> x <+> zero padLen
+bytepad x w = prefix <> x <> zero padLen
   where
     prefix = leftEncode w
     padLen = (w - builderLength prefix - builderLength x) `mod` w
 
 encodeString :: ByteArrayAccess bin => bin -> Builder
-encodeString s = leftEncode (8 * B.length s) <+> bytes s
+encodeString s = leftEncode (8 * B.length s) <> bytes s
 
 leftEncode :: Int -> Builder
-leftEncode x = byte len <+> digits
+leftEncode x = byte len <> digits
   where
     digits = i2osp x
     len    = fromIntegral (builderLength digits)
 
 rightEncode :: Int -> Builder
-rightEncode x = digits <+> byte len
+rightEncode x = digits <> byte len
   where
     digits = i2osp x
     len    = fromIntegral (builderLength digits)
 
 i2osp :: Int -> Builder
-i2osp i | i >= 256  = i2osp (shiftR i 8) <+> byte (fromIntegral i)
+i2osp i | i >= 256  = i2osp (shiftR i 8) <> byte (fromIntegral i)
         | otherwise = byte (fromIntegral i)
-
-
--- Delaying and merging ByteArray allocations
-
-data Builder = Builder !Int (Ptr Word8 -> IO ())  -- size and initializer
-
-(<+>) :: Builder -> Builder -> Builder
-(Builder s1 f1) <+> (Builder s2 f2) = Builder (s1 + s2) f
-  where f p = f1 p >> f2 (p `plusPtr` s1)
-
-builderLength :: Builder -> Int
-builderLength (Builder s _) = s
-
-builderAllocAndFreeze :: ByteArray ba => Builder -> ba
-builderAllocAndFreeze (Builder s f) = B.allocAndFreeze s f
-
-byte :: Word8 -> Builder
-byte !b = Builder 1 (`poke` b)
-
-bytes :: ByteArrayAccess ba => ba -> Builder
-bytes bs = Builder (B.length bs) (B.copyByteArrayToPtr bs)
-
-zero :: Int -> Builder
-zero s = Builder s (\p -> memSet p 0 s)
diff --git a/Crypto/MAC/Poly1305.hs b/Crypto/MAC/Poly1305.hs
--- a/Crypto/MAC/Poly1305.hs
+++ b/Crypto/MAC/Poly1305.hs
@@ -33,6 +33,11 @@
 import           Crypto.Error
 
 -- | Poly1305 State
+--
+-- This type is an instance of 'ByteArrayAccess' for debugging purpose. Internal
+-- layout is architecture dependent, may contain uninitialized data fragments,
+-- and change in future versions.  The bytearray should not be used as input to
+-- cryptographic algorithms.
 newtype State = State ScrubbedBytes
     deriving (ByteArrayAccess)
 
diff --git a/Crypto/Number/Compat.hs b/Crypto/Number/Compat.hs
--- a/Crypto/Number/Compat.hs
+++ b/Crypto/Number/Compat.hs
@@ -72,7 +72,9 @@
 -- | Compute the power modulus using extra security to remain constant
 -- time wise through GMP
 gmpPowModSecInteger :: Integer -> Integer -> Integer -> GmpSupported Integer
-#if MIN_VERSION_integer_gmp(1,0,2)
+#if MIN_VERSION_integer_gmp(1,1,0)
+gmpPowModSecInteger _ _ _ = GmpUnsupported
+#elif MIN_VERSION_integer_gmp(1,0,2)
 gmpPowModSecInteger b e m = GmpSupported (powModSecInteger b e m)
 #elif MIN_VERSION_integer_gmp(1,0,0)
 gmpPowModSecInteger _ _ _ = GmpUnsupported
@@ -103,7 +105,9 @@
 
 -- | Get the next prime from a specific value through GMP
 gmpNextPrime :: Integer -> GmpSupported Integer
-#if MIN_VERSION_integer_gmp(0,5,1)
+#if MIN_VERSION_integer_gmp(1,1,0)
+gmpNextPrime _ = GmpUnsupported
+#elif MIN_VERSION_integer_gmp(0,5,1)
 gmpNextPrime n = GmpSupported (nextPrimeInteger n)
 #else
 gmpNextPrime _ = GmpUnsupported
@@ -111,7 +115,9 @@
 
 -- | Test if a number is prime using Miller Rabin
 gmpTestPrimeMillerRabin :: Int -> Integer -> GmpSupported Bool
-#if MIN_VERSION_integer_gmp(0,5,1)
+#if MIN_VERSION_integer_gmp(1,1,0)
+gmpTestPrimeMillerRabin _ _ = GmpUnsupported
+#elif MIN_VERSION_integer_gmp(0,5,1)
 gmpTestPrimeMillerRabin (I# tries) !n = GmpSupported $
     case testPrimeInteger n tries of
         0# -> False
diff --git a/Crypto/PubKey/EdDSA.hs b/Crypto/PubKey/EdDSA.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/EdDSA.hs
@@ -0,0 +1,390 @@
+-- |
+-- Module      : Crypto.PubKey.EdDSA
+-- License     : BSD-style
+-- Maintainer  : Olivier Chéron <olivier.cheron@gmail.com>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- EdDSA signature generation and verification, implemented in Haskell and
+-- parameterized with elliptic curve and hash algorithm.  Only edwards25519 is
+-- supported at the moment.
+--
+-- The module provides \"context\" and \"prehash\" variants defined in
+-- <https://tools.ietf.org/html/rfc8032 RFC 8032>.
+--
+-- This implementation is most useful when wanting to customize the hash
+-- algorithm.  See module "Crypto.PubKey.Ed25519" for faster Ed25519 with
+-- SHA-512.
+--
+{-# LANGUAGE DataKinds                  #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE TypeFamilies               #-}
+module Crypto.PubKey.EdDSA
+    ( SecretKey
+    , PublicKey
+    , Signature
+    -- * Curves with EdDSA implementation
+    , EllipticCurveEdDSA(CurveDigestSize)
+    , publicKeySize
+    , secretKeySize
+    , signatureSize
+    -- * Smart constructors
+    , signature
+    , publicKey
+    , secretKey
+    -- * Methods
+    , toPublic
+    , sign
+    , signCtx
+    , signPh
+    , verify
+    , verifyCtx
+    , verifyPh
+    , generateSecretKey
+    ) where
+
+import           Data.Bits
+import           Data.ByteArray (ByteArray, ByteArrayAccess, Bytes, ScrubbedBytes, View)
+import qualified Data.ByteArray as B
+import           Data.ByteString (ByteString)
+import           Data.Proxy
+
+import           Crypto.ECC
+import qualified Crypto.ECC.Edwards25519 as Edwards25519
+import           Crypto.Error
+import           Crypto.Hash (Digest)
+import           Crypto.Hash.IO
+import           Crypto.Random
+
+import           GHC.TypeLits (KnownNat, Nat)
+
+import           Crypto.Internal.Builder
+import           Crypto.Internal.Compat
+import           Crypto.Internal.Imports
+import           Crypto.Internal.Nat (integralNatVal)
+
+import           Foreign.Storable
+
+
+-- API
+
+-- | An EdDSA Secret key
+newtype SecretKey curve = SecretKey ScrubbedBytes
+    deriving (Show,Eq,ByteArrayAccess,NFData)
+
+-- | An EdDSA public key
+newtype PublicKey curve hash = PublicKey Bytes
+    deriving (Show,Eq,ByteArrayAccess,NFData)
+
+-- | An EdDSA signature
+newtype Signature curve hash = Signature Bytes
+    deriving (Show,Eq,ByteArrayAccess,NFData)
+
+-- | Elliptic curves with an implementation of EdDSA
+class ( EllipticCurveBasepointArith curve
+      , KnownNat (CurveDigestSize curve)
+      ) => EllipticCurveEdDSA curve where
+
+    -- | Size of the digest for this curve (in bytes)
+    type CurveDigestSize curve :: Nat
+
+    -- | Size of secret keys for this curve (in bytes)
+    secretKeySize :: proxy curve -> Int
+
+    -- hash with specified parameters
+    hashWithDom :: (HashAlgorithm hash, ByteArrayAccess ctx, ByteArrayAccess msg)
+                => proxy curve -> hash -> Bool -> ctx -> Builder -> msg -> Bytes
+
+    -- conversion between scalar, point and public key
+    pointPublic :: proxy curve -> Point curve -> PublicKey curve hash
+    publicPoint :: proxy curve -> PublicKey curve hash -> CryptoFailable (Point curve)
+    encodeScalarLE :: ByteArray bs => proxy curve -> Scalar curve -> bs
+    decodeScalarLE :: ByteArrayAccess bs => proxy curve -> bs -> CryptoFailable (Scalar curve)
+
+    -- how to use bits in a secret key
+    scheduleSecret :: ( HashAlgorithm hash
+                      , HashDigestSize hash ~ CurveDigestSize curve
+                      )
+                   => proxy curve
+                   -> hash
+                   -> SecretKey curve
+                   -> (Scalar curve, View Bytes)
+
+-- | Size of public keys for this curve (in bytes)
+publicKeySize :: EllipticCurveEdDSA curve => proxy curve -> Int
+publicKeySize prx = signatureSize prx `div` 2
+
+-- | Size of signatures for this curve (in bytes)
+signatureSize :: forall proxy curve . EllipticCurveEdDSA curve
+              => proxy curve -> Int
+signatureSize _ = integralNatVal (Proxy :: Proxy (CurveDigestSize curve))
+
+
+-- Constructors
+
+-- | Try to build a public key from a bytearray
+publicKey :: ( EllipticCurveEdDSA curve
+             , HashAlgorithm hash
+             , HashDigestSize hash ~ CurveDigestSize curve
+             , ByteArrayAccess ba
+             )
+          => proxy curve -> hash -> ba -> CryptoFailable (PublicKey curve hash)
+publicKey prx _ bs
+    | B.length bs == publicKeySize prx =
+        CryptoPassed (PublicKey $ B.convert bs)
+    | otherwise =
+        CryptoFailed CryptoError_PublicKeySizeInvalid
+
+-- | Try to build a secret key from a bytearray
+secretKey :: (EllipticCurveEdDSA curve, ByteArrayAccess ba)
+          => proxy curve -> ba -> CryptoFailable (SecretKey curve)
+secretKey prx bs
+    | B.length bs == secretKeySize prx =
+        CryptoPassed (SecretKey $ B.convert bs)
+    | otherwise                        =
+        CryptoFailed CryptoError_SecretKeyStructureInvalid
+
+-- | Try to build a signature from a bytearray
+signature :: ( EllipticCurveEdDSA curve
+             , HashAlgorithm hash
+             , HashDigestSize hash ~ CurveDigestSize curve
+             , ByteArrayAccess ba
+             )
+          => proxy curve -> hash -> ba -> CryptoFailable (Signature curve hash)
+signature prx _ bs
+    | B.length bs == signatureSize prx =
+        CryptoPassed (Signature $ B.convert bs)
+    | otherwise =
+        CryptoFailed CryptoError_SecretKeyStructureInvalid
+
+
+-- Conversions
+
+-- | Generate a secret key
+generateSecretKey :: (EllipticCurveEdDSA curve, MonadRandom m)
+                  => proxy curve -> m (SecretKey curve)
+generateSecretKey prx = SecretKey <$> getRandomBytes (secretKeySize prx)
+
+-- | Create a public key from a secret key
+toPublic :: ( EllipticCurveEdDSA curve
+            , HashAlgorithm hash
+            , HashDigestSize hash ~ CurveDigestSize curve
+            )
+         => proxy curve -> hash -> SecretKey curve -> PublicKey curve hash
+toPublic prx alg priv =
+    let p = pointBaseSmul prx (secretScalar prx alg priv)
+     in pointPublic prx p
+
+secretScalar :: ( EllipticCurveEdDSA curve
+                , HashAlgorithm hash
+                , HashDigestSize hash ~ CurveDigestSize curve
+                )
+             => proxy curve -> hash -> SecretKey curve -> Scalar curve
+secretScalar prx alg priv = fst (scheduleSecret prx alg priv)
+
+
+-- EdDSA signature generation & verification
+
+-- | Sign a message using the key pair
+sign :: ( EllipticCurveEdDSA curve
+        , HashAlgorithm hash
+        , HashDigestSize hash ~ CurveDigestSize curve
+        , ByteArrayAccess msg
+        )
+     => proxy curve -> SecretKey curve -> PublicKey curve hash -> msg -> Signature curve hash
+sign prx = signCtx prx emptyCtx
+
+-- | Verify a message
+verify :: ( EllipticCurveEdDSA curve
+          , HashAlgorithm hash
+          , HashDigestSize hash ~ CurveDigestSize curve
+          , ByteArrayAccess msg
+          )
+       => proxy curve -> PublicKey curve hash -> msg -> Signature curve hash -> Bool
+verify prx = verifyCtx prx emptyCtx
+
+-- | Sign a message using the key pair under context @ctx@
+signCtx :: ( EllipticCurveEdDSA curve
+           , HashAlgorithm hash
+           , HashDigestSize hash ~ CurveDigestSize curve
+           , ByteArrayAccess ctx
+           , ByteArrayAccess msg
+           )
+        => proxy curve -> ctx -> SecretKey curve -> PublicKey curve hash -> msg -> Signature curve hash
+signCtx prx = signPhCtx prx False
+
+-- | Verify a message under context @ctx@
+verifyCtx :: ( EllipticCurveEdDSA curve
+             , HashAlgorithm hash
+             , HashDigestSize hash ~ CurveDigestSize curve
+             , ByteArrayAccess ctx
+             , ByteArrayAccess msg
+             )
+          => proxy curve -> ctx -> PublicKey curve hash -> msg -> Signature curve hash -> Bool
+verifyCtx prx = verifyPhCtx prx False
+
+-- | Sign a prehashed message using the key pair under context @ctx@
+signPh :: ( EllipticCurveEdDSA curve
+          , HashAlgorithm hash
+          , HashDigestSize hash ~ CurveDigestSize curve
+          , ByteArrayAccess ctx
+          )
+       => proxy curve -> ctx -> SecretKey curve -> PublicKey curve hash -> Digest prehash -> Signature curve hash
+signPh prx = signPhCtx prx True
+
+-- | Verify a prehashed message under context @ctx@
+verifyPh :: ( EllipticCurveEdDSA curve
+            , HashAlgorithm hash
+            , HashDigestSize hash ~ CurveDigestSize curve
+            , ByteArrayAccess ctx
+            )
+         => proxy curve -> ctx -> PublicKey curve hash -> Digest prehash -> Signature curve hash -> Bool
+verifyPh prx = verifyPhCtx prx True
+
+signPhCtx :: forall proxy curve hash ctx msg .
+             ( EllipticCurveEdDSA curve
+             , HashAlgorithm hash
+             , HashDigestSize hash ~ CurveDigestSize curve
+             , ByteArrayAccess ctx
+             , ByteArrayAccess msg
+             )
+          => proxy curve -> Bool -> ctx -> SecretKey curve -> PublicKey curve hash -> msg -> Signature curve hash
+signPhCtx prx ph ctx priv pub msg =
+    let alg  = undefined :: hash
+        (s, prefix) = scheduleSecret prx alg priv
+        digR = hashWithDom prx alg ph ctx (bytes prefix) msg
+        r    = decodeScalarNoErr prx digR
+        pR   = pointBaseSmul prx r
+        bsR  = encodePoint prx pR
+        sK   = getK prx ph ctx pub bsR msg
+        sS   = scalarAdd prx r (scalarMul prx sK s)
+     in encodeSignature prx (bsR, pR, sS)
+
+verifyPhCtx :: ( EllipticCurveEdDSA curve
+               , HashAlgorithm hash
+               , HashDigestSize hash ~ CurveDigestSize curve
+               , ByteArrayAccess ctx
+               , ByteArrayAccess msg
+               )
+            => proxy curve -> Bool -> ctx -> PublicKey curve hash -> msg -> Signature curve hash -> Bool
+verifyPhCtx prx ph ctx pub msg sig =
+    case doVerify of
+        CryptoPassed verified -> verified
+        CryptoFailed _        -> False
+  where
+    doVerify = do
+        (bsR, pR, sS) <- decodeSignature prx sig
+        nPub <- pointNegate prx `fmap` publicPoint prx pub
+        let sK  = getK prx ph ctx pub bsR msg
+            pR' = pointsSmulVarTime prx sS sK nPub
+        return (pR == pR')
+
+emptyCtx :: Bytes
+emptyCtx = B.empty
+
+getK :: forall proxy curve hash ctx msg .
+        ( EllipticCurveEdDSA curve
+        , HashAlgorithm hash
+        , HashDigestSize hash ~ CurveDigestSize curve
+        , ByteArrayAccess ctx
+        , ByteArrayAccess msg
+        )
+     => proxy curve -> Bool -> ctx -> PublicKey curve hash -> Bytes -> msg -> Scalar curve
+getK prx ph ctx (PublicKey pub) bsR msg =
+    let alg  = undefined :: hash
+        digK = hashWithDom prx alg ph ctx (bytes bsR <> bytes pub) msg
+     in decodeScalarNoErr prx digK
+
+encodeSignature :: EllipticCurveEdDSA curve
+                => proxy curve
+                -> (Bytes, Point curve, Scalar curve)
+                -> Signature curve hash
+encodeSignature prx (bsR, _, sS) = Signature $ buildAndFreeze $
+    bytes bsR <> bytes bsS <> zero len0
+  where
+    bsS  = encodeScalarLE prx sS :: Bytes
+    len0 = signatureSize prx - B.length bsR - B.length bsS
+
+decodeSignature :: ( EllipticCurveEdDSA curve
+                   , HashDigestSize hash ~ CurveDigestSize curve
+                   )
+                => proxy curve
+                -> Signature curve hash
+                -> CryptoFailable (Bytes, Point curve, Scalar curve)
+decodeSignature prx (Signature bs) = do
+    let (bsR, bsS) = B.splitAt (publicKeySize prx) bs
+    pR <- decodePoint prx bsR
+    sS <- decodeScalarLE prx bsS
+    return (bsR, pR, sS)
+
+-- implementations are supposed to decode any scalar up to the size of the digest
+decodeScalarNoErr :: (EllipticCurveEdDSA curve, ByteArrayAccess bs)
+                  => proxy curve -> bs -> Scalar curve
+decodeScalarNoErr prx = unwrap "decodeScalarNoErr" . decodeScalarLE prx
+
+unwrap :: String -> CryptoFailable a -> a
+unwrap name (CryptoFailed _) = error (name ++ ": assumption failed")
+unwrap _    (CryptoPassed x) = x
+
+
+-- Ed25519 implementation
+
+instance EllipticCurveEdDSA Curve_Edwards25519 where
+    type CurveDigestSize Curve_Edwards25519 = 64
+    secretKeySize _ = 32
+
+    hashWithDom _ alg ph ctx bss
+        | not ph && B.null ctx = digestDomMsg alg bss
+        | otherwise            = digestDomMsg alg (dom <> bss)
+      where dom = bytes ("SigEd25519 no Ed25519 collisions" :: ByteString) <>
+                  byte (if ph then 1 else 0) <>
+                  byte (fromIntegral $ B.length ctx) <>
+                  bytes ctx
+
+    pointPublic _ = PublicKey . Edwards25519.pointEncode
+    publicPoint _ = Edwards25519.pointDecode
+    encodeScalarLE _ = Edwards25519.scalarEncode
+    decodeScalarLE _ = Edwards25519.scalarDecodeLong
+
+    scheduleSecret prx alg priv =
+        (decodeScalarNoErr prx clamped, B.dropView hashed 32)
+      where
+        hashed  = digest alg $ \update -> update priv
+
+        clamped :: Bytes
+        clamped = B.copyAndFreeze (B.takeView hashed 32) $ \p -> do
+                      b0  <- peekElemOff p 0  :: IO Word8
+                      b31 <- peekElemOff p 31 :: IO Word8
+                      pokeElemOff p 31 ((b31 .&. 0x7F) .|. 0x40)
+                      pokeElemOff p 0  (b0 .&. 0xF8)
+
+
+{-
+  Optimize hashing by limiting the number of roundtrips between Haskell and C.
+  Hash "update" functions do not use unsafe FFI call, so better concanetate
+  small fragments together and call the update function once.
+
+  Using the IO hash interface avoids context buffer copies.
+
+  Data type Digest is not used directly but converted to Bytes early. Any use of
+  withByteArray on the unpinned Digest backend would require copy through a
+  pinned trampoline.
+-}
+
+digestDomMsg :: (HashAlgorithm alg, ByteArrayAccess msg)
+             => alg -> Builder -> msg -> Bytes
+digestDomMsg alg bss bs = digest alg $ \update ->
+    update (buildAndFreeze bss :: Bytes) >> update bs
+
+digest :: HashAlgorithm alg
+       => alg
+       -> ((forall bs . ByteArrayAccess bs => bs -> IO ()) -> IO ())
+       -> Bytes
+digest alg fn = B.convert $ unsafeDoIO $ do
+    mc <- hashMutableInitWith alg
+    fn (hashMutableUpdate mc)
+    hashMutableFinalize mc
diff --git a/Crypto/Random.hs b/Crypto/Random.hs
--- a/Crypto/Random.hs
+++ b/Crypto/Random.hs
@@ -84,6 +84,9 @@
 -- Note that the @Arbitrary@ instance provided by QuickCheck for 'Word64' does
 -- not have a uniform distribution.  It is often better to use instead
 -- @arbitraryBoundedRandom@.
+--
+-- System endianness impacts how the tuple is interpreted and therefore changes
+-- the resulting DRG.
 drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG
 drgNewTest = initializeWords
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -52,6 +52,10 @@
 with the lack of autodetection feature builtin in .cabal file, it is left on
 the user to disable the aesni. See the [Disabling AESNI] section
 
+On CentOS 7 the default C compiler includes intrinsic header files incompatible
+with per-function target options.  Solutions are to use GCC >= 4.9 or disable
+flag *use_target_attributes* (see flag configuration examples below).
+
 Disabling AESNI
 ---------------
 
@@ -71,6 +75,13 @@
 ```
 
 For help with cabal flags, see: [stackoverflow : is there a way to define flags for cabal](http://stackoverflow.com/questions/23523869/is-there-any-way-to-define-flags-for-cabal-dependencies)
+
+Enabling PCLMULDQ
+-----------------
+
+When the C toolchain supports it, enabling flag *support_pclmuldq* can bring
+additional security and performance for AES GCM.  A CPU with the necessary
+instruction set will use an alternate implementation selected at runtime.
 
 Links
 -----
diff --git a/benchs/Bench.hs b/benchs/Bench.hs
--- a/benchs/Bench.hs
+++ b/benchs/Bench.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies #-}
 module Main where
 
 import Gauge.Main
@@ -24,6 +25,8 @@
 import qualified Crypto.PubKey.ECC.Types as ECC
 import qualified Crypto.PubKey.ECC.Prim as ECC
 import qualified Crypto.PubKey.ECDSA as ECDSA
+import qualified Crypto.PubKey.Ed25519 as Ed25519
+import qualified Crypto.PubKey.EdDSA as EdDSA
 import           Crypto.Random
 
 import           Control.DeepSeq (NFData)
@@ -325,6 +328,44 @@
                   , ("secp521r1_sha512", CurveHashECDSA Curve_P521R1 SHA512)
                   ]
 
+benchEdDSA =
+    [ bgroup "EdDSA-Ed25519" benchGenEd25519
+    , bgroup "Ed25519"       benchEd25519
+    ]
+  where
+    benchGen prx alg =
+        [ bench "sign"   $ perBatchEnv (genEnv prx alg) (run_gen_sign   prx)
+        , bench "verify" $ perBatchEnv (genEnv prx alg) (run_gen_verify prx)
+        ]
+
+    benchGenEd25519 = benchGen (Just Curve_Edwards25519) SHA512
+    benchEd25519    =
+        [ bench "sign"   $ perBatchEnv ed25519Env run_ed25519_sign
+        , bench "verify" $ perBatchEnv ed25519Env run_ed25519_verify
+        ]
+
+    msg = B.empty -- empty message = worst-case scenario showing API overhead
+
+    genEnv prx alg _ = do
+        sec <- EdDSA.generateSecretKey prx
+        let pub = EdDSA.toPublic prx alg sec
+            sig = EdDSA.sign prx sec pub msg
+        return (sec, pub, sig)
+
+    run_gen_sign prx (sec, pub, _) = return (EdDSA.sign prx sec pub msg)
+
+    run_gen_verify prx (_, pub, sig) = return (EdDSA.verify prx pub msg sig)
+
+    ed25519Env _ = do
+        sec <- Ed25519.generateSecretKey
+        let pub = Ed25519.toPublic sec
+            sig = Ed25519.sign sec pub msg
+        return (sec, pub, sig)
+
+    run_ed25519_sign (sec, pub, _) = return (Ed25519.sign sec pub msg)
+
+    run_ed25519_verify (_, pub, sig) = return (Ed25519.verify pub msg sig)
+
 main = defaultMain
     [ bgroup "hash" benchHash
     , bgroup "block-cipher" benchBlockCipher
@@ -338,5 +379,6 @@
           , bgroup "ECDH" benchECDH
           ]
     , bgroup "ECDSA" benchECDSA
+    , bgroup "EdDSA" benchEdDSA
     , bgroup "F2m" benchF2m
     ]
diff --git a/cbits/argon2/core.c b/cbits/argon2/core.c
--- a/cbits/argon2/core.c
+++ b/cbits/argon2/core.c
@@ -4,7 +4,7 @@
  * Copyright 2015
  * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves
  *
- * You may use this work under the terms of a Creative Commons CC0 1.0 
+ * You may use this work under the terms of a Creative Commons CC0 1.0
  * License/Waiver or the Apache Public License 2.0, at your option. The terms of
  * these licenses can be found at:
  *
@@ -83,25 +83,25 @@
     } while ((void)0, 0)
 
     if (outlen <= BLAKE2B_OUTBYTES) {
-        TRY(blake2b_init(&blake_state, outlen));
-        TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
-        TRY(blake2b_update(&blake_state, in, inlen));
-        TRY(blake2b_final(&blake_state, out, outlen));
+        TRY(_cryptonite_blake2b_init(&blake_state, outlen));
+        TRY(_cryptonite_blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
+        TRY(_cryptonite_blake2b_update(&blake_state, in, inlen));
+        TRY(_cryptonite_blake2b_final(&blake_state, out, outlen));
     } else {
         uint32_t toproduce;
         uint8_t out_buffer[BLAKE2B_OUTBYTES];
         uint8_t in_buffer[BLAKE2B_OUTBYTES];
-        TRY(blake2b_init(&blake_state, BLAKE2B_OUTBYTES));
-        TRY(blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
-        TRY(blake2b_update(&blake_state, in, inlen));
-        TRY(blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES));
+        TRY(_cryptonite_blake2b_init(&blake_state, BLAKE2B_OUTBYTES));
+        TRY(_cryptonite_blake2b_update(&blake_state, outlen_bytes, sizeof(outlen_bytes)));
+        TRY(_cryptonite_blake2b_update(&blake_state, in, inlen));
+        TRY(_cryptonite_blake2b_final(&blake_state, out_buffer, BLAKE2B_OUTBYTES));
         memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
         out += BLAKE2B_OUTBYTES / 2;
         toproduce = (uint32_t)outlen - BLAKE2B_OUTBYTES / 2;
 
         while (toproduce > BLAKE2B_OUTBYTES) {
             memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
-            TRY(blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer,
+            TRY(_cryptonite_blake2b(out_buffer, BLAKE2B_OUTBYTES, in_buffer,
                         BLAKE2B_OUTBYTES, NULL, 0));
             memcpy(out, out_buffer, BLAKE2B_OUTBYTES / 2);
             out += BLAKE2B_OUTBYTES / 2;
@@ -109,7 +109,7 @@
         }
 
         memcpy(in_buffer, out_buffer, BLAKE2B_OUTBYTES);
-        TRY(blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL,
+        TRY(_cryptonite_blake2b(out_buffer, toproduce, in_buffer, BLAKE2B_OUTBYTES, NULL,
                     0));
         memcpy(out, out_buffer, toproduce);
     }
@@ -149,7 +149,7 @@
 }
 
 /***************Memory functions*****************/
-static 
+static
 int allocate_memory(const argon2_context *context, uint8_t **memory,
                     size_t num, size_t size) {
     size_t memory_size = num*size;
@@ -175,7 +175,7 @@
 
     return ARGON2_OK;
 }
-static 
+static
 void free_memory(const argon2_context *context, uint8_t *memory,
                  size_t num, size_t size) {
     size_t memory_size = num*size;
@@ -425,7 +425,7 @@
 }
 
 #endif /* ARGON2_NO_THREADS */
-static 
+static
 int fill_memory_blocks(argon2_instance_t *instance) {
 	if (instance == NULL || instance->lanes == 0) {
 	    return ARGON2_INCORRECT_PARAMETER;
@@ -437,7 +437,7 @@
 			fill_memory_blocks_st(instance) : fill_memory_blocks_mt(instance);
 #endif
 }
-static 
+static
 int validate_inputs(const argon2_context *context) {
     if (NULL == context) {
         return ARGON2_INCORRECT_PARAMETER;
@@ -564,7 +564,7 @@
 
     return ARGON2_OK;
 }
-static 
+static
 void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) {
     uint32_t l;
     /* Make the first and second block in each lane as G(H0||i||0) or
@@ -587,7 +587,7 @@
     }
     clear_internal_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
 }
-static 
+static
 void initial_hash(uint8_t *blockhash, argon2_context *context,
                   argon2_type type) {
     blake2b_state BlakeHash;
@@ -597,31 +597,31 @@
         return;
     }
 
-    blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
+    _cryptonite_blake2b_init(&BlakeHash, ARGON2_PREHASH_DIGEST_LENGTH);
 
     store32(&value, context->lanes);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     store32(&value, context->outlen);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     store32(&value, context->m_cost);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     store32(&value, context->t_cost);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     store32(&value, context->version);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     store32(&value, (uint32_t)type);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     store32(&value, context->pwdlen);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     if (context->pwd != NULL) {
-        blake2b_update(&BlakeHash, (const uint8_t *)context->pwd,
+        _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)context->pwd,
                        context->pwdlen);
 
         if (context->flags & ARGON2_FLAG_CLEAR_PASSWORD) {
@@ -631,18 +631,18 @@
     }
 
     store32(&value, context->saltlen);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     if (context->salt != NULL) {
-        blake2b_update(&BlakeHash, (const uint8_t *)context->salt,
+        _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)context->salt,
                        context->saltlen);
     }
 
     store32(&value, context->secretlen);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     if (context->secret != NULL) {
-        blake2b_update(&BlakeHash, (const uint8_t *)context->secret,
+        _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)context->secret,
                        context->secretlen);
 
         if (context->flags & ARGON2_FLAG_CLEAR_SECRET) {
@@ -652,16 +652,16 @@
     }
 
     store32(&value, context->adlen);
-    blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
+    _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)&value, sizeof(value));
 
     if (context->ad != NULL) {
-        blake2b_update(&BlakeHash, (const uint8_t *)context->ad,
+        _cryptonite_blake2b_update(&BlakeHash, (const uint8_t *)context->ad,
                        context->adlen);
     }
 
-    blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
+    _cryptonite_blake2b_final(&BlakeHash, blockhash, ARGON2_PREHASH_DIGEST_LENGTH);
 }
-static 
+static
 int initialize(argon2_instance_t *instance, argon2_context *context) {
     uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
     int result = ARGON2_OK;
diff --git a/cbits/blake2/ref/blake2.h b/cbits/blake2/ref/blake2.h
--- a/cbits/blake2/ref/blake2.h
+++ b/cbits/blake2/ref/blake2.h
@@ -142,51 +142,51 @@
   };
 
   /* Streaming API */
-  int blake2s_init( blake2s_state *S, size_t outlen );
-  int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
-  int blake2s_update( blake2s_state *S, const void *in, size_t inlen );
-  int blake2s_final( blake2s_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2s_init( blake2s_state *S, size_t outlen );
+  int _cryptonite_blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+  int _cryptonite_blake2s_update( blake2s_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2s_final( blake2s_state *S, void *out, size_t outlen );
 
-  int blake2b_init( blake2b_state *S, size_t outlen );
-  int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
-  int blake2b_update( blake2b_state *S, const void *in, size_t inlen );
-  int blake2b_final( blake2b_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2b_init( blake2b_state *S, size_t outlen );
+  int _cryptonite_blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+  int _cryptonite_blake2b_update( blake2b_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2b_final( blake2b_state *S, void *out, size_t outlen );
 
-  int blake2sp_init( blake2sp_state *S, size_t outlen );
-  int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
-  int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2sp_init( blake2sp_state *S, size_t outlen );
+  int _cryptonite_blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
 
-  int blake2bp_init( blake2bp_state *S, size_t outlen );
-  int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
-  int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2bp_init( blake2bp_state *S, size_t outlen );
+  int _cryptonite_blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
 
   /* Variable output length API */
-  int blake2xs_init( blake2xs_state *S, const size_t outlen );
-  int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
-  int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
-  int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
+  int _cryptonite_blake2xs_init( blake2xs_state *S, const size_t outlen );
+  int _cryptonite_blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
 
-  int blake2xb_init( blake2xb_state *S, const size_t outlen );
-  int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
-  int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
-  int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
+  int _cryptonite_blake2xb_init( blake2xb_state *S, const size_t outlen );
+  int _cryptonite_blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
 
   /* Simple API */
-  int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-  int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
-  int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-  int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
-  int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-  int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
   /* This is simply an alias for blake2b */
-  int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
 #if defined(__cplusplus)
 }
diff --git a/cbits/blake2/ref/blake2b-ref.c b/cbits/blake2/ref/blake2b-ref.c
--- a/cbits/blake2/ref/blake2b-ref.c
+++ b/cbits/blake2/ref/blake2b-ref.c
@@ -78,7 +78,7 @@
 }
 
 /* init xors IV with input parameter block */
-int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+int _cryptonite_blake2b_init_param( blake2b_state *S, const blake2b_param *P )
 {
   const uint8_t *p = ( const uint8_t * )( P );
   size_t i;
@@ -95,7 +95,7 @@
 
 
 
-int blake2b_init( blake2b_state *S, size_t outlen )
+int _cryptonite_blake2b_init( blake2b_state *S, size_t outlen )
 {
   blake2b_param P[1];
 
@@ -113,11 +113,11 @@
   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 );
+  return _cryptonite_blake2b_init_param( S, P );
 }
 
 
-int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
 {
   blake2b_param P[1];
 
@@ -138,13 +138,13 @@
   memset( P->salt,     0, sizeof( P->salt ) );
   memset( P->personal, 0, sizeof( P->personal ) );
 
-  if( blake2b_init_param( S, P ) < 0 ) return -1;
+  if( _cryptonite_blake2b_init_param( S, P ) < 0 ) return -1;
 
   {
     uint8_t block[BLAKE2B_BLOCKBYTES];
     memset( block, 0, BLAKE2B_BLOCKBYTES );
     memcpy( block, key, keylen );
-    blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
+    _cryptonite_blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
     secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
   }
   return 0;
@@ -218,7 +218,7 @@
 #undef G
 #undef ROUND
 
-int blake2b_update( blake2b_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2b_update( blake2b_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   if( inlen > 0 )
@@ -245,7 +245,7 @@
   return 0;
 }
 
-int blake2b_final( blake2b_state *S, void *out, size_t outlen )
+int _cryptonite_blake2b_final( blake2b_state *S, void *out, size_t outlen )
 {
   uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
   size_t i;
@@ -270,7 +270,7 @@
 }
 
 /* inlen, at least, should be uint64_t. Others can be size_t. */
-int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+int _cryptonite_blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
 {
   blake2b_state S[1];
 
@@ -287,26 +287,26 @@
 
   if( keylen > 0 )
   {
-    if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+    if( _cryptonite_blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
   }
   else
   {
-    if( blake2b_init( S, outlen ) < 0 ) return -1;
+    if( _cryptonite_blake2b_init( S, outlen ) < 0 ) return -1;
   }
 
-  blake2b_update( S, ( const uint8_t * )in, inlen );
-  blake2b_final( S, out, outlen );
+  _cryptonite_blake2b_update( S, ( const uint8_t * )in, inlen );
+  _cryptonite_blake2b_final( S, out, outlen );
   return 0;
 }
 
-int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) {
-  return blake2b(out, outlen, in, inlen, key, keylen);
+int _cryptonite_blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) {
+  return _cryptonite_blake2b(out, outlen, in, inlen, key, keylen);
 }
 
 #if defined(SUPERCOP)
 int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
 {
-  return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 );
+  return _cryptonite_blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 );
 }
 #endif
 
@@ -329,9 +329,9 @@
   for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
   {
     uint8_t hash[BLAKE2B_OUTBYTES];
-    blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );
+    _cryptonite_blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );
 
-    if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) )
+    if( 0 != memcmp( hash, _cryptonite_blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) )
     {
       goto fail;
     }
@@ -346,25 +346,25 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2b_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2b_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2b_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2b_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
         goto fail;
       }
 
-      if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) {
+      if (0 != memcmp(hash, _cryptonite_blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) {
         goto fail;
       }
     }
diff --git a/cbits/blake2/ref/blake2bp-ref.c b/cbits/blake2/ref/blake2bp-ref.c
--- a/cbits/blake2/ref/blake2bp-ref.c
+++ b/cbits/blake2/ref/blake2bp-ref.c
@@ -36,7 +36,7 @@
 */
 static int blake2bp_init_leaf_param( blake2b_state *S, const blake2b_param *P )
 {
-  int err = blake2b_init_param(S, P);
+  int err = _cryptonite_blake2b_init_param(S, P);
   S->outlen = P->inner_length;
   return err;
 }
@@ -74,11 +74,11 @@
   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 );
+  return _cryptonite_blake2b_init_param( S, P );
 }
 
 
-int blake2bp_init( blake2bp_state *S, size_t outlen )
+int _cryptonite_blake2bp_init( blake2bp_state *S, size_t outlen )
 {
   size_t i;
 
@@ -99,7 +99,7 @@
   return 0;
 }
 
-int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen )
 {
   size_t i;
 
@@ -125,7 +125,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -133,7 +133,7 @@
 }
 
 
-int blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   size_t left = S->buflen;
@@ -145,7 +145,7 @@
     memcpy( S->buf + left, in, fill );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
 
     in += fill;
     inlen -= fill;
@@ -168,7 +168,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
     {
-      blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
     }
@@ -184,7 +184,7 @@
   return 0;
 }
 
-int blake2bp_final( blake2bp_state *S, void *out, size_t outlen )
+int _cryptonite_blake2bp_final( blake2bp_state *S, void *out, size_t outlen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
   size_t i;
@@ -201,19 +201,19 @@
 
       if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES;
 
-      blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
+      _cryptonite_blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
     }
 
-    blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
   }
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
 
-  return blake2b_final( S->R, out, S->outlen );
+  return _cryptonite_blake2b_final( S->R, out, S->outlen );
 }
 
-int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+int _cryptonite_blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
   blake2b_state S[PARALLELISM_DEGREE][1];
@@ -243,7 +243,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -264,7 +264,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
     {
-      blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
     }
@@ -273,10 +273,10 @@
     {
       const size_t left = inlen__ - i * BLAKE2B_BLOCKBYTES;
       const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES;
-      blake2b_update( S[i], in__, len );
+      _cryptonite_blake2b_update( S[i], in__, len );
     }
 
-    blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES );
   }
 
   if( blake2bp_init_root( FS, outlen, keylen ) < 0 )
@@ -285,9 +285,9 @@
   FS->last_node = 1; /* Mark as last node */
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
 
-  return blake2b_final( FS, out, outlen );;
+  return _cryptonite_blake2b_final( FS, out, outlen );;
 }
 
 #if defined(BLAKE2BP_SELFTEST)
@@ -326,21 +326,21 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2bp_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2bp_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2bp_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2bp_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
         goto fail;
       }
 
diff --git a/cbits/blake2/ref/blake2s-ref.c b/cbits/blake2/ref/blake2s-ref.c
--- a/cbits/blake2/ref/blake2s-ref.c
+++ b/cbits/blake2/ref/blake2s-ref.c
@@ -73,7 +73,7 @@
 }
 
 /* init2 xors IV with input parameter block */
-int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+int _cryptonite_blake2s_init_param( blake2s_state *S, const blake2s_param *P )
 {
   const unsigned char *p = ( const unsigned char * )( P );
   size_t i;
@@ -90,7 +90,7 @@
 
 
 /* Sequential blake2s initialization */
-int blake2s_init( blake2s_state *S, size_t outlen )
+int _cryptonite_blake2s_init( blake2s_state *S, size_t outlen )
 {
   blake2s_param P[1];
 
@@ -109,10 +109,10 @@
   /* memset(P->reserved, 0, sizeof(P->reserved) ); */
   memset( P->salt,     0, sizeof( P->salt ) );
   memset( P->personal, 0, sizeof( P->personal ) );
-  return blake2s_init_param( S, P );
+  return _cryptonite_blake2s_init_param( S, P );
 }
 
-int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
 {
   blake2s_param P[1];
 
@@ -133,13 +133,13 @@
   memset( P->salt,     0, sizeof( P->salt ) );
   memset( P->personal, 0, sizeof( P->personal ) );
 
-  if( blake2s_init_param( S, P ) < 0 ) return -1;
+  if( _cryptonite_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 );
+    _cryptonite_blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
     secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
   }
   return 0;
@@ -211,7 +211,7 @@
 #undef G
 #undef ROUND
 
-int blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   if( inlen > 0 )
@@ -238,7 +238,7 @@
   return 0;
 }
 
-int blake2s_final( blake2s_state *S, void *out, size_t outlen )
+int _cryptonite_blake2s_final( blake2s_state *S, void *out, size_t outlen )
 {
   uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
   size_t i;
@@ -262,7 +262,7 @@
   return 0;
 }
 
-int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+int _cryptonite_blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
 {
   blake2s_state S[1];
 
@@ -279,22 +279,22 @@
 
   if( keylen > 0 )
   {
-    if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+    if( _cryptonite_blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
   }
   else
   {
-    if( blake2s_init( S, outlen ) < 0 ) return -1;
+    if( _cryptonite_blake2s_init( S, outlen ) < 0 ) return -1;
   }
 
-  blake2s_update( S, ( const uint8_t * )in, inlen );
-  blake2s_final( S, out, outlen );
+  _cryptonite_blake2s_update( S, ( const uint8_t * )in, inlen );
+  _cryptonite_blake2s_final( S, out, outlen );
   return 0;
 }
 
 #if defined(SUPERCOP)
 int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
 {
-  return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 );
+  return _cryptonite_blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 );
 }
 #endif
 
@@ -317,7 +317,7 @@
   for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
   {
     uint8_t hash[BLAKE2S_OUTBYTES];
-    blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
+    _cryptonite_blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
 
     if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) )
     {
@@ -334,21 +334,21 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2s_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2s_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2s_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2s_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
         goto fail;
       }
 
diff --git a/cbits/blake2/ref/blake2sp-ref.c b/cbits/blake2/ref/blake2sp-ref.c
--- a/cbits/blake2/ref/blake2sp-ref.c
+++ b/cbits/blake2/ref/blake2sp-ref.c
@@ -35,7 +35,7 @@
 */
 static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P )
 {
-  int err = blake2s_init_param(S, P);
+  int err = _cryptonite_blake2s_init_param(S, P);
   S->outlen = P->inner_length;
   return err;
 }
@@ -71,11 +71,11 @@
   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 );
+  return _cryptonite_blake2s_init_param( S, P );
 }
 
 
-int blake2sp_init( blake2sp_state *S, size_t outlen )
+int _cryptonite_blake2sp_init( blake2sp_state *S, size_t outlen )
 {
   size_t i;
 
@@ -96,7 +96,7 @@
   return 0;
 }
 
-int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
 {
   size_t i;
 
@@ -122,7 +122,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -130,7 +130,7 @@
 }
 
 
-int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   size_t left = S->buflen;
@@ -142,7 +142,7 @@
     memcpy( S->buf + left, in, fill );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
 
     in += fill;
     inlen -= fill;
@@ -164,7 +164,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
     {
-      blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
     }
@@ -181,7 +181,7 @@
 }
 
 
-int blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
+int _cryptonite_blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
   size_t i;
@@ -198,20 +198,20 @@
 
       if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
 
-      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+      _cryptonite_blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
     }
 
-    blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
   }
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
 
-  return blake2s_final( S->R, out, S->outlen );
+  return _cryptonite_blake2s_final( S->R, out, S->outlen );
 }
 
 
-int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+int _cryptonite_blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
   blake2s_state S[PARALLELISM_DEGREE][1];
@@ -241,7 +241,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -262,7 +262,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
     {
-      blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
     }
@@ -271,10 +271,10 @@
     {
       const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES;
       const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
-      blake2s_update( S[i], in__, len );
+      _cryptonite_blake2s_update( S[i], in__, len );
     }
 
-    blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
   }
 
   if( blake2sp_init_root( FS, outlen, keylen ) < 0 )
@@ -283,9 +283,9 @@
   FS->last_node = 1;
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
 
-  return blake2s_final( FS, out, outlen );
+  return _cryptonite_blake2s_final( FS, out, outlen );
 }
 
 
@@ -309,7 +309,7 @@
   for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
   {
     uint8_t hash[BLAKE2S_OUTBYTES];
-    blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
+    _cryptonite_blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
 
     if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) )
     {
@@ -326,21 +326,21 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2sp_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2sp_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2sp_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2sp_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
         goto fail;
       }
 
diff --git a/cbits/blake2/sse/blake2.h b/cbits/blake2/sse/blake2.h
--- a/cbits/blake2/sse/blake2.h
+++ b/cbits/blake2/sse/blake2.h
@@ -142,51 +142,51 @@
   };
 
   /* Streaming API */
-  int blake2s_init( blake2s_state *S, size_t outlen );
-  int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
-  int blake2s_update( blake2s_state *S, const void *in, size_t inlen );
-  int blake2s_final( blake2s_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2s_init( blake2s_state *S, size_t outlen );
+  int _cryptonite_blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+  int _cryptonite_blake2s_update( blake2s_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2s_final( blake2s_state *S, void *out, size_t outlen );
 
-  int blake2b_init( blake2b_state *S, size_t outlen );
-  int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
-  int blake2b_update( blake2b_state *S, const void *in, size_t inlen );
-  int blake2b_final( blake2b_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2b_init( blake2b_state *S, size_t outlen );
+  int _cryptonite_blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+  int _cryptonite_blake2b_update( blake2b_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2b_final( blake2b_state *S, void *out, size_t outlen );
 
-  int blake2sp_init( blake2sp_state *S, size_t outlen );
-  int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
-  int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2sp_init( blake2sp_state *S, size_t outlen );
+  int _cryptonite_blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
 
-  int blake2bp_init( blake2bp_state *S, size_t outlen );
-  int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
-  int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
-  int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
+  int _cryptonite_blake2bp_init( blake2bp_state *S, size_t outlen );
+  int _cryptonite_blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
 
   /* Variable output length API */
-  int blake2xs_init( blake2xs_state *S, const size_t outlen );
-  int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
-  int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
-  int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
+  int _cryptonite_blake2xs_init( blake2xs_state *S, const size_t outlen );
+  int _cryptonite_blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
 
-  int blake2xb_init( blake2xb_state *S, const size_t outlen );
-  int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
-  int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
-  int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
+  int _cryptonite_blake2xb_init( blake2xb_state *S, const size_t outlen );
+  int _cryptonite_blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
+  int _cryptonite_blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
 
   /* Simple API */
-  int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-  int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
-  int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-  int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
-  int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
-  int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
   /* This is simply an alias for blake2b */
-  int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int _cryptonite_blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
 
 #if defined(__cplusplus)
 }
diff --git a/cbits/blake2/sse/blake2b.c b/cbits/blake2/sse/blake2b.c
--- a/cbits/blake2/sse/blake2b.c
+++ b/cbits/blake2/sse/blake2b.c
@@ -74,7 +74,7 @@
 }
 
 /* init xors IV with input parameter block */
-int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+int _cryptonite_blake2b_init_param( blake2b_state *S, const blake2b_param *P )
 {
   size_t i;
   /*blake2b_init0( S ); */
@@ -92,7 +92,7 @@
 
 
 /* Some sort of default parameter block initialization, for sequential blake2b */
-int blake2b_init( blake2b_state *S, size_t outlen )
+int _cryptonite_blake2b_init( blake2b_state *S, size_t outlen )
 {
   blake2b_param P[1];
 
@@ -111,10 +111,10 @@
   memset( P->salt,     0, sizeof( P->salt ) );
   memset( P->personal, 0, sizeof( P->personal ) );
 
-  return blake2b_init_param( S, P );
+  return _cryptonite_blake2b_init_param( S, P );
 }
 
-int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
 {
   blake2b_param P[1];
 
@@ -135,14 +135,14 @@
   memset( P->salt,     0, sizeof( P->salt ) );
   memset( P->personal, 0, sizeof( P->personal ) );
 
-  if( blake2b_init_param( S, P ) < 0 )
+  if( _cryptonite_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 );
+    _cryptonite_blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
     secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
   }
   return 0;
@@ -218,7 +218,7 @@
 }
 
 
-int blake2b_update( blake2b_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2b_update( blake2b_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   if( inlen > 0 )
@@ -246,7 +246,7 @@
 }
 
 
-int blake2b_final( blake2b_state *S, void *out, size_t outlen )
+int _cryptonite_blake2b_final( blake2b_state *S, void *out, size_t outlen )
 {
   if( out == NULL || outlen < S->outlen )
     return -1;
@@ -264,7 +264,7 @@
 }
 
 
-int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+int _cryptonite_blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
 {
   blake2b_state S[1];
 
@@ -281,26 +281,26 @@
 
   if( keylen )
   {
-    if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+    if( _cryptonite_blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
   }
   else
   {
-    if( blake2b_init( S, outlen ) < 0 ) return -1;
+    if( _cryptonite_blake2b_init( S, outlen ) < 0 ) return -1;
   }
 
-  blake2b_update( S, ( const uint8_t * )in, inlen );
-  blake2b_final( S, out, outlen );
+  _cryptonite_blake2b_update( S, ( const uint8_t * )in, inlen );
+  _cryptonite_blake2b_final( S, out, outlen );
   return 0;
 }
 
-int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) {
-  return blake2b(out, outlen, in, inlen, key, keylen);
+int _cryptonite_blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) {
+  return _cryptonite_blake2b(out, outlen, in, inlen, key, keylen);
 }
 
 #if defined(SUPERCOP)
 int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
 {
-  return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 );
+  return _cryptonite_blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 );
 }
 #endif
 
@@ -340,21 +340,21 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2b_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2b_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2b_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2b_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
         goto fail;
       }
 
diff --git a/cbits/blake2/sse/blake2bp.c b/cbits/blake2/sse/blake2bp.c
--- a/cbits/blake2/sse/blake2bp.c
+++ b/cbits/blake2/sse/blake2bp.c
@@ -36,7 +36,7 @@
 */
 static int blake2bp_init_leaf_param( blake2b_state *S, const blake2b_param *P )
 {
-  int err = blake2b_init_param(S, P);
+  int err = _cryptonite_blake2b_init_param(S, P);
   S->outlen = P->inner_length;
   return err;
 }
@@ -74,11 +74,11 @@
   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 );
+  return _cryptonite_blake2b_init_param( S, P );
 }
 
 
-int blake2bp_init( blake2bp_state *S, size_t outlen )
+int _cryptonite_blake2bp_init( blake2bp_state *S, size_t outlen )
 {
   size_t i;
   if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
@@ -98,7 +98,7 @@
   return 0;
 }
 
-int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen )
 {
   size_t i;
 
@@ -124,7 +124,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -132,7 +132,7 @@
 }
 
 
-int blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   size_t left = S->buflen;
@@ -144,7 +144,7 @@
     memcpy( S->buf + left, in, fill );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
 
     in += fill;
     inlen -= fill;
@@ -167,7 +167,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
     {
-      blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
     }
@@ -185,7 +185,7 @@
 
 
 
-int blake2bp_final( blake2bp_state *S, void *out, size_t outlen )
+int _cryptonite_blake2bp_final( blake2bp_state *S, void *out, size_t outlen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
   size_t i;
@@ -202,19 +202,19 @@
 
       if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES;
 
-      blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
+      _cryptonite_blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
     }
 
-    blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
   }
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
 
-  return blake2b_final( S->R, out, S->outlen );
+  return _cryptonite_blake2b_final( S->R, out, S->outlen );
 }
 
-int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+int _cryptonite_blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
   blake2b_state S[PARALLELISM_DEGREE][1];
@@ -244,7 +244,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -265,7 +265,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
     {
-      blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES );
+      _cryptonite_blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
     }
@@ -274,10 +274,10 @@
     {
       const size_t left = inlen__ - i * BLAKE2B_BLOCKBYTES;
       const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES;
-      blake2b_update( S[i], in__, len );
+      _cryptonite_blake2b_update( S[i], in__, len );
     }
 
-    blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES );
   }
 
   if( blake2bp_init_root( FS, outlen, keylen ) < 0 )
@@ -286,9 +286,9 @@
   FS->last_node = 1; /* Mark as last node */
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
+    _cryptonite_blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
 
-  return blake2b_final( FS, out, outlen );
+  return _cryptonite_blake2b_final( FS, out, outlen );
 }
 
 
@@ -311,7 +311,7 @@
   for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
   {
     uint8_t hash[BLAKE2B_OUTBYTES];
-    blake2bp( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );
+    _cryptonite_blake2bp( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );
 
     if( 0 != memcmp( hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES ) )
     {
@@ -328,21 +328,21 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2bp_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2bp_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2bp_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2bp_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {
         goto fail;
       }
 
diff --git a/cbits/blake2/sse/blake2s.c b/cbits/blake2/sse/blake2s.c
--- a/cbits/blake2/sse/blake2s.c
+++ b/cbits/blake2/sse/blake2s.c
@@ -72,7 +72,7 @@
 }
 
 /* init2 xors IV with input parameter block */
-int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+int _cryptonite_blake2s_init_param( blake2s_state *S, const blake2s_param *P )
 {
   size_t i;
   /*blake2s_init0( S ); */
@@ -90,7 +90,7 @@
 
 
 /* Some sort of default parameter block initialization, for sequential blake2s */
-int blake2s_init( blake2s_state *S, size_t outlen )
+int _cryptonite_blake2s_init( blake2s_state *S, size_t outlen )
 {
   blake2s_param P[1];
 
@@ -110,11 +110,11 @@
   memset( P->salt,     0, sizeof( P->salt ) );
   memset( P->personal, 0, sizeof( P->personal ) );
 
-  return blake2s_init_param( S, P );
+  return _cryptonite_blake2s_init_param( S, P );
 }
 
 
-int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
 {
   blake2s_param P[1];
 
@@ -136,14 +136,14 @@
   memset( P->salt,     0, sizeof( P->salt ) );
   memset( P->personal, 0, sizeof( P->personal ) );
 
-  if( blake2s_init_param( S, P ) < 0 )
+  if( _cryptonite_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 );
+    _cryptonite_blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
     secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
   }
   return 0;
@@ -206,7 +206,7 @@
   STOREU( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row2, row4 ) ) );
 }
 
-int blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   if( inlen > 0 )
@@ -233,7 +233,7 @@
   return 0;
 }
 
-int blake2s_final( blake2s_state *S, void *out, size_t outlen )
+int _cryptonite_blake2s_final( blake2s_state *S, void *out, size_t outlen )
 {
   uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
   size_t i;
@@ -275,15 +275,15 @@
 
   if( keylen > 0 )
   {
-    if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+    if( _cryptonite_blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
   }
   else
   {
-    if( blake2s_init( S, outlen ) < 0 ) return -1;
+    if( _cryptonite_blake2s_init( S, outlen ) < 0 ) return -1;
   }
 
-  blake2s_update( S, ( const uint8_t * )in, inlen );
-  blake2s_final( S, out, outlen );
+  _cryptonite_blake2s_update( S, ( const uint8_t * )in, inlen );
+  _cryptonite_blake2s_final( S, out, outlen );
   return 0;
 }
 
@@ -330,21 +330,21 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2s_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2s_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2s_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2s_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
         goto fail;
       }
 
diff --git a/cbits/blake2/sse/blake2sp.c b/cbits/blake2/sse/blake2sp.c
--- a/cbits/blake2/sse/blake2sp.c
+++ b/cbits/blake2/sse/blake2sp.c
@@ -35,7 +35,7 @@
 */
 static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P )
 {
-  int err = blake2s_init_param(S, P);
+  int err = _cryptonite_blake2s_init_param(S, P);
   S->outlen = P->inner_length;
   return err;
 }
@@ -71,11 +71,11 @@
   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 );
+  return _cryptonite_blake2s_init_param( S, P );
 }
 
 
-int blake2sp_init( blake2sp_state *S, size_t outlen )
+int _cryptonite_blake2sp_init( blake2sp_state *S, size_t outlen )
 {
   size_t i;
 
@@ -96,7 +96,7 @@
   return 0;
 }
 
-int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
+int _cryptonite_blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
 {
   size_t i;
 
@@ -122,7 +122,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -130,7 +130,7 @@
 }
 
 
-int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )
+int _cryptonite_blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )
 {
   const unsigned char * in = (const unsigned char *)pin;
   size_t left = S->buflen;
@@ -142,7 +142,7 @@
     memcpy( S->buf + left, in, fill );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
 
     in += fill;
     inlen -= fill;
@@ -165,7 +165,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
     {
-      blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
     }
@@ -182,7 +182,7 @@
 }
 
 
-int blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
+int _cryptonite_blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
   size_t i;
@@ -199,20 +199,20 @@
 
       if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
 
-      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+      _cryptonite_blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
     }
 
-    blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
   }
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
 
-  return blake2s_final( S->R, out, S->outlen );
+  return _cryptonite_blake2s_final( S->R, out, S->outlen );
 }
 
 
-int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+int _cryptonite_blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
 {
   uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
   blake2s_state S[PARALLELISM_DEGREE][1];
@@ -242,7 +242,7 @@
     memcpy( block, key, keylen );
 
     for( i = 0; i < PARALLELISM_DEGREE; ++i )
-      blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
 
     secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
   }
@@ -263,7 +263,7 @@
 
     while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
     {
-      blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
+      _cryptonite_blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
       in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
       inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
     }
@@ -272,10 +272,10 @@
     {
       const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES;
       const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
-      blake2s_update( S[i], in__, len );
+      _cryptonite_blake2s_update( S[i], in__, len );
     }
 
-    blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
   }
 
   if( blake2sp_init_root( FS, outlen, keylen ) < 0 )
@@ -284,9 +284,9 @@
   FS->last_node = 1;
 
   for( i = 0; i < PARALLELISM_DEGREE; ++i )
-    blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
+    _cryptonite_blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
 
-  return blake2s_final( FS, out, outlen );
+  return _cryptonite_blake2s_final( FS, out, outlen );
 }
 
 #if defined(BLAKE2SP_SELFTEST)
@@ -308,7 +308,7 @@
   for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
   {
     uint8_t hash[BLAKE2S_OUTBYTES];
-    blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
+    _cryptonite_blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
 
     if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) )
     {
@@ -325,21 +325,21 @@
       size_t mlen = i;
       int err = 0;
 
-      if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+      if( (err = _cryptonite_blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
         goto fail;
       }
 
       while (mlen >= step) {
-        if ( (err = blake2sp_update(&S, p, step)) < 0 ) {
+        if ( (err = _cryptonite_blake2sp_update(&S, p, step)) < 0 ) {
           goto fail;
         }
         mlen -= step;
         p += step;
       }
-      if ( (err = blake2sp_update(&S, p, mlen)) < 0) {
+      if ( (err = _cryptonite_blake2sp_update(&S, p, mlen)) < 0) {
         goto fail;
       }
-      if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+      if ( (err = _cryptonite_blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
         goto fail;
       }
 
diff --git a/cbits/cryptonite_align.h b/cbits/cryptonite_align.h
--- a/cbits/cryptonite_align.h
+++ b/cbits/cryptonite_align.h
@@ -44,11 +44,21 @@
 	*((uint32_t *) dst) = cpu_to_le32(v);
 }
 
+static inline void xor_le32_aligned(uint8_t *dst, const uint32_t v)
+{
+	*((uint32_t *) dst) ^= cpu_to_le32(v);
+}
+
 static inline void store_be32_aligned(uint8_t *dst, const uint32_t v)
 {
 	*((uint32_t *) dst) = cpu_to_be32(v);
 }
 
+static inline void xor_be32_aligned(uint8_t *dst, const uint32_t v)
+{
+	*((uint32_t *) dst) ^= cpu_to_be32(v);
+}
+
 static inline void store_le64_aligned(uint8_t *dst, const uint64_t v)
 {
 	*((uint64_t *) dst) = cpu_to_le64(v);
@@ -59,6 +69,11 @@
 	*((uint64_t *) dst) = cpu_to_be64(v);
 }
 
+static inline void xor_be64_aligned(uint8_t *dst, const uint64_t v)
+{
+	*((uint64_t *) dst) ^= cpu_to_be64(v);
+}
+
 #ifdef UNALIGNED_ACCESS_OK
 #define load_le32(a) load_le32_aligned(a)
 #else
@@ -70,20 +85,30 @@
 
 #ifdef UNALIGNED_ACCESS_OK
 #define store_le32(a, b) store_le32_aligned(a, b)
+#define xor_le32(a, b) xor_le32_aligned(a, b)
 #else
 static inline void store_le32(uint8_t *dst, const uint32_t v)
 {
 	dst[0] = v; dst[1] = v >> 8; dst[2] = v >> 16; dst[3] = v >> 24;
 }
+static inline void xor_le32(uint8_t *dst, const uint32_t v)
+{
+	dst[0] ^= v; dst[1] ^= v >> 8; dst[2] ^= v >> 16; dst[3] ^= v >> 24;
+}
 #endif
 
 #ifdef UNALIGNED_ACCESS_OK
 #define store_be32(a, b) store_be32_aligned(a, b)
+#define xor_be32(a, b) xor_be32_aligned(a, b)
 #else
 static inline void store_be32(uint8_t *dst, const uint32_t v)
 {
 	dst[3] = v; dst[2] = v >> 8; dst[1] = v >> 16; dst[0] = v >> 24;
 }
+static inline void xor_be32(uint8_t *dst, const uint32_t v)
+{
+	dst[3] ^= v; dst[2] ^= v >> 8; dst[1] ^= v >> 16; dst[0] ^= v >> 24;
+}
 #endif
 
 #ifdef UNALIGNED_ACCESS_OK
@@ -98,11 +123,17 @@
 
 #ifdef UNALIGNED_ACCESS_OK
 #define store_be64(a, b) store_be64_aligned(a, b)
+#define xor_be64(a, b) xor_be64_aligned(a, b)
 #else
 static inline void store_be64(uint8_t *dst, const uint64_t v)
 {
 	dst[7] = v      ; dst[6] = v >> 8 ; dst[5] = v >> 16; dst[4] = v >> 24;
 	dst[3] = v >> 32; dst[2] = v >> 40; dst[1] = v >> 48; dst[0] = v >> 56;
+}
+static inline void xor_be64(uint8_t *dst, const uint64_t v)
+{
+	dst[7] ^= v      ; dst[6] ^= v >> 8 ; dst[5] ^= v >> 16; dst[4] ^= v >> 24;
+	dst[3] ^= v >> 32; dst[2] ^= v >> 40; dst[1] ^= v >> 48; dst[0] ^= v >> 56;
 }
 #endif
 
diff --git a/cbits/cryptonite_blake2b.c b/cbits/cryptonite_blake2b.c
--- a/cbits/cryptonite_blake2b.c
+++ b/cbits/cryptonite_blake2b.c
@@ -2,15 +2,15 @@
 
 void cryptonite_blake2b_init(blake2b_ctx *ctx, uint32_t hashlen)
 {
-	blake2b_init(ctx, hashlen / 8);
+	_cryptonite_blake2b_init(ctx, hashlen / 8);
 }
 
 void cryptonite_blake2b_update(blake2b_ctx *ctx, const uint8_t *data, uint32_t len)
 {
-	blake2b_update(ctx, data, len);
+	_cryptonite_blake2b_update(ctx, data, len);
 }
 
 void cryptonite_blake2b_finalize(blake2b_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
-	blake2b_final(ctx, out, hashlen / 8);
+	_cryptonite_blake2b_final(ctx, out, hashlen / 8);
 }
diff --git a/cbits/cryptonite_blake2bp.c b/cbits/cryptonite_blake2bp.c
--- a/cbits/cryptonite_blake2bp.c
+++ b/cbits/cryptonite_blake2bp.c
@@ -2,15 +2,15 @@
 
 void cryptonite_blake2bp_init(blake2bp_ctx *ctx, uint32_t hashlen)
 {
-	blake2bp_init(ctx, hashlen / 8);
+	_cryptonite_blake2bp_init(ctx, hashlen / 8);
 }
 
 void cryptonite_blake2bp_update(blake2bp_ctx *ctx, const uint8_t *data, uint32_t len)
 {
-	blake2bp_update(ctx, data, len);
+	_cryptonite_blake2bp_update(ctx, data, len);
 }
 
 void cryptonite_blake2bp_finalize(blake2bp_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
-	blake2bp_final(ctx, out, hashlen / 8);
+	_cryptonite_blake2bp_final(ctx, out, hashlen / 8);
 }
diff --git a/cbits/cryptonite_blake2s.c b/cbits/cryptonite_blake2s.c
--- a/cbits/cryptonite_blake2s.c
+++ b/cbits/cryptonite_blake2s.c
@@ -2,15 +2,15 @@
 
 void cryptonite_blake2s_init(blake2s_ctx *ctx, uint32_t hashlen)
 {
-	blake2s_init(ctx, hashlen / 8);
+	_cryptonite_blake2s_init(ctx, hashlen / 8);
 }
 
 void cryptonite_blake2s_update(blake2s_ctx *ctx, const uint8_t *data, uint32_t len)
 {
-	blake2s_update(ctx, data, len);
+	_cryptonite_blake2s_update(ctx, data, len);
 }
 
 void cryptonite_blake2s_finalize(blake2s_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
-	blake2s_final(ctx, out, hashlen / 8);
+	_cryptonite_blake2s_final(ctx, out, hashlen / 8);
 }
diff --git a/cbits/cryptonite_blake2sp.c b/cbits/cryptonite_blake2sp.c
--- a/cbits/cryptonite_blake2sp.c
+++ b/cbits/cryptonite_blake2sp.c
@@ -2,15 +2,15 @@
 
 void cryptonite_blake2sp_init(blake2sp_ctx *ctx, uint32_t hashlen)
 {
-	blake2sp_init(ctx, hashlen / 8);
+	_cryptonite_blake2sp_init(ctx, hashlen / 8);
 }
 
 void cryptonite_blake2sp_update(blake2sp_ctx *ctx, const uint8_t *data, uint32_t len)
 {
-	blake2sp_update(ctx, data, len);
+	_cryptonite_blake2sp_update(ctx, data, len);
 }
 
 void cryptonite_blake2sp_finalize(blake2sp_ctx *ctx, uint32_t hashlen, uint8_t *out)
 {
-	blake2sp_final(ctx, out, hashlen / 8);
+	_cryptonite_blake2sp_final(ctx, out, hashlen / 8);
 }
diff --git a/cbits/cryptonite_hash_prefix.c b/cbits/cryptonite_hash_prefix.c
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_hash_prefix.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 Olivier Chéron <olivier.cheron@gmail.com>
+ *
+ * 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 <cryptonite_hash_prefix.h>
+
+void CRYPTONITE_HASHED(finalize_prefix)(struct HASHED_LOWER(ctx) *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out)
+{
+	uint64_t bits[HASHED(BITS_ELEMS)];
+	uint8_t *p = (uint8_t *) &bits;
+	uint32_t index, padidx, padlen, pos, out_mask;
+	static const uint32_t cut_off = HASHED(BLOCK_SIZE) - sizeof(bits);
+
+	/* Make sure n <= len */
+	n += (len - n) & constant_time_lt(len, n);
+
+	/* Initial index, based on current context state */
+	index = CRYPTONITE_HASHED(get_index)(ctx);
+
+	/* Final size after n bytes */
+	CRYPTONITE_HASHED(incr_sz)(ctx, bits, n);
+
+	/* Padding index and length */
+	padidx = CRYPTONITE_HASHED(get_index)(ctx);
+	padlen = HASHED(BLOCK_SIZE) + cut_off - padidx;
+	padlen -= HASHED(BLOCK_SIZE) & constant_time_lt(padidx, cut_off);
+
+	/* Initialize buffers because we will XOR into them */
+	memset(ctx->buf + index, 0, HASHED(BLOCK_SIZE) - index);
+	memset(out, 0, HASHED(DIGEST_SIZE));
+	pos = 0;
+
+	/* Iterate based on the full buffer length, regardless of n, and include
+	 * the maximum overhead with padding and size bytes
+	 */
+	while (pos < len + HASHED(BLOCK_SIZE) + sizeof(bits)) {
+		uint8_t b;
+
+		/* Take as many bytes from the input buffer as possible */
+		if (pos < len)
+			b = *(data++) & (uint8_t) constant_time_lt(pos, n);
+		else
+			b = 0;
+
+		/* First padding byte */
+		b |= 0x80 & (uint8_t) constant_time_eq(pos, n);
+
+		/* Size bytes are always at the end of a block */
+		if (index >= cut_off)
+			b |= p[index - cut_off] & (uint8_t) constant_time_ge(pos, n + padlen);
+
+		/* Store this byte into the buffer */
+		ctx->buf[index++] ^= b;
+		pos++;
+
+		/* Process a full block, at a boundary which is independent from n */
+		if (index >= HASHED(BLOCK_SIZE)) {
+			index = 0;
+			HASHED_LOWER(do_chunk)(ctx, (void *) ctx->buf);
+			memset(ctx->buf, 0, HASHED(BLOCK_SIZE));
+
+			/* Try to store the result: this is a no-op except when we reach the
+			 * actual size based on n, more iterations may continue after that
+			 * when len is really larger
+			 */
+			out_mask = constant_time_eq(pos, n + padlen + sizeof(bits));
+			CRYPTONITE_HASHED(select_digest)(ctx, out, out_mask);
+		}
+	}
+}
diff --git a/cbits/cryptonite_hash_prefix.h b/cbits/cryptonite_hash_prefix.h
new file mode 100644
--- /dev/null
+++ b/cbits/cryptonite_hash_prefix.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 Olivier Chéron <olivier.cheron@gmail.com>
+ *
+ * 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 CRYPTONITE_HASH_PREFIX_H
+#define CRYPTONITE_HASH_PREFIX_H
+
+#include <stdint.h>
+
+static inline uint32_t constant_time_msb(uint32_t a)
+{
+	return 0 - (a >> 31);
+}
+
+static inline uint32_t constant_time_lt(uint32_t a, uint32_t b)
+{
+	return constant_time_msb(a ^ ((a ^ b) | ((a - b) ^ b)));
+}
+
+static inline uint32_t constant_time_ge(uint32_t a, uint32_t b)
+{
+	return ~constant_time_lt(a, b);
+}
+
+static inline uint32_t constant_time_is_zero(uint32_t a)
+{
+	return constant_time_msb(~a & (a - 1));
+}
+
+static inline uint32_t constant_time_eq(uint32_t a, uint32_t b)
+{
+	return constant_time_is_zero(a ^ b);
+}
+
+static inline uint64_t constant_time_msb_64(uint64_t a)
+{
+	return 0 - (a >> 63);
+}
+
+static inline uint64_t constant_time_lt_64(uint64_t a, uint64_t b)
+{
+	return constant_time_msb_64(a ^ ((a ^ b) | ((a - b) ^ b)));
+}
+
+#endif
diff --git a/cbits/cryptonite_md5.c b/cbits/cryptonite_md5.c
--- a/cbits/cryptonite_md5.c
+++ b/cbits/cryptonite_md5.c
@@ -185,3 +185,30 @@
 	store_le32(out+ 8, ctx->h[2]);
 	store_le32(out+12, ctx->h[3]);
 }
+
+#define HASHED(m) MD5_##m
+#define HASHED_LOWER(m) md5_##m
+#define CRYPTONITE_HASHED(m) cryptonite_md5_##m
+#define MD5_BLOCK_SIZE 64
+#define MD5_BITS_ELEMS 1
+
+static inline uint32_t cryptonite_md5_get_index(const struct md5_ctx *ctx)
+{
+	return (uint32_t) (ctx->sz & 0x3f);
+}
+
+static inline void cryptonite_md5_incr_sz(struct md5_ctx *ctx, uint64_t *bits, uint32_t n)
+{
+	ctx->sz += n;
+	*bits = cpu_to_le64(ctx->sz << 3);
+}
+
+static inline void cryptonite_md5_select_digest(const struct md5_ctx *ctx, uint8_t *out, uint32_t out_mask)
+{
+	xor_le32(out   , ctx->h[0] & out_mask);
+	xor_le32(out+ 4, ctx->h[1] & out_mask);
+	xor_le32(out+ 8, ctx->h[2] & out_mask);
+	xor_le32(out+12, ctx->h[3] & out_mask);
+}
+
+#include <cryptonite_hash_prefix.c>
diff --git a/cbits/cryptonite_md5.h b/cbits/cryptonite_md5.h
--- a/cbits/cryptonite_md5.h
+++ b/cbits/cryptonite_md5.h
@@ -39,5 +39,6 @@
 void cryptonite_md5_init(struct md5_ctx *ctx);
 void cryptonite_md5_update(struct md5_ctx *ctx, const uint8_t *data, uint32_t len);
 void cryptonite_md5_finalize(struct md5_ctx *ctx, uint8_t *out);
+void cryptonite_md5_finalize_prefix(struct md5_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);
 
 #endif
diff --git a/cbits/cryptonite_sha1.c b/cbits/cryptonite_sha1.c
--- a/cbits/cryptonite_sha1.c
+++ b/cbits/cryptonite_sha1.c
@@ -216,3 +216,31 @@
 	store_be32(out+12, ctx->h[3]);
 	store_be32(out+16, ctx->h[4]);
 }
+
+#define HASHED(m) SHA1_##m
+#define HASHED_LOWER(m) sha1_##m
+#define CRYPTONITE_HASHED(m) cryptonite_sha1_##m
+#define SHA1_BLOCK_SIZE 64
+#define SHA1_BITS_ELEMS 1
+
+static inline uint32_t cryptonite_sha1_get_index(const struct sha1_ctx *ctx)
+{
+	return (uint32_t) (ctx->sz & 0x3f);
+}
+
+static inline void cryptonite_sha1_incr_sz(struct sha1_ctx *ctx, uint64_t *bits, uint32_t n)
+{
+	ctx->sz += n;
+	*bits = cpu_to_be64(ctx->sz << 3);
+}
+
+static inline void cryptonite_sha1_select_digest(const struct sha1_ctx *ctx, uint8_t *out, uint32_t out_mask)
+{
+	xor_be32(out   , ctx->h[0] & out_mask);
+	xor_be32(out+ 4, ctx->h[1] & out_mask);
+	xor_be32(out+ 8, ctx->h[2] & out_mask);
+	xor_be32(out+12, ctx->h[3] & out_mask);
+	xor_be32(out+16, ctx->h[4] & out_mask);
+}
+
+#include <cryptonite_hash_prefix.c>
diff --git a/cbits/cryptonite_sha1.h b/cbits/cryptonite_sha1.h
--- a/cbits/cryptonite_sha1.h
+++ b/cbits/cryptonite_sha1.h
@@ -41,5 +41,6 @@
 void cryptonite_sha1_init(struct sha1_ctx *ctx);
 void cryptonite_sha1_update(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len);
 void cryptonite_sha1_finalize(struct sha1_ctx *ctx, uint8_t *out);
+void cryptonite_sha1_finalize_prefix(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);
 
 #endif
diff --git a/cbits/cryptonite_sha256.c b/cbits/cryptonite_sha256.c
--- a/cbits/cryptonite_sha256.c
+++ b/cbits/cryptonite_sha256.c
@@ -161,6 +161,14 @@
 	memcpy(out, intermediate, SHA224_DIGEST_SIZE);
 }
 
+void cryptonite_sha224_finalize_prefix(struct sha224_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out)
+{
+	uint8_t intermediate[SHA256_DIGEST_SIZE];
+
+	cryptonite_sha256_finalize_prefix(ctx, data, len, n, intermediate);
+	memcpy(out, intermediate, SHA224_DIGEST_SIZE);
+}
+
 void cryptonite_sha256_finalize(struct sha256_ctx *ctx, uint8_t *out)
 {
 	static uint8_t padding[64] = { 0x80, };
@@ -182,3 +190,29 @@
 	for (i = 0; i < 8; i++)
 		store_be32(out+4*i, ctx->h[i]);
 }
+
+#define HASHED(m) SHA256_##m
+#define HASHED_LOWER(m) sha256_##m
+#define CRYPTONITE_HASHED(m) cryptonite_sha256_##m
+#define SHA256_BLOCK_SIZE 64
+#define SHA256_BITS_ELEMS 1
+
+static inline uint32_t cryptonite_sha256_get_index(const struct sha256_ctx *ctx)
+{
+	return (uint32_t) (ctx->sz & 0x3f);
+}
+
+static inline void cryptonite_sha256_incr_sz(struct sha256_ctx *ctx, uint64_t *bits, uint32_t n)
+{
+	ctx->sz += n;
+	*bits = cpu_to_be64(ctx->sz << 3);
+}
+
+static inline void cryptonite_sha256_select_digest(const struct sha256_ctx *ctx, uint8_t *out, uint32_t out_mask)
+{
+	uint32_t i;
+	for (i = 0; i < 8; i++)
+		xor_be32(out+4*i, ctx->h[i] & out_mask);
+}
+
+#include <cryptonite_hash_prefix.c>
diff --git a/cbits/cryptonite_sha256.h b/cbits/cryptonite_sha256.h
--- a/cbits/cryptonite_sha256.h
+++ b/cbits/cryptonite_sha256.h
@@ -47,9 +47,11 @@
 void cryptonite_sha224_init(struct sha224_ctx *ctx);
 void cryptonite_sha224_update(struct sha224_ctx *ctx, const uint8_t *data, uint32_t len);
 void cryptonite_sha224_finalize(struct sha224_ctx *ctx, uint8_t *out);
+void cryptonite_sha224_finalize_prefix(struct sha224_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);
 
 void cryptonite_sha256_init(struct sha256_ctx *ctx);
 void cryptonite_sha256_update(struct sha256_ctx *ctx, const uint8_t *data, uint32_t len);
 void cryptonite_sha256_finalize(struct sha256_ctx *ctx, uint8_t *out);
+void cryptonite_sha256_finalize_prefix(struct sha256_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, 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
@@ -180,6 +180,14 @@
 	memcpy(out, intermediate, SHA384_DIGEST_SIZE);
 }
 
+void cryptonite_sha384_finalize_prefix(struct sha384_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out)
+{
+	uint8_t intermediate[SHA512_DIGEST_SIZE];
+
+	cryptonite_sha512_finalize_prefix(ctx, data, len, n, intermediate);
+	memcpy(out, intermediate, SHA384_DIGEST_SIZE);
+}
+
 void cryptonite_sha512_finalize(struct sha512_ctx *ctx, uint8_t *out)
 {
 	static uint8_t padding[128] = { 0x80, };
@@ -202,6 +210,38 @@
 	for (i = 0; i < 8; i++)
 		store_be64(out+8*i, ctx->h[i]);
 }
+
+#define HASHED(m) SHA512_##m
+#define HASHED_LOWER(m) sha512_##m
+#define CRYPTONITE_HASHED(m) cryptonite_sha512_##m
+#define SHA512_BLOCK_SIZE 128
+#define SHA512_BITS_ELEMS 2
+
+#include <cryptonite_hash_prefix.h>
+
+static inline uint32_t cryptonite_sha512_get_index(const struct sha512_ctx *ctx)
+{
+	return (uint32_t) (ctx->sz[0] & 0x7f);
+}
+
+static inline void cryptonite_sha512_incr_sz(struct sha512_ctx *ctx, uint64_t *bits, uint32_t n)
+{
+	ctx->sz[0] += n;
+	ctx->sz[1] += 1 & constant_time_lt_64(ctx->sz[0], n);
+	bits[0] = cpu_to_be64((ctx->sz[1] << 3 | ctx->sz[0] >> 61));
+	bits[1] = cpu_to_be64((ctx->sz[0] << 3));
+}
+
+static inline void cryptonite_sha512_select_digest(const struct sha512_ctx *ctx, uint8_t *out, uint32_t out_mask)
+{
+	uint32_t i;
+	uint64_t out_mask_64 = out_mask;
+	out_mask_64 |= out_mask_64 << 32;
+	for (i = 0; i < 8; i++)
+		xor_be64(out+8*i, ctx->h[i] & out_mask_64);
+}
+
+#include <cryptonite_hash_prefix.c>
 
 #include <stdio.h>
 
diff --git a/cbits/cryptonite_sha512.h b/cbits/cryptonite_sha512.h
--- a/cbits/cryptonite_sha512.h
+++ b/cbits/cryptonite_sha512.h
@@ -46,10 +46,12 @@
 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);
+void cryptonite_sha384_finalize_prefix(struct sha384_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);
 
 void cryptonite_sha512_init(struct sha512_ctx *ctx);
 void cryptonite_sha512_update(struct sha512_ctx *ctx, const uint8_t *data, uint32_t len);
 void cryptonite_sha512_finalize(struct sha512_ctx *ctx, uint8_t *out);
+void cryptonite_sha512_finalize_prefix(struct sha512_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);
 
 /* only multiples of 8 are supported as valid t values */
 void cryptonite_sha512t_init(struct sha512_ctx *ctx, uint32_t hashlen);
diff --git a/cryptonite.cabal b/cryptonite.cabal
--- a/cryptonite.cabal
+++ b/cryptonite.cabal
@@ -1,5 +1,5 @@
 Name:                cryptonite
-version:             0.27
+version:             0.30
 Synopsis:            Cryptography Primitives sink
 Description:
     A repository of cryptographic primitives.
@@ -36,7 +36,7 @@
 Homepage:            https://github.com/haskell-crypto/cryptonite
 Bug-reports:         https://github.com/haskell-crypto/cryptonite/issues
 Cabal-Version:       1.18
-tested-with:         GHC==8.8.2, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2
+tested-with:         GHC==9.2.2, GHC==9.0.2, GHC==8.10.7, GHC==8.8.4
 extra-doc-files:     README.md CHANGELOG.md
 extra-source-files:  cbits/*.h
                      cbits/aes/*.h
@@ -57,6 +57,7 @@
                      cbits/argon2/*.h
                      cbits/argon2/*.c
                      cbits/aes/x86ni_impl.c
+                     cbits/cryptonite_hash_prefix.c
                      tests/*.hs
 
 source-repository head
@@ -169,6 +170,7 @@
                      Crypto.PubKey.ECIES
                      Crypto.PubKey.Ed25519
                      Crypto.PubKey.Ed448
+                     Crypto.PubKey.EdDSA
                      Crypto.PubKey.RSA
                      Crypto.PubKey.RSA.PKCS15
                      Crypto.PubKey.RSA.Prim
@@ -234,6 +236,7 @@
                      Crypto.PubKey.ElGamal
                      Crypto.ECC.Simple.Types
                      Crypto.ECC.Simple.Prim
+                     Crypto.Internal.Builder
                      Crypto.Internal.ByteArray
                      Crypto.Internal.Compat
                      Crypto.Internal.CompatPrim
@@ -242,7 +245,7 @@
                      Crypto.Internal.Nat
                      Crypto.Internal.Words
                      Crypto.Internal.WordArray
-  if impl(ghc < 8.0)
+  if impl(ghc < 8.8)
     Buildable: False
   else
     Build-depends:   base
@@ -428,6 +431,7 @@
                      KAT_DES
                      KAT_Ed25519
                      KAT_Ed448
+                     KAT_EdDSA
                      KAT_CMAC
                      KAT_HKDF
                      KAT_HMAC
diff --git a/tests/Hash.hs b/tests/Hash.hs
--- a/tests/Hash.hs
+++ b/tests/Hash.hs
@@ -221,6 +221,24 @@
 runhashinc (HashAlg hashAlg) v = B.convertToBase B.Base16 $ hashinc $ v
   where hashinc = hashFinalize . foldl hashUpdate (hashInitWith hashAlg)
 
+data HashPrefixAlg = forall alg . HashAlgorithmPrefix alg => HashPrefixAlg alg
+
+expectedPrefix :: [ (String, HashPrefixAlg) ]
+expectedPrefix =
+    [ ("MD5", HashPrefixAlg MD5)
+    , ("SHA1", HashPrefixAlg SHA1)
+    , ("SHA224", HashPrefixAlg SHA224)
+    , ("SHA256", HashPrefixAlg SHA256)
+    , ("SHA384", HashPrefixAlg SHA384)
+    , ("SHA512", HashPrefixAlg SHA512)
+    ]
+
+runhashpfx :: HashPrefixAlg -> ByteString -> ByteString
+runhashpfx (HashPrefixAlg hashAlg) v = B.convertToBase B.Base16 $ hashWith hashAlg v
+
+runhashpfxpfx :: HashPrefixAlg -> ByteString -> Int -> ByteString
+runhashpfxpfx (HashPrefixAlg hashAlg) v len = B.convertToBase B.Base16 $ hashPrefixWith hashAlg v len
+
 makeTestAlg (name, hashAlg, results) =
     testGroup name $ concatMap maketest (zip3 is vectors results)
   where
@@ -236,6 +254,19 @@
         runhash hashAlg inp `propertyEq` runhashinc hashAlg (chunkS ckLen inp)
     ]
 
+makeTestPrefix (hashName, hashAlg) =
+    [ testProperty hashName $ \(ArbitraryBS0_2901 inp) (Int0_2901 len) ->
+        runhashpfx hashAlg (B.take len inp) `propertyEq` runhashpfxpfx hashAlg inp len
+    ]
+
+makeTestHybrid (hashName, HashPrefixAlg alg) =
+    [ testProperty hashName $ \(ArbitraryBS0_2901 start) (ArbitraryBS0_2901 end) -> do
+        len <- choose (0, B.length end)
+        let ref = hashWith alg (start `B.append` B.take len end)
+            hyb = hashFinalizePrefix (hashUpdate (hashInitWith alg) start) end len
+        return (ref `propertyEq` hyb)
+    ]
+
 -- SHAKE128 truncation example with expected byte at final position
 -- <https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/ShakeTruncation.pdf>
 shake128TruncationBytes = [0x01, 0x03, 0x07, 0x0f, 0x0f, 0x2f, 0x6f, 0x6f]
@@ -253,6 +284,8 @@
 tests = testGroup "hash"
     [ testGroup "KATs" (map makeTestAlg expected)
     , testGroup "Chunking" (concatMap makeTestChunk expected)
+    , testGroup "Prefix" (concatMap makeTestPrefix expectedPrefix)
+    , testGroup "Hybrid" (concatMap makeTestHybrid expectedPrefix)
     , testGroup "Truncating"
         [ testGroup "SHAKE128"
             (zipWith makeTestSHAKE128Truncation [1..] shake128TruncationBytes)
diff --git a/tests/KAT_EdDSA.hs b/tests/KAT_EdDSA.hs
new file mode 100644
--- /dev/null
+++ b/tests/KAT_EdDSA.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+module KAT_EdDSA ( tests ) where
+
+import           Crypto.Error
+import           Crypto.ECC
+import           Crypto.Hash.Algorithms
+import           Crypto.Hash.IO
+import qualified Crypto.PubKey.EdDSA as EdDSA
+import           Imports
+
+data Vec = forall curve hash .
+           ( EdDSA.EllipticCurveEdDSA curve
+           , HashAlgorithm hash
+           , HashDigestSize hash ~ EdDSA.CurveDigestSize curve
+           ) => Vec
+    { vecPrx :: Maybe curve
+    , vecAlg :: hash
+    , vecSec :: ByteString
+    , vecPub :: ByteString
+    , vecMsg :: ByteString
+    , vecSig :: ByteString
+    }
+
+vectors =
+    [ Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = SHA512
+        , vecSec = "\x9d\x61\xb1\x9d\xef\xfd\x5a\x60\xba\x84\x4a\xf4\x92\xec\x2c\xc4\x44\x49\xc5\x69\x7b\x32\x69\x19\x70\x3b\xac\x03\x1c\xae\x7f\x60"
+        , vecPub = "\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a"
+        , vecMsg = ""
+        , vecSig = "\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = SHA512
+        , vecSec = "\x4c\xcd\x08\x9b\x28\xff\x96\xda\x9d\xb6\xc3\x46\xec\x11\x4e\x0f\x5b\x8a\x31\x9f\x35\xab\xa6\x24\xda\x8c\xf6\xed\x4f\xb8\xa6\xfb"
+        , vecPub = "\x3d\x40\x17\xc3\xe8\x43\x89\x5a\x92\xb7\x0a\xa7\x4d\x1b\x7e\xbc\x9c\x98\x2c\xcf\x2e\xc4\x96\x8c\xc0\xcd\x55\xf1\x2a\xf4\x66\x0c"
+        , vecMsg = "\x72"
+        , vecSig = "\x92\xa0\x09\xa9\xf0\xd4\xca\xb8\x72\x0e\x82\x0b\x5f\x64\x25\x40\xa2\xb2\x7b\x54\x16\x50\x3f\x8f\xb3\x76\x22\x23\xeb\xdb\x69\xda\x08\x5a\xc1\xe4\x3e\x15\x99\x6e\x45\x8f\x36\x13\xd0\xf1\x1d\x8c\x38\x7b\x2e\xae\xb4\x30\x2a\xee\xb0\x0d\x29\x16\x12\xbb\x0c\x00"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = SHA512
+        , vecSec = "\xc5\xaa\x8d\xf4\x3f\x9f\x83\x7b\xed\xb7\x44\x2f\x31\xdc\xb7\xb1\x66\xd3\x85\x35\x07\x6f\x09\x4b\x85\xce\x3a\x2e\x0b\x44\x58\xf7"
+        , vecPub = "\xfc\x51\xcd\x8e\x62\x18\xa1\xa3\x8d\xa4\x7e\xd0\x02\x30\xf0\x58\x08\x16\xed\x13\xba\x33\x03\xac\x5d\xeb\x91\x15\x48\x90\x80\x25"
+        , vecMsg = "\xaf\x82"
+        , vecSig = "\x62\x91\xd6\x57\xde\xec\x24\x02\x48\x27\xe6\x9c\x3a\xbe\x01\xa3\x0c\xe5\x48\xa2\x84\x74\x3a\x44\x5e\x36\x80\xd7\xdb\x5a\xc3\xac\x18\xff\x9b\x53\x8d\x16\xf2\x90\xae\x67\xf7\x60\x98\x4d\xc6\x59\x4a\x7c\x15\xe9\x71\x6e\xd2\x8d\xc0\x27\xbe\xce\xea\x1e\xc4\x0a"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = SHA512
+        , vecSec = "\xf5\xe5\x76\x7c\xf1\x53\x31\x95\x17\x63\x0f\x22\x68\x76\xb8\x6c\x81\x60\xcc\x58\x3b\xc0\x13\x74\x4c\x6b\xf2\x55\xf5\xcc\x0e\xe5"
+        , vecPub = "\x27\x81\x17\xfc\x14\x4c\x72\x34\x0f\x67\xd0\xf2\x31\x6e\x83\x86\xce\xff\xbf\x2b\x24\x28\xc9\xc5\x1f\xef\x7c\x59\x7f\x1d\x42\x6e"
+        , vecMsg = "\x08\xb8\xb2\xb7\x33\x42\x42\x43\x76\x0f\xe4\x26\xa4\xb5\x49\x08\x63\x21\x10\xa6\x6c\x2f\x65\x91\xea\xbd\x33\x45\xe3\xe4\xeb\x98\xfa\x6e\x26\x4b\xf0\x9e\xfe\x12\xee\x50\xf8\xf5\x4e\x9f\x77\xb1\xe3\x55\xf6\xc5\x05\x44\xe2\x3f\xb1\x43\x3d\xdf\x73\xbe\x84\xd8\x79\xde\x7c\x00\x46\xdc\x49\x96\xd9\xe7\x73\xf4\xbc\x9e\xfe\x57\x38\x82\x9a\xdb\x26\xc8\x1b\x37\xc9\x3a\x1b\x27\x0b\x20\x32\x9d\x65\x86\x75\xfc\x6e\xa5\x34\xe0\x81\x0a\x44\x32\x82\x6b\xf5\x8c\x94\x1e\xfb\x65\xd5\x7a\x33\x8b\xbd\x2e\x26\x64\x0f\x89\xff\xbc\x1a\x85\x8e\xfc\xb8\x55\x0e\xe3\xa5\xe1\x99\x8b\xd1\x77\xe9\x3a\x73\x63\xc3\x44\xfe\x6b\x19\x9e\xe5\xd0\x2e\x82\xd5\x22\xc4\xfe\xba\x15\x45\x2f\x80\x28\x8a\x82\x1a\x57\x91\x16\xec\x6d\xad\x2b\x3b\x31\x0d\xa9\x03\x40\x1a\xa6\x21\x00\xab\x5d\x1a\x36\x55\x3e\x06\x20\x3b\x33\x89\x0c\xc9\xb8\x32\xf7\x9e\xf8\x05\x60\xcc\xb9\xa3\x9c\xe7\x67\x96\x7e\xd6\x28\xc6\xad\x57\x3c\xb1\x16\xdb\xef\xef\xd7\x54\x99\xda\x96\xbd\x68\xa8\xa9\x7b\x92\x8a\x8b\xbc\x10\x3b\x66\x21\xfc\xde\x2b\xec\xa1\x23\x1d\x20\x6b\xe6\xcd\x9e\xc7\xaf\xf6\xf6\xc9\x4f\xcd\x72\x04\xed\x34\x55\xc6\x8c\x83\xf4\xa4\x1d\xa4\xaf\x2b\x74\xef\x5c\x53\xf1\xd8\xac\x70\xbd\xcb\x7e\xd1\x85\xce\x81\xbd\x84\x35\x9d\x44\x25\x4d\x95\x62\x9e\x98\x55\xa9\x4a\x7c\x19\x58\xd1\xf8\xad\xa5\xd0\x53\x2e\xd8\xa5\xaa\x3f\xb2\xd1\x7b\xa7\x0e\xb6\x24\x8e\x59\x4e\x1a\x22\x97\xac\xbb\xb3\x9d\x50\x2f\x1a\x8c\x6e\xb6\xf1\xce\x22\xb3\xde\x1a\x1f\x40\xcc\x24\x55\x41\x19\xa8\x31\xa9\xaa\xd6\x07\x9c\xad\x88\x42\x5d\xe6\xbd\xe1\xa9\x18\x7e\xbb\x60\x92\xcf\x67\xbf\x2b\x13\xfd\x65\xf2\x70\x88\xd7\x8b\x7e\x88\x3c\x87\x59\xd2\xc4\xf5\xc6\x5a\xdb\x75\x53\x87\x8a\xd5\x75\xf9\xfa\xd8\x78\xe8\x0a\x0c\x9b\xa6\x3b\xcb\xcc\x27\x32\xe6\x94\x85\xbb\xc9\xc9\x0b\xfb\xd6\x24\x81\xd9\x08\x9b\xec\xcf\x80\xcf\xe2\xdf\x16\xa2\xcf\x65\xbd\x92\xdd\x59\x7b\x07\x07\xe0\x91\x7a\xf4\x8b\xbb\x75\xfe\xd4\x13\xd2\x38\xf5\x55\x5a\x7a\x56\x9d\x80\xc3\x41\x4a\x8d\x08\x59\xdc\x65\xa4\x61\x28\xba\xb2\x7a\xf8\x7a\x71\x31\x4f\x31\x8c\x78\x2b\x23\xeb\xfe\x80\x8b\x82\xb0\xce\x26\x40\x1d\x2e\x22\xf0\x4d\x83\xd1\x25\x5d\xc5\x1a\xdd\xd3\xb7\x5a\x2b\x1a\xe0\x78\x45\x04\xdf\x54\x3a\xf8\x96\x9b\xe3\xea\x70\x82\xff\x7f\xc9\x88\x8c\x14\x4d\xa2\xaf\x58\x42\x9e\xc9\x60\x31\xdb\xca\xd3\xda\xd9\xaf\x0d\xcb\xaa\xaf\x26\x8c\xb8\xfc\xff\xea\xd9\x4f\x3c\x7c\xa4\x95\xe0\x56\xa9\xb4\x7a\xcd\xb7\x51\xfb\x73\xe6\x66\xc6\xc6\x55\xad\xe8\x29\x72\x97\xd0\x7a\xd1\xba\x5e\x43\xf1\xbc\xa3\x23\x01\x65\x13\x39\xe2\x29\x04\xcc\x8c\x42\xf5\x8c\x30\xc0\x4a\xaf\xdb\x03\x8d\xda\x08\x47\xdd\x98\x8d\xcd\xa6\xf3\xbf\xd1\x5c\x4b\x4c\x45\x25\x00\x4a\xa0\x6e\xef\xf8\xca\x61\x78\x3a\xac\xec\x57\xfb\x3d\x1f\x92\xb0\xfe\x2f\xd1\xa8\x5f\x67\x24\x51\x7b\x65\xe6\x14\xad\x68\x08\xd6\xf6\xee\x34\xdf\xf7\x31\x0f\xdc\x82\xae\xbf\xd9\x04\xb0\x1e\x1d\xc5\x4b\x29\x27\x09\x4b\x2d\xb6\x8d\x6f\x90\x3b\x68\x40\x1a\xde\xbf\x5a\x7e\x08\xd7\x8f\xf4\xef\x5d\x63\x65\x3a\x65\x04\x0c\xf9\xbf\xd4\xac\xa7\x98\x4a\x74\xd3\x71\x45\x98\x67\x80\xfc\x0b\x16\xac\x45\x16\x49\xde\x61\x88\xa7\xdb\xdf\x19\x1f\x64\xb5\xfc\x5e\x2a\xb4\x7b\x57\xf7\xf7\x27\x6c\xd4\x19\xc1\x7a\x3c\xa8\xe1\xb9\x39\xae\x49\xe4\x88\xac\xba\x6b\x96\x56\x10\xb5\x48\x01\x09\xc8\xb1\x7b\x80\xe1\xb7\xb7\x50\xdf\xc7\x59\x8d\x5d\x50\x11\xfd\x2d\xcc\x56\x00\xa3\x2e\xf5\xb5\x2a\x1e\xcc\x82\x0e\x30\x8a\xa3\x42\x72\x1a\xac\x09\x43\xbf\x66\x86\xb6\x4b\x25\x79\x37\x65\x04\xcc\xc4\x93\xd9\x7e\x6a\xed\x3f\xb0\xf9\xcd\x71\xa4\x3d\xd4\x97\xf0\x1f\x17\xc0\xe2\xcb\x37\x97\xaa\x2a\x2f\x25\x66\x56\x16\x8e\x6c\x49\x6a\xfc\x5f\xb9\x32\x46\xf6\xb1\x11\x63\x98\xa3\x46\xf1\xa6\x41\xf3\xb0\x41\xe9\x89\xf7\x91\x4f\x90\xcc\x2c\x7f\xff\x35\x78\x76\xe5\x06\xb5\x0d\x33\x4b\xa7\x7c\x22\x5b\xc3\x07\xba\x53\x71\x52\xf3\xf1\x61\x0e\x4e\xaf\xe5\x95\xf6\xd9\xd9\x0d\x11\xfa\xa9\x33\xa1\x5e\xf1\x36\x95\x46\x86\x8a\x7f\x3a\x45\xa9\x67\x68\xd4\x0f\xd9\xd0\x34\x12\xc0\x91\xc6\x31\x5c\xf4\xfd\xe7\xcb\x68\x60\x69\x37\x38\x0d\xb2\xea\xaa\x70\x7b\x4c\x41\x85\xc3\x2e\xdd\xcd\xd3\x06\x70\x5e\x4d\xc1\xff\xc8\x72\xee\xee\x47\x5a\x64\xdf\xac\x86\xab\xa4\x1c\x06\x18\x98\x3f\x87\x41\xc5\xef\x68\xd3\xa1\x01\xe8\xa3\xb8\xca\xc6\x0c\x90\x5c\x15\xfc\x91\x08\x40\xb9\x4c\x00\xa0\xb9\xd0"
+        , vecSig = "\x0a\xab\x4c\x90\x05\x01\xb3\xe2\x4d\x7c\xdf\x46\x63\x32\x6a\x3a\x87\xdf\x5e\x48\x43\xb2\xcb\xdb\x67\xcb\xf6\xe4\x60\xfe\xc3\x50\xaa\x53\x71\xb1\x50\x8f\x9f\x45\x28\xec\xea\x23\xc4\x36\xd9\x4b\x5e\x8f\xcd\x4f\x68\x1e\x30\xa6\xac\x00\xa9\x70\x4a\x18\x8a\x03"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = SHA512
+        , vecSec = "\x83\x3f\xe6\x24\x09\x23\x7b\x9d\x62\xec\x77\x58\x75\x20\x91\x1e\x9a\x75\x9c\xec\x1d\x19\x75\x5b\x7d\xa9\x01\xb9\x6d\xca\x3d\x42"
+        , vecPub = "\xec\x17\x2b\x93\xad\x5e\x56\x3b\xf4\x93\x2c\x70\xe1\x24\x50\x34\xc3\x54\x67\xef\x2e\xfd\x4d\x64\xeb\xf8\x19\x68\x34\x67\xe2\xbf"
+        , vecMsg = "\xdd\xaf\x35\xa1\x93\x61\x7a\xba\xcc\x41\x73\x49\xae\x20\x41\x31\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a\x21\x92\x99\x2a\x27\x4f\xc1\xa8\x36\xba\x3c\x23\xa3\xfe\xeb\xbd\x45\x4d\x44\x23\x64\x3c\xe8\x0e\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f"
+        , vecSig = "\xdc\x2a\x44\x59\xe7\x36\x96\x33\xa5\x2b\x1b\xf2\x77\x83\x9a\x00\x20\x10\x09\xa3\xef\xbf\x3e\xcb\x69\xbe\xa2\x18\x6c\x26\xb5\x89\x09\x35\x1f\xc9\xac\x90\xb3\xec\xfd\xfb\xc7\xc6\x64\x31\xe0\x30\x3d\xca\x17\x9c\x13\x8a\xc1\x7a\xd9\xbe\xf1\x17\x73\x31\xa7\x04"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = Blake2b_512
+        , vecSec = "\x9d\x61\xb1\x9d\xef\xfd\x5a\x60\xba\x84\x4a\xf4\x92\xec\x2c\xc4\x44\x49\xc5\x69\x7b\x32\x69\x19\x70\x3b\xac\x03\x1c\xae\x7f\x60"
+        , vecPub = "\x78\xe6\x5b\xf3\x0f\x89\x3d\x32\xfc\x57\xef\x05\x1c\x34\x1b\xde\xde\x24\x25\x44\xfc\x2a\x21\x12\xf0\xfa\x2c\x7a\xfd\xeb\xc0\x2f"
+        , vecMsg = ""
+        , vecSig = "\x99\xa5\x23\xbd\x46\x16\xc8\x16\x11\x44\xd6\xa9\x9d\x3c\x32\x40\x0c\xb4\xa3\x26\xf4\xd7\x9e\x30\x73\x40\xf6\xaf\xa1\x17\x50\xa0\x08\x5d\x7d\x84\x62\x6b\xc9\xe4\xb1\x53\xfc\x0e\x39\x6d\x15\xce\x44\xc3\x9b\xae\x45\x33\x80\x4d\xb1\xfe\x5b\x52\xf2\xb1\xb8\x05"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = Blake2b_512
+        , vecSec = "\x4c\xcd\x08\x9b\x28\xff\x96\xda\x9d\xb6\xc3\x46\xec\x11\x4e\x0f\x5b\x8a\x31\x9f\x35\xab\xa6\x24\xda\x8c\xf6\xed\x4f\xb8\xa6\xfb"
+        , vecPub = "\x5e\x71\x39\x2d\x91\xe6\xa5\x8f\xed\xeb\x08\x50\x36\x4f\x56\xcd\x15\x8a\x60\x44\x75\x57\xd7\x89\x03\x89\xc9\xb3\xd4\x57\x6d\x4d"
+        , vecMsg = "\x72"
+        , vecSig = "\x6d\xa7\x5e\x15\xb5\x70\x7f\x4d\xe5\xa1\x53\xc4\x8a\x5d\x83\x9f\xb8\x50\x74\xc3\x8a\xeb\x62\x85\x97\x7f\x03\xa1\x39\x77\x59\x7f\x97\x60\x69\xfd\xb9\x03\xf1\x83\x47\x4a\xaa\x5e\xd0\xcf\xe8\x78\xba\x8e\xf8\x68\xc5\xe4\x7c\xa3\xf9\x6c\xcf\xb3\xa8\x9b\x2a\x06"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = Blake2b_512
+        , vecSec = "\xc5\xaa\x8d\xf4\x3f\x9f\x83\x7b\xed\xb7\x44\x2f\x31\xdc\xb7\xb1\x66\xd3\x85\x35\x07\x6f\x09\x4b\x85\xce\x3a\x2e\x0b\x44\x58\xf7"
+        , vecPub = "\x8d\x53\xca\x70\xf0\xea\xb2\x3b\x91\x78\x34\x57\x85\xfc\xdb\x69\xed\x67\x23\xf8\x14\x8f\x7e\x33\x9e\x88\x65\x37\x00\xb7\x18\xda"
+        , vecMsg = "\xaf\x82"
+        , vecSig = "\x7c\xc3\xc1\x38\x52\xbd\x12\xab\xf3\xce\x4c\xa8\xca\x28\x36\xcb\xf8\x6d\xa9\x6c\x46\x34\xc5\x0d\xf3\xfb\x80\xdc\x80\x9e\x29\xdb\x0e\x10\x9c\x36\x13\x53\x40\x7c\x12\x36\xa9\x04\xf6\x36\x86\x8a\xa3\x39\x77\xa9\x9d\x3f\x84\x45\x98\xdb\x15\x38\xb4\x29\x52\x03"
+        }
+    , Vec
+        { vecPrx = Just Curve_Edwards25519
+        , vecAlg = Blake2b_512
+        , vecSec = "\xf5\xe5\x76\x7c\xf1\x53\x31\x95\x17\x63\x0f\x22\x68\x76\xb8\x6c\x81\x60\xcc\x58\x3b\xc0\x13\x74\x4c\x6b\xf2\x55\xf5\xcc\x0e\xe5"
+        , vecPub = "\x9e\x3c\xa4\x9b\xb2\xd9\xe3\x6b\x8f\x0c\x94\x4a\x7b\x1c\x29\x26\x45\xda\x87\xce\x6f\xa6\xb4\x28\x86\xe5\xd7\xc8\x68\x33\xa7\x14"
+        , vecMsg = "\x08\xb8\xb2\xb7\x33\x42\x42\x43\x76\x0f\xe4\x26\xa4\xb5\x49\x08\x63\x21\x10\xa6\x6c\x2f\x65\x91\xea\xbd\x33\x45\xe3\xe4\xeb\x98\xfa\x6e\x26\x4b\xf0\x9e\xfe\x12\xee\x50\xf8\xf5\x4e\x9f\x77\xb1\xe3\x55\xf6\xc5\x05\x44\xe2\x3f\xb1\x43\x3d\xdf\x73\xbe\x84\xd8\x79\xde\x7c\x00\x46\xdc\x49\x96\xd9\xe7\x73\xf4\xbc\x9e\xfe\x57\x38\x82\x9a\xdb\x26\xc8\x1b\x37\xc9\x3a\x1b\x27\x0b\x20\x32\x9d\x65\x86\x75\xfc\x6e\xa5\x34\xe0\x81\x0a\x44\x32\x82\x6b\xf5\x8c\x94\x1e\xfb\x65\xd5\x7a\x33\x8b\xbd\x2e\x26\x64\x0f\x89\xff\xbc\x1a\x85\x8e\xfc\xb8\x55\x0e\xe3\xa5\xe1\x99\x8b\xd1\x77\xe9\x3a\x73\x63\xc3\x44\xfe\x6b\x19\x9e\xe5\xd0\x2e\x82\xd5\x22\xc4\xfe\xba\x15\x45\x2f\x80\x28\x8a\x82\x1a\x57\x91\x16\xec\x6d\xad\x2b\x3b\x31\x0d\xa9\x03\x40\x1a\xa6\x21\x00\xab\x5d\x1a\x36\x55\x3e\x06\x20\x3b\x33\x89\x0c\xc9\xb8\x32\xf7\x9e\xf8\x05\x60\xcc\xb9\xa3\x9c\xe7\x67\x96\x7e\xd6\x28\xc6\xad\x57\x3c\xb1\x16\xdb\xef\xef\xd7\x54\x99\xda\x96\xbd\x68\xa8\xa9\x7b\x92\x8a\x8b\xbc\x10\x3b\x66\x21\xfc\xde\x2b\xec\xa1\x23\x1d\x20\x6b\xe6\xcd\x9e\xc7\xaf\xf6\xf6\xc9\x4f\xcd\x72\x04\xed\x34\x55\xc6\x8c\x83\xf4\xa4\x1d\xa4\xaf\x2b\x74\xef\x5c\x53\xf1\xd8\xac\x70\xbd\xcb\x7e\xd1\x85\xce\x81\xbd\x84\x35\x9d\x44\x25\x4d\x95\x62\x9e\x98\x55\xa9\x4a\x7c\x19\x58\xd1\xf8\xad\xa5\xd0\x53\x2e\xd8\xa5\xaa\x3f\xb2\xd1\x7b\xa7\x0e\xb6\x24\x8e\x59\x4e\x1a\x22\x97\xac\xbb\xb3\x9d\x50\x2f\x1a\x8c\x6e\xb6\xf1\xce\x22\xb3\xde\x1a\x1f\x40\xcc\x24\x55\x41\x19\xa8\x31\xa9\xaa\xd6\x07\x9c\xad\x88\x42\x5d\xe6\xbd\xe1\xa9\x18\x7e\xbb\x60\x92\xcf\x67\xbf\x2b\x13\xfd\x65\xf2\x70\x88\xd7\x8b\x7e\x88\x3c\x87\x59\xd2\xc4\xf5\xc6\x5a\xdb\x75\x53\x87\x8a\xd5\x75\xf9\xfa\xd8\x78\xe8\x0a\x0c\x9b\xa6\x3b\xcb\xcc\x27\x32\xe6\x94\x85\xbb\xc9\xc9\x0b\xfb\xd6\x24\x81\xd9\x08\x9b\xec\xcf\x80\xcf\xe2\xdf\x16\xa2\xcf\x65\xbd\x92\xdd\x59\x7b\x07\x07\xe0\x91\x7a\xf4\x8b\xbb\x75\xfe\xd4\x13\xd2\x38\xf5\x55\x5a\x7a\x56\x9d\x80\xc3\x41\x4a\x8d\x08\x59\xdc\x65\xa4\x61\x28\xba\xb2\x7a\xf8\x7a\x71\x31\x4f\x31\x8c\x78\x2b\x23\xeb\xfe\x80\x8b\x82\xb0\xce\x26\x40\x1d\x2e\x22\xf0\x4d\x83\xd1\x25\x5d\xc5\x1a\xdd\xd3\xb7\x5a\x2b\x1a\xe0\x78\x45\x04\xdf\x54\x3a\xf8\x96\x9b\xe3\xea\x70\x82\xff\x7f\xc9\x88\x8c\x14\x4d\xa2\xaf\x58\x42\x9e\xc9\x60\x31\xdb\xca\xd3\xda\xd9\xaf\x0d\xcb\xaa\xaf\x26\x8c\xb8\xfc\xff\xea\xd9\x4f\x3c\x7c\xa4\x95\xe0\x56\xa9\xb4\x7a\xcd\xb7\x51\xfb\x73\xe6\x66\xc6\xc6\x55\xad\xe8\x29\x72\x97\xd0\x7a\xd1\xba\x5e\x43\xf1\xbc\xa3\x23\x01\x65\x13\x39\xe2\x29\x04\xcc\x8c\x42\xf5\x8c\x30\xc0\x4a\xaf\xdb\x03\x8d\xda\x08\x47\xdd\x98\x8d\xcd\xa6\xf3\xbf\xd1\x5c\x4b\x4c\x45\x25\x00\x4a\xa0\x6e\xef\xf8\xca\x61\x78\x3a\xac\xec\x57\xfb\x3d\x1f\x92\xb0\xfe\x2f\xd1\xa8\x5f\x67\x24\x51\x7b\x65\xe6\x14\xad\x68\x08\xd6\xf6\xee\x34\xdf\xf7\x31\x0f\xdc\x82\xae\xbf\xd9\x04\xb0\x1e\x1d\xc5\x4b\x29\x27\x09\x4b\x2d\xb6\x8d\x6f\x90\x3b\x68\x40\x1a\xde\xbf\x5a\x7e\x08\xd7\x8f\xf4\xef\x5d\x63\x65\x3a\x65\x04\x0c\xf9\xbf\xd4\xac\xa7\x98\x4a\x74\xd3\x71\x45\x98\x67\x80\xfc\x0b\x16\xac\x45\x16\x49\xde\x61\x88\xa7\xdb\xdf\x19\x1f\x64\xb5\xfc\x5e\x2a\xb4\x7b\x57\xf7\xf7\x27\x6c\xd4\x19\xc1\x7a\x3c\xa8\xe1\xb9\x39\xae\x49\xe4\x88\xac\xba\x6b\x96\x56\x10\xb5\x48\x01\x09\xc8\xb1\x7b\x80\xe1\xb7\xb7\x50\xdf\xc7\x59\x8d\x5d\x50\x11\xfd\x2d\xcc\x56\x00\xa3\x2e\xf5\xb5\x2a\x1e\xcc\x82\x0e\x30\x8a\xa3\x42\x72\x1a\xac\x09\x43\xbf\x66\x86\xb6\x4b\x25\x79\x37\x65\x04\xcc\xc4\x93\xd9\x7e\x6a\xed\x3f\xb0\xf9\xcd\x71\xa4\x3d\xd4\x97\xf0\x1f\x17\xc0\xe2\xcb\x37\x97\xaa\x2a\x2f\x25\x66\x56\x16\x8e\x6c\x49\x6a\xfc\x5f\xb9\x32\x46\xf6\xb1\x11\x63\x98\xa3\x46\xf1\xa6\x41\xf3\xb0\x41\xe9\x89\xf7\x91\x4f\x90\xcc\x2c\x7f\xff\x35\x78\x76\xe5\x06\xb5\x0d\x33\x4b\xa7\x7c\x22\x5b\xc3\x07\xba\x53\x71\x52\xf3\xf1\x61\x0e\x4e\xaf\xe5\x95\xf6\xd9\xd9\x0d\x11\xfa\xa9\x33\xa1\x5e\xf1\x36\x95\x46\x86\x8a\x7f\x3a\x45\xa9\x67\x68\xd4\x0f\xd9\xd0\x34\x12\xc0\x91\xc6\x31\x5c\xf4\xfd\xe7\xcb\x68\x60\x69\x37\x38\x0d\xb2\xea\xaa\x70\x7b\x4c\x41\x85\xc3\x2e\xdd\xcd\xd3\x06\x70\x5e\x4d\xc1\xff\xc8\x72\xee\xee\x47\x5a\x64\xdf\xac\x86\xab\xa4\x1c\x06\x18\x98\x3f\x87\x41\xc5\xef\x68\xd3\xa1\x01\xe8\xa3\xb8\xca\xc6\x0c\x90\x5c\x15\xfc\x91\x08\x40\xb9\x4c\x00\xa0\xb9\xd0"
+        , vecSig = "\xd0\x39\x65\xac\x31\x6a\x20\xf5\xa4\x7a\xb2\xd6\x18\x5e\xb3\xf0\xae\xea\x9c\x2e\xb8\xab\xe9\x22\xe9\x6d\x31\x7b\x3b\xd0\xef\x02\xe8\xd4\x7f\xd9\x23\x84\xe2\x86\x15\xeb\x33\x14\xad\xbc\x71\xc4\x67\x59\x96\x09\x9e\x48\x4c\xeb\x16\x28\x47\xc4\x0c\x32\x44\x0e"
+        }
+    ]
+
+
+doPublicKeyTest :: Int -> Vec -> TestTree
+doPublicKeyTest i Vec{..} =
+    testCase (show i) (pub @=? EdDSA.toPublic vecPrx vecAlg sec)
+  where
+    !pub = throwCryptoError $ EdDSA.publicKey vecPrx vecAlg vecPub
+    !sec = throwCryptoError $ EdDSA.secretKey vecPrx vecSec
+
+doSignatureTest :: Int -> Vec -> TestTree
+doSignatureTest i Vec{..} =
+    testCase (show i) (sig @=? EdDSA.sign vecPrx sec pub vecMsg)
+  where
+    !sig = throwCryptoError $ EdDSA.signature vecPrx vecAlg vecSig
+    !pub = throwCryptoError $ EdDSA.publicKey vecPrx vecAlg vecPub
+    !sec = throwCryptoError $ EdDSA.secretKey vecPrx vecSec
+
+doVerifyTest :: Int -> Vec -> TestTree
+doVerifyTest i Vec{..} =
+    testCase (show i) (True @=? EdDSA.verify vecPrx pub vecMsg sig)
+  where
+    !sig = throwCryptoError $ EdDSA.signature vecPrx vecAlg vecSig
+    !pub = throwCryptoError $ EdDSA.publicKey vecPrx vecAlg vecPub
+
+
+tests = testGroup "EdDSA"
+    [ testGroup "gen publickey" $ zipWith doPublicKeyTest [katZero..] vectors
+    , testGroup "gen signature" $ zipWith doSignatureTest [katZero..] vectors
+    , testGroup "verify sig" $ zipWith doVerifyTest [katZero..] vectors
+    ]
diff --git a/tests/Tests.hs b/tests/Tests.hs
--- a/tests/Tests.hs
+++ b/tests/Tests.hs
@@ -29,6 +29,7 @@
 import qualified KAT_Curve448
 import qualified KAT_Ed25519
 import qualified KAT_Ed448
+import qualified KAT_EdDSA
 import qualified KAT_OTP
 import qualified KAT_PubKey
 import qualified KAT_Scrypt
@@ -67,6 +68,7 @@
     , KAT_Curve448.tests
     , KAT_Ed25519.tests
     , KAT_Ed448.tests
+    , KAT_EdDSA.tests
     , KAT_PubKey.tests
     , KAT_OTP.tests
     , testGroup "KDF"
