Enable extended output in SHAKE-128 and SHAKE-256 (GH #805, PR #806)

pull/809/head
Jeffrey Walton 2019-02-13 11:31:18 -05:00
parent 2e440959b1
commit 00f9c1f0eb
No known key found for this signature in database
GPG Key ID: B36AB348921B1838
8 changed files with 207 additions and 11 deletions

View File

@ -93,7 +93,7 @@ private:
// ensure there was no underflow in the math // ensure there was no underflow in the math
CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200); CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200);
// this is a general expectation by HMAC // this is a general expectation by HMAC
CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE > (int)T_DigestSize); CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE > (int)DIGESTSIZE);
#endif #endif
}; };

2
sha3.h
View File

@ -80,7 +80,7 @@ private:
// ensure there was no underflow in the math // ensure there was no underflow in the math
CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200); CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200);
// this is a general expectation by HMAC // this is a general expectation by HMAC
CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE > (int)T_DigestSize); CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE > (int)DIGESTSIZE);
#endif #endif
}; };

View File

@ -52,6 +52,13 @@ void SHAKE::Restart()
m_counter = 0; 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) void SHAKE::TruncatedFinal(byte *hash, size_t size)
{ {
CRYPTOPP_ASSERT(hash != NULLPTR); 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()[m_counter] ^= 0x1F;
m_state.BytePtr()[r()-1] ^= 0x80; m_state.BytePtr()[r()-1] ^= 0x80;
// FIPS 202, Algorithm 8, pp 18-19.
while (size > 0)
{
KeccakF1600(m_state); KeccakF1600(m_state);
std::memcpy(hash, m_state, size);
const size_t segmentLen = STDMIN(size, (size_t)BlockSize());
std::memcpy(hash, m_state, segmentLen);
hash += segmentLen;
size -= segmentLen;
}
Restart(); Restart();
} }

37
shake.h
View File

@ -47,6 +47,11 @@ public:
protected: protected:
inline unsigned int r() const {return BlockSize();} 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<word64, 25> m_state; FixedSizeSecBlock<word64, 25> m_state;
unsigned int m_digestSize, m_counter; unsigned int m_digestSize, m_counter;
}; };
@ -64,7 +69,7 @@ public:
{ return "SHAKE-" + IntToString(T_Strength); } { return "SHAKE-" + IntToString(T_Strength); }
/// \brief Construct a SHAKE-X message digest /// \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 /// \brief Provides the block size of the compression function
/// \return block size of the compression function, in bytes /// \return block size of the compression function, in bytes
@ -79,17 +84,41 @@ private:
// ensure there was no underflow in the math // ensure there was no underflow in the math
CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200); CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE < 200);
// this is a general expectation by HMAC // this is a general expectation by HMAC
CRYPTOPP_COMPILE_ASSERT(BLOCKSIZE > (int)DIGESTSIZE); CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE > (int)DIGESTSIZE);
#endif #endif
}; };
/// \brief SHAKE128 message digest /// \brief SHAKE128 message digest
/// \since Crypto++ 8.1 /// \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 <tt>outputSize</tt> digest size
/// \since Crypto++ 8.1
SHAKE128(unsigned int outputSize) : SHAKE_Final(outputSize) {}
};
/// \brief SHAKE256 message digest /// \brief SHAKE256 message digest
/// \since Crypto++ 8.1 /// \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 <tt>outputSize</tt> digest size
/// \since Crypto++ 8.1
SHAKE256(unsigned int outputSize) : SHAKE_Final(outputSize) {}
};
NAMESPACE_END NAMESPACE_END

View File

@ -909,6 +909,7 @@ bool Validate(int alg, bool thorough, const char *seedInput)
g_testBegin = ::time(NULLPTR); g_testBegin = ::time(NULLPTR);
PrintSeedAndThreads(); PrintSeedAndThreads();
// TODO: we need to group these tests like benchmarks...
switch (alg) switch (alg)
{ {
case 0: result = ValidateAll(thorough); break; 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 102: result = ValidateSIMON(); break;
case 103: result = ValidateSPECK(); 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) #if defined(CRYPTOPP_EXTENDED_VALIDATION)
// http://github.com/weidai11/cryptopp/issues/92 // http://github.com/weidai11/cryptopp/issues/92
case 9999: result = TestSecBlock(); break; case 9999: result = TestSecBlock(); break;

View File

@ -101,8 +101,10 @@ bool ValidateAll(bool thorough)
pass=ValidateMD5() && pass; pass=ValidateMD5() && pass;
pass=ValidateSHA() && pass; pass=ValidateSHA() && pass;
pass=RunTestDataFile("TestVectors/keccak.txt") && pass; pass=ValidateKeccak() && pass;
pass=RunTestDataFile("TestVectors/sha3.txt") && pass; pass=ValidateSHA3() && pass;
pass=ValidateSHAKE() && pass;
pass=ValidateSHAKE_XOF() && pass;
pass=ValidateHashDRBG() && pass; pass=ValidateHashDRBG() && pass;
pass=ValidateHmacDRBG() && pass; pass=ValidateHmacDRBG() && pass;

View File

@ -20,7 +20,8 @@
#include "sha.h" #include "sha.h"
#include "sha3.h" #include "sha3.h"
#include "pssr.h" #include "shake.h"
#include "keccak.h"
#include "tiger.h" #include "tiger.h"
#include "blake2.h" #include "blake2.h"
#include "ripemd.h" #include "ripemd.h"
@ -28,6 +29,7 @@
#include "poly1305.h" #include "poly1305.h"
#include "whrlpool.h" #include "whrlpool.h"
#include "pssr.h"
#include "hkdf.h" #include "hkdf.h"
#include "scrypt.h" #include "scrypt.h"
#include "pwdbased.h" #include "pwdbased.h"
@ -223,12 +225,148 @@ bool ValidateSHA2()
return RunTestDataFile("TestVectors/sha2.txt"); return RunTestDataFile("TestVectors/sha2.txt");
} }
bool ValidateKeccak()
{
std::cout << "\nKeccak validation suite running...\n";
return RunTestDataFile("TestVectors/keccak.txt");
}
bool ValidateSHA3() bool ValidateSHA3()
{ {
std::cout << "\nSHA-3 validation suite running...\n"; std::cout << "\nSHA-3 validation suite running...\n";
return RunTestDataFile("TestVectors/sha3.txt"); 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() bool ValidateTiger()
{ {
std::cout << "\nTiger validation suite running...\n\n"; std::cout << "\nTiger validation suite running...\n\n";

View File

@ -52,6 +52,10 @@ bool ValidateMD4();
bool ValidateMD5(); bool ValidateMD5();
bool ValidateSHA(); bool ValidateSHA();
bool ValidateSHA2(); 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 ValidateTiger();
bool ValidateRIPEMD(); bool ValidateRIPEMD();
bool ValidatePanama(); bool ValidatePanama();