/****************************************************************************
*																			*
*				cryptlib Key Derivation Mechanism Routines					*
*					Copyright Peter Gutmann 1992-2025						*
*																			*
****************************************************************************/

#ifdef INC_ALL
  #include "crypt.h"
  #include "asn1.h"
  #include "mech_int.h"
  #include "pgp.h"
#else
  #include "crypt.h"
  #include "enc_dec/asn1.h"
  #include "mechs/mech_int.h"
  #include "misc/pgp.h"
#endif /* Compiler-specific includes */

/* The minimum and maximum data sizes for HMAC, corresponding to the 
   smallest and largest input block size for the hash function that it's 
   built from, in this case SHA-1 and SHA-512 (and also SHA-384) at 64
   and 128 bytes */

#define HMAC_MIN_DATASIZE		64
#define HMAC_MAX_DATASIZE		128

/****************************************************************************
*																			*
*								PRF Building Blocks							*
*																			*
****************************************************************************/

/* HMAC-based PRF used for PBKDF2 / PKCS #5v2, HKDF, TLS, and HOTP / TOTP */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 6, 8, 9 ) ) \
static int prfInit( IN_PTR const HASH_FUNCTION hashFunction, 
					IN_PTR const HASH_FUNCTION_ATOMIC hashFunctionAtomic,
					OUT_PTR HASHINFO hashState, 
					IN_LENGTH_HASH const int hashSize, 
					IN_RANGE( HMAC_MIN_DATASIZE, HMAC_MAX_DATASIZE ) \
						const int hashBlockSize,
					OUT_BUFFER( processedKeyMaxLength, *processedKeyLength ) \
						void *processedKey, 
					IN_RANGE( HMAC_MIN_DATASIZE, HMAC_MAX_DATASIZE ) \
						const int processedKeyMaxLength,
					OUT_RANGE( 0, HMAC_MAX_DATASIZE ) int *processedKeyLength, 
					IN_BUFFER( keyLength ) const void *key, 
					IN_LENGTH_SHORT const int keyLength )
	{
	const BYTE *keyPtr = processedKey;
	BYTE hashBuffer[ HMAC_MAX_DATASIZE + 8 ];
	LOOP_INDEX i;

	assert( isWritePtr( hashState, sizeof( HASHINFO ) ) );
	assert( isWritePtrDynamic( processedKey, processedKeyMaxLength ) );
	assert( isWritePtr( processedKeyLength, sizeof( int ) ) );
	assert( isReadPtrDynamic( key, keyLength ) );

	REQUIRES( hashFunction != NULL && hashFunctionAtomic != NULL );
	REQUIRES( hashSize >= MIN_HASHSIZE && hashSize <= CRYPT_MAX_HASHSIZE );
	REQUIRES( hashBlockSize >= HMAC_MIN_DATASIZE && \
			  hashBlockSize <= HMAC_MAX_DATASIZE );
	REQUIRES( processedKeyMaxLength >= HMAC_MIN_DATASIZE && \
			  processedKeyMaxLength <= HMAC_MAX_DATASIZE );
	REQUIRES( isShortIntegerRangeNZ( keyLength ) );

	/* Clear return values */
	REQUIRES( rangeCheck( processedKeyMaxLength, HMAC_MIN_DATASIZE, 
						  HMAC_MAX_DATASIZE ) );
	memset( processedKey, 0, min( 16, processedKeyMaxLength ) );
	*processedKeyLength = 0;

	/* If the key size is larger than the HMAC data size reduce it to the 
	   hash size before processing it (yuck.  You're required to do this
	   though) */
	if( keyLength > hashBlockSize )
		{
		/* 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.  Note that this has the 
		   potential to leak a tiny amount of timing information about the
		   key length, but given the mass of operations that follow this is
		   unlikely to be significant */
		REQUIRES( rangeCheck( keyLength, 1, processedKeyMaxLength ) );
		memcpy( processedKey, key, keyLength );
		*processedKeyLength = keyLength;
		}

	/* Perform the start of the inner hash using the zero-padded key XORed
	   with the ipad value.  This could be done slightly more efficiently, 
	   but the following sequence of operations minimises timing channels 
	   leaking the key length */
	REQUIRES( rangeCheck( *processedKeyLength, 1, HMAC_MAX_DATASIZE ) );
	memcpy( hashBuffer, keyPtr, *processedKeyLength );
	if( *processedKeyLength < hashBlockSize )
		{
		REQUIRES( rangeCheck( *processedKeyLength, 1, hashBlockSize - 1 ) );
		REQUIRES( !checkOverflowSub( hashBlockSize, *processedKeyLength ) );
		memset( hashBuffer + *processedKeyLength, 0, 
				hashBlockSize - *processedKeyLength );
		}
	LOOP_EXT( i = 0, i < hashBlockSize, i++, hashBlockSize + 1 )
		{
		ENSURES( LOOP_INVARIANT_EXT( i, 0, hashBlockSize - 1,
									 hashBlockSize + 1 ) );

		hashBuffer[ i ] ^= HMAC_IPAD;
		}
	ENSURES( LOOP_BOUND_OK );
	hashFunction( hashState, NULL, 0, hashBuffer, hashBlockSize, 
				  HASH_STATE_START );
	zeroise( hashBuffer, HMAC_MAX_DATASIZE );

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 6 ) ) \
static int prfEnd( IN_PTR const HASH_FUNCTION hashFunction, 
				   INOUT_PTR HASHINFO hashState,
				   IN_LENGTH_HASH const int hashSize, 
				   IN_RANGE( HMAC_MIN_DATASIZE, HMAC_MAX_DATASIZE ) \
						const int hashBlockSize,
				   OUT_BUFFER_FIXED( hashMaxSize ) void *hash, 
				   IN_LENGTH_HASH const int hashMaxSize, 
				   IN_BUFFER( processedKeyLength ) const void *processedKey, 
				   IN_RANGE( 1, HMAC_MAX_DATASIZE ) \
						const int processedKeyLength )
	{
	BYTE hashBuffer[ HMAC_MAX_DATASIZE + 8 ];
	BYTE digestBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
	LOOP_INDEX i;

	assert( isWritePtr( hashState, sizeof( HASHINFO ) ) );
	assert( isWritePtrDynamic( hash, hashMaxSize ) );
	assert( isReadPtrDynamic( processedKey, processedKeyLength ) );

	REQUIRES( hashFunction != NULL );
	REQUIRES( hashSize >= MIN_HASHSIZE && hashSize <= CRYPT_MAX_HASHSIZE );
	REQUIRES( hashBlockSize >= HMAC_MIN_DATASIZE && \
			  hashBlockSize <= HMAC_MAX_DATASIZE );
	REQUIRES( hashMaxSize >= MIN_HASHSIZE && \
			  hashMaxSize <= CRYPT_MAX_HASHSIZE );
	REQUIRES( processedKeyLength >= 1 && \
			  processedKeyLength <= HMAC_MAX_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.  As with the init
	   function this could be done slightly more efficiently, but the 
	   following sequence of operations minimises timing channels leaking 
	   the key length */
	REQUIRES( rangeCheck( processedKeyLength, 1, HMAC_MAX_DATASIZE ) );
	memcpy( hashBuffer, processedKey, processedKeyLength );
	if( processedKeyLength < hashBlockSize )
		{
		REQUIRES( rangeCheck( processedKeyLength, 1, hashBlockSize - 1 ) );
		REQUIRES( !checkOverflowSub( hashBlockSize, processedKeyLength ) );
		memset( hashBuffer + processedKeyLength, 0, 
				hashBlockSize - processedKeyLength );
		}
	LOOP_EXT( i = 0, i < hashBlockSize, i++, hashBlockSize + 1 )
		{
		ENSURES( LOOP_INVARIANT_EXT( i, 0, hashBlockSize - 1,
									 hashBlockSize + 1 ) );

		hashBuffer[ i ] ^= HMAC_OPAD;
		}
	ENSURES( LOOP_BOUND_OK );
	hashFunction( hashState, NULL, 0, hashBuffer, hashBlockSize, 
				  HASH_STATE_START );
	zeroise( hashBuffer, HMAC_MAX_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, 7, 9 ) ) \
static int pbkdf2Hash( OUT_BUFFER_FIXED( outLength ) BYTE *out, 
					   IN_RANGE( 1, CRYPT_MAX_HASHSIZE ) const int outLength, 
					   IN_PTR const HASH_FUNCTION hashFunction, 
					   INOUT_PTR HASHINFO initialHashState,
					   IN_LENGTH_HASH const int hashSize, 
					   IN_RANGE( HMAC_MIN_DATASIZE, HMAC_MAX_DATASIZE ) \
							const int hashBlockSize,
					   IN_BUFFER( keyLength ) const void *key, 
					   IN_RANGE( 1, HMAC_MAX_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, 255 ) const int blockCount )
	{
	HASHINFO hashInfo;
	BYTE block[ CRYPT_MAX_HASHSIZE + 8 ], countBuffer[ 4 + 8 ];
	LOOP_INDEX i;
	int status;

	assert( isWritePtrDynamic( out, outLength ) );
	assert( isWritePtr( initialHashState, sizeof( HASHINFO ) ) );
	assert( isReadPtrDynamic( key, keyLength ) );
	assert( isReadPtrDynamic( salt, saltLength ) );

	REQUIRES( hashFunction != NULL );
	REQUIRES( outLength > 0 && outLength <= hashSize && \
			  outLength <= CRYPT_MAX_HASHSIZE );
	REQUIRES( hashSize >= MIN_HASHSIZE && hashSize <= CRYPT_MAX_HASHSIZE );
	REQUIRES( hashBlockSize >= HMAC_MIN_DATASIZE && \
			  hashBlockSize <= HMAC_MAX_DATASIZE );
	REQUIRES( keyLength >= 1 && keyLength <= HMAC_MAX_DATASIZE );
	REQUIRES( saltLength >= 4 && saltLength <= 512 );
	REQUIRES( isIntegerRangeNZ( iterations ) );
	REQUIRES( blockCount > 0 && blockCount <= 255 );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( outLength ) ); 
	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 T0 = 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, hashBlockSize, 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 ) */
	LOOP_MAX( i = 0, i < iterations - 1, i++ )
		{
		LOOP_INDEX_ALT j;

		ENSURES( LOOP_INVARIANT_MAX( i, 0, iterations - 2 ) );

		/* 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, hashBlockSize, 
						 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 */
		LOOP_EXT_ALT( j = 0, j < outLength, j++, CRYPT_MAX_HASHSIZE + 1 )
			{
			ENSURES( LOOP_INVARIANT_EXT_ALT( j, 0, outLength - 1,
											 CRYPT_MAX_HASHSIZE + 1 ) );

			out[ j ] ^= block[ j ];
			}
		ENSURES( LOOP_BOUND_OK_ALT );
		}
	ENSURES( LOOP_BOUND_OK );

	zeroise( hashInfo, sizeof( HASHINFO ) );
	zeroise( block, CRYPT_MAX_HASHSIZE );

	return( CRYPT_OK );
	}

/* Perform PBKDF2 / PKCS #5v2 derivation */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int derivePBKDF2( STDC_UNUSED void *dummy, 
				  INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	CRYPT_ALGO_TYPE hashAlgo;
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	HASH_FUNCTION hashFunction;
	HASHINFO initialHashInfo;
	BYTE processedKey[ HMAC_MAX_DATASIZE + 8 ];
	BYTE *dataOutPtr = mechanismInfo->dataOut;
	static const MAP_TABLE mapTbl[] = {
		{ CRYPT_ALGO_HMAC_SHA1, CRYPT_ALGO_SHA1 },
		{ CRYPT_ALGO_HMAC_SHA2, CRYPT_ALGO_SHA2 },
		{ CRYPT_ALGO_HMAC_SHAng, CRYPT_ALGO_SHAng },
		{ CRYPT_ERROR, CRYPT_ERROR }, { CRYPT_ERROR, CRYPT_ERROR }
		};
	int hashSize, hashBlockSize, processedKeyLength, blockCount = 1;
	LOOP_INDEX keyIndex;
	int value, status;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	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;
	if( !algoAvailable( hashAlgo ) )
		return( CRYPT_ERROR_NOTAVAIL );

	/* 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, mechanismInfo->hashParam, 
							 &hashFunctionAtomic, &hashSize );
	getHashParameters( hashAlgo, mechanismInfo->hashParam, &hashFunction, 
					   NULL, &hashBlockSize );
	status = prfInit( hashFunction, hashFunctionAtomic, initialHashInfo, 
					  hashSize, hashBlockSize, processedKey, 
					  HMAC_MAX_DATASIZE, &processedKeyLength, 
					  mechanismInfo->dataIn, 
					  mechanismInfo->dataInLength );
	if( cryptStatusError( status ) )
		{
		zeroise( processedKey, HMAC_MAX_DATASIZE );
		return( status );
		}

	/* Produce enough blocks of output to fill the key (nil sine magno 
	   labore) */
	LOOP_MED( keyIndex = 0, 
			  keyIndex < mechanismInfo->dataOutLength,
			  ( keyIndex += hashSize, dataOutPtr += hashSize ) )
		{
		const int noKeyBytes = \
			( mechanismInfo->dataOutLength - keyIndex > hashSize ) ? \
			hashSize : mechanismInfo->dataOutLength - keyIndex;

		ENSURES( LOOP_INVARIANT_MED_XXX( keyIndex, 0, 
										 mechanismInfo->dataOutLength - 1 ) );

		REQUIRES( !checkOverflowSub( mechanismInfo->dataOutLength, 
									 keyIndex ) );

		REQUIRES( !checkOverflowInc( blockCount ) );
		status = pbkdf2Hash( dataOutPtr, noKeyBytes, 
							 hashFunction, initialHashInfo, hashSize,
							 hashBlockSize, processedKey, processedKeyLength,
							 mechanismInfo->salt, mechanismInfo->saltLength,
							 mechanismInfo->iterations, blockCount++ );
		if( cryptStatusError( status ) )
			break;
		}
	ENSURES( LOOP_BOUND_OK );
	zeroise( initialHashInfo, sizeof( HASHINFO ) );
	zeroise( processedKey, HMAC_MAX_DATASIZE );
	if( cryptStatusError( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
		zeroise( mechanismInfo->dataOut, mechanismInfo->dataOutLength );
		return( status );
		}

	return( CRYPT_OK );
	}

/* Apply PBKDF2 / PKCS #5v2 as a pure (single-round) KDF */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int kdfPBKDF2( STDC_UNUSED void *dummy, 
			   INOUT_PTR MECHANISM_KDF_INFO *mechanismInfo )
	{
	MECHANISM_DERIVE_INFO mechanismDeriveInfo;
	MESSAGE_DATA msgData;
	BYTE masterSecretBuffer[ CRYPT_MAX_KEYSIZE + 8 ];
	BYTE keyBuffer[ CRYPT_MAX_KEYSIZE + 8 ];
	int masterSecretSize, keySize DUMMY_INIT, status;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* Get the key payload details from the key contexts */
	status = krnlSendMessage( mechanismInfo->masterKeyContext, 
							  IMESSAGE_GETATTRIBUTE, &masterSecretSize,
							  CRYPT_CTXINFO_KEYSIZE );
	if( cryptStatusOK( status ) )
		{
		status = krnlSendMessage( mechanismInfo->keyContext, 
								  IMESSAGE_GETATTRIBUTE, &keySize,
								  CRYPT_CTXINFO_KEYSIZE );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Extract the master secret value from the generic-secret context and 
	   derive the key from it using PBKDF2 as the KDF */
	REQUIRES( rangeCheck( masterSecretSize, 1, CRYPT_MAX_KEYSIZE ) );
	status = extractKeyData( mechanismInfo->masterKeyContext,
							 masterSecretBuffer, CRYPT_MAX_KEYSIZE, 
							 "keydata", 7 );
	if( cryptStatusError( status ) )
		return( status );
	setMechanismDeriveInfo( &mechanismDeriveInfo, keyBuffer, keySize,
							masterSecretBuffer, masterSecretSize,
							mechanismInfo->hashAlgo, mechanismInfo->salt,
							mechanismInfo->saltLength, 1 );
	mechanismDeriveInfo.hashParam = mechanismInfo->hashParam;
	status = derivePBKDF2( NULL, &mechanismDeriveInfo );
	zeroise( masterSecretBuffer, CRYPT_MAX_KEYSIZE );
	if( cryptStatusError( status ) )
		{
		zeroise( keyBuffer, CRYPT_MAX_KEYSIZE );
		return( status );
		}

	/* Load the derived key into the context */
	setMessageData( &msgData, keyBuffer, keySize );
	status = krnlSendMessage( mechanismInfo->keyContext, 
							  IMESSAGE_SETATTRIBUTE_S, &msgData,
							  CRYPT_CTXINFO_KEY );
	zeroise( keyBuffer, CRYPT_MAX_KEYSIZE );

	return( status );
	}

/****************************************************************************
*																			*
*								HKDF Key Derivation 						*
*																			*
****************************************************************************/

/* Perform HKDF derivation */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
static int deriveHKDF( STDC_UNUSED void *dummy, 
					   INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo,
					   IN_BUFFER_OPT( infoStringLen ) const void *infoString,
					   IN_LENGTH_SHORT_Z const int infoStringLen )
	{
	CRYPT_ALGO_TYPE hashAlgo;
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	HASH_FUNCTION hashFunction;
	HASHINFO initialHashInfo, hashInfo;
	BYTE processedKey[ HMAC_MAX_DATASIZE + 8 ];
	BYTE hkdfKey[ CRYPT_MAX_HASHSIZE + 8 ];
	BYTE block[ CRYPT_MAX_HASHSIZE + 8 ];
	BYTE *dataOutPtr = mechanismInfo->dataOut, counter = 1;
	static const MAP_TABLE mapTbl[] = {
		{ CRYPT_ALGO_HMAC_SHA1, CRYPT_ALGO_SHA1 },
		{ CRYPT_ALGO_HMAC_SHA2, CRYPT_ALGO_SHA2 },
		{ CRYPT_ALGO_HMAC_SHAng, CRYPT_ALGO_SHAng },
		{ CRYPT_ERROR, CRYPT_ERROR }, { CRYPT_ERROR, CRYPT_ERROR }
		};
	int hashSize, hashBlockSize, processedKeyLength, value, status;
	LOOP_INDEX keyIndex;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );
	assert( infoString == NULL || \
			isReadPtr( infoString, infoStringLen ) );
	
	REQUIRES( ( infoString == NULL && infoStringLen == 0 ) || \
			  ( infoString != NULL && \
				isShortIntegerRangeNZ( infoStringLen ) ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	/* Convert the HMAC algorithm into the equivalent hash algorithm for use 
	   with the PRF.  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 */
	status = mapValue( mechanismInfo->hashAlgo, &value, mapTbl, 
					   FAILSAFE_ARRAYSIZE( mapTbl, MAP_TABLE ) );
	if( cryptStatusError( status ) )
		return( status );
	hashAlgo = value;
	if( !algoAvailable( hashAlgo ) )
		return( CRYPT_ERROR_NOTAVAIL );

	/* Extract the master secret value from the generic-secret context and 
	   convert it into the KDF key:

		KDF-Key = HMAC( key )
		
	   Note that this is not a typo: The key is HMAC'd using the salt as the 
	   HMAC key, not the other way round */
	getHashAtomicParameters( hashAlgo, mechanismInfo->hashParam, 
							 &hashFunctionAtomic, &hashSize );
	getHashParameters( hashAlgo, mechanismInfo->hashParam, &hashFunction, 
					   NULL, &hashBlockSize );
	status = prfInit( hashFunction, hashFunctionAtomic, initialHashInfo, 
					  hashSize, hashBlockSize, processedKey, 
					  HMAC_MAX_DATASIZE, &processedKeyLength, 
					  mechanismInfo->salt, mechanismInfo->saltLength );
	if( cryptStatusOK( status ) )
		{
		hashFunction( initialHashInfo, NULL, 0, mechanismInfo->dataIn, 
					  mechanismInfo->dataInLength, HASH_STATE_CONTINUE );
		status = prfEnd( hashFunction, initialHashInfo, hashSize, 
						 hashBlockSize, hkdfKey, CRYPT_MAX_HASHSIZE, 
						 processedKey, processedKeyLength );
		}
	if( cryptStatusError( status ) )
		{
		zeroise( initialHashInfo, sizeof( HASHINFO ) );
		zeroise( processedKey, HMAC_MAX_DATASIZE );
		return( status );
		}

	/* Initialise the HMAC information with the HKDF key */
	status = prfInit( hashFunction, hashFunctionAtomic, initialHashInfo, 
					  hashSize, hashBlockSize, processedKey, 
					  HMAC_MAX_DATASIZE, &processedKeyLength, hkdfKey, 
					  hashSize );
	if( cryptStatusError( status ) )
		{
		zeroise( initialHashInfo, sizeof( HASHINFO ) );
		zeroise( processedKey, HMAC_MAX_DATASIZE );
		zeroise( hkdfKey, CRYPT_MAX_HASHSIZE );
		return( status );
		}

	/* Produce enough blocks of output to fill the key:

		T0 = {}
		T1 = HMAC( T0 || info || 0x01 )
		T2 = HMAC( T1 || info || 0x02 )
		Tn = HMAC( Tn-1 || info || 0x0n ) */
	LOOP_MED( keyIndex = 0, 
			  keyIndex < mechanismInfo->dataOutLength,
			  ( keyIndex += hashSize, dataOutPtr += hashSize ) )
		{
		const int noKeyBytes = \
			( mechanismInfo->dataOutLength - keyIndex > hashSize ) ? \
			hashSize : mechanismInfo->dataOutLength - keyIndex;

		ENSURES( LOOP_INVARIANT_MED_XXX( keyIndex, 0, 
										 mechanismInfo->dataOutLength - 1 ) );

		REQUIRES( !checkOverflowSub( mechanismInfo->dataOutLength,
									 keyIndex ) );

		/* Calculate HMAC( block[ n-1 ] || counter ) */
		memcpy( hashInfo, initialHashInfo, sizeof( HASHINFO ) );
		if( keyIndex > 0 )
			{
			hashFunction( hashInfo, NULL, 0, dataOutPtr - hashSize, 
						  hashSize, HASH_STATE_CONTINUE );
			}
		if( infoString != NULL )
			{
			hashFunction( hashInfo, NULL, 0, infoString, infoStringLen, 
						  HASH_STATE_CONTINUE );
			}
		hashFunction( hashInfo, NULL, 0, &counter, 1, 
					  HASH_STATE_CONTINUE );
		status = prfEnd( hashFunction, hashInfo, hashSize, hashBlockSize, 
						 block, CRYPT_MAX_HASHSIZE, processedKey, 
						 processedKeyLength );
		if( cryptStatusError( status ) )
			break;
		counter++;		/* Byte counter, no checkOverflowInc() */
		memcpy( dataOutPtr, block, noKeyBytes );
		}
	ENSURES( LOOP_BOUND_OK );
	zeroise( hashInfo, sizeof( HASHINFO ) );
	zeroise( initialHashInfo, sizeof( HASHINFO ) );
	zeroise( processedKey, HMAC_MAX_DATASIZE );
	zeroise( hkdfKey, CRYPT_MAX_HASHSIZE );
	zeroise( block, CRYPT_MAX_HASHSIZE );
	if( cryptStatusError( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
		zeroise( mechanismInfo->dataOut, mechanismInfo->dataOutLength );
		return( status );
		}

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int kdfHKDF( STDC_UNUSED void *dummy, 
			 INOUT_PTR MECHANISM_KDF_INFO *mechanismInfo )
	{
	MECHANISM_DERIVE_INFO mechanismDeriveInfo;
	MESSAGE_DATA msgData;
	BYTE masterSecretBuffer[ CRYPT_MAX_KEYSIZE + 8 ];
	BYTE keyBuffer[ CRYPT_MAX_KEYSIZE + 8 ];
	int masterSecretSize, keySize DUMMY_INIT, status;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* This code currently isn't used, the only protocol that uses HKDF is 
	   TLS 1.3 for which session/tls13_crypt.c synthesises it directly from
	   hashFunctionAtomic() to avoid having to make dozens of HKDF kernel
	   calls with all of the little bits and pieces that TLS 1.3 hashes up */
	DEBUG_DIAG(( "Unexpected invocation of HKDF mechanism" ));
	retIntError();
	
	/* Get the key payload details from the key contexts */
	status = krnlSendMessage( mechanismInfo->masterKeyContext, 
							  IMESSAGE_GETATTRIBUTE, &masterSecretSize,
							  CRYPT_CTXINFO_KEYSIZE );
	if( cryptStatusOK( status ) )
		{
		status = krnlSendMessage( mechanismInfo->keyContext, 
								  IMESSAGE_GETATTRIBUTE, &keySize,
								  CRYPT_CTXINFO_KEYSIZE );
		}
	if( cryptStatusError( status ) )
		return( status );
	status = extractKeyData( mechanismInfo->masterKeyContext,
							 masterSecretBuffer, CRYPT_MAX_KEYSIZE, 
							 "keydata", 7 );
	if( cryptStatusError( status ) )
		return( status );

	/* Derive the key from it using HKDF as the KDF */
	setMechanismDeriveInfo( &mechanismDeriveInfo, keyBuffer, keySize,
							masterSecretBuffer, masterSecretSize,
							mechanismInfo->hashAlgo, mechanismInfo->salt,
							mechanismInfo->saltLength, 1 );
	mechanismDeriveInfo.hashParam = mechanismInfo->hashParam;
	status = deriveHKDF( NULL, &mechanismDeriveInfo, NULL, 0 );
	zeroise( masterSecretBuffer, CRYPT_MAX_KEYSIZE );
	if( cryptStatusError( status ) )
		{
		zeroise( keyBuffer, CRYPT_MAX_KEYSIZE );
		return( status );
		}

	/* Load the derived key into the context */
	setMessageData( &msgData, keyBuffer, keySize );
	status = krnlSendMessage( mechanismInfo->keyContext, 
							  IMESSAGE_SETATTRIBUTE_S, &msgData,
							  CRYPT_CTXINFO_KEY );
	zeroise( keyBuffer, CRYPT_MAX_KEYSIZE );

	return( status );
	}

/****************************************************************************
*																			*
*							PKCS #12 Key Derivation 						*
*																			*
****************************************************************************/

#ifdef USE_PKCS12

/* The nominal block size for PKCS #12 derivation, based on the MD5/SHA-1 
   input size of 512 bits */

#define P12_BLOCKSIZE		64

/* The maximum size of the expanded diversifier, salt, and password (DSP),
   one block for the expanded diversifier, one block for the expanded salt,
   and up to three blocks for the password converted to Unicode with a
   terminating \x00 appended, which for a maximum password length of
   CRYPT_MAX_TEXTSIZE can be ( 64 + 1 ) * 2, rounded up to a multiple of
   P12_BLOCKSIZE */

#define P12_DSPSIZE			( P12_BLOCKSIZE + P12_BLOCKSIZE + \
							  ( P12_BLOCKSIZE * 3 ) )

/* Add two P12_BLOCKSIZE-byte blocks as 64-byte big-endian values:

	dest = (dest + src + 1) mod 2^P12_BLOCKSIZE */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int add64( INOUT_BUFFER_FIXED( destLen ) BYTE *dest,
				  IN_LENGTH_FIXED( P12_BLOCKSIZE ) const int destLen,
				  IN_BUFFER( srcLen ) const BYTE *src,
				  IN_LENGTH_FIXED( P12_BLOCKSIZE ) const int srcLen )
	{
	LOOP_INDEX destIndex, srcIndex;
	int carry = 1;

	assert( isWritePtrDynamic( dest, destLen ) );
	assert( isReadPtrDynamic( src, srcLen ) );

	REQUIRES( destLen == P12_BLOCKSIZE );
	REQUIRES( srcLen == P12_BLOCKSIZE );

	/* dest = (dest + src + 1) mod 2^P12_BLOCKSIZE */
	LOOP_EXT_REV( ( destIndex = P12_BLOCKSIZE - 1, 
					srcIndex = P12_BLOCKSIZE - 1 ),
				  destIndex >= 0, ( destIndex--, srcIndex-- ), 
				  P12_BLOCKSIZE + 1 )
		{
		int value;

		ENSURES( LOOP_INVARIANT_REV( srcIndex, 0, P12_BLOCKSIZE - 1 ) );
		ENSURES( LOOP_INVARIANT_SECONDARY( destIndex, 0, 
										   P12_BLOCKSIZE - 1 ) );
		
		value = dest[ destIndex ] + src[ srcIndex ] + carry;
		dest[ destIndex ] = intToByte( value );
		carry = value >> 8;
		}
	ENSURES( LOOP_BOUND_EXT_REV_OK( P12_BLOCKSIZE + 1 ) );

	return( CRYPT_OK );
	}

/* Concatenate enough copies of the input data together to fill an output 
   buffer */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int expandData( OUT_BUFFER_FIXED( destLen ) BYTE *dest, 
					   IN_LENGTH_SHORT const int destLen, 
					   IN_BUFFER( srcLen ) const BYTE *src, 
					   IN_LENGTH_SHORT const int srcLen )
	{
	LOOP_INDEX index;

	assert( isWritePtrDynamic( dest, destLen ) );
	assert( isReadPtrDynamic( src, srcLen ) );

	REQUIRES( isShortIntegerRangeNZ( destLen ) );
	REQUIRES( isShortIntegerRangeNZ( srcLen ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( destLen ) ); 
	memset( dest, 0, min( 16, destLen ) );

	LOOP_MED_INITCHECK( index = 0, index < destLen )
		{
		const int bytesToCopy = min( srcLen, destLen - index );

		ENSURES( LOOP_INVARIANT_MED_XXX( index, 0, destLen - 1 ) );

		REQUIRES( !checkOverflowSub( destLen, index ) );

		REQUIRES( boundsCheckZ( index, bytesToCopy, destLen ) );
		memcpy( dest, src, bytesToCopy );
		dest += bytesToCopy;
		REQUIRES( !checkOverflowAdd( index, bytesToCopy ) );
		index += bytesToCopy;
		}
	ENSURES( LOOP_BOUND_OK );

	return( CRYPT_OK );
	}

/* Build the diversifier/salt/password (DSP) string to use as the initial 
   input to the hash function:

	<---- 64 bytes ----><------- 64 bytes -------><-- Mult.64 bytes ->
	[ ID | ID | ID ... ][ salt | salt | salt ... ][ pw | pw | pw ... ] */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4, 6 ) ) \
static int initDSP( OUT_BUFFER( dspMaxLen, *dspLen ) BYTE *dsp,
					IN_RANGE( P12_DSPSIZE, 512 ) const int dspMaxLen,
					OUT_RANGE( 0, 512 ) int *dspLen,
					IN_BUFFER( keyLength ) const BYTE *key,
					IN_LENGTH_TEXT const int keyLength,
					IN_BUFFER( saltLength ) const BYTE *salt,
					IN_RANGE( 1, P12_BLOCKSIZE ) const int saltLength,
					IN_RANGE( 1, 3 ) const int diversifier )
	{
	BYTE bmpString[ ( ( CRYPT_MAX_TEXTSIZE + 1 ) * 2 ) + 8 ];
	BYTE *dspPtr = dsp;
	LOOP_INDEX i, keyIndex, bmpIndex;
	int status;

	assert( isWritePtrDynamic( dsp, dspMaxLen ) );
	assert( isWritePtr( dspLen, sizeof( int ) ) );
	assert( isReadPtrDynamic( key, keyLength ) );
	assert( isReadPtrDynamic( salt, saltLength ) );

	REQUIRES( dspMaxLen >= P12_DSPSIZE && dspMaxLen <= 512 );
	REQUIRES( diversifier >= 1 && diversifier <= 3 );
	REQUIRES( saltLength >= 1 && saltLength <= P12_BLOCKSIZE );
	REQUIRES( keyLength >= MIN_NAME_LENGTH && \
			  keyLength <= CRYPT_MAX_TEXTSIZE );

	/* Clear return values */
	REQUIRES( isShortIntegerRangeNZ( dspMaxLen ) ); 
	memset( dsp, 0, min( 16, dspMaxLen ) );
	*dspLen = 0;

	/* Set up the diversifier in the first P12_BLOCKSIZE bytes */
	LOOP_EXT( i = 0, i < P12_BLOCKSIZE, i++, P12_BLOCKSIZE + 1 )
		{
		ENSURES( LOOP_INVARIANT_EXT( i, 0, P12_BLOCKSIZE - 1,
									 P12_BLOCKSIZE + 1 ) );

		dsp[ i ] = intToByte( diversifier );
		}
	ENSURES( LOOP_BOUND_OK );
	dspPtr += P12_BLOCKSIZE;

	/* Set up the salt in the next P12_BLOCKSIZE bytes */
	status = expandData( dspPtr, P12_BLOCKSIZE, salt, saltLength );
	if( cryptStatusError( status ) )
		return( status );
	dspPtr += P12_BLOCKSIZE;

	/* Convert the password to a null-terminated Unicode string, a Microsoft
	   bug that was made part of the standard */
	LOOP_EXT( ( keyIndex = 0, bmpIndex = 0 ), 
			  keyIndex < keyLength, 
			  ( keyIndex++, bmpIndex += 2 ), CRYPT_MAX_TEXTSIZE + 1 )
		{
		ENSURES( LOOP_INVARIANT_EXT( keyIndex, 0, keyLength - 1,
									 CRYPT_MAX_TEXTSIZE + 1 ) );
		ENSURES( LOOP_INVARIANT_SECONDARY( bmpIndex, 0, 
										   ( keyLength * 2 ) - 2 ) );

		bmpString[ bmpIndex ] = '\0';
		bmpString[ bmpIndex + 1 ] = key[ keyIndex ];
		}
	ENSURES( LOOP_BOUND_OK );
	bmpString[ bmpIndex++ ] = '\0';
	bmpString[ bmpIndex++ ] = '\0';
	ENSURES( dspMaxLen >= P12_BLOCKSIZE + P12_BLOCKSIZE + \
						  roundUp( bmpIndex, P12_BLOCKSIZE ) );

	/* Set up the Unicode password string as the remaining bytes */
	status = expandData( dspPtr, roundUp( bmpIndex, P12_BLOCKSIZE ),
						 bmpString, bmpIndex );
	zeroise( bmpString, ( CRYPT_MAX_TEXTSIZE + 1 ) * 2 );
	if( cryptStatusError( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( dspMaxLen ) ); 
		zeroise( dsp, dspMaxLen );
		return( status );
		}
	*dspLen = P12_BLOCKSIZE + P12_BLOCKSIZE + \
			  roundUp( bmpIndex, P12_BLOCKSIZE );
	
	return( CRYPT_OK );
	}

/* Perform PKCS #12 derivation */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int derivePKCS12( STDC_UNUSED void *dummy, 
				  INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	BYTE p12_DSP[ P12_DSPSIZE + 8 ];
	BYTE p12_Ai[ CRYPT_MAX_HASHSIZE + 8 ], p12_B[ P12_BLOCKSIZE + 8 ];
	BYTE *dataOutPtr = mechanismInfo->dataOut;
	const BYTE *saltPtr = mechanismInfo->salt;
	LOOP_INDEX keyIndex;
	int dspLen, hashSize, status;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	getHashAtomicParameters( mechanismInfo->hashAlgo, 0, &hashFunctionAtomic, 
							 &hashSize );

	/* Set up the diversifier/salt/password (DSP) string.  The first byte of 
	   the PKCS #12 salt acts as a diversifier so we separate this out from 
	   the rest of the salt data */
	status = initDSP( p12_DSP, P12_DSPSIZE, &dspLen, mechanismInfo->dataIn,
					  mechanismInfo->dataInLength, saltPtr + 1,
					  mechanismInfo->saltLength - 1, byteToInt( *saltPtr ) );
	if( cryptStatusError( status ) )
		return( status );

	/* Produce enough blocks of output to fill the key */
	LOOP_MED( keyIndex = 0, 
			  keyIndex < mechanismInfo->dataOutLength, 
			  keyIndex += hashSize )
		{
		const int noKeyBytes = \
			( mechanismInfo->dataOutLength - keyIndex > hashSize ) ? \
			hashSize : mechanismInfo->dataOutLength - keyIndex;
		LOOP_INDEX_ALT i, dspIndex;

		ENSURES( LOOP_INVARIANT_MED_XXX( keyIndex, 0, 
										 mechanismInfo->dataOutLength - 1 ) );

		REQUIRES( !checkOverflowSub( mechanismInfo->dataOutLength,
									 keyIndex ) );

		/* Hash the keying material the required number of times to obtain the
		   output value */
		hashFunctionAtomic( p12_Ai, CRYPT_MAX_HASHSIZE, p12_DSP, dspLen );
		LOOP_MAX_ALT( i = 1, i < mechanismInfo->iterations, i++ )
			{
			ENSURES( LOOP_INVARIANT_MAX_ALT( i, 1, 
											 mechanismInfo->iterations - 1 ) );

			hashFunctionAtomic( p12_Ai, CRYPT_MAX_HASHSIZE, 
								p12_Ai, hashSize );
			}
		ENSURES( LOOP_BOUND_OK_ALT );
		REQUIRES( boundsCheckZ( keyIndex, noKeyBytes, 
							    mechanismInfo->dataOutLength ) );
		memcpy( dataOutPtr + keyIndex, p12_Ai, noKeyBytes );

		/* Update the input keying material for the next iteration by 
		   adding the output value expanded to P12_BLOCKSIZE, to 
		   P12_BLOCKSIZE-sized blocks of the salt/password portion of the 
		   DSP string */
		status = expandData( p12_B, P12_BLOCKSIZE, p12_Ai, hashSize );
		if( cryptStatusError( status ) )
			break;
		LOOP_LARGE_ALT( dspIndex = P12_BLOCKSIZE, 
						dspIndex < dspLen, 
						dspIndex += P12_BLOCKSIZE )
			{
			ENSURES( LOOP_INVARIANT_LARGE_XXX_ALT( dspIndex, P12_BLOCKSIZE, 
												   dspLen - 1 ) );

			status = add64( p12_DSP + dspIndex, P12_BLOCKSIZE,
							p12_B, P12_BLOCKSIZE );
			if( cryptStatusError( status ) )
				break;
			}
		ENSURES( LOOP_BOUND_OK_ALT );
		}
	ENSURES( LOOP_BOUND_OK );
	zeroise( p12_DSP, P12_DSPSIZE );
	zeroise( p12_Ai, CRYPT_MAX_HASHSIZE );
	zeroise( p12_B, P12_BLOCKSIZE );
	if( cryptStatusError( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
		zeroise( mechanismInfo->dataOut, mechanismInfo->dataOutLength );
		return( status );
		}

	return( CRYPT_OK );
	}
#endif /* USE_PKCS12 */

/****************************************************************************
*																			*
*								TLS Key Derivation 							*
*																			*
****************************************************************************/

#ifdef USE_TLS

/* Structure used to store TLS PRF state information */

typedef struct {
	/* The hash functions and hash info */
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	HASH_FUNCTION hashFunction;
	int hashSize, hashBlockSize;

	/* The initial hash state from prfInit() and the current hash state */
	HASHINFO initialHashInfo, hashInfo;

	/* The HMAC processed key and intermediate data value */
	BUFFER( HMAC_MAX_DATASIZE, processedKeyLength ) \
	BYTE processedKey[ HMAC_MAX_DATASIZE + 8 ];
	BUFFER( CRYPT_MAX_HASHSIZE, hashSize ) \
	BYTE hashA[ CRYPT_MAX_HASHSIZE + 8 ];
	int processedKeyLength;
	} TLS_PRF_INFO;

/* Initialise the TLS PRF */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
static int tlsPrfInit( INOUT_PTR TLS_PRF_INFO *prfInfo, 
					   IN_BUFFER( keyLength ) const void *key, 
					   IN_LENGTH_SHORT const int keyLength,
					   IN_BUFFER( saltLength ) const void *salt, 
					   IN_LENGTH_SHORT const int saltLength )
	{
	int status;

	assert( isWritePtr( prfInfo, sizeof( TLS_PRF_INFO ) ) );
	assert( isReadPtrDynamic( key, keyLength ) );
	assert( isReadPtrDynamic( salt, saltLength ) );

	REQUIRES( isShortIntegerRangeNZ( keyLength ) );
	REQUIRES( isShortIntegerRangeNZ( saltLength ) );

	/* Initialise the hash information with the keying info.  This is
	   reused for any future hashing since it's constant */
	status = prfInit( prfInfo->hashFunction, prfInfo->hashFunctionAtomic, 
					  prfInfo->initialHashInfo, prfInfo->hashSize, 
					  prfInfo->hashBlockSize, prfInfo->processedKey, 
					  HMAC_MAX_DATASIZE, &prfInfo->processedKeyLength, 
					  key, keyLength );
	if( cryptStatusError( status ) )
		return( status );

	/* Calculate A1 = HMAC( salt ) */
	memcpy( prfInfo->hashInfo, prfInfo->initialHashInfo, sizeof( HASHINFO ) );
	prfInfo->hashFunction( prfInfo->hashInfo, NULL, 0, salt, saltLength, 
						  HASH_STATE_CONTINUE );
	return( prfEnd( prfInfo->hashFunction, prfInfo->hashInfo, 
					prfInfo->hashSize, prfInfo->hashBlockSize, 
					prfInfo->hashA, CRYPT_MAX_HASHSIZE, 
					prfInfo->processedKey, prfInfo->processedKeyLength ) );
	}

/* Implement one round of the TLS PRF */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
static int tlsPrfHash( INOUT_BUFFER_FIXED( outLength ) BYTE *out, 
					   IN_LENGTH_SHORT const int outLength, 
					   INOUT_PTR TLS_PRF_INFO *prfInfo, 
					   IN_BUFFER( saltLength ) const void *salt, 
					   IN_LENGTH_SHORT const int saltLength )
	{
	HASHINFO hashInfo, AnHashInfo;
	BYTE hash[ CRYPT_MAX_HASHSIZE + 8 ];
	LOOP_INDEX i;
	int status;

	assert( isWritePtrDynamic( out, outLength ) );
	assert( isWritePtr( prfInfo, sizeof( TLS_PRF_INFO ) ) );
	assert( isReadPtrDynamic( salt, saltLength ) );

	REQUIRES( outLength > 0 && outLength <= prfInfo->hashSize && \
			  outLength <= CRYPT_MAX_HASHSIZE );
	REQUIRES( saltLength >= 13 && saltLength <= 512 );

	/* The result of the hashing is XORd to the output so we don't clear the 
	   return value like usual */

	/* Calculate HMAC( An || salt ) */
	memcpy( hashInfo, prfInfo->initialHashInfo, sizeof( HASHINFO ) );
	prfInfo->hashFunction( hashInfo, NULL, 0, prfInfo->hashA, 
						   prfInfo->hashSize, HASH_STATE_CONTINUE );
	memcpy( AnHashInfo, hashInfo, sizeof( HASHINFO ) );
	prfInfo->hashFunction( hashInfo, NULL, 0, salt, saltLength, 
						   HASH_STATE_CONTINUE );
	status = prfEnd( prfInfo->hashFunction, hashInfo, prfInfo->hashSize, 
					 prfInfo->hashBlockSize, hash, CRYPT_MAX_HASHSIZE, 
					 prfInfo->processedKey, prfInfo->processedKeyLength );
	if( cryptStatusError( status ) )
		{
		zeroise( AnHashInfo, sizeof( HASHINFO ) );
		zeroise( hashInfo, sizeof( HASHINFO ) );
		zeroise( hash, CRYPT_MAX_HASHSIZE );
		return( status );
		}

	/* Calculate An+1 = HMAC( An ) */
	memcpy( hashInfo, AnHashInfo, sizeof( HASHINFO ) );
	status = prfEnd( prfInfo->hashFunction, hashInfo, prfInfo->hashSize, 
					 prfInfo->hashBlockSize, prfInfo->hashA, 
					 prfInfo->hashSize, prfInfo->processedKey, 
					 prfInfo->processedKeyLength );
	if( cryptStatusError( status ) )
		{
		zeroise( AnHashInfo, sizeof( HASHINFO ) );
		zeroise( hashInfo, sizeof( HASHINFO ) );
		zeroise( hash, CRYPT_MAX_HASHSIZE );
		return( status );
		}

	/* Copy the result to the output */
	LOOP_EXT( i = 0, i < outLength, i++, CRYPT_MAX_HASHSIZE + 1 )
		{
		ENSURES( LOOP_INVARIANT_EXT( i, 0, outLength - 1,
									 CRYPT_MAX_HASHSIZE + 1 ) );

		out[ i ] ^= hash[ i ];
		}
	ENSURES( LOOP_BOUND_OK );

	zeroise( AnHashInfo, sizeof( HASHINFO ) );
	zeroise( hashInfo, sizeof( HASHINFO ) );
	zeroise( hash, CRYPT_MAX_HASHSIZE );

	return( CRYPT_OK );
	}

/* Perform TLS 1.0 - 1.1 key derivation.  This implements the function 
   described as 'PRF()' in the TLS spec */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int deriveTLS( STDC_UNUSED void *dummy, 
			   INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	TLS_PRF_INFO md5Info, shaInfo;
	BYTE *dataOutPtr = mechanismInfo->dataOut;
	const void *s1, *s2;
	const int dataOutLength = mechanismInfo->dataOutLength;
	const int sLen = ( mechanismInfo->dataInLength + 1 ) / 2;
	LOOP_INDEX md5Index;
	int shaIndex, status;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	memset( &md5Info, 0, sizeof( TLS_PRF_INFO ) );
	getHashAtomicParameters( CRYPT_ALGO_MD5, 0, &md5Info.hashFunctionAtomic, 
							 &md5Info.hashSize );
	getHashParameters( CRYPT_ALGO_MD5, 0, &md5Info.hashFunction, NULL, 
					   &md5Info.hashBlockSize );
	memset( &shaInfo, 0, sizeof( TLS_PRF_INFO ) );
	getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &shaInfo.hashFunctionAtomic, 
							 &shaInfo.hashSize );
	getHashParameters( CRYPT_ALGO_SHA1, 0, &shaInfo.hashFunction, NULL, 
					   &shaInfo.hashBlockSize );

	/* Find the start of the two halves of the keying info used for the
	   HMACing.  The size of each half is given by ceil( dataInLength / 2 ) 
	   so there's a one-byte overlap if the input is an odd number of bytes 
	   long */
	REQUIRES( !checkOverflowSub( mechanismInfo->dataInLength, sLen ) );
	s1 = mechanismInfo->dataIn;
	s2 = ( BYTE * ) mechanismInfo->dataIn + \
		 ( mechanismInfo->dataInLength - sLen );

	/* The two hash functions have different block sizes that would require
	   complex buffering to handle leftover bytes from SHA-1, a simpler
	   method is to zero the output data block and XOR in the values from
	   each hash mechanism using separate output location indices for MD5 and
	   SHA-1 */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	/* Initialise the TLS PRF and calculate A1 = HMAC( salt ) */
	status = tlsPrfInit( &md5Info, s1, sLen, mechanismInfo->salt,
						 mechanismInfo->saltLength );
	if( cryptStatusOK( status ) )
		{
		status = tlsPrfInit( &shaInfo, s2, sLen, mechanismInfo->salt,
							 mechanismInfo->saltLength );
		}
	if( cryptStatusError( status ) )
		{
		zeroise( &md5Info, sizeof( TLS_PRF_INFO ) );
		zeroise( &shaInfo, sizeof( TLS_PRF_INFO ) );
		return( status );
		}

	/* Produce enough blocks of output to fill the key.  We use the MD5 hash
	   size as the loop increment since this produces the smaller output
	   block */
	LOOP_MED_INITCHECK( md5Index = shaIndex = 0, md5Index < dataOutLength )
		{
		const int md5NoKeyBytes = min( dataOutLength - md5Index, \
									   md5Info.hashSize );
		const int shaNoKeyBytes = min( dataOutLength - shaIndex, \
									   shaInfo.hashSize );

		ENSURES( LOOP_INVARIANT_MED_XXX( md5Index, 0, dataOutLength - 1 ) );
				 /* md5Index is incremented by the number of output bytes */
		ENSURES( LOOP_INVARIANT_SECONDARY( shaIndex, 0, dataOutLength ) );
				 /* SHA-1 produces more output than MD5 so it can get to
 				    dataOutLength while MD5 is still producing output */

		REQUIRES( !checkOverflowSub( dataOutLength, md5Index ) );
		REQUIRES( !checkOverflowSub( dataOutLength, shaIndex ) );

		status = tlsPrfHash( dataOutPtr + md5Index, md5NoKeyBytes, 
							 &md5Info, mechanismInfo->salt, 
							 mechanismInfo->saltLength );
		if( cryptStatusError( status ) )
			break;
		REQUIRES( !checkOverflowAdd( md5Index, md5NoKeyBytes ) );
		md5Index += md5NoKeyBytes;
		if( shaNoKeyBytes > 0 )
			{
			/* Since the SHA-1 counter advances faster than the MD5 one we
			   can end up with zero bytes left to process for SHA-1 when MD5 
			   is processing its last block */
			status = tlsPrfHash( dataOutPtr + shaIndex, shaNoKeyBytes, 
								 &shaInfo, mechanismInfo->salt, 
								 mechanismInfo->saltLength );
			if( cryptStatusError( status ) )
				break;
			REQUIRES( !checkOverflowAdd( shaIndex, shaNoKeyBytes ) );
			shaIndex += shaNoKeyBytes;
			}
		}
	ENSURES( LOOP_BOUND_OK );
	zeroise( &md5Info, sizeof( TLS_PRF_INFO ) );
	zeroise( &shaInfo, sizeof( TLS_PRF_INFO ) );
	if( cryptStatusError( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
		zeroise( mechanismInfo->dataOut, mechanismInfo->dataOutLength );
		return( status );
		}

	return( CRYPT_OK );
	}

/* Perform TLS 1.2 key derivation using a gratuitously incompatible PRF 
   invented for TLS 1.2 */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int deriveTLS12( STDC_UNUSED void *dummy, 
				 INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	TLS_PRF_INFO shaInfo;
	BYTE *dataOutPtr = mechanismInfo->dataOut;
	const int dataOutLength = mechanismInfo->dataOutLength;
	LOOP_INDEX keyIndex;
	int status;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	memset( &shaInfo, 0, sizeof( TLS_PRF_INFO ) );
	getHashAtomicParameters( mechanismInfo->hashAlgo, 
							 mechanismInfo->hashParam,
							 &shaInfo.hashFunctionAtomic, 
							 &shaInfo.hashSize );
	getHashParameters( mechanismInfo->hashAlgo, mechanismInfo->hashParam,
					   &shaInfo.hashFunction, NULL, &shaInfo.hashBlockSize );

	/* Initialise the TLS PRF and calculate A1 = HMAC( salt ) */
	status = tlsPrfInit( &shaInfo, mechanismInfo->dataIn, 
						 mechanismInfo->dataInLength, mechanismInfo->salt,
						 mechanismInfo->saltLength );
	if( cryptStatusError( status ) )
		{
		zeroise( &shaInfo, sizeof( TLS_PRF_INFO ) );
		return( status );
		}

	/* Produce enough blocks of output to fill the key */
	LOOP_MED( keyIndex = 0, 
			  keyIndex < dataOutLength, 
			  keyIndex += shaInfo.hashSize )
		{
		const int noKeyBytes = min( dataOutLength - keyIndex, \
									shaInfo.hashSize );

		ENSURES( LOOP_INVARIANT_MED_XXX( keyIndex, 0, dataOutLength - 1 ) );
				 /* keyIndex is incremented by the number of output bytes */

		REQUIRES( !checkOverflowSub( dataOutLength, keyIndex ) );

		status = tlsPrfHash( dataOutPtr + keyIndex, noKeyBytes, &shaInfo, 
							 mechanismInfo->salt, mechanismInfo->saltLength );
		if( cryptStatusError( status ) )
			break;
		}
	ENSURES( LOOP_BOUND_OK );
	zeroise( &shaInfo, sizeof( TLS_PRF_INFO ) );
	if( cryptStatusError( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
		zeroise( mechanismInfo->dataOut, mechanismInfo->dataOutLength );
		return( status );
		}

	return( CRYPT_OK );
	}
#endif /* USE_TLS */

/****************************************************************************
*																			*
*							PGP Key Derivation 								*
*																			*
****************************************************************************/

#if defined( USE_PGP ) || defined( USE_PGPKEYS )

/* Implement one round of the OpenPGP PRF */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4, 6, 8, 10 ) ) \
static int pgpPrfHash( OUT_BUFFER_FIXED( outLength ) BYTE *out, 
					   IN_LENGTH_HASH const int outLength, 
					   IN_PTR const HASH_FUNCTION hashFunction, 
					   INOUT_PTR HASHINFO hashInfo,
					   IN_LENGTH_HASH const int hashSize, 
					   IN_BUFFER( keyLength ) const void *key, 
					   IN_LENGTH_SHORT_MIN( 2 ) const int keyLength,
					   IN_BUFFER( saltLength ) const void *salt, 
					   IN_LENGTH_FIXED( PGP_SALTSIZE ) const int saltLength,
					   INOUT_LENGTH_Z int *byteCount, 
					   IN_RANGE( CRYPT_UNUSED, 1 ) const int preloadLength )
	{
	int count = *byteCount;

	assert( isWritePtrDynamic( out, outLength ) );
	assert( isWritePtr( hashInfo, sizeof( HASHINFO ) ) );
	assert( isReadPtrDynamic( key, keyLength ) );
	assert( isReadPtrDynamic( salt, saltLength ) );
	assert( isWritePtr( byteCount, sizeof( int ) ) );

	REQUIRES( hashFunction != NULL );
	REQUIRES( outLength == hashSize );
	REQUIRES( hashSize >= MIN_HASHSIZE && hashSize <= CRYPT_MAX_HASHSIZE );
	REQUIRES( isShortIntegerRangeMin( keyLength, 2 ) );
	REQUIRES( saltLength == PGP_SALTSIZE );
	REQUIRES( preloadLength == CRYPT_UNUSED || \
			  ( preloadLength >= 0 && preloadLength <= 1 ) );
	REQUIRES( isIntegerRangeNZ( count ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( outLength ) ); 
	memset( out, 0, outLength );

	/* If it's a subsequent round of hashing, preload the hash with zero 
	   bytes.  If it's the first round (preloadLength == 0) it's handled
	   specially below */
	if( preloadLength > 0 )
		{
		hashFunction( hashInfo, NULL, 0, 
					  ( const BYTE * ) "\x00\x00\x00\x00", preloadLength, 
					  HASH_STATE_START );
		}

	/* Hash the next round of salt || password.  Since we're being asked to 
	   stop once we've processed 'count' input bytes we implement an early-
	   out mechanism that exits if the length of the item being hashed is 
	   sufficient to reach 'count' */
	if( count <= saltLength )
		{
		hashFunction( hashInfo, out, outLength, salt, count, 
					  HASH_STATE_END );
		*byteCount = 0;
		
		return( CRYPT_OK );
		}
	hashFunction( hashInfo, NULL, 0, salt, saltLength, 
				  ( preloadLength == 0 ) ? HASH_STATE_START : \
										   HASH_STATE_CONTINUE );
	REQUIRES( !checkOverflowSub( count, saltLength ) );
	count -= saltLength;
	if( count <= keyLength )
		{
		hashFunction( hashInfo, out, outLength, key, count, HASH_STATE_END );
		*byteCount = 0;

		return( CRYPT_OK );
		}
	hashFunction( hashInfo, NULL, 0, key, keyLength, HASH_STATE_CONTINUE );
	REQUIRES( !checkOverflowSub( count, keyLength ) );
	count -= keyLength;
	ENSURES( isIntegerRangeNZ( count ) );

	*byteCount = count;
	return( CRYPT_OK );
	}

/* Perform OpenPGP S2K key derivation */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int derivePGP( STDC_UNUSED void *dummy, 
			   INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	HASH_FUNCTION hashFunction;
	HASHINFO hashInfo;
	int byteCount = mechanismInfo->iterations << 6;
	int secondByteCount = 0, hashSize, status = CRYPT_OK;
	LOOP_INDEX i;

	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* The checks below are a bit complex, see the long comment in 
	   misc/consts.h for the bizarro way that the S2K processing-complexity
	   specifier works.  In brief the count, once shifted/multiplied by 64,
	   is capped at 65,011,712 so we don't have to worry about an overflow 
	   in the shift */
	REQUIRES( mechanismInfo->iterations >= 0 && \
			  mechanismInfo->iterations <= MAX_KEYSETUP_HASHSPECIFIER );
	REQUIRES( isBufsizeRange( byteCount ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	/* Set up the hash parameters */
	getHashParameters( mechanismInfo->hashAlgo, mechanismInfo->hashParam, 
					   &hashFunction, &hashSize, NULL );
	memset( hashInfo, 0, sizeof( HASHINFO ) );

	REQUIRES( mechanismInfo->dataOutLength < 2 * hashSize );

	/* If it's a non-iterated hash or the count won't allow even a single
	   pass over the 8-byte salt and password, adjust it to make sure that 
	   we run at least one full iteration */
	if( byteCount < PGP_SALTSIZE + mechanismInfo->dataInLength )
		byteCount = PGP_SALTSIZE + mechanismInfo->dataInLength;

	/* If the hash output size is less than the required key size we run a 
	   second round of hashing after the first one to provide the total 
	   required amount of keying material */
	if( hashSize < mechanismInfo->dataOutLength )
		secondByteCount = byteCount;

	/* Repeatedly hash the salt and password until we've met the byte count.  
	   In effect this hashes:

		salt || password || salt || password || ...

	   until we've processed 'byteCount' bytes of data.

	   This processing is complicated by the ridiculous number of iterations
	   of processing specified by some versions of GPG (see the long comment
	   in misc/consts.h) so that we can no longer employ the standard
	   FAILSAFE_ITERATIONS_MAX as a failsafe value but have to use an 
	   estimate based on the largest possible hash specifier.  The 
	   calculation is based on the fact that MAX_KEYSETUP_HASHSPECIFIER is 
	   the maximum count divided by 64 (shifted 6 bits) to both get it away 
	   from any possible overflow range and past the kernel ACLs, the 
	   GPG_FAILSAFE_ITERATIONS_MAX assumes a minimum 8-character password 
	   which, along with the 8-character salt consumes 16 bytes per 
	   iteration */
	#define GPG_FAILSAFE_ITERATIONS_MAX		( MAX_KEYSETUP_HASHSPECIFIER * ( 64 / 16 ) )
	   
	/* The worst-case situation is when we have a 2-character password which,
	   with the 8-byte PGP_SALTSIZE consumes 10 bytes per iteration to reach
	   the hash specifier count which worst-case is 65,011,712 (see again the 
	   long comment in misc/consts.h).  This means that in theory a 
	   combination of a silly-length password and silly-length hash count can 
	   trigger the loop failsafe.  To deal with this in a slightly cleaner 
	   manner we check for an overflow and report it as such, alongside an 
	   alert in debug mode since this is another should-never-occur 
	   situation */
	REQUIRES( !checkOverflowDiv( byteCount, mechanismInfo->dataInLength + \
											mechanismInfo->saltLength ) );
	if( byteCount / ( mechanismInfo->dataInLength + \
					  mechanismInfo->saltLength ) > GPG_FAILSAFE_ITERATIONS_MAX )
		{
		assert( DEBUG_WARN );
		return( CRYPT_ERROR_OVERFLOW );
		}
	LOOP_EXT( i = 0, byteCount > 0 && cryptStatusOK( status ), i++, 
			  GPG_FAILSAFE_ITERATIONS_MAX + 1 )
		{
		/* There's not really a proper LOOP_INVARIANT possible for this 
		   case, i is really just a boolean value used to indicate the first 
		   iteration and byteCount is supplied by pgpPrfHash() and again is 
		   really just a boolean value indicating when we can stop */
		ENSURES( LOOP_INVARIANT_EXT_GENERIC( GPG_FAILSAFE_ITERATIONS_MAX + 1 ) );

		status = pgpPrfHash( mechanismInfo->dataOut, 
							 hashSize, hashFunction, hashInfo, hashSize, 
							 mechanismInfo->dataIn,
							 mechanismInfo->dataInLength,
							 mechanismInfo->salt, 
							 mechanismInfo->saltLength, &byteCount, 
							 ( i <= 0 ) ? 0 : CRYPT_UNUSED );
		}
	ENSURES( LOOP_BOUND_OK );
	if( cryptStatusOK( status ) && secondByteCount > 0 )
		{
		LOOP_EXT( i = 0, secondByteCount > 0 && cryptStatusOK( status ), i++,
				  GPG_FAILSAFE_ITERATIONS_MAX + 1 )
			{
			/* As before there's not really a proper LOOP_INVARIANT possible 
			   for this case */
			ENSURES( LOOP_INVARIANT_EXT_GENERIC( GPG_FAILSAFE_ITERATIONS_MAX + 1 ) );

			status = pgpPrfHash( ( BYTE * ) mechanismInfo->dataOut + hashSize, 
								 hashSize, hashFunction, hashInfo, hashSize, 
								 mechanismInfo->dataIn, 
								 mechanismInfo->dataInLength,
								 mechanismInfo->salt, 
								 mechanismInfo->saltLength, &secondByteCount, 
								 ( i <= 0 ) ? 1 : CRYPT_UNUSED );
			}
		ENSURES( LOOP_BOUND_OK );
		}
	zeroise( hashInfo, sizeof( HASHINFO ) );
	if( cryptStatusError( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
		zeroise( mechanismInfo->dataOut, mechanismInfo->dataOutLength );
		return( status );
		}

	return( CRYPT_OK );
	}
#endif /* USE_PGP || USE_PGPKEYS */

/****************************************************************************
*																			*
*								Misc Key Derivation 						*
*																			*
****************************************************************************/

#ifdef USE_CMP

/* Perform CMP/Entrust key derivation */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int deriveCMP( STDC_UNUSED void *dummy, 
			   INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	HASH_FUNCTION hashFunction;
	HASHINFO hashInfo;
	LOOP_INDEX iterations;
	int hashSize;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	/* Calculate SHA1( password || salt ) */
	getHashAtomicParameters( mechanismInfo->hashAlgo, 
							 mechanismInfo->hashParam, &hashFunctionAtomic, 
							 &hashSize );
	getHashParameters( mechanismInfo->hashAlgo, mechanismInfo->hashParam, 
					   &hashFunction, NULL, NULL );
	REQUIRES( isShortIntegerRangeMin( mechanismInfo->dataOutLength, 
									  hashSize ) );
	hashFunction( hashInfo, NULL, 0, mechanismInfo->dataIn,
				  mechanismInfo->dataInLength, HASH_STATE_START );
	hashFunction( hashInfo, mechanismInfo->dataOut, 
				  mechanismInfo->dataOutLength, mechanismInfo->salt,
				  mechanismInfo->saltLength, HASH_STATE_END );

	/* Iterate the hashing the remaining number of times.  We start the 
	   count at one since the first iteration has already been performed */
	LOOP_MAX( iterations = 1, 
			  iterations < mechanismInfo->iterations, 
			  iterations++ )
		{
		ENSURES( LOOP_INVARIANT_MAX( iterations, 1, 
									 mechanismInfo->iterations - 1 ) );

		hashFunctionAtomic( mechanismInfo->dataOut, 
							mechanismInfo->dataOutLength, 
							mechanismInfo->dataOut, hashSize );
		}
	ENSURES( LOOP_BOUND_OK );
	zeroise( hashInfo, sizeof( HASHINFO ) );

	return( CRYPT_OK );
	}
#endif /* USE_CMP */

#if defined( USE_TLS ) || defined( USE_SSH )

/* Perform HOTP_TOTP key derivation */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int deriveHOTP( STDC_UNUSED void *dummy, 
				INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	HASH_FUNCTION hashFunction;
	HASHINFO hashInfo;
	BYTE processedKey[ HMAC_MAX_DATASIZE + 8 ];
	BYTE hash[ CRYPT_MAX_HASHSIZE + 8 ];
	char totpString[ 16 + 8 ];
	int hashSize, hashBlockSize, processedKeyLength, index;
	int totpValue, totpStringLen, status;

	UNUSED_ARG_OPT( dummy );
	assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_DERIVE_INFO ) ) );

	/* Clear return value */
	REQUIRES( isShortIntegerRangeNZ( mechanismInfo->dataOutLength ) ); 
	memset( mechanismInfo->dataOut, 0, mechanismInfo->dataOutLength );

	/* HMAC the counter, present in the mechanism info as the salt, using the 
	   key */
	getHashAtomicParameters( mechanismInfo->hashAlgo, mechanismInfo->hashParam, 
							 &hashFunctionAtomic, &hashSize );
	getHashParameters( mechanismInfo->hashAlgo, mechanismInfo->hashParam, 
					   &hashFunction, NULL, &hashBlockSize );
	status = prfInit( hashFunction, hashFunctionAtomic, hashInfo, hashSize, 
					  hashBlockSize, processedKey, HMAC_MAX_DATASIZE, 
					  &processedKeyLength, mechanismInfo->dataIn, 
					  mechanismInfo->dataInLength );
	if( cryptStatusOK( status ) )
		{
		hashFunction( hashInfo, NULL, 0, mechanismInfo->salt, 
					  mechanismInfo->saltLength, HASH_STATE_CONTINUE );
		status = prfEnd( hashFunction, hashInfo, hashSize, hashBlockSize, 
						 hash, CRYPT_MAX_HASHSIZE, processedKey, 
						 processedKeyLength );
		}
	zeroise( processedKey, HMAC_MAX_DATASIZE );
	zeroise( hashInfo, sizeof( HASHINFO ) );
	if( cryptStatusError( status ) )
		{
		zeroise( hash, CRYPT_MAX_HASHSIZE );
		return( status );
		}

	/* Using the last 4 bits of the hash value as an index into the hash (a
	   Rube Goldberg step that achieves nothing apart from adding 
	   unnecessary complexity), extract the 31-bit value starting at that 
	   location */
	index = byteToInt( hash[ hashSize - 1 ] ) & 0x0F;
	ENSURES( rangeCheck( index, 0, hashSize - 4 ) );
	totpValue = ( byteToInt( hash[ index ] & 0x7F ) << 24 ) | \
				( byteToInt( hash[ index + 1 ] ) << 16 ) | \
				( byteToInt( hash[ index + 2 ] ) << 8 ) | \
					   hash[ index + 3 ];
	zeroise( hash, CRYPT_MAX_HASHSIZE );

	/* Truncate the value to six digits and return the result as a text 
	   string.  The RFC never states how results shorter than 6 digits are 
	   treated but Authy adds leading zeroes so we go with that */
	totpValue %= 1000000L;
	totpStringLen = sprintf_s( totpString, 16, "%06d", totpValue );
	ENSURES( rangeCheck( totpStringLen, 6, 16 - 1 ) );
	REQUIRES( isShortIntegerRangeMin( mechanismInfo->dataOutLength, 6 ) );
	memcpy( mechanismInfo->dataOut, totpString, 6 ); 
	zeroise( totpString, 16 );

	return( CRYPT_OK );
	}
#endif /* USE_TLS || USE_SSH */

/****************************************************************************
*																			*
*							Derivation Self-test Functions 					*
*																			*
****************************************************************************/

#ifndef CONFIG_NO_SELFTEST

/* Test key derivation mechanisms.  The externally-defined 
   MECHANISM_FUNCTION_INFO.function uses 'void *mechanismInfo' for the 
   second parameter because it could be one of several types while here we 
   use the specific 'MECHANISM_DERIVE_INFO *mechanismInfo', this leads to
   warnings from some compilers about assigning incompatible pointer types
   so we explicitly cast the function type via the FUNCTION_CAST below */

#define FUNCTION_CAST		const MECHANISM_FUNCTION

typedef struct {
	MECHANISM_FUNCTION_INFO mechanismFunctionInfo;
	MECHANISM_DERIVE_INFO mechanismInfo;
	} MECHANISM_TEST_INFO;

#define MECHANISM_OUTPUT_SIZE		32
#define MECHANISM_INPUT_SIZE		32
#define MECHANISM_SALT_SIZE			16

#define MECHANISM_OUTPUT_SIZE_TLS	48
#define MECHANISM_INPUT_SIZE_TLS	48
#define MECHANISM_SALT_SIZE_TLS		64

static const BYTE inputValue[] = {
	/* More than a single hash block size for SHA-1 */
	0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 
	0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
	0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 
	0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F,
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
	};
static const BYTE saltValue[] = {
	/* At least 64 bytes for TLS PRF */
	0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 
	0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
	0x78, 0x69, 0x5A, 0x4B, 0x3C, 0x2D, 0x1E, 0x0F, 
	0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
	0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 
	0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
	0x80, 0x91, 0xA2, 0xB3, 0xC4, 0xD5, 0xE6, 0xF7, 
	0x08, 0x19, 0x2A, 0x3B, 0x4C, 0x5D, 0x6E, 0x7F
	};
#ifdef USE_PKCS12
static const BYTE pkcs12saltValue[] = {
	/* PKCS #12 has a single-byte diversifier at the start of the salt */
	0x01,
	0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87, 
	0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
	};
#endif /* USE_PKCS12 */

static const MECHANISM_TEST_INFO deriveMechanismTestInfo[] = {
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PBKDF2, ( FUNCTION_CAST ) derivePBKDF2 },
	  { MKDATA( "\x73\xF7\x8A\xBE\x3C\x9C\x65\x80\x97\x60\x56\xDE\x04\x2A\x0C\x97"
				"\x99\xF5\x06\x0F\x43\x06\xA5\xD0\x74\xC9\xD5\xC5\xA5\x05\xB5\x7F" ), MECHANISM_OUTPUT_SIZE,
		inputValue, MECHANISM_INPUT_SIZE, CRYPT_ALGO_HMAC_SHA1, 0,
		saltValue, MECHANISM_SALT_SIZE, 10 } },
#if defined( USE_PGP ) || defined( USE_PGPKEYS )
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PGP, ( FUNCTION_CAST ) derivePGP },
	  { MKDATA( "\x4A\x4B\x90\x09\x27\xF8\xD0\x93\x56\x16\xEA\xC1\x45\xCD\xEE\x05"
				"\x67\xE1\x09\x38\x66\xEB\xB2\xB2\xB9\x1F\xD3\xF7\x48\x2B\xDC\xCA" ), MECHANISM_OUTPUT_SIZE,
		inputValue, MECHANISM_INPUT_SIZE, CRYPT_ALGO_SHA1, 0,
		saltValue, 8, 10 } },
#endif /* USE_PGP || USE_PGPKEYS */
#if defined( USE_TLS ) || defined( USE_SSH )
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "755224" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x00", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "287082" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x01", 8, 1 } },
  #if 0		/* Additional HOTP tests that don't add anything to the above tests */
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "359152" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x02", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "969429" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x03", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "338314" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x04", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "254676" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x05", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "287922" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x06", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "162583" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x00\x00\x00\x07", 8, 1 } },
  #endif /* 0 */
  #if 0		/* TOTP tests that just changed the counter value to a time-based value */
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "081804" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x02\x35\x23\xEC", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "050471" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x02\x35\x23\xED", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "005924" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x02\x73\xEF\x07", 8, 1 } },
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HOTP, ( FUNCTION_CAST ) deriveHOTP },
	  { MKDATA( "279037" ), 6,
		MKDATA( "12345678901234567890" ), 20, CRYPT_ALGO_SHA1, 0, 
		"\x00\x00\x00\x00\x03\xF9\x40\xAA", 8, 1 } },
  #endif /* 0 */
#endif /* USE_TLS || USE_SSH */
#ifdef USE_TLS
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_TLS, ( FUNCTION_CAST ) deriveTLS },
	  { MKDATA( "\xD3\xD4\x2F\xD6\xE3\x7D\xC0\x3C\xA6\x9F\x92\xDF\x3E\x40\x0A\x64"
				"\x49\xB4\x0E\xC4\x14\x04\x2F\xC8\xDD\x27\xD5\x1C\x62\xD2\x2C\x97"
				"\x90\xAE\x08\x4B\xEE\xF4\x8D\x22\xF0\x2A\x1E\x38\x2D\x31\xCB\x68" ), MECHANISM_OUTPUT_SIZE_TLS,
		inputValue, MECHANISM_INPUT_SIZE_TLS, ( CRYPT_ALGO_TYPE ) CRYPT_USE_DEFAULT, 0,
		saltValue, MECHANISM_SALT_SIZE_TLS, 1 } },				  /* Both MD5 and SHA1 */
#endif /* USE_TLS */
#ifdef USE_CMP
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_CMP, ( FUNCTION_CAST ) deriveCMP },
	  { MKDATA( "\x80\x0B\x95\x73\x74\x3B\xC1\x63\x6B\x28\x2B\x04\x47\xFD\xF0\x04"
				"\x80\x40\x31\xB1" ), 20,
		inputValue, MECHANISM_INPUT_SIZE, CRYPT_ALGO_SHA1, 0,
		saltValue, MECHANISM_SALT_SIZE, 10 } },
#endif /* USE_CMP */
#ifdef USE_PKCS12
  #if 0		/* Additional check value from OpenSSL, this only uses 1 iteration */
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PKCS12, ( FUNCTION_CAST ) derivePKCS12 },
	  { MKDATA( "\x8A\xAA\xE6\x29\x7B\x6C\xB0\x46\x42\xAB\x5B\x07\x78\x51\x28\x4E"
				"\xB7\x12\x8F\x1A\x2A\x7F\xBC\xA3" ), 24,
		MKDATA( "smeg" ), 4, CRYPT_ALGO_SHA1, 0,
		MKDATA( "\x01\x0A\x58\xCF\x64\x53\x0D\x82\x3F" ), 1 + 8, 1 } },
  #endif /* 0 */
  #if 0		/* Additional check value from OpenSSL, now with 1,000 iterations */
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PKCS12, ( FUNCTION_CAST ) derivePKCS12 },
	  { MKDATA( "\xED\x20\x34\xE3\x63\x28\x83\x0F\xF0\x9D\xF1\xE1\xA0\x7D\xD3\x57"
				"\x18\x5D\xAC\x0D\x4F\x9E\xB3\xD4" ), 24,
		MKDATA( "queeg" ), 5, CRYPT_ALGO_SHA1, 0,
		MKDATA( "\x01\x05\xDE\xC9\x59\xAC\xFF\x72\xF7" ), 1 + 8, 1000 } },
  #endif /* 0 */
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PKCS12, ( FUNCTION_CAST ) derivePKCS12 },
	  { MKDATA( "\x8B\xFB\x1D\x77\xFE\x78\xFF\xE8\xE9\x69\x76\xE0\xC5\x0A\xB6\xD2"
				"\x64\xEC\xA3\x01\xE9\xD2\xE0\xC0\xBC\x60\x3D\x63\xB2\x4A\xB2\x63" ), MECHANISM_OUTPUT_SIZE,
		inputValue, MECHANISM_INPUT_SIZE, CRYPT_ALGO_SHA1, 0,
		pkcs12saltValue, 1 + MECHANISM_SALT_SIZE, 10 } },
#endif /* USE_PKCS12 */
	{ { MESSAGE_NONE } }, { { MESSAGE_NONE } }
	};

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int deriveSelftest( STDC_UNUSED void *dummy, 
					STDC_UNUSED MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	BYTE buffer[ MECHANISM_OUTPUT_SIZE_TLS + 8 ];
	LOOP_INDEX i;
	int status;

	UNUSED_ARG_OPT( dummy );
	UNUSED_ARG( mechanismInfo );

	LOOP_LARGE( i = 0, 
				i < FAILSAFE_ARRAYSIZE( deriveMechanismTestInfo, \
										MECHANISM_TEST_INFO ) && \
					deriveMechanismTestInfo[ i ].mechanismFunctionInfo.action != MESSAGE_NONE,
				i++ )
		{
		const MECHANISM_FUNCTION_INFO *mechanismFunctionInfo;
		const MECHANISM_TEST_INFO *mechanismTestInfoPtr;
		MECHANISM_DERIVE_INFO testMechanismInfo;

		ENSURES( LOOP_INVARIANT_LARGE( i, 0, 
									   FAILSAFE_ARRAYSIZE( deriveMechanismTestInfo, \
														   MECHANISM_TEST_INFO ) - 1 ) );

		mechanismFunctionInfo = &deriveMechanismTestInfo[ i ].mechanismFunctionInfo;
		mechanismTestInfoPtr = &deriveMechanismTestInfo[ i ];
		memcpy( &testMechanismInfo, &mechanismTestInfoPtr->mechanismInfo, 
				sizeof( MECHANISM_DERIVE_INFO ) );
		testMechanismInfo.dataOut = buffer;
		status = mechanismFunctionInfo->function( NULL, &testMechanismInfo );
		if( cryptStatusError( status ) )
			return( status );
		if( memcmp( mechanismTestInfoPtr->mechanismInfo.dataOut, buffer, 
					mechanismTestInfoPtr->mechanismInfo.dataOutLength ) )
			{
			DEBUG_PRINT(( "Mechanism self-test #%d for mechanism %d failed.\n", 
						  i, mechanismFunctionInfo->mechanism ));
			return( CRYPT_ERROR_FAILED );
			}
		}
	ENSURES( LOOP_BOUND_OK );
	ENSURES( i < FAILSAFE_ARRAYSIZE( deriveMechanismTestInfo, \
									 MECHANISM_TEST_INFO ) );

	return( CRYPT_OK );
	}

/* Test KDF mechanisms */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
static int testDeriveHKDF( STDC_UNUSED void *dummy, 
						   INOUT_PTR MECHANISM_DERIVE_INFO *mechanismInfo )
	{
	/* The HKDF test requires an extra parameter so we need to go through an 
	   intermediate function that adds this */
	return( deriveHKDF( dummy, mechanismInfo,
						"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9", 10 ) ); 
	}

static const MECHANISM_TEST_INFO kdfMechanismTestInfo[] = {
	/* For the KDF tests we call deriveXXX() rather than kdfXXX() since the 
	   latter works directly on encryption contexts rather than on user-
	   supplied input data */
	{ { MESSAGE_DEV_KDF, MECHANISM_DERIVE_PBKDF2, ( FUNCTION_CAST ) derivePBKDF2 },
	  { "\x46\x9D\x41\x22\x45\x10\x28\x4A\xF9\x80\x62\xCF\xD6\x4F\x4D\x66"
		"\x4B\x76\xEC\x7E\xF0\x48\x7A\xC3\x9A\xDB\x2E\xAE\x56\x94\x65\x01", MECHANISM_OUTPUT_SIZE,
		inputValue, MECHANISM_INPUT_SIZE, CRYPT_ALGO_HMAC_SHA2, 0,
		saltValue, MECHANISM_SALT_SIZE, 1 } },
#if 1	/* RFC 5869 Test 1 needs 'info' value */
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HKDF, ( FUNCTION_CAST ) testDeriveHKDF },
	  { MKDATA( "\x3C\xB2\x5F\x25\xFA\xAC\xD5\x7A\x90\x43\x4F\x64\xD0\x36\x2F\x2A"
				"\x2D\x2D\x0A\x90\xCF\x1A\x5A\x4C\x5D\xB0\x2D\x56\xEC\xC4\xC5\xBF"
				"\x34\x00\x72\x08\xD5\xB8\x87\x18\x58\x65" ), 42,
		MKDATA( "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B"
				"\x0B\x0B\x0B\x0B\x0B\x0B" ), 22,
		CRYPT_ALGO_HMAC_SHA2, 32,
		MKDATA( "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C" ), 13, 1 } },
		/* RFC 5869 Test 2 exceeds CRYPT_MAX_HASHSIZE */
		/* RFC 5869 Test 3 zero-length salt */
		/* RFC 5869 Test 3 Test Case 4 needs 'info' value */
	{ { MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_HKDF, ( FUNCTION_CAST ) testDeriveHKDF },
	  { MKDATA( "\x08\x5A\x01\xEA\x1B\x10\xF3\x69\x33\x06\x8B\x56\xEF\xA5\xAD\x81"
				"\xA4\xF1\x4B\x82\x2F\x5B\x09\x15\x68\xA9\xCD\xD4\xF1\x55\xFD\xA2"
				"\xC2\x2E\x42\x24\x78\xD3\x05\xF3\xF8\x96" ), 42,
		MKDATA( "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" ), 11,
		CRYPT_ALGO_HMAC_SHA1, 0,
		MKDATA( "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C" ), 13, 1 } },
		/* RFC 5869 Test 5 exceeds CRYPT_MAX_HASHSIZE */
		/* RFC 5869 Test 6 zero-length salt */
		/* RFC 5869 Test 7 same as Test 6 */
#endif /* 0 */
	{ { MESSAGE_NONE } }, { { MESSAGE_NONE } }
	};

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int kdfSelftest( STDC_UNUSED void *dummy, 
				 STDC_UNUSED MECHANISM_KDF_INFO *mechanismInfo )
	{
	BYTE buffer[ MECHANISM_OUTPUT_SIZE_TLS + 8 ];
	LOOP_INDEX i;
	int status;

	UNUSED_ARG_OPT( dummy );
	UNUSED_ARG( mechanismInfo );

	LOOP_LARGE( i = 0, 
				i < FAILSAFE_ARRAYSIZE( kdfMechanismTestInfo, \
										MECHANISM_TEST_INFO ) && \
					kdfMechanismTestInfo[ i ].mechanismFunctionInfo.action != MESSAGE_NONE,
				i++ )
		{
		const MECHANISM_FUNCTION_INFO *mechanismFunctionInfo;
		const MECHANISM_TEST_INFO *mechanismTestInfoPtr;
		MECHANISM_DERIVE_INFO testMechanismInfo;

		ENSURES( LOOP_INVARIANT_LARGE( i, 0, 
									   FAILSAFE_ARRAYSIZE( kdfMechanismTestInfo, \
														   MECHANISM_TEST_INFO ) - 1 ) );

		mechanismFunctionInfo = &kdfMechanismTestInfo[ i ].mechanismFunctionInfo;
		mechanismTestInfoPtr = &kdfMechanismTestInfo[ i ];
		memcpy( &testMechanismInfo, &mechanismTestInfoPtr->mechanismInfo, 
				sizeof( MECHANISM_DERIVE_INFO ) );
		testMechanismInfo.dataOut = buffer;
		status = mechanismFunctionInfo->function( NULL, &testMechanismInfo );
		if( cryptStatusError( status ) )
			return( status );
		if( memcmp( mechanismTestInfoPtr->mechanismInfo.dataOut, buffer, 
					mechanismTestInfoPtr->mechanismInfo.dataOutLength ) )
			{
			DEBUG_PRINT(( "Mechanism self-test #%d for mechanism %d failed.\n", 
						  i, mechanismFunctionInfo->mechanism ));
			return( CRYPT_ERROR_FAILED );
			}
		}
	ENSURES( LOOP_BOUND_OK );
	ENSURES( i < FAILSAFE_ARRAYSIZE( kdfMechanismTestInfo, \
									 MECHANISM_TEST_INFO ) );

	return( CRYPT_OK );
	}
#endif /* CONFIG_NO_SELFTEST */
