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;
pull/461/head
Jeffrey Walton 2017-08-01 18:53:31 -04:00
parent 2171a3a379
commit 02e3a79444
No known key found for this signature in database
GPG Key ID: B36AB348921B1838
3 changed files with 159 additions and 33 deletions

35
mdc.h
View File

@ -13,46 +13,46 @@
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)
//! \class MDC_Info //! \class MDC_Info
//! \tparam B BlockCipher derived class
//! \brief MDC_Info cipher information //! \brief MDC_Info cipher information
template <class T> template <class B>
struct MDC_Info : public FixedBlockSize<T::DIGESTSIZE>, public FixedKeyLength<T::BLOCKSIZE> 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 //! \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 //! \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> //! \sa <a href="http://www.weidai.com/scan-mirror/cs.html#MDC">MDC</a>
template <class T> template <class H>
class MDC : public MDC_Info<T> class MDC : public MDC_Info<H>
{ {
//! \class Enc //! \class Enc
//! \brief MDC cipher encryption operation //! \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: public:
void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs &params) void UncheckedSetKey(const byte *userKey, unsigned int length, const NameValuePairs &params)
{ {
this->AssertValidKeyLength(length); this->AssertValidKeyLength(length);
memcpy_s(m_key, m_key.size(), userKey, this->KEYLENGTH); 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 void ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
{ {
T::CorrectEndianess(Buffer(), (HashWordType *)inBlock, this->BLOCKSIZE); m_hash.CorrectEndianess(Buffer(), (HashWordType *)inBlock, this->BLOCKSIZE);
T::Transform(Buffer(), Key()); H::Transform(Buffer(), Key());
if (xorBlock) if (xorBlock)
{ {
T::CorrectEndianess(Buffer(), Buffer(), this->BLOCKSIZE); m_hash.CorrectEndianess(Buffer(), Buffer(), this->BLOCKSIZE);
xorbuf(outBlock, xorBlock, m_buffer, this->BLOCKSIZE); xorbuf(outBlock, xorBlock, m_buffer, this->BLOCKSIZE);
} }
else else
T::CorrectEndianess((HashWordType *)outBlock, Buffer(), this->BLOCKSIZE); m_hash.CorrectEndianess((HashWordType *)outBlock, Buffer(), this->BLOCKSIZE);
} }
bool IsPermutation() const {return false;} bool IsPermutation() const {return false;}
@ -65,12 +65,13 @@ class MDC : public MDC_Info<T>
HashWordType *Buffer() const {return (HashWordType *)m_buffer.data();} HashWordType *Buffer() const {return (HashWordType *)m_buffer.data();}
// VC60 workaround: bug triggered if using FixedSizeAllocatorWithCleanup // VC60 workaround: bug triggered if using FixedSizeAllocatorWithCleanup
FixedSizeSecBlock<byte, MDC_Info<T>::KEYLENGTH, AllocatorWithCleanup<byte> > m_key; FixedSizeSecBlock<byte, MDC_Info<H>::KEYLENGTH, AllocatorWithCleanup<byte> > m_key;
mutable FixedSizeSecBlock<byte, MDC_Info<T>::BLOCKSIZE, AllocatorWithCleanup<byte> > m_buffer; mutable FixedSizeSecBlock<byte, MDC_Info<H>::BLOCKSIZE, AllocatorWithCleanup<byte> > m_buffer;
mutable H m_hash;
}; };
public: public:
//! use BlockCipher interface // use BlockCipher interface
typedef BlockCipherFinal<ENCRYPTION, Enc> Encryption; typedef BlockCipherFinal<ENCRYPTION, Enc> Encryption;
}; };

View File

@ -12,7 +12,13 @@
#include "aes.h" #include "aes.h"
#include "sha.h" #include "sha.h"
#include "hrtimer.h" #include "hrtimer.h"
#include <time.h> #include "trap.h"
// OldRandomPool
#include "mdc.h"
#include "modes.h"
#include <ctime>
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)
@ -69,6 +75,88 @@ 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)
{
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 NAMESPACE_END
#endif #endif

View File

@ -1,4 +1,5 @@
// randpool.h - originally written and placed in the public domain by Wei Dai // randpool.h - originally written and placed in the public domain by Wei Dai
// OldRandPool added by JW in August, 2017.
//! \file randpool.h //! \file randpool.h
//! \brief Class file for Randomness Pool //! \brief Class file for Randomness Pool
@ -9,20 +10,9 @@
//! RandomPool was redesigned to reduce the risk of reusing random numbers after state //! 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 //! rollback (which may occur when running in a virtual machine like VMware or a hosted
//! environment). //! environment).
//! \details If you need the pre-Crypto++ 5.5 generator then you can find it with: //! \details If you need the pre-Crypto++ 5.5 generator then use OldRandomPool class. You
//! <pre> //! should migrate away from OldRandomPool at the earliest opportunity. Use RandomPool
//! $ git clone https://github.com/weidai11/cryptopp cryptopp-ancient //! or AutoSeededRandomPool instead.
//! $ 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>
//! \since Crypto++ 4.0 (PGP 2.6.x style), Crypto++ 5.5 (AES-256 based) //! \since Crypto++ 4.0 (PGP 2.6.x style), Crypto++ 5.5 (AES-256 based)
#ifndef CRYPTOPP_RANDPOOL_H #ifndef CRYPTOPP_RANDPOOL_H
@ -45,6 +35,9 @@ NAMESPACE_BEGIN(CryptoPP)
//! RandomPool was redesigned to reduce the risk of reusing random numbers after state //! 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 //! rollback (which may occur when running in a virtual machine like VMware or a hosted
//! environment). //! 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) //! \since Crypto++ 4.0 (PGP 2.6.x style), Crypto++ 5.5 (AES-256 based)
class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, public NotCopyable class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, public NotCopyable
{ {
@ -56,7 +49,8 @@ public:
void IncorporateEntropy(const byte *input, size_t length); void IncorporateEntropy(const byte *input, size_t length);
void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size); 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);} void Put(const byte *input, size_t length) {IncorporateEntropy(input, length);}
private: private:
@ -66,6 +60,49 @@ private:
bool m_keySet; 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
{
throw NotImplemented("OldRandomPool: CopyRangeTo2() is not supported by this store");
}
byte GenerateByte();
void GenerateBlock(byte *output, size_t size);
void IsolatedInitialize(const NameValuePairs &parameters) {}
protected:
void Stir();
private:
SecByteBlock pool, key;
size_t addPos, getPos;
};
NAMESPACE_END NAMESPACE_END
#endif #endif