diff --git a/mdc.h b/mdc.h index a84996e0..c3e8d2ce 100644 --- a/mdc.h +++ b/mdc.h @@ -13,46 +13,47 @@ NAMESPACE_BEGIN(CryptoPP) //! \class MDC_Info +//! \tparam B BlockCipher derived class //! \brief MDC_Info cipher information -template -struct MDC_Info : public FixedBlockSize, public FixedKeyLength +template +struct MDC_Info : public FixedBlockSize, public FixedKeyLength { - 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 MDC -template -class MDC : public MDC_Info +template +class MDC : public MDC_Info { //! \class Enc //! \brief MDC cipher encryption operation - class CRYPTOPP_NO_VTABLE Enc : public BlockCipherImpl > + class CRYPTOPP_NO_VTABLE Enc : public BlockCipherImpl > { - typedef typename T::HashWordType HashWordType; + typedef typename H::HashWordType HashWordType; public: void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs ¶ms) { + 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 HashWordType *Buffer() const {return (HashWordType *)m_buffer.data();} // VC60 workaround: bug triggered if using FixedSizeAllocatorWithCleanup - FixedSizeSecBlock::KEYLENGTH, AllocatorWithCleanup > m_key; - mutable FixedSizeSecBlock::BLOCKSIZE, AllocatorWithCleanup > m_buffer; + FixedSizeSecBlock::KEYLENGTH, AllocatorWithCleanup > m_key; + mutable FixedSizeSecBlock::BLOCKSIZE, AllocatorWithCleanup > m_buffer; + mutable H m_hash; }; public: - //! use BlockCipher interface + // use BlockCipher interface typedef BlockCipherFinal Encryption; }; diff --git a/randpool.cpp b/randpool.cpp index a7d2efdf..39afd72f 100644 --- a/randpool.cpp +++ b/randpool.cpp @@ -12,15 +12,21 @@ #include "aes.h" #include "sha.h" #include "hrtimer.h" -#include +#include "trap.h" + +// OldRandomPool +#include "mdc.h" +#include "modes.h" + +#include 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 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::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 diff --git a/randpool.h b/randpool.h index 3214e3c0..37dda408 100644 --- a/randpool.h +++ b/randpool.h @@ -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: -//!
-//!    $ 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' *.h *.cpp
-//!    randpool.cpp:typedef MDC RandomPoolCipher;
-//! 
+//! \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 +{ +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 ¶meters) {CRYPTOPP_UNUSED(parameters);} + +protected: + void Stir(); + +private: + SecByteBlock pool, key; + size_t addPos, getPos; +}; + NAMESPACE_END #endif diff --git a/validat1.cpp b/validat1.cpp index be903af1..c1f14e37 100644 --- a/validat1.cpp +++ b/validat1.cpp @@ -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(&result), 4); + prng.GenerateBlock(reinterpret_cast(&result), 3); + prng.GenerateBlock(reinterpret_cast(&result), 2); + prng.GenerateBlock(reinterpret_cast(&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; }