📄 cryptmis.c
字号:
/****************************************************************************
* *
* cryptlib Misc Routines *
* Copyright Peter Gutmann 1992-2003 *
* *
****************************************************************************/
/* A generic module that implements a rug under which all problems not
solved elsewhere are swept */
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "crypt.h"
#ifdef INC_ALL
#include "md2.h"
#include "md5.h"
#include "ripemd.h"
#include "sha.h"
#ifdef USE_SHA2
#include "sha2.h"
#endif /* USE_SHA2 */
#include "stream.h"
#else
#include "crypt/md2.h"
#include "crypt/md5.h"
#include "crypt/ripemd.h"
#include "crypt/sha.h"
#ifdef USE_SHA2
#include "crypt/sha2.h"
#endif /* USE_SHA2 */
#include "misc/stream.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* Internal API Functions *
* *
****************************************************************************/
/* Get the system time safely. The first function implements hard failures,
converting invalid time values to zero, which yield a warning date of
1/1/1970 rather than an out-of-bounds value or garbage value. The second
function implements soft failures, returning an estimate of the
approximate current date. The third function is used for operations such
as signing certs and timestamping and tries to get the time from a
hardware time source if one is available */
time_t getTime( void )
{
const time_t theTime = time( NULL );
return( ( theTime < MIN_TIME_VALUE ) ? 0 : theTime );
}
time_t getApproxTime( void )
{
const time_t theTime = time( NULL );
return( ( theTime < MIN_TIME_VALUE ) ? CURRENT_TIME_VALUE : theTime );
}
time_t getReliableTime( const CRYPT_HANDLE cryptHandle )
{
CRYPT_DEVICE cryptDevice;
RESOURCE_DATA msgData;
time_t theTime;
int status;
/* Get the dependent device for the object that needs the time */
status = krnlSendMessage( cryptHandle, IMESSAGE_GETDEPENDENT,
&cryptDevice, OBJECT_TYPE_DEVICE );
if( cryptStatusError( status ) )
cryptDevice = SYSTEM_OBJECT_HANDLE;
/* Try and get the time from the device */
setMessageData( &msgData, &theTime, sizeof( time_t ) );
status = krnlSendMessage( cryptDevice, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_TIME );
if( cryptStatusError( status ) && cryptDevice != SYSTEM_OBJECT_HANDLE )
/* We couldn't get the time from a crypto token, fall back to the
system device */
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_TIME );
if( cryptStatusError( status ) )
return( 0 );
return( ( theTime < MIN_TIME_VALUE ) ? 0 : theTime );
}
/* Calculate a 16-bit Fletcher-like checksum of a block of data. We don't
bother with masking to 16 bits since it's not being used as a true
checksum */
int checksumData( const void *data, const int dataLength )
{
const BYTE *dataPtr = data;
int sum1 = 0, sum2 = 0, i;
for( i = 0; i < dataLength; i++ )
{
sum1 += dataPtr[ i ];
sum2 += sum1;
}
return( sum2 & 0xFFFF );
}
/* Determine the parameters for a particular hash algorithm */
void getHashParameters( const CRYPT_ALGO_TYPE hashAlgorithm,
HASHFUNCTION *hashFunction, int *hashSize )
{
void md2HashBuffer( HASHINFO hashInfo, BYTE *outBuffer,
const BYTE *inBuffer, const int length,
const HASH_STATE hashState );
void md5HashBuffer( HASHINFO hashInfo, BYTE *outBuffer,
const BYTE *inBuffer, const int length,
const HASH_STATE hashState );
void ripemd160HashBuffer( HASHINFO hashInfo, BYTE *outBuffer,
const BYTE *inBuffer, const int length,
const HASH_STATE hashState );
void shaHashBuffer( HASHINFO hashInfo, BYTE *outBuffer,
const BYTE *inBuffer, const int length,
const HASH_STATE hashState );
void sha2HashBuffer( HASHINFO hashInfo, BYTE *outBuffer,
const BYTE *inBuffer, const int length,
const HASH_STATE hashState );
assert( isWritePtr( hashFunction, sizeof( HASHFUNCTION ) ) );
assert( ( hashSize == NULL ) || isWritePtr( hashSize, sizeof( int ) ) );
switch( hashAlgorithm )
{
#ifdef USE_MD2
case CRYPT_ALGO_MD2:
*hashFunction = md2HashBuffer;
if( hashSize != NULL )
*hashSize = MD2_DIGEST_LENGTH;
return;
#endif /* USE_MD2 */
case CRYPT_ALGO_MD5:
*hashFunction = md5HashBuffer;
if( hashSize != NULL )
*hashSize = MD5_DIGEST_LENGTH;
return;
#ifdef USE_RIPEMD160
case CRYPT_ALGO_RIPEMD160:
*hashFunction = ripemd160HashBuffer;
if( hashSize != NULL )
*hashSize = RIPEMD160_DIGEST_LENGTH;
return;
#endif /* USE_RIPEMD160 */
case CRYPT_ALGO_SHA:
*hashFunction = shaHashBuffer;
if( hashSize != NULL )
*hashSize = SHA_DIGEST_LENGTH;
return;
#ifdef USE_SHA2
case CRYPT_ALGO_SHA2:
*hashFunction = sha2HashBuffer;
if( hashSize != NULL )
*hashSize = SHA256_DIGEST_SIZE;
return;
#endif /* USE_SHA2 */
}
/* Make sure that we always get some sort of hash function rather than
just dying. This code always works because the internal self-test
has confirmed the availability and functioning of SHA-1 on startup */
*hashFunction = shaHashBuffer;
if( hashSize != NULL )
*hashSize = SHA_DIGEST_LENGTH;
assert( NOTREACHED );
}
/* Perform the FIPS-140 statistical checks that are feasible on a byte
string. The full suite of tests assumes an infinite source of values (and
time) is available, the following is a scaled-down version used to sanity-
check keys and other short random data blocks. Note that this check
requires at least 64 bits of data in order to produce useful results */
BOOLEAN checkEntropy( const BYTE *data, const int dataLength )
{
const int delta = ( dataLength < 16 ) ? 1 : 0;
int bitCount[ 4 ] = { 0 }, noOnes, i;
assert( isReadPtr( data, dataLength ) );
assert( dataLength >= 8 );
for( i = 0; i < dataLength; i++ )
{
const int value = data[ i ];
bitCount[ value & 3 ]++;
bitCount[ ( value >> 2 ) & 3 ]++;
bitCount[ ( value >> 4 ) & 3 ]++;
bitCount[ value >> 6 ]++;
}
/* Monobit test: Make sure that at least 1/4 of the bits are ones and 1/4
are zeroes */
noOnes = bitCount[ 1 ] + bitCount[ 2 ] + ( 2 * bitCount[ 3 ] );
if( noOnes < dataLength * 2 || noOnes > dataLength * 6 )
return( FALSE );
/* Poker test (almost): Make sure that each bit pair is present at least
1/16 of the time. The FIPS 140 version uses 4-bit values, but the
numer of samples available from the keys is far too small for this.
This isn't precisely 1/16, for short samples (< 128 bits) we adjust
the count by one because of the small sample size, and for odd-length
data we're getting four more samples so the actual figure is slightly
less than 1/16 */
if( ( bitCount[ 0 ] + delta < dataLength / 2 ) || \
( bitCount[ 1 ] + delta < dataLength / 2 ) || \
( bitCount[ 2 ] + delta < dataLength / 2 ) || \
( bitCount[ 3 ] + delta < dataLength / 2 ) )
return( FALSE );
return( TRUE );
}
/* Copy a string attribute to external storage, with various range checks
to follow the cryptlib semantics (these will already have been done by
the caller, this is just a backup check) */
int attributeCopy( RESOURCE_DATA *msgData, const void *attribute,
const int attributeLength )
{
assert( isWritePtr( msgData, sizeof( RESOURCE_DATA ) ) );
if( attributeLength == 0 )
{
msgData->length = 0;
return( CRYPT_ERROR_NOTFOUND );
}
if( msgData->data != NULL )
{
assert( attribute != NULL );
assert( attributeLength > 0 );
if( attributeLength > msgData->length || \
checkBadPtrWrite( msgData->data, attributeLength ) )
return( CRYPT_ARGERROR_STR1 );
memcpy( msgData->data, attribute, attributeLength );
}
msgData->length = attributeLength;
return( CRYPT_OK );
}
/* Check whether a given algorithm is available */
BOOLEAN algoAvailable( const CRYPT_ALGO_TYPE cryptAlgo )
{
CRYPT_QUERY_INFO queryInfo;
return( cryptStatusOK( krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_QUERYCAPABILITY, &queryInfo,
cryptAlgo ) ) ? TRUE : FALSE );
}
/* Dynamic buffer management functions. When reading variable-length
attribute data we can usually fit the data in a small, fixed-length
buffer, but occasionally we have to cope with larger data amounts that
require a dynamically-allocated buffer. The following routines manage
this process, dynamically allocating and freeing a larger buffer if
required */
int dynCreate( DYNBUF *dynBuf, const CRYPT_HANDLE cryptHandle,
const CRYPT_ATTRIBUTE_TYPE attributeType )
{
RESOURCE_DATA msgData;
const MESSAGE_TYPE message = \
( attributeType == CRYPT_CERTFORMAT_CERTIFICATE ) ? \
IMESSAGE_CRT_EXPORT : IMESSAGE_GETATTRIBUTE_S;
void *dataPtr = NULL;
int status;
assert( isWritePtr( dynBuf, DYNBUF ) );
assert( ( cryptHandle == CRYPT_UNUSED && \
attributeType == CRYPT_UNUSED ) || \
( checkHandleRange( cryptHandle ) && \
( isAttribute( attributeType ) || \
isInternalAttribute( attributeType ) ) ) );
/* Clear return value */
dynBuf->data = dynBuf->dataBuffer;
dynBuf->length = 0;
/* If we're just creating a placeholder buffer, return now */
if( cryptHandle == CRYPT_UNUSED )
return( CRYPT_OK );
/* Get the data from the object */
setMessageData( &msgData, NULL, 0 );
status = krnlSendMessage( cryptHandle, message, &msgData,
attributeType );
if( cryptStatusError( status ) )
return( status );
if( msgData.length > DYNBUF_SIZE )
{
/* The data is larger than the built-in buffer size, dynamically
allocate a larger buffer */
if( ( dataPtr = clDynAlloc( "dynCreate", msgData.length ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
msgData.data = dataPtr;
status = krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE_S,
&msgData, attributeType );
if( cryptStatusError( status ) )
{
clFree( "dynCreate", dataPtr );
return( status );
}
dynBuf->data = dataPtr;
}
else
{
/* The data will fit into the built-in buffer, read it directly into
the buffer */
msgData.data = dynBuf->data;
status = krnlSendMessage( cryptHandle, message, &msgData,
attributeType );
if( cryptStatusError( status ) )
return( status );
}
dynBuf->length = msgData.length;
return( CRYPT_OK );
}
void dynDestroy( DYNBUF *dynBuf )
{
assert( isWritePtr( dynBuf, DYNBUF ) );
assert( dynBuf->length == 0 || \
isWritePtr( dynBuf->data, dynBuf->length ) );
if( dynBuf->length <= 0 )
return;
zeroise( dynBuf->data, dynBuf->length );
if( dynBuf->data != dynBuf->dataBuffer )
clFree( "dynDestroy", dynBuf->data );
}
/* Memory pool management functions. When allocating many little blocks of
memory, especially in resource-constrained systems, it's better if we pre-
allocate a small memory pool ourselves and grab chunks of it as required,
falling back to dynamically allocating memory later on if we exhaust the
pool. The following functions implement the custom memory pool
management */
typedef struct {
void *storage; /* Memory pool */
int storagePos, storageSize; /* Current usage and total size of pool */
} MEMPOOL_INFO;
void initMemPool( void *statePtr, void *memPool, const int memPoolSize )
{
MEMPOOL_INFO *state = ( MEMPOOL_INFO * ) statePtr;
assert( isWritePtr( state, sizeof( MEMPOOL_INFO ) ) );
assert( isWritePtr( memPool, memPoolSize ) );
memset( state, 0, sizeof( MEMPOOL_INFO ) );
state->storage = memPool;
state->storageSize = memPoolSize;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -