diff --git a/keccak.h b/keccak.h index 38e0b680..bbc357b0 100644 --- a/keccak.h +++ b/keccak.h @@ -93,7 +93,7 @@ private: // ensure there was no underflow in the math CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200); // this is a general expectation by HMAC - CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE > (int)T_DigestSize); + CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE > (int)DIGESTSIZE); #endif }; diff --git a/sha3.h b/sha3.h index 1c5d88bb..34de9c78 100644 --- a/sha3.h +++ b/sha3.h @@ -80,7 +80,7 @@ private: // ensure there was no underflow in the math CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200); // this is a general expectation by HMAC - CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE > (int)T_DigestSize); + CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE > (int)DIGESTSIZE); #endif }; diff --git a/shake.cpp b/shake.cpp index aa880389..65e08235 100644 --- a/shake.cpp +++ b/shake.cpp @@ -52,6 +52,13 @@ void SHAKE::Restart() m_counter = 0; } +void SHAKE::ThrowIfInvalidTruncatedSize(size_t size) const +{ + if (size > UINT_MAX) + throw InvalidArgument(std::string("HashTransformation: can't truncate a ") + + IntToString(UINT_MAX) + " byte digest to " + IntToString(size) + " bytes"); +} + void SHAKE::TruncatedFinal(byte *hash, size_t size) { CRYPTOPP_ASSERT(hash != NULLPTR); @@ -59,8 +66,19 @@ void SHAKE::TruncatedFinal(byte *hash, size_t size) m_state.BytePtr()[m_counter] ^= 0x1F; m_state.BytePtr()[r()-1] ^= 0x80; - KeccakF1600(m_state); - std::memcpy(hash, m_state, size); + + // FIPS 202, Algorithm 8, pp 18-19. + while (size > 0) + { + KeccakF1600(m_state); + + const size_t segmentLen = STDMIN(size, (size_t)BlockSize()); + std::memcpy(hash, m_state, segmentLen); + + hash += segmentLen; + size -= segmentLen; + } + Restart(); } diff --git a/shake.h b/shake.h index e9ce38a8..d044ebb1 100644 --- a/shake.h +++ b/shake.h @@ -47,6 +47,11 @@ public: protected: inline unsigned int r() const {return BlockSize();} + // SHAKE-128 and SHAKE-256 effectively allow unlimited + // output length. However, we use an unsigned int so + // we are limited in practice to UINT_MAX. + void ThrowIfInvalidTruncatedSize(size_t size) const; + FixedSizeSecBlock m_state; unsigned int m_digestSize, m_counter; }; @@ -64,7 +69,7 @@ public: { return "SHAKE-" + IntToString(T_Strength); } /// \brief Construct a SHAKE-X message digest - SHAKE_Final() : SHAKE(DIGESTSIZE) {} + SHAKE_Final(unsigned int outputSize=DIGESTSIZE) : SHAKE(outputSize) {} /// \brief Provides the block size of the compression function /// \return block size of the compression function, in bytes @@ -79,17 +84,41 @@ private: // ensure there was no underflow in the math CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200); // this is a general expectation by HMAC - CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE > (int)DIGESTSIZE); + CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE > (int)DIGESTSIZE); #endif }; /// \brief SHAKE128 message digest /// \since Crypto++ 8.1 -class SHAKE128 : public SHAKE_Final<128> {}; +class SHAKE128 : public SHAKE_Final<128> +{ +public: + /// \details Construct a SHAKE128 message digest + /// \details SHAKE128() uses the default output digest size + /// \since Crypto++ 8.1 + SHAKE128() {} + + /// \details Construct a SHAKE128 message digest + /// \details SHAKE128() uses outputSize digest size + /// \since Crypto++ 8.1 + SHAKE128(unsigned int outputSize) : SHAKE_Final(outputSize) {} +}; /// \brief SHAKE256 message digest /// \since Crypto++ 8.1 -class SHAKE256 : public SHAKE_Final<256> {}; +class SHAKE256 : public SHAKE_Final<256> +{ +public: + /// \details Construct a SHAKE256 message digest + /// \details SHAKE256() uses the default output digest size + /// \since Crypto++ 8.1 + SHAKE256() {} + + /// \details Construct a SHAKE256 message digest + /// \details SHAKE256() uses outputSize digest size + /// \since Crypto++ 8.1 + SHAKE256(unsigned int outputSize) : SHAKE_Final(outputSize) {} +}; NAMESPACE_END diff --git a/test.cpp b/test.cpp index 8226b975..5eb5dcbf 100644 --- a/test.cpp +++ b/test.cpp @@ -909,6 +909,7 @@ bool Validate(int alg, bool thorough, const char *seedInput) g_testBegin = ::time(NULLPTR); PrintSeedAndThreads(); + // TODO: we need to group these tests like benchmarks... switch (alg) { case 0: result = ValidateAll(thorough); break; @@ -1005,6 +1006,10 @@ bool Validate(int alg, bool thorough, const char *seedInput) case 102: result = ValidateSIMON(); break; case 103: result = ValidateSPECK(); break; + case 110: result = ValidateSHA3(); break; + case 111: result = ValidateSHAKE(); break; + case 112: result = ValidateSHAKE_XOF(); break; + #if defined(CRYPTOPP_EXTENDED_VALIDATION) // http://github.com/weidai11/cryptopp/issues/92 case 9999: result = TestSecBlock(); break; diff --git a/validat3.cpp b/validat3.cpp index 1dd9ac2e..8f6367fb 100644 --- a/validat3.cpp +++ b/validat3.cpp @@ -101,8 +101,10 @@ bool ValidateAll(bool thorough) pass=ValidateMD5() && pass; pass=ValidateSHA() && pass; - pass=RunTestDataFile("TestVectors/keccak.txt") && pass; - pass=RunTestDataFile("TestVectors/sha3.txt") && pass; + pass=ValidateKeccak() && pass; + pass=ValidateSHA3() && pass; + pass=ValidateSHAKE() && pass; + pass=ValidateSHAKE_XOF() && pass; pass=ValidateHashDRBG() && pass; pass=ValidateHmacDRBG() && pass; diff --git a/validat5.cpp b/validat5.cpp index e451c308..a2fa6642 100644 --- a/validat5.cpp +++ b/validat5.cpp @@ -20,7 +20,8 @@ #include "sha.h" #include "sha3.h" -#include "pssr.h" +#include "shake.h" +#include "keccak.h" #include "tiger.h" #include "blake2.h" #include "ripemd.h" @@ -28,6 +29,7 @@ #include "poly1305.h" #include "whrlpool.h" +#include "pssr.h" #include "hkdf.h" #include "scrypt.h" #include "pwdbased.h" @@ -223,12 +225,148 @@ bool ValidateSHA2() return RunTestDataFile("TestVectors/sha2.txt"); } +bool ValidateKeccak() +{ + std::cout << "\nKeccak validation suite running...\n"; + return RunTestDataFile("TestVectors/keccak.txt"); +} + bool ValidateSHA3() { std::cout << "\nSHA-3 validation suite running...\n"; return RunTestDataFile("TestVectors/sha3.txt"); } +bool ValidateSHAKE() +{ + std::cout << "\nSHAKE validation suite running...\n"; + return RunTestDataFile("TestVectors/shake.txt"); +} + +// We needed to hand craft the SHAKE tests because SHAKE128 and SHAKE256 +// use digestSize 'd' as an input parameter for the hash. When 'd > r', +// where 'r' is rate and acts like a blockSize, then TruncatedFinal acts +// like a traditional KDF and applies KeccakF1600 core function multiple +// times on state to create the stream. Regarding the NIST test vectors, +// the SHAKE128 KATs do not engage 'd > r'. However, the SHAKE256 KATs +// do engage it. +bool ValidateSHAKE_XOF() +{ + std::cout << "\nSHAKE XOF validation suite running...\n"; + bool pass = true, fail; + + ////// NIST test vectors SHAKE128VariableOut.rsp ////// + + // SHAKE128, COUNT = 0 (first test) + { + std::string m, msg = "84e950051876050dc851fbd99e6247b8"; + std::string o, out = "8599bd89f63a848c49ca593ec37a12c6"; + std::string r; + + StringSource(msg, true, new HexDecoder(new StringSink(m))); + StringSource(out, true, new HexDecoder(new StringSink(o))); + r.reserve(o.size()); + + SHAKE128 hash((unsigned int)r.size()); + hash.Update((const byte*)&m[0], m.size()); + hash.TruncatedFinal((byte*)&o[0], o.size()); + + fail = (std::memcmp(r.data(), r.data(), o.size()) != 0); + pass = pass & !fail; + + if (fail) + std::cout << "FAILED " << "SHAKE128 test COUNT=0" << std::endl; + + pass = pass && !fail; + } + + // SHAKE128, COUNT = 1125 (last test) + { + std::string m, msg = "0a13ad2c7a239b4ba73ea6592ae84ea9"; + std::string o, out = "5feaf99c15f48851943ff9baa6e5055d 8377f0dd347aa4dbece51ad3a6d9ce0c" + "01aee9fe2260b80a4673a909b532adcd d1e421c32d6460535b5fe392a58d2634" + "979a5a104d6c470aa3306c400b061db9 1c463b2848297bca2bc26d1864ba49d7" + "ff949ebca50fbf79a5e63716dc82b600 bd52ca7437ed774d169f6bf02e464879" + "56fba2230f34cd2a0485484d"; + std::string r; + + StringSource(msg, true, new HexDecoder(new StringSink(m))); + StringSource(out, true, new HexDecoder(new StringSink(o))); + r.reserve(o.size()); + + SHAKE128 hash((unsigned int)r.size()); + hash.Update((const byte*)&m[0], m.size()); + hash.TruncatedFinal((byte*)&o[0], o.size()); + + fail = (std::memcmp(r.data(), r.data(), o.size()) != 0); + pass = pass & !fail; + + if (fail) + std::cout << "FAILED " << "SHAKE128 test COUNT=1125" << std::endl; + + pass = pass && !fail; + } + + ////// NIST test vectors SHAKE256VariableOut.rsp ////// + + // SHAKE256, COUNT = 0 (first test) + { + std::string m, msg = "c61a9188812ae73994bc0d6d4021e31b f124dc72669749111232da7ac29e61c4"; + std::string o, out = "23ce"; + std::string r; + + StringSource(msg, true, new HexDecoder(new StringSink(m))); + StringSource(out, true, new HexDecoder(new StringSink(o))); + r.reserve(o.size()); + + SHAKE256 hash((unsigned int)r.size()); + hash.Update((const byte*)&m[0], m.size()); + hash.TruncatedFinal((byte*)&o[0], o.size()); + + fail = (std::memcmp(r.data(), r.data(), o.size()) != 0); + pass = pass & !fail; + + if (fail) + std::cout << "FAILED " << "SHAKE256 test COUNT=0" << std::endl; + + pass = pass && !fail; + } + + // SHAKE256, COUNT = 1245 (last test) + { + std::string m, msg = "8d8001e2c096f1b88e7c9224a086efd4 797fbf74a8033a2d422a2b6b8f6747e4"; + std::string o, out = "2e975f6a8a14f0704d51b13667d8195c 219f71e6345696c49fa4b9d08e9225d3" + "d39393425152c97e71dd24601c11abcf a0f12f53c680bd3ae757b8134a9c10d4" + "29615869217fdd5885c4db174985703a 6d6de94a667eac3023443a8337ae1bc6" + "01b76d7d38ec3c34463105f0d3949d78 e562a039e4469548b609395de5a4fd43" + "c46ca9fd6ee29ada5efc07d84d553249 450dab4a49c483ded250c9338f85cd93" + "7ae66bb436f3b4026e859fda1ca57143 2f3bfc09e7c03ca4d183b741111ca048" + "3d0edabc03feb23b17ee48e844ba2408 d9dcfd0139d2e8c7310125aee801c61a" + "b7900d1efc47c078281766f361c5e611 1346235e1dc38325666c"; + std::string r; + + StringSource(msg, true, new HexDecoder(new StringSink(m))); + StringSource(out, true, new HexDecoder(new StringSink(o))); + r.reserve(o.size()); + + SHAKE256 hash((unsigned int)r.size()); + hash.Update((const byte*)&m[0], m.size()); + hash.TruncatedFinal((byte*)&o[0], o.size()); + + fail = (std::memcmp(r.data(), r.data(), o.size()) != 0); + pass = pass & !fail; + + if (fail) + std::cout << "FAILED " << "SHAKE256 test COUNT=0" << std::endl; + + pass = pass && !fail; + } + + std::cout << (!pass ? "FAILED " : "passed ") << " SHAKE XOF message digests" << std::endl; + + return pass; +} + bool ValidateTiger() { std::cout << "\nTiger validation suite running...\n\n"; diff --git a/validate.h b/validate.h index d4c35d2c..37ab53d5 100644 --- a/validate.h +++ b/validate.h @@ -52,6 +52,10 @@ bool ValidateMD4(); bool ValidateMD5(); bool ValidateSHA(); bool ValidateSHA2(); +bool ValidateSHA3(); +bool ValidateSHAKE(); // output <= r, where r is blocksize +bool ValidateSHAKE_XOF(); // output > r, needs hand crafted tests +bool ValidateKeccak(); bool ValidateTiger(); bool ValidateRIPEMD(); bool ValidatePanama();