Updated MersenneTwister tests

The tests now include the first 10 elements of the sequence to ensure a properly implemented algorithm and endianess correctness.
This commit is contained in:
Jeffrey Walton 2017-05-05 19:00:17 -04:00
parent c1377b2955
commit 9225ca09cb
No known key found for this signature in database
GPG Key ID: B36AB348921B1838
3 changed files with 130 additions and 39 deletions

View File

@ -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 <unsigned int K, unsigned int M, unsigned int N, unsigned int F, unsigned long S>
template <unsigned int K, unsigned int M, unsigned int N, unsigned int F, word32 S>
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 <tt>input</tt> 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<word32>(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<word32, N+1> 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

View File

@ -98,11 +98,6 @@ bool TestZinflate()
return !fail;
}
bool TestMersenne()
{
return true;
}
bool TestDefaultEncryptor()
{
std::cout << "\nTesting DefaultEncryptor...\n\n";

View File

@ -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<byte*>(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