Sync with Upstream master

pull/461/head
Jeffrey Walton 2017-08-01 20:46:31 -04:00
commit 460d7e47dc
4 changed files with 276 additions and 37 deletions

36
mdc.h
View File

@ -13,46 +13,47 @@
NAMESPACE_BEGIN(CryptoPP)
//! \class MDC_Info
//! \tparam B BlockCipher derived class
//! \brief MDC_Info cipher information
template <class T>
struct MDC_Info : public FixedBlockSize<T::DIGESTSIZE>, public FixedKeyLength<T::BLOCKSIZE>
template <class B>
struct MDC_Info : public FixedBlockSize<B::DIGESTSIZE>, public FixedKeyLength<B::BLOCKSIZE>
{
static std::string StaticAlgorithmName() {return std::string("MDC/")+T::StaticAlgorithmName();}
static std::string StaticAlgorithmName() {return std::string("MDC/")+B::StaticAlgorithmName();}
};
//! \class MDC
//! \brief MDC cipher
//! \tparam T HashTransformation derived class
//! \details MDC() is a construction by Peter Gutmann to turn an iterated hash function into a PRF
//! \sa <a href="http://www.weidai.com/scan-mirror/cs.html#MDC">MDC</a>
template <class T>
class MDC : public MDC_Info<T>
template <class H>
class MDC : public MDC_Info<H>
{
//! \class Enc
//! \brief MDC cipher encryption operation
class CRYPTOPP_NO_VTABLE Enc : public BlockCipherImpl<MDC_Info<T> >
class CRYPTOPP_NO_VTABLE Enc : public BlockCipherImpl<MDC_Info<H> >
{
typedef typename T::HashWordType HashWordType;
typedef typename H::HashWordType HashWordType;
public:
void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs &params)
{
CRYPTOPP_UNUSED(params);
this->AssertValidKeyLength(length);
memcpy_s(m_key, m_key.size(), userKey, this->KEYLENGTH);
T::CorrectEndianess(Key(), Key(), this->KEYLENGTH);
m_hash.CorrectEndianess(Key(), Key(), this->KEYLENGTH);
}
void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
{
T::CorrectEndianess(Buffer(), (HashWordType *)inBlock, this->BLOCKSIZE);
T::Transform(Buffer(), Key());
m_hash.CorrectEndianess(Buffer(), (HashWordType *)inBlock, this->BLOCKSIZE);
H::Transform(Buffer(), Key());
if (xorBlock)
{
T::CorrectEndianess(Buffer(), Buffer(), this->BLOCKSIZE);
m_hash.CorrectEndianess(Buffer(), Buffer(), this->BLOCKSIZE);
xorbuf(outBlock, xorBlock, m_buffer, this->BLOCKSIZE);
}
else
T::CorrectEndianess((HashWordType *)outBlock, Buffer(), this->BLOCKSIZE);
m_hash.CorrectEndianess((HashWordType *)outBlock, Buffer(), this->BLOCKSIZE);
}
bool IsPermutation() const {return false;}
@ -65,12 +66,13 @@ class MDC : public MDC_Info<T>
HashWordType *Buffer() const {return (HashWordType *)m_buffer.data();}
// VC60 workaround: bug triggered if using FixedSizeAllocatorWithCleanup
FixedSizeSecBlock<byte, MDC_Info<T>::KEYLENGTH, AllocatorWithCleanup<byte> > m_key;
mutable FixedSizeSecBlock<byte, MDC_Info<T>::BLOCKSIZE, AllocatorWithCleanup<byte> > m_buffer;
FixedSizeSecBlock<byte, MDC_Info<H>::KEYLENGTH, AllocatorWithCleanup<byte> > m_key;
mutable FixedSizeSecBlock<byte, MDC_Info<H>::BLOCKSIZE, AllocatorWithCleanup<byte> > m_buffer;
mutable H m_hash;
};
public:
//! use BlockCipher interface
// use BlockCipher interface
typedef BlockCipherFinal<ENCRYPTION, Enc> Encryption;
};

View File

@ -12,15 +12,21 @@
#include "aes.h"
#include "sha.h"
#include "hrtimer.h"
#include <time.h>
#include "trap.h"
// OldRandomPool
#include "mdc.h"
#include "modes.h"
#include <ctime>
NAMESPACE_BEGIN(CryptoPP)
RandomPool::RandomPool()
: m_pCipher(new AES::Encryption), m_keySet(false)
{
memset(m_key, 0, m_key.SizeInBytes());
memset(m_seed, 0, m_seed.SizeInBytes());
::memset(m_key, 0, m_key.SizeInBytes());
::memset(m_seed, 0, m_seed.SizeInBytes());
}
void RandomPool::IncorporateEntropy(const byte *input, size_t length)
@ -51,8 +57,8 @@ void RandomPool::GenerateIntoBufferedTransformation(BufferedTransformation &targ
// UBsan finding: signed integer overflow: 1876017710 + 1446085457 cannot be represented in type 'long int'
// *(time_t *)(m_seed.data()+8) += t;
word64 tt1 = 0, tt2 = (word64)t;
memcpy(&tt1, m_seed.data()+8, 8);
memcpy(m_seed.data()+8, &(tt2 += tt1), 8);
::memcpy(&tt1, m_seed.data()+8, 8);
::memcpy(m_seed.data()+8, &(tt2 += tt1), 8);
// Wipe the intermediates
*((volatile TimerWord*)&tw) = 0;
@ -69,6 +75,89 @@ void RandomPool::GenerateIntoBufferedTransformation(BufferedTransformation &targ
}
}
// OldRandomPool is provided for backwards compatibility for a migration path
typedef MDC<SHA1> OldRandomPoolCipher;
OldRandomPool::OldRandomPool(unsigned int poolSize)
: pool(poolSize), key(OldRandomPoolCipher::DEFAULT_KEYLENGTH), addPos(0), getPos(poolSize)
{
CRYPTOPP_ASSERT(poolSize > key.size());
::memset(pool, 0, poolSize);
::memset(key, 0, key.size());
}
void OldRandomPool::Stir()
{
CFB_Mode<OldRandomPoolCipher>::Encryption cipher;
for (int i=0; i<2; i++)
{
cipher.SetKeyWithIV(key, key.size(), pool.end()-cipher.IVSize());
cipher.ProcessString(pool, pool.size());
::memcpy(key, pool, key.size());
}
addPos = 0;
getPos = key.size();
}
size_t OldRandomPool::Put2(const byte *inString, size_t length, int messageEnd, bool blocking)
{
CRYPTOPP_UNUSED(messageEnd); CRYPTOPP_UNUSED(blocking);
size_t t;
while (length > (t = pool.size() - addPos))
{
xorbuf(pool+addPos, inString, t);
inString += t;
length -= t;
Stir();
}
if (length)
{
xorbuf(pool+addPos, inString, length);
addPos += length;
getPos = pool.size(); // Force stir on get
}
return 0;
}
size_t OldRandomPool::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking)
{
if (!blocking)
throw NotImplemented("OldRandomPool: nonblocking transfer is not implemented by this object");
lword size = transferBytes;
while (size > 0)
{
if (getPos == pool.size())
Stir();
size_t t = UnsignedMin(pool.size() - getPos, size);
target.ChannelPut(channel, pool+getPos, t);
size -= t;
getPos += t;
}
return 0;
}
byte OldRandomPool::GenerateByte()
{
if (getPos == pool.size())
Stir();
return pool[getPos++];
}
void OldRandomPool::GenerateBlock(byte *outString, size_t size)
{
ArraySink sink(outString, size);
TransferTo(sink, size);
}
NAMESPACE_END
#endif

View File

@ -1,4 +1,5 @@
// randpool.h - originally written and placed in the public domain by Wei Dai
// OldRandPool added by JW in August, 2017.
//! \file randpool.h
//! \brief Class file for Randomness Pool
@ -9,20 +10,9 @@
//! RandomPool was redesigned to reduce the risk of reusing random numbers after state
//! rollback (which may occur when running in a virtual machine like VMware or a hosted
//! environment).
//! \details If you need the pre-Crypto++ 5.5 generator then you can find it with:
//! <pre>
//! $ git clone https://github.com/weidai11/cryptopp cryptopp-ancient
//! $ cryptopp-ancient
//!
//! # Checkout the RandomPool change
//! $ git checkout f41245df6fb9b85574260eca9cd32777e8ab5136
//!
//! # Go back one more
//! git checkout HEAD~1
//!
//! $ grep 'MDC<SHA1>' *.h *.cpp
//! randpool.cpp:typedef MDC<SHA1> RandomPoolCipher;
//! </pre>
//! \details If you need the pre-Crypto++ 5.5 generator then use OldRandomPool class. You
//! should migrate away from OldRandomPool at the earliest opportunity. Use RandomPool
//! or AutoSeededRandomPool instead.
//! \since Crypto++ 4.0 (PGP 2.6.x style), Crypto++ 5.5 (AES-256 based)
#ifndef CRYPTOPP_RANDPOOL_H
@ -45,6 +35,9 @@ NAMESPACE_BEGIN(CryptoPP)
//! RandomPool was redesigned to reduce the risk of reusing random numbers after state
//! rollback (which may occur when running in a virtual machine like VMware or a hosted
//! environment).
//! \details If you need the pre-Crypto++ 5.5 generator then use OldRandomPool class. You
//! should migrate away from OldRandomPool at the earliest opportunity. Use RandomPool
//! or AutoSeededRandomPool instead.
//! \since Crypto++ 4.0 (PGP 2.6.x style), Crypto++ 5.5 (AES-256 based)
class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, public NotCopyable
{
@ -56,7 +49,8 @@ public:
void IncorporateEntropy(const byte *input, size_t length);
void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size);
// for backwards compatibility. use RandomNumberSource, RandomNumberStore, and RandomNumberSink for other BufferTransformation functionality
// for backwards compatibility. use RandomNumberSource, RandomNumberStore, and
// RandomNumberSink for other BufferTransformation functionality
void Put(const byte *input, size_t length) {IncorporateEntropy(input, length);}
private:
@ -66,6 +60,51 @@ private:
bool m_keySet;
};
//! \class OldRandomPool
//! \brief Randomness Pool based on PGP 2.6.x with MDC
//! \details If you need the pre-Crypto++ 5.5 generator then use OldRandomPool class. The
//! OldRandomPool class is always available so you dont need to define
//! CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY. However, you should migrate away from
//! OldRandomPool at the earliest opportunity. Use RandomPool or AutoSeededRandomPool instead.
//! \deprecated This class uses an old style PGP 2.6.x with MDC. The generator risks reusing
//! random random numbers after state rollback. Migrate to RandomPool or AutoSeededRandomPool
//! at the earliest opportunity.
//! \since Crypto++ 6.0 (PGP 2.6.x style)
class CRYPTOPP_DLL OldRandomPool : public RandomNumberGenerator,
public Bufferless<BufferedTransformation>
{
public:
//! \brief Construct an OldRandomPool
//! \param poolSize internal pool size of the generator
//! \details poolSize must be greater than 16
OldRandomPool(unsigned int poolSize=384);
size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking);
bool AnyRetrievable() const {return true;}
lword MaxRetrievable() const {return ULONG_MAX;}
size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true);
size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=DEFAULT_CHANNEL, bool blocking=true) const
{
CRYPTOPP_UNUSED(target); CRYPTOPP_UNUSED(begin); CRYPTOPP_UNUSED(end);
CRYPTOPP_UNUSED(channel); CRYPTOPP_UNUSED(blocking);
throw NotImplemented("OldRandomPool: CopyRangeTo2() is not supported by this store");
}
byte GenerateByte();
void GenerateBlock(byte *output, size_t size);
void IsolatedInitialize(const NameValuePairs &parameters) {CRYPTOPP_UNUSED(parameters);}
protected:
void Stir();
private:
SecByteBlock pool, key;
size_t addPos, getPos;
};
NAMESPACE_END
#endif

View File

@ -718,6 +718,115 @@ bool TestRandomPool()
}
#endif
// Old, PGP 2.6 style RandomPool. Added because users were still having problems
// with it in 2017. The missing functionality was a barrier to upgrades.
std::cout << "\nTesting OldRandomPool generator...\n\n";
{
OldRandomPool prng;
static const unsigned int ENTROPY_SIZE = 32;
// https://github.com/weidai11/cryptopp/issues/452
byte result[32], expected[32] = {
0x58,0x3E,0x0A,0xAC,0x79,0x71,0x19,0x18,
0x51,0x97,0xC6,0x9B,0xEF,0x82,0x18,0x1E,
0x9C,0x0F,0x5C,0xEF,0xC7,0x89,0xB2,0x94,
0x04,0x96,0xD6,0xD9,0xA4,0x3B,0xB7,0xEC
};
SecByteBlock seed(0x00, 384);
prng.Put(seed, seed.size());
prng.GenerateBlock(result, sizeof(result));
fail = (0 != ::memcmp(result, expected, sizeof(expected)));
pass &= !fail;
if (fail)
std::cout << "FAILED:";
else
std::cout << "passed:";
std::cout << " Expected sequence from PGP-style RandomPool (2007 version)\n";
MeterFilter meter(new Redirector(TheBitBucket()));
RandomNumberSource test(prng, 100000, true, new Deflator(new Redirector(meter)));
fail = false;
if (meter.GetTotalBytes() < 100000)
fail = true;
pass &= !fail;
if (fail)
std::cout << "FAILED:";
else
std::cout << "passed:";
std::cout << " 100000 generated bytes compressed to " << meter.GetTotalBytes() << " bytes by DEFLATE\n";
try
{
fail = false;
prng.DiscardBytes(100000);
}
catch (const Exception&)
{
fail = true;
}
pass &= !fail;
if (fail)
std::cout << "FAILED:";
else
std::cout << "passed:";
std::cout << " discarded 10000 bytes" << std::endl;
try
{
fail = false;
if(prng.CanIncorporateEntropy())
{
SecByteBlock entropy(ENTROPY_SIZE);
GlobalRNG().GenerateBlock(entropy, entropy.SizeInBytes());
prng.IncorporateEntropy(entropy, entropy.SizeInBytes());
prng.IncorporateEntropy(entropy, entropy.SizeInBytes());
prng.IncorporateEntropy(entropy, entropy.SizeInBytes());
prng.IncorporateEntropy(entropy, entropy.SizeInBytes());
}
}
catch (const Exception& /*ex*/)
{
fail = true;
}
pass &= !fail;
if (fail)
std::cout << "FAILED:";
else
std::cout << "passed:";
std::cout << " IncorporateEntropy with " << 4*ENTROPY_SIZE << " bytes\n";
try
{
// Miscellaneous for code coverage
fail = false;
word32 result = prng.GenerateWord32();
result = prng.GenerateWord32((result & 0xff), 0xffffffff - (result & 0xff));
prng.GenerateBlock(reinterpret_cast<byte*>(&result), 4);
prng.GenerateBlock(reinterpret_cast<byte*>(&result), 3);
prng.GenerateBlock(reinterpret_cast<byte*>(&result), 2);
prng.GenerateBlock(reinterpret_cast<byte*>(&result), 1);
}
catch (const Exception&)
{
fail = true;
}
pass &= !fail;
if (fail)
std::cout << "FAILED:";
else
std::cout << "passed:";
std::cout << " GenerateWord32 and Crop\n";
}
return pass;
}