From 9225ca09cb33711278cfb9d5cf8d87fdb2c1c460 Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Fri, 5 May 2017 19:00:17 -0400 Subject: [PATCH] Updated MersenneTwister tests The tests now include the first 10 elements of the sequence to ensure a properly implemented algorithm and endianess correctness. --- mersenne.h | 58 ++++++++++++++++++---------- validat0.cpp | 5 --- validat1.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 130 insertions(+), 39 deletions(-) diff --git a/mersenne.h b/mersenne.h index 4937d9d0..d5bd039b 100644 --- a/mersenne.h +++ b/mersenne.h @@ -26,7 +26,7 @@ NAMESPACE_BEGIN(CryptoPP) //! required quickly. It should not be used for cryptographic purposes. //! \sa MT19937, MT19937ar //! \since Crypto++ 5.6.3 -template +template class MersenneTwister : public RandomNumberGenerator { public: @@ -38,11 +38,26 @@ public: //! \param seed 32-bit seed //! \details Defaults to template parameter S due to changing algorithm //! parameters over time - MersenneTwister(unsigned long seed = S) : m_seed(seed), m_idx(N) + MersenneTwister(word32 seed = S) : m_seed(seed), m_idx(N) { - m_state[0] = seed; - for (unsigned int i = 1; i < N+1; i++) - m_state[i] = word32(F * (m_state[i-1] ^ (m_state[i-1] >> 30)) + i); + Reset(seed); + } + + bool CanIncorporateEntropy() const {return true;} + + //! \brief Update RNG state with additional unpredictable values + //! \param input the entropy to add to the generator + //! \param length the size of the input buffer + //! \details MersenneTwister uses the first 32-bits of input to reseed the + //! generator. If fewer bytes are provided, then the seed is padded with 0's. + void IncorporateEntropy(const byte *input, size_t length) + { + word32 temp = 0; + ::memcpy(&temp, input, STDMIN(sizeof(temp), length)); + Reset(temp); + + // Wipe temp + SecureWipeArray(&temp, 1); } //! \brief Generate random array of bytes @@ -58,24 +73,15 @@ public: word32 temp; for (size_t i=0; i < size/4; i++, output += 4) { -#if defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) && defined(IS_LITTLE_ENDIAN) - *((word32*)output) = ByteReverse(NextMersenneWord()); -#elif defined(CRYPTOPP_ALLOW_UNALIGNED_DATA_ACCESS) - *((word32*)output) = NextMersenneWord(); -#else temp = NextMersenneWord(); - output[3] = CRYPTOPP_GET_BYTE_AS_BYTE(temp, 0); - output[2] = CRYPTOPP_GET_BYTE_AS_BYTE(temp, 1); - output[1] = CRYPTOPP_GET_BYTE_AS_BYTE(temp, 2); - output[0] = CRYPTOPP_GET_BYTE_AS_BYTE(temp, 3); -#endif + PutWord(false, LITTLE_ENDIAN_ORDER, output, temp); } // No tail bytes if (size%4 == 0) { // Wipe temp - *((volatile word32*)&temp) = 0; + SecureWipeArray(&temp, 1); return; } @@ -91,7 +97,7 @@ public: } // Wipe temp - *((volatile word32*)&temp) = 0; + SecureWipeArray(&temp, 1); } //! \brief Generate a random 32-bit word in the range min to max, inclusive @@ -128,6 +134,16 @@ public: protected: + void Reset(word32 seed) + { + m_seed = seed; + m_idx = N; + + m_state[0] = seed; + for (unsigned int i = 1; i < N+1; i++) + m_state[i] = word32(F * (m_state[i-1] ^ (m_state[i-1] >> 30)) + i); + } + //! \brief Returns the next 32-bit word from the state array //! \returns the next 32-bit word from the state array //! \details fetches the next word frm the state array, performs bit operations on @@ -148,7 +164,7 @@ protected: //! \brief Performs the twist operaton on the state array void Twist() { - static const unsigned long magic[2]={0x0UL, K}; + static const word32 magic[2]={0x0UL, K}; word32 kk, temp; CRYPTOPP_ASSERT(N >= M); @@ -171,7 +187,7 @@ protected: m_idx = 0; // Wipe temp - *((volatile word32*)&temp) = 0; + SecureWipeArray(&temp, 1); } private: @@ -179,9 +195,9 @@ private: //! \brief 32-bit word state array of size N FixedSizeSecBlock m_state; //! \brief the value used to seed the generator - unsigned int m_seed; + word32 m_seed; //! \brief the current index into the state array - unsigned int m_idx; + word32 m_idx; }; //! \class MT19937 diff --git a/validat0.cpp b/validat0.cpp index ae8c8fc7..8fd86932 100644 --- a/validat0.cpp +++ b/validat0.cpp @@ -98,11 +98,6 @@ bool TestZinflate() return !fail; } -bool TestMersenne() -{ - return true; -} - bool TestDefaultEncryptor() { std::cout << "\nTesting DefaultEncryptor...\n\n"; diff --git a/validat1.cpp b/validat1.cpp index 15743f12..2dc48572 100644 --- a/validat1.cpp +++ b/validat1.cpp @@ -45,6 +45,7 @@ #include "osrng.h" #include "drbg.h" #include "rdrand.h" +#include "mersenne.h" #include "zdeflate.h" #include "smartptr.h" #include "channels.h" @@ -72,7 +73,9 @@ bool ValidateAll(bool thorough) pass=TestAutoSeeded() && pass; pass=TestAutoSeededX917() && pass; // pass=TestSecRandom() && pass; - +#if (defined(CRYPTOPP_DEBUG) || defined(CRYPTOPP_COVERAGE)) && !defined(CRYPTOPP_IMPORTS) + pass=TestMersenne() && pass; +#endif #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) pass=TestRDRAND() && pass; pass=TestRDSEED() && pass; @@ -96,7 +99,6 @@ bool ValidateAll(bool thorough) // Additional tests due to no coverage pass=TestGzip() && pass; pass=TestZinflate() && pass; - pass=TestMersenne() && pass; pass=TestDefaultEncryptor() && pass; pass=TestDefaultEncryptorWithMAC() && pass; pass=TestLegacyEncryptor() && pass; @@ -449,7 +451,7 @@ bool TestOS_RNG() } else std::cout << "passed:"; - std::cout << " " << total << " generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE" << std::endl; + std::cout << " " << total << " generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE\n"; } else std::cout << "\nNo operating system provided blocking random number generator, skipping test." << std::endl; @@ -474,7 +476,7 @@ bool TestOS_RNG() } else std::cout << "passed:"; - std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE" << std::endl; + std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE\n"; } else std::cout << "\nNo operating system provided nonblocking random number generator, skipping test." << std::endl; @@ -511,7 +513,7 @@ bool TestAutoSeeded() } else std::cout << "passed:"; - std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE" << std::endl; + std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE\n"; try { @@ -575,7 +577,7 @@ bool TestAutoSeededX917() } else std::cout << "passed:"; - std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE" << std::endl; + std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE\n"; try { @@ -621,6 +623,90 @@ bool TestAutoSeededX917() } #endif // NO_OS_DEPENDENCE +#if (defined(CRYPTOPP_DEBUG) || defined(CRYPTOPP_COVERAGE)) && !defined(CRYPTOPP_IMPORTS) +bool TestMersenne() +{ + std::cout << "\nTesting Mersenne Twister...\n\n"; + + static const unsigned int ENTROPY_SIZE = 32; + bool equal = true, generate = true, discard = true, incorporate = false; + + // First 10; http://create.stephan-brumme.com/mersenne-twister/ + word32 result[10], expected[10] = {0xD091BB5C, 0x22AE9EF6, + 0xE7E1FAEE, 0xD5C31F79, 0x2082352C, 0xF807B7DF, 0xE9D30005, + 0x3895AFE1, 0xA1E24BBA, 0x4EE4092B}; + + MT19937ar prng; + prng.GenerateBlock(reinterpret_cast(result), sizeof(result)); + equal = (0 == ::memcmp(result, expected, sizeof(expected))); + + if (equal) + { + std::cout << "passed:"; + } + else + { + std::cout << "FAILED:"; + equal = false; + } + std::cout << " Expected sequence from MT19937ar (2002 version)\n"; + + MeterFilter meter(new Redirector(TheBitBucket())); + RandomNumberSource test(prng, 100000, true, new Deflator(new Redirector(meter))); + + if (meter.GetTotalBytes() < 100000) + { + std::cout << "FAILED:"; + generate = false; + } + else + std::cout << "passed:"; + std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE\n"; + + try + { + prng.DiscardBytes(100000); + } + catch(const Exception&) + { + discard = false; + } + + if (!discard) + std::cout << "FAILED:"; + else + std::cout << "passed:"; + std::cout << " discarded 10000 bytes\n"; + + try + { + if(prng.CanIncorporateEntropy()) + { + SecByteBlock entropy(ENTROPY_SIZE); + OS_GenerateRandomBlock(false, entropy, entropy.SizeInBytes()); + + prng.IncorporateEntropy(entropy, entropy.SizeInBytes()); + prng.IncorporateEntropy(entropy, entropy.SizeInBytes()); + prng.IncorporateEntropy(entropy, entropy.SizeInBytes()); + prng.IncorporateEntropy(entropy, entropy.SizeInBytes()); + + incorporate = true; + } + } + catch(const Exception& /*ex*/) + { + } + + if (!incorporate) + std::cout << "FAILED:"; + else + std::cout << "passed:"; + std::cout << " IncorporateEntropy with " << 4*ENTROPY_SIZE << " bytes" << std::endl; + + return equal && generate && discard && incorporate; +} +#endif + #if (CRYPTOPP_BOOL_X86 || CRYPTOPP_BOOL_X32 || CRYPTOPP_BOOL_X64) bool TestRDRAND() { @@ -657,7 +743,7 @@ bool TestRDRAND() // Coverity finding, also see http://stackoverflow.com/a/34509163/608639. StreamState ss(std::cout); std::cout << std::setiosflags(std::ios::fixed) << std::setprecision(6); - std::cout << " Maurer Randomness Test returned value " << mv << std::endl; + std::cout << " Maurer Randomness Test returned value " << mv << "\n"; if (meter.GetTotalBytes() < SIZE) { @@ -691,9 +777,6 @@ bool TestRDRAND() (void)rdrand.CanIncorporateEntropy(); rdrand.IncorporateEntropy(NULLPTR, 0); - if (!(entropy && compress && discard)) - std::cout.flush(); - return entropy && compress && discard; } #endif @@ -769,9 +852,6 @@ bool TestRDSEED() (void)rdseed.CanIncorporateEntropy(); rdseed.IncorporateEntropy(NULLPTR, 0); - if (!(entropy && compress && discard)) - std::cout.flush(); - return entropy && compress && discard; } #endif