📄 dev_sys.c
字号:
/****************************************************************************
* *
* Randomness Routines *
* *
****************************************************************************/
/* Self-test code for the two crypto algorithms that are used for random
number generation. The self-test of these two algorithms is performed
every time the randomness subsystem is initialised. Note that the same
tests have already been performed as part of the startup self-test, but
we perform them again here for the benefit of the randomness subsystem,
which doesn't necessarily trust (or even know about) the startup self-
test */
#define DES_BLOCKSIZE X917_POOLSIZE
#if defined( INC_ALL )
#include "testdes.h"
#elif defined( INC_CHILD )
#include "../crypt/testdes.h"
#else
#include "crypt/testdes.h"
#endif /* Compiler-specific includes */
static int des3TestLoop( const DES_TEST *testData, int iterations )
{
BYTE temp[ DES_BLOCKSIZE ];
BYTE key1[ DES_KEYSIZE ], key2[ DES_KEYSIZE ], key3[ DES_KEYSIZE ];
int i;
for( i = 0; i < iterations; i++ )
{
memcpy( temp, testData[ i ].plaintext, DES_BLOCKSIZE );
/* Some of the old NBS test vectors have bad key parity values so we
explicitly call the key-schedule function that ignores parity
bits */
des_set_key_unchecked( ( C_Block * ) testData[ i ].key,
*( ( Key_schedule * ) key1 ) );
des_set_key_unchecked( ( C_Block * ) testData[ i ].key,
*( ( Key_schedule * ) key2 ) );
des_set_key_unchecked( ( C_Block * ) testData[ i ].key,
*( ( Key_schedule * ) key3 ) );
des_ecb3_encrypt( ( C_Block * ) temp, ( C_Block * ) temp,
*( ( Key_schedule * ) key1 ),
*( ( Key_schedule * ) key2 ),
*( ( Key_schedule * ) key3 ), DES_ENCRYPT );
if( memcmp( testData[ i ].ciphertext, temp, DES_BLOCKSIZE ) )
return( CRYPT_ERROR );
}
return( CRYPT_OK );
}
static int algorithmSelfTest( void )
{
static const FAR_BSS struct {
const char *data;
const int length;
const BYTE hashValue[ 20 ];
} hashData[] = { /* FIPS 180-1 SHA-1 test vectors */
{ "abc", 3,
{ 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A,
0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C,
0x9C, 0xD0, 0xD8, 0x9D } },
{ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56,
{ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E,
0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5,
0xE5, 0x46, 0x70, 0xF1 } },
{ NULL, 0, { 0 } }
};
HASHFUNCTION hashFunction;
BYTE hashValue[ CRYPT_MAX_HASHSIZE ];
int hashSize, i;
getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
/* Test the SHA-1 code against the values given in FIPS 180-1. We don't
perform the final test (using 10MB of data) because this takes too
long to run */
for( i = 0; hashData[ i ].data != NULL; i++ )
{
hashFunction( NULL, hashValue, ( BYTE * ) hashData[ i ].data,
hashData[ i ].length, HASH_ALL );
if( memcmp( hashValue, hashData[ i ].hashValue, hashSize ) )
return( CRYPT_ERROR_FAILED );
}
/* Test the 3DES code against the values given in NIST Special Pub.800-20,
1999, which are actually the same as 500-20, 1980, since they require
that K1 = K2 = K3 */
if( ( des3TestLoop( testIP, sizeof( testIP ) / sizeof( DES_TEST ) ) != CRYPT_OK ) || \
( des3TestLoop( testVP, sizeof( testVP ) / sizeof( DES_TEST ) ) != CRYPT_OK ) || \
( des3TestLoop( testKP, sizeof( testKP ) / sizeof( DES_TEST ) ) != CRYPT_OK ) || \
( des3TestLoop( testDP, sizeof( testDP ) / sizeof( DES_TEST ) ) != CRYPT_OK ) || \
( des3TestLoop( testSB, sizeof( testSB ) / sizeof( DES_TEST ) ) != CRYPT_OK ) )
return( CRYPT_ERROR_FAILED );
return( CRYPT_OK );
}
/* Initialise and shut down the randomness subsystem */
static int initRandomInfo( DEVICE_INFO *deviceInfo )
{
int status;
/* Make sure that the crypto we need is functioning as required */
status = algorithmSelfTest();
if( cryptStatusError( status ) )
{
assert( NOTREACHED );
return( status );
}
/* Allocate and initialise the random pool */
if( ( status = krnlMemalloc( ( void ** ) &deviceInfo->randomInfo,
sizeof( RANDOM_INFO ) ) ) != CRYPT_OK )
return( status );
initRandomPool( deviceInfo->randomInfo );
/* Initialise any helper routines that may be needed */
initRandomPolling();
return( CRYPT_OK );
}
static void endRandomInfo( DEVICE_INFO *deviceInfo )
{
/* Make sure that there are no background threads/processes still trying
to send us data */
waitforRandomCompletion( TRUE );
/* Call any special-case shutdown functions */
endRandomPolling();
/* Shut down the random data pool */
endRandomPool( deviceInfo->randomInfo );
krnlMemfree( ( void ** ) &deviceInfo->randomInfo );
}
/* Get a block of random data from the randomness pool in such a way that
compromise of the data doesn't compromise the pool, and vice versa. This
is done by performing the (one-way) pool mixing operation on the pool, and
on a transformed version of the pool that becomes the key. The
transformed version of the pool from which the key data will be drawn is
then further processed by running each 64-bit block through the X9.17
generator. As an additional precaution the key data is folded in half to
ensure that not even a hashed or encrypted form of the previous contents
is available. No pool data ever leaves the pool.
This function performs a more paranoid version of the FIPS 140 continuous
test on both the main pool contents and the X9.17 generator output that
will detect stuck-at faults and short cycles in the output. In addition
the higher-level message handler applies the FIPS 140 statistical tests
to the output and will retry the fetch if the output fails the tests (this
is performed at the higher level because it's then applied to all
randomness sources used by cryptlib, not just the built-in one).
Since the pool output is folded to mask the output, the output from each
round of mixing is only half the pool size as defined below */
#define RANDOM_OUTPUTSIZE ( RANDOMPOOL_SIZE / 2 )
static int tryGetRandomOutput( RANDOM_INFO *randomInfo,
RANDOM_INFO *exportedRandomInfo )
{
const BYTE *samplePtr = randomInfo->randomPool;
const BYTE *x917SamplePtr = exportedRandomInfo->randomPool;
unsigned long sample;
int i, status;
/* Precondition: The pool is ready to do. This check isn't so much to
confirm that this really is the case (it's already been checked
elsewhere) but to ensure that the two pool parameters haven't been
reversed. The use of generic pools for all types of random output is
useful in terms of providing a nice abstraction, but less useful for
type safety */
PRE( randomInfo->randomQuality >= 100 && \
randomInfo->randomPoolMixes >= RANDOMPOOL_MIXES && \
randomInfo->x917Inited == TRUE );
PRE( exportedRandomInfo->randomQuality == 0 && \
exportedRandomInfo->randomPoolMixes == 0 && \
exportedRandomInfo->x917Inited == FALSE );
/* Copy the contents of the main pool across to the export pool,
transforming it as we go by flipping all of the bits */
for( i = 0; i < RANDOMPOOL_ALLOCSIZE; i++ )
exportedRandomInfo->randomPool[ i ] = randomInfo->randomPool[ i ] ^ 0xFF;
/* Postcondition for the bit-flipping: The two pools differ, and the
difference is in the flipped bits */
POST( 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 */
mixRandomPool( randomInfo );
mixRandomPool( exportedRandomInfo );
/* Postcondition for the mixing: The two pools differ, and the difference
is more than just the bit flipping (this has a 1e-12 chance of a false
positive and even that's only in the debug version) */
POST( memcmp( randomInfo->randomPool, exportedRandomInfo->randomPool,
RANDOMPOOL_ALLOCSIZE ) );
POST( 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 ) );
/* Precondition for sampling the output: It's a sample from the start of
the pool */
PRE( samplePtr == randomInfo->randomPool );
PRE( 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 */
sample = mgetLong( x917SamplePtr );
for( i = 0; i < RANDOMPOOL_SAMPLES; i++ )
if( randomInfo->x917PrevOutput[ 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->x917PrevOutput[ i ] != sample );
return( CRYPT_OK );
}
static int getRandomOutput( RANDOM_INFO *randomInfo, BYTE *buffer,
const int length )
{
RANDOM_INFO exportedRandomInfo;
BYTE *samplePtr;
int noRandomRetries, i, status;
/* 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( length > 0 && length <= RANDOM_OUTPUTSIZE );
PRE( length <= RANDOMPOOL_SIZE / 2 );
PRE( RANDOM_OUTPUTSIZE == 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 */
if( !randomInfo->x917Inited || \
randomInfo->x917Count >= X917_MAX_CYCLES )
{
mixRandomPool( randomInfo );
status = setKeyX917( randomInfo, randomInfo->randomPool,
randomInfo->randomPool + X917_KEYSIZE );
if( cryptStatusOK( status ) )
{
mixRandomPool( randomInfo );
status = generateX917( randomInfo, randomInfo->randomPool,
RANDOMPOOL_ALLOCSIZE );
}
if( cryptStatusOK( status ) )
{
mixRandomPool( randomInfo );
generateX917( randomInfo, randomInfo->randomPool,
RANDOMPOOL_ALLOCSIZE );
}
if( cryptStatusError( status ) )
return( status );
}
/* 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 */
PRE( randomInfo->randomPoolMixes == RANDOMPOOL_MIXES );
PRE( randomInfo->randomQuality >= 100 );
PRE( randomInfo->x917Inited );
/* Initialise the pool to contain the exported random data */
initRandomPool( &exportedRandomInfo );
/* Try to obtain random data from the pool */
for( noRandomRetries = 0; noRandomRetries < RANDOMPOOL_RETRIES;
noRandomRetries++ )
{
status = tryGetRandomOutput( randomInfo, &exportedRandomInfo );
if( status != OK_SPECIAL )
break;
}
/* If we ran out of retries so that we're repeating the same output
data or there was an error, fail */
if( cryptStatusError( status ) )
{
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 quality
estimate to zero. Ideally we should flash lights and sound
klaxons as well, this is a catastrophic failure */
randomInfo->randomQuality = 0;
assert( NOTREACHED );
return( CRYPT_ERROR_RANDOM );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -