⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pgprandompool.c

📁 PGP8.0源码 请认真阅读您的文件包然后写出其具体功能
💻 C
📖 第 1 页 / 共 3 页
字号:
/*____________________________________________________________________________
	Copyright (C) 2002 PGP Corporation
	All rights reserved.

	$Id: pgpRandomPool.c,v 1.28 2002/11/11 04:49:06 dallen Exp $
____________________________________________________________________________*/
/*
 * True random number computation and storage
 *
 * Wrtten by Colin Plumb.
 *
 * This performs a strong hash on the data as it is fed into the
 * pool, and the pool is again strongly hashed to produce the output
 * of the random-number generator.  This is overkill, as either step
 * should produce high-quality numbers.
 */
#include "pgpConfig.h"
#include "pgpSDKPriv.h"
#if ! PGPSDK_DRIVER
#include "pgpKeyPriv.h"
#endif /* not PGPSDK_DRIVER */

#include <stdlib.h>
#include <string.h>

#include "pgpBase.h"
#include "pgpMem.h"

#include "pgpSHA.h"
#include "pgpRnd.h"

#include "pgpDebug.h"
#include "pgpRandomPoolPriv.h"
#include "pgpRndSeed.h"
#include "pgpRMWOLock.h"
#include "pgpSDKBuildFlags.h"

#if PGP_MACINTOSH
#include "PGPsdkDriverAPI.h"
#endif

#if PGP_WIN32
#include "PGPsdkDriver.h"
#endif

#if !PGP_WIN32 || PGPSDK_DRIVER
#undef PGP_INTEL_RNG_SUPPORT
#define PGP_INTEL_RNG_SUPPORT 0
#endif

#if PGP_INTEL_RNG_SUPPORT
#include "isec.h"
#endif

/* We import the following things from the SHA code */
#define HashTransform pgpSHATransform
#define HASH_INWORDS 16
#define HASH_OUTWORDS 5

/*
 * The pool must be a multiple of the hash input size and the hash
 * output size.  For SHA, these are 16 and 5 words, respectively,
 * so the pool must be a multiple of 80 words.  If the hash were
 * MD5, it would have to be a multiple of 16 and of 4, or a multiple
 * of 16 all together.  (80 words is 2560 bits.)
 */
#define RANDPOOLWORDS 320	/* 10,240 bits */
#define RANDPOOLBITS (32*RANDPOOLWORDS)
#if RANDPOOLWORDS % HASH_OUTWORDS
#error RANDPOOLWORDS must be a multiple of HASH_OUTWORDS
#endif
#if RANDPOOLWORDS % HASH_INWORDS
#error RANDPOOLWORDS must be a multiple of HASH_INWORDS
#endif

#define RANDKEYBITS	(32*HASH_INWORDS)

/* minimum number of bytes to consider the random pool seeded */
#define kMinimumSeedBytes		24
#define kMinimumSeedBits		( 8 * kMinimumSeedBytes )



typedef struct RandomPool
{
	/*
	 * Accumulators to estimate entropy in the pool.
	 * See comments with pgpGlobalRandomPoolEntropyWasAdded for how
	 * fractional bits are stored.
	 */
	PGPUInt32	randBits; /* Bits of entropy in pool */
	PGPUInt32	randFrac; /* Fraction of a bit of entropy in pool */
	PGPUInt32	randInBits; /* Bits of entropy added to pool */

	/* Estimate entropy in randKey array, a buffer which dumps into pool */
	PGPUInt32	randKeyBits; /* Bits of entropy in randKey */
	PGPUInt32	randKeyFrac; /* Fraction of a bit of entropy in randKey */

	/* Must be word-aligned, so make it words.  Cast to bytes as needed. */
	PGPUInt32	randPool[RANDPOOLWORDS];	/* Random pool */
	PGPUInt32	randPoolAddPos;	/* Position to encrypt into (words) */

	PGPUInt32	randKey[HASH_INWORDS];	/* Random input buffer */
	PGPUInt32	randKeyAddPos;	/* Position to add to (bytes) */

	PGPUInt32	randOut[HASH_OUTWORDS];	/* Random output buffer */
	PGPUInt32	randOutGetPos; /* Position to get from (b) */

	/* Keeps track of when we need to do another hashing step */
	PGPUInt32	randHashCounter;
	
	PGPRMWOLock	criticalLock;
		
} RandomPool;

/* This is the global random pool */
static RandomPool	sPool	= {0,};
#define GetPool()		( &sPool )

static void sRandAddKeyEntropy( RandomPool *pool );


	PGPError
pgpInitGlobalRandomPool()
{
	RandomPool *	pool	= NULL;
	
	pool	= GetPool();
	
	pool->randBits			= 0;
	pool->randFrac			= 0;
	pool->randInBits		= 0;
	pool->randKeyBits		= 0;
	pool->randKeyFrac		= 0;
	pool->randPoolAddPos	= 0;
	pool->randKeyAddPos		= 0;
	pool->randOutGetPos		= sizeof( pool->randOut);
	pool->randHashCounter	= 0;
	
	InitializePGPRMWOLock( &pool->criticalLock );
	
	return( kPGPError_NoErr );
}

	PGPError
pgpCleanupGlobalRandomPool()
{
	RandomPool *	pool	= NULL;
	
	pool	= GetPool();
	
	DeletePGPRMWOLock( &pool->criticalLock );
	
	return( kPGPError_NoErr );
}

/*
 * To mix input into the pool, we insert it into a key buffer and then
 * use the key buffer to CBC-encrypt the pool.  Actually, to make sure
 * that the input (which includes such sensitive information as passphrase
 * keystrokes) is not left in memory, we XOR it with the old contents of
 * the key buffer along with some of the the data about to be encrypted.
 * Since none of the disguising data depends on the byte, this does
 * not destroy any entropy inherent in the input byte.
 *
 * To reduce burstiness in the timing, rather than wait until the key
 * buffer is full to encrypt the entire pool, we update part of the key
 * and then encrypt part of the pool.  The randHashCounter keeps track
 * of what's going on to ensure that a byte of key is not overwritten
 * until it has participated in the encryption of every block of the pool.
 *
 * The pool contains RANDPOOLWORDS/HASH_OUTWORDS blocks.  We must do
 * that many hash-encryption steps before 4*HASH_INWORDS bytes of the
 * key have been overwritten.  So randHashCounter is incremented by
 * RANDPOOLWORDS/HASH_OUTWORDS each time a byte is added to the
 * randKey array, and it is decreased by 4*HASH_INWORDS each time we
 * do a hash-encryption step.  This keeps the two processes in
 * balance.
 *
 * We store entropy in the randKey array until some minimum is
 * reached, or until we have filled the entire randKey array, then we
 * dump the entropy into the randPool as just described.  This
 * prevents an attacker who knows the initial seed value and who has
 * access to the output of the randPool from guessing the input values
 * by trying all possibilities.
 *
 * The bits deposited need not have any particular distribution; the
 * stirring operation transforms them to uniformly-distributed bits.
 */
	static void
sRandPoolAddByte(
	RandomPool *	pool,
	PGPByte			b)
{
	int				i;
	PGPUInt32		pos;

	/* Mix new byte into pool */
	((PGPByte *)pool->randKey)[pool->randKeyAddPos] ^= b;
	if (++pool->randKeyAddPos == sizeof(pool->randKey))
		pool->randKeyAddPos = 0;

	/*
	 * If we've reached the end of a word, mask the next word with
	 * a soon-to-be-overwritten byte of the pool.  With a 64-byte input
	 * hash, this will use a different word for masking each of the
	 * 16 words as long as there are at least 16 output blocks
	 * (64 words at 4 words per output block, or 512 bits total) in the
	 * pool.  Just to be sure, let's enforce it...
	 */
#if HASH_INWORDS > RANDPOOLWORDS / HASH_OUTWORDS
#error Please make RANDPOOLWORDS bigger than HASH_INWORDS * HASH_OUTWORDS.
#endif
	if (pool->randKeyAddPos % sizeof(PGPUInt32) == 0)
		*(PGPUInt32 *)((PGPByte *)pool->randKey + pool->randKeyAddPos) ^=
		                            pool->randPool[pool->randPoolAddPos];

	/* Do a hashing step if required */
	pool->randHashCounter += RANDPOOLWORDS/HASH_OUTWORDS;

	/* Update pool every 64 bits of entropy, or when about to wrap around */
	if (pool->randKeyBits >= 64 ||
		pool->randHashCounter >= 4*HASH_INWORDS*RANDPOOLWORDS/HASH_OUTWORDS) {
		while (pool->randHashCounter >= 4*HASH_INWORDS) {
			pool->randHashCounter -= 4*HASH_INWORDS;

			/* CBC-encrypt the current block */
			pos = pool->randPoolAddPos;
			HashTransform(pool->randPool + pos, pool->randKey);

			/* Get the offset of the next block and XOR this one in */
			pool->randPoolAddPos = pos + HASH_OUTWORDS;
			if (pool->randPoolAddPos == RANDPOOLWORDS)
				pool->randPoolAddPos = 0;
			for (i = 0; i < HASH_OUTWORDS; i++)
				pool->randPool[pool->randPoolAddPos + i] ^=
													pool->randPool[pos+i];
		}
		/* Accumulate entropy from key into pool */
		sRandAddKeyEntropy( pool );
	}
}

/*
 * Make a deposit of information (entropy) into the pool.
 */
	static void
sRandPoolAddBytes(
	void *			priv,
	PGPByte const *	p,
	unsigned		len)
{
	RandomPool *pool	= GetPool();
	
	pgpAssert( pool );

	(void)priv;

	while (len--) {
		sRandPoolAddByte( pool, *p++ );
	}

	/* Pool has changed, randOut is out of date */
	pool->randOutGetPos = sizeof(pool->randOut);
}

static void
sRandGetOutput( RandomPool *	pool )
{
	int i;

	/*
	 * Hash the pending input and the entire pool, with the old
	 * randOut as IV.
	 */
	HashTransform( pool->randOut, pool->randKey );
	for (i = 0; i < RANDPOOLWORDS; i += HASH_INWORDS)
		HashTransform( pool->randOut, pool->randPool + i );

	/* Okay, we can now fetch starting at the beginning of randOut */
	pool->randOutGetPos = 0;
}

/*
 * Withdraw some bits from the pool.  Regardless of the distribution of the
 * input bits, the bits returned are uniformly distributed, although they
 * cannot, of course, contain more Shannon entropy than the input bits.
 */
	static void
sRandPoolGetBytesEntropy(
	void *			priv,
	PGPByte *		buf,
	unsigned		len,
	unsigned		bits)
{
	RandomPool *pool	= GetPool();
	unsigned t;
	
	pgpAssert( pool );

	(void)priv;

	if (pool->randBits >= bits)
		pool->randBits -= bits;
	else
		pool->randFrac = pool->randBits = 0;

	while (len > (t = sizeof(pool->randOut) - pool->randOutGetPos)) {
		pgpCopyMemory( (PGPByte *)pool->randOut + pool->randOutGetPos, buf, t);
		buf += t;
		len -= t;
		sRandGetOutput( pool );
	}

	if (len) {
		pgpCopyMemory( (PGPByte *)pool->randOut+pool->randOutGetPos, buf, len);
		pool->randOutGetPos += len;
	}
}


/*
 * Destroys already-used random numbers.  Ensures no sensitive data
 * remains in memory that can be recovered later.  This repeatedly
 * mixes the output of the generator back into itself until all internal
 * state has been overwritten.
 */
	static void
sRandPoolStir(void *	priv )
{
	RandomPool *pool	= GetPool();
	unsigned	i;
	
	pgpAssert( pool );

	(void)priv;

	for (i = 0; i < 5*sizeof(pool->randKey)/sizeof(pool->randOut); i++) {
		sRandPoolAddBytes(priv, (PGPByte *)pool->randOut,
						  sizeof(pool->randOut));
		sRandGetOutput( pool );
	}
}

	static void
sRandPoolAddBytesLock(
	void *			priv,
	PGPByte const *	p,
	unsigned		len)
{
	RandomPool *pool	= GetPool();
	
	PGPRMWOLockStartWriting( &pool->criticalLock );
		sRandPoolAddBytes( priv, p, len );
	PGPRMWOLockStopWriting( &pool->criticalLock );
}

	static void
sRandPoolGetBytesEntropyLock(
	void *			priv,
	PGPByte *		buf,
	unsigned		len,
	unsigned		bits)
{
	RandomPool *pool	= GetPool();
	
	PGPRMWOLockStartWriting( &pool->criticalLock );
		sRandPoolGetBytesEntropy( priv, buf, len, bits );
	PGPRMWOLockStopWriting( &pool->criticalLock );
}

	static void
sRandPoolStirLock(void *priv)
{
	RandomPool *pool	= GetPool();
	
	PGPRMWOLockStartWriting( &pool->criticalLock );
		sRandPoolStir( priv );
	PGPRMWOLockStopWriting( &pool->criticalLock );
}

static const PGPRandomVTBL sGlobalRandomPoolVTBL =
{
	"Global true random-number pool",
	sRandPoolAddBytesLock,
	sRandPoolGetBytesEntropyLock,
	sRandPoolStirLock
};


	PGPRandomVTBL const *
pgpGetGlobalRandomPoolVTBL( void )
{
	return( &sGlobalRandomPoolVTBL );
}


/*
 * Used for cases where we want to create a temporary PGPRandomContext
 * which wraps the random pool.
 */
	void
pgpInitGlobalRandomPoolContext( PGPRandomContext *rc )
{
	rc->context	= NULL;
	rc->vtbl	= pgpGetGlobalRandomPoolVTBL();
	rc->priv	= NULL;
	rc->destroy	= NULL;
}




/*
 * True random bit accumulation.  These are extra functions exported
 * for entropy estimation.
 *
 * Entropy is expressed in terms of a delta, essentially the difference
 * between the observed value and the expected value, whose logarithm
 * is a measure of entropy.  (The logarithm to the base 2 is entropy

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -