📄 pgprandompool.c
字号:
unsigned n;
RandomPool * pool = GetPool();
if (delta < 1 << DERATING)
return 0;
n = 31-DERATING;
if (!(delta & 0xffff0000))
delta <<= 16, n -= 16;
if (!(delta & 0xff000000))
delta <<= 8, n -= 8;
if (!(delta & 0xf0000000))
delta <<= 4, n -= 4;
if (!(delta & 0xc0000000))
delta <<= 2, n -= 2;
if (!(delta & 0x80000000))
delta <<= 1, n -= 1;
pgpAssert(n < 32);
/* Lose high-order bit of delta */
pgpAssert(delta & 0x80000000);
delta <<= 1;
frac = pool->randKeyFrac;
UMULH_32(t,delta,frac);
if ((frac += t) < t) {
if ((frac += delta) < delta)
frac = (frac >> 1) + 0x80000000ul;
else
frac >>= 1;
n++;
} else if ((frac += delta) < delta) {
frac >>= 1;
n++;
}
/* The timers used are very high-resolution. Be
** conservative with our estimates. No more than
** 10 bits per sample.
*/
if( n > 10 )
n = 10;
/* Store back updated fractional bits */
pool->randKeyFrac = frac;
/* n is now the count of whole bits */
if ((pool->randKeyBits += n) >= RANDKEYBITS) {
/* Overflow - saturate at RANDKEYBITS */
pool->randKeyBits = RANDKEYBITS;
pool->randKeyFrac = 0;
}
return n;
}
PGPUInt32
PGPGlobalRandomPoolGetEntropy()
{
PGPUInt32 randBits;
RandomPool *pool = GetPool();
PGPRMWOLockStartReading( &pool->criticalLock );
randBits = pool->randBits;
PGPRMWOLockStopReading( &pool->criticalLock );
return( randBits );
}
PGPUInt32
PGPGlobalRandomPoolGetSize()
{
return RANDPOOLBITS;
}
PGPUInt32
pgpGlobalRandomPoolGetInflow()
{
PGPUInt32 randInBits;
RandomPool *pool = GetPool();
PGPRMWOLockStartReading( &pool->criticalLock );
randInBits = pool->randInBits;
PGPRMWOLockStopReading( &pool->criticalLock );
return( randInBits );
}
/*
* Returns true if we have enough entropy to proceed with signature or
* encryption functions. We go ahead if we have either seeded the RNG
* successfully from the disk, or if we have acquired a certain
* minimum of bits from the environment during our run.
*/
PGPBoolean
PGPGlobalRandomPoolHasMinimumEntropy( void )
{
return pgpGlobalRandomPoolIsSeeded() ||
pgpGlobalRandomPoolGetInflow() >= kMinimumSeedBits ;
}
/*
* Returns the minimum amount of entropy we would like to see in the random
* pool before signing or encryption. Before doing one of those functions,
* we first use PGPGlobalRandomPoolHasMinimumEntropy to determine whether
* to proceed. If that returns FALSE, we should acquire entropy until the
* amount in the random pool is at least equal to what this function returns.
* Note that PGPGlobalRandomPoolHasMinimumEntropy uses the inflow value,
* while if that returns false we will use the actual amount of randomness
* in the pool, which is always <= the inflow value. Using the inflow value
* is safe enough as it indicates that we are seeded, but once we decide we
* need to go to the effort of acquiring more entropy from the environment,
* we might as well use the GetEntropy() value, which is more conservative.
*/
PGPUInt32
PGPGlobalRandomPoolGetMinimumEntropy( void )
{
return kMinimumSeedBits;
}
/* Number of elements in the given array */
#define elemsof(x) ( (PGPUInt32)( sizeof(x) / sizeof(*x) ) )
/*
* Estimate the amount of entropy in an input value x by seeing how
* different it is from what has come before.
*
* This is based on computing the delta from the previous value, and the
* second-order delta from the previous delta, and so on, for h orders.
* The minimum of any of those deltas is returned as the entropy estimate.
* (Which must, of course, have a logarithm taken to produce a "number
* of bits" output.)
*
* This requires a pointer to a h-word history table of previous deltas.
* A value of h = 3 is generally good, but some things (like keystroke
* timings) feed deltas and not input values into this, so that removes
* the first level.
*/
static PGPUInt32
randEstimate(PGPUInt32 x, PGPUInt32 *history, unsigned h)
{
PGPUInt32 t,
min = x;
while (h--) {
t = history[h]; /* Last delta */
t = (x > t) ? x - t : t - x; /* |x - history[h]| */
history[h] = x;
x = t;
if (min > x)
min = x;
}
return min;
}
static PGPUInt32
sCollectEntropy()
{
PGPUInt32 delta;
PGPRandomContext rc;
/* call machine specific routine to get some entropy */
pgpInitGlobalRandomPoolContext( &rc );
delta = pgpRandomCollectEntropy( &rc );
return( delta );
}
/*
* Gather and estimate entropy from keyboard timings. Double letters
* are allowed, but triples and more are considered suspiscious and
* entropy is not counted. (The actual criterion is that the current
* letter has appeared more than once in the previous four letters,
* which rejects aaaa... and ababa...)
*
* The "letter" can be generalized to mouse-clicks, button-pushes, menu
* selections, or anything else that can be categorized into a finite
* number of events.
*
* Question: is there a way to achieve this effect without remembering
* the recent keystrokes so explicitly? It seems like a possible
* security hole.
*
* We incorporate entropy from the first 3 samples, but don't count them
* since only after that many do we get reliable per-sample entropy estimates.
* (This is time for the two entries in teh hist array to get initialized,
* plus the one level of delta history implicitly included in the
* ranGetEntropy timing. It has to be there unless we want to export
* knowledge about the modulus at which the timer it uses wraps.)
*/
PGPUInt32
PGPGlobalRandomPoolAddKeystroke( PGPInt32 event)
{
static PGPInt32 pastevent[4]; /* Last 4 events */
static PGPUInt32 hist[2];
static unsigned histcount=elemsof(hist)+1;
/* # invalid entries in hist array */
PGPUInt32 delta;
unsigned n = 0;
int i;
RandomPool *pool = GetPool();
PGPUInt32 result;
delta = sCollectEntropy();
delta = randEstimate(delta, hist, elemsof(hist));
PGPRMWOLockStartWriting( &pool->criticalLock );
sRandPoolAddBytes(NULL, (PGPByte *)&event, sizeof(event));
/* Check for repetitive keystroke patterns */
i = elemsof(pastevent) - 1;
n = (event == pastevent[i]);
do {
n += (event == (pastevent[i] = pastevent[i-1]));
} while (--i);
pastevent[0] = event;
if (histcount > 0) {
/* Not yet filled hist array */
--histcount;
result = 0;
}
else if( n > 1 )
{
result = 0;
}
else
{
result = pgpGlobalRandomPoolEntropyWasAdded(delta);
}
PGPRMWOLockStopWriting( &pool->criticalLock );
return( result );
}
/*
* Generate entropy from mouse motion. This simply measures entropy
* of timing, although that may be pretty low due to OS synchrony.
*
* We incorporate entropy from the first 2 samples, but don't count them
* since only after that many do the predictors start working.
*/
PGPUInt32
PGPGlobalRandomPoolMouseMoved(void)
{
static PGPUInt32 hist[2]; /* Timing history */
static unsigned histcount=elemsof(hist);
/* # invalid entries in histx array */
PGPUInt32 delta; /* Timing delta */
RandomPool *pool = GetPool();
PGPUInt32 result;
delta = sCollectEntropy();
delta = randEstimate(delta, hist, elemsof(hist));
PGPRMWOLockStartWriting( &pool->criticalLock );
/* Wait until we have filled our arrays to start counting entropy */
if (histcount > 0)
{
--histcount;
result = 0;
}
else
{
result = pgpGlobalRandomPoolEntropyWasAdded(delta);
}
PGPRMWOLockStopWriting( &pool->criticalLock );
return( result );
}
/*
* Generate entropy from mouse motion. This all is provided for backwards
* compatability. Mouse coordinates are no longer used.
*/
PGPUInt32
PGPGlobalRandomPoolAddMouse(
PGPUInt32 x,
PGPUInt32 y)
{
(void) x;
(void) y;
return( PGPGlobalRandomPoolMouseMoved() );
}
/*
* DUMMY POOL
*
* The dummy pool is a pluggable replacement for the RandPool to be used
* for generating values which we want to be unique but don't have to be
* cryptographically strong: such as the p and q values used in discrete
* log keys. We prefer not to use the regular RandPool for such values
* so that we don't leak information about the secret values which are
* calculated in close proximity.
*
* The dummy pool should be used as the "base" paramater for
* pgpRandomCreateX9_17. It always returns 0 values, and the X9.17 RNG
* will then produce non-cryptographically-strong pseudo-random numbers.
*
* Use pgpRandomAddBytes on the X9.17 RandomContext to seed it.
*/
/* Dummy pool always returns zeros */
static void
sDummyPoolGetBytesEntropy(
void * priv,
PGPByte * buf,
unsigned len,
unsigned bits)
{
(void)priv;
(void)bits;
pgpClearMemory( buf, len );
}
static void
sDummyPoolAddBytes(
void * priv,
PGPByte const * p,
unsigned len)
{
(void)priv;
(void)p;
(void)len;
}
static void
sDummyPoolStir(void *priv)
{
(void)priv;
}
static const PGPRandomVTBL sDummyRandomPoolVTBL =
{
"Dummy random-number pool",
sDummyPoolAddBytes,
sDummyPoolGetBytesEntropy,
sDummyPoolStir
};
PGPRandomVTBL const *
pgpGetGlobalDummyRandomPoolVTBL( void )
{
return( &sDummyRandomPoolVTBL );
}
PGPError
PGPGlobalRandomPoolAddSystemState()
{
PGPRandomContext rc;
pgpInitGlobalRandomPoolContext( &rc );
if (pgpRandomCollectOsData( &rc )) {
pgpSetIsSeeded();
return kPGPError_NoErr;
}
else {
return kPGPError_UnknownError;
}
}
/*
* Accumulate entropy from key into pool.
* See comments with pgpGlobalRandomPoolEntropyWasAdded for how
* the arithmetic works.
*/
static void
sRandAddKeyEntropy( RandomPool *pool )
{
PGPUInt32 frac, /* Fractional bits to add */
keyfrac,
t;
PGPUInt32 n; /* Number of whole bits to add */
/* Add entropy from randKey into randPool */
keyfrac = pool->randKeyFrac;
frac = pool->randFrac;
n = pool->randKeyBits;
UMULH_32(t,keyfrac,frac);
/* The comparison idiom used here detects overflow on addition */
if ((frac += t) < t) {
if ((frac += keyfrac) < keyfrac)
frac = (frac >> 1) + 0x80000000ul;
else
frac >>= 1;
n++;
} else if ((frac += keyfrac) < keyfrac) {
frac >>= 1;
n++;
}
pool->randFrac = frac;
if ((pool->randBits += n) >= RANDPOOLBITS) {
/* Overflow - saturate at RANDPOOLBITS */
pool->randBits = RANDPOOLBITS;
pool->randFrac = 0;
}
/* Also count the inflow without regard to outflow */
if ((pool->randInBits += n) >= RANDPOOLBITS) {
pool->randInBits = RANDPOOLBITS;
}
pool->randKeyBits = pool->randKeyFrac = 0;
}
/*__Editor_settings____
Local Variables:
tab-width: 4
End:
vi: ts=4 sw=4
vim: si
_____________________*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -