📄 dev_sys.c
字号:
/* Postcondition: We produced output without running out of retries */
POST( noRandomRetries < RANDOMPOOL_RETRIES );
/* Save a short sample from the current output for future checks */
PRE( 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 %= RANDOMPOOL_SAMPLES;
POST( 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 */
POST( i <= RANDOMPOOL_SIZE / 2 );
/* Clean up */
endRandomPool( &exportedRandomInfo );
/* Postcondition: Nulla vestigia retrorsum */
FORALL( i, 0, RANDOMPOOL_ALLOCSIZE, \
exportedRandomInfo.randomPool[ i ] == 0 );
return( CRYPT_OK );
}
static int getRandomFunction( DEVICE_INFO *deviceInfo, void *buffer,
const int length )
{
RANDOM_INFO *randomInfo = deviceInfo->randomInfo;
BYTE *bufPtr = buffer;
int count;
/* Clear the return value and make sure that we fail the FIPS 140 tests
on the output if there's a problem */
zeroise( buffer, length );
/* Precondition: We're not asking for more data than the maximum needed
in any cryptlib operation, which in this case is the size of a
maximum-length PKC key */
PRE( length >= 1 && length <= CRYPT_MAX_PKCSIZE );
/* Perform a failsafe check to make sure that there's data available.
This should only ever be called once per app because after the first
blocking poll the programmer of the calling app will make sure that
there's a slow poll done earlier on */
if( randomInfo->randomQuality < 100 )
slowPoll();
/* Make sure that any background randomness-gathering process has
finished */
waitforRandomCompletion( FALSE );
/* If we still can't get any random information, let the user know */
if( randomInfo->randomQuality < 100 )
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 */
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 */
do
{
fastPoll();
mixRandomPool( randomInfo );
}
while( randomInfo->randomPoolMixes < RANDOMPOOL_MIXES );
/* 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 );
int status;
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 */
PRE( length - count < RANDOM_OUTPUTSIZE || \
outputBytes == RANDOM_OUTPUTSIZE );
PRE( outputBytes <= RANDOMPOOL_SIZE / 2 );
status = getRandomOutput( randomInfo, bufPtr, outputBytes );
if( cryptStatusError( status ) )
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 */
POST( 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;
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Device Init/Shutdown/Device Control Routines *
* *
****************************************************************************/
/* Initialise and shut down the system device */
static int initFunction( DEVICE_INFO *deviceInfo, const char *name,
const int nameLength )
{
STATIC_FN void initCapabilities( void );
int status;
UNUSED( name );
/* Set up the randomness info */
status = initRandomInfo( deviceInfo );
if( cryptStatusError( status ) )
return( status );
/* Set up the capability information for this device and mark it as
active */
initCapabilities();
deviceInfo->label = "cryptlib system device";
deviceInfo->flags = DEVICE_ACTIVE | DEVICE_LOGGEDIN | DEVICE_TIME;
return( CRYPT_OK );
}
static void shutdownFunction( DEVICE_INFO *deviceInfo )
{
endRandomInfo( deviceInfo );
}
/* Handle device control functions */
static int controlFunction( DEVICE_INFO *deviceInfo,
const CRYPT_ATTRIBUTE_TYPE type,
const void *data, const int dataLength )
{
assert( type == CRYPT_IATTRIBUTE_ENTROPY || \
type == CRYPT_IATTRIBUTE_ENTROPY_QUALITY || \
type == CRYPT_IATTRIBUTE_RANDOM_NONCE || \
type == CRYPT_IATTRIBUTE_SELFTEST || \
type == CRYPT_IATTRIBUTE_TIME );
/* Handle entropy addition */
if( type == CRYPT_IATTRIBUTE_ENTROPY )
{
RANDOM_INFO *randomInfo = deviceInfo->randomInfo;
BYTE *bufPtr = ( BYTE * ) data;
int count = dataLength;
ORIGINAL_INT_VAR( entropyByteCount, randomInfo->entropyByteCount );
ORIGINAL_PTR( data );
/* Precondition: The current entropy byte count has a sensible
value */
PRE( randomInfo->entropyByteCount >= 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 */
while( count-- )
{
ORIGINAL_INT_VAR( bufVal, *bufPtr );
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 )
mixRandomPool( randomInfo );
STORE_ORIGINAL_INT( poolVal,
randomInfo->randomPool[ randomInfo->randomPoolPos ] );
STORE_ORIGINAL_INT( poolPos, randomInfo->randomPoolPos );
/* Precondition: We're adding data inside the pool */
PRE( randomInfo->randomPoolPos >= 0 && \
randomInfo->randomPoolPos < RANDOMPOOL_SIZE );
randomInfo->randomPool[ randomInfo->randomPoolPos++ ] ^= *bufPtr++;
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 ) ) );
}
/* Remember how many bytes of entropy we added on this update */
randomInfo->entropyByteCount += dataLength;
/* Postcondition: We processed all of the data */
POST( bufPtr == ORIGINAL_VALUE( data ) + dataLength );
POST( randomInfo->entropyByteCount == \
ORIGINAL_VALUE( entropyByteCount ) + dataLength );
return( CRYPT_OK );
}
if( type == CRYPT_IATTRIBUTE_ENTROPY_QUALITY )
{
RANDOM_INFO *randomInfo = deviceInfo->randomInfo;
/* If there's not enough entropy data present to justify the claimed
entropy quality level, signal an error */
if( randomInfo->entropyByteCount <= 0 || \
dataLength / 2 > randomInfo->entropyByteCount )
{
assert( NOTREACHED );
return( CRYPT_ERROR_RANDOM );
}
randomInfo->entropyByteCount = 0;
/* If we haven't reached the minimum quality level for generating
keys yet, update the quality level */
if( randomInfo->randomQuality < 100 )
randomInfo->randomQuality += dataLength;
return( CRYPT_OK );
}
/* Handle nonces */
if( type == CRYPT_IATTRIBUTE_RANDOM_NONCE )
{
static BOOLEAN nonceDataInitialised = FALSE;
static BYTE nonceData[ CRYPT_MAX_HASHSIZE + 8 ];
static HASHFUNCTION hashFunction;
static int hashSize;
BYTE *noncePtr = ( BYTE * ) data;
int nonceLength = dataLength;
/* Get a random (but not necessarily cryptographically strong random)
nonce. Some nonces can simply be fresh (for which a monotonically
increasing sequence will do), some should be random (for which a
hash of the sequence is adequate), and some need to be
unpredictable. In order to avoid problems arising from the
inadvertent use of a nonce with the wrong properties, we use
unpredictable nonces in all cases, even where it isn't strictly
necessary.
This simple generator divides the nonce state up into a public
section of the same size as the hash output, and a private section
which contains 64 bits of data from the crypto RNG which
influences the public section. The public and private sections
are repeatedly hashed to produce the required amount of output.
Note that this leaks a small amount of information about the
crypto RNG output since an attacker knows that
public_state_n = hash( public_state_n - 1, private_state ), but
this isn't a major weakness.
If the nonce generator hasn't been initialised yet, we set up the
hashing and get 64 bits of private nonce state. What to do if
the attempt to initialise the state fails is somewhat debatable.
Since nonces are only ever used in protocols alongside crypto
keys, and an RNG failure will be detected when the key is
generated, we can generally ignore a failure at this point.
However, nonces are sometimes also used in non-crypto contexts
(for example to generate cert serial numbers) where this
detection in the RNG won't happen. On the other hand we
shouldn't really abort processing just because we can't get some
no-value nonce data, so what we do is retry the fetch of nonce
data (in case the system object was busy and the first attempt
timed out), and if that fails too fall back to the system time.
This is no longer unpredictable, but the only location where
unpredictability matters is when used in combination with crypto
operations, for which the absence of random data will be detected
during key generation */
if( !nonceDataInitialised )
{
RESOURCE_DATA msgData;
int status;
getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
setMessageData( &msgData, nonceData + hashSize, 8 );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_RANDOM );
if( cryptStatusError( status ) )
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_RANDOM );
if( cryptStatusError( status ) )
{
const time_t theTime = getTime();
memcpy( nonceData + hashSize, &theTime, sizeof( time_t ) );
}
nonceDataInitialised = TRUE;
}
/* Shuffle the public state and copy it to the output buffer until
it's full */
while( nonceLength > 0 )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -