/****************************************************************************
*																			*
*					cryptlib PKC_INFO Bignum Support Routines				*
*						Copyright Peter Gutmann 1995-2025					*
*																			*
****************************************************************************/

#define PKC_CONTEXT		/* Indicate that we're working with PKC contexts */
#include "crypt.h"
#if defined( INC_ALL )
  #include "context.h"
#else
  #include "context/context.h"
#endif /* Compiler-specific includes */

#ifdef USE_PKC

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Make sure that the metadata for bignums in a PKC_INFO is valid */

#ifndef CONFIG_CONSERVE_MEMORY_EXTRA

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
BOOLEAN sanityCheckPKCInfo( const PKC_INFO *pkcInfo )
	{
	assert( isReadPtr( pkcInfo, sizeof( PKC_INFO ) ) );

	/* Check the PKC info metadata */
	if( !isPkcAlgo( pkcInfo->cryptAlgo ) || \
		!CHECK_FLAGS( pkcInfo->flags, PKCINFO_FLAG_NONE, 
					  PKCINFO_FLAG_MAX ) )
		return( FALSE );
	if( isPqcAlgo( pkcInfo->cryptAlgo ) )
		{
		if( pkcInfo->keySizeBits < 0 || \
			pkcInfo->keySizeBits > bytesToBits( MAX_PKCSIZE_PQC ) )
			return( FALSE );
		}
	else
		{
		if( pkcInfo->keySizeBits < 0 || \
			pkcInfo->keySizeBits > bytesToBits( CRYPT_MAX_PKCSIZE ) )
			return( FALSE );
		}
	if( pkcInfo->publicKeyInfo == NULL )
		{
		if( pkcInfo->publicKeyInfoSize != 0 )
			return( FALSE );
		}
	else
		{
		if( !isShortIntegerRangeMin( pkcInfo->publicKeyInfoSize, 
									 MIN_CRYPT_OBJECTSIZE ) )
			return( FALSE );
		}

	/* If it's a dummy context with the keys held in hardware, we're done.
	   In theory we could check for BN_is_zero() for each of the bignums, 
	   but that only looks at the metadata to see if the bignum has a length
	   of zero, and it's not clear what this would achieve */
	if( TEST_FLAG( pkcInfo->flags, PKCINFO_FLAG_DUMMY ) )
		return( TRUE );

	/* Check the PKC info bignums, first the shared values */
	if( !sanityCheckBignum( &pkcInfo->param1 ) || \
		!sanityCheckBignum( &pkcInfo->param2 ) || \
		!sanityCheckBignum( &pkcInfo->param3 ) || \
		!sanityCheckBignum( &pkcInfo->param4 ) || \
		!sanityCheckBignum( &pkcInfo->param5 ) || \
		!sanityCheckBignum( &pkcInfo->param6 ) || \
		!sanityCheckBignum( &pkcInfo->param7 ) || \
		!sanityCheckBignum( &pkcInfo->param8 ) || \
		!sanityCheckBignum( &pkcInfo->blind1 ) || \
		!sanityCheckBignum( &pkcInfo->blind2 ) || \
		!sanityCheckBignum( &pkcInfo->tmp1 ) || \
		!sanityCheckBignum( &pkcInfo->tmp2 ) || \
		!sanityCheckBignum( &pkcInfo->tmp3 ) )
		return( FALSE );

	/* Some algorithms don't use any of the remaining values so we exit
	   now */
#if defined( USE_X25519 ) || defined( USE_ED25519 ) 
	if( isBernsteinAlgo( pkcInfo->cryptAlgo ) )
		return( TRUE );
#endif /* USE_X25519 || USE_ED25519 */
#ifdef USE_MLKEM 
	if( isPqcAlgo( pkcInfo->cryptAlgo ) )
		{
		/* ML-KEM uses somewhat different key storage than the standard 
		   PKCs */
		if( pkcInfo->mlkemKey != ( MLKEM_KEY_INFO * ) &pkcInfo->bnCTX || \
			pkcInfo->mlkemKeySize != sizeof( MLKEM_KEY_INFO ) )
			return( FALSE );
			
		return( TRUE );
		}
#endif /* USE_MLKEM */

	/* Check the remaining bignums */
	if( !sanityCheckBNCTX( &pkcInfo->bnCTX ) )
		return( FALSE );
	if( !sanityCheckBNMontCTX( &pkcInfo->montCTX1 ) || \
		!sanityCheckBNMontCTX( &pkcInfo->montCTX2 ) || \
		!sanityCheckBNMontCTX( &pkcInfo->montCTX3 ) )
		return( FALSE );

	return( TRUE );
	}
#endif /* !CONFIG_CONSERVE_MEMORY_EXTRA */

/****************************************************************************
*																			*
*						Bignum Init/Shutdown Routines 						*
*																			*
****************************************************************************/

/* Clear temporary bignum values used during PKC operations */

STDC_NONNULL_ARG( ( 1 ) ) \
void clearTempBignums( INOUT_PTR PKC_INFO *pkcInfo )
	{
	assert( isWritePtr( pkcInfo, sizeof( PKC_INFO ) ) );

	/* Some algorithms don't use the bignums but store their own data in the
	   space so we don't want to touch them */
#if defined( USE_X25519 ) || defined( USE_ED25519 ) || defined( USE_MLKEM )
	if( isBernsteinAlgo( pkcInfo->cryptAlgo ) || \
		isPqcAlgo( pkcInfo->cryptAlgo ) )
		return;
#endif /* USE_X25519 || USE_ED25519 || USE_MLKEM */

	BN_clear( &pkcInfo->tmp1 ); BN_clear( &pkcInfo->tmp2 );
	BN_clear( &pkcInfo->tmp3 );
#if defined( USE_ECDH ) || defined( USE_ECDSA )
	if( isEccAlgo( pkcInfo->cryptAlgo ) && \
		!isBernsteinAlgo( pkcInfo->cryptAlgo ) )
		{
		BN_clear( &pkcInfo->eccParam_tmp4 ); 
		BN_clear( &pkcInfo->eccParam_tmp5 );
		}
#endif /* USE_ECDH || USE_ECDSA */
	BN_CTX_final( &pkcInfo->bnCTX );
	}

/* Initialise and free the bignum information in a context */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int initContextBignums( INOUT_PTR PKC_INFO *pkcInfo, 
						IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo )
	{
	assert( isWritePtr( pkcInfo, sizeof( PKC_INFO ) ) );

	REQUIRES( isEnumRange( cryptAlgo, CRYPT_ALGO ) );

	/* Initialise the overall PKC information */
	memset( pkcInfo, 0, sizeof( PKC_INFO ) );
	pkcInfo->cryptAlgo = cryptAlgo;
	INIT_FLAGS( pkcInfo->flags, PKCINFO_FLAG_NONE );

	/* Initialise the bignum information.  The values aren't used for all
	   algorithm types but we clear everything in any case to keep things
	   clean */
	BN_init( &pkcInfo->param1 ); BN_init( &pkcInfo->param2 );
	BN_init( &pkcInfo->param3 ); BN_init( &pkcInfo->param4 );
	BN_init( &pkcInfo->param5 ); BN_init( &pkcInfo->param6 );
	BN_init( &pkcInfo->param7 ); BN_init( &pkcInfo->param8 );
	BN_init( &pkcInfo->blind1 ); BN_init( &pkcInfo->blind2 );
	BN_init( &pkcInfo->tmp1 ); BN_init( &pkcInfo->tmp2 );
	BN_init( &pkcInfo->tmp3 );
	switch( pkcInfo->cryptAlgo )
		{
#if defined( USE_ECDH ) || defined( USE_ECDSA )
		case CRYPT_ALGO_ECDSA:
		case CRYPT_ALGO_ECDH:
			pkcInfo->ecCTX = EC_GROUP_new( EC_GFp_simple_method() );
			if( pkcInfo->ecCTX == NULL )
				return( CRYPT_ERROR_MEMORY );
			pkcInfo->ecPoint = EC_POINT_new( pkcInfo->ecCTX );
			pkcInfo->tmpPoint = EC_POINT_new( pkcInfo->ecCTX );
			if( pkcInfo->ecPoint == NULL || pkcInfo->tmpPoint == NULL )
				{
				if( pkcInfo->tmpPoint != NULL )
					EC_POINT_free( pkcInfo->tmpPoint );
				if( pkcInfo->ecPoint != NULL )
					EC_POINT_free( pkcInfo->ecPoint );
				EC_GROUP_free( pkcInfo->ecCTX );
				return( CRYPT_ERROR_MEMORY );
				}
			STDC_FALLTHROUGH;
#endif /* USE_ECDH || USE_ECDSA */

		case CRYPT_ALGO_RSA:
		case CRYPT_ALGO_DH:
		case CRYPT_ALGO_DSA:
		case CRYPT_ALGO_ELGAMAL:
			BN_CTX_init( &pkcInfo->bnCTX );
			BN_MONT_CTX_init( &pkcInfo->montCTX1 );
			BN_MONT_CTX_init( &pkcInfo->montCTX2 );
			BN_MONT_CTX_init( &pkcInfo->montCTX3 );
			break;

#if defined( USE_X25519 ) || defined( USE_ED25519 )
		case CRYPT_ALGO_25519:
		case CRYPT_ALGO_ED25519:
			/* The 25519 parameters are just a block of memory that was 
			   cleared as part of the overall PKC_INFO clearing and don't 
			   need any initialisation */
			break;
#endif /* USE_X25519 || defined( USE_ED25519 */

#ifdef USE_MLKEM
		case CRYPT_ALGO_MLKEM:
			{
			/* PQC keys, which are enormous, are stored in the otherwise 
			   unused bnCTX storage.  This is just an unstructured block of
			   memory, the actual size used will be set by the caller */
			pkcInfo->mlkemKey = ( MLKEM_KEY_INFO * ) &pkcInfo->bnCTX;
			pkcInfo->mlkemKeySize = sizeof( MLKEM_KEY_INFO );
			memset( pkcInfo->mlkemKey, 0, sizeof( MLKEM_KEY_INFO ) );
			break;
			}
#endif /* USE_MLKEM */

		default:
			retIntError();
		}

	ENSURES( sanityCheckPKCInfo( pkcInfo ) );

	return( CRYPT_OK );
	}

STDC_NONNULL_ARG( ( 1 ) ) \
void endContextBignums( INOUT_PTR PKC_INFO *pkcInfo, 
						IN_BOOL const BOOLEAN isDummyContext )
	{
	assert( isWritePtr( pkcInfo, sizeof( PKC_INFO ) ) );
	assert( sanityCheckPKCInfo( pkcInfo ) );
			/* We don't make this an ENSURES since that would prevent 
			   clearing the data.  Since the cleanup is just a series of 
			   memset()s of fixed-sized data it doesn't matter if a field 
			   is in an inconsistent state.
			   
			   Note that the pkcInfo itself is cleared by the caller as part
			   of the overall context information */

	REQUIRES_V( isBooleanValue( isDummyContext ) );

	/* If it's a dummy context then there's nothing to do except optionally
	   free the encoded publicKeyInfo */
	if( isDummyContext )
		{
		if( pkcInfo->publicKeyInfo != NULL )
			{
			clFree( "endContextBignums", pkcInfo->publicKeyInfo );
			pkcInfo->publicKeyInfo = NULL;
			}
		return;
		}

	/* Clear the bignum information.  The values aren't used for all
	   algorithm types but we clear everything in any case to keep things
	   clean */
	BN_clear( &pkcInfo->param1 ); BN_clear( &pkcInfo->param2 );
	BN_clear( &pkcInfo->param3 ); BN_clear( &pkcInfo->param4 );
	BN_clear( &pkcInfo->param5 ); BN_clear( &pkcInfo->param6 );
	BN_clear( &pkcInfo->param7 ); BN_clear( &pkcInfo->param8 );
	BN_clear( &pkcInfo->blind1 ); BN_clear( &pkcInfo->blind2 );
	BN_clear( &pkcInfo->tmp1 ); BN_clear( &pkcInfo->tmp2 );
	BN_clear( &pkcInfo->tmp3 );
	switch( pkcInfo->cryptAlgo )
		{
#if defined( USE_ECDH ) || defined( USE_ECDSA )
		case CRYPT_ALGO_ECDSA:
		case CRYPT_ALGO_ECDH:
			EC_POINT_free( pkcInfo->tmpPoint );
			EC_POINT_free( pkcInfo->ecPoint );
			EC_GROUP_free( pkcInfo->ecCTX );
			STDC_FALLTHROUGH;
#endif /* USE_ECDH || USE_ECDSA */

		case CRYPT_ALGO_RSA:
		case CRYPT_ALGO_DH:
		case CRYPT_ALGO_DSA:
		case CRYPT_ALGO_ELGAMAL:
			BN_CTX_final( &pkcInfo->bnCTX );
			BN_MONT_CTX_free( &pkcInfo->montCTX1 );
			BN_MONT_CTX_free( &pkcInfo->montCTX2 );
			BN_MONT_CTX_free( &pkcInfo->montCTX3 );
			break;

#if defined( USE_X25519 ) || defined( USE_ED25519 )
		case CRYPT_ALGO_25519:
		case CRYPT_ALGO_ED25519:
			/* The 25519 parameters are just a block of memory that's
			   cleared as part of the overall PKC_INFO clearing and don't 
			   need any cleanup */
			break;
#endif /* USE_X25519 || defined( USE_ED25519 */

#ifdef USE_MLKEM
		case CRYPT_ALGO_MLKEM:
			zeroise( pkcInfo->mlkemKey, pkcInfo->mlkemKeySize );
			break;
#endif /* USE_MLKEM */

		default:
			retIntError_Void();
		}
	if( pkcInfo->publicKeyInfo != NULL )
		{
		clFree( "endContextBignums", pkcInfo->publicKeyInfo );
		pkcInfo->publicKeyInfo = NULL;
		}
	}

/****************************************************************************
*																			*
*							Bignum Checksum Routines 						*
*																			*
****************************************************************************/

/* Checksum a bignum's metadata and its data payload.  We can't use the 
   standard checksumData() here both because we need to keep a running total 
   of the existing checksum value and because we don't want to truncate the 
   checksum value to 16 bits at the end of each calculation because it's fed 
   to the next round of checksumming */

CHECK_RETVAL_RANGE_NOERROR( 0, INT_MAX ) STDC_NONNULL_ARG( ( 1 ) ) \
static int checksumBignumData( IN_BUFFER( length ) const void *data, 
							   IN_LENGTH_SHORT const int length,
							   const int initialSum )
	{
	const unsigned char *dataPtr = data;
	LOOP_INDEX i;
	int sum1 = 0, sum2 = initialSum;

	assert( isReadPtrDynamic( data, length ) );

	REQUIRES_EXT( isShortIntegerRangeNZ( length ), 0 );

	LOOP_MAX( i = 0, i < length, i++ )
		{
		ENSURES_EXT( LOOP_INVARIANT_MAX( i, 0, length - 1 ), 0 );

		sum1 += dataPtr[ i ];
		sum2 += sum1;
		}
	ENSURES_EXT( LOOP_BOUND_OK, 0 );
	return( sum2 );
	}

#define BN_checksum( bignum, checksum ) \
	*( checksum ) = checksumBignumData( bignum, sizeof( BIGNUM ), *( checksum ) )
#define BN_checksum_montgomery( montCTX, checksum ) \
	*( checksum ) = checksumBignumData( montCTX, sizeof( BN_MONT_CTX ), *( checksum ) )
#define Keydata_checksum( data, length, checksum ) \
	*( checksum ) = checksumBignumData( data, length, *( checksum ) )

#if defined( USE_ECDH ) || defined( USE_ECDSA )

/* Checksum the ECC structure metadata and their data payloads */

STDC_NONNULL_ARG( ( 1, 2 ) ) \
static void BN_checksum_ec_group( IN_PTR const EC_GROUP *group, 
								  INOUT_PTR int *checksum )
	{
	int value = *checksum;

	assert( isReadPtr( group, sizeof( EC_GROUP ) ) );
	assert( isWritePtr( checksum, sizeof( int ) ) );

	/* Checksum the EC_GROUP metadata */
	value = checksumBignumData( group, sizeof( EC_GROUP ), value );

	/* EC_GROUPs have an incredibly complex inner structure (see 
	   bn/ec_lcl.h), the following checksums the common data without getting 
	   into the lists of method-specific data values */
	BN_checksum( &group->order, &value );
	BN_checksum( &group->cofactor, &value );
	BN_checksum( &group->field, &value );
	BN_checksum( &group->a, &value );
	BN_checksum( &group->b, &value );
	
	*checksum = value;
	}

STDC_NONNULL_ARG( ( 1, 2 ) ) \
static void BN_checksum_ec_point( IN_PTR const EC_POINT *point, 
								  INOUT_PTR int *checksum )
	{
	int value = *checksum;

	assert( isReadPtr( point, sizeof( EC_POINT ) ) );
	assert( isWritePtr( checksum, sizeof( int ) ) );

	/* Checksum the EC_POINT metadata */
	value = checksumBignumData( point, sizeof( EC_POINT ), value );

	/* Checksum the EC_POINT data */
	BN_checksum( &point->X, &value );
	BN_checksum( &point->Y, &value );
	BN_checksum( &point->Z, &value );

	*checksum = value;
	}
#endif /* USE_ECDH || USE_ECDSA */

/* Calculate a bignum checksum */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int bignumChecksum( INOUT_PTR PKC_INFO *pkcInfo, 
						   IN_BOOL const BOOLEAN isPrivateKey,
						   OUT_PTR int *checksum )
	{
	int value = 0;

	assert( isWritePtr( pkcInfo, sizeof( PKC_INFO ) ) );
	assert( isWritePtr( checksum, sizeof( int ) ) );

	REQUIRES( isBooleanValue( isPrivateKey ) );

	/* Clear return value */
	*checksum = 0;

	/* Calculate the key data checksum */
	switch( pkcInfo->cryptAlgo )
		{
		case CRYPT_ALGO_DH:
		case CRYPT_ALGO_DSA:
		case CRYPT_ALGO_ELGAMAL:
			BN_checksum( &pkcInfo->dlpParam_p, &value );
			BN_checksum( &pkcInfo->dlpParam_g, &value );
			BN_checksum( &pkcInfo->dlpParam_q, &value );
			BN_checksum( &pkcInfo->dlpParam_y, &value );
			if( isPrivateKey )
				BN_checksum( &pkcInfo->dlpParam_x, &value );
			BN_checksum_montgomery( &pkcInfo->dlpParam_mont_p, &value );
			break;
		
		case CRYPT_ALGO_RSA:
			/* Note that we don't checksum the (optional) blinding values 
			   rsaParam_blind_k and rsaParam_blind_kInv since these are 
			   updated on every RSA operation so the checksum would change 
			   every time they're used.  Since these are random values, a 
			   fault will only make them even more random */
			BN_checksum( &pkcInfo->rsaParam_n, &value );
			BN_checksum( &pkcInfo->rsaParam_e, &value );
			BN_checksum_montgomery( &pkcInfo->rsaParam_mont_n, &value );
			if( isPrivateKey )
				{
				BN_checksum( &pkcInfo->rsaParam_d, &value );
				BN_checksum( &pkcInfo->rsaParam_p, &value );
				BN_checksum( &pkcInfo->rsaParam_q, &value );
				BN_checksum( &pkcInfo->rsaParam_u, &value );
				BN_checksum( &pkcInfo->rsaParam_exponent1, &value );
				BN_checksum( &pkcInfo->rsaParam_exponent2, &value );
				BN_checksum_montgomery( &pkcInfo->rsaParam_mont_p, &value );
				BN_checksum_montgomery( &pkcInfo->rsaParam_mont_q, &value );
				}
			break;
		
#if defined( USE_ECDH ) || defined( USE_ECDSA )
		case CRYPT_ALGO_ECDSA:
		case CRYPT_ALGO_ECDH:
			BN_checksum( &pkcInfo->eccParam_qx, &value );
			BN_checksum( &pkcInfo->eccParam_qy, &value );
			if( isPrivateKey )
				BN_checksum( &pkcInfo->eccParam_d, &value );
			BN_checksum_ec_group( pkcInfo->ecCTX, &value );
			BN_checksum_ec_point( pkcInfo->ecPoint, &value );
			break;
#endif /* USE_ECDH || USE_ECDSA */
		
#if defined( USE_X25519 ) || defined( USE_ED25519 )
		case CRYPT_ALGO_25519:
		case CRYPT_ALGO_ED25519:
			BN_checksum( &pkcInfo->curve25519Param_pub, &value );
			if( isPrivateKey )
				{
				BN_checksum( &pkcInfo->curve25519Param_priv, &value );
				BN_checksum( &pkcInfo->curve25519Param_s, &value );
				}
			break;
#endif /* USE_X25519 || defined( USE_ED25519 */

#ifdef USE_MLKEM
		case CRYPT_ALGO_MLKEM:
			Keydata_checksum( pkcInfo->mlkemKey, pkcInfo->mlkemKeySize, 
							  &value );
			break;
#endif /* USE_MLKEM */

		default:
			retIntError();
		}
	*checksum = value;

	return( CRYPT_OK ); 
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int checksumContextData( INOUT_PTR PKC_INFO *pkcInfo, 
						 IN_BOOL const BOOLEAN isPrivateKey )
	{
	int checksum, status;

	assert( isWritePtr( pkcInfo, sizeof( PKC_INFO ) ) );

	REQUIRES( isBooleanValue( isPrivateKey ) );

	/* Set or update the data checksum */
	status = bignumChecksum( pkcInfo, isPrivateKey, &checksum );
	ENSURES( cryptStatusOK( status ) );
	if( pkcInfo->checksum == 0L )
		pkcInfo->checksum = checksum;
	else
		{
		if( pkcInfo->checksum != checksum )
			return( CRYPT_ERROR );
		}

	/* Check static domain parameters if required */
	if( pkcInfo->domainParams != NULL )
		{
		if( !checksumDomainParameters( pkcInfo->domainParams, 
									   pkcInfo->cryptAlgo ) )
			return( CRYPT_ERROR );
		}

	return( CRYPT_OK );
	}

/* Checksum a static bignum, which is one where the bignum image is stored
   in read-only memory.  This is used for things like DLP and ECDLP domain
   parameters, for which there's no need to parse and process them into 
   dynamically-allocated RAM space.

   We can't use the standard Fletcher checksum for this because it performs
   poorly on data with long strings of repeated bits, particularly all-zero
   and all-one words, which occur in both the DLP and ECDLP domain 
   parameters.  What we need is a function with good avalanche, of which
   MurmurHash seems to be the best tradeoff between effectiveness in
   defeating bit flips and the like and speed.

   The principal alternative is CityHash (and its follow-on FarmHash), but 
   that's incredibly complex (FarmHash is 12K LOC, do have a cow), and in 
   any case CityHash only exists as 64- and 128-bit variants, there's no 
   32-bit version of the hash.

   The following code is based on the MurmurHash3 implementation by Austin 
   Appleby, who placed it in the public domain.  It's built around a core
   that takes the input word and pre-mixes it using multiplies by two 
   constants and a rotate (thus the Murmur name, multiply-and-rotate), xors 
   the result into the hash value, and them mixes the hash using a rotate 
   and multiply-accumulate:

	k *= c1;
	k = rotl( k, r1 );
	k *= c2;

	h ^= k;

	h = rotl( h, r1 );
	h = h * m1 + n1; */

#ifdef _MSC_VER
  #define ROTL32( x, r )	_rotl( x, r )
  #define ROTL64( x, r )	_rotl64( x, r )
#else
  /* Most compilers have rotate-recognisers, this is the form that will 
     produce a native rotate instruction */
  #define ROTL32( x, r )	( ( ( x ) << ( r ) ) | ( ( x ) >> ( 32 - ( r ) ) ) )
  #define ROTL64( x, r )	( ( ( x ) << ( r ) ) | ( ( x ) >> ( 64 - ( r ) ) ) )
#endif /* _MSC_VER */

#if BN_BITS2 == 64

#define CONST_1		0x87C37B91114253D5ULL
#define CONST_2		0x4CF5AD432745937FULL

static BN_ULONG fmix64( BN_ULONG k )
	{
	/* The constants were generated by Austin Appleby using a simulated-
	   annealing algorithm, and will avalanche all bits of 'h' to within 
	   a 0.25% bias */ 
	k ^= k >> 33;
	k *= 0xFF51AFD7ED558CCDULL;
	k ^= k >> 33;
	k *= 0xC4CEB9FE1A85EC53ULL;
	k ^= k >> 33;

	return( k );
	}

static BN_ULONG MurmurHash3( const BN_ULONG *data, const int len, BN_ULONG seed )
	{
	BN_ULONG h1 = seed, h2 = seed;
	const int endOffset = ( len & 1 ) ? len - 1 : INT_MAX;
	LOOP_INDEX i;

	assert( isReadPtr( data, len * sizeof( BN_ULONG ) ) );

	ENSURES_EXT( len > 0 && len <= BIGNUM_ALLOC_WORDS_EXT2, 0 );

	/* Process each input word, two words at a time.  Since we're reading two 
	   words at a time the length has to be an even value which is always the 
	   case for standard-sized bignums, however we need to be able do deal with 
	   odd-sized ones in case we ever encounter them at some point, and the 
	   bignum self-tests do use very short values for testing.  To deal with 
	   this we use a zero value as the last value if the bignum has an odd 
	   number of words.  We'd do this anyway because of the over-allocation of 
	   data for each bignum, but making it explicit here means that we're not 
	   just relying on a side-effect of the way that the bignums are stored */
	LOOP_EXT( i = 0, i < len, i += 2, BIGNUM_ALLOC_WORDS_EXT2 )
		{
		BN_ULONG k1, k2;

		ENSURES_EXT( LOOP_INVARIANT_EXT_XXX( i, 0, len - 1,
											 BIGNUM_ALLOC_WORDS_EXT2 ), 0 );

		k1 = data[ i ];
		k2 = ( i < endOffset ) ? data[ i + 1 ] : 0;

		k1 *= CONST_1; 
		k1 = ROTL64( k1, 31 ); 
		k1 *= CONST_2; 
		h1 ^= k1;

		h1 = ROTL64( h1, 27 ); 
		h1 += h2; 
		h1 = ( h1 * 5 ) + 0x52DCE729;

		k2 *= CONST_2; 
		k2 = ROTL64( k2, 33 ); 
		k2 *= CONST_1; 
		h2 ^= k2;

		h2 = ROTL64( h2, 31 ); 
		h2 += h1; 
		h2 = ( h2 * 5 ) + 0x38495AB5;
		}
	ENSURES_EXT( LOOP_BOUND_OK, 0 );

	/* Finalisation and avalanche */
	h1 ^= len; 
	h2 ^= len;
	h1 += h2;
	h2 += h1;
	h1 = fmix64( h1 );
	h2 = fmix64( h2 );
	h1 += h2;
/*	h2 += h1; Not used for 64-bit result */

	return( h1 );
	}
#else

#define CONST_1		0xCC9E2D51UL
#define CONST_2		0x1B873593UL

static BN_ULONG MurmurHash3( const BN_ULONG *data, const int len, BN_ULONG seed )
	{
	BN_ULONG h1 = seed;
	LOOP_INDEX i;

	assert( isReadPtr( data, len * sizeof( BN_ULONG ) ) );

	ENSURES_EXT( len > 0 && len <= BIGNUM_ALLOC_WORDS_EXT2, 0 );

	/* Process each input word */
	LOOP_EXT( i = 0, i < len, i++, BIGNUM_ALLOC_WORDS_EXT2 )
		{
		BN_ULONG k1;

		ENSURES_EXT( LOOP_INVARIANT_EXT( i, 0, len - 1, 
										 BIGNUM_ALLOC_WORDS_EXT2 ), 0 );

		k1 = data[ i ];

		k1 *= CONST_1;
		k1 = ROTL32( k1, 15 );
		k1 *= CONST_2;
    
		h1 ^= k1;
		h1 = ROTL32( h1,13 ); 
		h1 = ( h1 * 5 ) + 0xE6546B64UL;
		}
	ENSURES_EXT( LOOP_BOUND_OK, 0 );

	/* Finalisation and avalanche using the fmix32() version of the 64-bit 
	   fmix64(), see the comment on that for how it works */
	h1 ^= len;
	h1 ^= h1 >> 16;
	h1 *= 0x85EBCA6BUL;
	h1 ^= h1 >> 13;
	h1 *= 0xC2B2AE35UL;
	h1 ^= h1 >> 16;

	return( h1 );
	} 
#endif /* 64- vs 32-bit */

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN checksumStaticBignum( const BIGNUM *bignum,
									 const BN_ULONG checksum )
	{
	BN_ULONG hash;
	LOOP_INDEX i;

	assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );

	/* Make sure that the metadata fields are valid.  These have fixed 
	   values so we just check that they contain the values that they're
	   supposed to */
	if( bignum->neg != FALSE || \
		bignum->flags != BN_FLG_STATIC_DATA )
		return( FALSE );
	if( bignum->top <= 0 || bignum->top > BIGNUM_ALLOC_WORDS )
		return( FALSE );

	/* Check that the data matches the checksum */
	hash = MurmurHash3( bignum->d, bignum->top, 0 );
	if( hash != checksum )
		return( FALSE );

	/* Finally, make sure that the rest of the bignum data is all zeroes */
	LOOP_EXT( i = bignum->top, i < BIGNUM_ALLOC_WORDS, i++,
			  BIGNUM_ALLOC_WORDS )
		{
		ENSURES_B( LOOP_INVARIANT_EXT( i, bignum->top, 
									   BIGNUM_ALLOC_WORDS - 1,
									   BIGNUM_ALLOC_WORDS ) );

		if( bignum->d[ i ] != 0 )
			return( FALSE );
		}
	ENSURES_B( LOOP_BOUND_OK );

	return( TRUE );
	}

/* Print a bignum checksum, used when hardcoding constant values like DLP 
   and ECDLP domain parameters from their bignum representations */

#if defined( DEBUG_DIAGNOSTIC_ENABLE ) && !defined( NDEBUG )

void printBignumChecksum( const BIGNUM *bignum )
	{
	assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );

#if BN_BITS2 == 64
	DEBUG_PRINT(( "0x%016llXULL, ", 
				  MurmurHash3( bignum->d, bignum->top, 0 ) ));
#else
	DEBUG_PRINT(( "0x%08X, ", 
				  MurmurHash3( bignum->d, bignum->top, 0 ) ));
#endif /* 64- vs 32-bit */
	}
#endif /* DEBUG_DIAGNOSTIC_ENABLE && Debug */

#if defined( USE_ECDH ) || defined( USE_ECDSA )

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN checksumECCParameters( const ECC_DOMAINPARAMS *domainParams )
	{
	assert( isReadPtr( domainParams, sizeof( ECC_DOMAINPARAMS ) ) );

	if( !checksumStaticBignum( &domainParams->p, domainParams->p_checksum ) || \
		!checksumStaticBignum( &domainParams->a, domainParams->a_checksum ) || \
		!checksumStaticBignum( &domainParams->b, domainParams->b_checksum ) || \
		!checksumStaticBignum( &domainParams->gx, domainParams->gx_checksum ) || \
		!checksumStaticBignum( &domainParams->gy, domainParams->gy_checksum ) || \
		!checksumStaticBignum( &domainParams->n, domainParams->n_checksum ) || \
		!checksumStaticBignum( &domainParams->h, domainParams->h_checksum ) )
		return( FALSE );

	return( TRUE );
	}
#endif /* USE_ECDH || USE_ECDSA */

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN checksumDHParameters( const DH_DOMAINPARAMS *domainParams )
	{
	assert( isReadPtr( domainParams, sizeof( DH_DOMAINPARAMS ) ) );

	if( !checksumStaticBignum( &domainParams->p, domainParams->p_checksum ) || \
		!checksumStaticBignum( &domainParams->q, domainParams->q_checksum ) || \
		!checksumStaticBignum( &domainParams->g, domainParams->g_checksum ) )
		return( FALSE );

	return( TRUE );
	}

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
BOOLEAN checksumDomainParameters( IN_PTR const void *domainParams, 
								  IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo )
	{
	REQUIRES_B( isPkcAlgo( cryptAlgo ) );

#if defined( USE_ECDH ) || defined( USE_ECDSA )
	return( isEccAlgo( cryptAlgo ) ? \
			checksumECCParameters( domainParams ) : \
			checksumDHParameters( domainParams ) );
#else
	return( checksumDHParameters( domainParams ) );
#endif /* USE_ECDH || USE_ECDSA */
	}
#else

/****************************************************************************
*																			*
*								Non-Bignum Stubs 							*
*																			*
****************************************************************************/

STDC_NONNULL_ARG( ( 1 ) ) \
void clearTempBignums( INOUT_PTR PKC_INFO *pkcInfo )
	{
	}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int initContextBignums( INOUT_PTR PKC_INFO *pkcInfo, 
						IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo )
	{
	}
STDC_NONNULL_ARG( ( 1 ) ) \
void endContextBignums( INOUT_PTR PKC_INFO *pkcInfo, 
						IN_BOOL const BOOLEAN isDummyContext )
	{
	}
#endif /* USE_PKC */
