📄 random.c
字号:
threat involved. Being able to cause a change in the data being
signed after the random DSA k value is generated would be a problem,
but k is only generated after the data has already been hashed and
the signature is about to be generated.
In general this type of attack would require cooperation between the
VM and a hostile external party to, for example, ignore the fact
that the VM has rolled back to an earlier point in the protocol so a
repeat of a previous handshake message will be seen. In other words
it more or less requires control over the VM by an external party, and
anyone faced with this level of attack has bigger things to worry
about than RNG state rollback */
restartPoint:
/* Prepare to get data from the randomness pool. Before we do this we
perform a final quick poll of the system to get any last bit of
entropy, and mix the entire pool. If the pool hasn't been sufficiently
mixed, we iterate until we've reached the minimum mix count */
for( iterationCount = 0; iterationCount < FAILSAFE_ITERATIONS_LARGE;
iterationCount++ )
{
DECLARE_ORIGINAL_INT( randomPoolMixes );
fastPoll();
/* Mix the pool after the fast poll. The poll itself can result in
multiple sets of mixing, this final mix ensures that there's no
unmixed data left */
STORE_ORIGINAL_INT( randomPoolMixes, randomInfo->randomPoolMixes );
status = mixRandomPool( randomInfo );
if( cryptStatusError( status ) )
{
krnlExitMutex( MUTEX_RANDOM );
return( status );
}
POST( randomInfo->randomPoolMixes == RANDOMPOOL_MIXES || \
randomInfo->randomPoolMixes == ORIGINAL_VALUE( randomPoolMixes ) + 1 );
/* If the pool is sufficiently mixed, we're done */
if( randomInfo->randomPoolMixes >= RANDOMPOOL_MIXES )
break;
}
ENSURES_MUTEX( iterationCount < FAILSAFE_ITERATIONS_LARGE );
/* Keep producing RANDOMPOOL_OUTPUTSIZE bytes of output until the request
is satisfied */
for( count = 0; count < length; count += RANDOM_OUTPUTSIZE )
{
const int outputBytes = min( length - count, RANDOM_OUTPUTSIZE );
ORIGINAL_PTR( bufPtr );
/* Precondition for output quantity: Either we're on the last output
block or we're producing the maximum-size output quantity, and
we're never trying to use more than half the pool contents */
REQUIRES_MUTEX( length - count < RANDOM_OUTPUTSIZE || \
outputBytes == RANDOM_OUTPUTSIZE );
REQUIRES_MUTEX( outputBytes <= RANDOMPOOL_SIZE / 2 );
status = getRandomOutput( randomInfo, bufPtr, outputBytes );
if( cryptStatusError( status ) )
{
krnlExitMutex( MUTEX_RANDOM );
return( status );
}
bufPtr += outputBytes;
/* Postcondition: We're filling the output buffer and we wrote the
output to the correct portion of the output buffer */
POST( ( bufPtr > ( BYTE * ) buffer ) && \
( bufPtr <= ( BYTE * ) buffer + length ) );
POST( bufPtr == ORIGINAL_VALUE( bufPtr ) + outputBytes );
}
/* Postcondition: We filled the output buffer with the required amount
of output */
ENSURES_MUTEX( bufPtr == ( BYTE * ) buffer + length );
/* Check whether the process forked while we were generating output. If
it did, force a complete remix of the pool and restart the output
generation process (the fast poll will ensure that the pools in the
parent and child differ) */
if( checkForked() )
{
randomInfo->randomPoolMixes = 0;
bufPtr = buffer;
goto restartPoint;
}
krnlExitMutex( MUTEX_RANDOM );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Init/Shutdown Routines *
* *
****************************************************************************/
/* Initialise the randomness subsystem */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int initRandomInfo( OUT_PTR TYPECAST( RANDOM_INFO ** ) void **randomInfoPtrPtr )
{
RANDOM_INFO testRandomInfo, *randomInfoPtr;
BYTE buffer[ 16 + 8 ];
int status;
assert( isWritePtr( randomInfoPtrPtr, sizeof( void * ) ) );
/* Clear return value */
*randomInfoPtrPtr = NULL;
/* Make sure that the crypto that we need is functioning as required */
status = randomAlgorithmSelfTest();
ENSURES( cryptStatusOK( status ) );
/* The underlying crypto is OK, check that the cryptlib PRNG is working
correctly */
initRandomPool( &testRandomInfo );
status = mixRandomPool( &testRandomInfo );
if( cryptStatusOK( status ) && \
memcmp( testRandomInfo.randomPool,
"\xF6\x8F\x30\xEE\x52\x13\x3E\x40\x06\x06\xA6\xBE\x91\xD2\xD9\x82", 16 ) )
status = CRYPT_ERROR_FAILED;
if( cryptStatusOK( status ) )
status = mixRandomPool( &testRandomInfo );
if( cryptStatusOK( status ) && \
memcmp( testRandomInfo.randomPool,
"\xAE\x94\x3B\xF2\x86\x5F\xCF\x76\x36\x2B\x80\xD5\x73\x86\x9B\x69", 16 ) )
status = CRYPT_ERROR_FAILED;
if( cryptStatusOK( status ) )
status = mixRandomPool( &testRandomInfo );
if( cryptStatusOK( status ) && \
memcmp( testRandomInfo.randomPool,
"\xBC\x2D\xC1\x03\x8C\x78\x6D\x04\xA8\xBD\xD5\x51\x80\xCA\x42\xF4", 16 ) )
status = CRYPT_ERROR_FAILED;
if( cryptStatusError( status ) )
{
endRandomPool( &testRandomInfo );
retIntError();
}
/* Check that the ANSI X9.17 PRNG is working correctly */
status = selfTestX917( &testRandomInfo, testRandomInfo.randomPool );
if( cryptStatusError( status ) )
{
endRandomPool( &testRandomInfo );
retIntError();
}
/* The underlying PRNGs are OK, check the overall random number
generation system. Since we started with an all-zero seed we have
to fake the entropy-quality values for the artificial test pool */
testRandomInfo.randomQuality = 100;
testRandomInfo.randomPoolMixes = RANDOMPOOL_MIXES;
status = getRandomOutput( &testRandomInfo, buffer, 16 );
if( cryptStatusOK( status ) && \
memcmp( buffer, "\x6B\x59\x1D\xCD\xE1\xB3\xA8\x50\x32\x84\x8C\x8D\x93\xB0\x74\xD7", 16 ) )
status = CRYPT_ERROR_FAILED;
if( cryptStatusError( status ) )
{
endRandomPool( &testRandomInfo );
retIntError();
}
endRandomPool( &testRandomInfo );
/* Check the ANSI X9.17 PRNG again, this time using FIPS test vectors */
status = fipsTestX917( &testRandomInfo );
if( cryptStatusError( status ) )
{
endRandomPool( &testRandomInfo );
retIntError();
}
/* Allocate and initialise the random pool */
if( ( status = krnlMemalloc( ( void ** ) &randomInfoPtr, \
sizeof( RANDOM_INFO ) ) ) != CRYPT_OK )
return( status );
initRandomPool( randomInfoPtr );
/* Initialise any helper routines that may be needed */
initRandomPolling();
*randomInfoPtrPtr = randomInfoPtr;
return( CRYPT_OK );
}
/* Shut down the randomness subsystem. Exactly what to do if we can't
exit the polling thread or acquire the mutex is a bit complicated, this
is a shouldn't-occur exception condition condition so it's not even
possible to plan for this since it's uncertain under which conditions (if
ever) this situation would occur. We can't even perform a failsafe
zeroise of the pool data because it could lead to the other thread using
an all-zero key from the unexpectedly-cleared pool. For now we play it
by the book and don't do anything if we can't exit the thread or acquire
the mutex, which avoids a segfault from pulling the random data out from
underneath the other thread */
STDC_NONNULL_ARG( ( 1 ) ) \
void endRandomInfo( INOUT TYPECAST( RANDOM_INFO ** ) void **randomInfoPtrPtr )
{
RANDOM_INFO *randomInfoPtr = *randomInfoPtrPtr;
int status;
assert( isWritePtr( randomInfoPtrPtr, sizeof( void * ) ) );
assert( isWritePtr( *randomInfoPtrPtr, sizeof( RANDOM_INFO ) ) );
/* Make sure that there are no background threads/processes still trying
to send us data */
status = waitforRandomCompletion( TRUE );
ENSURES_V( cryptStatusOK( status ) ); /* See comment above */
/* Call any special-case shutdown functions */
endRandomPolling();
/* Shut down the random data pool. We acquire the randomness mutex
while we're doing this to ensure that any threads still using the
randomness info have exited before we destroy it */
status = krnlEnterMutex( MUTEX_RANDOM );
ENSURES_V( cryptStatusOK( status ) ); /* See comment above */
endRandomPool( randomInfoPtr );
krnlExitMutex( MUTEX_RANDOM );
krnlMemfree( randomInfoPtrPtr );
}
/****************************************************************************
* *
* Add Random (Entropy) Data *
* *
****************************************************************************/
/* Add new entropy data and an entropy quality estimate to the random pool */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int addEntropyData( INOUT TYPECAST( RANDOM_INFO * ) void *randomInfoPtr,
IN_BUFFER( length ) const void *buffer,
IN_LENGTH const int length )
{
RANDOM_INFO *randomInfo = ( RANDOM_INFO * ) randomInfoPtr;
const BYTE *bufPtr = ( BYTE * ) buffer;
int count, status;
#if 0 /* See comment in addEntropyQuality */
DECLARE_ORIGINAL_INT( entropyByteCount );
#endif /* 0 */
assert( isWritePtr( randomInfo, sizeof( RANDOM_INFO ) ) );
assert( isReadPtr( buffer, length ) );
REQUIRES( length > 0 && length < MAX_INTLENGTH );
status = krnlEnterMutex( MUTEX_RANDOM );
if( cryptStatusError( status ) )
return( status );
REQUIRES_MUTEX( sanityCheck( randomInfo ) );
#if 0 /* See comment in addEntropyQuality */
STORE_ORIGINAL_INT( entropyByteCount, randomInfo->entropyByteCount );
#endif /* 0 */
/* Mix the incoming data into the pool. This operation is resistant to
chosen- and known-input attacks because the pool contents are unknown
to an attacker so XORing in known data won't help them. If an
attacker could determine pool contents by observing the generator
output (which is defeated by the postprocessing) we'd have to perform
an extra input mixing operation to defeat these attacks */
for( count = 0; count < length; count++ )
{
ORIGINAL_INT_VAR( bufVal, bufPtr[ count ] );
DECLARE_ORIGINAL_INT( poolVal );
DECLARE_ORIGINAL_INT( newPoolVal );
DECLARE_ORIGINAL_INT( poolPos );
/* If the pool write position has reached the end of the pool, mix
the pool */
if( randomInfo->randomPoolPos >= RANDOMPOOL_SIZE )
{
status = mixRandomPool( randomInfo );
if( cryptStatusError( status ) )
{
krnlExitMutex( MUTEX_RANDOM );
return( status );
}
ENSURES_MUTEX( randomInfo->randomPoolPos == 0 );
}
STORE_ORIGINAL_INT( poolVal,
randomInfo->randomPool[ randomInfo->randomPoolPos ] );
STORE_ORIGINAL_INT( poolPos, randomInfo->randomPoolPos );
/* Precondition: We're adding data inside the pool */
REQUIRES_MUTEX( randomInfo->randomPoolPos >= 0 && \
randomInfo->randomPoolPos < RANDOMPOOL_SIZE );
randomInfo->randomPool[ randomInfo->randomPoolPos++ ] ^= bufPtr[ count ];
STORE_ORIGINAL_INT( newPoolVal,
randomInfo->randomPool[ randomInfo->randomPoolPos - 1 ] );
/* Postcondition: We've updated the byte at the current pool
position, and the value really was XORed into the pool rather
than (for example) overwriting it as with PGP/xorbytes or
GPG/add_randomness. Note that in this case we can use a non-XOR
operation to check that the XOR succeeded, unlike the pool mixing
code which requires an XOR to check the original XOR */
POST( randomInfo->randomPoolPos == \
ORIGINAL_VALUE( poolPos ) + 1 );
POST( ( ( ORIGINAL_VALUE( newPoolVal ) == \
ORIGINAL_VALUE( bufVal ) ) && \
( ORIGINAL_VALUE( poolVal ) == 0 ) ) || \
( ORIGINAL_VALUE( newPoolVal ) != \
ORIGINAL_VALUE( bufVal ) ) );
}
#if 0 /* See comment in addEntropyQuality */
/* Remember how many bytes of entropy we added on this update */
randomInfo->entropyByteCount += length;
#endif /* 0 */
/* Postcondition: We processed all of the data */
POST( count == length );
#if 0 /* See comment in addEntropyQuality */
POST( randomInfo->entropyByteCount == \
ORIGINAL_VALUE( entropyByteCount ) + length );
#endif /* 0 */
ENSURES_MUTEX( sanityCheck( randomInfo ) );
krnlExitMutex( MUTEX_RANDOM );
return( CRYPT_OK );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -