diff --git a/argnames.h b/argnames.h index 9d8c1613..04b025b4 100644 --- a/argnames.h +++ b/argnames.h @@ -20,6 +20,7 @@ CRYPTOPP_DEFINE_NAME_STRING(Seed) //!< ConstByteArrayParameter CRYPTOPP_DEFINE_NAME_STRING(Key) //!< ConstByteArrayParameter CRYPTOPP_DEFINE_NAME_STRING(IV) //!< ConstByteArrayParameter, also accepts const byte * for backwards compatibility CRYPTOPP_DEFINE_NAME_STRING(StolenIV) //!< byte * +CRYPTOPP_DEFINE_NAME_STRING(Nonce) //!< ConstByteArrayParameter CRYPTOPP_DEFINE_NAME_STRING(Rounds) //!< int CRYPTOPP_DEFINE_NAME_STRING(FeedbackSize) //!< int CRYPTOPP_DEFINE_NAME_STRING(WordSize) //!< int, in bytes diff --git a/bench1.cpp b/bench1.cpp index f4f9ea94..ce29d45a 100644 --- a/bench1.cpp +++ b/bench1.cpp @@ -304,6 +304,7 @@ void BenchmarkAll(double t, double hertz) BenchMarkByName("Two-Track-MAC"); BenchMarkByName("CMAC(AES)"); BenchMarkByName("DMAC(AES)"); + BenchMarkByName("Poly1305(AES)"); BenchMarkByName("BLAKE2s"); BenchMarkByName("BLAKE2b"); diff --git a/poly1305.cpp b/poly1305.cpp new file mode 100644 index 00000000..55f1775b --- /dev/null +++ b/poly1305.cpp @@ -0,0 +1,248 @@ +// poly1305.cpp - written and placed in the public domain by Jeffrey Walton and Jean-Pierre Munch +// Based on Andy Polyakov's 32-bit OpenSSL implementation using scalar multiplication. +// Copyright assigned to the Crypto++ project + +#include "pch.h" +#include "cryptlib.h" +#include "aes.h" +#include "cpu.h" +#include "poly1305.h" + +NAMESPACE_BEGIN(CryptoPP) + +#define CONSTANT_TIME_CARRY(a,b) ((a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(a) * 8 - 1)) + +template +void Poly1305_Base::UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms) +{ + if(key && length) + { + // key is {k,r} pair, r is 16 bytes + length = SaturatingSubtract(length, (unsigned)BLOCKSIZE); + m_cipher.SetKey(key, length); + key += length; + + // Rbar is clamped and little endian + m_r[0] = GetWord(false, LITTLE_ENDIAN_ORDER, key + 0) & 0x0fffffff; + m_r[1] = GetWord(false, LITTLE_ENDIAN_ORDER, key + 4) & 0x0ffffffc; + m_r[2] = GetWord(false, LITTLE_ENDIAN_ORDER, key + 8) & 0x0ffffffc; + m_r[3] = GetWord(false, LITTLE_ENDIAN_ORDER, key + 12) & 0x0ffffffc; + + m_used = false; + } + + ConstByteArrayParameter t; + if(params.GetValue(Name::IV(), t) && t.begin() && t.size()) + { + SecByteBlock nk(16); + m_cipher.ProcessBlock(t.begin(), nk); + + m_n[0] = GetWord(false, LITTLE_ENDIAN_ORDER, nk + 0); + m_n[1] = GetWord(false, LITTLE_ENDIAN_ORDER, nk + 4); + m_n[2] = GetWord(false, LITTLE_ENDIAN_ORDER, nk + 8); + m_n[3] = GetWord(false, LITTLE_ENDIAN_ORDER, nk + 12); + + m_used = false; + } + + Restart(); +} + +template +void Poly1305_Base::Update(const byte *input, size_t length) +{ + CRYPTOPP_ASSERT((input && length) || !(input || length)); + // if(!input || !length) {return;} + + size_t rem, num = m_idx; + if (num) + { + rem = BLOCKSIZE - num; + if (length >= rem) + { + // Process + memcpy_s(m_acc + num, BLOCKSIZE - num, input, rem); + ProcessBlocks(m_acc, BLOCKSIZE, 1); + input += rem; + length -= rem; + } + else + { + // Accumulate + memcpy_s(m_acc + num, BLOCKSIZE - num, input, length); + m_idx = num + length; + return; + } + } + + rem = length % BLOCKSIZE; + length -= rem; + + if (length >= BLOCKSIZE) { + ProcessBlocks(input, length, 1); + input += length; + } + + if (rem) + memcpy(m_acc, input, rem); + + m_idx = rem; +} + +template +void Poly1305_Base::ProcessBlocks(const byte *input, size_t length, word32 padbit) +{ + word32 r0 = m_r[0], r1 = m_r[1], r2 = m_r[2], r3 = m_r[3]; + word32 h0 = m_h[0], h1 = m_h[1], h2 = m_h[2], h3 = m_h[3], h4 = m_h[4]; + word32 c, s1 = r1 + (r1 >> 2), s2 = r2 + (r2 >> 2), s3 = r3 + (r3 >> 2); + word64 d0, d1, d2, d3; + + while (length >= BLOCKSIZE) + { + // h += m[i] + h0 = (word32)(d0 = (word64)h0 + GetWord(false, LITTLE_ENDIAN_ORDER, input + 0)); + h1 = (word32)(d1 = (word64)h1 + (d0 >> 32) + GetWord(false, LITTLE_ENDIAN_ORDER, input + 4)); + h2 = (word32)(d2 = (word64)h2 + (d1 >> 32) + GetWord(false, LITTLE_ENDIAN_ORDER, input + 8)); + h3 = (word32)(d3 = (word64)h3 + (d2 >> 32) + GetWord(false, LITTLE_ENDIAN_ORDER, input + 12)); + h4 += (word32)(d3 >> 32) + padbit; + + // h *= r "%" p + d0 = ((word64)h0 * r0) + + ((word64)h1 * s3) + + ((word64)h2 * s2) + + ((word64)h3 * s1); + d1 = ((word64)h0 * r1) + + ((word64)h1 * r0) + + ((word64)h2 * s3) + + ((word64)h3 * s2) + + (h4 * s1); + d2 = ((word64)h0 * r2) + + ((word64)h1 * r1) + + ((word64)h2 * r0) + + ((word64)h3 * s3) + + (h4 * s2); + d3 = ((word64)h0 * r3) + + ((word64)h1 * r2) + + ((word64)h2 * r1) + + ((word64)h3 * r0) + + (h4 * s3); + h4 = (h4 * r0); + + // a) h4:h0 = h4<<128 + d3<<96 + d2<<64 + d1<<32 + d0 + h0 = (word32)d0; + h1 = (word32)(d1 += d0 >> 32); + h2 = (word32)(d2 += d1 >> 32); + h3 = (word32)(d3 += d2 >> 32); + h4 += (word32)(d3 >> 32); + + // b) (h4:h0 += (h4:h0>>130) * 5) %= 2^130 + c = (h4 >> 2) + (h4 & ~3U); + h4 &= 3; + h0 += c; + h1 += (c = CONSTANT_TIME_CARRY(h0,c)); + h2 += (c = CONSTANT_TIME_CARRY(h1,c)); + h3 += (c = CONSTANT_TIME_CARRY(h2,c)); + h4 += CONSTANT_TIME_CARRY(h3,c); + + input += BLOCKSIZE; + length -= BLOCKSIZE; + } + + m_h[0] = h0; m_h[1] = h1; m_h[2] = h2; + m_h[3] = h3; m_h[4] = h4; +} + +template +void Poly1305_Base::TruncatedFinal(byte *mac, size_t size) +{ + CRYPTOPP_ASSERT(!m_used); // Nonce is fresh + + ThrowIfInvalidTruncatedSize(size); + + size_t num = m_idx; + if (num) + { + m_acc[num++] = 1; /* pad bit */ + while (num < BLOCKSIZE) + m_acc[num++] = 0; + ProcessBlocks(m_acc, BLOCKSIZE, 0); + } + + ProcessFinal(mac, size); + + // Restart + m_used = true; + Restart(); +} + +template +void Poly1305_Base::ProcessFinal(byte *mac, size_t size) +{ + word32 h0 = m_h[0], h1 = m_h[1], h2 = m_h[2], h3 = m_h[3], h4 = m_h[4]; + word32 g0, g1, g2, g3, g4, mask; + word64 t; + + // compare to modulus by computing h + -p + g0 = (word32)(t = (word64)h0 + 5); + g1 = (word32)(t = (word64)h1 + (t >> 32)); + g2 = (word32)(t = (word64)h2 + (t >> 32)); + g3 = (word32)(t = (word64)h3 + (t >> 32)); + g4 = h4 + (word32)(t >> 32); + + // if there was carry into 131st bit, h3:h0 = g3:g0 + mask = 0 - (g4 >> 2); + g0 &= mask; g1 &= mask; + g2 &= mask; g3 &= mask; + mask = ~mask; + h0 = (h0 & mask) | g0; h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; h3 = (h3 & mask) | g3; + + // mac = (h + nonce) % (2^128) + h0 = (word32)(t = (word64)h0 + m_n[0]); + h1 = (word32)(t = (word64)h1 + (t >> 32) + m_n[1]); + h2 = (word32)(t = (word64)h2 + (t >> 32) + m_n[2]); + h3 = (word32)(t = (word64)h3 + (t >> 32) + m_n[3]); + + if (size >= BLOCKSIZE) + { + PutWord(false, LITTLE_ENDIAN_ORDER, mac + 0, h0); + PutWord(false, LITTLE_ENDIAN_ORDER, mac + 4, h1); + PutWord(false, LITTLE_ENDIAN_ORDER, mac + 8, h2); + PutWord(false, LITTLE_ENDIAN_ORDER, mac + 12, h3); + } + else + { + FixedSizeAlignedSecBlock t; + PutWord(false, LITTLE_ENDIAN_ORDER, t + 0, h0); + PutWord(false, LITTLE_ENDIAN_ORDER, t + 4, h1); + PutWord(false, LITTLE_ENDIAN_ORDER, t + 8, h2); + PutWord(false, LITTLE_ENDIAN_ORDER, t + 12, h3); + memcpy(mac, t, size); + } +} + +template +void Poly1305_Base::Resynchronize(const byte *nonce, int nonceLength) +{ + CRYPTOPP_ASSERT(nonceLength == -1 || nonceLength == (int)BLOCKSIZE); + nonceLength == -1 ? nonceLength = BLOCKSIZE : nonceLength; + this->UncheckedSetKey(NULL, 0, MakeParameters(Name::IV(), ConstByteArrayParameter(nonce, nonceLength))); +} + +template +void Poly1305_Base::GetNextIV(RandomNumberGenerator &rng, byte *iv) +{ + rng.GenerateBlock(iv, BLOCKSIZE); +} + +template +void Poly1305_Base::Restart() +{ + m_h[0] = m_h[1] = m_h[2] = m_h[3] = m_h[4] = 0; + // m_r[0] = m_r[1] = m_r[2] = m_r[3] = 0; + m_idx = 0; +} + +template class Poly1305; + +NAMESPACE_END diff --git a/poly1305.h b/poly1305.h new file mode 100644 index 00000000..d848ed2d --- /dev/null +++ b/poly1305.h @@ -0,0 +1,102 @@ +// poly1305.h - written and placed in the public domain by Jeffrey Walton and Jean-Pierre Munch +// Based on Andy Polyakov's 32-bit OpenSSL implementation using scalar multiplication. +// Copyright assigned to the Crypto++ project + +//! \file poly1305.h +//! \brief Classes for Poly1305 message authentication code +//! \sa Daniel J. Bernstein The Poly1305-AES +//! Message-Authentication Code (20050329) and Andy Polyakov Poly1305 Revised +//! \since Crypto++ 5.7 + +#ifndef CRYPTOPP_POLY1305_H +#define CRYPTOPP_POLY1305_H + +#include "cryptlib.h" +#include "seckey.h" +#include "secblock.h" +#include "argnames.h" +#include "algparam.h" + + +#include "files.h" +#include "filters.h" +#include "hex.h" +#include + +NAMESPACE_BEGIN(CryptoPP) + +//! \class Poly1305_Base +//! \brief Poly1305 message authentication code base class +//! \tparam T class derived from BlockCipherDocumentation with 16-byte key and 16-byte blocksize +//! \since Crypto++ 5.7 +template +class CRYPTOPP_NO_VTABLE Poly1305_Base : public FixedKeyLength<32, SimpleKeyingInterface::UNIQUE_IV, 16>, public MessageAuthenticationCode +{ + CRYPTOPP_COMPILE_ASSERT(T::DEFAULT_KEYLENGTH == 16); + CRYPTOPP_COMPILE_ASSERT(T::BLOCKSIZE == 16); + +public: + static std::string StaticAlgorithmName() {return std::string("Poly1305(") + T::StaticAlgorithmName() + ")";} + + CRYPTOPP_CONSTANT(DIGESTSIZE=T::BLOCKSIZE) + CRYPTOPP_CONSTANT(BLOCKSIZE=T::BLOCKSIZE) + + Poly1305_Base() : m_used(true) {} + + void Resynchronize (const byte *iv, int ivLength=-1); + void GetNextIV (RandomNumberGenerator &rng, byte *iv); + + void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); + void Update(const byte *input, size_t length); + void TruncatedFinal(byte *mac, size_t size); + void Restart(); + + unsigned int BlockSize() const {return BLOCKSIZE;} + unsigned int DigestSize() const {return DIGESTSIZE;} + +protected: + void ProcessBlocks(const byte *input, size_t length, word32 padbit); + void ProcessFinal(byte *mac, size_t length); + + CPP_TYPENAME T::Encryption m_cipher; + + // Accumulated hash, clamped r-key, and encrypted nonce + FixedSizeAlignedSecBlock m_h; + FixedSizeAlignedSecBlock m_r; + FixedSizeAlignedSecBlock m_n; + + // Accumulated message bytes and index + FixedSizeAlignedSecBlock m_acc; + size_t m_idx; + + // Track nonce reuse; assert in debug but continue + bool m_used; +}; + +//! \class Poly1305 +//! \brief Poly1305 message authentication code +//! \tparam T class derived from BlockCipherDocumentation +//! \sa Daniel J. Bernstein The Poly1305-AES +//! Message-Authentication Code (20050329) and Andy Polyakov Poly1305 Revised +//! \since Crypto++ 5.7 +template +class Poly1305 : public MessageAuthenticationCodeFinal > +{ +public: + CRYPTOPP_CONSTANT(DEFAULT_KEYLENGTH=Poly1305_Base::DEFAULT_KEYLENGTH); + + //! \brief Construct a Poly1305 + Poly1305() {} + + //! \brief Construct a Poly1305 + //! \param key a byte array used to key the cipher + //! \param length the size of the byte array, in bytes + Poly1305(const byte *key, size_t keyLength=DEFAULT_KEYLENGTH, const byte *nonce=NULL, size_t nonceLength=0) + {this->SetKey(key, keyLength, MakeParameters(Name::IV(), ConstByteArrayParameter(nonce, nonceLength)));} +}; + +NAMESPACE_END + +#endif // CRYPTOPP_POLY1305_H diff --git a/regtest.cpp b/regtest.cpp index 31ef689d..0216b149 100644 --- a/regtest.cpp +++ b/regtest.cpp @@ -53,6 +53,7 @@ #include "keccak.h" #include "sha3.h" #include "blake2.h" +#include "poly1305.h" #include "hkdf.h" // Aggressive stack checking with VS2005 SP1 and above. @@ -114,6 +115,7 @@ void RegisterFactories() RegisterDefaultFactoryFor >(); RegisterDefaultFactoryFor >(); RegisterDefaultFactoryFor >(); + RegisterDefaultFactoryFor >(); RegisterDefaultFactoryFor >(); RegisterDefaultFactoryFor(); RegisterDefaultFactoryFor(); diff --git a/validat1.cpp b/validat1.cpp index 6eb1d3f8..f673a949 100644 --- a/validat1.cpp +++ b/validat1.cpp @@ -101,6 +101,8 @@ bool ValidateAll(bool thorough) pass=ValidateRIPEMD() && pass; pass=ValidatePanama() && pass; pass=ValidateWhirlpool() && pass; + + pass=ValidatePoly1305() && pass; pass=ValidateBLAKE2s() && pass; pass=ValidateBLAKE2b() && pass; diff --git a/validat3.cpp b/validat3.cpp index 5a5b15ec..16bc6402 100644 --- a/validat3.cpp +++ b/validat3.cpp @@ -9,7 +9,6 @@ #include "gfpcrypt.h" #include "eccrypto.h" -#include "smartptr.h" #include "crc.h" #include "adler32.h" #include "md2.h" @@ -20,6 +19,8 @@ #include "ripemd.h" #include "whrlpool.h" #include "hkdf.h" +#include "poly1305.h" +#include "aes.h" #include "blake2.h" #include "hmac.h" #include "ttmac.h" @@ -771,6 +772,130 @@ bool ValidateHKDF() return pass; } +struct Poly1305_TestTuples +{ + const char *key, *message, *nonce, *digest; + size_t klen, mlen, nlen, dlen; +}; + +bool ValidatePoly1305() +{ + cout << "\nPoly1305 validation suite running...\n\n"; + bool fail, pass = true; + + { + fail = (Poly1305::StaticAlgorithmName() != "Poly1305(AES)"); + cout << (fail ? "FAILED " : "passed ") << "algorithm name\n"; + pass = pass && !fail; + } + + // Test data from http://cr.yp.to/mac/poly1305-20050329.pdf + static const Poly1305_TestTuples tests[] = + { + // Appendix B, Test 1 + { + "\xec\x07\x4c\x83\x55\x80\x74\x17\x01\x42\x5b\x62\x32\x35\xad\xd6" // Key + "\x85\x1f\xc4\x0c\x34\x67\xac\x0b\xe0\x5c\xc2\x04\x04\xf3\xf7\x00", + "\xf3\xf6", // Message + "\xfb\x44\x73\x50\xc4\xe8\x68\xc5\x2a\xc3\x27\x5c\xf9\xd4\x32\x7e", // Nonce + "\xf4\xc6\x33\xc3\x04\x4f\xc1\x45\xf8\x4f\x33\x5c\xb8\x19\x53\xde", // Digest + 32, 2, 16, 16 + }, + // Appendix B, Test 2 + { + "\x75\xde\xaa\x25\xc0\x9f\x20\x8e\x1d\xc4\xce\x6b\x5c\xad\x3f\xbf" // Key + "\x61\xee\x09\x21\x8d\x29\xb0\xaa\xed\x7e\x15\x4a\x2c\x55\x09\xcc", + "", // Message + "\x61\xee\x09\x21\x8d\x29\xb0\xaa\xed\x7e\x15\x4a\x2c\x55\x09\xcc", // Nonce + "\xdd\x3f\xab\x22\x51\xf1\x1a\xc7\x59\xf0\x88\x71\x29\xcc\x2e\xe7", // Digest + 32, 0, 16, 16 + }, + // Appendix B, Test 3 + { + "\x6a\xcb\x5f\x61\xa7\x17\x6d\xd3\x20\xc5\xc1\xeb\x2e\xdc\xdc\x74" // Key + "\x48\x44\x3d\x0b\xb0\xd2\x11\x09\xc8\x9a\x10\x0b\x5c\xe2\xc2\x08", + "\x66\x3c\xea\x19\x0f\xfb\x83\xd8\x95\x93\xf3\xf4\x76\xb6\xbc\x24" // Message + "\xd7\xe6\x79\x10\x7e\xa2\x6a\xdb\x8c\xaf\x66\x52\xd0\x65\x61\x36", + "\xae\x21\x2a\x55\x39\x97\x29\x59\x5d\xea\x45\x8b\xc6\x21\xff\x0e", // Nonce + "\x0e\xe1\xc1\x6b\xb7\x3f\x0f\x4f\xd1\x98\x81\x75\x3c\x01\xcd\xbe", // Digest + 32, 32, 16, 16 + }, + // Appendix B, Test 4 + { + "\xe1\xa5\x66\x8a\x4d\x5b\x66\xa5\xf6\x8c\xc5\x42\x4e\xd5\x98\x2d" // Key + "\x12\x97\x6a\x08\xc4\x42\x6d\x0c\xe8\xa8\x24\x07\xc4\xf4\x82\x07", + "\xab\x08\x12\x72\x4a\x7f\x1e\x34\x27\x42\xcb\xed\x37\x4d\x94\xd1" // Message + "\x36\xc6\xb8\x79\x5d\x45\xb3\x81\x98\x30\xf2\xc0\x44\x91\xfa\xf0" + "\x99\x0c\x62\xe4\x8b\x80\x18\xb2\xc3\xe4\xa0\xfa\x31\x34\xcb\x67" + "\xfa\x83\xe1\x58\xc9\x94\xd9\x61\xc4\xcb\x21\x09\x5c\x1b\xf9", + "\x9a\xe8\x31\xe7\x43\x97\x8d\x3a\x23\x52\x7c\x71\x28\x14\x9e\x3a", // Nonce + "\x51\x54\xad\x0d\x2c\xb2\x6e\x01\x27\x4f\xc5\x11\x48\x49\x1f\x1b", // Digest + 32, 63, 16, 16 + } + }; + + unsigned int count = 0; + byte digest[Poly1305::DIGESTSIZE]; + + // Positive tests + for (unsigned int i=0; i poly1305((const byte*)tests[i].key, tests[i].klen); + poly1305.Resynchronize((const byte*)tests[i].nonce, (int)tests[i].nlen); + poly1305.Update((const byte*)tests[i].message, tests[i].mlen); + poly1305.Final(digest); + + fail = memcmp(digest, tests[i].digest, tests[i].dlen) != 0; + if (fail) + { + cout << "FAILED " << "Poly1305 test set " << count << endl; + } + + count++; + pass = pass && !fail; + } + + // Positive tests + for (unsigned int i=0; i poly1305((const byte*)tests[i].key, tests[i].klen,(const byte*)tests[i].nonce, (int)tests[i].nlen); + poly1305.Update((const byte*)tests[i].message, tests[i].mlen); + poly1305.Final(digest); + + fail = memcmp(digest, tests[i].digest, tests[i].dlen) != 0; + if (fail) + { + cout << "FAILED " << "Poly1305 test set " << count << endl; + } + + count++; + pass = pass && !fail; + } + + // Negative tests + for (unsigned int i=0; i poly1305((const byte*)tests[i].key, tests[i].klen); + poly1305.Resynchronize((const byte*)tests[i].nonce, (int)tests[i].nlen); + poly1305.Update((const byte*)tests[i].message, tests[i].mlen); + poly1305.Final(digest); + + unsigned int next = (i+1) % COUNTOF(tests); + fail = memcmp(digest, tests[next].digest, tests[next].dlen) == 0; + if (fail) + { + cout << "FAILED " << "Poly1305 test set " << count << endl; + } + + count++; + pass = pass && !fail; + } + + cout << (!pass ? "FAILED " : "passed ") << count << " message authentication codes" << endl; + + return pass; +} + struct BLAKE2_TestTuples { const char *key, *message, *digest; @@ -1167,7 +1292,7 @@ bool ValidateBLAKE2s() pass = pass && !fail; } - cout << (fail ? "FAILED " : "passed ") << COUNTOF(tests) << " hashes and keyed hashes" << endl; + cout << (!pass ? "FAILED " : "passed ") << COUNTOF(tests) << " hashes and keyed hashes" << endl; return pass; } @@ -1562,7 +1687,7 @@ bool ValidateBLAKE2b() pass = pass && !fail; } - cout << (fail ? "FAILED " : "passed ") << COUNTOF(tests) << " hashes and keyed hashes" << endl; + cout << (!pass ? "FAILED " : "passed ") << COUNTOF(tests) << " hashes and keyed hashes" << endl; return pass; } diff --git a/validate.h b/validate.h index c3d22525..9b9124ef 100644 --- a/validate.h +++ b/validate.h @@ -29,6 +29,8 @@ bool ValidateTiger(); bool ValidateRIPEMD(); bool ValidatePanama(); bool ValidateWhirlpool(); + +bool ValidatePoly1305(); bool ValidateBLAKE2s(); bool ValidateBLAKE2b();