Skip to content

Commit

Permalink
Add OldRandomPool class (Issue 452)
Browse files Browse the repository at this point in the history
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;
  • Loading branch information
noloader committed Aug 1, 2017
1 parent 2171a3a commit 02e3a79
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 33 deletions.
35 changes: 18 additions & 17 deletions mdc.h
Expand Up @@ -13,46 +13,46 @@
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)
{
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;}
Expand All @@ -65,12 +65,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;
};

Expand Down
90 changes: 89 additions & 1 deletion randpool.cpp
Expand Up @@ -12,7 +12,13 @@
#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)

Expand Down Expand Up @@ -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

#endif
67 changes: 52 additions & 15 deletions 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
Expand All @@ -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
Expand All @@ -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
{
Expand All @@ -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:
Expand All @@ -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<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

#endif

1 comment on commit 02e3a79

@noloader
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleared at Commit 02e3a794443ae6d9 (addition of OldRandomPool) and Commit 5fbbc5311ceafeba (self tests). Two commits were used in case the self tests need to be updated.

Also see Issue 452.

Please sign in to comment.