📄 pgprandompool.c
字号:
* expressed in bits.)
*
* The internal amount-of-entropy accumulator is maintained in two
* halves: a counter of bits, and a fraction of a bit, expressed
* in the form of a normalized delta. If 0 <= f < 1 is a fraction
* of a bit, then 1 <= 2^f < 2, so it's remembered as
* 0 <= x = 2^f - 1 < 1, a fixed-point number.
*
* Given a new fractional-bit delta, 1+y in similar form (obtained
* by normalizing the delta into the desired form), the output
* fractional bit delta 1+z = (1+x) * (1+y). If this exceeds 2,
* divide it by 2 and add a full bit to the bit counter.
*
* The implementation, of course, actually computes z = x + y + x*y,
* where 0 <= z < 3. This can be done by adding each of the three terms
* (each of which is less than 1) and noticing the overflow. If the
* addition does not overflow, z < 1 and nothing needs to be done.
*
* If one addition overflows, 1 <= z < 2, but after overflow we get
* z-1. We want (1+z)/2-1 = (z-1)/2, so just divide the result of
* the wrapped addition by 2.
*
* If both additions overflow, the addition wraps to z-2, but we
* still want (z-1)/2 = (z-2)/2 + 1/2, so divide the wrapped result
* by 2 and add 1/2.
*
* Due to the way that the fixed-point numbers are stored, the x*y part is
* the high half of the 32-bit unsigned product of x and y. This can be
* safely underestimated if desired, if a 64-bit product is difficult to
* compute.
*
* The simplest snd safest definition is
* #define UMULH_32(r,a,b) (r) = 0
*/
#ifndef UMULH_32
/* It appears that gcc 2.95+ doesn't like this */
#if defined(__GNUC__) && defined(__i386__) && (__GNUC__ <= 2) && (__GNUC_MINOR__ < 95)
/* Inline asm goodies */
#define UMULH_32(r,a,b) __asm__("mull %2" : "=d"(r) : "%a"(a), "mr"(b) : "ax")
#elif HAVE64
#define UMULH_32(r,a,b) ((r) = (PGPUInt32)((word64)(a) * (b) >> 32))
#else
/* Underestimate the product */
#define UMULH_32(r,a,b) ((r) = ((a) >> 16) * ((b) >> 16))
#endif
#endif /* !UMULH_32 */
#define DERATING 2 /* # of bits to underestimate */
static PGPUInt32
pgpGlobalRandomPoolEntropyWasAdded(
PGPUInt32 delta )
{
PGPUInt32 frac,
t;
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;
}
/*
* PGPGlobalRandomPoolGetEntropy
* - Returns the amount of entropy currently in the random pool.
* - Modified to fill the pool with SDK driver random pool entropy when we can.
* - Modified to fill the pool with Intel-RNG randomness when we can.
*/
#if ! PGPSDK_DRIVER
static PGPUInt32
pgpGlobalRandomPoolGetEntropy_internal()
#else /* PGPSDK_DRIVER */
PGPUInt32
PGPGlobalRandomPoolGetEntropy()
#endif /* PGPSDK_DRIVER */
{
PGPUInt32 randBits;
RandomPool *pool = GetPool();
PGPRMWOLockStartWriting( &pool->criticalLock );
randBits = pool->randBits;
#if (PGP_WIN32 || PGP_MACINTOSH) && ! PGPSDK_DRIVER
if( randBits < RANDPOOLBITS && pgpSDKEntropyDriverIsRunning() )
{
PGPUInt32 getBits;
PGPUInt32 getBytes;
PGPError err;
/* Add bits from entropy driver, if present */
getBits = pgpDriverRandomPoolGetEntropy();
if( randBits + getBits > RANDPOOLBITS )
{
getBits = RANDPOOLBITS - randBits;
}
getBytes = getBits / 8;
while( getBytes != 0 )
{
PGPByte buffer[32];
PGPSize readCount;
readCount = getBytes;
if( readCount > sizeof( buffer ) )
readCount = sizeof( buffer );
err = pgpDriverRandomPoolGetBytesEntropy(buffer, readCount);
pgpAssert( err == kPGPError_NoErr );
if (err != kPGPError_NoErr) break;
pgpGlobalRandomPoolAddEntropyBits( readCount * 8 );
sRandPoolAddBytes( NULL, &buffer[0], readCount );
getBytes -= readCount;
}
randBits = pool->randBits;
}
#endif
#if PGP_INTEL_RNG_SUPPORT
/*
* If the pool isn't full and we have Intel RNG,
* add RNG bits into the pool.
*/
if (randBits < RANDPOOLBITS && pgpIntelRngEnabled()) {
while (randBits < RANDPOOLBITS) {
uint32 uiRandom;
if (IsdGetRandomNumber(&uiRandom) != ISD_EOK)
break;
pgpGlobalRandomPoolAddEntropyBits(INTEL_RNG_ENTROPY_BITS);
sRandPoolAddBytes(NULL, (PGPByte *)&uiRandom, sizeof(uiRandom));
randBits = pool->randBits;
}
}
#endif
PGPRMWOLockStopWriting( &pool->criticalLock );
return( randBits );
}
/*
* This is used for the case where we do a calculation that uses up some
* entropy, then we abort it and discard the result. Before doing the calc,
* call PGPGlobalRandomPoolGetEntropy. Then, after the abort, call
* pgpGlobalRandomPoolSetEntropy to set randBits back to what it was.
* This should only be done within a single thread so that we don't miscount
* by having another thread drain entropy in between the calls.
*/
void
pgpGlobalRandomPoolSetEntropy( PGPUInt32 randBits )
{
RandomPool *pool = GetPool();
PGPRMWOLockStartWriting( &pool->criticalLock );
pool->randBits = pgpMax( randBits, pool->randBits );
PGPRMWOLockStopWriting( &pool->criticalLock );
}
#if ! PGPSDK_DRIVER
PGPUInt32
PGPGlobalRandomPoolGetEntropy()
{
PGPUInt32 entropy;
pgpEnterZeroFunction();
pgpGlobalRandomPoolGetInfo_back( &entropy, NULL, NULL, NULL, NULL );
return entropy;
}
#endif /* not PGPSDK_DRIVER */
void
pgpGlobalRandomPoolAddEntropyBits(PGPInt32 num)
{
RandomPool *pool = GetPool();
if ((pool->randKeyBits += num) >= RANDKEYBITS) {
pool->randKeyBits = RANDKEYBITS;
pool->randKeyFrac = 0;
}
}
void
pgpGlobalRandomPoolAddEntropy(
void * priv,
PGPByte const * p,
unsigned len) /* in bytes */
{
RandomPool *pool = GetPool();
PGPRMWOLockStartWriting( &pool->criticalLock );
pgpGlobalRandomPoolAddEntropyBits( len * 8 );
sRandPoolAddBytes( priv, p, len );
PGPRMWOLockStopWriting( &pool->criticalLock );
}
#if ! PGPSDK_DRIVER
static PGPUInt32
pgpGlobalRandomPoolGetSize_internal()
{
return RANDPOOLBITS;
}
#endif /* not PGPSDK_DRIVER */
PGPUInt32
PGPGlobalRandomPoolGetSize()
{
#if ! PGPSDK_DRIVER
PGPUInt32 size;
pgpGlobalRandomPoolGetInfo_back( NULL, &size, NULL, NULL, NULL );
return size;
#else /* PGPSDK_DRIVER */
return RANDPOOLBITS;
#endif /* PGPSDK_DRIVER */
}
static 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.
*/
#if ! PGPSDK_DRIVER
static PGPBoolean
pgpGlobalRandomPoolHasMinimumEntropy_internal( void )
{
PGPUInt32 poolbits;
pgpEnterBooleanFunction( FALSE );
if( pgpGlobalRandomPoolIsSeeded( ) )
return TRUE;
poolbits = pgpGlobalRandomPoolGetInflow( );
if( poolbits >= kMinimumSeedBits )
return TRUE;
#if (PGP_WIN32 || PGP_MACINTOSH) && ! PGPSDK_DRIVER
if( pgpSDKEntropyDriverIsRunning( ) )
poolbits += pgpDriverRandomPoolGetEntropy( );
if( poolbits >= kMinimumSeedBits )
return TRUE;
#endif
return FALSE;
}
#endif /* not PGPSDK_DRIVER */
PGPBoolean
PGPGlobalRandomPoolHasMinimumEntropy( void )
{
#if ! PGPSDK_DRIVER
PGPBoolean hasMinEntropy;
#endif /* not PGPSDK_DRIVER */
pgpEnterBooleanFunction( FALSE );
#if ! PGPSDK_DRIVER
pgpGlobalRandomPoolGetInfo_back( NULL, NULL, NULL, &hasMinEntropy, NULL );
return hasMinEntropy;
#else /* PGPSDK_DRIVER */
return pgpGlobalRandomPoolIsSeeded() ||
pgpGlobalRandomPoolGetInflow() >= kMinimumSeedBits ;
#endif /* PGPSDK_DRIVER */
}
/*
* 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.
*/
#if ! PGPSDK_DRIVER
static PGPUInt32
pgpGlobalRandomPoolGetMinimumEntropy_internal( void )
{
return kMinimumSeedBits;
}
#endif /* not PGPSDK_DRIVER */
PGPUInt32
PGPGlobalRandomPoolGetMinimumEntropy( void )
{
#if ! PGPSDK_DRIVER
PGPUInt32 minEntropy;
pgpGlobalRandomPoolGetInfo_back( NULL, NULL, &minEntropy, NULL, NULL );
return minEntropy;
#else /* PGPSDK_DRIVER */
return kMinimumSeedBits;
#endif /* PGPSDK_DRIVER */
}
#if ! PGPSDK_DRIVER
PGPError
pgpGlobalRandomPoolGetInfo_internal( PGPUInt32 *entropy, PGPUInt32 *size,
PGPUInt32 *minEntropy, PGPBoolean *hasMinEntropy,
PGPBoolean *hasIntelRNG )
{
if( IsntNull( entropy ) )
*entropy = pgpGlobalRandomPoolGetEntropy_internal( );
if( IsntNull( size ) )
*size = pgpGlobalRandomPoolGetSize_internal( );
if( IsntNull( minEntropy ) )
*minEntropy = pgpGlobalRandomPoolGetMinimumEntropy_internal( );
if( IsntNull( hasMinEntropy ) )
*hasMinEntropy = pgpGlobalRandomPoolHasMinimumEntropy_internal( );
if( IsntNull( hasIntelRNG ) )
{
*hasIntelRNG = FALSE;
#if PGP_INTEL_RNG_SUPPORT
*hasIntelRNG = pgpIntelRngEnabled();
#endif
}
return kPGPError_NoErr;
}
#endif /* PGPSDK_DRIVER */
/* 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -