diff options
author | weidai <weidai@57ff6487-cd31-0410-9ec3-f628ee90f5f0> | 2007-05-04 15:37:46 +0000 |
---|---|---|
committer | weidai <weidai@57ff6487-cd31-0410-9ec3-f628ee90f5f0> | 2007-05-04 15:37:46 +0000 |
commit | 48e0b8231e112953680cacd9fa2bb6157184a657 (patch) | |
tree | 5c790bf6c465f48e0dca552dfff508cda8f7235f | |
parent | d37d0425edebab09ec1ff767e9b89b68db52533d (diff) | |
download | cryptopp-48e0b8231e112953680cacd9fa2bb6157184a657.tar.gz |
reduce risk of reusing random numbers after VM state rollback
git-svn-id: svn://svn.code.sf.net/p/cryptopp/code/trunk/c5@340 57ff6487-cd31-0410-9ec3-f628ee90f5f0
-rw-r--r-- | modes.cpp | 14 | ||||
-rw-r--r-- | modes.h | 5 | ||||
-rw-r--r-- | pubkey.h | 20 | ||||
-rw-r--r-- | randpool.cpp | 112 | ||||
-rw-r--r-- | randpool.h | 39 | ||||
-rwxr-xr-x | salsa.cpp | 29 | ||||
-rwxr-xr-x | salsa.h | 1 | ||||
-rw-r--r-- | seal.h | 1 | ||||
-rw-r--r-- | strciphr.h | 5 |
9 files changed, 80 insertions, 146 deletions
@@ -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; @@ -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 <class POLICY_INTERFACE> @@ -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) @@ -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<T> &alg = this->GetSignatureAlgorithm(); const DL_GroupParameters<T> ¶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 c2b44fa..11ece6d 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 <time.h> NAMESPACE_BEGIN(CryptoPP) -typedef MDC<SHA> 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<RandomPoolCipher>::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 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; + SHA256 hash; + hash.Update(m_key, 32); + hash.Update(input, length); + hash.Final(m_key); + m_keySet = false; } -size_t RandomPool::TransferTo2(BufferedTransformation &target, lword &transferBytes, const std::string &channel, bool blocking) +void RandomPool::GenerateIntoBufferedTransformation(BufferedTransformation &target, const std::string &channel, lword size) { - if (!blocking) - throw NotImplemented("RandomPool: nonblocking transfer is not implemented by this object"); - - lword size = transferBytes; - - while (size > 0) + if (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; + 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); } - - 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 @@ -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<BufferedTransformation> +/*! 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<byte, 32> m_key; + FixedSizeSecBlock<byte, 16> m_seed; + member_ptr<BlockCipher> m_pCipher; + bool m_keySet; }; NAMESPACE_END @@ -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 @@ -18,7 +18,6 @@ class CRYPTOPP_NO_VTABLE Salsa20_Policy : public AdditiveCipherConcretePolicy<wo protected: void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length); void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount); - void CipherGetNextIV(byte *IV); void CipherResynchronize(byte *keystreamBuffer, const byte *IV); bool IsRandomAccess() const {return true;} void SeekToIteration(lword iterationCount); @@ -18,7 +18,6 @@ class CRYPTOPP_NO_VTABLE SEAL_Policy : public AdditiveCipherConcretePolicy<word3 protected: void CipherSetKey(const NameValuePairs ¶ms, const byte *key, size_t length); void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount); - void CipherGetNextIV(byte *IV) {PutWord(false, BIG_ENDIAN_ORDER, IV, m_outsideCounter+1);} void CipherResynchronize(byte *keystreamBuffer, const byte *IV); bool IsRandomAccess() const {return true;} void SeekToIteration(lword iterationCount); @@ -73,7 +73,6 @@ struct CRYPTOPP_DLL CRYPTOPP_NO_VTABLE AdditiveCipherAbstractPolicy virtual bool CanOperateKeystream() const {return false;} virtual void OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, 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(byte *keystreamBuffer, const byte *iv) {throw NotImplemented("SimpleKeyingInterface: this object doesn't support resynchronization");} virtual bool IsRandomAccess() const =0; virtual void SeekToIteration(lword iterationCount) {assert(!IsRandomAccess()); throw NotImplemented("StreamTransformation: this object doesn't support random access");} @@ -133,10 +132,8 @@ template <class BASE = AbstractPolicyHolder<AdditiveCipherAbstractPolicy, TwoBas class CRYPTOPP_NO_VTABLE AdditiveCipherTemplate : public BASE { public: - byte GenerateByte(); void GenerateBlock(byte *output, size_t size); 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().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;} |