📄 mech_drv.c
字号:
/****************************************************************************
* *
* cryptlib Key Derivation Mechanism Routines *
* Copyright Peter Gutmann 1992-2007 *
* *
****************************************************************************/
#ifdef INC_ALL
#include "crypt.h"
#include "mech_int.h"
#include "asn1.h"
#include "pgp.h"
#else
#include "crypt.h"
#include "mechs/mech_int.h"
#include "misc/asn1.h"
#include "misc/pgp.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* PRF Building Blocks *
* *
****************************************************************************/
/* HMAC-based PRF used for PKCS #5 v2 and TLS */
#define HMAC_DATASIZE 64
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5, 7, 8 ) ) \
static int prfInit( IN const HASHFUNCTION hashFunction,
IN const HASHFUNCTION_ATOMIC hashFunctionAtomic,
INOUT TYPECAST( HASHINFO ) void *hashState,
IN_LENGTH_HASH const int hashSize,
OUT_BUFFER( processedKeyMaxLength, *processedKeyLength ) \
void *processedKey,
IN_LENGTH_FIXED( HMAC_DATASIZE ) \
const int processedKeyMaxLength,
OUT_RANGE( 0, HMAC_DATASIZE ) int *processedKeyLength,
IN_BUFFER( keyLength ) const void *key,
IN_LENGTH_SHORT const int keyLength )
{
BYTE hashBuffer[ HMAC_DATASIZE + 8 ], *keyPtr = processedKey;
int i;
assert( isWritePtr( hashState, sizeof( HASHINFO ) ) );
assert( isWritePtr( processedKey, processedKeyMaxLength ) );
assert( isWritePtr( processedKeyLength, sizeof( int ) ) );
assert( isReadPtr( key, keyLength ) );
REQUIRES( hashFunction != NULL && hashFunctionAtomic != NULL );
REQUIRES( hashSize >= 16 && hashSize <= CRYPT_MAX_HASHSIZE );
REQUIRES( processedKeyMaxLength == HMAC_DATASIZE );
REQUIRES( keyLength > 0 && keyLength < MAX_INTLENGTH_SHORT );
/* Clear return values */
memset( processedKey, 0, min( 16, processedKeyMaxLength ) );
*processedKeyLength = 0;
/* If the key size is larger than the hash data size reduce it to the
hash size before processing it (yuck. You're required to do this
though) */
if( keyLength > HMAC_DATASIZE )
{
/* Hash the user key down to the hash size and use the hashed form of
the key */
hashFunctionAtomic( processedKey, processedKeyMaxLength, key,
keyLength );
*processedKeyLength = hashSize;
}
else
{
/* Copy the key to internal storage */
memcpy( processedKey, key, keyLength );
*processedKeyLength = keyLength;
}
/* Perform the start of the inner hash using the zero-padded key XORed
with the ipad value */
memset( hashBuffer, HMAC_IPAD, HMAC_DATASIZE );
for( i = 0; i < *processedKeyLength; i++ )
hashBuffer[ i ] ^= *keyPtr++;
hashFunction( hashState, NULL, 0, hashBuffer, HMAC_DATASIZE,
HASH_STATE_START );
zeroise( hashBuffer, HMAC_DATASIZE );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 6 ) ) \
static int prfEnd( IN const HASHFUNCTION hashFunction,
INOUT TYPECAST( HASHINFO ) void *hashState,
IN_LENGTH_HASH const int hashSize,
OUT_BUFFER_FIXED( hashMaxSize ) void *hash,
IN_LENGTH_HASH const int hashMaxSize,
IN_BUFFER( processedKeyLength ) const void *processedKey,
IN_RANGE( 1, HMAC_DATASIZE ) const int processedKeyLength )
{
BYTE hashBuffer[ HMAC_DATASIZE + 8 ];
BYTE digestBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
int i;
assert( isWritePtr( hashState, sizeof( HASHINFO ) ) );
assert( isWritePtr( hash, hashMaxSize ) );
assert( isReadPtr( processedKey, processedKeyLength ) );
REQUIRES( hashFunction != NULL );
REQUIRES( hashSize >= 16 && hashSize <= CRYPT_MAX_HASHSIZE );
REQUIRES( hashMaxSize >= 16 && hashMaxSize <= CRYPT_MAX_HASHSIZE );
REQUIRES( processedKeyLength >= 1 && \
processedKeyLength <= HMAC_DATASIZE );
/* Complete the inner hash and extract the digest */
hashFunction( hashState, digestBuffer, CRYPT_MAX_HASHSIZE, NULL, 0,
HASH_STATE_END );
/* Perform the outer hash using the zero-padded key XORed with the opad
value followed by the digest from the inner hash */
memset( hashBuffer, HMAC_OPAD, HMAC_DATASIZE );
memcpy( hashBuffer, processedKey, processedKeyLength );
for( i = 0; i < processedKeyLength; i++ )
hashBuffer[ i ] ^= HMAC_OPAD;
hashFunction( hashState, NULL, 0, hashBuffer, HMAC_DATASIZE,
HASH_STATE_START );
zeroise( hashBuffer, HMAC_DATASIZE );
hashFunction( hashState, hash, hashMaxSize, digestBuffer, hashSize,
HASH_STATE_END );
zeroise( digestBuffer, CRYPT_MAX_HASHSIZE );
return( CRYPT_OK );
}
/****************************************************************************
* *
* PKCS #5v2 Key Derivation *
* *
****************************************************************************/
/* Implement one round of the PKCS #5v2 PRF */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4, 6, 8 ) ) \
static int pbkdf2Hash( OUT_BUFFER_FIXED( outLength ) BYTE *out,
IN_RANGE( 1, CRYPT_MAX_HASHSIZE ) const int outLength,
IN const HASHFUNCTION hashFunction,
INOUT TYPECAST( HASHINFO ) void *initialHashState,
IN_LENGTH_HASH const int hashSize,
IN_BUFFER( keyLength ) const void *key,
IN_RANGE( 1, HMAC_DATASIZE ) const int keyLength,
IN_BUFFER( saltLength ) const void *salt,
IN_RANGE( 4, 512 ) const int saltLength,
IN_INT const int iterations,
IN_RANGE( 1, 1000 ) const int blockCount )
{
HASHINFO hashInfo;
BYTE block[ CRYPT_MAX_HASHSIZE + 8 ], countBuffer[ 4 + 8 ];
int i, status;
assert( isWritePtr( out, outLength ) );
assert( isWritePtr( initialHashState, sizeof( HASHINFO ) ) );
assert( isReadPtr( key, keyLength ) );
assert( isReadPtr( salt, saltLength ) );
REQUIRES( hashFunction != NULL );
REQUIRES( outLength > 0 && outLength <= hashSize && \
outLength <= CRYPT_MAX_HASHSIZE );
REQUIRES( hashSize >= 16 && hashSize <= CRYPT_MAX_HASHSIZE );
REQUIRES( keyLength >= 1 && keyLength <= HMAC_DATASIZE );
REQUIRES( saltLength >= 4 && saltLength <= 512 );
REQUIRES( iterations > 0 && iterations < MAX_INTLENGTH );
REQUIRES( blockCount > 0 && blockCount <= 1000 );
/* Clear return value */
memset( out, 0, outLength );
/* Set up the block counter buffer. This will never have more than the
last few bits set (8 bits = 5100 bytes of key) so we only change the
last byte */
memset( countBuffer, 0, 4 );
countBuffer[ 3 ] = ( BYTE ) blockCount;
/* Calculate HMAC( salt || counter ) */
memcpy( hashInfo, initialHashState, sizeof( HASHINFO ) );
hashFunction( hashInfo, NULL, 0, salt, saltLength, HASH_STATE_CONTINUE );
hashFunction( hashInfo, NULL, 0, countBuffer, 4, HASH_STATE_CONTINUE );
status = prfEnd( hashFunction, hashInfo, hashSize, block,
CRYPT_MAX_HASHSIZE, key, keyLength );
if( cryptStatusError( status ) )
{
zeroise( hashInfo, sizeof( HASHINFO ) );
return( status );
}
memcpy( out, block, outLength );
/* Calculate HMAC( T1 ) ^ HMAC( T2 ) ^ ... HMAC( Tc ) */
for( i = 0; i < iterations - 1 && i < FAILSAFE_ITERATIONS_MAX; i++ )
{
int j;
/* Generate the PRF output for the current iteration */
memcpy( hashInfo, initialHashState, sizeof( HASHINFO ) );
hashFunction( hashInfo, NULL, 0, block, hashSize, HASH_STATE_CONTINUE );
status = prfEnd( hashFunction, hashInfo, hashSize, block,
CRYPT_MAX_HASHSIZE, key, keyLength );
if( cryptStatusError( status ) )
{
zeroise( hashInfo, sizeof( HASHINFO ) );
zeroise( block, CRYPT_MAX_HASHSIZE );
return( status );
}
/* XOR the new PRF output into the existing PRF output */
for( j = 0; j < outLength; j++ )
out[ j ] ^= block[ j ];
}
ENSURES( i < FAILSAFE_ITERATIONS_MAX );
zeroise( hashInfo, sizeof( HASHINFO ) );
zeroise( block, CRYPT_MAX_HASHSIZE );
return( CRYPT_OK );
}
/* Perform PKCS #5v2 derivation */
CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int derivePKCS5( STDC_UNUSED void *dummy,
INOUT MECHANISM_DERIVE_INFO *mechanismInfo )
{
CRYPT_ALGO_TYPE hashAlgo;
HASHFUNCTION_ATOMIC hashFunctionAtomic;
HASHFUNCTION hashFunction;
HASHINFO initialHashInfo;
BYTE processedKey[ HMAC_DATASIZE + 8 ];
BYTE *dataOutPtr = mechanismInfo->dataOut;
static const MAP_TABLE mapTbl[] = {
{ CRYPT_ALGO_HMAC_MD5, CRYPT_ALGO_MD5 },
{ CRYPT_ALGO_HMAC_SHA1, CRYPT_ALGO_SHA1 },
{ CRYPT_ALGO_HMAC_RIPEMD160, CRYPT_ALGO_RIPEMD160 },
{ CRYPT_ALGO_HMAC_SHA2, CRYPT_ALGO_SHA2 },
{ CRYPT_ERROR, CRYPT_ERROR }, { CRYPT_ERROR, CRYPT_ERROR }
};
int hashSize, keyIndex, processedKeyLength, blockCount = 1;
int value, iterationCount, status;
UNUSED_ARG( dummy );
assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );
/* Clear return value */
memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );
/* Convert the HMAC algorithm into the equivalent hash algorithm for use
with the PRF */
status = mapValue( mechanismInfo->hashAlgo, &value, mapTbl,
FAILSAFE_ARRAYSIZE( mapTbl, MAP_TABLE ) );
if( cryptStatusError( status ) )
return( status );
hashAlgo = value;
/* Initialise the HMAC information with the user key. Although the user
has specified the algorithm in terms of an HMAC we're synthesising it
from the underlying hash algorithm since this allows us to perform the
PRF setup once and reuse the initial value for any future hashing */
getHashAtomicParameters( hashAlgo, &hashFunctionAtomic, &hashSize );
getHashParameters( hashAlgo, &hashFunction, NULL );
status = prfInit( hashFunction, hashFunctionAtomic, initialHashInfo,
hashSize, processedKey, HMAC_DATASIZE,
&processedKeyLength, mechanismInfo->dataIn,
mechanismInfo->dataInLength );
if( cryptStatusError( status ) )
return( status );
/* Produce enough blocks of output to fill the key */
for( keyIndex = 0, iterationCount = 0;
keyIndex < mechanismInfo->dataOutLength && \
iterationCount < FAILSAFE_ITERATIONS_MED;
keyIndex += hashSize, dataOutPtr += hashSize, iterationCount++ )
{
const int noKeyBytes = \
( mechanismInfo->dataOutLength - keyIndex > hashSize ) ? \
hashSize : mechanismInfo->dataOutLength - keyIndex;
status = pbkdf2Hash( dataOutPtr, noKeyBytes,
hashFunction, initialHashInfo, hashSize,
processedKey, processedKeyLength,
mechanismInfo->salt, mechanismInfo->saltLength,
mechanismInfo->iterations, blockCount++ );
if( cryptStatusError( status ) )
break;
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
zeroise( initialHashInfo, sizeof( HASHINFO ) );
zeroise( processedKey, HMAC_DATASIZE );
if( cryptStatusError( status ) )
{
zeroise( mechanismInfo->dataOut, mechanismInfo->dataOutLength );
return( status );
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* PKCS #12 Key Derivation *
* *
****************************************************************************/
#ifdef USE_PKCS12
/* Concantenate enough copies of input data together to fill an output
buffer */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int expandData( OUT_BUFFER_FIXED( outLen ) BYTE *outPtr,
IN_LENGTH_SHORT const int outLen,
IN_BUFFER( inLen ) const BYTE *inPtr,
IN_LENGTH_SHORT const int inLen )
{
int remainder, iterationCount;
assert( isWritePtr( outPtr, outLen ) );
assert( isReadPtr( inPtr, inLen ) );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -