From 02e3a794443ae6d985ddf881d52a87bae1e7cd6f Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Tue, 1 Aug 2017 18:53:31 -0400 Subject: [PATCH] Add OldRandomPool class (Issue 452) RandomPool used to be a PGP-style deterministic generator and folks used it as a key generation function. At Crypto++ 5.5 the design changed to harden it agianst rollback attacks. The design change resulted in an upgrade barrier. That is, some folks are stuck at Crypto++ 4.2 or Crypto++ 5.2 because they must interoperate with existing software. Below is the test program we used for the test vector. It was run against Crypto++ 5.4. RandomPool prng; SecByteBlock seed(0x00, 384), result(64); prng.Put(seed, seed.size()); prng.GenerateBlock(result, result.size()); HexEncoder encoder(new FileSink(std::cout)); std::cout << "RandomPool: "; encoder.Put(result, sizeof(result)); std::cout << std::endl; --- mdc.h | 35 ++++++++++---------- randpool.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++- randpool.h | 67 +++++++++++++++++++++++++++++--------- 3 files changed, 159 insertions(+), 33 deletions(-) diff --git a/mdc.h b/mdc.h index a84996e07..f246b5bbc 100644 --- a/mdc.h +++ b/mdc.h @@ -13,46 +13,46 @@ 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) { 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 +65,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 a7d2efdfe..a904c6eb8 100644 --- a/randpool.cpp +++ b/randpool.cpp @@ -12,7 +12,13 @@ #include "aes.h" #include "sha.h" #include "hrtimer.h" -#include +#include "trap.h" + +// OldRandomPool +#include "mdc.h" +#include "modes.h" + +#include NAMESPACE_BEGIN(CryptoPP) @@ -69,6 +75,88 @@ 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) +{ + 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 3214e3c00..a9c1a749d 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 @@ class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, public NotCopyable 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,49 @@ class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, public NotCopyable 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 + { + throw NotImplemented("OldRandomPool: CopyRangeTo2() is not supported by this store"); + } + + byte GenerateByte(); + void GenerateBlock(byte *output, size_t size); + + void IsolatedInitialize(const NameValuePairs ¶meters) {} + +protected: + void Stir(); + +private: + SecByteBlock pool, key; + size_t addPos, getPos; +}; + NAMESPACE_END #endif