reduce risk of random number reuse after VM rollback

pull/2/head
weidai 2007-05-04 15:04:58 +00:00
parent c09618a557
commit f9261eb107
6 changed files with 111 additions and 121 deletions

View File

@ -4,7 +4,6 @@
#include "hrtimer.h" #include "hrtimer.h"
#include "misc.h" #include "misc.h"
#include <stddef.h> // for NULL #include <stddef.h> // for NULL
#include <time.h>
#if defined(CRYPTOPP_WIN32_AVAILABLE) #if defined(CRYPTOPP_WIN32_AVAILABLE)
#include <windows.h> #include <windows.h>
@ -18,6 +17,8 @@
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)
#ifndef CRYPTOPP_IMPORTS
double TimerBase::ConvertTo(TimerWord t, Unit unit) double TimerBase::ConvertTo(TimerWord t, Unit unit)
{ {
static unsigned long unitsPerSecondTable[] = {1, 1000, 1000*1000, 1000*1000*1000}; static unsigned long unitsPerSecondTable[] = {1, 1000, 1000*1000, 1000*1000*1000};
@ -56,6 +57,46 @@ unsigned long TimerBase::ElapsedTime()
return (unsigned long)elapsed; return (unsigned long)elapsed;
} }
TimerWord Timer::GetCurrentTimerValue()
{
#if defined(CRYPTOPP_WIN32_AVAILABLE)
LARGE_INTEGER now;
if (!QueryPerformanceCounter(&now))
throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceCounter failed with error " + IntToString(GetLastError()));
return now.QuadPart;
#elif defined(CRYPTOPP_UNIX_AVAILABLE)
timeval now;
gettimeofday(&now, NULL);
return (TimerWord)now.tv_sec * 1000000 + now.tv_usec;
#else
clock_t now;
return clock();
#endif
}
TimerWord Timer::TicksPerSecond()
{
#if defined(CRYPTOPP_WIN32_AVAILABLE)
static LARGE_INTEGER freq = {0};
if (freq.QuadPart == 0)
{
if (!QueryPerformanceFrequency(&freq))
throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceFrequency failed with error " + IntToString(GetLastError()));
}
return freq.QuadPart;
#elif defined(CRYPTOPP_UNIX_AVAILABLE)
return 1000000;
#elif defined(CLOCKS_PER_SEC)
return CLOCKS_PER_SEC;
#elif defined(CLK_TCK)
return CLK_TCK;
#else
return 1000000;
#endif
}
#endif // #ifndef CRYPTOPP_IMPORTS
TimerWord ThreadUserTimer::GetCurrentTimerValue() TimerWord ThreadUserTimer::GetCurrentTimerValue()
{ {
#if defined(CRYPTOPP_WIN32_AVAILABLE) #if defined(CRYPTOPP_WIN32_AVAILABLE)
@ -98,37 +139,4 @@ TimerWord ThreadUserTimer::TicksPerSecond()
#endif #endif
} }
#ifdef HIGHRES_TIMER_AVAILABLE
TimerWord Timer::GetCurrentTimerValue()
{
#if defined(CRYPTOPP_WIN32_AVAILABLE)
LARGE_INTEGER now;
if (!QueryPerformanceCounter(&now))
throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceCounter failed with error " + IntToString(GetLastError()));
return now.QuadPart;
#elif defined(CRYPTOPP_UNIX_AVAILABLE)
timeval now;
gettimeofday(&now, NULL);
return (TimerWord)now.tv_sec * 1000000 + now.tv_usec;
#endif
}
TimerWord Timer::TicksPerSecond()
{
#if defined(CRYPTOPP_WIN32_AVAILABLE)
static LARGE_INTEGER freq = {0};
if (freq.QuadPart == 0)
{
if (!QueryPerformanceFrequency(&freq))
throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceFrequency failed with error " + IntToString(GetLastError()));
}
return freq.QuadPart;
#elif defined(CRYPTOPP_UNIX_AVAILABLE)
return 1000000;
#endif
}
#endif // HIGHRES_TIMER_AVAILABLE
NAMESPACE_END NAMESPACE_END

View File

@ -5,14 +5,19 @@
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)
#ifdef HIGHRES_TIMER_AVAILABLE
#ifdef WORD64_AVAILABLE #ifdef WORD64_AVAILABLE
typedef word64 TimerWord; typedef word64 TimerWord;
#else #else
typedef word32 TimerWord; typedef word32 TimerWord;
#endif #endif
#else
#include <time.h>
typedef clock_t TimerWord;
#endif
//! _ //! _
class TimerBase class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TimerBase
{ {
public: public:
enum Unit {SECONDS = 0, MILLISECONDS, MICROSECONDS, NANOSECONDS}; enum Unit {SECONDS = 0, MILLISECONDS, MICROSECONDS, NANOSECONDS};
@ -44,10 +49,8 @@ public:
TimerWord TicksPerSecond(); TimerWord TicksPerSecond();
}; };
#ifdef HIGHRES_TIMER_AVAILABLE
//! high resolution timer //! high resolution timer
class Timer : public TimerBase class CRYPTOPP_DLL Timer : public TimerBase
{ {
public: public:
Timer(Unit unit = TimerBase::SECONDS, bool stuckAtZero = false) : TimerBase(unit, stuckAtZero) {} Timer(Unit unit = TimerBase::SECONDS, bool stuckAtZero = false) : TimerBase(unit, stuckAtZero) {}
@ -55,8 +58,6 @@ public:
TimerWord TicksPerSecond(); TimerWord TicksPerSecond();
}; };
#endif // HIGHRES_TIMER_AVAILABLE
NAMESPACE_END NAMESPACE_END
#endif #endif

View File

@ -74,13 +74,6 @@ NonblockingRng::~NonblockingRng()
#endif #endif
} }
byte NonblockingRng::GenerateByte()
{
byte b;
GenerateBlock(&b, 1);
return b;
}
void NonblockingRng::GenerateBlock(byte *output, size_t size) void NonblockingRng::GenerateBlock(byte *output, size_t size)
{ {
#ifdef CRYPTOPP_WIN32_AVAILABLE #ifdef CRYPTOPP_WIN32_AVAILABLE
@ -121,13 +114,6 @@ BlockingRng::~BlockingRng()
close(m_fd); close(m_fd);
} }
byte BlockingRng::GenerateByte()
{
byte b;
GenerateBlock(&b, 1);
return b;
}
void BlockingRng::GenerateBlock(byte *output, size_t size) void BlockingRng::GenerateBlock(byte *output, size_t size)
{ {
while (size) while (size)
@ -175,7 +161,7 @@ void AutoSeededRandomPool::Reseed(bool blocking, unsigned int seedSize)
{ {
SecByteBlock seed(seedSize); SecByteBlock seed(seedSize);
OS_GenerateRandomBlock(blocking, seed, seedSize); OS_GenerateRandomBlock(blocking, seed, seedSize);
Put(seed, seedSize); IncorporateEntropy(seed, seedSize);
} }
NAMESPACE_END NAMESPACE_END

54
osrng.h
View File

@ -7,7 +7,7 @@
#include "randpool.h" #include "randpool.h"
#include "rng.h" #include "rng.h"
#include "des.h" #include "aes.h"
#include "fips140.h" #include "fips140.h"
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)
@ -46,7 +46,6 @@ class CRYPTOPP_DLL NonblockingRng : public RandomNumberGenerator
public: public:
NonblockingRng(); NonblockingRng();
~NonblockingRng(); ~NonblockingRng();
byte GenerateByte();
void GenerateBlock(byte *output, size_t size); void GenerateBlock(byte *output, size_t size);
protected: protected:
@ -69,7 +68,6 @@ class CRYPTOPP_DLL BlockingRng : public RandomNumberGenerator
public: public:
BlockingRng(); BlockingRng();
~BlockingRng(); ~BlockingRng();
byte GenerateByte();
void GenerateBlock(byte *output, size_t size); void GenerateBlock(byte *output, size_t size);
protected: protected:
@ -99,39 +97,39 @@ public:
//! use blocking to choose seeding with BlockingRng or NonblockingRng. the parameter is ignored if only one of these is available //! use blocking to choose seeding with BlockingRng or NonblockingRng. the parameter is ignored if only one of these is available
explicit AutoSeededX917RNG(bool blocking = false) explicit AutoSeededX917RNG(bool blocking = false)
{Reseed(blocking);} {Reseed(blocking);}
void Reseed(bool blocking = false); void Reseed(bool blocking = false, const byte *additionalEntropy = NULL, size_t length = 0);
// exposed for testing // exposed for testing
void Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector); void Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector);
byte GenerateByte(); bool CanIncorporateEntropy() const {return true;}
void IncorporateEntropy(const byte *input, size_t length) {Reseed(false, input, length);}
void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword length) {m_rng->GenerateIntoBufferedTransformation(target, channel, length);}
private: private:
member_ptr<RandomNumberGenerator> m_rng; member_ptr<RandomNumberGenerator> m_rng;
SecByteBlock m_lastBlock;
bool m_isDifferent;
unsigned int m_counter;
}; };
template <class BLOCK_CIPHER> template <class BLOCK_CIPHER>
void AutoSeededX917RNG<BLOCK_CIPHER>::Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector) void AutoSeededX917RNG<BLOCK_CIPHER>::Reseed(const byte *key, size_t keylength, const byte *seed, const byte *timeVector)
{ {
m_rng.reset(new X917RNG(new typename BLOCK_CIPHER::Encryption(key, keylength), seed, timeVector)); m_rng.reset(new X917RNG(new typename BLOCK_CIPHER::Encryption(key, keylength), seed, timeVector));
// for FIPS 140-2
m_lastBlock.resize(16);
m_rng->GenerateBlock(m_lastBlock, m_lastBlock.size());
m_counter = 0;
m_isDifferent = false;
} }
template <class BLOCK_CIPHER> template <class BLOCK_CIPHER>
void AutoSeededX917RNG<BLOCK_CIPHER>::Reseed(bool blocking) void AutoSeededX917RNG<BLOCK_CIPHER>::Reseed(bool blocking, const byte *input, size_t length)
{ {
SecByteBlock seed(BLOCK_CIPHER::BLOCKSIZE + BLOCK_CIPHER::DEFAULT_KEYLENGTH); SecByteBlock seed(BLOCK_CIPHER::BLOCKSIZE + BLOCK_CIPHER::DEFAULT_KEYLENGTH);
const byte *key; const byte *key;
do do
{ {
OS_GenerateRandomBlock(blocking, seed, seed.size()); OS_GenerateRandomBlock(blocking, seed, seed.size());
if (length > 0)
{
SHA256 hash;
hash.Update(seed, seed.size());
hash.Update(input, length);
hash.TruncatedFinal(seed, UnsignedMin(hash.DigestSize(), seed.size()));
}
key = seed + BLOCK_CIPHER::BLOCKSIZE; key = seed + BLOCK_CIPHER::BLOCKSIZE;
} // check that seed and key don't have same value } // check that seed and key don't have same value
while (memcmp(key, seed, STDMIN((unsigned int)BLOCK_CIPHER::BLOCKSIZE, (unsigned int)BLOCK_CIPHER::DEFAULT_KEYLENGTH)) == 0); while (memcmp(key, seed, STDMIN((unsigned int)BLOCK_CIPHER::BLOCKSIZE, (unsigned int)BLOCK_CIPHER::DEFAULT_KEYLENGTH)) == 0);
@ -139,27 +137,13 @@ void AutoSeededX917RNG<BLOCK_CIPHER>::Reseed(bool blocking)
Reseed(key, BLOCK_CIPHER::DEFAULT_KEYLENGTH, seed, NULL); Reseed(key, BLOCK_CIPHER::DEFAULT_KEYLENGTH, seed, NULL);
} }
template <class BLOCK_CIPHER> CRYPTOPP_DLL_TEMPLATE_CLASS AutoSeededX917RNG<AES>;
byte AutoSeededX917RNG<BLOCK_CIPHER>::GenerateByte()
{
byte b = m_rng->GenerateByte();
// for FIPS 140-2 #if CRYPTOPP_ENABLE_COMPLIANCE_WITH_FIPS_140_2
m_isDifferent = m_isDifferent || b != m_lastBlock[m_counter]; typedef AutoSeededX917RNG<AES> DefaultAutoSeededRNG;
m_lastBlock[m_counter] = b; #else
++m_counter; typedef AutoSeededRandomPool DefaultAutoSeededRNG;
if (m_counter == m_lastBlock.size()) #endif
{
if (!m_isDifferent)
throw SelfTestFailure("AutoSeededX917RNG: Continuous random number generator test failed.");
m_counter = 0;
m_isDifferent = false;
}
return b;
}
CRYPTOPP_DLL_TEMPLATE_CLASS AutoSeededX917RNG<DES_EDE3>;
NAMESPACE_END NAMESPACE_END

44
rng.cpp
View File

@ -3,6 +3,7 @@
#include "pch.h" #include "pch.h"
#include "rng.h" #include "rng.h"
#include "fips140.h"
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
@ -35,7 +36,9 @@ const word16 LC_RNG::a=16807;
const word16 LC_RNG::r=2836; const word16 LC_RNG::r=2836;
#endif #endif
byte LC_RNG::GenerateByte() void LC_RNG::GenerateBlock(byte *output, size_t size)
{
while (size--)
{ {
word32 hi = seed/q; word32 hi = seed/q;
word32 lo = seed%q; word32 lo = seed%q;
@ -47,7 +50,8 @@ byte LC_RNG::GenerateByte()
else else
seed = test+ m; seed = test+ m;
return (GETBYTE(seed, 0) ^ GETBYTE(seed, 1) ^ GETBYTE(seed, 2) ^ GETBYTE(seed, 3)); *output++ = (GETBYTE(seed, 0) ^ GETBYTE(seed, 1) ^ GETBYTE(seed, 2) ^ GETBYTE(seed, 3));
}
} }
// ******************************************************** // ********************************************************
@ -59,24 +63,26 @@ X917RNG::X917RNG(BlockTransformation *c, const byte *seed, const byte *determini
S(cipher->BlockSize()), S(cipher->BlockSize()),
dtbuf(S), dtbuf(S),
randseed(seed, S), randseed(seed, S),
randbuf(S), m_lastBlock(S),
randbuf_counter(0),
m_deterministicTimeVector(deterministicTimeVector, deterministicTimeVector ? S : 0) m_deterministicTimeVector(deterministicTimeVector, deterministicTimeVector ? S : 0)
{ {
if (!deterministicTimeVector) if (!deterministicTimeVector)
{ {
time_t tstamp1 = time(0); time_t tstamp1 = time(0);
xorbuf(dtbuf, (byte *)&tstamp1, STDMIN((int)sizeof(tstamp1), S)); xorbuf(dtbuf, (byte *)&tstamp1, UnsignedMin(sizeof(tstamp1), S));
cipher->ProcessBlock(dtbuf); cipher->ProcessBlock(dtbuf);
clock_t tstamp2 = clock(); clock_t tstamp2 = clock();
xorbuf(dtbuf, (byte *)&tstamp2, STDMIN((int)sizeof(tstamp2), S)); xorbuf(dtbuf, (byte *)&tstamp2, UnsignedMin(sizeof(tstamp2), S));
cipher->ProcessBlock(dtbuf); cipher->ProcessBlock(dtbuf);
} }
// for FIPS 140-2
GenerateBlock(m_lastBlock, S);
} }
byte X917RNG::GenerateByte() void X917RNG::GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size)
{ {
if (randbuf_counter==0) while (size > 0)
{ {
// calculate new enciphered timestamp // calculate new enciphered timestamp
if (m_deterministicTimeVector.size()) if (m_deterministicTimeVector.size())
@ -86,8 +92,10 @@ byte X917RNG::GenerateByte()
} }
else else
{ {
clock_t tstamp = clock(); clock_t c = clock();
xorbuf(dtbuf, (byte *)&tstamp, STDMIN((int)sizeof(tstamp), S)); xorbuf(dtbuf, (byte *)&c, UnsignedMin(sizeof(c), S));
time_t t = time(NULL);
xorbuf(dtbuf+S-UnsignedMin(sizeof(t), S), (byte *)&t, UnsignedMin(sizeof(t), S));
cipher->ProcessBlock(dtbuf); cipher->ProcessBlock(dtbuf);
} }
@ -95,16 +103,20 @@ byte X917RNG::GenerateByte()
xorbuf(randseed, dtbuf, S); xorbuf(randseed, dtbuf, S);
// generate a new block of random bytes // generate a new block of random bytes
cipher->ProcessBlock(randseed, randbuf); cipher->ProcessBlock(randseed);
if (memcmp(m_lastBlock, randseed, S) == 0)
throw SelfTestFailure("X917RNG: Continuous random number generator test failed.");
// output random bytes
size_t len = UnsignedMin(size, S);
target.ChannelPut(channel, randseed, len);
size -= len;
// compute new seed vector // compute new seed vector
for (int i=0; i<S; i++) memcpy(m_lastBlock, randseed, S);
randseed[i] = randbuf[i] ^ dtbuf[i]; xorbuf(randseed, dtbuf, S);
cipher->ProcessBlock(randseed); cipher->ProcessBlock(randseed);
randbuf_counter=S;
} }
return(randbuf[S-randbuf_counter--]);
} }
#endif #endif

9
rng.h
View File

@ -16,7 +16,7 @@ public:
LC_RNG(word32 init_seed) LC_RNG(word32 init_seed)
: seed(init_seed) {} : seed(init_seed) {}
byte GenerateByte(); void GenerateBlock(byte *output, size_t size);
word32 GetSeed() {return seed;} word32 GetSeed() {return seed;}
@ -37,14 +37,13 @@ public:
// cipher will be deleted by destructor, deterministicTimeVector = 0 means obtain time vector from system // cipher will be deleted by destructor, deterministicTimeVector = 0 means obtain time vector from system
X917RNG(BlockTransformation *cipher, const byte *seed, const byte *deterministicTimeVector = 0); X917RNG(BlockTransformation *cipher, const byte *seed, const byte *deterministicTimeVector = 0);
byte GenerateByte(); void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size);
private: private:
member_ptr<BlockTransformation> cipher; member_ptr<BlockTransformation> cipher;
const int S; // blocksize of cipher unsigned int S; // blocksize of cipher
SecByteBlock dtbuf; // buffer for enciphered timestamp SecByteBlock dtbuf; // buffer for enciphered timestamp
SecByteBlock randseed, randbuf, m_deterministicTimeVector; SecByteBlock randseed, m_lastBlock, m_deterministicTimeVector;
int randbuf_counter; // # of unused bytes left in randbuf
}; };
/** This class implements Maurer's Universal Statistical Test for Random Bit Generators /** This class implements Maurer's Universal Statistical Test for Random Bit Generators