diff --git a/Crypto/Hash/Blake2.hs b/Crypto/Hash/Blake2.hs index 82a3f44c..aa0ca81d 100644 --- a/Crypto/Hash/Blake2.hs +++ b/Crypto/Hash/Blake2.hs @@ -33,7 +33,8 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} module Crypto.Hash.Blake2 - ( Blake2s(..) + ( HashBlake2(..) + , Blake2s(..) , Blake2sp(..) , Blake2b(..) , Blake2bp(..) @@ -46,6 +47,13 @@ import Data.Word (Word8, Word32) import GHC.TypeLits (Nat, KnownNat) import Crypto.Internal.Nat +-- | Typeclass for the Blake2 family of digest functions. +class HashAlgorithm a => HashBlake2 a where + + -- | Init Blake2 algorithm with the specified key of the specified length. + -- The key length is specified in bytes. + blake2InternalKeyedInit :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO () + -- | Fast and secure alternative to SHA1 and HMAC-SHA1 -- -- It is espacially known to target 32bits architectures. @@ -72,8 +80,16 @@ instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost b hashInternalUpdate = c_blake2s_update hashInternalFinalize p = c_blake2s_finalize p (integralNatVal (Proxy :: Proxy bitlen)) +instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 256) + => HashBlake2 (Blake2s bitlen) + where + blake2InternalKeyedInit p = c_blake2s_init_key p outLen + where outLen = integralNatVal (Proxy :: Proxy bitlen) + foreign import ccall unsafe "crypton_blake2s_init" c_blake2s_init :: Ptr (Context a) -> Word32 -> IO () +foreign import ccall unsafe "crypton_blake2s_init_key" + c_blake2s_init_key :: Ptr (Context a) -> Word32 -> Ptr Word8 -> Word32 -> IO () foreign import ccall "crypton_blake2s_update" c_blake2s_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO () foreign import ccall unsafe "crypton_blake2s_finalize" @@ -107,8 +123,16 @@ instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost b hashInternalUpdate = c_blake2b_update hashInternalFinalize p = c_blake2b_finalize p (integralNatVal (Proxy :: Proxy bitlen)) +instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 512) + => HashBlake2 (Blake2b bitlen) + where + blake2InternalKeyedInit p = c_blake2b_init_key p outLen + where outLen = integralNatVal (Proxy :: Proxy bitlen) + foreign import ccall unsafe "crypton_blake2b_init" c_blake2b_init :: Ptr (Context a) -> Word32 -> IO () +foreign import ccall unsafe "crypton_blake2b_init_key" + c_blake2b_init_key :: Ptr (Context a) -> Word32 -> Ptr Word8 -> Word32 -> IO () foreign import ccall "crypton_blake2b_update" c_blake2b_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO () foreign import ccall unsafe "crypton_blake2b_finalize" @@ -130,8 +154,16 @@ instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost b hashInternalUpdate = c_blake2sp_update hashInternalFinalize p = c_blake2sp_finalize p (integralNatVal (Proxy :: Proxy bitlen)) +instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 256) + => HashBlake2 (Blake2sp bitlen) + where + blake2InternalKeyedInit p = c_blake2sp_init_key p outLen + where outLen = integralNatVal (Proxy :: Proxy bitlen) + foreign import ccall unsafe "crypton_blake2sp_init" c_blake2sp_init :: Ptr (Context a) -> Word32 -> IO () +foreign import ccall unsafe "crypton_blake2sp_init_key" + c_blake2sp_init_key :: Ptr (Context a) -> Word32 -> Ptr Word8 -> Word32 -> IO () foreign import ccall "crypton_blake2sp_update" c_blake2sp_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO () foreign import ccall unsafe "crypton_blake2sp_finalize" @@ -153,9 +185,16 @@ instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost b hashInternalUpdate = c_blake2bp_update hashInternalFinalize p = c_blake2bp_finalize p (integralNatVal (Proxy :: Proxy bitlen)) +instance (IsDivisibleBy8 bitlen, KnownNat bitlen, IsAtLeast bitlen 8, IsAtMost bitlen 512) + => HashBlake2 (Blake2bp bitlen) + where + blake2InternalKeyedInit p = c_blake2bp_init_key p outLen + where outLen = integralNatVal (Proxy :: Proxy bitlen) foreign import ccall unsafe "crypton_blake2bp_init" c_blake2bp_init :: Ptr (Context a) -> Word32 -> IO () +foreign import ccall unsafe "crypton_blake2bp_init_key" + c_blake2bp_init_key :: Ptr (Context a) -> Word32 -> Ptr Word8 -> Word32 -> IO () foreign import ccall "crypton_blake2bp_update" c_blake2bp_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO () foreign import ccall unsafe "crypton_blake2bp_finalize" diff --git a/Crypto/MAC/KeyedBlake2.hs b/Crypto/MAC/KeyedBlake2.hs new file mode 100644 index 00000000..2a7abab1 --- /dev/null +++ b/Crypto/MAC/KeyedBlake2.hs @@ -0,0 +1,88 @@ +-- | +-- Module : Crypto.MAC.KeyedBlake2 +-- License : BSD-style +-- Maintainer : Matthias Valvekens +-- Stability : experimental +-- Portability : unknown +-- +-- Expose a MAC interface to the keyed Blake2 algorithms +-- defined in RFC 7693. +-- +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Crypto.MAC.KeyedBlake2 + ( HashBlake2 + , KeyedBlake2(..) + , keyedBlake2 + , keyedBlake2Lazy + -- * Incremental + , Context + , initialize + , update + , updates + , finalize + ) where + +import qualified Crypto.Hash as H +import qualified Crypto.Hash.Types as H +import Crypto.Hash.Blake2 +import Crypto.Internal.DeepSeq (NFData) +import qualified Data.ByteArray as B +import Data.ByteArray (ByteArrayAccess) +import qualified Data.ByteString.Lazy as L + +import Foreign.Ptr (Ptr) + + +-- Keyed Blake2b + +-- | Represent a Blake2b MAC that is a phantom type with the hash used to produce the +-- MAC. +-- +-- The Eq instance is constant time. No Show instance is provided, to avoid +-- printing by mistake. +newtype KeyedBlake2 a = KeyedBlake2 { keyedBlake2GetDigest :: H.Digest a } + deriving (ByteArrayAccess,NFData) + +instance Eq (KeyedBlake2 a) where + KeyedBlake2 x == KeyedBlake2 y = B.constEq x y + +-- | Represent an ongoing Blake2 state, that can be appended with 'update' and +-- finalized to a 'KeyedBlake2' with 'finalize'. +newtype Context a = Context (H.Context a) + +-- | Initialize a new incremental keyed Blake2 context with the supplied key. +initialize :: forall a key . (HashBlake2 a, ByteArrayAccess key) + => key -> Context a +initialize k = Context $ H.Context $ B.allocAndFreeze ctxSz performInit + where ctxSz = H.hashInternalContextSize (undefined :: a) + digestSz = H.hashDigestSize (undefined :: a) + -- cap the number of key bytes at digestSz, + -- since that's the maximal key size + keyByteLen = min (B.length k) digestSz + performInit :: Ptr (H.Context a) -> IO () + performInit ptr = B.withByteArray k + $ \keyPtr -> blake2InternalKeyedInit ptr keyPtr (fromIntegral keyByteLen) + +-- | Incrementally update a keyed Blake2 context. +update :: (HashBlake2 a, ByteArrayAccess ba) => Context a -> ba -> Context a +update (Context ctx) = Context . H.hashUpdate ctx + +-- | Incrementally update a keyed Blake2 context with multiple inputs. +updates :: (HashBlake2 a, ByteArrayAccess ba) => Context a -> [ba] -> Context a +updates (Context ctx) = Context . H.hashUpdates ctx + +-- | Finalize a keyed Blake2 context and return the computed MAC. +finalize :: HashBlake2 a => Context a -> KeyedBlake2 a +finalize (Context ctx) = KeyedBlake2 $ H.hashFinalize ctx + +-- | Compute a Blake2 MAC using the supplied key. +keyedBlake2 :: (HashBlake2 a, ByteArrayAccess key, ByteArrayAccess ba) + => key -> ba -> KeyedBlake2 a +keyedBlake2 key msg = finalize $ update (initialize key) msg + +-- | Compute a Blake2 MAC using the supplied key, for a lazy input. +keyedBlake2Lazy :: (HashBlake2 a, ByteArrayAccess key) + => key -> L.ByteString -> KeyedBlake2 a +keyedBlake2Lazy key msg = finalize $ updates (initialize key) (L.toChunks msg) diff --git a/cbits/crypton_blake2b.c b/cbits/crypton_blake2b.c index 9c40b96c..5b80ae5b 100644 --- a/cbits/crypton_blake2b.c +++ b/cbits/crypton_blake2b.c @@ -5,6 +5,11 @@ void crypton_blake2b_init(blake2b_ctx *ctx, uint32_t hashlen) _crypton_blake2b_init(ctx, hashlen / 8); } +void crypton_blake2b_init_key(blake2b_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen) +{ + _crypton_blake2b_init_key(ctx, hashlen / 8, (const void *) key, keylen); +} + void crypton_blake2b_update(blake2b_ctx *ctx, const uint8_t *data, uint32_t len) { _crypton_blake2b_update(ctx, data, len); diff --git a/cbits/crypton_blake2b.h b/cbits/crypton_blake2b.h index a79f406b..f4918d4a 100644 --- a/cbits/crypton_blake2b.h +++ b/cbits/crypton_blake2b.h @@ -6,6 +6,7 @@ typedef blake2b_state blake2b_ctx; void crypton_blake2b_init(blake2b_ctx *ctx, uint32_t hashlen); +void crypton_blake2b_init_key(blake2b_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen); void crypton_blake2b_update(blake2b_ctx *ctx, const uint8_t *data, uint32_t len); void crypton_blake2b_finalize(blake2b_ctx *ctx, uint32_t hashlen, uint8_t *out); diff --git a/cbits/crypton_blake2bp.c b/cbits/crypton_blake2bp.c index 8d50d7f5..c999e345 100644 --- a/cbits/crypton_blake2bp.c +++ b/cbits/crypton_blake2bp.c @@ -5,6 +5,11 @@ void crypton_blake2bp_init(blake2bp_ctx *ctx, uint32_t hashlen) _crypton_blake2bp_init(ctx, hashlen / 8); } +void crypton_blake2bp_init_key(blake2bp_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen) +{ + _crypton_blake2bp_init_key(ctx, hashlen / 8, (const void *) key, keylen); +} + void crypton_blake2bp_update(blake2bp_ctx *ctx, const uint8_t *data, uint32_t len) { _crypton_blake2bp_update(ctx, data, len); diff --git a/cbits/crypton_blake2bp.h b/cbits/crypton_blake2bp.h index 19cb9e20..bbebbf6a 100644 --- a/cbits/crypton_blake2bp.h +++ b/cbits/crypton_blake2bp.h @@ -6,6 +6,7 @@ typedef blake2bp_state blake2bp_ctx; void crypton_blake2bp_init(blake2bp_ctx *ctx, uint32_t hashlen); +void crypton_blake2bp_init_key(blake2bp_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen); void crypton_blake2bp_update(blake2bp_ctx *ctx, const uint8_t *data, uint32_t len); void crypton_blake2bp_finalize(blake2bp_ctx *ctx, uint32_t hashlen, uint8_t *out); diff --git a/cbits/crypton_blake2s.c b/cbits/crypton_blake2s.c index c83c0cf3..911ca99a 100644 --- a/cbits/crypton_blake2s.c +++ b/cbits/crypton_blake2s.c @@ -5,6 +5,11 @@ void crypton_blake2s_init(blake2s_ctx *ctx, uint32_t hashlen) _crypton_blake2s_init(ctx, hashlen / 8); } +void crypton_blake2s_init_key(blake2s_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen) +{ + _crypton_blake2s_init_key(ctx, hashlen / 8, (const void *) key, keylen); +} + void crypton_blake2s_update(blake2s_ctx *ctx, const uint8_t *data, uint32_t len) { _crypton_blake2s_update(ctx, data, len); diff --git a/cbits/crypton_blake2s.h b/cbits/crypton_blake2s.h index 274cd68a..732562ce 100644 --- a/cbits/crypton_blake2s.h +++ b/cbits/crypton_blake2s.h @@ -6,6 +6,7 @@ typedef blake2s_state blake2s_ctx; void crypton_blake2s_init(blake2s_ctx *ctx, uint32_t hashlen); +void crypton_blake2s_init_key(blake2s_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen); void crypton_blake2s_update(blake2s_ctx *ctx, const uint8_t *data, uint32_t len); void crypton_blake2s_finalize(blake2s_ctx *ctx, uint32_t hashlen, uint8_t *out); diff --git a/cbits/crypton_blake2sp.c b/cbits/crypton_blake2sp.c index c62440c2..465b2d08 100644 --- a/cbits/crypton_blake2sp.c +++ b/cbits/crypton_blake2sp.c @@ -5,6 +5,11 @@ void crypton_blake2sp_init(blake2sp_ctx *ctx, uint32_t hashlen) _crypton_blake2sp_init(ctx, hashlen / 8); } +void crypton_blake2sp_init_key(blake2sp_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen) +{ + _crypton_blake2sp_init_key(ctx, hashlen / 8, (const void *) key, keylen); +} + void crypton_blake2sp_update(blake2sp_ctx *ctx, const uint8_t *data, uint32_t len) { _crypton_blake2sp_update(ctx, data, len); diff --git a/cbits/crypton_blake2sp.h b/cbits/crypton_blake2sp.h index d598be34..f3acb774 100644 --- a/cbits/crypton_blake2sp.h +++ b/cbits/crypton_blake2sp.h @@ -6,6 +6,7 @@ typedef blake2sp_state blake2sp_ctx; void crypton_blake2sp_init(blake2sp_ctx *ctx, uint32_t hashlen); +void crypton_blake2sp_init_key(blake2sp_ctx *ctx, uint32_t hashlen, const uint8_t *key, size_t keylen); void crypton_blake2sp_update(blake2sp_ctx *ctx, const uint8_t *data, uint32_t len); void crypton_blake2sp_finalize(blake2sp_ctx *ctx, uint32_t hashlen, uint8_t *out); diff --git a/crypton.cabal b/crypton.cabal index c747d6c7..a393114f 100644 --- a/crypton.cabal +++ b/crypton.cabal @@ -134,6 +134,7 @@ Library Crypto.MAC.CMAC Crypto.MAC.Poly1305 Crypto.MAC.HMAC + Crypto.MAC.KeyedBlake2 Crypto.MAC.KMAC Crypto.Number.Basic Crypto.Number.F2m @@ -432,6 +433,7 @@ Test-Suite test-crypton KAT_Ed25519 KAT_Ed448 KAT_EdDSA + KAT_Blake2 KAT_CMAC KAT_HKDF KAT_HMAC diff --git a/tests/KAT_Blake2.hs b/tests/KAT_Blake2.hs new file mode 100644 index 00000000..f3b6a894 --- /dev/null +++ b/tests/KAT_Blake2.hs @@ -0,0 +1,166 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +module KAT_Blake2 (tests) where + +import Crypto.Hash (digestFromByteString) +import Crypto.Hash.Algorithms +import qualified Crypto.MAC.KeyedBlake2 as KB + +import qualified Data.ByteString as B + +import Imports + + +data MACVector hash = MACVector + { macMessage :: ByteString + , macKey :: ByteString + , macResult :: KB.KeyedBlake2 hash + } + +instance Show (KB.KeyedBlake2 hash) where + show (KB.KeyedBlake2 d) = show d + +digest :: KB.HashBlake2 hash => ByteString -> KB.KeyedBlake2 hash +digest = maybe (error "cannot get digest") KB.KeyedBlake2 . digestFromByteString + + +-- From: https://github.com/BLAKE2/BLAKE2/blob/master/testvectors/ +vectorsBlake2bKAT :: [MACVector (Blake2b 512)] +vectorsBlake2bKAT = + [ MACVector + { macMessage = "" + , macKey = fixedKey + , macResult = digest "\x10\xeb\xb6\x77\x00\xb1\x86\x8e\xfb\x44\x17\x98\x7a\xcf\x46\x90\xae\x9d\x97\x2f\xb7\xa5\x90\xc2\xf0\x28\x71\x79\x9a\xaa\x47\x86\xb5\xe9\x96\xe8\xf0\xf4\xeb\x98\x1f\xc2\x14\xb0\x05\xf4\x2d\x2f\xf4\x23\x34\x99\x39\x16\x53\xdf\x7a\xef\xcb\xc1\x3f\xc5\x15\x68" + } + , MACVector + { macMessage = "\x00" + , macKey = fixedKey + , macResult = digest "\x96\x1f\x6d\xd1\xe4\xdd\x30\xf6\x39\x01\x69\x0c\x51\x2e\x78\xe4\xb4\x5e\x47\x42\xed\x19\x7c\x3c\x5e\x45\xc5\x49\xfd\x25\xf2\xe4\x18\x7b\x0b\xc9\xfe\x30\x49\x2b\x16\xb0\xd0\xbc\x4e\xf9\xb0\xf3\x4c\x70\x03\xfa\xc0\x9a\x5e\xf1\x53\x2e\x69\x43\x02\x34\xce\xbd" + } + , MACVector + { macMessage = B.pack [ 0x00 .. 0xfe ] + , macKey = fixedKey + , macResult = digest "\x14\x27\x09\xd6\x2e\x28\xfc\xcc\xd0\xaf\x97\xfa\xd0\xf8\x46\x5b\x97\x1e\x82\x20\x1d\xc5\x10\x70\xfa\xa0\x37\x2a\xa4\x3e\x92\x48\x4b\xe1\xc1\xe7\x3b\xa1\x09\x06\xd5\xd1\x85\x3d\xb6\xa4\x10\x6e\x0a\x7b\xf9\x80\x0d\x37\x3d\x6d\xee\x2d\x46\xd6\x2e\xf2\xa4\x61" + } + ] + where fixedKey = B.pack [ 0x00 .. 0x3f ] + +vectorsBlake2bpKAT :: [MACVector (Blake2bp 512)] +vectorsBlake2bpKAT = + [ MACVector + { macMessage = "" + , macKey = fixedKey + , macResult = digest "\x9d\x94\x61\x07\x3e\x4e\xb6\x40\xa2\x55\x35\x7b\x83\x9f\x39\x4b\x83\x8c\x6f\xf5\x7c\x9b\x68\x6a\x3f\x76\x10\x7c\x10\x66\x72\x8f\x3c\x99\x56\xbd\x78\x5c\xbc\x3b\xf7\x9d\xc2\xab\x57\x8c\x5a\x0c\x06\x3b\x9d\x9c\x40\x58\x48\xde\x1d\xbe\x82\x1c\xd0\x5c\x94\x0a" + } + , MACVector + { macMessage = "\x00" + , macKey = fixedKey + , macResult = digest "\xff\x8e\x90\xa3\x7b\x94\x62\x39\x32\xc5\x9f\x75\x59\xf2\x60\x35\x02\x9c\x37\x67\x32\xcb\x14\xd4\x16\x02\x00\x1c\xbb\x73\xad\xb7\x92\x93\xa2\xdb\xda\x5f\x60\x70\x30\x25\x14\x4d\x15\x8e\x27\x35\x52\x95\x96\x25\x1c\x73\xc0\x34\x5c\xa6\xfc\xcb\x1f\xb1\xe9\x7e" + } + , MACVector + { macMessage = B.pack [ 0x00 .. 0xfe ] + , macKey = fixedKey + , macResult = digest "\x96\xfb\xcb\xb6\x0b\xd3\x13\xb8\x84\x50\x33\xe5\xbc\x05\x8a\x38\x02\x74\x38\x57\x2d\x7e\x79\x57\xf3\x68\x4f\x62\x68\xaa\xdd\x3a\xd0\x8d\x21\x76\x7e\xd6\x87\x86\x85\x33\x1b\xa9\x85\x71\x48\x7e\x12\x47\x0a\xad\x66\x93\x26\x71\x6e\x46\x66\x7f\x69\xf8\xd7\xe8" + } + ] + where fixedKey = B.pack [ 0x00 .. 0x3f ] + +vectorsBlake2sKAT :: [MACVector (Blake2s 256)] +vectorsBlake2sKAT = + [ MACVector + { macMessage = "" + , macKey = fixedKey + , macResult = digest "\x48\xa8\x99\x7d\xa4\x07\x87\x6b\x3d\x79\xc0\xd9\x23\x25\xad\x3b\x89\xcb\xb7\x54\xd8\x6a\xb7\x1a\xee\x04\x7a\xd3\x45\xfd\x2c\x49" + } + , MACVector + { macMessage = "\x00" + , macKey = fixedKey + , macResult = digest "\x40\xd1\x5f\xee\x7c\x32\x88\x30\x16\x6a\xc3\xf9\x18\x65\x0f\x80\x7e\x7e\x01\xe1\x77\x25\x8c\xdc\x0a\x39\xb1\x1f\x59\x80\x66\xf1" + } + , MACVector + { macMessage = B.pack [ 0x00 .. 0xfe ] + , macKey = fixedKey + , macResult = digest "\x3f\xb7\x35\x06\x1a\xbc\x51\x9d\xfe\x97\x9e\x54\xc1\xee\x5b\xfa\xd0\xa9\xd8\x58\xb3\x31\x5b\xad\x34\xbd\xe9\x99\xef\xd7\x24\xdd" + } + ] + where fixedKey = B.pack [ 0x00 .. 0x1f ] + +vectorsBlake2spKAT :: [MACVector (Blake2sp 256)] +vectorsBlake2spKAT = + [ MACVector + { macMessage = "" + , macKey = fixedKey + , macResult = digest "\x71\x5c\xb1\x38\x95\xae\xb6\x78\xf6\x12\x41\x60\xbf\xf2\x14\x65\xb3\x0f\x4f\x68\x74\x19\x3f\xc8\x51\xb4\x62\x10\x43\xf0\x9c\xc6" + } + , MACVector + { macMessage = "\x00" + , macKey = fixedKey + , macResult = digest "\x40\x57\x8f\xfa\x52\xbf\x51\xae\x18\x66\xf4\x28\x4d\x3a\x15\x7f\xc1\xbc\xd3\x6a\xc1\x3c\xbd\xcb\x03\x77\xe4\xd0\xcd\x0b\x66\x03" + } + , MACVector + { macMessage = B.pack [ 0x00 .. 0xfe ] + , macKey = fixedKey + , macResult = digest "\x0c\x8a\x36\x59\x7d\x74\x61\xc6\x3a\x94\x73\x28\x21\xc9\x41\x85\x6c\x66\x83\x76\x60\x6c\x86\xa5\x2d\xe0\xee\x41\x04\xc6\x15\xdb" + } + ] + where fixedKey = B.pack [ 0x00 .. 0x1f ] + +macTests :: [TestTree] +macTests = + [ testGroup "Blake2b_512" (concatMap toMACTest $ zip is vectorsBlake2bKAT) + , testGroup "Blake2bp_512" (concatMap toMACTest $ zip is vectorsBlake2bpKAT) + , testGroup "Blake2s_512" (concatMap toMACTest $ zip is vectorsBlake2sKAT) + , testGroup "Blake2sp_512" (concatMap toMACTest $ zip is vectorsBlake2spKAT) + ] + where toMACTest (i, MACVector{..}) = + [ testCase (show i) (macResult @=? KB.keyedBlake2 macKey macMessage) + , testCase ("incr-" ++ show i) (macResult @=? + KB.finalize (KB.update (KB.initialize macKey) macMessage)) + ] + is :: [Int] + is = [1..] + +data MacIncremental a = MacIncremental ByteString ByteString (KB.KeyedBlake2 a) + deriving (Show,Eq) + +instance KB.HashBlake2 a => Arbitrary (MacIncremental a) where + arbitrary = do + key <- arbitraryBSof 32 64 + msg <- arbitraryBSof 1 99 + return $ MacIncremental key msg (KB.keyedBlake2 key msg) + +data MacIncrementalList a = MacIncrementalList ByteString [ByteString] (KB.KeyedBlake2 a) + deriving (Show,Eq) + +instance KB.HashBlake2 a => Arbitrary (MacIncrementalList a) where + arbitrary = do + key <- arbitraryBSof 32 64 + msgs <- choose (1,20) >>= \n -> replicateM n (arbitraryBSof 1 99) + return $ MacIncrementalList key msgs (KB.keyedBlake2 key (B.concat msgs)) + +macIncrementalTests :: [TestTree] +macIncrementalTests = + [ testIncrProperties "Blake2b_512" (Blake2b :: Blake2b 512) + , testIncrProperties "Blake2bp_512" (Blake2bp :: Blake2bp 512) + , testIncrProperties "Blake2s_256" (Blake2s :: Blake2s 256) + , testIncrProperties "Blake2sp_256" (Blake2sp :: Blake2sp 256) + ] + where + testIncrProperties :: KB.HashBlake2 a => TestName -> a -> TestTree + testIncrProperties name a = testGroup name + [ testProperty "list-one" (prop_inc0 a) + , testProperty "list-multi" (prop_inc1 a) + ] + + prop_inc0 :: KB.HashBlake2 a => a -> MacIncremental a -> Bool + prop_inc0 _ (MacIncremental secret msg result) = + result `assertEq` KB.finalize (KB.update (KB.initialize secret) msg) + + prop_inc1 :: KB.HashBlake2 a => a -> MacIncrementalList a -> Bool + prop_inc1 _ (MacIncrementalList secret msgs result) = + result `assertEq` KB.finalize (foldl' KB.update (KB.initialize secret) msgs) + +tests = testGroup "Blake2" + [ testGroup "KATs" macTests + , testGroup "properties" macIncrementalTests ] diff --git a/tests/Tests.hs b/tests/Tests.hs index b3b0b277..437dee78 100644 --- a/tests/Tests.hs +++ b/tests/Tests.hs @@ -19,6 +19,7 @@ import qualified XSalsa import qualified ChaCha import qualified ChaChaPoly1305 import qualified KAT_MiyaguchiPreneel +import qualified KAT_Blake2 import qualified KAT_CMAC import qualified KAT_HMAC import qualified KAT_KMAC @@ -60,6 +61,7 @@ tests = testGroup "cryptonite" ] , testGroup "MAC" [ Poly1305.tests + , KAT_Blake2.tests , KAT_CMAC.tests , KAT_HMAC.tests , KAT_KMAC.tests