📄 random.c
字号:
/* 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 + -