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

📄 random.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 4 页
字号:
	/* Postcondition for the bit-flipping: The two pools differ, and the
	   difference is in the flipped bits */
	ENSURES( memcmp( randomInfo->randomPool, exportedRandomInfo->randomPool,
					 RANDOMPOOL_ALLOCSIZE ) );
	FORALL( i, 0, RANDOMPOOL_ALLOCSIZE, \
			randomInfo->randomPool[ i ] == \
							( exportedRandomInfo->randomPool[ i ] ^ 0xFF ) );

	/* Mix the original and export pools so that neither can be recovered
	   from the other */
	status = mixRandomPool( randomInfo );
	if( cryptStatusOK( status ) )
		status = mixRandomPool( exportedRandomInfo );
	if( cryptStatusError( status ) )
		return( status );

	/* Postcondition for the mixing: The two pools differ, and the difference
	   is more than just the bit flipping (this has a ~1e-14 chance of a false
	   positive, which should be safe) */
	ENSURES( memcmp( randomInfo->randomPool, exportedRandomInfo->randomPool,
					 RANDOMPOOL_ALLOCSIZE ) );
	ENSURES( randomInfo->randomPool[ 0 ] != \
					( exportedRandomInfo->randomPool[ 0 ] ^ 0xFF ) ||
			 randomInfo->randomPool[ 8 ] != \
					( exportedRandomInfo->randomPool[ 8 ] ^ 0xFF ) ||
			 randomInfo->randomPool[ 16 ] != \
					( exportedRandomInfo->randomPool[ 16 ] ^ 0xFF ) ||
			 randomInfo->randomPool[ 24 ] != \
					( exportedRandomInfo->randomPool[ 24 ] ^ 0xFF ) ||
			 randomInfo->randomPool[ 32 ] != \
					( exportedRandomInfo->randomPool[ 32 ] ^ 0xFF ) ||
			 randomInfo->randomPool[ 40 ] != \
					( exportedRandomInfo->randomPool[ 40 ] ^ 0xFF ) );

	/* Precondition for sampling the output: It's a sample from the start of
	   the pool */
	ENSURES( samplePtr == randomInfo->randomPool && \
			 x917SamplePtr == exportedRandomInfo->randomPool );

	/* Check for stuck-at faults by comparing a short sample from the current
	   output with samples from the previous RANDOMPOOL_SAMPLES outputs */
	sample = mgetLong( samplePtr );
	for( i = 0; i < RANDOMPOOL_SAMPLES; i++ )
		{
		if( randomInfo->prevOutput[ i ] == sample )
			{
			/* We're repeating previous output, tell the caller to try
			   again */
			return( OK_SPECIAL );
			}
		}

	/* Postcondition: There are no values seen during a previous run present
	   in the output */
	FORALL( i, 0, RANDOMPOOL_SAMPLES, \
			randomInfo->prevOutput[ i ] != sample );

	/* Process the exported pool with the X9.17 generator */
	status = generateX917( randomInfo, exportedRandomInfo->randomPool,
						   RANDOMPOOL_ALLOCSIZE );
	if( cryptStatusError( status ) )
		return( status );

	/* Check for stuck-at faults in the X9.17 generator by comparing a short
	   sample from the current output with samples from the previous
	   RANDOMPOOL_SAMPLES outputs.  If it's the most recent sample then FIPS
	   140 requires an absolute failure if there's a duplicate rather than
	   simply signalling a problem and letting the higher layer handle it.
	   Because this will lead to false positives even for a perfect 
	   generator we provide a custom check in which if we get a match in the 
	   first 32 bits then we perform a backup check on the full 
	   RANDOMPOOL_SAMPLE_SIZE bytes and return a hard failure if all of the 
	   bits match.

	   There's an implied additional requirement in the sampling process in 
	   which the zero'th iteration of the X9.17 generator doesn't have a 
	   previous sample to compare to and therefore can't meet the 
	   requirements for previous-sample checking, however this is handled by
	   having the generator cranked twice on init/reinit in 
	   getRandomOutput(), which provides the necessary zero'th sample */
	sample = mgetLong( x917SamplePtr );
	for( i = 0; i < RANDOMPOOL_SAMPLES; i++ )
		{
		if( randomInfo->x917PrevOutput[ i ] == sample )
			{
			/* If we've failed on the first sample and the full match also
			   fails, return a hard error */
			if( i == 0 && \
				!memcmp( randomInfo->x917OuputSample,
						 exportedRandomInfo->randomPool,
						 RANDOMPOOL_SAMPLE_SIZE ) )
				{
				retIntError();
				}

			/* We're repeating previous output, tell the caller to try
			   again */
			return( OK_SPECIAL );
			}
		}

	/* Postcondition: There are no values seen during a previous run present
	   in the output */
	FORALL( i, 0, RANDOMPOOL_SAMPLES, \
			randomInfo->x917PrevOutput[ i ] != sample );

	ENSURES( sanityCheck( randomInfo ) );

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int getRandomOutput( INOUT RANDOM_INFO *randomInfo, 
							OUT_BUFFER_FIXED( length ) BYTE *buffer, 
							IN_RANGE( 1, RANDOM_OUTPUTSIZE ) const int length )
	{
	RANDOM_INFO exportedRandomInfo;
	BYTE *samplePtr;
	int noRandomRetries, i, status;
	ORIGINAL_INT_VAR( prevOutputIndex, randomInfo->prevOutputIndex );

	assert( isWritePtr( randomInfo, sizeof( RANDOM_INFO ) ) );
	assert( isWritePtr( buffer, length ) );

	/* Precondition for output quantity: We're being asked for a valid output
	   length and we're not trying to use more than half the pool contents */
	PRE( RANDOM_OUTPUTSIZE == RANDOMPOOL_SIZE / 2 );
	REQUIRES( sanityCheck( randomInfo ) );
	REQUIRES( length > 0 && length <= RANDOM_OUTPUTSIZE && \
			  length <= RANDOMPOOL_SIZE / 2 );

	/* If the X9.17 generator cryptovariables haven't been initialised yet
	   or have reached their use-by date, set the generator key and seed from
	   the pool contents, then mix the pool and crank the generator twice to
	   obscure the data that was used.  This also provides the zero'th sample
	   of output required by the FIPS 140 tests */
	if( !randomInfo->x917Inited || \
		randomInfo->x917Count >= X917_MAX_CYCLES )
		{
		status = mixRandomPool( randomInfo );
		if( cryptStatusOK( status ) )
			status = setKeyX917( randomInfo, randomInfo->randomPool,
								 randomInfo->randomPool + X917_KEYSIZE, 
								 NULL );
		if( cryptStatusOK( status ) )
			status = mixRandomPool( randomInfo );
		if( cryptStatusOK( status ) )
			status = generateX917( randomInfo, randomInfo->randomPool,
								   RANDOMPOOL_ALLOCSIZE );
		if( cryptStatusOK( status ) )
			status = mixRandomPool( randomInfo );
		if( cryptStatusOK( status ) )
			status = generateX917( randomInfo, randomInfo->randomPool,
								   RANDOMPOOL_ALLOCSIZE );
		if( cryptStatusError( status ) )
			return( status );
		memcpy( randomInfo->x917OuputSample, randomInfo->randomPool,
				RANDOMPOOL_SAMPLE_SIZE );	/* Save zero'th output sample */
		}

	/* Precondition for drawing output from the generator: The pool is
	   sufficiently mixed, there's enough entropy present, and the X9.17
	   post-processor is ready for use */
	REQUIRES( randomInfo->randomPoolMixes == RANDOMPOOL_MIXES && \
			  randomInfo->randomQuality >= 100 && randomInfo->x917Inited );

	/* Initialise the pool to contain the exported random data */
	initRandomPool( &exportedRandomInfo );

	/* Try to obtain random data from the pool.  If the initial attempt to 
	   get entropy fails, retry a fixed number of times */
	status = tryGetRandomOutput( randomInfo, &exportedRandomInfo );
	for( noRandomRetries = 1; 
	     status == OK_SPECIAL && noRandomRetries < RANDOMPOOL_RETRIES; 
		 noRandomRetries++ )
		{
		status = tryGetRandomOutput( randomInfo, &exportedRandomInfo );
		}
	if( cryptStatusError( status ) )
		{
		/* We ran out of retries so that we're repeating the same output
		   data or there was some other type of error, fail */
		endRandomPool( &exportedRandomInfo );

		/* Postcondition: Nulla vestigia retrorsum */
		FORALL( i, 0, RANDOMPOOL_ALLOCSIZE, \
				exportedRandomInfo.randomPool[ i ] == 0 );

		/* We can't trust the pool data any more so we set its content
		   value to zero.  Ideally we should flash lights and sound
		   klaxons as well, this is a catastrophic failure */
		randomInfo->randomQuality = randomInfo->randomPoolMixes = 0;
		randomInfo->x917Inited = FALSE;
		retIntError();
		}

	/* Save a short sample from the current output for future checks */
	REQUIRES( randomInfo->prevOutputIndex >= 0 && \
			  randomInfo->prevOutputIndex < RANDOMPOOL_SAMPLES );
	samplePtr = randomInfo->randomPool;
	randomInfo->prevOutput[ randomInfo->prevOutputIndex ] = mgetLong( samplePtr );
	samplePtr = exportedRandomInfo.randomPool;
	randomInfo->x917PrevOutput[ randomInfo->prevOutputIndex ] = mgetLong( samplePtr );
	randomInfo->prevOutputIndex = ( randomInfo->prevOutputIndex + 1 ) % \
								  RANDOMPOOL_SAMPLES;
	memcpy( randomInfo->x917OuputSample, exportedRandomInfo.randomPool,
			RANDOMPOOL_SAMPLE_SIZE );
	POST( randomInfo->prevOutputIndex != ORIGINAL_VALUE( prevOutputIndex ) );
	POST( randomInfo->prevOutputIndex == 0 || \
		  randomInfo->prevOutputIndex == ORIGINAL_VALUE( prevOutputIndex ) + 1 );
	ENSURES( randomInfo->prevOutputIndex >= 0 && \
			 randomInfo->prevOutputIndex < RANDOMPOOL_SAMPLES );

	/* Copy the transformed data to the output buffer, folding it in half as
	   we go to mask the original content */
	for( i = 0; i < length; i++ )
		{
		buffer[ i ] = exportedRandomInfo.randomPool[ i ] ^ \
					  exportedRandomInfo.randomPool[ RANDOM_OUTPUTSIZE + i ];
		}

	/* Postcondition: We drew at most half of the transformed output from the
	   export pool, and the output came from the export pool and not the main
	   pool */
	ENSURES( i <= RANDOMPOOL_SIZE / 2 );
	EXISTS( i, 0, RANDOMPOOL_SIZE / 2, \
			buffer[ i ] != ( randomInfo->randomPool[ i ] ^ \
							 randomInfo->randomPool[ RANDOM_OUTPUTSIZE + i ] ) );

	/* Clean up */
	endRandomPool( &exportedRandomInfo );

	/* Postcondition: Nulla vestigia retrorsum */
	FORALL( i, 0, RANDOMPOOL_ALLOCSIZE, \
			exportedRandomInfo.randomPool[ i ] == 0 );

	ENSURES( sanityCheck( randomInfo ) );

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int getRandomData( INOUT TYPECAST( RANDOM_INFO * ) void *randomInfoPtr, 
				   OUT_BUFFER_FIXED( length ) void *buffer, 
				   IN_RANGE( 1, MAX_RANDOM_BYTES ) const int length )
	{
	RANDOM_INFO *randomInfo = ( RANDOM_INFO * ) randomInfoPtr;
	BYTE *bufPtr = buffer;
	int randomQuality, count, iterationCount, status;

	assert( isWritePtr( randomInfo, sizeof( RANDOM_INFO ) ) );
	assert( isWritePtr( buffer, length ) );

	/* Precondition: We're not asking for more data than the maximum that
	   should be needed */
	REQUIRES( length > 0 && length <= MAX_RANDOM_BYTES );

	/* Clear the return value and by extension make sure that we fail the
	   FIPS 140-like entropy tests on the output if there's a problem */
	zeroise( buffer, length );

	status = krnlEnterMutex( MUTEX_RANDOM );
	if( cryptStatusError( status ) )
		return( status );
	REQUIRES_MUTEX( sanityCheck( randomInfo ) );

	/* If we're using a stored random seed, add it to the entropy pool if
	   necessary.  Note that we do this here rather than when we initialise
	   the randomness subsystem both because at that point the stream
	   subsystem may not be ready for use yet and because there may be a
	   requirement to periodically re-read the seed data if it's changed
	   by another process/task */
#ifdef CONFIG_RANDSEED
	if( !randomInfo->seedProcessed )
		addStoredSeedData( randomInfo );
#endif /* CONFIG_RANDSEED */

	/* Get the randomness quality before we release the randomness info
	   again */
	randomQuality = randomInfo->randomQuality;

	krnlExitMutex( MUTEX_RANDOM );

	/* Perform a failsafe check to make sure that there's data available.
	   This should only ever be called once per application because after 
	   the first blocking poll that occurs because the user has tried to
	   generate keying material without having first seeded the generator
	   the programmer of the calling application will make sure that 
	   there's a slow poll done earlier on */
	if( randomQuality < 100 )
		slowPoll();

	/* Make sure that any background randomness-gathering process has
	   finished */
	status = waitforRandomCompletion( FALSE );
	if( cryptStatusError( status ) )
		return( status );

	status = krnlEnterMutex( MUTEX_RANDOM );
	if( cryptStatusError( status ) )
		return( status );
	REQUIRES_MUTEX( sanityCheck( randomInfo ) );

	/* If we still can't get any random information, let the user know */
	if( randomInfo->randomQuality < 100 )
		{
		krnlExitMutex( MUTEX_RANDOM );
		assert( DEBUG_WARN );
		return( CRYPT_ERROR_RANDOM );
		}

	/* If the process has forked we need to restart the generator output
	   process but we can't determine this until after we've already
	   produced the output.  If we do need to restart, we do it from this
	   point.

	   There is one variant of this problem that we can't work around and 
	   that's where we're running inside a VM with rollback support.  Some 
	   VMs can take periodic snapshots of the system state to allow rollback 
	   to a known-good state if an error occurs.  Since the VM's rollback is 
	   transparent to the OS there's no way to detect that this has 
	   occcurred (although getSysVars() can detect the presence of some 
	   common VMs it can't detect whether a rollback has occurred).  In this 
	   case we'd roll back to a previous state of the RNG and continue from 
	   there.  OTOH it's hard to identify a situation in which this would 
	   pose a serious threat.  Consider for example SSL or SSH session key 
	   setup/generation: If we haven't committed the data to the remote 
	   system yet it's no problem and if we have then we're now out of sync 
	   with the remote system and the handshake will fail.  Similarly, if 
	   we're generating a DSA signature then we'll end up generating the 
	   same signature again but since it's over the same data there's no 

⌨️ 快捷键说明

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