/****************************************************************************
*																			*
*				cryptlib Encryption Context Attribute Routines				*
*						Copyright Peter Gutmann 1992-2015					*
*																			*
****************************************************************************/

#define PKC_CONTEXT		/* Indicate that we're working with PKC contexts */
#include "crypt.h"
#ifdef INC_ALL
  #include "context.h"
  #include "asn1.h"
#else
  #include "context/context.h"
  #include "enc_dec/asn1.h"
#endif /* Compiler-specific includes */

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

/* Exit after setting extended error information */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int exitError( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
					  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus,
					  IN_ENUM( CRYPT_ERRTYPE ) const CRYPT_ERRTYPE_TYPE errorType, 
					  IN_ERROR const int status )
	{
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isAttribute( errorLocus ) || \
			  isInternalAttribute( errorLocus ) );
	REQUIRES( isEnumRange( errorType, CRYPT_ERRTYPE ) );
	REQUIRES( cryptStatusError( status ) );

	setObjectErrorInfo( contextInfoPtr, errorLocus, errorType );
	return( status );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int exitErrorInited( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
							IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
	{
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isAttribute( errorLocus ) || \
			  isInternalAttribute( errorLocus ) );

	return( exitError( contextInfoPtr, errorLocus, 
					   CRYPT_ERRTYPE_ATTR_PRESENT, CRYPT_ERROR_INITED ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int exitErrorNotInited( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
							   IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
	{
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isAttribute( errorLocus ) || \
			  isInternalAttribute( errorLocus ) );

	return( exitError( contextInfoPtr, errorLocus, 
					   CRYPT_ERRTYPE_ATTR_ABSENT, CRYPT_ERROR_NOTINITED ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int exitErrorNotFound( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
							  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
	{
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isAttribute( errorLocus ) || \
			  isInternalAttribute( errorLocus ) );

	return( exitError( contextInfoPtr, errorLocus, 
					   CRYPT_ERRTYPE_ATTR_ABSENT, CRYPT_ERROR_NOTFOUND ) );
	}

/****************************************************************************
*																			*
*								Get Attributes								*
*																			*
****************************************************************************/

/* Get a numeric/boolean attribute */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int getContextAttribute( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
						 OUT_INT_Z int *valuePtr, 
						 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
	{
	const CAPABILITY_INFO *capabilityInfoPtr = \
								DATAPTR_GET( contextInfoPtr->capabilityInfo );
	const CONTEXT_TYPE contextType = contextInfoPtr->type;
	int value;

	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
	assert( isWritePtr( valuePtr, sizeof( int ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );
	REQUIRES( capabilityInfoPtr != NULL );

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

	switch( attribute )
		{
		case CRYPT_ATTRIBUTE_ERRORTYPE:
			*valuePtr = contextInfoPtr->errorType;
			return( CRYPT_OK );

		case CRYPT_ATTRIBUTE_ERRORLOCUS:
			*valuePtr = contextInfoPtr->errorLocus;
			return( CRYPT_OK );

		case CRYPT_OPTION_MISC_SIDECHANNELPROTECTION:
			*valuePtr = TEST_FLAG( contextInfoPtr->flags,
								   CONTEXT_FLAG_SIDECHANNELPROTECTION ) ? \
						1 : 0;
						/* This is actually a protection level rather than a 
						   boolean value, although internally it's stored as
						   a boolean flag */
			return( CRYPT_OK );

		case CRYPT_CTXINFO_ALGO:
			*valuePtr = capabilityInfoPtr->cryptAlgo;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_MODE:
			REQUIRES( contextType == CONTEXT_CONV );

			*valuePtr = contextInfoPtr->ctxConv->mode;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_KEYSIZE:
			switch( contextType )
				{
				case CONTEXT_CONV:
					value = contextInfoPtr->ctxConv->userKeyLength;
					break;

				case CONTEXT_PKC:
					value = bitsToBytes( contextInfoPtr->ctxPKC->keySizeBits );
					break;

				case CONTEXT_MAC:
					value = contextInfoPtr->ctxMAC->userKeyLength;
					break;

				case CONTEXT_GENERIC:
					value = contextInfoPtr->ctxGeneric->genericSecretLength;
					break;

				default:
					retIntError();
				}
			if( value <= 0 )
				{
				/* If a key hasn't been loaded yet then we return the 
				   default key size */
				value = capabilityInfoPtr->keySize;
				}
			*valuePtr = value;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_BLOCKSIZE:
#ifdef USE_CFB
			if( contextType == CONTEXT_CONV && \
				contextInfoPtr->ctxConv->mode == CRYPT_MODE_CFB )
				*valuePtr = 1;	/* Block cipher in stream mode */
			else
#endif /* USE_CFB */
				*valuePtr = capabilityInfoPtr->blockSize;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_IVSIZE:
			REQUIRES( contextType == CONTEXT_CONV );

			if( !needsIV( contextInfoPtr->ctxConv->mode ) || \
				isStreamCipher( capabilityInfoPtr->cryptAlgo ) )
				return( CRYPT_ERROR_NOTAVAIL );
			*valuePtr = capabilityInfoPtr->blockSize;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_KEYING_ALGO:
		case CRYPT_OPTION_KEYING_ALGO:
			switch( contextType )
				{
				case CONTEXT_CONV:
					value = contextInfoPtr->ctxConv->keySetupAlgorithm;
					break;

				case CONTEXT_MAC:
					value = contextInfoPtr->ctxMAC->keySetupAlgorithm;
					break;

				default:
					retIntError();
				}
			if( value <= 0 )
				{
				return( exitErrorNotInited( contextInfoPtr,
											CRYPT_CTXINFO_KEYING_ALGO ) );
				}
			*valuePtr = value;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_KEYING_ITERATIONS:
		case CRYPT_OPTION_KEYING_ITERATIONS:
			switch( contextType )
				{
				case CONTEXT_CONV:
					value = contextInfoPtr->ctxConv->keySetupIterations;
					break;

				case CONTEXT_MAC:
					value = contextInfoPtr->ctxMAC->keySetupIterations;
					break;

				default:
					retIntError();
				}
			if( value <= 0 )
				{
				return( exitErrorNotInited( contextInfoPtr,
											CRYPT_CTXINFO_KEYING_ITERATIONS ) );
				}
			*valuePtr = value;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_PERSISTENT:
			*valuePtr = TEST_FLAG( contextInfoPtr->flags, 
								   CONTEXT_FLAG_PERSISTENT ) ? TRUE : FALSE;
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_KEYFEATURES:
			REQUIRES( contextType == CONTEXT_PKC );

			*valuePtr = TEST_FLAG( contextInfoPtr->flags, 
								   CONTEXT_FLAG_PBO ) ? 1 : 0;
#ifdef USE_DEVICES
			*valuePtr |= isHandleRangeValid( contextInfoPtr->deviceObject ) ? 2 : 0;
#endif /* USE_DEVICES */
			return( CRYPT_OK );

#if defined( USE_ECDH ) || defined( USE_ECDSA ) 
		case CRYPT_IATTRIBUTE_KEY_ECCPARAM:
			REQUIRES( contextType == CONTEXT_PKC );

			*valuePtr = contextInfoPtr->ctxPKC->curveType;
			return( CRYPT_OK );
#endif /* USE_ECDH || USE_ECDSA */

		case CRYPT_IATTRIBUTE_DEVICEOBJECT:
#ifdef USE_DEVICES
			if( isHandleRangeValid( contextInfoPtr->deviceObject ) )
				{
				*valuePtr = contextInfoPtr->deviceObject;
				return( CRYPT_OK );
				}
#endif /* USE_DEVICES */
			return( CRYPT_ERROR_NOTFOUND );
		}

	retIntError();
	}

/* Get a string attribute */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int getContextAttributeS( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
						  INOUT_PTR MESSAGE_DATA *msgData, 
						  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
	{
	const CAPABILITY_INFO *capabilityInfoPtr = \
								DATAPTR_GET( contextInfoPtr->capabilityInfo );
	const CONTEXT_TYPE contextType = contextInfoPtr->type;
	int status;

	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
	assert( isWritePtr( msgData, sizeof( MESSAGE_DATA ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );
	REQUIRES( capabilityInfoPtr != NULL );

	switch( attribute )
		{
		case CRYPT_CTXINFO_NAME_ALGO:
			return( attributeCopy( msgData, capabilityInfoPtr->algoName,
								   capabilityInfoPtr->algoNameLen ) );

		case CRYPT_CTXINFO_NAME_MODE:
			REQUIRES( contextType == CONTEXT_CONV );

			switch( contextInfoPtr->ctxConv->mode )
				{
				case CRYPT_MODE_ECB:
					return( attributeCopy( msgData, "ECB", 3 ) );
				case CRYPT_MODE_CBC:
					return( attributeCopy( msgData, "CBC", 3 ) );
#ifdef USE_CFB
				case CRYPT_MODE_CFB:
					return( attributeCopy( msgData, "CFB", 3 ) );
#endif /* USE_CFB */
				case CRYPT_MODE_GCM:
					return( attributeCopy( msgData, "GCM", 3 ) );
				}
			retIntError();

		case CRYPT_CTXINFO_KEYING_SALT:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );

			if( contextType == CONTEXT_CONV )
				{
				if( contextInfoPtr->ctxConv->saltLength <= 0 )
					{
					return( exitErrorInited( contextInfoPtr,
											 CRYPT_CTXINFO_KEYING_SALT ) );
					}
				return( attributeCopy( msgData, contextInfoPtr->ctxConv->salt,
									   contextInfoPtr->ctxConv->saltLength ) );
				}
			if( contextInfoPtr->ctxMAC->saltLength <= 0 )
				{
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_CTXINFO_KEYING_SALT ) );
				}
			return( attributeCopy( msgData, contextInfoPtr->ctxMAC->salt,
								   contextInfoPtr->ctxMAC->saltLength ) );

		case CRYPT_CTXINFO_IV:
			REQUIRES( contextType == CONTEXT_CONV );

			if( !needsIV( contextInfoPtr->ctxConv->mode ) || \
				isStreamCipher( capabilityInfoPtr->cryptAlgo ) )
				return( CRYPT_ERROR_NOTAVAIL );
			if( !TEST_FLAG( contextInfoPtr->flags, CONTEXT_FLAG_IV_SET ) )
				return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_IV ) );
			return( attributeCopy( msgData, contextInfoPtr->ctxConv->iv,
								   contextInfoPtr->ctxConv->ivLength ) );

		case CRYPT_CTXINFO_HASHVALUE:
			REQUIRES( contextType == CONTEXT_HASH || \
					  contextType == CONTEXT_MAC );

			if( !TEST_FLAG( contextInfoPtr->flags, 
							CONTEXT_FLAG_HASH_INITED ) )
				return( CRYPT_ERROR_NOTINITED );
			if( !TEST_FLAG( contextInfoPtr->flags, 
							CONTEXT_FLAG_HASH_DONE ) )
				return( CRYPT_ERROR_INCOMPLETE );
			return( attributeCopy( msgData, ( contextType == CONTEXT_HASH ) ? \
										contextInfoPtr->ctxHash->hash : \
										contextInfoPtr->ctxMAC->mac,
								   capabilityInfoPtr->blockSize ) );

		case CRYPT_CTXINFO_LABEL:
			if( contextInfoPtr->labelSize <= 0 )
				{
				return( exitErrorNotInited( contextInfoPtr,
											CRYPT_CTXINFO_LABEL ) );
				}
			return( attributeCopy( msgData, contextInfoPtr->label,
								   contextInfoPtr->labelSize ) );

		case CRYPT_IATTRIBUTE_KEYID:
			REQUIRES( contextType == CONTEXT_PKC );
			REQUIRES( !isEmptyData( contextInfoPtr->ctxPKC->keyID, 0 ) );

			return( attributeCopy( msgData, contextInfoPtr->ctxPKC->keyID,
								   KEYID_SIZE ) );

#ifdef USE_PGP
		case CRYPT_IATTRIBUTE_KEYID_PGP2:
			REQUIRES( contextType == CONTEXT_PKC );

			if( !TEST_FLAG( contextInfoPtr->ctxPKC->flags, 
							PKCINFO_FLAG_PGPKEYID_SET ) )
				return( CRYPT_ERROR_NOTFOUND );
			return( attributeCopy( msgData, contextInfoPtr->ctxPKC->pgp2KeyID,
								   PGP_KEYID_SIZE ) );

		case CRYPT_IATTRIBUTE_KEYID_OPENPGP:
			REQUIRES( contextType == CONTEXT_PKC );

			if( !TEST_FLAG( contextInfoPtr->ctxPKC->flags, 
							PKCINFO_FLAG_OPENPGPKEYID_SET ) )
				return( CRYPT_ERROR_NOTFOUND );
			return( attributeCopy( msgData, contextInfoPtr->ctxPKC->openPgpKeyID,
								   PGP_KEYID_SIZE ) );
#endif /* USE_PGP */

		case CRYPT_IATTRIBUTE_KEY_SPKI:
		case CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL:
			/* CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL is used to read from dummy
			   contexts used as placeholders for external crypto hardware
			   functionality, these aren't necessarily in the high state as
			   required by a CRYPT_IATTRIBUTE_KEY_SPKI read when they're
			   accessed because the hardware may not be ready yet but we 
			   can still fetch the stored public-key data from them */
			REQUIRES( contextType == CONTEXT_PKC && \
					  !needsKey( contextInfoPtr ) );

			if( contextInfoPtr->ctxPKC->publicKeyInfo != NULL )
				{
				/* If the data is available in pre-encoded form, copy it
				   out */
				return( attributeCopy( msgData, contextInfoPtr->ctxPKC->publicKeyInfo,
									   contextInfoPtr->ctxPKC->publicKeyInfoSize ) );
				}
			STDC_FALLTHROUGH;

		case CRYPT_IATTRIBUTE_KEY_PGP:
		case CRYPT_IATTRIBUTE_KEY_SSH:
		case CRYPT_IATTRIBUTE_KEY_TLS:
		case CRYPT_IATTRIBUTE_KEY_TLS_EXT:
			{
			STREAM stream;
			KEYFORMAT_TYPE formatType;

			REQUIRES( contextType == CONTEXT_PKC && \
					  !needsKey( contextInfoPtr ) );

			/* Write the appropriately-formatted key data from the context */
			status = attributeToFormatType( attribute, &formatType );
			if( cryptStatusError( status ) )
				return( status );
			sMemOpenOpt( &stream, msgData->data, msgData->length );
			status = capabilityInfoPtr->writePublicKeyFunction( &stream, 
								contextInfoPtr, capabilityInfoPtr->cryptAlgo,
								formatType,  "public_key", 10 );
			if( cryptStatusOK( status ) )
				msgData->length = stell( &stream );
			sMemDisconnect( &stream );
			return( status );
			}

#ifdef USE_PGP
		case CRYPT_IATTRIBUTE_PGPVALIDITY:
			REQUIRES( contextType == CONTEXT_PKC );

			*( ( time_t * ) msgData->data ) = \
									contextInfoPtr->ctxPKC->pgpCreationTime;
			return( CRYPT_OK );
#endif /* USE_PGP */

		case CRYPT_IATTRIBUTE_DEVICESTORAGEID:
#if defined( USE_DEVICES ) && ( defined( USE_HARDWARE ) || defined( USE_TPM ) )
			if( contextInfoPtr->deviceStorageIDset )
				{
				return( attributeCopy( msgData, contextInfoPtr->deviceStorageID, 
									   KEYID_SIZE ) );
				}
#endif /* USE_DEVICES && ( USE_HARDWARE || USE_TPM ) */
			return( CRYPT_ERROR_NOTFOUND );

		case CRYPT_IATTRIBUTE_KDFPARAMS:
			REQUIRES( contextType == CONTEXT_GENERIC );

			if( contextInfoPtr->ctxGeneric->kdfParamSize <= 0 )
				return( CRYPT_ERROR_NOTFOUND );
			return( attributeCopy( msgData, 
								   contextInfoPtr->ctxGeneric->kdfParams, 
								   contextInfoPtr->ctxGeneric->kdfParamSize ) );

		case CRYPT_IATTRIBUTE_ENCPARAMS:
			REQUIRES( contextType == CONTEXT_GENERIC );

			if( contextInfoPtr->ctxGeneric->encAlgoParamSize <= 0 )
				return( CRYPT_ERROR_NOTFOUND );
			return( attributeCopy( msgData, 
								   contextInfoPtr->ctxGeneric->encAlgoParams, 
								   contextInfoPtr->ctxGeneric->encAlgoParamSize ) );

		case CRYPT_IATTRIBUTE_MACPARAMS:
			REQUIRES( contextType == CONTEXT_GENERIC );

			if( contextInfoPtr->ctxGeneric->macAlgoParamSize <= 0 )
				return( CRYPT_ERROR_NOTFOUND );
			return( attributeCopy( msgData, 
								   contextInfoPtr->ctxGeneric->macAlgoParams, 
								   contextInfoPtr->ctxGeneric->macAlgoParamSize ) );

		case CRYPT_IATTRIBUTE_ICV:
			REQUIRES( contextType == CONTEXT_CONV );

			if( contextInfoPtr->ctxConv->mode != CRYPT_MODE_GCM )
				return( CRYPT_ERROR_NOTAVAIL );
			return( capabilityInfoPtr->getInfoFunction( CAPABILITY_INFO_ICV, 
											contextInfoPtr, msgData->data,
											msgData->length ) );
		}

	retIntError();
	}

/****************************************************************************
*																			*
*								Set Attributes								*
*																			*
****************************************************************************/

/* Set a numeric/boolean attribute */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int setContextAttribute( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
						 IN_INT_Z const int value, 
						 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
	{
	const CAPABILITY_INFO *capabilityInfoPtr = \
								DATAPTR_GET( contextInfoPtr->capabilityInfo );
	const CONTEXT_TYPE contextType = contextInfoPtr->type;
	int *valuePtr;
#if defined( USE_DH ) || defined( USE_ECDH ) || defined( USE_ECDSA ) 
	int status;
#endif /* USE_DH || USE_ECDH || USE_ECDSA */ 

	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isIntegerRange( value ) || \
			  attribute == CRYPT_IATTRIBUTE_DEVICEOBJECT );
			  /* Some PKCS #11 device handles are most likely disguised
				 pointers so we have to allow for outrageous values for
				 these */
	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );
	REQUIRES( capabilityInfoPtr != NULL );

	switch( attribute )
		{
		case CRYPT_OPTION_MISC_SIDECHANNELPROTECTION:
			if( value > 0 )
				{
				SET_FLAG( contextInfoPtr->flags, 
						  CONTEXT_FLAG_SIDECHANNELPROTECTION );
				}
			else
				{
				CLEAR_FLAG( contextInfoPtr->flags, 
							CONTEXT_FLAG_SIDECHANNELPROTECTION );
				}
			return( CRYPT_OK );

		case CRYPT_CTXINFO_MODE:
			REQUIRES( contextType == CONTEXT_CONV );

			/* If the mode for the context isn't set to the initial default 
			   value, it's already been explicitly set and we can't change 
			   it again.  This isn't quite as straightforward as it seems
			   because the definition of "initial default mode" is a bit
			   vague, for stream ciphers it's (pseudo-)CFB, for block 
			   ciphers it's usually CBC unless we're working with specific 
			   hardware crypto that only supports one mode and that mode 
			   isn't CBC.  For now this only seems to be ECB so we add a 
			   special-case check for ECB-only operation */
			if( isStreamCipher( capabilityInfoPtr->cryptAlgo ) || \
				isSpecialStreamCipher( capabilityInfoPtr->cryptAlgo ) )
				{
				/* It's a stream cipher, the only possible mode is an
				   implicit (pseudo-)CFB so any attempt to change it isn't 
				   valid */
				if( value == CRYPT_MODE_CFB )
					return( CRYPT_OK );
				return( exitError( contextInfoPtr, CRYPT_CTXINFO_MODE,
								   CRYPT_ERRTYPE_ATTR_VALUE, 
								   CRYPT_ERROR_NOTAVAIL ) );
				}
			else
				{
#if 1			/* So far no devices without CBC support have been 
				   encountered, so we always assume a default of CBC */
				if( contextInfoPtr->ctxConv->mode != CRYPT_MODE_CBC )
					{
					return( exitErrorInited( contextInfoPtr, 
											 CRYPT_CTXINFO_MODE ) );
					}
#else
				if( capabilityInfoPtr->encryptCBCFunction != NULL )
					{
					if( contextInfoPtr->ctxConv->mode != CRYPT_MODE_CBC )
						{
						return( exitErrorInited( contextInfoPtr, 
												 CRYPT_CTXINFO_MODE ) );
						}
					}
				else
					{
					/* This algorithm isn't available in CBC mode, the 
					   default will be ECB */
					if( contextInfoPtr->ctxConv->mode != CRYPT_MODE_ECB )
						{
						return( exitErrorInited( contextInfoPtr, 
												 CRYPT_CTXINFO_MODE ) );
						}
					}
#endif
				}

			/* Set the en/decryption mode */
			return( capabilityInfoPtr->initParamsFunction( contextInfoPtr,
											KEYPARAM_MODE, NULL, value ) );

		case CRYPT_CTXINFO_KEYSIZE:
			/* Make sure that the requested size is within range */
			if( value < capabilityInfoPtr->minKeySize || \
				value > capabilityInfoPtr->maxKeySize )
				return( CRYPT_ARGERROR_NUM1 );

			/* Get the location to store the key size and make sure that 
			   it's not already set */
			switch( contextType )
				{
				case CONTEXT_CONV:
					valuePtr = &contextInfoPtr->ctxConv->userKeyLength;
					break;

				case CONTEXT_PKC:
					valuePtr = &contextInfoPtr->ctxPKC->keySizeBits;
					break;

				case CONTEXT_MAC:
					valuePtr = &contextInfoPtr->ctxMAC->userKeyLength;
					break;

				case CONTEXT_GENERIC:
					valuePtr = &contextInfoPtr->ctxGeneric->genericSecretLength;
					break;

				default:
					retIntError();
				}
			if( *valuePtr != 0 )
				{
				/* If this is a no-op add that doesn't change anything then
				   it's not a problem */
				if( *valuePtr == value )
					return( CRYPT_OK );

				/* We can't change the key size once it's already 
				   initialised */
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_CTXINFO_KEYSIZE ) );
				}

			/* Trim the user-supplied value to the correct shape, taking
			   into account various issues such as limitations with the
			   underlying crypto code/hardware and interop problems with 
			   algorithms that allow excessively long keys.  In virtute sunt 
			   multi ascensus.

			   If it's a PKC key then there's nothing further to do, since 
			   the range check above is all that we need.  ECC keys are a 
			   bit complicated because if we're using fixed curve parameters 
			   (which in practice we always do) there are only a small 
			   number of quantised key sizes that we can use, but since some 
			   of those correspond to very odd bit sizes like 521 bits that 
			   can't be specified as an integral byte count what we do in 
			   the ECC code is use the nearest fixed curve parameters equal 
			   to or above the given value, avoiding the need for the caller 
			   to play guessing games as to which byte-count value 
			   corresponds to which curve.
			   
			   For conventional/MAC keys we need to limit the maximum 
			   working key length to a sane size since the other side may 
			   not be able to handle stupidly large keys */
			if( contextType == CONTEXT_PKC )
				*valuePtr = bytesToBits( value );
			else
				*valuePtr = min( value, MAX_WORKING_KEYSIZE );
			return( CRYPT_OK );

		case CRYPT_CTXINFO_BLOCKSIZE:
			REQUIRES( contextType == CONTEXT_HASH || \
					  contextType == CONTEXT_MAC );

			/* Some hash (and corresponding MAC) algorithms have variable-
			   length outputs, in which case the blocksize is user-
			   definable */
			if( capabilityInfoPtr->initParamsFunction == NULL )
				return( CRYPT_ERROR_NOTAVAIL );
			return( capabilityInfoPtr->initParamsFunction( contextInfoPtr,
										KEYPARAM_BLOCKSIZE, NULL, value ) );

		case CRYPT_CTXINFO_KEYING_ALGO:
		case CRYPT_OPTION_KEYING_ALGO:
			{
			CRYPT_ALGO_TYPE *algoValuePtr;

			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );

			/* The kernel only allows (potentially) valid values to be set,
			   but these may be disabled at the algorithm level so we have 
			   to perform an additional check here */
			if( !algoAvailable( value ) )
				{
				return( exitError( contextInfoPtr, attribute,
								   CRYPT_ERRTYPE_ATTR_VALUE, 
								   CRYPT_ERROR_NOTAVAIL ) );
				}

			algoValuePtr = ( contextType == CONTEXT_CONV ) ? \
						   &contextInfoPtr->ctxConv->keySetupAlgorithm : \
						   &contextInfoPtr->ctxMAC->keySetupAlgorithm;
			if( *algoValuePtr != CRYPT_ALGO_NONE )
				return( exitErrorInited( contextInfoPtr, attribute ) );
			*algoValuePtr = value;
			return( CRYPT_OK );
			}

		case CRYPT_CTXINFO_KEYING_ITERATIONS:
		case CRYPT_OPTION_KEYING_ITERATIONS:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );

			valuePtr = ( contextType == CONTEXT_CONV ) ? \
					   &contextInfoPtr->ctxConv->keySetupIterations : \
					   &contextInfoPtr->ctxMAC->keySetupIterations;
			if( *valuePtr )
				{
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_CTXINFO_KEYING_ITERATIONS ) );
				}
			*valuePtr = value;
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_KEYING_ALGO_PARAM:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );

			valuePtr = ( contextType == CONTEXT_CONV ) ? \
					   &contextInfoPtr->ctxConv->keySetupParam : \
					   &contextInfoPtr->ctxMAC->keySetupParam;
			if( *valuePtr )
				{
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_IATTRIBUTE_KEYING_ALGO_PARAM ) );
				}
			*valuePtr = value;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_PERSISTENT:
			/* The is-object-persistent attribute functions as follows:

					 | Software	| Hardware
				-----+----------+-------------------
				PKC	 | R/O (1)	| R/O (2)
				-----+----------+-------------------
				Conv | R/O (1)	| R/W low state (3)
					 |			| R/O high state

			   (1) = Set if stored to or read from a keyset.
			   (2) = Always set.  Private-key objects are automatically
					 created as persistent objects, public-key objects
					 are (transparently) created as software objects since
					 the operation is much faster on the host system than
					 by going via the device.
			   (3) = Can be set in the low state, if set then the object
					 is created as a persistent object in the device.

			   Most of these checks are enforced by the kernel, the one 
			   thing that the ACL language can't specify is the requirement 
			   that a persistent conventional-encryption object be tied to
			   a device, which we do explicitly here */
			if( ( value != 0 ) && \
				!TEST_FLAG( contextInfoPtr->flags, CONTEXT_FLAG_DUMMY ) )
				return( CRYPT_ERROR_PERMISSION );

			/* Set or clear the persistent flag as required */
			if( value != 0 )
				{
				SET_FLAG( contextInfoPtr->flags, 
						  CONTEXT_FLAG_PERSISTENT );
				}
			else
				{
				CLEAR_FLAG( contextInfoPtr->flags, 
							CONTEXT_FLAG_PERSISTENT );
				}
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_KEYSIZE:
			/* If it's a private key context or a persistent context we need 
			   to have a key label set before we can continue */
			if( ( ( contextInfoPtr->type == CONTEXT_PKC ) || \
				  TEST_FLAG( contextInfoPtr->flags, 
							 CONTEXT_FLAG_PERSISTENT ) ) && \
				contextInfoPtr->labelSize <= 0 )
				retIntError();

			/* If the key is held outside the context (e.g. in a device) we
			   may need to manually supply the key-related information 
			   needed by the context if it can't be obtained through other
			   means such as when the SubjectPublicKeyInfo is set */
			switch( contextType )
				{
				case CONTEXT_CONV:
					contextInfoPtr->ctxConv->userKeyLength = value;
					break;

				case CONTEXT_PKC:
					contextInfoPtr->ctxPKC->keySizeBits = bytesToBits( value );
					break;

				case CONTEXT_MAC:
					contextInfoPtr->ctxMAC->userKeyLength = value;
					break;

				case CONTEXT_GENERIC:
					contextInfoPtr->ctxGeneric->genericSecretLength = value;
					break;

				default:
					retIntError();
				}
			return( CRYPT_OK );

#ifdef USE_DH
		case CRYPT_IATTRIBUTE_KEY_DLPPARAM:
			/* TLS 1.3 introduced additional nonstandard DH values that no-
			   one used, or possibly even wanted, alongside the standard 
			   ones, so the caller can signal the use of these alternative 
			   values by setting the LSB of the requested key size */
			status = loadDHparams( contextInfoPtr, value & ~1, 
								   ( value & 1 ) ? TRUE : FALSE );
			if( cryptStatusOK( status ) )
				status = completeKeyLoad( contextInfoPtr, FALSE );
			return( status );
#endif /* USE_DH */

#if defined( USE_ECDH ) || defined( USE_ECDSA ) 
		case CRYPT_IATTRIBUTE_KEY_ECCPARAM:
			status = loadECCparams( contextInfoPtr, value );
			if( cryptStatusOK( status ) )
				status = completeKeyLoad( contextInfoPtr, FALSE );
			return( status );
#endif /* USE_ECDH || USE_ECDSA */

		case CRYPT_IATTRIBUTE_DEVICEOBJECT:
#ifdef USE_DEVICES
			/* Setting the device object means that the crypto functionality 
			   for the context is enabled, which means that it's effectively 
			   in the key-loaded state, however for standard key-loaded
			   operations to be possible certain other preconditions need to 
			   be met, which we check for here */
			REQUIRES( ( contextType == CONTEXT_CONV && \
						isShortIntegerRangeMin( \
							contextInfoPtr->ctxConv->userKeyLength, \
							MIN_KEYSIZE ) ) || \
					  ( contextType == CONTEXT_PKC && \
						isShortIntegerRangeMin( \
							contextInfoPtr->ctxPKC->keySizeBits, \
							MIN_PKCSIZE_ECC ) ) || \
					  ( contextType == CONTEXT_MAC && \
						isShortIntegerRangeMin( \
							contextInfoPtr->ctxMAC->userKeyLength, \
							MIN_KEYSIZE ) ) || \
					  ( contextType == CONTEXT_GENERIC && \
						isShortIntegerRangeMin( \
							contextInfoPtr->ctxGeneric->genericSecretLength, \
							MIN_KEYSIZE ) ) || \
					  ( contextType == CONTEXT_HASH ) );

			/* Remember the reference to the associated crypto functionality
			   in the device and mark the context as having a key set since we're 
			   now using the crypto in the underlying device */
			contextInfoPtr->deviceObject = value;
			SET_FLAG( contextInfoPtr->flags, CONTEXT_FLAG_KEY_SET );
#endif /* USE_DEVICES */
			return( CRYPT_OK );
		}

	retIntError();
	}

/* Set a string attribute */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int setContextAttributeS( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
						  IN_BUFFER( dataLength ) const void *data,
						  IN_LENGTH const int dataLength,
						  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
	{
	const CAPABILITY_INFO *capabilityInfoPtr = \
								DATAPTR_GET( contextInfoPtr->capabilityInfo );
	const CONTEXT_TYPE contextType = contextInfoPtr->type;
	int status;

	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );
	assert( isReadPtrDynamic( data, dataLength ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isIntegerRangeNZ( dataLength ) );
	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );
	REQUIRES( capabilityInfoPtr != NULL );

	switch( attribute )
		{
		case CRYPT_CTXINFO_KEYING_SALT:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );
			REQUIRES( dataLength > 0 && dataLength <= CRYPT_MAX_HASHSIZE );

			if( contextType == CONTEXT_CONV )
				{
				if( contextInfoPtr->ctxConv->saltLength > 0 )
					{
					return( exitErrorInited( contextInfoPtr,
											 CRYPT_CTXINFO_KEYING_SALT ) );
					}
				REQUIRES( rangeCheck( dataLength, 1, CRYPT_MAX_HASHSIZE ) );
				memcpy( contextInfoPtr->ctxConv->salt, data, dataLength );
				contextInfoPtr->ctxConv->saltLength = dataLength;
				return( CRYPT_OK );
				}
			if( contextInfoPtr->ctxMAC->saltLength > 0 )
				{
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_CTXINFO_KEYING_SALT ) );
				}
			REQUIRES( rangeCheck( dataLength, 1, CRYPT_MAX_HASHSIZE ) );
			memcpy( contextInfoPtr->ctxMAC->salt, data, dataLength );
			contextInfoPtr->ctxMAC->saltLength = dataLength;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_KEYING_VALUE:
			return( deriveKey( contextInfoPtr, data, dataLength ) );

		case CRYPT_CTXINFO_KEY:
			{
			const CTX_LOADKEY_FUNCTION loadKeyFunction = \
						( CTX_LOADKEY_FUNCTION ) \
						FNPTR_GET( contextInfoPtr->loadKeyFunction );

			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC || \
					  contextType == CONTEXT_GENERIC );
			REQUIRES( needsKey( contextInfoPtr ) );
			REQUIRES( loadKeyFunction != NULL );

			/* If it's a persistent context we need to have a key label set 
			   before we can continue */
			if( TEST_FLAG( contextInfoPtr->flags, 
						   CONTEXT_FLAG_PERSISTENT ) && \
				contextInfoPtr->labelSize <= 0 )
				{
				return( exitErrorNotInited( contextInfoPtr, 
											CRYPT_CTXINFO_LABEL ) );
				}

			/* The kernel performs a general check on the size of this
			   attribute but doesn't know about context subtype-specific
			   limits so we perform a context-specific check here */
			if( dataLength < capabilityInfoPtr->minKeySize || \
				dataLength > capabilityInfoPtr->maxKeySize )
				return( CRYPT_ARGERROR_NUM1 );

			/* Load the key into the context */
			status = loadKeyFunction( contextInfoPtr, data, dataLength );
			if( cryptStatusOK( status ) )
				SET_FLAG( contextInfoPtr->flags, CONTEXT_FLAG_KEY_SET );
			return( status );
			}

#ifndef USE_FIPS140
		case CRYPT_CTXINFO_KEY_COMPONENTS:
			return( setKeyComponents( contextInfoPtr, data, dataLength ) );
#endif /* !USE_FIPS140 */

		case CRYPT_CTXINFO_IV:
			REQUIRES( contextType == CONTEXT_CONV );

			/* If it's a mode that doesn't use an IV then the load IV 
			   operation is meaningless */
			if( !needsIV( contextInfoPtr->ctxConv->mode ) || \
				isStreamCipher( capabilityInfoPtr->cryptAlgo ) )
				return( CRYPT_ERROR_NOTAVAIL );

			/* Make sure that the data size is valid.  The AEAD modes 
			   require custom IV handling because of the special-snowflake 
			   nature of the algorithms */
			if( isSpecialStreamCipher( capabilityInfoPtr->cryptAlgo ) )
				{
				/* Pseudo-stream-ciphers like Chacha20 have complex 
				   composite IVs that incorporate counters and other
				   information, as well as having no relation to the pseudo-
				   stream block size */
				if( dataLength != 16 )
					return( CRYPT_ARGERROR_NUM1 );
				}
			else
				{
				if( contextInfoPtr->ctxConv->mode == CRYPT_MODE_GCM )
					{
					/* The GCM IV can be either a full IV or a 96-bit value to
					   which a 32-bit counter is prepended by the GCM 
					   implementation */
					if( dataLength < bitsToBytes( 96 ) || \
						dataLength > capabilityInfoPtr->blockSize )
						return( CRYPT_ARGERROR_NUM1 );
					}
				else
					{
					if( dataLength != capabilityInfoPtr->blockSize )
						return( CRYPT_ARGERROR_NUM1 );
					}
				}

			/* Load the IV */
			return( capabilityInfoPtr->initParamsFunction( contextInfoPtr,
										KEYPARAM_IV, data, dataLength ) );

		case CRYPT_CTXINFO_LABEL:
			{
			CRYPT_HANDLE iCryptHandle;

			if( contextInfoPtr->labelSize > 0 )
				{
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_CTXINFO_LABEL ) );
				}

			/* Check any device object that the context is associated with 
			   to ensure that nothing with that label already exists in the
			   device.  For keysets the check for duplicates is performed 
			   when the context is explicitly added to the keyset but with 
			   devices the context will be implicitly created within the 
			   device at some future point (at context creation, on key 
			   load/generation, or at some other point) that depends on the 
			   device.  Because of this we perform a preemptive check for 
			   duplicates to avoid a potentially confusing error condition 
			   at some indeterminate point in the future.  
			   
			   Since objects are typed, we have to check for the three 
			   possible { label, type } combinations.  In theory we could 
			   require that labels are only unique per object type but this 
			   can lead to problems with underlying devices or keysets that 
			   only support a check by label and not by { label, type } 
			   combination.  In addition we can't send the message to the 
			   context because the kernel won't forward this message type 
			   (sending a get-key message to a context doesn't make sense) 
			   so we have to explicitly get the dependent device and then 
			   send the get-key directly to it */
			status = krnlSendMessage( contextInfoPtr->objectHandle,
									  IMESSAGE_GETDEPENDENT, &iCryptHandle, 
									  OBJECT_TYPE_DEVICE );
			if( cryptStatusOK( status ) && \
				( iCryptHandle != SYSTEM_OBJECT_HANDLE ) )
				{
				MESSAGE_KEYMGMT_INFO getkeyInfo;

				setMessageKeymgmtInfo( &getkeyInfo, CRYPT_KEYID_NAME, 
									   data, dataLength, NULL, 0, 
									   KEYMGMT_FLAG_CHECK_ONLY );
				status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_GETKEY, 
										  &getkeyInfo, KEYMGMT_ITEM_SECRETKEY );
				if( cryptStatusError( status ) )
					{
					status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_GETKEY, 
											  &getkeyInfo,
											  KEYMGMT_ITEM_PUBLICKEY );
					}
				if( cryptStatusError( status ) )
					{
					status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_GETKEY, 
											  &getkeyInfo,
											  KEYMGMT_ITEM_PRIVATEKEY );
					}
				if( cryptStatusOK( status ) )
					{
					/* We've found something with this label already 
					   present, we can't use it again */
					return( CRYPT_ERROR_DUPLICATE );
					}

				/* Make sure that the error status wasn't due to a problem 
				   with the getkeyInfo.  We can get a CRYPT_ARGERROR_OBJECT 
				   if the target doesn't support a particular operation, so
				   this error is allowed */
				assert( ( status == CRYPT_ARGERROR_OBJECT ) || \
						!cryptArgError( status ) );
				}
			
			STDC_FALLTHROUGH;
			}

		case CRYPT_IATTRIBUTE_EXISTINGLABEL:
			REQUIRES( dataLength > 0 && dataLength <= CRYPT_MAX_TEXTSIZE );

			/* The difference between CRYPT_CTXINFO_LABEL and 
			   CRYPT_IATTRIBUTE_EXISTINGLABEL is that the latter is used to 
			   set a label for a context that's being instantiated from a 
			   persistent object in a device.  We can't perform the 
			   duplicate-label check in this case because we'll always get a 
			   match for the device object's label */
			if( contextInfoPtr->labelSize > 0 )
				{
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_CTXINFO_LABEL ) );
				}

			/* Set the label */
			REQUIRES( rangeCheck( dataLength, 1, CRYPT_MAX_TEXTSIZE ) );
			memcpy( contextInfoPtr->label, data, dataLength );
			contextInfoPtr->labelSize = dataLength;
			return( CRYPT_OK );

#ifdef USE_PGP
		case CRYPT_IATTRIBUTE_KEYID_OPENPGP:
			REQUIRES( contextType == CONTEXT_PKC );
			REQUIRES( capabilityInfoPtr->cryptAlgo == CRYPT_ALGO_RSA || \
					  capabilityInfoPtr->cryptAlgo == CRYPT_ALGO_DSA || \
					  capabilityInfoPtr->cryptAlgo == CRYPT_ALGO_ELGAMAL || \
					  capabilityInfoPtr->cryptAlgo == CRYPT_ALGO_ECDSA );
			REQUIRES( dataLength == PGP_KEYID_SIZE );

			REQUIRES( rangeCheck( dataLength, 1, PGP_KEYID_SIZE ) );
			memcpy( contextInfoPtr->ctxPKC->openPgpKeyID, data, dataLength );
			SET_FLAG( contextInfoPtr->ctxPKC->flags, 
					  PKCINFO_FLAG_OPENPGPKEYID_SET );

			/* If it's a non-PGP 2.x key type, set the PGP 2.x keyID to the 
			   OpenPGP keyID.  This is necessary because non-PGP 2.x keys can
			   be used with PGP 2.x message formats which would imply the use 
			   of a PGP 2.x keyID except that it's not defined for this key 
			   type */
			if( capabilityInfoPtr->cryptAlgo != CRYPT_ALGO_RSA )
				{
				memcpy( contextInfoPtr->ctxPKC->pgp2KeyID, 
						contextInfoPtr->ctxPKC->openPgpKeyID, PGP_KEYID_SIZE );
				SET_FLAG( contextInfoPtr->ctxPKC->flags, 
						  PKCINFO_FLAG_PGPKEYID_SET );
				}
			return( CRYPT_OK );
#endif /* USE_PGP */

		case CRYPT_IATTRIBUTE_KEY_SPKI:
		case CRYPT_IATTRIBUTE_KEY_PGP:
		case CRYPT_IATTRIBUTE_KEY_SSH:
		case CRYPT_IATTRIBUTE_KEY_TLS:
		case CRYPT_IATTRIBUTE_KEY_TLS_EXT:
		case CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL:
		case CRYPT_IATTRIBUTE_KEY_PGP_PARTIAL:
			REQUIRES( contextType == CONTEXT_PKC );

			return( setEncodedKey( contextInfoPtr, attribute, data, 
								   dataLength ) );

#ifdef USE_PGP
		case CRYPT_IATTRIBUTE_PGPVALIDITY:
			REQUIRES( contextType == CONTEXT_PKC );

			contextInfoPtr->ctxPKC->pgpCreationTime = *( ( time_t * ) data );
			return( CRYPT_OK );
#endif /* USE_PGP */

		case CRYPT_IATTRIBUTE_DEVICESTORAGEID:
#if defined( USE_DEVICES ) && ( defined( USE_HARDWARE ) || defined( USE_TPM ) )
			REQUIRES( dataLength > 0 && dataLength <= KEYID_SIZE );
			memset( contextInfoPtr->deviceStorageID, 0, KEYID_SIZE );
			REQUIRES( rangeCheck( dataLength, 1, KEYID_SIZE ) );
			memcpy( contextInfoPtr->deviceStorageID, data, dataLength );
			contextInfoPtr->deviceStorageIDset = TRUE;
#endif /* USE_DEVICES && ( USE_HARDWARE || USE_TPM ) */
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_KDFPARAMS:
			REQUIRES( rangeCheck( dataLength, 1, CRYPT_MAX_TEXTSIZE ) );
			memcpy( contextInfoPtr->ctxGeneric->kdfParams, data, 
					dataLength );
			contextInfoPtr->ctxGeneric->kdfParamSize = dataLength;

			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_ENCPARAMS:
			REQUIRES( rangeCheck( dataLength, 1, CRYPT_MAX_TEXTSIZE ) );
			memcpy( contextInfoPtr->ctxGeneric->encAlgoParams, data, 
					dataLength );
			contextInfoPtr->ctxGeneric->encAlgoParamSize = dataLength;

			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_MACPARAMS:
			REQUIRES( rangeCheck( dataLength, 1, CRYPT_MAX_TEXTSIZE ) );
			memcpy( contextInfoPtr->ctxGeneric->macAlgoParams, data, 
					dataLength );
			contextInfoPtr->ctxGeneric->macAlgoParamSize = dataLength;

			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_AAD:
			REQUIRES( contextType == CONTEXT_CONV );

			if( contextInfoPtr->ctxConv->mode != CRYPT_MODE_GCM )
				return( CRYPT_ERROR_NOTAVAIL );

			/* Process the AAD */
			return( capabilityInfoPtr->initParamsFunction( contextInfoPtr,
											KEYPARAM_AAD, data, dataLength ) );

		case CRYPT_IATTRIBUTE_REKEY:
			{
			const CTX_LOADKEY_FUNCTION loadKeyFunction = \
						( CTX_LOADKEY_FUNCTION ) \
						FNPTR_GET( contextInfoPtr->loadKeyFunction );

			REQUIRES( contextType == CONTEXT_MAC );
			REQUIRES( !needsKey( contextInfoPtr ) );
			REQUIRES( loadKeyFunction != NULL );

			/* Some special-snowflake algorithms require rekeying on each 
			   message processed.  This attribute is in effect a 
			   CRYPT_CTXINFO_KEY that works on already-initialised contexts.
			   We don't perform all of operations used for CRYPT_CTXINFO_KEY
			   because there's already a key loaded and we're just replacing
			   it with a new one */
			if( capabilityInfoPtr->cryptAlgo != CRYPT_ALGO_POLY1305 )
				return( CRYPT_ERROR_NOTAVAIL );
			if( dataLength < capabilityInfoPtr->minKeySize || \
				dataLength > capabilityInfoPtr->maxKeySize )
				return( CRYPT_ARGERROR_NUM1 );
			status = loadKeyFunction( contextInfoPtr, data, dataLength );
			if( cryptStatusError( status ) )
				return( status );

			/* Since we've now rekeyed the context, we need to reset any in-
			   progress state that was being held for it */
			zeroise( contextInfoPtr->ctxMAC->mac, CRYPT_MAX_HASHSIZE );
			CLEAR_FLAGS( contextInfoPtr->flags, 
						 CONTEXT_FLAG_HASH_INITED | CONTEXT_FLAG_HASH_DONE );

			return( CRYPT_OK );
			}
		}

	retIntError();
	}

/****************************************************************************
*																			*
*								Delete Attributes							*
*																			*
****************************************************************************/

/* Delete an attribute */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int deleteContextAttribute( INOUT_PTR CONTEXT_INFO *contextInfoPtr,
							IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
	{
	const CAPABILITY_INFO *capabilityInfoPtr = \
								DATAPTR_GET( contextInfoPtr->capabilityInfo );
	const CONTEXT_TYPE contextType = contextInfoPtr->type;

	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	REQUIRES( sanityCheckContext( contextInfoPtr ) );
	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );
	REQUIRES( capabilityInfoPtr != NULL );

	switch( attribute )
		{
		case CRYPT_CTXINFO_KEYING_ALGO:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );

			if( contextType == CONTEXT_CONV )
				{
				if( contextInfoPtr->ctxConv->keySetupAlgorithm == CRYPT_ALGO_NONE )
					{
					return( exitErrorNotFound( contextInfoPtr,
											   CRYPT_CTXINFO_KEYING_ALGO ) );
					}
				contextInfoPtr->ctxConv->keySetupAlgorithm = CRYPT_ALGO_NONE;
				return( CRYPT_OK );
				}
			if( contextInfoPtr->ctxMAC->keySetupAlgorithm == CRYPT_ALGO_NONE )
				{
				return( exitErrorNotFound( contextInfoPtr,
										   CRYPT_CTXINFO_KEYING_ALGO ) );
				}
			contextInfoPtr->ctxMAC->keySetupAlgorithm = CRYPT_ALGO_NONE;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_KEYING_ITERATIONS:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );

			if( contextType == CONTEXT_CONV )
				{
				if( contextInfoPtr->ctxConv->keySetupIterations == 0 )
					{
					return( exitErrorNotFound( contextInfoPtr,
											   CRYPT_CTXINFO_KEYING_ITERATIONS ) );
					}
				contextInfoPtr->ctxConv->keySetupIterations = 0;
				return( CRYPT_OK );
				}
			if( contextInfoPtr->ctxMAC->keySetupIterations == 0 )
				{
				return( exitErrorNotFound( contextInfoPtr,
										   CRYPT_CTXINFO_KEYING_ITERATIONS ) );
				}
			contextInfoPtr->ctxMAC->keySetupIterations = 0;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_KEYING_SALT:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );

			if( contextType == CONTEXT_CONV )
				{
				if( contextInfoPtr->ctxConv->saltLength == 0 )
					{
					return( exitErrorNotFound( contextInfoPtr,
											   CRYPT_CTXINFO_KEYING_SALT ) );
					}
				zeroise( contextInfoPtr->ctxConv->salt, CRYPT_MAX_HASHSIZE );
				contextInfoPtr->ctxConv->saltLength = 0;
				return( CRYPT_OK );
				}
			if( contextInfoPtr->ctxMAC->saltLength == 0 )
				{
				return( exitErrorNotFound( contextInfoPtr,
										   CRYPT_CTXINFO_KEYING_SALT ) );
				}
			zeroise( contextInfoPtr->ctxMAC->salt, CRYPT_MAX_HASHSIZE );
			contextInfoPtr->ctxMAC->saltLength = 0;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_IV:
			REQUIRES( contextType == CONTEXT_CONV );

			if( !needsIV( contextInfoPtr->ctxConv->mode ) || \
				isStreamCipher( capabilityInfoPtr->cryptAlgo ) )
				return( exitErrorNotFound( contextInfoPtr,
										   CRYPT_CTXINFO_IV ) );
			contextInfoPtr->ctxConv->ivLength = \
					contextInfoPtr->ctxConv->ivCount = 0;
			CLEAR_FLAG( contextInfoPtr->flags, CONTEXT_FLAG_IV_SET );
			return( CRYPT_OK );

		case CRYPT_CTXINFO_LABEL:
			if( contextInfoPtr->labelSize <= 0 )
				{
				return( exitErrorNotFound( contextInfoPtr,
										   CRYPT_CTXINFO_LABEL ) );
				}
			REQUIRES( isShortIntegerRangeNZ( contextInfoPtr->labelSize ) ); 
			zeroise( contextInfoPtr->label, contextInfoPtr->labelSize );
			contextInfoPtr->labelSize = 0;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_HASHVALUE:
			switch( contextType )
				{
				case CONTEXT_HASH:
					zeroise( contextInfoPtr->ctxHash->hash, CRYPT_MAX_HASHSIZE );
					break;

				case CONTEXT_MAC:
					zeroise( contextInfoPtr->ctxMAC->mac, CRYPT_MAX_HASHSIZE );
					break;

				default:
					retIntError();
				}
			CLEAR_FLAGS( contextInfoPtr->flags, 
						 CONTEXT_FLAG_HASH_INITED | CONTEXT_FLAG_HASH_DONE );
			return( CRYPT_OK );
		}

	retIntError();
	}
