📄 random.c
字号:
/****************************************************************************
* *
* cryptlib Randomness Management Routines *
* Copyright Peter Gutmann 1995-2005 *
* *
****************************************************************************/
/* The random pool handling code in this module and the other modules in the
/random subdirectory represent the cryptlib continuously seeded
pseudorandom number generator (CSPRNG) as described in my 1998 Usenix
Security Symposium paper "The generation of practically strong random
numbers".
The CSPRNG code is copyright Peter Gutmann (and various others) 1995-2004
all rights reserved. Redistribution of the CSPRNG modules and use in
source and binary forms, with or without modification, are permitted
provided that the following BSD-style license conditions are met:
1. Redistributions of source code must retain the above copyright notice
and this permission notice in its entirety.
2. Redistributions in binary form must reproduce the copyright notice in
the documentation and/or other materials provided with the distribution.
3. A copy of any bugfixes or enhancements made must be provided to the
author, <pgut001@cs.auckland.ac.nz> to allow them to be added to the
baseline version of the code.
ALTERNATIVELY, the code may be distributed under the terms of the GNU
General Public License, version 2 or any later version published by the
Free Software Foundation, in which case the provisions of the GNU GPL are
required INSTEAD OF the above restrictions.
Although not required under the terms of the GPL, it would still be nice
if you could make any changes available to the author to allow a
consistent code base to be maintained */
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "des.h"
#ifdef CONFIG_RANDSEED
#include "stream.h"
#endif /* CONFIG_RANDSEED */
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../crypt/des.h"
#ifdef CONFIG_RANDSEED
#include "../misc/stream.h"
#endif /* CONFIG_RANDSEED */
#else
#include "crypt.h"
#include "crypt/des.h"
#ifdef CONFIG_RANDSEED
#include "misc/stream.h"
#endif /* CONFIG_RANDSEED */
#endif /* Compiler-specific includes */
/* The maximum amount of random data needed by any cryptlib operation,
equivalent to the size of a maximum-length PKC key. However this isn't
the absolute length because when generating the k value for DLP
operations we get n + m bits and then reduce via one of the DLP
parameters to get the value within range. If we just got n bits, this
would introduce a bias into the top bit, see the DLP code for more
details. Because of this we allow a length slightly larger than the
maximum PKC key size */
#define MAX_RANDOM_BYTES ( CRYPT_MAX_PKCSIZE + 8 )
/* If we don't have a defined randomness interface, complain */
#if !( defined( __BEOS__ ) || defined( __IBM4758__ ) || \
defined( __MAC__ ) || defined( __MSDOS__ ) || defined( __MVS__ ) || \
defined( __OS2__ ) || defined( __PALMOS__ ) || \
defined( __TANDEM_NSK__ ) || defined( __TANDEM_OSS__ ) || \
defined( __UNIX__ ) || defined( __VMCMS__ ) || \
defined( __WIN16__ ) || defined( __WIN32__ ) || \
defined( __WINCE__ ) )
#error You need to create OS-specific randomness-gathering functions in random/<os-name>.c
#endif /* Various OS-specific defines */
/* If we're using stored seed data, make sure that the seed quality setting
is in order */
#ifdef CONFIG_RANDSEED
#ifndef CONFIG_RANDSEED_QUALITY
/* If the user hasn't provided a quality estimate, default to 80 */
#define CONFIG_RANDSEED_QUALITY 80
#endif /* !CONFIG_RANDSEED_QUALITY */
#if ( CONFIG_RANDSEED_QUALITY < 10 ) || ( CONFIG_RANDSEED_QUALITY > 100 )
#error CONFIG_RANDSEED_QUALITY must be between 10 and 100
#endif /* CONFIG_RANDSEED_QUALITY check */
#endif /* CONFIG_RANDSEED */
/* Some systems systems require special-case initialisation to allow
background randomness gathering, where this doesn't apply the routines to
do this are nop'd out */
#if defined( __WIN32__ ) || defined( __WINCE__ )
void initRandomPolling( void );
void endRandomPolling( void );
void waitforRandomCompletion( const BOOLEAN force );
#elif defined( __UNIX__ ) && \
!( defined( __MVS__ ) || defined( __TANDEM_NSK__ ) || \
defined( __TANDEM_OSS__ ) )
void initRandomPolling( void );
void endRandomPolling( void );
void waitforRandomCompletion( const BOOLEAN force );
#else
#define initRandomPolling()
#define endRandomPolling()
#define waitforRandomCompletion( dummy )
#endif /* !( __WIN32__ || __UNIX__ ) */
/* On Unix systems the randomness pool may be duplicated at any point if
the process forks (qualis pater, talis filius), so we need to perform a
complex check to make sure that we're running with a unique copy of the
pool contents rather than a clone of data held in another process. The
following function checks whether we've forked or not, which is used as a
signal to adjust the pool contents */
#if defined( __UNIX__ ) && \
!( defined( __MVS__ ) || defined( __TANDEM_NSK__ ) || \
defined( __TANDEM_OSS__ ) )
BOOLEAN checkForked( void );
#else
#define checkForked() FALSE
#endif /* __UNIX__ */
/* Prototypes for functions in the OS-specific randomness polling routines */
void slowPoll( void );
void fastPoll( void );
/****************************************************************************
* *
* Randomness Interface Definitions *
* *
****************************************************************************/
/* The size in bytes of the randomness pool and the size of the X9.17
post-processor generator pool */
#define RANDOMPOOL_SIZE 256
#define X917_POOLSIZE 8
/* The allocated size of the randomness pool, which allows for the overflow
created by the fact that the hash function blocksize isn't any useful
multiple of a power of 2 */
#define RANDOMPOOL_ALLOCSIZE ( ( ( RANDOMPOOL_SIZE + 20 - 1 ) / 20 ) * 20 )
/* In order to avoid the pool startup problem (where initial pool data may
consist of minimally-mixed entropy samples) we require that the pool be
mixed at least the following number of times before we can draw data from
it. This usually happens automatically because a slow poll adds enough
data to cause many mixing iterations, however if this doesn't happen we
manually mix it the appropriate number of times to get it up to the
correct level */
#define RANDOMPOOL_MIXES 10
/* The number of samples of previous output that we keep for the FIPS 140
continuous tests, and the number of retries that we perform if we detect
a repeat of a previous output */
#define RANDOMPOOL_SAMPLES 16
#define RANDOMPOOL_RETRIES 5
/* The number of times that we cycle the X9.17 generator before we load new
key and state variables. This means that we re-seed for every
X917_MAX_BYTES of output produced */
#define X917_MAX_BYTES 4096
#define X917_MAX_CYCLES ( X917_MAX_BYTES / X917_POOLSIZE )
/* The scheduled DES keys for the X9.17 generator */
typedef struct {
Key_schedule desKey1, desKey2, desKey3;
} X917_3DES_KEY;
#define DES_KEYSIZE sizeof( Key_schedule )
/* The size of the X9.17 generator key (112 bits for EDE 3DES) */
#define X917_KEYSIZE 16
/* Random pool information. We keep track of the write position in the
pool, which tracks where new data is added. Whenever we add new data the
write position is updated, once we reach the end of the pool we mix the
pool and start again at the beginning. We track the pool status by
recording the quality of the pool contents (1-100) and the number of
times the pool has been mixed, we can't draw data from the pool unless
both of these values have reached an acceptable level. In addition to
the pool state information we keep track of the previous
RANDOMPOOL_SAMPLES output samples to check for stuck-at faults or (short)
cyles */
typedef struct {
/* Pool state information */
BYTE randomPool[ RANDOMPOOL_ALLOCSIZE ]; /* Random byte pool */
int randomPoolPos; /* Current write position in the pool */
/* Pool status information */
int randomQuality; /* Level of randomness in the pool */
int randomPoolMixes; /* Number of times pool has been mixed */
/* X9.17 generator state information */
BYTE x917Pool[ X917_POOLSIZE ]; /* Generator state */
BYTE x917DT[ X917_POOLSIZE ]; /* Date/time vector */
X917_3DES_KEY x917Key; /* Scheduled 3DES key */
BOOLEAN x917Inited; /* Whether generator has been inited */
int x917Count; /* No.of times generator has been cycled */
BOOLEAN x917x931; /* X9.17 vs. X9.31 operation (see code comments */
/* Information for the FIPS 140 continuous tests */
unsigned long prevOutput[ RANDOMPOOL_SAMPLES ];
unsigned long x917PrevOutput[ RANDOMPOOL_SAMPLES ];
int prevOutputIndex;
/* Other status information used to check the pool's operation */
int entropyByteCount; /* Number of bytes entropy added */
/* Random seed data information if seeding is done from a stored seed */
#ifdef CONFIG_RANDSEED
BOOLEAN seedProcessed; /* Whether stored seed has been processed */
int seedSize; /* Size of stored seed data */
int seedUpdateCount; /* When to update stored seed data */
#endif /* CONFIG_RANDSEED */
} RANDOM_INFO;
/****************************************************************************
* *
* Randomness Utility Functions *
* *
****************************************************************************/
/* 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 (in
the debug build this will trigger an exception telling the caller to use
a direct krnlSendMessage() instead) */
typedef struct {
void *buffer; /* Entropy buffer */
int bufPos, bufSize; /* Current buffer pos.and total size */
int updateStatus; /* Error status if update failed */
} RANDOM_STATE_INFO;
void initRandomData( void *statePtr, void *buffer, 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 ) );
assert( maxSize >= 16 );
memset( state, 0, sizeof( RANDOM_STATE_INFO ) );
state->buffer = buffer;
state->bufSize = maxSize;
}
int addRandomData( void *statePtr, const void *value,
const int valueLength )
{
RANDOM_STATE_INFO *state = ( RANDOM_STATE_INFO * ) statePtr;
RESOURCE_DATA msgData;
const BYTE *valuePtr = value;
int length = min( valueLength, state->bufSize - state->bufPos );
int totalLength = valueLength, status;
assert( isWritePtr( state, sizeof( RANDOM_STATE_INFO ) ) );
assert( isReadPtr( value, valueLength ) );
assert( state->bufPos >= 0 && state->bufPos <= state->bufSize );
assert( valueLength > 0 && valueLength <= state->bufSize );
/* Sanity check on inputs (the length check checks both the input data
length and that bufSize > bufPos) */
if( state->bufPos < 0 || length < 0 || state->bufSize < 16 )
{
/* Some type of fatal data corruption has occurred */
state->updateStatus = CRYPT_ERROR_FAILED;
assert( NOTREACHED );
return( CRYPT_ERROR_FAILED );
}
/* Copy as much of the input as we can into the accumulator */
if( length > 0 )
{
memcpy( ( BYTE * ) state->buffer + state->bufPos, valuePtr, length );
state->bufPos += length;
valuePtr += length;
totalLength -= length;
}
assert( totalLength >= 0 );
/* If everything went into the accumulator, we're done */
if( state->bufPos < state->bufSize )
return( CRYPT_OK );
assert( 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, however
if cryptlib has been shut down from another thread 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 ) || NOTREACHED );
return( status );
}
state->bufPos = 0;
/* If there's uncopied data left, copy it in now */
if( totalLength > 0 )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -