This is noise.c in view mode; [Download] [Up]
/*
* Get environmental noise.
*
* (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved.
* The author assumes no liability for damages resulting from the use
* of this software, even if the damage results from defects in this
* software. No warranty is expressed or implied.
*
* Note that while most PGP source modules bear Philip Zimmermann's
* copyright notice, many of them have been revised or entirely written
* by contributors who frequently failed to put their names in their
* code. Code that has been incorporated into PGP from other authors
* was either originally published in the public domain or is used with
* permission from the various authors.
*
* PGP is available for free to the public under certain restrictions.
* See the PGP User's Guide (included in the release package) for
* important information about licensing, patent restrictions on
* certain algorithms, trademarks, copyrights, and export controls.
*
* Written by Colin Plumb.
*/
#ifdef UNIX
#include <sys/types.h>
#include <sys/time.h> /* For gettimeofday() */
#include <sys/times.h> /* for times() */
#include <stdlib.h> /* For qsort() */
#endif /* UNIX */
#include <time.h>
#include "usuals.h"
#include "randpool.h"
#include "noise.h"
/* Some machines just don't have clock_t */
#if defined(sun) && defined(i386)
typedef long clock_t;
#endif
#if defined(MSDOS) || defined(__MSDOS__)
/* Use IBM PC hardware timer (1.19 MHz) */
#include <conio.h> /* for inp() and outp() */
/* timer0 on 8253-5 on IBM PC or AT tics every .84 usec. */
#define timer0 0x40 /* 8253 timer 0 port */
#define timercntl 0x43 /* 8253 control register */
/*
* On an IBM PC, timer 0 ticks every .84 usec. It counts down
* from 65536 by twos, toggling its output line after each
* step. On an original IBM PC, we can thus only get 15 bits
* of the timer. On a PC-AT or later, with an 8284 timer chip,
* we can get all 16 bits by reading the status, which has the
* state of the output bit in bit 7, and is effectively the
* high bit of the counter.
*
* But latching the status is a command which the 8283 does not
* recognize; the subsequent load is interpreted as one of
* a pair to read the counter instead of the status. (We get a
* garbage bit instead of the one we expected, but that's no worse
* than constant 0.) But the 8283 doesn't like single reads.
* (The 8284 is more forgiving.)
*
* So, to resolve all this, the following sequence is used:
*
* - Dummy read from counter 0 (low byte)
* - Latch status and count (ignored by 8283)
* - Read status (high byte on 8283)
* - Latch count (ignored by 8284, as count is already latched)
* - Read count (low)
* - Read count (high)
*
* It would be better (a project for the future) to capture the counter
* in a keyboard ISR, put it in a global variable, and have noise() read
* the global. This gets the most accurate possible time, and avoids
* possible harmonic relationships with a keyboard polling loop.
* (Which MS-DOS, silly thing that it is, almost certainly uses
* internally.)
*/
static unsigned pctimer0(void)
{
unsigned count;
inp(timer0);
outp(timercntl, 0xC2); /* Latch status and count for timer 0 */
count = (inp(timer0) & 0x80) << 8;
outp(timercntl, 0x00); /* Latch count of timer 0 */
count |= (inp(timer0) & 0xFF) >> 1;
count |= (inp(timer0) & 0xFF) << 7;
return count;
}
#endif /* MSDOS || __MSDOS__ */
#ifdef UNIX
#define NOISEDEBUG
#ifdef NOISEDEBUG
#include "pgp.h" /* for verbose and pgpout */
#include <stdio.h>
#endif
/* Function needed for qsort() */
static int noiseCompare(void const *p1, void const *p2)
{
return *(int const *) p1 - *(int const *) p2;
}
#define DELTAS 15 /* Number of deltas to try */
/*
* Find the resolution of the gettimeofday() clock by sampling
* successive values until a tick boundary, at which point
* the delta is entered into a table. The median of the table is
* returned as the system tick size.
*
* Some trickery is needed to defeat the habit systems have of
* always incrementing the microseconds field so that no two calls
* return the same value. Thus, a "tick boundary" is assumed
* when successive calls return a difference of >2 us.
* (This catches cases where we make successive calls and one
* other task sneaks in between. More tasks in between are
* sufficiently unlikely that they'll get cut off by the median
* filter.
*
* When a tick boundary is found, the *first* time read during
* the previous tick (tv_base) is subtracted from the new time
* to get the microseconds per tick.
*
* The median of the ticks is taken to eliminate outliers due to
* descheduling (extra large) or tv_base not being the "zero" time
* in a given tick (slightly small).
*
* Note that Suns have a 1 us timer, and in SunOS 4.1, they return
* that timer, but there is ~50 us of system-call overhead to get
* it, so this overestimates the tick size consdierably. On
* SunOS 5.x/Solaris, the overhead has been cut to about 2.5 us,
* so the inter-call time alternates between 2 and 3 us. Some
* better algorithms are required to cope with potentially faster
* machines that really do return 1 us granularity.
*
* Current best idea (unimplemented): Sample a large number, and
* track small (< 100 us) deltas in an array of counters, and
* large ones in an array of deltas. There should be three
* bumps: 1 us auto-increment, the tick size (which may blend into
* the previous bump), and time-slicing. We want to throw out
* the latter, then compute the average delta as the average cost
* of making a call, then throw out the small values if they
* are suspisciously smaller than this value. Then some average
* of the remainder should provide a good value for the cost of
* making a call.
*
* The alternative to all this is to actually model the keystroke
* latencies and compute the entropy directly. A model considering
* the previous interval only should be adequate.
*/
static unsigned noiseTickSize(void)
{
int i;
int j;
unsigned deltas[DELTAS];
unsigned t;
struct timeval tv_base, tv_old, tv_new;
i = j = 0;
gettimeofday(&tv_base, 0);
tv_old = tv_base;
do {
gettimeofday(&tv_new, 0);
if (tv_new.tv_usec > tv_old.tv_usec + 2) {
deltas[i++] = tv_new.tv_usec - tv_base.tv_usec +
1000000 * (tv_new.tv_sec - tv_base.tv_sec);
tv_base = tv_new;
j = 0;
}
tv_old = tv_new;
/*
* If we are forever getting <= 2 us, then just assume
* it's 2 us.
*/
if (j++ > 10000)
return 2;
} while (i < DELTAS);
qsort(deltas, DELTAS, sizeof(deltas[0]), noiseCompare);
t = deltas[DELTAS / 2]; /* Median */
#ifdef NOISEDEBUG
if (verbose)
fprintf(pgpout, "t = %u, clock frequency is %u Hz\n",
t, (2000000 + t) / (2 * t));
#endif
return t;
}
#endif /* UNIX */
/*
* Add as much environmentally-derived random noise as possible
* to the randPool. Typically, this involves reading the most
* accurate system clocks available.
*
* Returns the number of ticks that hasve passed since the last call,
* for entropy estimation purposes.
*/
word32
noise(void)
{
static word32 lastcounter;
word32 delta;
time_t tnow;
clock_t cnow;
cnow = clock();
randPoolAddBytes((byte *) & cnow, sizeof(cnow));
tnow = time((time_t *) 0);
randPoolAddBytes((byte *) & tnow, sizeof(tnow));
#if defined(MSDOS) || defined(__MSDOS__)
{
unsigned t;
t = pctimer0();
randPoolAddBytes((byte *) & t, sizeof(t));
delta = t - (unsigned) lastcounter;
lastcounter = t;
}
#endif
#ifdef VMS
{
word32 t;
/* VMS Hardware Clock */
extern unsigned long vms_clock_bits[2];
/* Clock update int. */
extern const long vms_ticks_per_update;
/* Capture fast system timer: */
SYS$GETTIM(vms_clock_bits);
randPoolAddBytes((byte *) & vms_clock_bits, sizeof(vms_clock_bits));
t = vms_clock_bits[0] / vms_ticks_per_update;
delta = t - lastcounter;
lastcounter = t;
}
#endif /* VMS */
#ifdef UNIX
/* Get noise from gettimeofday() */
{
struct timeval tv;
word32 t;
static unsigned ticksize = 0;
if (!ticksize)
ticksize = noiseTickSize();
gettimeofday(&tv, NULL);
randPoolAddBytes((byte *) & tv, sizeof(tv));
/* This may wrap, but it's unsigned, so that's okay */
t = tv.tv_sec * 1000000 + tv.tv_usec;
delta = t - lastcounter;
lastcounter = t;
delta /= ticksize;
}
/* Get noise from times() */
{
clock_t t;
struct tms tms;
t = times(&tms);
randPoolAddBytes((byte *) & tms, sizeof(tms));
randPoolAddBytes((byte *) & t, sizeof(t));
}
#endif /* UNIX */
return delta;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.