📄 random.c
字号:
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int addEntropyQuality( INOUT TYPECAST( RANDOM_INFO * ) void *randomInfoPtr,
IN_RANGE( 1, 100 ) const int quality )
{
RANDOM_INFO *randomInfo = ( RANDOM_INFO * ) randomInfoPtr;
int status;
assert( isWritePtr( randomInfo, sizeof( RANDOM_INFO ) ) );
REQUIRES( quality > 0 && quality <= 100 );
status = krnlEnterMutex( MUTEX_RANDOM );
if( cryptStatusError( status ) )
return( status );
REQUIRES_MUTEX( sanityCheck( randomInfo ) );
/* In theory we could check to ensure that the claimed entropy quality
corresponds approximately to the amount of entropy data added,
however in a multithreaded environment this doesn't work because the
entropy addition is distinct from the entropy quality addition so
that (for example) with entropy being added by three threads we could
end up with the following:
entropy1, entropy1,
entropy2,
entropy1,
entropy3,
entropy1,
entropy3,
entropy2,
quality2, reset to 0
quality1, fail since reset to 0
quality3, fail since reset to 0
This means that the first entropy quality measure added is applied
to all of the previously added entropy after which the entropy byte
count is reset, causing subsequent attempts to add entropy quality to
fail. In addition the first quality value is applied to all of the
entropy added until that point rather than just the specific entropy
samples that it corresponds to. In theory this could be addressed by
requiring the entropy source to treat entropy addition as a
database-style BEGIN ... COMMIT transaction but this makes the
interface excessively complex for both source and sink, and more
prone to error than the small gain in entropy quality-checking is
worth */
#if 0
if( randomInfo->entropyByteCount <= 0 || \
quality / 2 > randomInfo->entropyByteCount )
{
/* If there's not enough entropy data present to justify the
claimed entropy quality level, signal an error. We do however
retain the existing entropy byte count for use the next time an
entropy quality estimate is added, since it's still contributing
to the total entropy quality */
krnlExitMutex( MUTEX_RANDOM );
retIntError();
}
randomInfo->entropyByteCount = 0;
#endif /* 0 */
/* If we haven't reached the minimum quality level for generating keys
yet, update the quality level */
if( randomInfo->randomQuality < 100 )
{
/* Update the quality count, making sure that it stays within
bounds */
if( randomInfo->randomQuality + quality > 100 )
randomInfo->randomQuality = 100;
else
randomInfo->randomQuality += quality;
}
ENSURES_MUTEX( sanityCheck( randomInfo ) );
krnlExitMutex( MUTEX_RANDOM );
return( CRYPT_OK );
}
#ifdef CONFIG_RANDSEED
/* Add entropy data from a stored seed value */
STDC_NONNULL_ARG( ( 1 ) ) \
static void addStoredSeedData( INOUT RANDOM_INFO *randomInfo )
{
STREAM stream;
BYTE streamBuffer[ STREAM_BUFSIZE + 8 ], seedBuffer[ 1024 + 8 ];
char seedFilePath[ MAX_PATH_LENGTH + 8 ];
int poolCount, length, status;
assert( isWritePtr( randomInfo, sizeof( RANDOM_INFO ) ) );
/* Try and access the stored seed data */
status = fileBuildCryptlibPath( seedFilePath, MAX_PATH_LENGTH, NULL,
BUILDPATH_RNDSEEDFILE );
if( cryptStatusOK( status ) )
status = sFileOpen( &stream, seedFilePath, FILE_READ );
if( cryptStatusError( status ) )
{
/* The seed data isn't present, don't try and access it again */
randomInfo->seedProcessed = TRUE;
assert( DEBUG_WARN );
return;
}
/* Read up to 1K of data from the stored seed */
sioctl( &stream, STREAM_IOCTL_IOBUFFER, streamBuffer, STREAM_BUFSIZE );
sioctl( &stream, STREAM_IOCTL_PARTIALREAD, NULL, 0 );
status = length = sread( &stream, seedBuffer, 1024 );
sFileClose( &stream );
zeroise( streamBuffer, STREAM_BUFSIZE );
if( cryptStatusError( status ) || length <= 0 )
{
/* The seed data is present but we can't read it, don't try and
access it again */
randomInfo->seedProcessed = TRUE;
assert( DEBUG_WARN );
return;
}
ENSURES_V( length > 0 && length <= 1024 );
randomInfo->seedSize = length;
/* Precondition: We got at least some non-zero data */
EXISTS( i, 0, length,
seedBuffer[ i ] != 0 );
/* Add the seed data to the entropy pool. Both because the entropy-
management code gets suspicious about very small amounts of data with
claimed high entropy and because it's a good idea to start with all
of the pool set to the seed data (rather than most of it set at zero
if the seed data is short), we add the seed data repeatedly until
we've filled the pool */
for( poolCount = RANDOMPOOL_SIZE; poolCount > 0; poolCount -= length )
{
status = addEntropyData( randomInfo, seedBuffer, length );
assert( cryptStatusOK( status ) );
}
/* If there were at least 128 bits of entropy present in the seed, set
the entropy quality to the user-provided value */
if( length >= 16 )
{
status = addEntropyQuality( randomInfo, CONFIG_RANDSEED_QUALITY );
assert( cryptStatusOK( status ) );
}
zeroise( seedBuffer, 1024 );
/* Postcondition: Nulla vestigia retrorsum */
FORALL( i, 0, 1024,
seedBuffer[ i ] == 0 );
}
#endif /* CONFIG_RANDSEED */
/****************************************************************************
* *
* Random Pool External Interface *
* *
****************************************************************************/
/* Convenience functions used by the system-specific randomness-polling
routines to send data to the system device. These just accumulate as
close to bufSize bytes of data as possible in a user-provided buffer and
then forward them to the device object. Note that addRandomData()
assumes that the quantity of data being added is small (a fixed-size
struct or something similar), it shouldn't be used to add large buffers
full of data since information at the end of the buffer will be lost */
typedef struct {
BUFFER( bufSize, bufPos ) \
void *buffer; /* Entropy buffer */
int bufPos, bufSize; /* Current buffer pos.and total size */
int updateStatus; /* Error status if update failed */
} RANDOM_STATE_INFO;
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int initRandomData( INOUT TYPECAST( RANDOM_STATE_INFO * ) void *statePtr,
IN_BUFFER( maxSize ) void *buffer,
IN_LENGTH_SHORT_MIN( 16 ) const int maxSize )
{
RANDOM_STATE_INFO *state = ( RANDOM_STATE_INFO * ) statePtr;
assert( isWritePtr( state, sizeof( RANDOM_STATE_INFO ) ) );
assert( sizeof( RANDOM_STATE_INFO ) <= sizeof( RANDOM_STATE ) );
assert( isWritePtr( buffer, maxSize ) );
REQUIRES( maxSize >= 16 && maxSize < MAX_INTLENGTH_SHORT );
memset( state, 0, sizeof( RANDOM_STATE_INFO ) );
state->buffer = buffer;
state->bufSize = maxSize;
return( CRYPT_OK );
}
RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int addRandomData( INOUT TYPECAST( RANDOM_STATE_INFO * ) void *statePtr,
IN_BUFFER( valueLength ) const void *value,
IN_LENGTH_SHORT const int valueLength )
{
RANDOM_STATE_INFO *state = ( RANDOM_STATE_INFO * ) statePtr;
MESSAGE_DATA msgData;
const BYTE *valuePtr = value;
int bytesToCopy = min( valueLength, state->bufSize - state->bufPos );
int totalLength = valueLength, status;
assert( isWritePtr( state, sizeof( RANDOM_STATE_INFO ) ) );
assert( isReadPtr( value, valueLength ) );
REQUIRES( state->bufSize >= 16 && state->bufSize < MAX_INTLENGTH_SHORT );
REQUIRES( state->bufPos >= 0 && state->bufPos <= state->bufSize );
REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );
/* If we're in an error state, don't try and do anything */
if( cryptStatusError( state->updateStatus ) )
return( state->updateStatus );
/* Copy as much of the input as we can into the accumulator */
if( bytesToCopy > 0 )
{
ENSURES( state->bufPos + bytesToCopy <= state->bufSize );
memcpy( ( BYTE * ) state->buffer + state->bufPos, valuePtr, bytesToCopy );
state->bufPos += bytesToCopy;
valuePtr += bytesToCopy;
totalLength -= bytesToCopy;
}
ENSURES( totalLength >= 0 && totalLength < MAX_INTLENGTH_SHORT );
/* If everything went into the accumulator, we're done */
if( state->bufPos < state->bufSize )
return( CRYPT_OK );
ENSURES( state->bufPos == state->bufSize );
/* The accumulator is full, send the data through to the system device */
setMessageData( &msgData, state->buffer, state->bufPos );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_ENTROPY );
if( cryptStatusError( status ) )
{
/* There was a problem moving the data through, make the error status
persistent. Normally this is a should-never-occur error
condition but if cryptlib has been shut down from another thread
then the kernel will fail all non shutdown-related calls with a
permission error. To avoid false alarms we mask out failures due
to permission errors */
state->updateStatus = status;
assert( ( status == CRYPT_ERROR_PERMISSION ) || DEBUG_WARN );
return( status );
}
state->bufPos = 0;
/* If we've consumed all of the data, we're done */
if( totalLength <= 0 )
return( CRYPT_OK );
/* There's uncopied data left, copy it in now. If there's more data
present than can fit in the accumulator's buffer we discard it
(although we warn in the debug build), the caller should be sending
quantities this large directly rather than using the addRandomData()
interface */
assert( totalLength < state->bufSize );
bytesToCopy = min( totalLength, state->bufSize );
memcpy( state->buffer, valuePtr, bytesToCopy );
state->bufPos += bytesToCopy;
return( CRYPT_OK );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int addRandomLong( INOUT void *statePtr, const long value )
{
return( addRandomData( statePtr, &value, sizeof( long ) ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int endRandomData( INOUT void *statePtr, \
IN_RANGE( 0, 100 ) const int quality )
{
RANDOM_STATE_INFO *state = ( RANDOM_STATE_INFO * ) statePtr;
int status = state->updateStatus;
assert( isWritePtr( state, sizeof( RANDOM_STATE_INFO ) ) );
REQUIRES( state->bufSize >= 16 && state->bufSize < MAX_INTLENGTH_SHORT );
REQUIRES( state->bufPos >= 0 && state->bufPos <= state->bufSize );
REQUIRES( quality >= 0 && quality <= 100 );
/* If we're in an error state, don't try and do anything */
if( cryptStatusError( state->updateStatus ) )
return( state->updateStatus );
/* If there's data still in the accumulator send it through to the
system device. A failure at this point is a should-never-occur
condition but if cryptlib has been shut down from another thread then
the kernel will fail all non shutdown-related calls with a permission
error. To avoid false alarms we mask out failures due to permission
errors */
if( state->bufPos > 0 )
{
MESSAGE_DATA msgData;
setMessageData( &msgData, state->buffer, state->bufPos );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_ENTROPY );
}
assert( cryptStatusOK( status ) || ( status == CRYPT_ERROR_PERMISSION ) );
/* If everything went OK, set the quality estimate for the data that
we've added */
if( cryptStatusOK( status ) && quality > 0 )
{
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_SETATTRIBUTE, ( void * ) &quality,
CRYPT_IATTRIBUTE_ENTROPY_QUALITY );
}
assert( cryptStatusOK( status ) || ( status == CRYPT_ERROR_PERMISSION ) );
/* Clear the accumulator and exit */
zeroise( state->buffer, state->bufSize );
zeroise( state, sizeof( RANDOM_STATE_INFO ) );
return( status );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -