From 30c1af2858da32e82ea22f58d14d9c4857ea0df0 Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Sun, 17 Apr 2016 03:06:28 -0400 Subject: Added BLAKE2 Cryptographic Hash and Message Authentication Code --- blake2.h | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 blake2.h (limited to 'blake2.h') diff --git a/blake2.h b/blake2.h new file mode 100644 index 00000000..bd081fc5 --- /dev/null +++ b/blake2.h @@ -0,0 +1,319 @@ +// blake2.cpp - written and placed in the public domain by Jeffrey Walton and Zooko Wilcox-O'Hearn +// Based on Aumasson, Neves, Wilcox-O’Hearn and Winnerlein's reference BLAKE2 +// implementation at http://github.com/BLAKE2/BLAKE2. + +//! \file blake2.h +//! \brief Classes for BLAKE2b and BLAKE2s message digests and keyed message digests +//! \details This implmentation follows Aumasson, Neves, Wilcox-O’Hearn and Winnerlein's +//! BLAKE2: simpler, smaller, fast as MD5 (2013.01.29). +//! Static algorithm names return either "BLAKE2b" or "BLAKE2s". An object algorithm name follows +//! the naming described in RFC 7693, The +//! BLAKE2 Cryptographic Hash and Message Authentication Code (MAC). + +#ifndef CRYPTOPP_BLAKE2_H +#define CRYPTOPP_BLAKE2_H + +#include "cryptlib.h" +#include "secblock.h" +#include "seckey.h" + +NAMESPACE_BEGIN(CryptoPP) + +// Can't use GetAlignmentOf() because its not a constant expression. GCC has +// some bugs spanning 4.0 through 4.9, so we can't use CRYPTOPP_CONSTANT, either. +// Also see http://stackoverflow.com/q/36642315. We may need to increase alignment +// to 32 bytes due to SSE4 alignment requirements. +#if CRYPTOPP_BOOL_ALIGN16 +# define BLAKE2_DALIGN 16 +#elif defined(_M_X64) || defined(__LP64__) || defined(__x86_64__) || defined(__amd64__) +# define BLAKE2_DALIGN 8 +#else +# define BLAKE2_DALIGN 4 +#endif + +//! \class BLAKE2_Info +//! \brief BLAKE2 hash information +//! \tparam T_64bit flag indicating 64-bit +template +struct CRYPTOPP_NO_VTABLE BLAKE2_Info : public VariableKeyLength<0,0,(T_64bit ? 64 : 32),1,SimpleKeyingInterface::NOT_RESYNCHRONIZABLE> +{ + CRYPTOPP_CONSTANT(BLOCKSIZE = (T_64bit ? 128 : 64)) + CRYPTOPP_CONSTANT(DIGESTSIZE = (T_64bit ? 64 : 32)) + CRYPTOPP_CONSTANT(SALTSIZE = (T_64bit ? 16 : 8)) + CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = (T_64bit ? 16 : 8)) + CRYPTOPP_CONSTANT(ALIGNSIZE = BLAKE2_DALIGN); + + static const char *StaticAlgorithmName() {return (T_64bit ? "BLAKE2b" : "BLAKE2s");} +}; + +//! \class BLAKE2_ParameterBlock +//! \brief BLAKE2 parameter block +//! \tparam T_64bit flag indicating 64-bit +//! \details BLAKE2b uses BLAKE2_ParameterBlock, while BLAKE2s +//! uses BLAKE2_ParameterBlock. +template +struct CRYPTOPP_NO_VTABLE BLAKE2_ParameterBlock +{ +}; + +//! \brief BLAKE2b parameter block specialization +template<> +struct CRYPTOPP_NO_VTABLE BLAKE2_ParameterBlock +{ + CRYPTOPP_CONSTANT(SALTSIZE = BLAKE2_Info::SALTSIZE); + CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2_Info::DIGESTSIZE); + CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = BLAKE2_Info::PERSONALIZATIONSIZE); + + ~BLAKE2_ParameterBlock() + { + // Easier than SecBlock or using an AlignedAllocatorWithCleanup + SecureWipeBuffer(reinterpret_cast(this), sizeof(*this)); + }; + + BLAKE2_ParameterBlock() + { + memset(this, 0x00, sizeof(*this)); + digestLength = DIGESTSIZE; + fanout = depth = 1; + } + + BLAKE2_ParameterBlock(size_t digestSize) + { + assert(digestSize <= DIGESTSIZE); + memset(this, 0x00, sizeof(*this)); + digestLength = (byte)digestSize; + fanout = depth = 1; + } + + BLAKE2_ParameterBlock(size_t digestSize, size_t keyLength, const byte* salt, size_t saltLength, + const byte* personalization, size_t personalizationLength); + + byte digestLength, keyLength, fanout, depth; + byte leafLength[4]; + byte nodeOffset[8]; + byte nodeDepth, innerLength, rfu[14]; + byte salt[SALTSIZE]; + byte personalization[PERSONALIZATIONSIZE]; +}; + +//! \brief BLAKE2s parameter block specialization +template<> +struct CRYPTOPP_NO_VTABLE BLAKE2_ParameterBlock +{ + CRYPTOPP_CONSTANT(SALTSIZE = BLAKE2_Info::SALTSIZE); + CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2_Info::DIGESTSIZE); + CRYPTOPP_CONSTANT(PERSONALIZATIONSIZE = BLAKE2_Info::PERSONALIZATIONSIZE); + + ~BLAKE2_ParameterBlock() + { + // Easier than SecBlock or using an AlignedAllocatorWithCleanup + SecureWipeBuffer(reinterpret_cast(this), sizeof(*this)); + }; + + BLAKE2_ParameterBlock() + { + memset(this, 0x00, sizeof(*this)); + digestLength = DIGESTSIZE; + fanout = depth = 1; + } + + BLAKE2_ParameterBlock(size_t digestSize) + { + assert(digestSize <= BLAKE2_Info::DIGESTSIZE); + memset(this, 0x00, sizeof(*this)); + digestLength = (byte)digestSize; + fanout = depth = 1; + } + + BLAKE2_ParameterBlock(size_t digestSize, size_t keyLength, const byte* salt, size_t saltLength, + const byte* personalization, size_t personalizationLength); + + byte digestLength, keyLength, fanout, depth; + byte leafLength[4]; + byte nodeOffset[6]; + byte nodeDepth, innerLength; + byte salt[SALTSIZE]; + byte personalization[PERSONALIZATIONSIZE]; +}; + +//! \class BLAKE2_State +//! \brief BLAKE2 state information +//! \tparam W word type +//! \tparam T_64bit flag indicating 64-bit +//! \details BLAKE2b uses BLAKE2_State, while BLAKE2s +//! uses BLAKE2_State. +template +struct CRYPTOPP_NO_VTABLE BLAKE2_State +{ + // CRYPTOPP_CONSTANT(ALIGNSIZE = BLAKE2_Info::ALIGNSIZE); + CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE2_Info::BLOCKSIZE); + + ~BLAKE2_State() + { + // Easier than SecBlock or using an AlignedAllocatorWithCleanup + SecureWipeBuffer(reinterpret_cast(this), sizeof(*this)); + }; + + BLAKE2_State() + { + // Set all members excpet scratch buf[] + memset(this, 0x00, sizeof(*this)-sizeof(buffer)); + } + + W t[2], h[8], f[2]; + size_t length; + byte buffer[BLOCKSIZE]; +}; + +//! \class BLAKE2_Base +//! \brief BLAKE2 hash implementation +//! \tparam W word type +//! \tparam T_64bit flag indicating 64-bit +//! \details BLAKE2b uses BLAKE2_Base, while BLAKE2s +//! uses BLAKE2_Base. +template +class CRYPTOPP_NO_VTABLE BLAKE2_Base : public SimpleKeyingInterfaceImpl > +{ +public: + CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2_Info::DIGESTSIZE); + CRYPTOPP_CONSTANT(BLOCKSIZE = BLAKE2_Info::BLOCKSIZE); + CRYPTOPP_CONSTANT(ALIGNSIZE = BLAKE2_Info::ALIGNSIZE); + + typedef BLAKE2_ParameterBlock ParameterBlock; + typedef BLAKE2_State State; + + virtual ~BLAKE2_Base() {} + + //! \brief Retrieve the static algorithm name + //! \returns the algorithm name (BLAKE2s or BLAKE2b) + static const char *StaticAlgorithmName() {return BLAKE2_Info::StaticAlgorithmName();} + + //! \brief Retrieve the object's name + //! \returns the object's algorithm name following RFC 7693 + //! \details Object algorithm name follows the naming described in + //! RFC 7693, The BLAKE2 Cryptographic Hash and + //! Message Authentication Code (MAC). For example, "BLAKE2b-512" and "BLAKE2s-256". + std::string AlgorithmName() const {return std::string(StaticAlgorithmName()) + "-" + IntToString(this->DigestSize()*8);} + + unsigned int DigestSize() const {return m_digestSize;} + unsigned int OptimalDataAlignment() const {return ALIGNSIZE;} + + void Update(const byte *input, size_t length); + void Restart(); + + //! \brief Restart a hash with parameter block and counter + //! \param block paramter block + //! \param counter counter array + //! \details Parameter block is persisted across calls to Restart(). + void Restart(const BLAKE2_ParameterBlock& block, const W counter[2]); + + //! \brief Set tree mode + //! \param mode the new tree mode + //! \details BLAKE2 has two finalization flags, called State::f[0] and State::f[1]. + //! If treeMode=false (default), then State::f[1] is never set. If + //! treeMode=true, then State::f[1] is set when State::f[0] is set. + //! Tree mode is persisted across calls to Restart(). + void SetTreeMode(bool mode) {m_treeMode=mode;} + + //! \brief Get tree mode + //! \returns the current tree mode + //! \details Tree mode is persisted across calls to Restart(). + bool GetTreeMode() const {return m_treeMode;} + + void TruncatedFinal(byte *hash, size_t size); + +protected: + BLAKE2_Base(); + BLAKE2_Base(bool treeMode, unsigned int digestSize); + BLAKE2_Base(const byte *key, size_t keyLength, const byte* salt, size_t saltLength, + const byte* personalization, size_t personalizationLength, + bool treeMode, unsigned int digestSize); + + // Operates on state buffer and/or input. Must be BLOCKSIZE, final block will pad with 0's. + void Compress(const byte *input); + inline void IncrementCounter(size_t count=BLOCKSIZE); + + void UncheckedSetKey(const byte* key, unsigned int length, const CryptoPP::NameValuePairs& params); + +private: + CRYPTOPP_ALIGN_DATA(BLAKE2_DALIGN) State m_state; + CRYPTOPP_ALIGN_DATA(BLAKE2_DALIGN) ParameterBlock m_block; + AlignedSecByteBlock m_key; + word32 m_digestSize; + bool m_treeMode; +}; + +//! \brief The BLAKE2b cryptographic hash function +//! \details BLAKE2b can function as both a hash and keyed hash. If you want only the hash, +//! then use the BLAKE2b constructor that accepts no parameters or digest size. If you +//! want a keyed hash, then use the constuctor that accpts the key as a parameter. +//! Once a key and digest size are selected, its effectively immutable. The Restart() +//! method that accepts a ParameterBlock does not allow you to change it. +//! \sa Aumasson, Neves, Wilcox-O’Hearn and Winnerlein's +//! BLAKE2: simpler, smaller, fast as MD5 (2013.01.29). +class BLAKE2b : public BLAKE2_Base +{ +public: + CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2_Info::DIGESTSIZE); + typedef BLAKE2_Base BaseClass; // Early Visual Studio workaround + typedef BLAKE2_ParameterBlock ParameterBlock; + CRYPTOPP_COMPILE_ASSERT(sizeof(ParameterBlock) == 64); + + //! \brief Construct a BLAKE2b hash + //! \param digestSize the digest size, in bytes + BLAKE2b(bool treeMode=false, unsigned int digestSize = DIGESTSIZE) : BaseClass(treeMode, digestSize) {} + + //! \brief Construct a BLAKE2b hash + //! \param key a byte array used to key the cipher + //! \param keyLength the size of the byte array + //! \param salt a byte array used as salt + //! \param saltLength the size of the byte array + //! \param personalization a byte array used as prsonalization string + //! \param personalizationLength the size of the byte array + //! \param treeMode flag indicating tree mode + //! \param digestSize the digest size, in bytes + BLAKE2b(const byte *key, size_t keyLength, const byte* salt = NULL, size_t saltLength = 0, + const byte* personalization = NULL, size_t personalizationLength = 0, + bool treeMode=false, unsigned int digestSize = DIGESTSIZE) + : BaseClass(key, keyLength, salt, saltLength, personalization, personalizationLength, treeMode, digestSize) {} +}; + +//! \brief The BLAKE2s cryptographic hash function +//! \details BLAKE2s can function as both a hash and keyed hash. If you want only the hash, +//! then use the BLAKE2s constructor that accepts no parameters or digest size. If you +//! want a keyed hash, then use the constuctor that accpts the key as a parameter. +//! Once a key and digest size are selected, its effectively immutable. The Restart() +//! method that accepts a ParameterBlock does not allow you to change it. +//! \sa Aumasson, Neves, Wilcox-O’Hearn and Winnerlein's +//! BLAKE2: simpler, smaller, fast as MD5 (2013.01.29). +class BLAKE2s : public BLAKE2_Base +{ +public: + CRYPTOPP_CONSTANT(DIGESTSIZE = BLAKE2_Info::DIGESTSIZE); + typedef BLAKE2_Base BaseClass; // Early Visual Studio workaround + typedef BLAKE2_ParameterBlock ParameterBlock; + CRYPTOPP_COMPILE_ASSERT(sizeof(ParameterBlock) == 32); + + //! \brief Construct a BLAKE2b hash + //! \param digestSize the digest size, in bytes + BLAKE2s(bool treeMode=false, unsigned int digestSize = DIGESTSIZE) : BaseClass(treeMode, digestSize) {} + + //! \brief Construct a BLAKE2b hash + //! \param key a byte array used to key the cipher + //! \param keyLength the size of the byte array + //! \param salt a byte array used as salt + //! \param saltLength the size of the byte array + //! \param personalization a byte array used as prsonalization string + //! \param personalizationLength the size of the byte array + //! \param treeMode flag indicating tree mode + //! \param digestSize the digest size, in bytes + BLAKE2s(const byte *key, size_t keyLength, const byte* salt = NULL, size_t saltLength = 0, + const byte* personalization = NULL, size_t personalizationLength = 0, + bool treeMode=false, unsigned int digestSize = DIGESTSIZE) + : BaseClass(key, keyLength, salt, saltLength, personalization, personalizationLength, treeMode, digestSize) {} +}; + +NAMESPACE_END + +#endif + -- cgit v1.2.1