[tahoe-dev] Windwos installation problems

Zooko O'Whielacronx zookog at gmail.com
Fri Jun 12 10:21:35 PDT 2009


[adding cryptopp-users since I'm talking about stripping out some code
that defends against a certain danger in the Crypto++ random pools.
For context, please see the tahoe-dev thread which starts here:
http://allmydata.org/pipermail/tahoe-dev/2009-June/001951.html ]

On Jun 12, 2009, at 5:58 AM, Black Dew wrote:

> I've traced the problem to the 'time' symbol that is missing from msvcr90.dll but mingw thinks it should be there.
>
> I've fixed my local libmsvcr90.a so mingw sees that it has no 'time' in it and imports it from msvcrt.dll instead - this makes pycryptopp build and work fine.
>
> I'm not sure this is the right long term solution (importing both msvcr90 an msvcrt is not good i think),

Here's another alternative -- attached is a patch that strips out the
uses of time() in Crypto++.  The only uses of time() are when Crypto++
throws the timestamp into random pools when reading from them,
presumably as a source of a few bits more entropy??  I don't think it
is important to do that, and I like the simplicity of removing it.
Wei Dai: what do you think about that?  Am I overlooking any more
important consequence of including those timestamps?

Oh!  I know, this is your defense for the "vm rollback RNG repeat"
problem, isn't it?  The problem is, what if we are running inside a VM
and we read a random number from the RNG, use it in ECDSA, and send
our resulting digital signature out over the network, then our state
gets rolled back by the the VM guest gets rolled back, and then we do
it again and get the same random number but use it to sign a different
message.  How bad is that?  I can't figure out if ECDSA breaks
horribly if you re-use your random integer "k".

We could defend against "the vm rollback RNG repeat" more safely (for
ECDSA specifically) than the current defense (for RNG in general) by
using the message itself (or the hash thereof) as one input of
deterministic generation of the random number k.  (For another input
we'll use a sample from the RNG or even just a static secret known
only to us which we initialize in RAM on startup -- either way nobody
will be able to learn k even after they can verify our digital
signature.)  Of course, then the kind of cryptographers who want
provable security in the standard model and worry about bizarre
weaknesses in SHA-256 would have their hair standing on end, and we
would wave a magic wand called "random oracle" at them and they would
mutter darkly.

But realistically being able to exploit some weird correlation or
pre-image in SHA-256 when used like this is a lot less likely than
suffering a vm rollback RNG repeat, and even, I would hazard a guess,
than the risk of simultaneously suffering both a vm rollback RNG
repeat *and* a clock repeat (which would defeat your current
clock-based defenses).

By the way I just wrote a quick benchmark to see how many cpu cycles
it takes to call time() on linux 2.7.27, and the answer is:

of 8192 trials; best: 273, worst: 4273, mean: 1196, mode: 273

If you're interested, Black Dew, you could try this patch and see if
it allows pycryptopp to link with unpatched mingw libmsvcr90.a.

Warning, this patch eliminates Wei Dai's clock-based defenses against
"vm rollback RNG repeat".  On the other hand, the danger may not be
great.  For example, as far as I know the other crypto libraries such
as OpenSSL or Botan don't implement any such defenses, so even with
this patch we're probably not less safe than they are.

:-)

Regards,

Zooko
-------------- next part --------------
diff -rN -u old-pristine/cryptopp/hrtimer.cpp new-pristine/cryptopp/hrtimer.cpp
--- old-pristine/cryptopp/hrtimer.cpp	2009-06-12 08:21:27.000000000 -0600
+++ new-pristine/cryptopp/hrtimer.cpp	1969-12-31 17:00:00.000000000 -0700
@@ -1,139 +0,0 @@
-// hrtimer.cpp - written and placed in the public domain by Wei Dai
-
-#include "pch.h"
-#include "hrtimer.h"
-#include "misc.h"
-#include <stddef.h>		// for NULL
-#include <time.h>
-
-#if defined(CRYPTOPP_WIN32_AVAILABLE)
-#include <windows.h>
-#elif defined(CRYPTOPP_UNIX_AVAILABLE)
-#include <sys/time.h>
-#include <sys/times.h>
-#include <unistd.h>
-#endif
-
-#include <assert.h>
-
-NAMESPACE_BEGIN(CryptoPP)
-
-#ifndef CRYPTOPP_IMPORTS
-
-double TimerBase::ConvertTo(TimerWord t, Unit unit)
-{
-	static unsigned long unitsPerSecondTable[] = {1, 1000, 1000*1000, 1000*1000*1000};
-
-	assert(unit < sizeof(unitsPerSecondTable) / sizeof(unitsPerSecondTable[0]));
-	return (double)CRYPTOPP_VC6_INT64 t * unitsPerSecondTable[unit] / CRYPTOPP_VC6_INT64 TicksPerSecond();
-}
-
-void TimerBase::StartTimer()
-{
-	m_last = m_start = GetCurrentTimerValue();
-	m_started = true;
-}
-
-double TimerBase::ElapsedTimeAsDouble()
-{
-	if (m_stuckAtZero)
-		return 0;
-
-	if (m_started)
-	{
-		TimerWord now = GetCurrentTimerValue();
-		if (m_last < now)	// protect against OS bugs where time goes backwards
-			m_last = now;
-		return ConvertTo(m_last - m_start, m_timerUnit);
-	}
-
-	StartTimer();
-	return 0;
-}
-
-unsigned long TimerBase::ElapsedTime()
-{
-	double elapsed = ElapsedTimeAsDouble();
-	assert(elapsed <= ULONG_MAX);
-	return (unsigned long)elapsed;
-}
-
-TimerWord Timer::GetCurrentTimerValue()
-{
-#if defined(CRYPTOPP_WIN32_AVAILABLE)
-	LARGE_INTEGER now;
-	if (!QueryPerformanceCounter(&now))
-		throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceCounter failed with error " + IntToString(GetLastError()));
-	return now.QuadPart;
-#elif defined(CRYPTOPP_UNIX_AVAILABLE)
-	timeval now;
-	gettimeofday(&now, NULL);
-	return (TimerWord)now.tv_sec * 1000000 + now.tv_usec;
-#else
-	clock_t now;
-	return clock();
-#endif
-}
-
-TimerWord Timer::TicksPerSecond()
-{
-#if defined(CRYPTOPP_WIN32_AVAILABLE)
-	static LARGE_INTEGER freq = {0};
-	if (freq.QuadPart == 0)
-	{
-		if (!QueryPerformanceFrequency(&freq))
-			throw Exception(Exception::OTHER_ERROR, "Timer: QueryPerformanceFrequency failed with error " + IntToString(GetLastError()));
-	}
-	return freq.QuadPart;
-#elif defined(CRYPTOPP_UNIX_AVAILABLE)
-	return 1000000;
-#else
-	return CLOCKS_PER_SEC;
-#endif
-}
-
-#endif	// #ifndef CRYPTOPP_IMPORTS
-
-TimerWord ThreadUserTimer::GetCurrentTimerValue()
-{
-#if defined(CRYPTOPP_WIN32_AVAILABLE)
-	static bool getCurrentThreadImplemented = true;
-	if (getCurrentThreadImplemented)
-	{
-		FILETIME now, ignored;
-		if (!GetThreadTimes(GetCurrentThread(), &ignored, &ignored, &ignored, &now))
-		{
-			DWORD lastError = GetLastError();
-			if (lastError == ERROR_CALL_NOT_IMPLEMENTED)
-			{
-				getCurrentThreadImplemented = false;
-				goto GetCurrentThreadNotImplemented;
-			}
-			throw Exception(Exception::OTHER_ERROR, "ThreadUserTimer: GetThreadTimes failed with error " + IntToString(lastError));
-		}
-		return now.dwLowDateTime + ((TimerWord)now.dwHighDateTime << 32);
-	}
-GetCurrentThreadNotImplemented:
-	return (TimerWord)clock() * (10*1000*1000 / CLOCKS_PER_SEC);
-#elif defined(CRYPTOPP_UNIX_AVAILABLE)
-	tms now;
-	times(&now);
-	return now.tms_utime;
-#else
-	return clock();
-#endif
-}
-
-TimerWord ThreadUserTimer::TicksPerSecond()
-{
-#if defined(CRYPTOPP_WIN32_AVAILABLE)
-	return 10*1000*1000;
-#elif defined(CRYPTOPP_UNIX_AVAILABLE)
-	static const long ticksPerSecond = sysconf(_SC_CLK_TCK);
-	return ticksPerSecond;
-#else
-	return CLOCKS_PER_SEC;
-#endif
-}
-
-NAMESPACE_END
diff -rN -u old-pristine/cryptopp/hrtimer.h new-pristine/cryptopp/hrtimer.h
--- old-pristine/cryptopp/hrtimer.h	2009-06-12 08:21:27.000000000 -0600
+++ new-pristine/cryptopp/hrtimer.h	1969-12-31 17:00:00.000000000 -0700
@@ -1,61 +0,0 @@
-#ifndef CRYPTOPP_HRTIMER_H
-#define CRYPTOPP_HRTIMER_H
-
-#include "config.h"
-#ifndef HIGHRES_TIMER_AVAILABLE
-#include <time.h>
-#endif
-
-NAMESPACE_BEGIN(CryptoPP)
-
-#ifdef HIGHRES_TIMER_AVAILABLE
-	typedef word64 TimerWord;
-#else
-	typedef clock_t TimerWord;
-#endif
-
-//! _
-class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE TimerBase
-{
-public:
-	enum Unit {SECONDS = 0, MILLISECONDS, MICROSECONDS, NANOSECONDS};
-	TimerBase(Unit unit, bool stuckAtZero)	: m_timerUnit(unit), m_stuckAtZero(stuckAtZero), m_started(false) {}
-
-	virtual TimerWord GetCurrentTimerValue() =0;	// GetCurrentTime is a macro in MSVC 6.0
-	virtual TimerWord TicksPerSecond() =0;	// this is not the resolution, just a conversion factor into seconds
-
-	void StartTimer();
-	double ElapsedTimeAsDouble();
-	unsigned long ElapsedTime();
-
-private:
-	double ConvertTo(TimerWord t, Unit unit);
-
-	Unit m_timerUnit;	// HPUX workaround: m_unit is a system macro on HPUX
-	bool m_stuckAtZero, m_started;
-	TimerWord m_start, m_last;
-};
-
-//! measure CPU time spent executing instructions of this thread (if supported by OS)
-/*! /note This only works correctly on Windows NT or later. On Unix it reports process time, and others wall clock time.
-*/
-class ThreadUserTimer : public TimerBase
-{
-public:
-	ThreadUserTimer(Unit unit = TimerBase::SECONDS, bool stuckAtZero = false)	: TimerBase(unit, stuckAtZero) {}
-	TimerWord GetCurrentTimerValue();
-	TimerWord TicksPerSecond();
-};
-
-//! high resolution timer
-class CRYPTOPP_DLL Timer : public TimerBase
-{
-public:
-	Timer(Unit unit = TimerBase::SECONDS, bool stuckAtZero = false)	: TimerBase(unit, stuckAtZero) {}
-	TimerWord GetCurrentTimerValue();
-	TimerWord TicksPerSecond();
-};
-
-NAMESPACE_END
-
-#endif
diff -rN -u old-pristine/cryptopp/randpool.cpp new-pristine/cryptopp/randpool.cpp
--- old-pristine/cryptopp/randpool.cpp	2009-06-12 08:21:27.000000000 -0600
+++ new-pristine/cryptopp/randpool.cpp	2009-06-12 08:21:28.000000000 -0600
@@ -11,14 +11,14 @@
 #include "randpool.h"
 #include "aes.h"
 #include "sha.h"
-#include "hrtimer.h"
-#include <time.h>
 
 NAMESPACE_BEGIN(CryptoPP)
 
 RandomPool::RandomPool()
 	: m_pCipher(new AES::Encryption), m_keySet(false)
 {
+	memset(m_key, 0, m_key.size());
+	memset(m_seed, 0, m_seed.SizeInBytes());
 }
 
 void RandomPool::IncorporateEntropy(const byte *input, size_t length)
@@ -37,15 +37,6 @@
 		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);
diff -rN -u old-pristine/cryptopp/rng.cpp new-pristine/cryptopp/rng.cpp
--- old-pristine/cryptopp/rng.cpp	2009-06-12 08:21:27.000000000 -0600
+++ new-pristine/cryptopp/rng.cpp	2009-06-12 08:21:28.000000000 -0600
@@ -5,7 +5,6 @@
 #include "rng.h"
 #include "fips140.h"
 
-#include <time.h>
 #include <math.h>
 
 NAMESPACE_BEGIN(CryptoPP)
@@ -66,15 +65,7 @@
 	  m_lastBlock(S),
 	  m_deterministicTimeVector(deterministicTimeVector, deterministicTimeVector ? S : 0)
 {
-	if (!deterministicTimeVector)
-	{
-		time_t tstamp1 = time(0);
-		xorbuf(dtbuf, (byte *)&tstamp1, UnsignedMin(sizeof(tstamp1), S));
-		cipher->ProcessBlock(dtbuf);
-		clock_t tstamp2 = clock();
-		xorbuf(dtbuf, (byte *)&tstamp2, UnsignedMin(sizeof(tstamp2), S));
-		cipher->ProcessBlock(dtbuf);
-	}
+  assert (deterministicTimeVector);
 
 	// for FIPS 140-2
 	GenerateBlock(m_lastBlock, S);
@@ -92,11 +83,7 @@
 		}
 		else
 		{
-			clock_t c = clock();
-			xorbuf(dtbuf, (byte *)&c, UnsignedMin(sizeof(c), S));
-			time_t t = time(NULL);
-			xorbuf(dtbuf+S-UnsignedMin(sizeof(t), S), (byte *)&t, UnsignedMin(sizeof(t), S));
-			cipher->ProcessBlock(dtbuf);
+		  assert(false);
 		}
 
 		// combine enciphered timestamp with seed


More information about the tahoe-dev mailing list