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

#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 "misc/asn1.h"
#endif /* Compiler-specific includes */

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

/* Exit after setting extended error information */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int exitError( INOUT 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( isAttribute( errorLocus ) || \
			  isInternalAttribute( errorLocus ) );
	REQUIRES( errorType > CRYPT_ERRTYPE_NONE && \
			  errorType < CRYPT_ERRTYPE_LAST );
	REQUIRES( cryptStatusError( status ) );

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

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

	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 CONTEXT_INFO *contextInfoPtr,
							   IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
	{
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	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 CONTEXT_INFO *contextInfoPtr,
							  IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE errorLocus )
	{
	assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) );

	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 CONTEXT_INFO *contextInfoPtr,
						 OUT_INT_Z int *valuePtr, 
						 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
	{
	const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo;
	const CONTEXT_TYPE contextType = contextInfoPtr->type;
	int value;

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

	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );

	/* 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 = ( 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;

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

		case CRYPT_CTXINFO_BLOCKSIZE:
			if( contextType == CONTEXT_CONV && \
				( contextInfoPtr->ctxConv->mode == CRYPT_MODE_CFB || \
				  contextInfoPtr->ctxConv->mode == CRYPT_MODE_OFB ) )
				*valuePtr = 1;	/* Block cipher in stream mode */
			else
				*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 = ( contextInfoPtr->flags & CONTEXT_FLAG_PERSISTENT ) ? \
						TRUE : FALSE;
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_KEYFEATURES:
			REQUIRES( contextType == CONTEXT_PKC );

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

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

	retIntError();
	}

/* Get a string attribute */

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

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

	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );

	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 ) );
				case CRYPT_MODE_CFB:
					return( attributeCopy( msgData, "CFB", 3 ) );
				case CRYPT_MODE_OFB:
					return( attributeCopy( msgData, "OFB", 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( contextInfoPtr->capabilityInfo->cryptAlgo ) )
				return( CRYPT_ERROR_NOTAVAIL );
			if( !( 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( !( contextInfoPtr->flags & CONTEXT_FLAG_HASH_INITED ) )
				return( CRYPT_ERROR_NOTINITED );
			if( !( 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 );

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

		case CRYPT_IATTRIBUTE_KEYID_PGP2:
			REQUIRES( contextType == CONTEXT_PKC );

			if( contextInfoPtr->capabilityInfo->cryptAlgo != CRYPT_ALGO_RSA )
				return( CRYPT_ERROR_NOTFOUND );
			return( attributeCopy( msgData, contextInfoPtr->ctxPKC->pgp2KeyID,
								   PGP_KEYID_SIZE ) );

		case CRYPT_IATTRIBUTE_KEYID_OPENPGP:
			REQUIRES( contextType == CONTEXT_PKC );
			REQUIRES( contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA || \
					  contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_DSA || \
					  contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_ELGAMAL );

			return( attributeCopy( msgData, contextInfoPtr->ctxPKC->openPgpKeyID,
								   PGP_KEYID_SIZE ) );

#ifdef USE_KEA
		case CRYPT_IATTRIBUTE_KEY_KEADOMAINPARAMS:
			REQUIRES( contextType == CONTEXT_PKC );

			return( attributeCopy( msgData, contextInfoPtr->ctxPKC->domainParamPtr,
								   contextInfoPtr->ctxPKC->domainParamSize ) );

		case CRYPT_IATTRIBUTE_KEY_KEAPUBLICVALUE:
			REQUIRES( contextType == CONTEXT_PKC );

			return( attributeCopy( msgData, contextInfoPtr->ctxPKC->publicValuePtr,
								   contextInfoPtr->ctxPKC->publicValueSize ) );
#else
		case CRYPT_IATTRIBUTE_KEY_KEADOMAINPARAMS:
		case CRYPT_IATTRIBUTE_KEY_KEAPUBLICVALUE:
			return( CRYPT_ERROR_NOTFOUND );
#endif /* USE_KEA */

		case CRYPT_IATTRIBUTE_KEY_SPKI:
			REQUIRES( contextType == CONTEXT_PKC && \
					  ( contextInfoPtr->flags & CONTEXT_FLAG_KEY_SET ) );

			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 ) );
				}
			/* Drop through */

		case CRYPT_IATTRIBUTE_KEY_SSH:
		case CRYPT_IATTRIBUTE_KEY_SSH1:
		case CRYPT_IATTRIBUTE_KEY_SSL:
			{
			STREAM stream;
			KEYFORMAT_TYPE formatType;

			REQUIRES( contextType == CONTEXT_PKC && \
					  ( contextInfoPtr->flags & CONTEXT_FLAG_KEY_SET ) );

			/* 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 = contextInfoPtr->ctxPKC->writePublicKeyFunction( &stream,
												contextInfoPtr, formatType, 
												"public_key", 10 );
			if( cryptStatusOK( status ) )
				msgData->length = stell( &stream );
			sMemDisconnect( &stream );
			return( status );
			}

		case CRYPT_IATTRIBUTE_PGPVALIDITY:
			REQUIRES( contextType == CONTEXT_PKC );

			*( ( time_t * ) msgData->data ) = \
									contextInfoPtr->ctxPKC->pgpCreationTime;
			return( CRYPT_OK );
		}

	retIntError();
	}

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

/* Set a numeric/boolean attribute */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int setContextAttribute( INOUT CONTEXT_INFO *contextInfoPtr,
						 IN_INT_Z const int value, 
						 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attribute )
	{
	const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo;
	const CONTEXT_TYPE contextType = contextInfoPtr->type;
	int *valuePtr, status;

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

	REQUIRES( value >= 0 && value < MAX_INTLENGTH );
	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );

	switch( attribute )
		{
		case CRYPT_OPTION_MISC_SIDECHANNELPROTECTION:
			if( value > 0 )
				contextInfoPtr->flags |= CONTEXT_FLAG_SIDECHANNELPROTECTION;
			else
				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 */
			if( contextInfoPtr->ctxConv->mode != \
						( isStreamCipher( capabilityInfoPtr->cryptAlgo ) ? \
						CRYPT_MODE_OFB : CRYPT_MODE_CBC ) )
				return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_MODE ) );

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

		case CRYPT_CTXINFO_KEYSIZE:
			{
			int keySize;

			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;

				default:
					retIntError();
				}
			if( *valuePtr != 0 )
				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 */
			status = adjustUserKeySize( contextInfoPtr, value, &keySize );
			if( cryptStatusError( status ) )
				return( status );
			*valuePtr = ( contextType == CONTEXT_PKC ) ? \
						bytesToBits( keySize ) : keySize;
			return( CRYPT_OK );
			}

		case CRYPT_CTXINFO_KEYING_ALGO:
		case CRYPT_OPTION_KEYING_ALGO:
			{
			CRYPT_ALGO_TYPE *algoValuePtr;

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

			algoValuePtr = ( contextType == CONTEXT_CONV ) ? \
						   &contextInfoPtr->ctxConv->keySetupAlgorithm : \
						   &contextInfoPtr->ctxMAC->keySetupAlgorithm;
			if( *algoValuePtr != CRYPT_ALGO_NONE )
				return( exitErrorInited( contextInfoPtr,
										 CRYPT_CTXINFO_KEYING_ALGO ) );
			*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_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 ) && \
				!( contextInfoPtr->flags & CONTEXT_FLAG_DUMMY ) )
				return( CRYPT_ERROR_PERMISSION );

			/* Set or clear the persistent flag as required */
			if( value != 0 )
				contextInfoPtr->flags |= CONTEXT_FLAG_PERSISTENT;
			else
				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 ) || \
				  ( contextInfoPtr->flags & CONTEXT_FLAG_PERSISTENT ) ) && \
				contextInfoPtr->labelSize <= 0 )
				retIntError();

			/* If the key is held outside the context (e.g. in a device) we
			   need to manually supply the key-related information needed by 
			   the context, which in this case is the key size.  Once this 
			   is set there is (effectively) a key loaded, although the 
			   actual keying values are held anderswhere */
			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;

				default:
					retIntError();
				}
			contextInfoPtr->flags |= CONTEXT_FLAG_KEY_SET;
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_DEVICEOBJECT:
#ifdef USE_DEVICES
			contextInfoPtr->deviceObject = value;
#endif /* USE_DEVICES */
			return( CRYPT_OK );
		}

	retIntError();
	}

/* Set a string attribute */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int setContextAttributeS( INOUT 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 = contextInfoPtr->capabilityInfo;
	const CONTEXT_TYPE contextType = contextInfoPtr->type;
	int status;

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

	REQUIRES( dataLength > 0 && dataLength < MAX_INTLENGTH );
	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );

	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 ) );
				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 ) );
			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:
			REQUIRES( contextType == CONTEXT_CONV || \
					  contextType == CONTEXT_MAC );
			REQUIRES( needsKey( contextInfoPtr ) );

			/* If it's a persistent context we need to have a key label set 
			   before we can continue */
			if( ( 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 = contextInfoPtr->loadKeyFunction( contextInfoPtr,
													  data, dataLength );
			if( cryptStatusOK( status ) )
				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( contextInfoPtr->capabilityInfo->cryptAlgo ) )
				return( CRYPT_ERROR_NOTAVAIL );

			/* Make sure that the data size is valid */
			if( dataLength != capabilityInfoPtr->blockSize )
				return( CRYPT_ARGERROR_NUM1 );

			/* Load the IV */
			return( capabilityInfoPtr->initKeyParamsFunction( 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 );
					}
				assert( !cryptArgError( status ) );
				}
			
			/* Fall through */
			}

		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 */
			memcpy( contextInfoPtr->label, data, dataLength );
			contextInfoPtr->labelSize = dataLength;
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_KEYID_OPENPGP:
			REQUIRES( contextType == CONTEXT_PKC );
			REQUIRES( contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA || \
					  contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_DSA || \
					  contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_ELGAMAL );
			REQUIRES( dataLength == PGP_KEYID_SIZE );

			memcpy( contextInfoPtr->ctxPKC->openPgpKeyID, data, dataLength );
			contextInfoPtr->ctxPKC->openPgpKeyIDSet = TRUE;

			/* 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( contextInfoPtr->capabilityInfo->cryptAlgo != CRYPT_ALGO_RSA )
				memcpy( contextInfoPtr->ctxPKC->pgp2KeyID, 
						contextInfoPtr->ctxPKC->openPgpKeyID, PGP_KEYID_SIZE );
			return( CRYPT_OK );

		case CRYPT_IATTRIBUTE_KEY_SPKI:
		case CRYPT_IATTRIBUTE_KEY_PGP:
		case CRYPT_IATTRIBUTE_KEY_SSH:
		case CRYPT_IATTRIBUTE_KEY_SSH1:
		case CRYPT_IATTRIBUTE_KEY_SSL:
		case CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL:
		case CRYPT_IATTRIBUTE_KEY_PGP_PARTIAL:
			REQUIRES( contextType == CONTEXT_PKC );

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

		case CRYPT_IATTRIBUTE_PGPVALIDITY:
			REQUIRES( contextType == CONTEXT_PKC );

			contextInfoPtr->ctxPKC->pgpCreationTime = *( ( time_t * ) data );
			return( CRYPT_OK );
		}

	retIntError();
	}

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

/* Delete an attribute */

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

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

	REQUIRES( isAttribute( attribute ) || \
			  isInternalAttribute( attribute ) );

	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( contextInfoPtr->capabilityInfo->cryptAlgo ) )
				return( exitErrorNotFound( contextInfoPtr,
										   CRYPT_CTXINFO_IV ) );
			contextInfoPtr->ctxConv->ivLength = \
					contextInfoPtr->ctxConv->ivCount = 0;
			contextInfoPtr->flags &= ~CONTEXT_FLAG_IV_SET;
			return( CRYPT_OK );

		case CRYPT_CTXINFO_LABEL:
			if( contextInfoPtr->labelSize <= 0 )
				return( exitErrorNotFound( contextInfoPtr,
										   CRYPT_CTXINFO_LABEL ) );
			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();
				}
			contextInfoPtr->flags &= ~( CONTEXT_FLAG_HASH_INITED | \
										CONTEXT_FLAG_HASH_DONE );
			return( CRYPT_OK );
		}

	retIntError();
	}
