/****************************************************************************
*																			*
*					cryptlib Bignum Import/Export 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 Routines 							*
*																			*
****************************************************************************/

/* Dump a bignum as a C structure, used for hardcoding constant values like
   DLP and ECDLP domain parameters from their bignum representations */

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

#if BN_BITS2 == 64
  #define BNULONG_FORMAT	"0x%016llXULL"
  #define BNULONG_PER_LINE	2
#else
  #define BNULONG_FORMAT	"0x%08X"
  #define BNULONG_PER_LINE	4
#endif /* 64- vs 32-bit */

void printBignum( const BIGNUM *bignum, const char *label )
	{
	const BN_ULONG *data = bignum->d;
	LOOP_INDEX i;

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

	DEBUG_PRINT(( "\t/* %s */\n\t", label ));
	DEBUG_PRINT(( "{ %d, FALSE, BN_FLG_STATIC_DATA, {\n\t", 
				  bignum->top ));
	LOOP_EXT( i = 0, i < bignum->top, i++, BIGNUM_ALLOC_WORDS )
		{
		ENSURES_V( LOOP_INVARIANT_EXT( i, 0, bignum->top - 1,
									   BIGNUM_ALLOC_WORDS ) );

		DEBUG_PRINT(( BNULONG_FORMAT, data[ i ] ));
		if( i < bignum->top - 1 )
			{
			DEBUG_PRINT(( ", " ));
			if( i > 0 && !( ( i + 1 ) & ( BNULONG_PER_LINE - 1 ) ) )
				DEBUG_PRINT(( "\n\t" ));
			}
		}
	ENSURES_V( LOOP_BOUND_OK );
	DEBUG_PRINT(( " } },\n" ));
	}
#endif /* DEBUG_DIAGNOSTIC_ENABLE && Debug */

/****************************************************************************
*																			*
*								Bignum Import Routines 						*
*																			*
****************************************************************************/

/* Check that an imported bignum value is valid */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkBignum( const BIGNUM *bignum,
						IN_LENGTH_PKC const int minLength, 
						IN_LENGTH_PKC const int maxLength, 
						IN_PTR_OPT const BIGNUM *maxRange,
						IN_ENUM_OPT( BIGNUM_CHECK ) \
							const BIGNUM_CHECK_TYPE checkType )
	{
	int bignumLengthBits, status;

	assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );
	assert( maxRange == NULL || isReadPtr( maxRange, sizeof( BIGNUM ) ) );

	REQUIRES( sanityCheckBignum( bignum ) );
	REQUIRES( minLength > 0 && minLength <= maxLength && \
			  maxLength <= CRYPT_MAX_PKCSIZE );
	REQUIRES( maxRange == NULL || sanityCheckBignum( maxRange ) );
	REQUIRES( isEnumRangeOpt( checkType, BIGNUM_CHECK ) );

	/* The following should never happen because bytesToBignum() works with 
	   unsigned values but we perform the check anyway just in case someone 
	   messes with the underlying bignum code */
	ENSURES( !( BN_is_negative( bignum ) ) );

	/* A zero- or one-valued bignum on the other hand is an error because we 
	   should never find zero or one in a PKC-related value.  This check is 
	   somewhat redundant with the one that follows, we place it here to 
	   make it explicit and because the cost is near zero */
	if( BN_cmp_word( bignum, 1 ) <= 0 )
		return( CRYPT_ERROR_BADDATA );

	/* Check that the bignum value falls within the allowed length range.  
	   Before we do the general length check we perform a more specific 
	   check for the case where the length is below the minimum allowed but 
	   still looks at least vaguely valid, in which case we report it as a 
	   too-short key rather than a bad data error.
	   
	   Note that we check the length in bits rather than bytes since the 
	   latter is rounded up, so a length that passes the check based on
	   its rounded-up byte-length value can then fail a later check based 
	   on its actual length in bits */
	status = bignumLengthBits = BN_num_bits( bignum );
	if( cryptStatusError( status ) )
		return( status );
	switch( checkType )
		{
		case BIGNUM_CHECK_NONE:
		case BIGNUM_CHECK_VALUE:
			/* No specific weak-key check for this value */
			break;

		case BIGNUM_CHECK_VALUE_PKC:
			if( isShortPKCKey( bitsToBytes( bignumLengthBits ) ) )
				return( CRYPT_ERROR_NOSECURE );
			break;

		case BIGNUM_CHECK_VALUE_ECC:
			if( isShortECCKey( bitsToBytes( bignumLengthBits ) ) )
				return( CRYPT_ERROR_NOSECURE );
			break;

		default:
			retIntError();
		}

	/* Check that the value is within range.  We have to be a bit careful
	   here when doing a bit-length based comparison because the minimum
	   length that can be specified as a byte is 8 bits, so if minLength == 1
	   then we allow any bit-length >= 2 bits, used for DH g values */
	if( minLength == 1 )
		{
		if( bignumLengthBits < 2 || \
			bignumLengthBits > bytesToBits( maxLength ) )
			return( CRYPT_ERROR_BADDATA );
		}
	else
		{
		if( bignumLengthBits < bytesToBits( minLength ) || \
			bignumLengthBits > bytesToBits( maxLength ) )
			return( CRYPT_ERROR_BADDATA );
		}

	/* Finally, if the caller has supplied a maximum-range bignum value, 
	   make sure that the value that we've read is less than this */
	if( maxRange != NULL && BN_cmp( bignum, maxRange ) >= 0 )
		return( CRYPT_ERROR_BADDATA );

	return( CRYPT_OK );
	}

/* Import a bignum from a byte string */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int bytesToBignum( OUT_PTR BIGNUM *bignum,
						  IN_BUFFER( length ) const BYTE *buffer, 
						  IN_LENGTH_PKC_Z const int length, 
						  const BOOLEAN storeByteString )
	{
	int index = 0, bnStatus = BN_STATUS;
	LOOP_INDEX byteCount, wordIndex;

	assert( isWritePtr( bignum, sizeof( BIGNUM ) ) );
	assert( length == 0 || isReadPtrDynamic( buffer, length ) );

	REQUIRES( sanityCheckBignum( bignum ) );
	REQUIRES( length >= 0 && length <= CRYPT_MAX_PKCSIZE );
	REQUIRES( isBooleanValue( storeByteString ) );

	/* Clear return value */
	BN_clear( bignum );

	/* Bignums with value zero are special-cased since they have a length of
	   zero */
	if( length <= 0 )
		return( CRYPT_OK );

	/* Walk down the bignum a word at a time adding the data bytes */
	bignum->top = ( ( length - 1 ) / BN_BYTES ) + 1;
	LOOP_EXT_REV( ( byteCount = length, wordIndex = bignum->top - 1 ), 
				  byteCount > 0 && wordIndex >= 0, wordIndex--, 
				  BIGNUM_ALLOC_WORDS )
		{
		BN_ULONG value = 0;
		int noBytes = ( ( byteCount - 1 ) % BN_BYTES ) + 1;
		int LOOP_ITERATOR_ALT;

		ENSURES( LOOP_INVARIANT_REV( wordIndex, 0, bignum->top - 1 ) )
		ENSURES( LOOP_INVARIANT_SECONDARY( byteCount, 1, length ) );

		REQUIRES( !checkOverflowSub( byteCount, noBytes ) );
		byteCount -= noBytes;
		LOOP_EXT_REV_CHECKINC_ALT( noBytes > 0, noBytes--, BN_BYTES + 1 )
			{
			ENSURES( LOOP_INVARIANT_EXT_REV_XXX_ALT( noBytes, 1, BN_BYTES,
													 BN_BYTES + 1 ) );

			value = ( value << 8 ) | buffer[ index++ ];
			}
		ENSURES( LOOP_BOUND_EXT_REV_OK_ALT( BN_BYTES + 1 ) );
		bignum->d[ wordIndex ] = value;
		}
	ENSURES( LOOP_BOUND_EXT_REV_OK( BIGNUM_ALLOC_WORDS ) );
	ENSURES( wordIndex == -1 && byteCount == 0 );

	/* Now that we've imported the raw data value, convert it to 
	   normalised form unless we're just storing a byte string */
	if( !storeByteString )
		{
		CK( BN_normalise( bignum ) );
		if( bnStatusError( bnStatus ) )
			return( getBnStatus( bnStatus ) );
		}

	ENSURES( sanityCheckBignum( bignum ) );

	return( CRYPT_OK );
	}

/* Convert a byte string to a BIGNUM value */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int importBignum( INOUT_PTR TYPECAST( BIGNUM * ) struct BN *bignumPtr, 
				  IN_BUFFER( length ) const void *buffer, 
				  IN_LENGTH_SHORT const int length,
				  IN_LENGTH_PKC const int minLength, 
				  IN_LENGTH_PKC const int maxLength,
				  IN_PTR_OPT TYPECAST( BIGNUM * ) \
					const struct BN *maxRangePtr,
				  IN_ENUM_OPT( BIGNUM_CHECK ) \
					const BIGNUM_CHECK_TYPE checkType )
	{
	BIGNUM *bignum = ( BIGNUM * ) bignumPtr;
	BIGNUM *maxRange = ( BIGNUM * ) maxRangePtr;
	int status;

	assert( isWritePtr( bignum, sizeof( BIGNUM ) ) );
	assert( isReadPtrDynamic( buffer, length ) );
	assert( maxRange == NULL || isReadPtr( maxRange, sizeof( BIGNUM ) ) );

	REQUIRES( sanityCheckBignum( bignum ) );
	REQUIRES( isShortIntegerRange( length ) );
	REQUIRES( minLength > 0 && minLength <= maxLength && \
			  maxLength <= CRYPT_MAX_PKCSIZE );
	REQUIRES( maxRange == NULL || sanityCheckBignum( maxRange ) );
	REQUIRES( isEnumRangeOpt( checkType, BIGNUM_CHECK ) );

	/* Make sure that we've been given valid input.  This should already 
	   have been checked by the caller using far more specific checks than 
	   the very generic values that we use here, but we perform the check 
	   anyway just to be sure */
	if( length < 1 )
		return( CRYPT_ERROR_BADDATA );
	switch( checkType )
		{
		case BIGNUM_CHECK_NONE:
		case BIGNUM_CHECK_VALUE:
			/* No specific length check for this value */
			break;

		case BIGNUM_CHECK_VALUE_PKC:
			if( length > CRYPT_MAX_PKCSIZE )
				return( CRYPT_ERROR_BADDATA );
			break;

		case BIGNUM_CHECK_VALUE_ECC:
			if( length > CRYPT_MAX_PKCSIZE_ECC )
				return( CRYPT_ERROR_BADDATA );
			break;

		default:
			retIntError();
		}

	/* Check that the bignum data appears valid if required */
	if( checkType != BIGNUM_CHECK_NONE )
		{
		/* Perform a debug-mode check for suspicious bignum data */
		assert_notest( checkEntropyInteger( buffer, length ) );
		}

	/* Convert the byte string into a bignum */
	status = bytesToBignum( bignum, buffer, length, FALSE );
	if( cryptStatusError( status ) )
		{
		BN_clear( bignum );
		return( status );
		}

	/* Check the bignum value that we've just imported */
	status = checkBignum( bignum, minLength, maxLength, maxRange, 
						  checkType );
	if( cryptStatusError( status ) )
		{
		BN_clear( bignum );
		return( status );
		}

	ENSURES( sanityCheckBignum( bignum ) );

	return( CRYPT_OK );
	}

/* Verify that a bignum value corresponds to encoded bignum data */

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
BOOLEAN verifyBignumImport( TYPECAST( const BIGNUM * ) \
								const struct BN *bignumPtr, 
							IN_BUFFER( length ) const void *buffer, 
							IN_LENGTH_SHORT const int length )
	{
	const BIGNUM *bignum = ( BIGNUM * ) bignumPtr;
	const BYTE *bufPtr = buffer;
	int index = 0;
	LOOP_INDEX byteCount, wordIndex;

	assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );
	assert( isReadPtrDynamic( buffer, length ) );

	REQUIRES_B( sanityCheckBignum( bignum ) );
	REQUIRES_B( isShortIntegerRange( length ) );

	/* Walk down the bignum a word at a time verifying that the data bytes 
	   correspond to the bignum words.  We can't check bignum->top since the
	   bignum is normalised on import, so it may not correspond exactly to 
	   the data length in bytes */
	LOOP_EXT_REV( ( byteCount = length, wordIndex = bignum->top - 1 ), 
				  byteCount > 0 && wordIndex >= 0, wordIndex--, 
				  BIGNUM_ALLOC_WORDS )
		{
		BN_ULONG value = 0;
		int noBytes = ( ( byteCount - 1 ) % BN_BYTES ) + 1;
		int LOOP_ITERATOR_ALT;

		ENSURES_B( LOOP_INVARIANT_REV( wordIndex, 0, bignum->top - 1 ) );
		ENSURES_B( LOOP_INVARIANT_SECONDARY( byteCount, 1, length ) );

		REQUIRES_B( !checkOverflowSub( byteCount, noBytes ) );
		byteCount -= noBytes;
		LOOP_EXT_REV_CHECKINC_ALT( noBytes > 0, noBytes--, BN_BYTES + 1 )
			{
			ENSURES_B( LOOP_INVARIANT_EXT_REV_XXX_ALT( noBytes, 1, BN_BYTES,
													   BN_BYTES + 1 ) );

			value = ( value << 8 ) | bufPtr[ index++ ];
			}
		ENSURES_B( LOOP_BOUND_EXT_REV_OK_ALT( BN_BYTES + 1 ) );
		if( bignum->d[ wordIndex ] != value )
			{
			DEBUG_DIAG(( "Bignum data memory corruption detected at word %d", 
						 wordIndex ));
			return( FALSE );
			}
		}
	ENSURES_B( LOOP_BOUND_OK );
	ENSURES_B( wordIndex == -1 && byteCount == 0 );

	ENSURES_B( sanityCheckBignum( bignum ) );

	return( TRUE );
	}

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

/* Convert a byte string to an X9.62 ECC point */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
int importECCPoint( INOUT_PTR TYPECAST( BIGNUM * ) struct BN *bignumPtr1, 
					INOUT_PTR TYPECAST( BIGNUM * ) struct BN *bignumPtr2, 
				    IN_BUFFER( length ) const void *buffer, 
				    IN_LENGTH_SHORT const int length,
					IN_LENGTH_PKC const int minLength, 
					IN_LENGTH_PKC const int maxLength, 
					IN_LENGTH_PKC const int fieldSize,
					IN_PTR_OPT TYPECAST( const BIGNUM * ) \
						const struct BN *maxRangePtr,
					IN_ENUM( BIGNUM_CHECK ) \
						const BIGNUM_CHECK_TYPE checkType )
	{
	const BYTE *eccPointData = buffer;
	BIGNUM *bignum1 = ( BIGNUM * ) bignumPtr1;
	BIGNUM *bignum2 = ( BIGNUM * ) bignumPtr2;
	BIGNUM *maxRange = ( BIGNUM * ) maxRangePtr;
	int status;

	assert( isWritePtr( bignum1, sizeof( BIGNUM ) ) );
	assert( isWritePtr( bignum2, sizeof( BIGNUM ) ) );
	assert( isReadPtrDynamic( buffer, length ) );
	assert( maxRange == NULL || isReadPtr( maxRange, sizeof( BIGNUM ) ) );

	REQUIRES( sanityCheckBignum( bignum1 ) );
	REQUIRES( sanityCheckBignum( bignum2 ) );
	REQUIRES( minLength > 0 && minLength <= maxLength && \
			  maxLength <= CRYPT_MAX_PKCSIZE_ECC );
	REQUIRES( fieldSize >= MIN_PKCSIZE_ECC && \
			  fieldSize <= CRYPT_MAX_PKCSIZE_ECC );
	REQUIRES( maxRange == NULL || sanityCheckBignum( maxRange ) );
	REQUIRES( isEnumRangeOpt( checkType, BIGNUM_CHECK ) );

	/* Make sure that we've been given valid input.  This should already 
	   have been checked by the caller using far more specific checks than 
	   the very generic values that we use here, but we perform the check 
	   anyway just to be sure */
	if( length < MIN_PKCSIZE_ECCPOINT_THRESHOLD || \
		length > MAX_PKCSIZE_ECCPOINT )
		return( CRYPT_ERROR_BADDATA );

	/* Decode the ECC point data.  At this point we run into another 
	   artefact of the chronic indecisiveness of the ECC standards authors, 
	   because of the large variety of ways in which ECC data can be encoded 
	   there's no single way of representing a point.  The only encoding 
	   that's actually of any practical use is the straight (x, y) 
	   coordinate form with no special-case encoding or other tricks, 
	   denoted by an 0x04 byte at the start of the data:

		+---+---------------+---------------+
		|ID	|		qx		|		qy		|
		+---+---------------+---------------+
			|<- fldSize --> |<- fldSize --> |

	   There's also a hybrid form that combines the patent encumbrance of 
	   point compression with the size of the uncompressed form that we 
	   could in theory allow for, but it's unlikely that anyone's going to
	   be crazy enough to use this */
	if( length != 1 + ( fieldSize * 2 ) )
		return( CRYPT_ERROR_BADDATA );
	if( eccPointData[ 0 ] != 0x04 )
		return( CRYPT_ERROR_BADDATA );
	eccPointData++;		/* Skip format type byte */
	status = importBignum( bignum1, eccPointData, fieldSize,
						   minLength, maxLength, maxRange, checkType );
	if( cryptStatusError( status ) )
		return( status );
	status = importBignum( bignum2, eccPointData + fieldSize, fieldSize, 
						   minLength, maxLength, maxRange, checkType );
	if( cryptStatusError( status ) )
		{
		BN_clear( bignum1 );
		return( status );
		}

	ENSURES( sanityCheckBignum( bignum1 ) );
	ENSURES( sanityCheckBignum( bignum2 ) );

	return( CRYPT_OK );
	}

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
BOOLEAN verifyECCPointImport( TYPECAST( const BIGNUM * ) \
									const struct BN *bignumPtr1,
							  TYPECAST( const BIGNUM * ) \
									const struct BN *bignumPtr2,	 
							  IN_BUFFER( length ) const void *buffer, 
							  IN_LENGTH_SHORT const int length,
							  IN_LENGTH_PKC const int fieldSize )
	{
	const BIGNUM *bignum1 = ( BIGNUM * ) bignumPtr1;
	const BIGNUM *bignum2 = ( BIGNUM * ) bignumPtr2;
	const BYTE *eccPointData = buffer;

	assert( isReadPtr( bignum1, sizeof( BIGNUM ) ) );
	assert( isReadPtr( bignum2, sizeof( BIGNUM ) ) );
	assert( isReadPtrDynamic( buffer, length ) );

	REQUIRES_B( sanityCheckBignum( bignum1 ) );
	REQUIRES_B( sanityCheckBignum( bignum2 ) );
	REQUIRES_B( isShortIntegerRange( length ) );
	REQUIRES_B( fieldSize >= MIN_PKCSIZE_ECC && \
				fieldSize <= CRYPT_MAX_PKCSIZE_ECC );
	REQUIRES_B( 1 + fieldSize + fieldSize <= length );
	
	/* At this point we're merely verifying data that we've decoded 
	   previously to catch any TOCTOU problems so we don't need to do all
	   of the checking that we did when we first decoded it */
	if( eccPointData[ 0 ] != 0x04 )
		return( FALSE );
	if( !verifyBignumImport( bignum1, eccPointData + 1, fieldSize ) )
		return( FALSE );
	if( !verifyBignumImport( bignum2, eccPointData + 1 + fieldSize, 
							 fieldSize ) )
		return( FALSE );

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

#if defined( USE_X25519 ) || defined( USE_ED25519 )

/* Store a byte string in a bignum, needed for the Bernstein special-
   snowflake format which uses fixed-length little-endian byte strings 
   instead of standard bignums */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int import25519ByteString( INOUT_PTR TYPECAST( BIGNUM * ) 
								struct BN *bignumPtr, 
						   IN_BUFFER( length ) const void *buffer, 
						   IN_LENGTH_SHORT_MIN( MIN_PKCSIZE_BERNSTEIN ) \
								const int length )
	{
	BIGNUM *bignum = ( BIGNUM * ) bignumPtr;
	int status;

	assert( isWritePtr( bignum, sizeof( BIGNUM ) ) );
	assert( isReadPtrDynamic( buffer, length ) );

	REQUIRES( sanityCheckBignum( bignum ) );
	REQUIRES( length >= MIN_PKCSIZE_BERNSTEIN && \
			  length <= CRYPT_MAX_PKCSIZE );

	/* Make sure that we've been given valid input.  This should already 
	   have been checked by the caller using far more specific checks than 
	   the very generic values that we use here and is in fact verified by
	   the REQUIRES() statement above, but we perform the check here to
	   document what's being done */
	if( length < MIN_PKCSIZE_BERNSTEIN || length > CRYPT_MAX_PKCSIZE )
		return( CRYPT_ERROR_BADDATA );

	/* Check that the bignum data appears valid unless we're using Ed25519, 
	   for which the self-test values contain said suspicious data values */
#ifndef USE_ED25519
	assert_notest( checkEntropyInteger( buffer, length ) );
#endif /* USE_ED25519 */

	/* Store the byte string as a bignum */
	status = bytesToBignum( bignum, buffer, length, TRUE );
	if( cryptStatusError( status ) )
		{
		BN_clear( bignum );
		return( status );
		}

	ENSURES( sanityCheckBignum( bignum ) );

	return( CRYPT_OK );
	}
#endif /* USE_X25519 || USE_ED25519 */

/****************************************************************************
*																			*
*								Bignum Export Routines 						*
*																			*
****************************************************************************/

/* Export a bignum to a buffer */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int bignumToBytes( OUT_BUFFER( dataMaxLength, *dataLength ) \
							void *data, 
						  IN_LENGTH_SHORT_MIN( 16 ) const int dataMaxLength, 
						  OUT_LENGTH_BOUNDED_Z( dataMaxLength ) \
							int *dataLength,
						  const BIGNUM *bignum )
	{
	BYTE *buffer = data;
	int length, index = 0;
	LOOP_INDEX byteCount, wordIndex;

	assert( isWritePtrDynamic( data, dataMaxLength ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );
	assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );

	REQUIRES( isShortIntegerRangeMin( dataMaxLength, 16 ) );
	REQUIRES( sanityCheckBignum( bignum ) );

	/* Clear return values */
	REQUIRES( isShortIntegerRangeNZ( dataMaxLength ) ); 
	memset( data, 0, min( 16, dataMaxLength ) );
	*dataLength = 0;

	/* Make sure that the result will fit into the output buffer */
	length = BN_num_bytes( bignum );
	ENSURES( length > 0 && length <= CRYPT_MAX_PKCSIZE );
	if( length > dataMaxLength )
		return( CRYPT_ERROR_OVERFLOW );

	/* Walk down the bignum a word at a time extracting the data bytes */
	LOOP_EXT_REV( ( byteCount = length, wordIndex = bignum->top - 1 ),
				  byteCount > 0 && wordIndex >= 0, wordIndex--, 
				  BIGNUM_ALLOC_WORDS )
		{
		const BN_ULONG value = bignum->d[ wordIndex ];
		int noBytes = ( ( byteCount - 1 ) % BN_BYTES ) + 1;
		int LOOP_ITERATOR_ALT;

		ENSURES( LOOP_INVARIANT_REV( wordIndex, 0, bignum->top - 1 ) );
		ENSURES( LOOP_INVARIANT_SECONDARY( byteCount, 1, length ) );

		REQUIRES( !checkOverflowSub( byteCount, noBytes ) );
		byteCount -= noBytes;
		LOOP_EXT_REV_CHECKINC_ALT( noBytes > 0, noBytes--, BN_BYTES + 1 )
			{
			const int shiftAmount = ( noBytes - 1 ) * 8;

			ENSURES( LOOP_INVARIANT_EXT_REV_XXX_ALT( noBytes, 1, BN_BYTES,
													 BN_BYTES + 1 ) );

			buffer[ index++ ] = intToByte( value >> shiftAmount );
			}
		ENSURES( LOOP_BOUND_EXT_REV_OK_ALT( BN_BYTES + 1 ) );
		}
	ENSURES( LOOP_BOUND_OK );
	ENSURES( wordIndex == -1 && byteCount == 0 );

	*dataLength = length;
	return( CRYPT_OK );
	}

/* Convert a BIGNUM value to a byte string */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
int exportBignum( OUT_BUFFER( dataMaxLength, *dataLength ) void *data, 
				  IN_LENGTH_SHORT_MIN( 16 ) const int dataMaxLength, 
				  OUT_LENGTH_BOUNDED_Z( dataMaxLength ) int *dataLength,
				  IN_PTR TYPECAST( BIGNUM * ) const struct BN *bignumPtr )
	{
	const BIGNUM *bignum = ( BIGNUM * ) bignumPtr;
	int length, status;

	assert( isWritePtrDynamic( data, dataMaxLength ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );
	assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );

	REQUIRES( isShortIntegerRangeMin( dataMaxLength, 16 ) );
	REQUIRES( sanityCheckBignum( bignum ) );

	/* Clear return values */
	REQUIRES( isShortIntegerRangeNZ( dataMaxLength ) ); 
	memset( data, 0, min( 16, dataMaxLength ) );
	*dataLength = 0;

	/* Export the bignum into the output buffer */
	status = bignumToBytes( data, dataMaxLength, &length, bignum );
	if( cryptStatusError( status ) )
		{
		zeroise( data, dataMaxLength );
		return( status );
		}
	*dataLength = length;

	return( CRYPT_OK );
	}

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

/* Convert an ECC point to an X9.62 byte string */

CHECK_RETVAL STDC_NONNULL_ARG( ( 3, 4, 5 ) ) \
int exportECCPoint( OUT_BUFFER_OPT( dataMaxLength, *dataLength ) void *data, 
					IN_LENGTH_SHORT_Z const int dataMaxLength, 
					OUT_LENGTH_BOUNDED_PKC_Z( dataMaxLength ) int *dataLength,
					IN_PTR TYPECAST( BIGNUM * ) const struct BN *bignumPtr1, 
					IN_PTR TYPECAST( BIGNUM * ) const struct BN *bignumPtr2,
					IN_LENGTH_PKC const int fieldSize )
	{
	const BIGNUM *bignum1 = ( BIGNUM * ) bignumPtr1;
	const BIGNUM *bignum2 = ( BIGNUM * ) bignumPtr2;
	BYTE *bufPtr = data;
	int length, dummy, status;

	assert( data == NULL || isWritePtrDynamic( data, dataMaxLength ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );
	assert( isReadPtr( bignum1, sizeof( BIGNUM ) ) );
	assert( isReadPtr( bignum2, sizeof( BIGNUM ) ) );

	REQUIRES( ( data == NULL && dataMaxLength == 0 ) || \
			  ( data != NULL && \
				isShortIntegerRangeMin( dataMaxLength, 16 ) ) );
	REQUIRES( sanityCheckBignum( bignum1 ) );
	REQUIRES( sanityCheckBignum( bignum2 ) );
	REQUIRES( fieldSize >= MIN_PKCSIZE_ECC && \
			  fieldSize <= CRYPT_MAX_PKCSIZE_ECC );

	/* Clear return values */
	if( data != NULL )
		{
		REQUIRES( isShortIntegerRangeNZ( dataMaxLength ) ); 
		memset( data, 0, min( 16, dataMaxLength ) );
		}
	*dataLength = 0;

	/* If it's just an encoding-length check then there's nothing to do */
	if( data == NULL )
		{
		*dataLength = 1 + ( fieldSize * 2 );
		return( CRYPT_OK );
		}

	/* Make sure that the result will fit into the output buffer */
	if( 1 + ( fieldSize * 2 ) > dataMaxLength )
		return( CRYPT_ERROR_OVERFLOW );

	/* Encode the ECC point data, which consists of the Q coordinates 
	   stuffed into a byte string with an encoding-specifier byte 0x04 at 
	   the start:

		+---+---------------+---------------+
		|ID	|		qx		|		qy		|
		+---+---------------+---------------+
			|<-- fldSize -> |<- fldSize --> | 
			
	   Because the data is encoded in fixed-length form we also need to add
	   leading-zero padding if required:
	   
			|<-- fldSize -->|
			+---------------+
			|0000	qx		|
			+---------------+
				 |<-- len ->|
	   
	   which we do by zeroing the entire data block and then writing
	   length = BN_num_bytes() of bignum data starting at offset
	   fieldSize - length */
	*bufPtr++ = 0x04;
	REQUIRES( rangeCheck( fieldSize, MIN_PKCSIZE_ECC, 
						  CRYPT_MAX_PKCSIZE_ECC ) );
	memset( bufPtr, 0, fieldSize * 2 );
	length = BN_num_bytes( bignum1 );
	ENSURES( length > 0 && length <= fieldSize );
	REQUIRES( !checkOverflowSub( fieldSize, length ) );
	status = bignumToBytes( bufPtr + ( fieldSize - length ), length, 
							&dummy, bignum1 );
	if( cryptStatusError( status ) )
		{
		zeroise( data, dataMaxLength );
		return( status );
		}
	bufPtr += fieldSize;
	length = BN_num_bytes( bignum2 );
	ENSURES( length > 0 && length <= fieldSize );
	REQUIRES( !checkOverflowSub( fieldSize, length ) );
	status = bignumToBytes( bufPtr + ( fieldSize - length ), length, 
							&dummy, bignum2 );
	if( cryptStatusError( status ) )
		{
		zeroise( data, dataMaxLength );
		return( status );
		}
	*dataLength = 1 + ( fieldSize * 2 );

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

#if defined( USE_X25519 ) || defined( USE_ED25519 )

/* Extract a byte string from a bignum in the Bernstein special-snowflake 
   format which uses fixed-length little-endian byte strings instead of 
   standard bignums */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 4 ) ) \
int export25519ByteString( OUT_BUFFER( dataMaxLength, *dataLength ) 
								void *data, 
						   IN_LENGTH_SHORT_MIN( MIN_PKCSIZE_BERNSTEIN ) \
								const int dataMaxLength, 
						   OUT_LENGTH_BOUNDED_Z( dataMaxLength ) 
								int *dataLength,
						   IN_PTR TYPECAST( BIGNUM * ) 
								const struct BN *bignumPtr )
	{
	const BIGNUM *bignum = ( BIGNUM * ) bignumPtr;
	BYTE *bufPtr = data;
	int length, dummy, status;

	assert( isWritePtrDynamic( data, dataMaxLength ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );
	assert( isReadPtr( bignum, sizeof( BIGNUM ) ) );

	REQUIRES( isShortIntegerRangeMin( dataMaxLength, 
									  MIN_PKCSIZE_BERNSTEIN ) );
	REQUIRES( sanityCheckBignum( bignum ) );

	/* Clear return values */
	REQUIRES( isShortIntegerRangeNZ( dataMaxLength ) ); 
	memset( data, 0, min( 16, dataMaxLength ) );
	*dataLength = 0;

	/* Make sure that the result will fit into the output buffer.  This 
	   should already have been arranged by the caller and is in fact 
	   verified by the REQUIRES() statement above, but we perform the check 
	   here to document what's being done */
	if( dataMaxLength < 32 )
		return( CRYPT_ERROR_OVERFLOW );

	/* Bignums are stored in normalised form while the Bernstein format uses
	   fixed-length values so we insert any leading zeroes as required */
	memset( bufPtr, 0, 16 );
	length = BN_num_bytes( bignum );
	ENSURES( length > 16 && length <= 32 );
	status = bignumToBytes( bufPtr + ( 32 - length ), length, &dummy, bignum );
	if( cryptStatusError( status ) )
		{
		zeroise( data, dataMaxLength );
		return( status );
		}
	*dataLength = 32;

	return( CRYPT_OK );
	}
#endif /* USE_X25519 || USE_ED25519 */
#endif /* USE_PKC */
