From f41245df6fb9b85574260eca9cd32777e8ab5136 Mon Sep 17 00:00:00 2001 From: weidai Date: Fri, 4 May 2007 15:37:46 +0000 Subject: [PATCH] reduce risk of reusing random numbers after VM state rollback --- modes.cpp | 14 ------- modes.h | 5 +-- pubkey.h | 20 ++++++++- randpool.cpp | 116 +++++++++++++++++---------------------------------- randpool.h | 39 ++++++----------- salsa.cpp | 29 ++++++------- salsa.h | 1 - seal.h | 1 - strciphr.h | 5 --- 9 files changed, 82 insertions(+), 148 deletions(-) diff --git a/modes.cpp b/modes.cpp index b51afe9b..5605922a 100644 --- a/modes.cpp +++ b/modes.cpp @@ -24,15 +24,6 @@ void Modes_TestInstantiations() } #endif -void CipherModeBase::GetNextIV(byte *IV) -{ - if (!IsForwardTransformation()) - throw NotImplemented("CipherModeBase: GetNextIV() must be called on an encryption object"); - - m_cipher->ProcessBlock(m_register); - memcpy(IV, m_register, BlockSize()); -} - void CTR_ModePolicy::SeekToIteration(lword iterationCount) { int carry=0; @@ -45,11 +36,6 @@ void CTR_ModePolicy::SeekToIteration(lword iterationCount) } } -void CTR_ModePolicy::CipherGetNextIV(byte *IV) -{ - IncrementCounterByOne(IV, m_counterArray, BlockSize()); -} - inline void CTR_ModePolicy::ProcessMultipleBlocks(byte *output, const byte *input, size_t n) { unsigned int s = BlockSize(), j = 0; diff --git a/modes.h b/modes.h index a4f5bbed..442c3205 100644 --- a/modes.h +++ b/modes.h @@ -40,7 +40,6 @@ public: unsigned int OptimalDataAlignment() const {return BlockSize();} unsigned int IVSize() const {return BlockSize();} - void GetNextIV(byte *IV); virtual IV_Requirement IVRequirement() const =0; protected: @@ -64,7 +63,6 @@ class CRYPTOPP_NO_VTABLE ModePolicyCommonTemplate : public CipherModeBase, publi { unsigned int GetAlignment() const {return m_cipher->BlockAlignment();} void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length); - void CipherGetNextIV(byte *IV) {CipherModeBase::GetNextIV(IV);} }; template @@ -137,7 +135,6 @@ private: assert(iterationCount == 1); assert(m_cipher->IsForwardTransformation()); // OFB mode needs the "encrypt" direction of the underlying block cipher, even to decrypt m_cipher->ProcessBlock(keystreamBuffer); - memcpy_s(m_register, m_register.size(), keystreamBuffer, BlockSize()); } void CipherResynchronize(byte *keystreamBuffer, const byte *iv) { @@ -150,10 +147,10 @@ class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE CTR_ModePolicy : public ModePolicyCommonTe public: bool IsRandomAccess() const {return true;} IV_Requirement IVRequirement() const {return UNIQUE_IV;} - void CipherGetNextIV(byte *IV); static const char * CRYPTOPP_API StaticAlgorithmName() {return "CTR";} private: + unsigned int GetAlignment() const {return m_cipher->BlockAlignment();} unsigned int GetBytesPerIteration() const {return BlockSize();} unsigned int GetIterationsToBuffer() const {return m_cipher->OptimalNumberOfParallelBlocks();} void WriteKeystream(byte *buffer, size_t iterationCount) diff --git a/pubkey.h b/pubkey.h index 0204ba6b..bdf00351 100644 --- a/pubkey.h +++ b/pubkey.h @@ -1033,13 +1033,23 @@ public: ma.m_empty = true; Integer e(representative, representative.size()); - Integer r; + // hash message digest into random number k to prevent reusing the same k on a different messages + // after virtual machine rollback + if (rng.CanIncorporateEntropy()) + rng.IncorporateEntropy(representative, representative.size()); + Integer k(rng, 1, params.GetSubgroupOrder()-1); + Integer r, s; + r = params.ConvertElementToInteger(params.ExponentiateBase(k)); + alg.Sign(params, key.GetPrivateExponent(), k, e, r, s); + + /* + Integer r, s; if (this->MaxRecoverableLength() > 0) r.Decode(ma.m_semisignature, ma.m_semisignature.size()); else r.Decode(ma.m_presignature, ma.m_presignature.size()); - Integer s; alg.Sign(params, key.GetPrivateExponent(), ma.m_k, e, r, s); + */ size_t rLen = alg.RLen(params); r.Encode(signature, rLen); @@ -1054,11 +1064,17 @@ public: protected: void RestartMessageAccumulator(RandomNumberGenerator &rng, PK_MessageAccumulatorBase &ma) const { + // k needs to be generated before hashing for signature schemes with recovery + // but to defend against VM rollbacks we need to generate k after hashing. + // so this code is commented out, since no DL-based signature scheme with recovery + // has been implemented in Crypto++ anyway + /* const DL_ElgamalLikeSignatureAlgorithm &alg = this->GetSignatureAlgorithm(); const DL_GroupParameters ¶ms = this->GetAbstractGroupParameters(); ma.m_k.Randomize(rng, 1, params.GetSubgroupOrder()-1); ma.m_presignature.New(params.GetEncodedElementSize(false)); params.ConvertElementToInteger(params.ExponentiateBase(ma.m_k)).Encode(ma.m_presignature, ma.m_presignature.size()); + */ } }; diff --git a/randpool.cpp b/randpool.cpp index c2b44fac..11ece6d4 100644 --- a/randpool.cpp +++ b/randpool.cpp @@ -1,99 +1,59 @@ // randpool.cpp - written and placed in the public domain by Wei Dai -// The algorithm in this module comes from PGP's randpool.c +// RandomPool used to follow the design of randpool in PGP 2.6.x, +// but as of version 5.5 it has been redesigned to reduce the risk +// of reusing random numbers after state rollback (which may occur +// when running in a virtual machine like VMware). #include "pch.h" #ifndef CRYPTOPP_IMPORTS #include "randpool.h" -#include "mdc.h" +#include "aes.h" #include "sha.h" -#include "modes.h" +#include "hrtimer.h" +#include NAMESPACE_BEGIN(CryptoPP) -typedef MDC RandomPoolCipher; - -RandomPool::RandomPool(unsigned int poolSize) - : pool(poolSize), key(RandomPoolCipher::DEFAULT_KEYLENGTH) +RandomPool::RandomPool() + : m_pCipher(new AES::Encryption), m_keySet(false) { - assert(poolSize > key.size()); - - addPos=0; - getPos=poolSize; - memset(pool, 0, poolSize); - memset(key, 0, key.size()); } -void RandomPool::Stir() +void RandomPool::IncorporateEntropy(const byte *input, size_t length) { - CFB_Mode::Encryption cipher; + SHA256 hash; + hash.Update(m_key, 32); + hash.Update(input, length); + hash.Final(m_key); + m_keySet = false; +} - for (int i=0; i<2; i++) +void RandomPool::GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size) +{ + if (size > 0) { - cipher.SetKeyWithIV(key, key.size(), pool.end()-cipher.IVSize()); - cipher.ProcessString(pool, pool.size()); - memcpy(key, pool, key.size()); + if (!m_keySet) + m_pCipher->SetKey(m_key, 32); + + Timer timer; + TimerWord tw = timer.GetCurrentTimerValue(); + CRYPTOPP_COMPILE_ASSERT(sizeof(tw) <= 16); + *(TimerWord *)m_seed.data() += tw; + + time_t t = time(NULL); + CRYPTOPP_COMPILE_ASSERT(sizeof(t) <= 8); + *(time_t *)(m_seed.data()+8) += t; + + do + { + m_pCipher->ProcessBlock(m_seed); + size_t len = UnsignedMin(16, size); + target.ChannelPut(channel, m_seed, len); + size -= len; + } while (size > 0); } - - addPos = 0; - getPos = key.size(); -} - -size_t RandomPool::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 RandomPool::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) -{ - if (!blocking) - throw NotImplemented("RandomPool: 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 RandomPool::GenerateByte() -{ - if (getPos == pool.size()) - Stir(); - - return pool[getPos++]; -} - -void RandomPool::GenerateBlock(byte *outString, size_t size) -{ - ArraySink sink(outString, size); - TransferTo(sink, size); } NAMESPACE_END diff --git a/randpool.h b/randpool.h index e4157f3a..c25bc9bb 100644 --- a/randpool.h +++ b/randpool.h @@ -7,38 +7,25 @@ NAMESPACE_BEGIN(CryptoPP) //! Randomness Pool -/*! This class can be used to generate - pseudorandom bytes after seeding the pool with - the Put() methods */ -class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, - public Bufferless +/*! This class can be used to generate cryptographic quality + pseudorandom bytes after seeding the pool with IncorporateEntropy() */ +class CRYPTOPP_DLL RandomPool : public RandomNumberGenerator, public NotCopyable { public: - //! poolSize must be greater than 16 - RandomPool(unsigned int poolSize=384); + RandomPool(); - size_t Put2(const byte *begin, size_t length, int messageEnd, bool blocking); + bool CanIncorporateEntropy() const {return true;} + void IncorporateEntropy(const byte *input, size_t length); + void GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size); - bool AnyRetrievable() const {return true;} - lword MaxRetrievable() const {return ULONG_MAX;} - - size_t TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel=NULL_CHANNEL, bool blocking=true); - size_t CopyRangeTo2(BufferedTransformation &target, lword &begin, lword end=LWORD_MAX, const std::string &channel=NULL_CHANNEL, bool blocking=true) const - { - throw NotImplemented("RandomPool: CopyRangeTo2() is not supported by this store"); - } - - byte GenerateByte(); - void GenerateBlock(byte *output, size_t size); - - void IsolatedInitialize(const NameValuePairs ¶meters) {} - -protected: - void Stir(); + // for backwards compatibility. use RandomNumberSource, RandomNumberStore, and RandomNumberSink for other BufferTransformation functionality + void Put(const byte *input, size_t length) {IncorporateEntropy(input, length);} private: - SecByteBlock pool, key; - size_t addPos, getPos; + FixedSizeSecBlock m_key; + FixedSizeSecBlock m_seed; + member_ptr m_pCipher; + bool m_keySet; }; NAMESPACE_END diff --git a/salsa.cpp b/salsa.cpp index 4781218a..5b2e1154 100755 --- a/salsa.cpp +++ b/salsa.cpp @@ -17,17 +17,6 @@ void Salsa20_TestInstantiations() Salsa20::Encryption x; } -void Salsa20_Policy::CipherGetNextIV(byte *IV) -{ - word32 j6, j7; - - j6 = m_state[14] + 1; - j7 = m_state[11] + (j6 == 0); - - PutWord(false, LITTLE_ENDIAN_ORDER, IV, j6); - PutWord(false, LITTLE_ENDIAN_ORDER, IV+4, j7); -} - void Salsa20_Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length) { m_rounds = params.GetIntValueWithDefault(Name::Rounds(), 20); @@ -87,10 +76,20 @@ void Salsa20_Policy::OperateKeystream(KeystreamOperation operation, byte *output { int i; #if CRYPTOPP_BOOL_SSE2_INTRINSICS_AVAILABLE + #define SSE2_QUARTER_ROUND(a, b, d, i) {\ + __m128i t = _mm_add_epi32(a, d); \ + b = _mm_xor_si128(b, _mm_slli_epi32(t, i)); \ + b = _mm_xor_si128(b, _mm_srli_epi32(t, 32-i));} + if (HasSSE2()) { __m128i *s = (__m128i *)m_state.data(); +#if CRYPTOPP_GCC_VERSION >= 40000 || _MSC_VER > 1400 || (defined(_MSC_VER) && CRYPTOPP_BOOL_X86) + // This code triggers an internal compiler error on MSVC 2005 when compiling + // for x64 with optimizations on. hopefully it will get fixed in the next release. + // A bug report has been submitted at http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=274123 + // Also, GCC 3.4.4 generates incorrect code for x86 at -O2. if (iterationCount >= 4) { __m128i ss[16]; @@ -139,11 +138,6 @@ void Salsa20_Policy::OperateKeystream(KeystreamOperation operation, byte *output for (i=m_rounds; i>0; i-=2) { - #define SSE2_QUARTER_ROUND(a, b, d, i) {\ - __m128i t = _mm_add_epi32(a, d); \ - b = _mm_xor_si128(b, _mm_slli_epi32(t, i)); \ - b = _mm_xor_si128(b, _mm_srli_epi32(t, 32-i));} - #define QUARTER_ROUND(a, b, c, d) \ SSE2_QUARTER_ROUND(a, b, d, 7) \ SSE2_QUARTER_ROUND(b, c, a, 9) \ @@ -205,6 +199,7 @@ void Salsa20_Policy::OperateKeystream(KeystreamOperation operation, byte *output #undef SALSA_OUTPUT } while ((iterationCount-=4) >= 4); } +#endif if (!IsP4()) while (iterationCount) { @@ -333,6 +328,6 @@ void Salsa20_Policy::OperateKeystream(KeystreamOperation operation, byte *output if (++m_state[8] == 0) ++m_state[5]; } -} +} // see comment above if an internal compiler error occurs here NAMESPACE_END diff --git a/salsa.h b/salsa.h index 176a1a3c..5b1431ac 100755 --- a/salsa.h +++ b/salsa.h @@ -18,7 +18,6 @@ class CRYPTOPP_NO_VTABLE Salsa20_Policy : public AdditiveCipherConcretePolicyAccessPolicy().CipherGetNextIV(iv);} void Resynchronize(const byte *iv); unsigned int OptimalBlockSize() const {return this->GetPolicy().GetOptimalBlockSize();} unsigned int GetOptimalNextBlockSize() const {return (unsigned int)this->m_leftOver;} @@ -170,7 +167,6 @@ public: virtual bool CanIterate() const {return false;} virtual void Iterate(byte *output, const byte *input, CipherDir dir, size_t iterationCount) {assert(false);} virtual void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length) =0; - virtual void CipherGetNextIV(byte *iv) {throw NotImplemented("SimpleKeyingInterface: this object doesn't support GetNextIV()");} virtual void CipherResynchronize(const byte *iv) {throw NotImplemented("SimpleKeyingInterface: this object doesn't support resynchronization");} }; @@ -236,7 +232,6 @@ class CRYPTOPP_NO_VTABLE CFB_CipherTemplate : public BASE { public: void ProcessData(byte *outString, const byte *inString, size_t length); - void GetNextIV(byte *iv) {this->AccessPolicy().CipherGetNextIV(iv);} void Resynchronize(const byte *iv); unsigned int OptimalBlockSize() const {return this->GetPolicy().GetBytesPerIteration();} unsigned int GetOptimalNextBlockSize() const {return (unsigned int)m_leftOver;}