// siphash.h - written and placed in public domain by Jeffrey Walton.
//! \file siphash.h
//! \brief Classes for SipHash message authentication code
//! \details SipHash computes a 64-bit or 128-bit message authentication code from a variable-length
//! message and 128-bit secret key. It was designed to be efficient even for short inputs, with
//! performance comparable to non-cryptographic hash functions.
//! \details To create a SipHash-2-4 object with a 64-bit MAC use code similar to the following.
//!
SecByteBlock key(16);
//! prng.GenerateBlock(key, key.size());
//!
//! SipHash<2,4,false> hash(key, key.size());
//! hash.Update(...);
//! hash.Final(...);
//! \details To create a SipHash-2-4 object with a 128-bit MAC use code similar to the following.
//! SecByteBlock key(16);
//! prng.GenerateBlock(key, key.size());
//!
//! SipHash<2,4,true> hash(key, key.size());
//! hash.Update(...);
//! hash.Final(...);
//! \sa Jean-Philippe Aumasson and Daniel J. Bernstein SipHash:
//! a fast short-input PRF
//! \since Crypto++ 6.0
#ifndef CRYPTOPP_SIPHASH_H
#define CRYPTOPP_SIPHASH_H
#include "cryptlib.h"
#include "secblock.h"
#include "misc.h"
NAMESPACE_BEGIN(CryptoPP)
//! \class SipHash_Info
//! \brief SipHash message authentication code information
//! \tparam T_128bit flag indicating 128-bit (true) versus 64-bit (false) digest size
template
class SipHash_Info : public FixedKeyLength<16>
{
public:
CRYPTOPP_STATIC_CONSTEXPR const char* StaticAlgorithmName() {return "SipHash";}
CRYPTOPP_CONSTANT(DIGESTSIZE = (T_128bit ? 16 : 8))
};
//! \class SipHash_Base
//! \brief SipHash message authentication code base class
//! \tparam C the number of compression rounds
//! \tparam D the number of finalization rounds
//! \tparam T_128bit flag indicating 128-bit (true) versus 64-bit (false) digest size
template
class SipHash_Base : public MessageAuthenticationCode, public SipHash_Info
{
public:
static std::string StaticAlgorithmName() {
return std::string(SipHash_Info::StaticAlgorithmName())+"-"+IntToString(C)+"-"+IntToString(D);
}
virtual ~SipHash_Base() {}
SipHash_Base() : m_idx(0) {}
virtual unsigned int DigestSize() const
{return SipHash_Info::DIGESTSIZE;}
virtual size_t MinKeyLength() const
{return SipHash_Info::MIN_KEYLENGTH;}
virtual size_t MaxKeyLength() const
{return SipHash_Info::MAX_KEYLENGTH;}
virtual size_t DefaultKeyLength() const
{return SipHash_Info::DEFAULT_KEYLENGTH;}
virtual size_t GetValidKeyLength(size_t keylength) const
{CRYPTOPP_UNUSED(keylength); return SipHash_Info::DEFAULT_KEYLENGTH;}
virtual IV_Requirement IVRequirement() const
{return SimpleKeyingInterface::NOT_RESYNCHRONIZABLE;}
virtual unsigned int IVSize() const
{return 0;}
virtual unsigned int OptimalBlockSize() const
{return sizeof(word64);}
virtual unsigned int OptimalDataAlignment () const
{return GetAlignmentOf();}
virtual void Update(const byte *input, size_t length);
virtual void TruncatedFinal(byte *digest, size_t digestSize);
protected:
virtual void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms);
virtual void Restart();
inline void SIPROUND()
{
m_v[0] += m_v[1];
m_v[1] = rotlFixed(m_v[1], 13U);
m_v[1] ^= m_v[0];
m_v[0] = rotlFixed(m_v[0], 32U);
m_v[2] += m_v[3];
m_v[3] = rotlFixed(m_v[3], 16U);
m_v[3] ^= m_v[2];
m_v[0] += m_v[3];
m_v[3] = rotlFixed(m_v[3], 21U);
m_v[3] ^= m_v[0];
m_v[2] += m_v[1];
m_v[1] = rotlFixed(m_v[1], 17U);
m_v[1] ^= m_v[2];
m_v[2] = rotlFixed(m_v[2], 32U);
}
private:
FixedSizeSecBlock m_v;
FixedSizeSecBlock m_k;
FixedSizeSecBlock m_b;
// Tail bytes
FixedSizeSecBlock m_acc;
size_t m_idx;
};
//! \class SipHash
//! \brief SipHash message authentication code
//! \tparam C the number of compression rounds
//! \tparam D the number of finalization rounds
//! \tparam T_128bit flag indicating 128-bit (true) versus 64-bit (false) digest size
//! \details SipHash computes a 64-bit or 128-bit message authentication code from a variable-length
//! message and 128-bit secret key. It was designed to be efficient even for short inputs, with
//! performance comparable to non-cryptographic hash functions.
//! \details To create a SipHash-2-4 object with a 64-bit MAC use code similar to the following.
//! SecByteBlock key(16);
//! prng.GenerateBlock(key, key.size());
//!
//! SipHash<2,4,false> hash(key, key.size());
//! hash.Update(...);
//! hash.Final(...);
//! \details To create a SipHash-2-4 object with a 128-bit MAC use code similar to the following.
//! SecByteBlock key(16);
//! prng.GenerateBlock(key, key.size());
//!
//! SipHash<2,4,true> hash(key, key.size());
//! hash.Update(...);
//! hash.Final(...);
//! \sa Jean-Philippe Aumasson and Daniel J. Bernstein SipHash:
//! a fast short-input PRF
//! \since Crypto++ 6.0
template
class SipHash : public SipHash_Base
{
public:
//! \brief Create a SipHash
SipHash()
{this->UncheckedSetKey(NULLPTR, 0, g_nullNameValuePairs);}
//! \brief Create a SipHash
//! \param key a byte array used to key the cipher
//! \param length the size of the byte array, in bytes
SipHash(const byte *key, unsigned int length)
{this->UncheckedSetKey(key, length, g_nullNameValuePairs);}
};
template
void SipHash_Base::Update(const byte *input, size_t length)
{
CRYPTOPP_ASSERT((input && length) || !length);
if (!length) return;
if (m_idx)
{
size_t head = STDMIN(size_t(8U-m_idx), length);
memcpy(m_acc+m_idx, input, head);
m_idx += head; input += head; length -= head;
if (m_idx == 8)
{
word64 m = GetWord(true, LITTLE_ENDIAN_ORDER, m_acc);
m_v[3] ^= m;
for (unsigned int i = 0; i < C; ++i)
SIPROUND();
m_v[0] ^= m;
m_b[0] += 8;
m_idx = 0;
}
}
while (length >= 8)
{
word64 m = GetWord(false, LITTLE_ENDIAN_ORDER, input);
m_v[3] ^= m;
for (unsigned int i = 0; i < C; ++i)
SIPROUND();
m_v[0] ^= m;
m_b[0] += 8;
input += 8;
length -= 8;
}
CRYPTOPP_ASSERT(length < 8);
size_t tail = length % 8;
if (tail)
{
memcpy(m_acc+m_idx, input, tail);
m_idx += tail;
}
}
template
void SipHash_Base::TruncatedFinal(byte *digest, size_t digestSize)
{
CRYPTOPP_ASSERT(digest); // Pointer is valid
ThrowIfInvalidTruncatedSize(digestSize);
// The high octet holds length and is digested mod 256
m_b[0] += m_idx; m_b[0] <<= 56U;
switch (m_idx)
{
case 7:
m_b[0] |= ((word64)m_acc[6]) << 48;
// fall through
case 6:
m_b[0] |= ((word64)m_acc[5]) << 40;
// fall through
case 5:
m_b[0] |= ((word64)m_acc[4]) << 32;
// fall through
case 4:
m_b[0] |= ((word64)m_acc[3]) << 24;
// fall through
case 3:
m_b[0] |= ((word64)m_acc[2]) << 16;
// fall through
case 2:
m_b[0] |= ((word64)m_acc[1]) << 8;
// fall through
case 1:
m_b[0] |= ((word64)m_acc[0]);
// fall through
case 0:
break;
}
m_v[3] ^= m_b[0];
for (unsigned int i=0; i::DIGESTSIZE));
Restart();
}
template
void SipHash_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms)
{
CRYPTOPP_UNUSED(params);
if (key && length)
{
m_k[0] = GetWord(false, LITTLE_ENDIAN_ORDER, key);
m_k[1] = GetWord(false, LITTLE_ENDIAN_ORDER, key+8);
}
else
{
// Avoid Coverity finding
m_k[0] = m_k[1] = 0;
}
Restart();
}
template
void SipHash_Base::Restart ()
{
m_v[0] = W64LIT(0x736f6d6570736575);
m_v[1] = W64LIT(0x646f72616e646f6d);
m_v[2] = W64LIT(0x6c7967656e657261);
m_v[3] = W64LIT(0x7465646279746573);
m_v[3] ^= m_k[1];
m_v[2] ^= m_k[0];
m_v[1] ^= m_k[1];
m_v[0] ^= m_k[0];
if (T_128bit)
{
m_v[1] ^= 0xee;
}
m_idx = 0;
m_b[0] = 0;
}
NAMESPACE_END
#endif // CRYPTOPP_SIPHASH_H