/****************************************************************************
*																			*
*					cryptlib De-enveloping Information Management			*
*						Copyright Peter Gutmann 1996-2006					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "envelope.h"
  #include "asn1.h"
  #include "asn1_ext.h"
  #include "pgp.h"
#else
  #include "envelope/envelope.h"
  #include "misc/asn1.h"
  #include "misc/asn1_ext.h"
  #include "misc/pgp.h"
#endif /* Compiler-specific includes */

#ifdef USE_ENVELOPES

/****************************************************************************
*																			*
*						Content List Management Functions					*
*																			*
****************************************************************************/

/* Create a content list item */

CONTENT_LIST *createContentListItem( MEMPOOL_STATE memPoolState,
									 const CRYPT_FORMAT_TYPE formatType,
									 const void *object, const int objectSize,
									 const BOOLEAN isSigObject )
	{
	CONTENT_LIST *contentListItem;

	assert( isWritePtr( memPoolState, sizeof( MEMPOOL_STATE ) ) );
	assert( formatType > CRYPT_FORMAT_NONE && \
			formatType < CRYPT_FORMAT_LAST );
	assert( ( object == NULL && objectSize == 0 ) || \
			isReadPtr( object, objectSize ) );

	if( ( contentListItem = getMemPool( memPoolState,
										sizeof( CONTENT_LIST ) ) ) == NULL )
		return( NULL );
	memset( contentListItem, 0, sizeof( CONTENT_LIST ) );
	contentListItem->formatType = formatType;
	contentListItem->object = object;
	contentListItem->objectSize = objectSize;
	if( isSigObject )
		{
		contentListItem->flags = CONTENTLIST_ISSIGOBJ;
		contentListItem->clSigInfo.iSigCheckKey = CRYPT_ERROR;
		contentListItem->clSigInfo.iExtraData = CRYPT_ERROR;
		contentListItem->clSigInfo.iTimestamp = CRYPT_ERROR;
		}

	return( contentListItem );
	}

/* Add an item to the content list */

void appendContentListItem( ENVELOPE_INFO *envelopeInfoPtr,
							CONTENT_LIST *contentListItem )
	{
	CONTENT_LIST *contentListPtr = envelopeInfoPtr->contentList;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( contentListPtr == NULL || \
			isWritePtr( contentListPtr, sizeof( CONTENT_LIST ) ) );

	/* Find the end of the list and add the new item */
	if( contentListPtr != NULL )
		{
		int iterationCount = 0;
		
		while( contentListPtr->next != NULL && \
			   iterationCount++ < FAILSAFE_ITERATIONS_MAX )
			contentListPtr = contentListPtr->next;
		if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
			retIntError_Void();
		}
	insertDoubleListElements( &envelopeInfoPtr->contentList, contentListPtr,
							  contentListItem, contentListItem );
	}

/* Delete a content list */

void deleteContentList( MEMPOOL_STATE memPoolState,
						CONTENT_LIST **contentListHeadPtr )
	{
	CONTENT_LIST *contentListCursor = *contentListHeadPtr;
	int iterationCount = 0;

	assert( isWritePtr( memPoolState, sizeof( MEMPOOL_STATE ) ) );
	assert( isWritePtr( contentListHeadPtr, sizeof( CONTENT_LIST * ) ) );

	while( contentListCursor != NULL && \
		   iterationCount++ < FAILSAFE_ITERATIONS_MAX )
		{
		CONTENT_LIST *contentListItem = contentListCursor;

		contentListCursor = contentListCursor->next;

		/* Destroy any attached objects if necessary */
		if( contentListItem->flags & CONTENTLIST_ISSIGOBJ )
			{
			CONTENT_SIG_INFO *sigInfo = &contentListItem->clSigInfo;

			if( sigInfo->iSigCheckKey != CRYPT_ERROR )
				krnlSendNotifier( sigInfo->iSigCheckKey, IMESSAGE_DECREFCOUNT );
			if( sigInfo->iExtraData != CRYPT_ERROR )
				krnlSendNotifier( sigInfo->iExtraData, IMESSAGE_DECREFCOUNT );
			if( sigInfo->iTimestamp != CRYPT_ERROR )
				krnlSendNotifier( sigInfo->iTimestamp, IMESSAGE_DECREFCOUNT );
			}

		/* Erase and free the object buffer if necessary */
		deleteDoubleListElement( contentListHeadPtr, contentListItem );
		if( contentListItem->object != NULL )
			{
			/* Clear the object.  We have to cheat a bit here with pointer
			   casting, the 'const' indicates that the object data is never 
			   modified while it's in the content list, but it has to be
			   modified at the end when we delete it */
			zeroise( ( void * ) contentListItem->object, 
					 contentListItem->objectSize );
			clFree( "deleteContentList", ( void * ) contentListItem->object );
			}
		zeroise( contentListItem, sizeof( CONTENT_LIST ) );
		freeMemPool( memPoolState, contentListItem );
		}
	if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
		retIntError_Void();
	}

/****************************************************************************
*																			*
*						Process Additional Envelope Information 			*
*																			*
****************************************************************************/

/* Process timestamps */

static int processTimestamp( CONTENT_LIST *contentListPtr, 
							 const void *timestamp, 
							 const int timestampLength )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	MESSAGE_DATA msgData;
	const int bufSize = max( timestampLength + 128, MIN_BUFFER_SIZE );
	int status;

	assert( isWritePtr( contentListPtr, sizeof( CONTENT_LIST ) ) );
	assert( isReadPtr( timestamp, timestampLength ) );

	/* Create an envelope to contain the timestamp data */
	setMessageCreateObjectInfo( &createInfo, CRYPT_FORMAT_AUTO );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_ENVELOPE );
	if( cryptStatusError( status ) )
		return( status );
	krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
					 ( void * ) &bufSize, CRYPT_ATTRIBUTE_BUFFERSIZE );

	/* Push in the timestamp data */
	setMessageData( &msgData, ( void * ) timestamp, timestampLength );
	status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_ENV_PUSHDATA,
							  &msgData, 0 );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, NULL, 0 );
		status = krnlSendMessage( createInfo.cryptHandle,
								  IMESSAGE_ENV_PUSHDATA, &msgData, 0 );
		}
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		return( status );
		}

	/* We've got the timestamp info in a sub-envelope, remember it for
	   later */
	contentListPtr->clSigInfo.iTimestamp = createInfo.cryptHandle;
	return( CRYPT_OK );
	}

/* Process CMS unauthenticated attributes.  We can't handle these as
   standard CMS attributes since the only thing that we're likely to see 
   here is a countersignature, which isn't an attribute in the normal 
   sense */

static int processUnauthAttributes( CONTENT_LIST *contentListPtr,
									const void *unauthAttr,
									const int unauthAttrLength )
	{
	STREAM stream;
	int iterationCount = 0, status;

	assert( isWritePtr( contentListPtr, sizeof( CONTENT_LIST ) ) );
	assert( isReadPtr( unauthAttr, unauthAttrLength ) );

	/* Make sure that the unauthenticated attributes are OK.  Normally this
	   is done when we import the attributes, but since we can't import
	   them we have to perform the check explicitly here */
	status = checkObjectEncoding( unauthAttr, unauthAttrLength );
	if( cryptStatusError( status ) )
		return( status );

	/* Process each attribute */
	sMemConnect( &stream, unauthAttr, unauthAttrLength );
	status = readConstructed( &stream, NULL, 1 );
	while( cryptStatusOK( status ) && \
		   sMemDataLeft( &stream ) > MIN_CRYPT_OBJECTSIZE && \
		   iterationCount++ < FAILSAFE_ITERATIONS_LARGE )
		{
		BYTE oid[ MAX_OID_SIZE + 8 ];
		int oidLength, length;

		/* See what we've got */
		readSequence( &stream, NULL );
		status = readEncodedOID( &stream, oid, &oidLength, MAX_OID_SIZE,
								 BER_OBJECT_IDENTIFIER );
		if( cryptStatusOK( status ) )
			status = readSet( &stream, &length );
		if( cryptStatusError( status ) )
			break;
		if( length < MIN_CRYPT_OBJECTSIZE )
			return( CRYPT_ERROR_UNDERFLOW );
		if( length > sMemDataLeft( &stream ) )
			return( CRYPT_ERROR_OVERFLOW );
		if( oidLength == sizeofOID( OID_TSP_TSTOKEN ) && \
			!memcmp( oid, OID_TSP_TSTOKEN, oidLength ) )
			{
			/* We've got a timestamp.  We can't really do much with this at
			   the moment since although it quacks like a countersignature,
			   in the PKIX tradition it's subtly (and gratuitously)
			   incompatible in various ways so that it can't be verified as
			   a standard countersignature.  Amusingly, the RFC actually
			   states that this is a stupid way to do things.  Specifically,
			   instead of using the normal MUST/SHOULD it first states that
			   the sensible solution to the problem is to use a
			   countersignature, and then goes on to mandate something that
			   isn't a countersignature.  Since this isn't the sensible
			   solution, it's obviously the stupid one.  QED */
			status = processTimestamp( contentListPtr, sMemBufPtr( &stream ), 
									   length );
			if( cryptStatusError( status ) )
				break;

			/* Drop through */
			}

		/* It's something that we don't recognise, skip it and continue */
		status = readUniversal( &stream );
		continue;
		}
	if( iterationCount >= FAILSAFE_ITERATIONS_LARGE )
		retIntError();
	sMemDisconnect( &stream );

	return( status );
	}

/* Import a wrapped session key */

static int importSessionKey( ENVELOPE_INFO *envelopeInfoPtr,
							 const CONTENT_LIST *contentListPtr,
							 const CRYPT_CONTEXT iImportContext,
							 CRYPT_CONTEXT *iSessionKeyContext )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	CONTENT_LIST *sessionKeyInfoPtr;
	int iterationCount = 0, status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( isReadPtr( contentListPtr, sizeof( CONTENT_LIST ) ) );
	assert( isHandleRangeValid( iImportContext ) );
	assert( isWritePtr( iSessionKeyContext, sizeof( CRYPT_CONTEXT ) ) );

	/* Clear the return value */
	*iSessionKeyContext = CRYPT_ERROR;

	/* PGP doesn't provide separate session key information with the
	   encrypted data but wraps it up alongside the encrypted key, so we
	   can't import the wrapped key into a context via the standard key
	   import functions but instead have to create the context as part of
	   the unwrap process */
	if( contentListPtr->formatType == CRYPT_FORMAT_PGP )
		return( iCryptImportKeyEx( contentListPtr->object,
								   contentListPtr->objectSize,
								   CRYPT_FORMAT_PGP, iImportContext,
								   CRYPT_UNUSED, iSessionKeyContext ) );

	/* Look for the information required to recreate the session key context */
	for( sessionKeyInfoPtr = envelopeInfoPtr->contentList;
		 sessionKeyInfoPtr != NULL && \
			sessionKeyInfoPtr->envInfo != CRYPT_ENVINFO_SESSIONKEY && \
			iterationCount++ < FAILSAFE_ITERATIONS_MAX;
		 sessionKeyInfoPtr = sessionKeyInfoPtr->next );
	if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
		retIntError();
	if( sessionKeyInfoPtr == NULL )
		/* We need to read more data before we can recreate the session key */
		return( CRYPT_ERROR_UNDERFLOW );

	/* Create the session key context and import the encrypted session key */
	setMessageCreateObjectInfo( &createInfo,
								sessionKeyInfoPtr->clEncrInfo.cryptAlgo );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
							  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
							  OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		return( status );
	status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
							  &sessionKeyInfoPtr->clEncrInfo.cryptMode,
							  CRYPT_CTXINFO_MODE );
	if( cryptStatusOK( status ) )
		status = iCryptImportKeyEx( contentListPtr->object,
									contentListPtr->objectSize,
									contentListPtr->formatType,
									iImportContext, createInfo.cryptHandle,
									NULL );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		return( status );
		}
	*iSessionKeyContext = createInfo.cryptHandle;
	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Add De-enveloping Information 					*
*																			*
****************************************************************************/

/* Add signature verification information */

static int addSignatureInfo( ENVELOPE_INFO *envelopeInfoPtr,
							 CONTENT_LIST *contentListPtr,
							 const CRYPT_HANDLE sigCheckContext,
							 const BOOLEAN isExternalKey )
	{
	CONTENT_SIG_INFO *sigInfo = &contentListPtr->clSigInfo;
	ACTION_LIST *actionListPtr;
	int iterationCount = 0, status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( isWritePtr( contentListPtr, sizeof( CONTENT_LIST ) ) );
	assert( isHandleRangeValid( sigCheckContext ) );

	/* If we've already processed this entry, return the cached processing 
	   result */
	if( contentListPtr->flags & CONTENTLIST_PROCESSED )
		return( sigInfo->processingResult );

	/* Find the hash action that we need to check this signature */
	for( actionListPtr = envelopeInfoPtr->actionList;
		 actionListPtr != NULL && iterationCount++ < FAILSAFE_ITERATIONS_MAX;
		 actionListPtr = actionListPtr->next )
		{
		int cryptAlgo;

		/* Check to see if it's the one that we want */
		if( cryptStatusOK( \
				krnlSendMessage( actionListPtr->iCryptHandle,
								 IMESSAGE_GETATTRIBUTE, &cryptAlgo,
								 CRYPT_CTXINFO_ALGO ) ) && \
			cryptAlgo == sigInfo->hashAlgo )
			break;
		}
	if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
		retIntError();

	/* If we can't find a hash action to match this signature, return a bad 
	   signature error since something must have altered the algorithm ID 
	   for the hash */
	if( actionListPtr == NULL || actionListPtr->action != ACTION_HASH )
		{
		contentListPtr->flags |= CONTENTLIST_PROCESSED;
		sigInfo->processingResult = CRYPT_ERROR_SIGNATURE;
		return( CRYPT_ERROR_SIGNATURE );
		}

	/* Check the signature */
	if( contentListPtr->formatType == CRYPT_FORMAT_CMS )
		{
		/* If it's CMS signed data then the sig.check key should be included 
		   with the signed data as a cert chain, however it's possible 
		   (though unlikely) that the certs may be unrelated to the 
		   signature, in which case the caller will have provided the sig.
		   check key from an external source */
		status = iCryptCheckSignatureEx( contentListPtr->object,
								contentListPtr->objectSize, CRYPT_FORMAT_CMS,
								( sigInfo->iSigCheckKey == CRYPT_ERROR ) ? \
									sigCheckContext : sigInfo->iSigCheckKey,
								actionListPtr->iCryptHandle,
								&sigInfo->iExtraData );

		/* If there are authenticated attributes present we have to perform 
		   an extra check here to make sure that the content-type specified 
		   in the authenticated attributes matches the actual data content 
		   type */
		if( cryptStatusOK( status ) && sigInfo->iExtraData != CRYPT_ERROR )
			{
			int contentType;

			status = krnlSendMessage( sigInfo->iExtraData,
									  IMESSAGE_GETATTRIBUTE, &contentType,
									  CRYPT_CERTINFO_CMS_CONTENTTYPE );
			if( cryptStatusError( status ) || \
				envelopeInfoPtr->contentType != contentType )
				status = CRYPT_ERROR_SIGNATURE;
			}

		/* If there are unauthenticated attributes present, process them.  
		   We don't record the processing status for these to ensure that 
		   some random error in the non signature-related attributes doesn't 
		   invalidate an otherwise OK signature */
		if( cryptStatusOK( status ) && sigInfo->extraData2 != NULL )
			processUnauthAttributes( contentListPtr, sigInfo->extraData2,
									 sigInfo->extraData2Length );
		}
	else
		{
		status = iCryptCheckSignatureEx( contentListPtr->object,
								contentListPtr->objectSize,
								contentListPtr->formatType, sigCheckContext,
								actionListPtr->iCryptHandle, NULL );

		/* If it's a format that includes signing key info, remember the key 
		   that was used to check the signature in case the user wants to 
		   query it later */
		if( contentListPtr->formatType != CRYPT_FORMAT_PGP )
			{
			krnlSendNotifier( sigCheckContext, IMESSAGE_INCREFCOUNT );
			sigInfo->iSigCheckKey = sigCheckContext;
			if( isExternalKey )
				contentListPtr->flags |= CONTENTLIST_EXTERNALKEY;
			}
		}

	/* Remember the processing result so that we don't have to repeat the 
	   processing if queried again.  Since we don't need the encoded 
	   signature data any more after this point, we free it to make the 
	   memory available for reuse */
	clFree( "addSignatureInfo", ( void * ) contentListPtr->object );
	contentListPtr->object = NULL;
	contentListPtr->objectSize = 0;
	contentListPtr->flags |= CONTENTLIST_PROCESSED;
	sigInfo->processingResult = cryptArgError( status ) ? \
								CRYPT_ERROR_SIGNATURE : status;
	return( status );
	}

/* Add a password for decryption of a private key */

static int addPrivkeyPasswordInfo( ENVELOPE_INFO *envelopeInfoPtr,
								   const CONTENT_LIST *contentListPtr,
								   const void *password, 
								   const int passwordLength )
	{
	MESSAGE_KEYMGMT_INFO getkeyInfo;
	int type, status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( isReadPtr( contentListPtr, sizeof( CONTENT_LIST ) ) );
	assert( isReadPtr( password, passwordLength ) );

	/* Make sure that there's a keyset available to pull the key from */
	if( envelopeInfoPtr->iDecryptionKeyset == CRYPT_ERROR )
		{
		setErrorInfo( envelopeInfoPtr, CRYPT_ENVINFO_KEYSET_DECRYPT,
					  CRYPT_ERRTYPE_ATTR_ABSENT );
		return( CRYPT_ERROR_NOTINITED );
		}

	/* Make sure that we're trying to send the password to something for
	   which it makes sense.  Private-key sources aren't just keysets but
	   can also be devices, if we're trying to send a password to a device
	   to get a private key then something's gone wrong, since it should
	   be retrieved automatically on a unlocked device */
	status = krnlSendMessage( envelopeInfoPtr->iDecryptionKeyset,
							  IMESSAGE_GETATTRIBUTE, &type,
							  CRYPT_IATTRIBUTE_TYPE );
	if( cryptStatusError( status ) || type != OBJECT_TYPE_KEYSET )
		{
		/* This one is very difficult to report appropriately, the best that
		   we can do is report the wrong key for this type of object */
		setErrorInfo( envelopeInfoPtr, CRYPT_ENVINFO_KEYSET_DECRYPT,
					  CRYPT_ERRTYPE_ATTR_VALUE );
		return( CRYPT_ERROR_WRONGKEY );
		}

	/* Try and get the key information */
	if( contentListPtr->issuerAndSerialNumber == NULL )
		{
		setMessageKeymgmtInfo( &getkeyInfo,
				( contentListPtr->formatType == CRYPT_FORMAT_PGP ) ? \
				CRYPT_IKEYID_PGPKEYID : CRYPT_IKEYID_KEYID,
				contentListPtr->keyID, contentListPtr->keyIDsize,
				( void * ) password, passwordLength, 
				KEYMGMT_FLAG_USAGE_CRYPT );
		}
	else
		{
		setMessageKeymgmtInfo( &getkeyInfo,
				CRYPT_IKEYID_ISSUERANDSERIALNUMBER,
				contentListPtr->issuerAndSerialNumber,
				contentListPtr->issuerAndSerialNumberSize,
				( void * ) password, passwordLength, 
				KEYMGMT_FLAG_USAGE_CRYPT );
		}
	status = krnlSendMessage( envelopeInfoPtr->iDecryptionKeyset,
							  IMESSAGE_KEY_GETKEY, &getkeyInfo,
							  KEYMGMT_ITEM_PRIVATEKEY );
	if( cryptStatusError( status ) )
		return( status );

	/* We managed to get the private key, push it into the envelope.  If the
	   call succeeds, this will import the session key and delete the 
	   required-information list */
	status = envelopeInfoPtr->addInfo( envelopeInfoPtr, 
									   CRYPT_ENVINFO_PRIVATEKEY,
									   &getkeyInfo.cryptHandle, 0 );
	krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
	return( status );
	}

/* Add a decryption password */

static int addPasswordInfo( ENVELOPE_INFO *envelopeInfoPtr,
							const CONTENT_LIST *contentListPtr,
							const void *password, const int passwordLength,
							CRYPT_CONTEXT *iNewContext )
	{
	const CONTENT_ENCR_INFO *encrInfo = &contentListPtr->clEncrInfo;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	int status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( isReadPtr( contentListPtr, sizeof( CONTENT_LIST ) ) );
	assert( isReadPtr( password, passwordLength ) );
	assert( isWritePtr( iNewContext, sizeof( CRYPT_CONTEXT ) ) );

	/* Create the appropriate encryption context and derive the key into it */
	setMessageCreateObjectInfo( &createInfo, encrInfo->cryptAlgo );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT, 
							  &createInfo, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		return( status );
	status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
							  ( void * ) &encrInfo->cryptMode,
							  CRYPT_CTXINFO_MODE );
	if( cryptStatusOK( status ) )
		{
#ifdef USE_PGP
		if( envelopeInfoPtr->type == CRYPT_FORMAT_PGP )
			{
			status = pgpPasswordToKey( createInfo.cryptHandle, CRYPT_UNUSED,
							password, passwordLength, encrInfo->keySetupAlgo,
							encrInfo->saltOrIVsize > 0 ? encrInfo->saltOrIV : NULL,
							encrInfo->keySetupIterations );
			}
		else
#endif /* USE_PGP */
			{
			MESSAGE_DATA msgData;

			/* Load the derivation information into the context */
			status = krnlSendMessage( createInfo.cryptHandle,
									  IMESSAGE_SETATTRIBUTE,
									  ( void * ) &encrInfo->keySetupIterations,
									  CRYPT_CTXINFO_KEYING_ITERATIONS );
			if( cryptStatusOK( status ) )
				{
				setMessageData( &msgData, ( void * ) encrInfo->saltOrIV,
								encrInfo->saltOrIVsize );
				status = krnlSendMessage( createInfo.cryptHandle,
									IMESSAGE_SETATTRIBUTE_S, &msgData,
									CRYPT_CTXINFO_KEYING_SALT );
				}
			if( cryptStatusOK( status ) )
				{
				setMessageData( &msgData, ( void * ) password, 
								passwordLength );
				status = krnlSendMessage( createInfo.cryptHandle,
									IMESSAGE_SETATTRIBUTE_S, &msgData,
									CRYPT_CTXINFO_KEYING_VALUE );
				}
			}
		}
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		return( status );
		}

	/* Recover the session key using the password context and destroy it 
	   when we're done with it */
	if( envelopeInfoPtr->type != CRYPT_FORMAT_PGP )
		{
		status = importSessionKey( envelopeInfoPtr, contentListPtr,
								   createInfo.cryptHandle, iNewContext );
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		if( cryptStatusError( status ) )
			return( status );
		}
	else
		/* In PGP there isn't any encrypted session key, so the context 
		   created from the password becomes the bulk encryption context */
		*iNewContext = createInfo.cryptHandle;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*					De-enveloping Information Management Functions			*
*																			*
****************************************************************************/

/* Add de-enveloping information to an envelope */

static int addDeenvelopeInfo( ENVELOPE_INFO *envelopeInfoPtr,
							  const CRYPT_ATTRIBUTE_TYPE envInfo,
							  const void *value, const int valueLength )
	{
	CONTENT_LIST *contentListPtr = envelopeInfoPtr->contentListCurrent;
	CRYPT_HANDLE cryptHandle = *( ( CRYPT_HANDLE * ) value ), iNewContext;
	ACTION_RESULT actionResult;
	int status = CRYPT_OK;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( envInfo == CRYPT_IATTRIBUTE_ATTRONLY || \
			( envInfo > CRYPT_ENVINFO_FIRST && \
			  envInfo < CRYPT_ENVINFO_LAST ) );

	/* Perform any final checking and setup */
	if( envInfo != CRYPT_IATTRIBUTE_ATTRONLY && \
		envInfo != CRYPT_ENVINFO_KEYSET_SIGCHECK && \
		envInfo != CRYPT_ENVINFO_KEYSET_ENCRYPT && \
		envInfo != CRYPT_ENVINFO_KEYSET_DECRYPT && \
		envInfo != CRYPT_ENVINFO_HASH )
		{
		/* Since we can add one of a multitude of necessary information 
		   types, we need to check to make sure that what we're adding is 
		   appropriate.  If the caller hasn't tried to read the required 
		   resource information yet, we try to match what's being added to 
		   the first information object of the correct type */
		if( contentListPtr == NULL )
			{
			int iterationCount = 0;
			
			/* Look for the first information object matching the supplied
			   information */
			for( contentListPtr = envelopeInfoPtr->contentList;
				 contentListPtr != NULL && \
					contentListPtr->envInfo != envInfo && \
					iterationCount++ < FAILSAFE_ITERATIONS_MAX;
				 contentListPtr = contentListPtr->next );
			if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
				retIntError();
			if( contentListPtr == NULL && \
				envInfo == CRYPT_ENVINFO_PASSWORD && \
				envelopeInfoPtr->iDecryptionKeyset != CRYPT_ERROR )
				{
				/* If we didn't find a direct match and we've been given a 
				   password, check for a private key that can (potentially)
				   be decrypted using the password.  This requires both a
				   keyset/device to fetch the key from and a private key as a
				   required info type */
				iterationCount = 0;
				for( contentListPtr = envelopeInfoPtr->contentList;
					 contentListPtr != NULL && \
						contentListPtr->envInfo != CRYPT_ENVINFO_PRIVATEKEY && \
						iterationCount++ < FAILSAFE_ITERATIONS_MAX;
					 contentListPtr = contentListPtr->next );
				if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
					retIntError();
				}
			if( contentListPtr == NULL )
				return( CRYPT_ARGERROR_VALUE );
			}
		else
			{
			/* Make sure that the information that we're adding matches the 
			   currently required information object.  The one exception to 
			   this is that we can be passed password information when we 
			   require a private key if the private key is encrypted */
			if( contentListPtr->envInfo != envInfo && \
				!( contentListPtr->envInfo == CRYPT_ENVINFO_PRIVATEKEY && \
				   envInfo == CRYPT_ENVINFO_PASSWORD && \
				   envelopeInfoPtr->iDecryptionKeyset != CRYPT_ERROR ) )
				return( CRYPT_ARGERROR_VALUE );
			}
		
		}

	switch( envInfo )
		{
		case CRYPT_IATTRIBUTE_ATTRONLY:
			/* This is off by default so we should only be turning it on */
			assert( ( *( int * ) value ) == TRUE );

			envelopeInfoPtr->flags |= ENVELOPE_ATTRONLY;
			return( CRYPT_OK );

		case CRYPT_ENVINFO_KEYSET_SIGCHECK:
		case CRYPT_ENVINFO_KEYSET_ENCRYPT:
		case CRYPT_ENVINFO_KEYSET_DECRYPT:
			/* It's keyset information, keep a record of it for later use */
			return( addKeysetInfo( envelopeInfoPtr, envInfo, cryptHandle ) );

		case CRYPT_ENVINFO_HASH:
			{
			ACTION_LIST *actionListItem;

			/* The user is checking a detached signature, remember the hash 
			   for later.  In theory we should also check the state of the 
			   hash context, however PGP requires that it not be completed 
			   (since it needs to hash further data) and everything else 
			   requires that it be completed, but we don't know at this 
			   point whether we're processing PGP or non-PGP data, so we 
			   can't perform any checking here */
			if( envelopeInfoPtr->actionList != NULL )
				{
				/* There's already a hash action present, we can't add 
				   anything further */
				setErrorInfo( envelopeInfoPtr, CRYPT_ENVINFO_HASH,
							  CRYPT_ERRTYPE_ATTR_PRESENT );
				return( CRYPT_ERROR_INITED );
				}

			/* Add the hash as an action list item */
			actionListItem = addAction( &envelopeInfoPtr->actionList,
										envelopeInfoPtr->memPoolState,
										ACTION_HASH, cryptHandle );
			if( actionListItem == NULL )
				return( CRYPT_ERROR_MEMORY );
			return( krnlSendNotifier( cryptHandle, IMESSAGE_INCREFCOUNT ) );
			}

		case CRYPT_ENVINFO_SIGNATURE:
			/* It's a signature object, check the signature and exit */
			return( addSignatureInfo( envelopeInfoPtr, contentListPtr,
									  cryptHandle, !valueLength ) );

		case CRYPT_ENVINFO_PASSWORD:
			/* If we've been given a password and we need private key 
			   information, it's the password required to decrypt the key 
			   so we treat this specially.  This action recursively calls
			   addDeenvelopeInfo() with the processed private key, so we
			   don't have to fall through to the session-key processing
			   code below like the other key-handling actions */
			if( contentListPtr->envInfo == CRYPT_ENVINFO_PRIVATEKEY )
				return( addPrivkeyPasswordInfo( envelopeInfoPtr, contentListPtr,
												value, valueLength ) );

			/* We've been given a standard decryption password, create the 
			   appropriate encryption context for it and derive the key from 
			   the password */
			status = addPasswordInfo( envelopeInfoPtr, contentListPtr,
									  value, valueLength, &iNewContext );
			break;


		case CRYPT_ENVINFO_PRIVATEKEY:
		case CRYPT_ENVINFO_KEY:
			/* Import the session key using the KEK */
			status = importSessionKey( envelopeInfoPtr, contentListPtr,
									   cryptHandle, &iNewContext );
			break;

		case CRYPT_ENVINFO_SESSIONKEY:
			{
			/* If we've been given the session key directly then we must 
			   have reached the encrypted data so we take a copy and set 
			   up the decryption with it */
			const CONTENT_ENCR_INFO *encrInfo = &contentListPtr->clEncrInfo;

			status = initEnvelopeEncryption( envelopeInfoPtr, cryptHandle,
							encrInfo->cryptAlgo, encrInfo->cryptMode,
							encrInfo->saltOrIV, encrInfo->saltOrIVsize, TRUE );
			if( cryptStatusOK( status ) )
				/* The session key context is the newly-created internal 
				   one */
				iNewContext = envelopeInfoPtr->iCryptContext;
			break;
			}

		default:
			assert( NOTREACHED );
			return( CRYPT_ARGERROR_NUM1 );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* We've now got the session key, if we recovered it from a key exchange
	   action (rather than having it passed directly to us by the user), try
	   and set up the decryption */
	assert( isHandleRangeValid( iNewContext ) );
	if( envInfo != CRYPT_ENVINFO_SESSIONKEY )
		{
		int iterationCount = 0;
		
		/* If we got as far as the encrypted data (indicated by the fact 
		   that there's content info present), we can set up the decryption.  
		   If we didn't get this far, it'll be set up by the de-enveloping 
		   code when we reach it */
		for( contentListPtr = envelopeInfoPtr->contentList;
			 contentListPtr != NULL && \
				contentListPtr->envInfo != CRYPT_ENVINFO_SESSIONKEY && \
				iterationCount++ < FAILSAFE_ITERATIONS_MAX;
			 contentListPtr = contentListPtr->next );
		if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
			retIntError();
		if( contentListPtr != NULL )
			{
			const CONTENT_ENCR_INFO *encrInfo = &contentListPtr->clEncrInfo;

			/* We got to the encrypted data, set up the decryption */
			status = initEnvelopeEncryption( envelopeInfoPtr, iNewContext,
						encrInfo->cryptAlgo, encrInfo->cryptMode,
						encrInfo->saltOrIV, encrInfo->saltOrIVsize, FALSE );
			if( cryptStatusError( status ) )
				return( status );
			}
		}

	/* Add the recovered session encryption action to the action list */
	actionResult = checkAction( envelopeInfoPtr->actionList, ACTION_CRYPT,
								iNewContext );
	if( actionResult == ACTION_RESULT_ERROR || \
		actionResult == ACTION_RESULT_INITED )
		{
		setErrorInfo( envelopeInfoPtr, envInfo,
					  CRYPT_ERRTYPE_ATTR_PRESENT );
		return( CRYPT_ERROR_INITED );
		}
	if( addAction( &envelopeInfoPtr->actionList,
				   envelopeInfoPtr->memPoolState, ACTION_CRYPT,
				   iNewContext ) == NULL )
		return( CRYPT_ERROR_MEMORY );

	/* Notify the kernel that the session key context is attached to the
	   envelope.  This is an internal object used only by the envelope so we
	   tell the kernel not to increment its reference count when it attaches
	   it */
	krnlSendMessage( envelopeInfoPtr->objectHandle, IMESSAGE_SETDEPENDENT,
					 &iNewContext, SETDEP_OPTION_NOINCREF );

	/* Destroy the content list, which at this point will contain only (now-
	   irrelevant) key exchange items */
	deleteContentList( envelopeInfoPtr->memPoolState,
					   &envelopeInfoPtr->contentList );
	envelopeInfoPtr->contentList = envelopeInfoPtr->contentListCurrent = NULL;

	/* If the only error was an information required error, we've now
	   resolved the problem and can continue */
	if( envelopeInfoPtr->errorState == CRYPT_ENVELOPE_RESOURCE )
		envelopeInfoPtr->errorState = CRYPT_OK;

	return( status );
	}

/****************************************************************************
*																			*
*							Envelope Access Routines						*
*																			*
****************************************************************************/

void initDenvResourceHandling( ENVELOPE_INFO *envelopeInfoPtr )
	{
	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) && \
			( envelopeInfoPtr->flags & ENVELOPE_ISDEENVELOPE ) );

	/* Set the access method pointers */
	envelopeInfoPtr->addInfo = addDeenvelopeInfo;
	}
#endif /* USE_ENVELOPES */
