/****************************************************************************
*																			*
*						  cryptlib PKCS #15 Routines						*
*						Copyright Peter Gutmann 1996-2006					*
*																			*
****************************************************************************/

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

/* Each PKCS #15 file can contain information for multiple personalities 
   (although it's extremely unlikely to contain more than one or two), we 
   allow a maximum of MAX_PKCS15_OBJECTS per file in order to discourage 
   them from being used as general-purpose public-key keysets, which they're 
   not supposed to be.  A setting of 32 objects consumes ~4K of memory 
   (32 x ~128), so we choose that as the limit */

#ifdef CONFIG_CONSERVE_MEMORY
  #define MAX_PKCS15_OBJECTS	8
#else
  #define MAX_PKCS15_OBJECTS	32
#endif /* CONFIG_CONSERVE_MEMORY */

#ifdef USE_PKCS15

/* OID information used to read a PKCS #15 file */

static const CMS_CONTENT_INFO FAR_BSS oidInfoPkcs15Data = { 0, 0 };

static const OID_INFO FAR_BSS keyFileOIDinfo[] = {
	{ OID_PKCS15_CONTENTTYPE, CRYPT_OK, &oidInfoPkcs15Data },
	{ NULL, 0 }, { NULL, 0 }
	};

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

/* Get the hash of various certificate name fields */

int getCertID( const CRYPT_HANDLE iCryptHandle, 
			   CRYPT_ATTRIBUTE_TYPE nameType, 
			   BYTE *nameID, const int nameIdMaxLen )
	{
	HASHFUNCTION hashFunction;
	DYNBUF idDB;
	int status;

	assert( isHandleRangeValid( iCryptHandle ) );
	assert( nameType == CRYPT_IATTRIBUTE_SPKI || \
			nameType == CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER || \
			nameType == CRYPT_IATTRIBUTE_SUBJECT || \
			nameType == CRYPT_IATTRIBUTE_ISSUER );
	assert( isWritePtr( nameID, nameIdMaxLen ) );

	/* Get the attribute data and hash algorithm information and hash the 
	   attribute to get the ID */
	status = dynCreate( &idDB, iCryptHandle, nameType );
	if( cryptStatusError( status ) )
		return( status );
	getHashParameters( CRYPT_ALGO_SHA, &hashFunction, NULL );
	hashFunction( NULL, nameID, nameIdMaxLen, dynData( idDB ), 
				  dynLength( idDB ), HASH_ALL );
	dynDestroy( &idDB );
	return( CRYPT_OK );
	}

/* Locate an object based on an ID */

#define matchID( src, srcLen, dest, destLen ) \
		( ( srcLen ) > 0 && ( srcLen ) == ( destLen ) && \
		  !memcmp( ( src ), ( dest ), ( destLen ) ) )

PKCS15_INFO *findEntry( const PKCS15_INFO *pkcs15info,
						const int noPkcs15objects,
						const CRYPT_KEYID_TYPE keyIDtype,
						const void *keyID, const int keyIDlength,
						const int requestedUsage )
	{
	int i;

	assert( isReadPtr( pkcs15info, \
					   sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( noPkcs15objects >= 1 );
	assert( keyIDtype == CRYPT_KEYID_NAME || \
			keyIDtype == CRYPT_KEYID_URI || \
			keyIDtype == CRYPT_IKEYID_KEYID || \
			keyIDtype == CRYPT_IKEYID_PGPKEYID || \
			keyIDtype == CRYPT_IKEYID_ISSUERID || \
			keyIDtype == CRYPT_KEYIDEX_ID || \
			keyIDtype == CRYPT_KEYIDEX_SUBJECTNAMEID );
	assert( ( keyID == NULL && keyIDlength == 0 ) || \
			isReadPtr( keyID, keyIDlength ) );
	assert( ( requestedUsage & KEYMGMT_MASK_USAGEOPTIONS ) != \
			KEYMGMT_MASK_USAGEOPTIONS );

	/* If there's no ID to search on, don't try and do anything.  This can
	   occur when we're trying to build a chain and the necessary chaining
	   data isn't present */
	if( keyID == NULL )
		return( NULL );

	/* Try and locate the appropriate object in the PKCS #15 collection */
	for( i = 0; i < noPkcs15objects; i++ )
		{
		const PKCS15_INFO *pkcs15infoPtr = &pkcs15info[ i ];
		const int compositeUsage = pkcs15infoPtr->pubKeyUsage | \
								   pkcs15infoPtr->privKeyUsage;

		/* If there's no entry at this position, continue */
		if( pkcs15infoPtr->type == PKCS15_SUBTYPE_NONE )
			continue;

		/* If there's an explicit usage requested, make sure that the key 
		   usage matches this.  This can get slightly complex since the 
		   advertised usage isn't necessarily the same as the usage 
		   permitted by the associated cert (PKCS #11 apps are particularly 
		   good at setting bogus usage types) and the overall result can be 
		   further influenced by trusted usage settings, so all that we 
		   check for here is an indicated usage for the key matching the 
		   requested usage */
		if( ( requestedUsage & KEYMGMT_FLAG_USAGE_CRYPT ) && \
			!( compositeUsage & ENCR_USAGE_MASK ) )
			continue;
		if( ( requestedUsage & KEYMGMT_FLAG_USAGE_SIGN ) && \
			!( compositeUsage & SIGN_USAGE_MASK ) )
			continue;

		/* Check for a match based on the ID type */
		switch( keyIDtype )
			{
			case CRYPT_KEYID_NAME:
			case CRYPT_KEYID_URI:
				if( matchID( pkcs15infoPtr->label, pkcs15infoPtr->labelLength,
							 keyID, keyIDlength ) )
					return( ( PKCS15_INFO * ) pkcs15infoPtr );
				break;

			case CRYPT_IKEYID_KEYID:
				if( matchID( pkcs15infoPtr->keyID, pkcs15infoPtr->keyIDlength,
							 keyID, keyIDlength ) )
					return( ( PKCS15_INFO * ) pkcs15infoPtr );
				break;

			case CRYPT_IKEYID_PGPKEYID:
				/* For the PGP keyID we compare both IDs for the reasons 
				   given in the PGP keyset read code */
				if( matchID( pkcs15infoPtr->pgp2KeyID,
							 pkcs15infoPtr->pgp2KeyIDlength, keyID,
							 keyIDlength ) || \
					matchID( pkcs15infoPtr->openPGPKeyID,
							 pkcs15infoPtr->openPGPKeyIDlength, keyID,
							 keyIDlength ) )
					return( ( PKCS15_INFO * ) pkcs15infoPtr );
				break;

			case CRYPT_IKEYID_ISSUERID:
				if( matchID( pkcs15infoPtr->iAndSID,
							 pkcs15infoPtr->iAndSIDlength, keyID,
							 keyIDlength ) )
					return( ( PKCS15_INFO * ) pkcs15infoPtr );
				break;

			case CRYPT_KEYIDEX_ID:
				if( matchID( pkcs15infoPtr->iD, pkcs15infoPtr->iDlength,
							 keyID, keyIDlength ) )
					return( ( PKCS15_INFO * ) pkcs15infoPtr );
				break;

			case CRYPT_KEYIDEX_SUBJECTNAMEID:
				if( matchID( pkcs15infoPtr->subjectNameID,
							 pkcs15infoPtr->subjectNameIDlength, keyID,
							 keyIDlength ) )
					return( ( PKCS15_INFO * ) pkcs15infoPtr );
				break;

			default:
				assert( NOTREACHED );
				return( NULL );
			}
		}

	/* If we're trying to match on the PGP key ID and didn't find anything,
	   retry it using the first PGP_KEYID_SIZE bytes of the object ID.  This
	   is necessary because calculation of the OpenPGP ID requires the
	   presence of data that may not be present in non-PGP keys, so we can't
	   calculate a real OpenPGP ID but have to use the next-best thing */
	if( keyIDtype == CRYPT_IKEYID_PGPKEYID )
		{
		for( i = 0; i < noPkcs15objects; i++ )
			{
			const PKCS15_INFO *pkcs15infoPtr = &pkcs15info[ i ];

			if( pkcs15infoPtr->type != PKCS15_SUBTYPE_NONE && \
				pkcs15infoPtr->iDlength >= PGP_KEYID_SIZE && \
				!memcmp( keyID, pkcs15infoPtr->iD, PGP_KEYID_SIZE ) )
				return( ( PKCS15_INFO * ) pkcs15infoPtr );
			}
		}

	return( NULL );
	}

/* Find a free PKCS #15 entry */

PKCS15_INFO *findFreeEntry( const PKCS15_INFO *pkcs15info,
							const int noPkcs15objects, int *index )
	{
	int i;

	assert( isReadPtr( pkcs15info, \
					   sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( ( index == NULL ) || isWritePtr( index, sizeof( int ) ) );

	/* Clear return value */
	if( index != NULL )
		*index = CRYPT_ERROR;

	for( i = 0; i < noPkcs15objects; i++ )
		if( pkcs15info[ i ].type == PKCS15_SUBTYPE_NONE )
			break;
	if( i >= noPkcs15objects )
		return( NULL );

	if( index != NULL )
		/* Remember the index value (used for enumerating PKCS #15 entries) 
		   for this entry */
		*index = i;
	return( ( PKCS15_INFO * ) &pkcs15info[ i ] );
	}

/* Free object entries */

void pkcs15freeEntry( PKCS15_INFO *pkcs15info )
	{
	assert( isWritePtr( pkcs15info, sizeof( PKCS15_INFO ) ) );

	if( pkcs15info->pubKeyData != NULL )
		{
		zeroise( pkcs15info->pubKeyData, pkcs15info->pubKeyDataSize );
		clFree( "pkcs15freeEntry", pkcs15info->pubKeyData );
		}
	if( pkcs15info->privKeyData != NULL )
		{
		zeroise( pkcs15info->privKeyData, pkcs15info->privKeyDataSize );
		clFree( "pkcs15freeEntry", pkcs15info->privKeyData );
		}
	if( pkcs15info->certData != NULL )
		{
		zeroise( pkcs15info->certData, pkcs15info->certDataSize );
		clFree( "pkcs15freeEntry", pkcs15info->certData );
		}
	if( pkcs15info->dataData != NULL )
		{
		zeroise( pkcs15info->dataData, pkcs15info->dataDataSize );
		clFree( "pkcs15freeEntry", pkcs15info->dataData );
		}
	zeroise( pkcs15info, sizeof( PKCS15_INFO ) );
	}

static void pkcs15Free( PKCS15_INFO *pkcs15info, const int noPkcs15objects )
	{
	int i;

	assert( isWritePtr( pkcs15info, \
						sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( noPkcs15objects >= 1 );

	for( i = 0; i < noPkcs15objects; i++ )
		pkcs15freeEntry( &pkcs15info[ i ] );
	zeroise( pkcs15info, sizeof( PKCS15_INFO ) * noPkcs15objects );
	}

/* Get the PKCS #15 validity information from a certificate */

int getValidityInfo( PKCS15_INFO *pkcs15info,
					 const CRYPT_HANDLE cryptHandle )
	{
	MESSAGE_DATA msgData;
	time_t validFrom, validTo;
	int status;

	assert( isWritePtr( pkcs15info, sizeof( PKCS15_INFO ) ) );
	assert( isHandleRangeValid( cryptHandle ) );

	/* Remember the validity information for later.  Note that we always
	   update the validity (even if it's already set) since we may be
	   replacing an older cert with a newer one */
	setMessageData( &msgData, &validFrom, sizeof( time_t ) );
	status = krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE_S,
							  &msgData, CRYPT_CERTINFO_VALIDFROM );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, &validTo, sizeof( time_t ) );
		status = krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE_S,
								  &msgData, CRYPT_CERTINFO_VALIDTO );
		}
	if( cryptStatusError( status ) )
		return( status );
	if( pkcs15info->validTo > validTo )
		/* There's an existing, newer cert already present, make sure that 
		   we don't try and add the new one */
		return( CRYPT_ERROR_DUPLICATE );
	pkcs15info->validFrom = validFrom;
	pkcs15info->validTo = validTo;
	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Init/Shutdown Functions							*
*																			*
****************************************************************************/

/* A PKCS #15 keyset can contain multiple keys and whatnot, so when we open
   it we parse the contents into memory for later use */

static int initFunction( KEYSET_INFO *keysetInfo, const char *name,
						 const int nameLength,
						 const CRYPT_KEYOPT_TYPE options )
	{
	PKCS15_INFO *pkcs15info;
	STREAM *stream = &keysetInfo->keysetFile->stream;
	long endPos;
	int status;

	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) && \
			keysetInfo->type == KEYSET_FILE && \
			keysetInfo->subType == KEYSET_SUBTYPE_PKCS15 );
	assert( name == NULL && nameLength == 0 );
	assert( options >= CRYPT_KEYOPT_NONE && options < CRYPT_KEYOPT_LAST );

	/* If we're opening an existing keyset, skip the outer header, optional
	   keyManagementInfo, and inner header.  We do this before we perform any
	   setup operations to weed out potential problem files */
	if( options != CRYPT_KEYOPT_CREATE )
		{
		long dataEndPos;

		/* Read the outer header and make sure that the length information is
		   valid.  readCMSheader() reads the version number field at the 
		   start of the content, so we have to adjust the stream position for
		   this when we calculate the data end position */
		status = readCMSheader( stream, keyFileOIDinfo, &dataEndPos, FALSE );
		if( cryptStatusError( status ) )
			return( status );
		if( dataEndPos == CRYPT_UNUSED )
			/* Indefinite length, don't try and go any further (the general
			   length check below will also catch this, but we make the 
			   check explicit here) */
			return( CRYPT_ERROR_BADDATA );
		endPos = ( stell( stream ) - sizeofShortInteger( 0 ) ) + dataEndPos;
		if( dataEndPos < MIN_OBJECT_SIZE || dataEndPos > MAX_INTLENGTH || \
			endPos < 16 + MIN_OBJECT_SIZE || endPos > MAX_INTLENGTH )
			/* Make sure that the length info is sensible */
			return( CRYPT_ERROR_BADDATA );

		/* Skip the key management info if there is any, and read the inner 
		   wrapper */
		if( peekTag( stream ) == MAKE_CTAG( 0 ) )
			readUniversal( stream );
		status = readLongSequence( stream, NULL );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Allocate the PKCS #15 object info */
	if( ( pkcs15info = clAlloc( "initFunction", \
								sizeof( PKCS15_INFO ) * \
								MAX_PKCS15_OBJECTS ) ) == NULL )
		{
		if( options != CRYPT_KEYOPT_CREATE )
			/* Reset the stream position to account for the header info that
			   we've already read */
			sseek( stream, 0 ) ;
		return( CRYPT_ERROR_MEMORY );
		}
	memset( pkcs15info, 0, sizeof( PKCS15_INFO ) * MAX_PKCS15_OBJECTS );
	keysetInfo->keyData = pkcs15info;
	keysetInfo->keyDataSize = sizeof( PKCS15_INFO ) * MAX_PKCS15_OBJECTS;
	keysetInfo->keyDataNoObjects = MAX_PKCS15_OBJECTS;

	/* If this is a newly-created keyset, there's nothing left to do */
	if( options == CRYPT_KEYOPT_CREATE )
		return( CRYPT_OK );

	/* Read all of the keys in the keyset */
	status = readKeyset( &keysetInfo->keysetFile->stream, pkcs15info,
						 MAX_PKCS15_OBJECTS, endPos );
	if( cryptStatusError( status ) )
		{
		pkcs15Free( pkcs15info, MAX_PKCS15_OBJECTS );
		clFree( "initFunction", keysetInfo->keyData );
		keysetInfo->keyData = NULL;
		keysetInfo->keyDataSize = 0;
		if( options != CRYPT_KEYOPT_CREATE )
			sseek( stream, 0 ) ;
		return( status );
		}

	return( CRYPT_OK );
	}

/* Shut down the PKCS #15 state, flushing information to disk if necessary */

static int shutdownFunction( KEYSET_INFO *keysetInfo )
	{
	int status = CRYPT_OK;

	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) && \
			keysetInfo->type == KEYSET_FILE && \
			keysetInfo->subType == KEYSET_SUBTYPE_PKCS15 );

	/* If the contents have been changed, commit the changes to disk.  The
	   STREAM_IOCTL_IOBUFFER ioctl allocates a working I/O buffer for the
	   duration of the flush and disconnects it again after it's complete */
	if( keysetInfo->flags & KEYSET_DIRTY )
		{
		STREAM *stream = &keysetInfo->keysetFile->stream;
		BYTE buffer[ STREAM_BUFSIZE + 8 ];

		sseek( stream, 0 );
		sioctl( stream, STREAM_IOCTL_IOBUFFER, buffer, STREAM_BUFSIZE );
		status = pkcs15Flush( stream, keysetInfo->keyData, 
							  keysetInfo->keyDataNoObjects );
		sioctl( stream, STREAM_IOCTL_IOBUFFER, NULL, 0 );
		if( status == OK_SPECIAL )
			{
			keysetInfo->flags |= KEYSET_EMPTY;
			status = CRYPT_OK;
			}
		}

	/* Free the PKCS #15 object info */
	if( keysetInfo->keyData != NULL )
		{
		pkcs15Free( keysetInfo->keyData, keysetInfo->keyDataNoObjects );
		zeroise( keysetInfo->keyData, keysetInfo->keyDataSize );
		clFree( "shutdownFunction", keysetInfo->keyData );
		}

	return( status );
	}

/****************************************************************************
*																			*
*									Get a Key								*
*																			*
****************************************************************************/

/* Set any optional attributes that may be associated with a key */

static int setKeyAttributes( const CRYPT_HANDLE iCryptHandle,
							 const PKCS15_INFO *pkcs15infoPtr,
							 const int actionFlags )
	{
	MESSAGE_DATA msgData;
	int status = CRYPT_OK;

	assert( isHandleRangeValid( iCryptHandle ) );
	assert( isReadPtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
	assert( ( actionFlags == CRYPT_UNUSED ) || ( actionFlags > 0 ) );

	if( actionFlags != CRYPT_UNUSED )
		status = krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE,
								  ( void * ) &actionFlags,
								  CRYPT_IATTRIBUTE_ACTIONPERMS );
	if( cryptStatusOK( status ) && \
		pkcs15infoPtr->openPGPKeyIDlength > 0 )
		{
		setMessageData( &msgData, ( void * ) pkcs15infoPtr->openPGPKeyID,
						pkcs15infoPtr->openPGPKeyIDlength );
		status = krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE_S,
								  &msgData, CRYPT_IATTRIBUTE_KEYID_OPENPGP );
		}
	if( cryptStatusOK( status ) && \
		pkcs15infoPtr->validFrom > MIN_TIME_VALUE )
		{
		/* This isn't really used for anything, but is required to generate
		   the OpenPGP keyID, which includes the key creation time in the
		   ID-generation process */
		setMessageData( &msgData, ( void * ) &pkcs15infoPtr->validFrom,
						sizeof( time_t ) );
		status = krnlSendMessage( iCryptHandle, IMESSAGE_SETATTRIBUTE_S,
								  &msgData, CRYPT_IATTRIBUTE_PGPVALIDITY );
		}
	return( status );
	}

/* Get an encoded trusted cert */

static int getTrustedCert( const PKCS15_INFO *pkcs15info,
						   const int noPkcs15objects,
						   void *data, const int dataMaxLength, 
						   int *dataLength, const BOOLEAN resetCertIndex )
	{
	static int trustedCertIndex;

	assert( isReadPtr( pkcs15info, \
					   sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( isWritePtr( data, dataMaxLength ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );

	/* Clear return values */
	memset( data, 0, dataMaxLength );
	*dataLength = 0;

	/* If this is the first cert, reset the index value.  This is pretty
	   ugly since this sort of state-information value should be stored with 
	   the caller, however there's no way to pass this back and forth in a 
	   MESSAGE_DATA without resorting to an even uglier hack and it's safe 
	   since this attribute is only ever read by the init thread when it 
	   reads the config keyset at startup */
	if( resetCertIndex )
		trustedCertIndex = 0;
	else
		{
		/* Move on to the next cert */
		if( trustedCertIndex >= noPkcs15objects - 1 )
			return( CRYPT_ERROR_NOTFOUND );
		trustedCertIndex++;	
		}

	/* Find the next trusted cert */
	while( trustedCertIndex < noPkcs15objects && \
		   !pkcs15info[ trustedCertIndex ].implicitTrust )	
		trustedCertIndex++;
	if( trustedCertIndex >= noPkcs15objects )
		return( CRYPT_ERROR_NOTFOUND );

	/* Return the data to the caller */
	return( dataCopy( data, dataMaxLength, dataLength,
					  ( BYTE * ) pkcs15info[ trustedCertIndex ].certData + \
								 pkcs15info[ trustedCertIndex ].certOffset,	
					  pkcs15info[ trustedCertIndex ].certDataSize - \
					  pkcs15info[ trustedCertIndex ].certOffset ) );
	}

/* Get an encoded configuration item */

static int getConfigItem( const PKCS15_INFO *pkcs15info,
						  const int noPkcs15objects,
						  const CRYPT_ATTRIBUTE_TYPE dataType,
						  void *data, const int dataMaxLength, 
						  int *dataLength )
	{
	const PKCS15_INFO *pkcs15infoPtr = NULL;
	int i;

	assert( isReadPtr( pkcs15info, \
					   sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( dataType == CRYPT_IATTRIBUTE_CONFIGDATA || \
			dataType == CRYPT_IATTRIBUTE_USERINDEX || \
			dataType == CRYPT_IATTRIBUTE_USERINFO );
	assert( ( data == NULL && dataMaxLength == 0 ) || \
			isWritePtr( data, dataMaxLength ) );
	assert( isWritePtr( dataLength, sizeof( int ) ) );

	/* Clear return values */
	*dataLength = 0;
	if( data != NULL )
		memset( data, 0, dataMaxLength );

	/* Find the particular data type that we're looking for */
	for( i = 0; i < noPkcs15objects; i++ )
		{
		if( ( pkcs15info[ i ].type == PKCS15_SUBTYPE_DATA && \
			  pkcs15info[ i ].dataType == dataType ) )
			{
			pkcs15infoPtr = &pkcs15info[ i ];
			break;
			}
		}
	if( pkcs15infoPtr == NULL )
		return( CRYPT_ERROR_NOTFOUND );

	/* If it's just a length check, we're done */
	if( data == NULL )
		{
		*dataLength = pkcs15infoPtr->dataDataSize - pkcs15infoPtr->dataOffset;
		return( CRYPT_OK );
		}

	/* Return it to the caller */
	return( dataCopy( data, dataMaxLength, dataLength,
					  ( BYTE * ) pkcs15infoPtr->dataData + \
								 pkcs15infoPtr->dataOffset,
					  pkcs15infoPtr->dataDataSize - \
					  pkcs15infoPtr->dataOffset ) );
	}

/* Read key data from a PKCS #15 collection */

static int getItemFunction( KEYSET_INFO *keysetInfo,
							CRYPT_HANDLE *iCryptHandle,
							const KEYMGMT_ITEM_TYPE itemType,
							const CRYPT_KEYID_TYPE keyIDtype,
							const void *keyID, const int keyIDlength,
							void *auxInfo, int *auxInfoLength,
							const int flags )
	{
	CRYPT_CERTIFICATE iDataCert = CRYPT_ERROR;
	CRYPT_CONTEXT iCryptContext;
	const PKCS15_INFO *pkcs15infoPtr;
	MESSAGE_DATA msgData;
	const BOOLEAN publicComponentsOnly = \
					( itemType != KEYMGMT_ITEM_PRIVATEKEY ) ? TRUE : FALSE;
	const int auxInfoMaxLength = *auxInfoLength;
	int pubkeyActionFlags = 0, privkeyActionFlags = 0, status;

	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) && \
			keysetInfo->type == KEYSET_FILE && \
			keysetInfo->subType == KEYSET_SUBTYPE_PKCS15 );

	/* If we're being asked for encoded configuration information, return it
	   and exit.  This is a bit odd, but more valid than defining a pile of
	   special-case KEYMGMT_ITEM types that only exist for PKCS #15 keysets,
	   since these are really attributes of the keyset rather than general
	   key types */
	if( iCryptHandle == NULL )
		{
		assert( itemType == KEYMGMT_ITEM_DATA );
		assert( keyIDtype == CRYPT_KEYID_NONE );
		assert( keyID == NULL && keyIDlength == 0 );
		assert( ( auxInfo == NULL && *auxInfoLength == 0 ) || \
				isWritePtr( auxInfo, *auxInfoLength ) );
		assert( isWritePtr( auxInfoLength, sizeof( int ) ) );
		assert( flags == CRYPT_IATTRIBUTE_CONFIGDATA || \
				flags == CRYPT_IATTRIBUTE_USERINDEX || \
				flags == CRYPT_IATTRIBUTE_USERINFO || \
				flags == CRYPT_IATTRIBUTE_TRUSTEDCERT || \
				flags == CRYPT_IATTRIBUTE_TRUSTEDCERT_NEXT );

		/* If we're being asked for pre-encoded trusted cert data, return it 
		   to the caller */
		if( flags == CRYPT_IATTRIBUTE_TRUSTEDCERT || \
			flags == CRYPT_IATTRIBUTE_TRUSTEDCERT_NEXT )
			{
			return( getTrustedCert( keysetInfo->keyData, 
									keysetInfo->keyDataNoObjects, 
									auxInfo, *auxInfoLength, auxInfoLength,
									( flags == \
										CRYPT_IATTRIBUTE_TRUSTEDCERT ) ? \
										TRUE : FALSE ) );
			}

		/* Return a config data item */
		return( getConfigItem( keysetInfo->keyData, 
							   keysetInfo->keyDataNoObjects, flags, 
							   auxInfo, *auxInfoLength, auxInfoLength ) );
		}

	assert( isWritePtr( iCryptHandle, sizeof( CRYPT_HANDLE ) ) );
	assert( itemType == KEYMGMT_ITEM_PUBLICKEY || \
			itemType == KEYMGMT_ITEM_PRIVATEKEY );
	assert( keyIDtype == CRYPT_KEYID_NAME || \
			keyIDtype == CRYPT_KEYID_URI || \
			keyIDtype == CRYPT_IKEYID_KEYID || \
			keyIDtype == CRYPT_IKEYID_PGPKEYID || \
			keyIDtype == CRYPT_IKEYID_ISSUERID );
	assert( isReadPtr( keyID, keyIDlength ) );
	assert( ( auxInfo == NULL && auxInfoMaxLength == 0 ) || \
			isReadPtr( auxInfo, auxInfoMaxLength ) );

	/* Clear the return values */
	*iCryptHandle = CRYPT_ERROR;

	/* Locate the appropriate object in the PKCS #15 collection and make 
	   sure that the components that we need are present: Either a public 
	   key or a cert for any read, and a private key as well for a private-
	   key read */
	pkcs15infoPtr = findEntry( keysetInfo->keyData, 
							   keysetInfo->keyDataNoObjects, keyIDtype,
							   keyID, keyIDlength, flags );
	if( pkcs15infoPtr == NULL )
		return( CRYPT_ERROR_NOTFOUND );
	if( pkcs15infoPtr->pubKeyData == NULL && \
		pkcs15infoPtr->certData == NULL )
		/* There's not enough information present to get a public key or the
		   public portions of a private key */
		return( CRYPT_ERROR_NOTFOUND );
	if( !publicComponentsOnly && pkcs15infoPtr->privKeyData == NULL )
		/* There's not enough information present to get a private key */
		return( CRYPT_ERROR_NOTFOUND );

	/* If we're just checking whether an object exists, return now.  If all
	   that we want is the key label, copy it back to the caller and exit */
	if( flags & KEYMGMT_FLAG_CHECK_ONLY )
		return( CRYPT_OK );
	if( flags & KEYMGMT_FLAG_LABEL_ONLY )
		return( dataCopy( auxInfo, auxInfoMaxLength, auxInfoLength,
						  pkcs15infoPtr->label, 
						  pkcs15infoPtr->labelLength ) );

	/* If we're reading the private key, make sure that the user has
	   supplied a password.  This is checked by the kernel, but we perform
	   another check here just to be safe*/
	if( !publicComponentsOnly && auxInfo == NULL )
		return( CRYPT_ERROR_WRONGKEY );

	/* Read the public components */
	status = readPublicKeyComponents( pkcs15infoPtr, keysetInfo->objectHandle,
									  keyIDtype, keyID, keyIDlength, 
									  publicComponentsOnly,
									  &iCryptContext, &iDataCert,
									  &pubkeyActionFlags, 
									  &privkeyActionFlags );
	if( cryptStatusError( status ) )
		return( status );

	/* If we're only interested in the public components, set the key
	   permissions and exit */
	if( publicComponentsOnly )
		{
		status = setKeyAttributes( iCryptContext, pkcs15infoPtr,
								   ( pkcs15infoPtr->pubKeyData != NULL ) ? \
									 pubkeyActionFlags : CRYPT_UNUSED );
		if( cryptStatusError( status ) )
			{
			krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
			return( status );
			}
		*iCryptHandle = iCryptContext;

		return( CRYPT_OK );
		}

	assert( ( pkcs15infoPtr->pubKeyData != NULL || \
			  pkcs15infoPtr->certData != NULL ) && \
			pkcs15infoPtr->privKeyData != NULL );

	/* Set the key label.  We have to do this before we load the key or the
	   key load will be blocked by the kernel */
	if( pkcs15infoPtr->labelLength > 0 )
		{ setMessageData( &msgData, ( void * ) pkcs15infoPtr->label,
						  min( pkcs15infoPtr->labelLength, \
							   CRYPT_MAX_TEXTSIZE ) ); }
	else
		{ setMessageData( &msgData, ( void * ) "Dummy label", 11 ); }
	krnlSendMessage( iCryptContext, IMESSAGE_SETATTRIBUTE_S, &msgData,
					 CRYPT_CTXINFO_LABEL );

	/* Read the private components */
	status = readPrivateKeyComponents( pkcs15infoPtr, iCryptContext, 
									   auxInfo, *auxInfoLength );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
		if( iDataCert != CRYPT_ERROR )
			krnlSendNotifier( iDataCert, IMESSAGE_DECREFCOUNT );
		return( status );
		}

	/* Connect the data-only certificate object to the context if it exists.
	   This is an internal object used only by the context so we tell the
	   kernel to mark it as owned by the context only */
	if( iDataCert != CRYPT_ERROR )
		{
		status = krnlSendMessage( iCryptContext, IMESSAGE_SETDEPENDENT, 
								  &iDataCert, SETDEP_OPTION_NOINCREF );
		if( cryptStatusError( status ) )
			{
			krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
			if( iDataCert != CRYPT_ERROR )
				krnlSendNotifier( iDataCert, IMESSAGE_DECREFCOUNT );
			return( status );
			}
		}

	/* Set the permitted action flags */
	status = setKeyAttributes( iCryptContext, pkcs15infoPtr,
							   privkeyActionFlags );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iCryptContext, MESSAGE_DECREFCOUNT );
		return( status );
		}

	*iCryptHandle = iCryptContext;
	return( CRYPT_OK );
	}

/* Fetch a sequence of certs.  These functions are called indirectly by the
   certificate code to fetch the first and subsequent certs in a cert 
   chain */

static int getItem( PKCS15_INFO *pkcs15info, const int noPkcs15objects, 
					CRYPT_CERTIFICATE *iCertificate, int *stateInfo, 
					const CRYPT_KEYID_TYPE keyIDtype, const void *keyID, 
					const int keyIDlength, const KEYMGMT_ITEM_TYPE itemType, 
					const int options )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	const PKCS15_INFO *pkcs15infoPtr;
	BYTE *certDataPtr;
	int tag, status;

	assert( isWritePtr( pkcs15info, \
						sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( isReadPtr( stateInfo, sizeof( int ) ) );
	assert( keyIDtype == CRYPT_KEYID_NAME || \
			keyIDtype == CRYPT_KEYID_URI || \
			keyIDtype == CRYPT_IKEYID_KEYID || \
			keyIDtype == CRYPT_IKEYID_PGPKEYID || \
			keyIDtype == CRYPT_IKEYID_ISSUERID || \
			keyIDtype == CRYPT_KEYIDEX_SUBJECTNAMEID );
	assert( isReadPtr( keyID, keyIDlength ) );
	assert( itemType == KEYMGMT_ITEM_PUBLICKEY );
	assert( ( options & KEYMGMT_MASK_USAGEOPTIONS ) != \
			KEYMGMT_MASK_USAGEOPTIONS );

	/* Find the appropriate entry based on the ID */
	pkcs15infoPtr = findEntry( pkcs15info, noPkcs15objects, keyIDtype, 
							   keyID, keyIDlength, options );
	if( pkcs15infoPtr == NULL )
		{
		*stateInfo = CRYPT_ERROR;
		return( CRYPT_ERROR_NOTFOUND );
		}
	*stateInfo = pkcs15infoPtr->index;

	/* Import the cert.  This gets somewhat ugly because early drafts of 
	   PKCS #15 wrote the cert as is while the final version wrapped it up
	   in a [0] IMPLICIT tag, so we can run into both the original untagged
	   SEQUENCE form and the newer [0] IMPLICIT SEQUENCE.  To handle this
	   we dynamically replace the tag with the standard SEQUENCE tag and
	   reinstate the original afterwards, this is easier than trying to pass
	   the special-case decoding requirement down through the kernel call */
	certDataPtr = ( BYTE * ) pkcs15infoPtr->certData + \
				  pkcs15infoPtr->certOffset;
	tag = *certDataPtr;
	*certDataPtr = BER_SEQUENCE;
	setMessageCreateObjectIndirectInfo( &createInfo, certDataPtr,
			pkcs15infoPtr->certDataSize - pkcs15infoPtr->certOffset,
			( options & KEYMGMT_FLAG_DATAONLY_CERT ) ? \
				CRYPT_ICERTTYPE_DATAONLY : CRYPT_CERTTYPE_CERTIFICATE );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
							  IMESSAGE_DEV_CREATEOBJECT_INDIRECT, &createInfo,
							  OBJECT_TYPE_CERTIFICATE );
	*certDataPtr = tag;
	if( cryptStatusError( status ) )
		return( status );
	*iCertificate = createInfo.cryptHandle;
	if( pkcs15infoPtr->validFrom <= MIN_TIME_VALUE )
		/* Perform an opportunistic update of the validity info if this 
		   hasn't already been set */
		getValidityInfo( pkcs15info, createInfo.cryptHandle );
	return( CRYPT_OK );
	}

static int getFirstItemFunction( KEYSET_INFO *keysetInfo,
								 CRYPT_CERTIFICATE *iCertificate,
								 int *stateInfo,
								 const CRYPT_KEYID_TYPE keyIDtype,
								 const void *keyID, const int keyIDlength,
								 const KEYMGMT_ITEM_TYPE itemType,
								 const int options )
	{
	PKCS15_INFO *pkcs15info = keysetInfo->keyData;
	const int noPkcs15objects = keysetInfo->keyDataNoObjects;

	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) && \
			keysetInfo->type == KEYSET_FILE && \
			keysetInfo->subType == KEYSET_SUBTYPE_PKCS15 );
	assert( isWritePtr( pkcs15info, \
						sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( isReadPtr( stateInfo, sizeof( int ) ) );
	assert( keyIDtype == CRYPT_KEYID_NAME || \
			keyIDtype == CRYPT_KEYID_URI || \
			keyIDtype == CRYPT_IKEYID_KEYID || \
			keyIDtype == CRYPT_IKEYID_PGPKEYID || \
			keyIDtype == CRYPT_IKEYID_ISSUERID );
	assert( isReadPtr( keyID, keyIDlength ) );
	assert( itemType == KEYMGMT_ITEM_PUBLICKEY );
	assert( ( options & KEYMGMT_MASK_USAGEOPTIONS ) != \
			KEYMGMT_MASK_USAGEOPTIONS );

	/* Clear return value */
	*stateInfo = CRYPT_ERROR;

	return( getItem( pkcs15info, noPkcs15objects, iCertificate, stateInfo,
					 keyIDtype, keyID, keyIDlength, itemType, options ) );
	}

static int getNextItemFunction( KEYSET_INFO *keysetInfo,
								CRYPT_CERTIFICATE *iCertificate,
								int *stateInfo, const int options )
	{
	PKCS15_INFO *pkcs15info = keysetInfo->keyData;
	const int noPkcs15objects = keysetInfo->keyDataNoObjects;
	const int lastEntry = *stateInfo;

	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) && \
			keysetInfo->type == KEYSET_FILE && \
			keysetInfo->subType == KEYSET_SUBTYPE_PKCS15 );
	assert( isWritePtr( pkcs15info, \
						sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( isWritePtr( stateInfo, sizeof( int ) ) );
	assert( ( lastEntry >= 0 && lastEntry < noPkcs15objects ) || \
			lastEntry == CRYPT_ERROR );
	assert( ( options & KEYMGMT_MASK_USAGEOPTIONS ) != \
			KEYMGMT_MASK_USAGEOPTIONS );

	/* If the previous cert was the last one, there's nothing left to fetch */
	if( lastEntry == CRYPT_ERROR )
		return( CRYPT_ERROR_NOTFOUND );

	/* Safety check */
	if( lastEntry < 0 || lastEntry >= noPkcs15objects )
		retIntError();

	/* Find the cert for which the subjectNameID matches this cert's
	   issuerNameID */
	return( getItem( pkcs15info, noPkcs15objects, iCertificate, stateInfo,
					 CRYPT_KEYIDEX_SUBJECTNAMEID,
					 pkcs15info[ lastEntry ].issuerNameID,
					 pkcs15info[ lastEntry ].issuerNameIDlength,
					 KEYMGMT_ITEM_PUBLICKEY, options ) );
	}

/****************************************************************************
*																			*
*									Add a Key								*
*																			*
****************************************************************************/

/* Check whether we can add anything to the PKCS #15 personality */

static int checkAddInfo( const PKCS15_INFO *pkcs15infoPtr,
						 const CRYPT_HANDLE iCryptHandle,
						 const BOOLEAN isCertChain, 
						 const BOOLEAN privkeyPresent,
						 const BOOLEAN certPresent,
						 const BOOLEAN pkcs15keyPresent,
						 const BOOLEAN pkcs15certPresent,
						 BOOLEAN *isCertUpdate )
	{
	MESSAGE_DATA msgData;
	BOOLEAN unneededCert, unneededKey;
	int status;

	assert( isReadPtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
	assert( isHandleRangeValid( iCryptHandle ) );
	assert( isWritePtr( isCertUpdate, sizeof( BOOLEAN ) ) );

	/* Clear return value */
	*isCertUpdate = FALSE;

	/* Check what we can update (if anything) */
	unneededKey = privkeyPresent & pkcs15keyPresent;
	unneededCert = certPresent & pkcs15certPresent;
	if( ( ( unneededCert && !privkeyPresent ) || \
		  ( unneededKey && unneededCert ) ) && \
		pkcs15infoPtr->validTo > MIN_TIME_VALUE )
		{
		time_t validTo;

		/* The cert would be a duplicate, see if it's more recent than the 
		   existing one.  We only perform this check if there's a validTo 
		   time stored for the cert since without this restriction any cert 
		   without a stored time could be overwritten */
		setMessageData( &msgData, &validTo, sizeof( time_t ) );
		status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
								  &msgData, CRYPT_CERTINFO_VALIDTO );
		if( cryptStatusOK( status ) && validTo > pkcs15infoPtr->validTo )
			{
			time_t validFrom;

			/* It's a newer cert, don't treat it as a duplicate.  This check 
			   is effectively impossible to perform automatically since there 
			   are an infinite number of variations that have to be taken 
			   into account (e.g. cert for the same key issued by a different 
			   CA, same CA but it's changed the bits it sets in the keyUsage 
			   (digitalSignature vs.nonRepudiation), slightly different 
			   issuer DN (Thawte certs with a date encoded in the DN), and so 
			   on an so on).  Because it requires manual processing by a 
			   human, we don't even try and sort it all but just allow a cert 
			   for a given key (checked by the ID match) to be replaced by a
			   newer cert for the same key.  This is restrictive enough to 
			   prevent most obviously-wrong replacements, while being 
			   permissive enough to allow most probably-OK replacements */
			unneededCert = FALSE;
			*isCertUpdate = TRUE;

			/* There's one special-case situation in which odd things can 
			   happen when updating certs and that's when adding a future-
			   dated cert, which would result in the cert being replaced with 
			   one that can't be used yet.  There's no clean way to handle 
			   this because in order to know what to do we'd have to be able 
			   to guess the intent of the user, however for anything but 
			   signature certs it's likely that the hit-and-miss cert 
			   checking performed by most software won't even notice a 
			   future-dated cert, and for signature certs the semantics of 
			   signing data now using a cert that isn't valid yet are 
			   somewhat uncertain.  Since in most cases no-one will even 
			   notice the problem, we throw an exception in the debug build 
			   but don't do anything in release builds.  This is probably 
			   less annoying to users than having the code reject an 
			   otherwise-valid future-dated cert */
			setMessageData( &msgData, &validFrom, sizeof( time_t ) );
			status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE_S,
									  &msgData, CRYPT_CERTINFO_VALIDFROM );
			if( cryptStatusOK( status ) && \
				validFrom > getApproxTime() + 86400L )
				{
				assert( !"Attempt to replace cert with future-dated cert" );
				}
			}
		}

	/* Make sure that we can update at least one of the objects in the PKCS 
	   #15 personality */
	if( ( unneededKey && !certPresent ) ||		/* Key only, duplicate */
		( unneededCert && !privkeyPresent ) ||	/* Cert only, duplicate */
		( unneededKey && unneededCert ) )		/* Key+cert, duplicate */
		{
		/* If it's anything other than a cert chain, we can't add anything */
		if( !isCertChain )
			return( CRYPT_ERROR_DUPLICATE );

		/* Tell the caller that it's an opportunistic cert-chain update */
		return( OK_SPECIAL );
		}

	return( CRYPT_OK );
	}

/* Add an item to the PKCS #15 keyset */

static int setItemFunction( KEYSET_INFO *keysetInfo,
							const CRYPT_HANDLE cryptHandle,
							const KEYMGMT_ITEM_TYPE itemType,
							const char *password, const int passwordLength,
							const int flags )
	{
	CRYPT_CERTIFICATE iCryptCert;
	PKCS15_INFO *pkcs15info = keysetInfo->keyData, *pkcs15infoPtr;
	MESSAGE_DATA msgData;
	BYTE iD[ CRYPT_MAX_HASHSIZE + 8 ];
	BOOLEAN certPresent = FALSE, privkeyPresent;
	BOOLEAN pkcs15certPresent = FALSE, pkcs15keyPresent = FALSE;
	BOOLEAN isCertChain = FALSE, isCertUpdate = FALSE;
	const int noPkcs15objects = keysetInfo->keyDataNoObjects;
	int pkcs15index = CRYPT_ERROR, iDsize, value, status;

	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) && \
			keysetInfo->type == KEYSET_FILE && \
			keysetInfo->subType == KEYSET_SUBTYPE_PKCS15 );
	assert( isWritePtr( pkcs15info, \
						sizeof( PKCS15_INFO ) * noPkcs15objects ) );
	assert( ( cryptHandle == CRYPT_UNUSED ) || \
			isHandleRangeValid( cryptHandle ) );
	assert( itemType == KEYMGMT_ITEM_DATA || \
			itemType == KEYMGMT_ITEM_PUBLICKEY || \
			itemType == KEYMGMT_ITEM_PRIVATEKEY );

	/* If we're being sent pre-encoded data or a secret key, add it to the
	   PKCS #15 data and exit */
	if( cryptHandle == CRYPT_UNUSED )
		{
		assert( itemType == KEYMGMT_ITEM_DATA );

		return( addConfigData( pkcs15info, noPkcs15objects, password, 
							   passwordLength, flags ) );
		}
	if( itemType == KEYMGMT_ITEM_SECRETKEY )
		return( addSecretKey( pkcs15info, noPkcs15objects, cryptHandle ) );

	assert( isHandleRangeValid( cryptHandle ) );
	assert( itemType == KEYMGMT_ITEM_PUBLICKEY || \
			itemType == KEYMGMT_ITEM_PRIVATEKEY );
	assert( ( itemType == KEYMGMT_ITEM_PUBLICKEY && \
			  password == NULL && passwordLength == 0 ) || \
			( itemType == KEYMGMT_ITEM_PRIVATEKEY && \
			  isReadPtr( password, passwordLength ) ) );

	/* Check the object, extract ID information from it, and determine
	   whether it's a standalone cert (which produces a PKCS #15 cert
	   object) or a private-key context (which produces a PKCS #15 private
	   key object and either a PKCS #15 public-key object (if there's no
	   cert present) or a cert object (if there's a cert present)).

	   Note that we don't allow the addition of standalone public keys
	   (without corresponding private keys) since file keysets are private-
	   key keysets and not general-purpose public key exchange mechanisms.
	   Without this safeguard, some users would use them as a type of
	   unsigned certificate for exchanging public keys.  In addition,
	   allowing the storage of standalone public keys is rather problematic
	   since they need to have a label attached in order to be identified,
	   so performing a public-key add with a private-key context would
	   work but performing one with a public-key context would fail.  A 
	   certificate update on this public-key-only item would result in the 
	   presence a private-key-labelled certificate, which is even more 
	   strange for users to comprehend.  To keep things sensible, we 
	   therefore disallow the addition of standalone public keys */
	status = krnlSendMessage( cryptHandle, IMESSAGE_CHECK, NULL,
							  MESSAGE_CHECK_PKC );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, iD, CRYPT_MAX_HASHSIZE );
		status = krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE_S,
								  &msgData, CRYPT_IATTRIBUTE_KEYID );
		iDsize = msgData.length;
		}
	if( cryptStatusError( status ) )
		return( ( status == CRYPT_ARGERROR_OBJECT ) ? \
				CRYPT_ARGERROR_NUM1 : status );
	privkeyPresent = cryptStatusOK( \
			krnlSendMessage( cryptHandle, IMESSAGE_CHECK, NULL,
							 MESSAGE_CHECK_PKC_PRIVATE ) ) ? TRUE : FALSE;

	/* If we're adding a private key, make sure that there's a context and a
	   password present.  Conversely, if we're adding a public key, make
	   sure that there's no password present.  The password-check has
	   already been performed by the kernel but we perform a second check
	   here just to be safe.  The private-key check can't be performed by
	   the kernel since it doesn't know the difference between public- and
	   private-key contexts */
	switch( itemType )
		{
		case KEYMGMT_ITEM_PUBLICKEY:
			if( privkeyPresent )
				return( CRYPT_ARGERROR_NUM1 );
			if( password != NULL )
				return( CRYPT_ARGERROR_STR1 );
			break;

		case KEYMGMT_ITEM_PRIVATEKEY:
			if( !privkeyPresent )
				return( CRYPT_ARGERROR_NUM1 );
			if( password == NULL )
				return( CRYPT_ARGERROR_STR1 );
			break;
		
		default:
			assert( NOTREACHED );
			return( CRYPT_ERROR_INTERNAL );
		}

	/* If there's a cert present, make sure that it's something that can be
	   stored.  We don't treat the wrong type as an error since we can still
	   store the public/private key components even if we don't store the
	   cert */
	if( cryptStatusOK( \
		krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE, &value,
						 CRYPT_CERTINFO_CERTTYPE ) ) && \
		( value == CRYPT_CERTTYPE_CERTIFICATE || \
		  value == CRYPT_CERTTYPE_CERTCHAIN ) )
		{
		/* If it's a cert chain, remember this for later since we may
		   need to store multiple certs */
		if( value == CRYPT_CERTTYPE_CERTCHAIN )
			isCertChain = TRUE;

		/* If the cert isn't signed, we can't store it in this state */
		status = krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE,
								  &value, CRYPT_CERTINFO_IMMUTABLE );
		if( cryptStatusError( status ) || !value )
			return( CRYPT_ERROR_NOTINITED );
		krnlSendMessage( cryptHandle, IMESSAGE_GETDEPENDENT, &iCryptCert,
						 OBJECT_TYPE_CERTIFICATE );
		certPresent = TRUE;
		}

	/* Find out where we can add data and what needs to be added.  The 
	   strategy for adding items is:

										Existing
			New		|	None	| Priv+Pub	| Priv+Cert	|	Cert	|
		------------+-----------+-----------+-----------+-----------+
		Priv + Pub	|	Add		|	----	|	----	|	Add		|
					|			|			|			|			|
		Priv + Cert	|	Add		| Repl.pubk	| Add cert	| Add cert	|
					|			| with cert	| if newer	| if newer	|
		Cert		| If trusted|	Add		| Add cert	| Add cert	|
					|			|			| if newer	| if newer	|
		------------+-----------+-----------+-----------+-----------+

	   We don't check for the addition of a trusted cert at this point since 
	   it could be buried in the middle of a cert chain, so we leave the 
	   checking to addCertChain() */
	pkcs15infoPtr = findEntry( pkcs15info, noPkcs15objects, CRYPT_KEYIDEX_ID,
							   iD, iDsize, KEYMGMT_FLAG_NONE );
	if( pkcs15infoPtr != NULL )
		{
		/* Determine what actually needs to be added */
		if( pkcs15infoPtr->privKeyData != NULL )
			pkcs15keyPresent = TRUE;
		if( pkcs15infoPtr->certData != NULL )
			pkcs15certPresent = TRUE;

		/* See what we can add */
		status = checkAddInfo( pkcs15infoPtr, cryptHandle, isCertChain, 
							   privkeyPresent, certPresent, 
							   pkcs15keyPresent, pkcs15certPresent,
							   &isCertUpdate );
		if( cryptStatusError( status ) )
			{
			/* If it's not an OK_SPECIAL status telling us that we can still 
			   try for an opportunistic cert chain add, exit */
			if( status != OK_SPECIAL )
				return( status );
			
			/* In theory we can't add anything, however since we've been 
			   given a cert chain there may be new certs present, so we can 
			   try and add them opportunistically */
			status = krnlSendMessage( cryptHandle, IMESSAGE_SETATTRIBUTE,
									  MESSAGE_VALUE_TRUE,
									  CRYPT_IATTRIBUTE_LOCKED );
			if( cryptStatusError( status ) )
				return( status );
			status = addCertChain( pkcs15infoPtr, noPkcs15objects, 
								   cryptHandle );
			krnlSendMessage( cryptHandle, IMESSAGE_SETATTRIBUTE,
							 MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
			return( status );
			}
		}
	else
		{
		/* This key/cert isn't already present, make sure that the label of
		   what we're adding doesn't duplicate the label of an existing
		   object */
		if( privkeyPresent )
			{
			char label[ CRYPT_MAX_TEXTSIZE + 8 ];

			setMessageData( &msgData, label, CRYPT_MAX_TEXTSIZE );
			status = krnlSendMessage( cryptHandle, IMESSAGE_GETATTRIBUTE_S,
									  &msgData, CRYPT_CTXINFO_LABEL );
			if( cryptStatusError( status ) )
				return( status );
			if( findEntry( pkcs15info, noPkcs15objects, CRYPT_KEYID_NAME,
						   msgData.data, msgData.length,
						   KEYMGMT_FLAG_NONE ) != NULL )
				return( CRYPT_ERROR_DUPLICATE );
			}

		/* Find out where we can add the new key data */
		pkcs15infoPtr = findFreeEntry( pkcs15info, noPkcs15objects, 
									   &pkcs15index );
		if( pkcs15infoPtr == NULL )
			return( CRYPT_ERROR_OVERFLOW );
		}

	/* We're ready to go, lock the object for our exclusive use */
	if( certPresent )
		{
		status = krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE,
								  MESSAGE_VALUE_TRUE,
								  CRYPT_IATTRIBUTE_LOCKED );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Add the key data.  This will add the public/private key and any cert 
	   data associated with the key as required */
	status = addKey( pkcs15infoPtr, cryptHandle, password, passwordLength,
					 keysetInfo->ownerHandle, privkeyPresent, certPresent, 
					 ( isCertUpdate || !pkcs15certPresent ) ? TRUE : FALSE,
					 pkcs15keyPresent );
	if( cryptStatusError( status ) )
		{
		if( certPresent )
			krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE,
							 MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
		return( status );
		}

	/* The update was successful, update the type and index info if this was
	   a newly-created entry */
	if( pkcs15index != CRYPT_ERROR )
		{
		pkcs15infoPtr->type = PKCS15_SUBTYPE_NORMAL;
		pkcs15infoPtr->index = pkcs15index;
		}

	/* If we've been given a cert chain, try and add opportunistically add 
	   any further certs that may be present in it.  Error handling once we
	   get this far gets a bit tricky, we can still get an error at this 
	   point if the cert chain update fails even if the main cert add 
	   succeeded, however it's uncertain whether we should still report an 
	   error when the main intended update (of the private key and public 
	   key or cert) succeeded.  Since the primary items to be added are the 
	   keys and a corresponding certificate (as handled in addKey()), we 
	   don't report an error if adding one of the coincidental certs fails, 
	   since the primary items were added successfully */
	if( isCertChain )
		addCertChain( pkcs15infoPtr, noPkcs15objects, cryptHandle );

	/* Clean up */
	if( certPresent )
		krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE,
						 MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
	return( status );
	}

/****************************************************************************
*																			*
*									Delete a Key							*
*																			*
****************************************************************************/

static int deleteItemFunction( KEYSET_INFO *keysetInfo,
							   const KEYMGMT_ITEM_TYPE itemType,
							   const CRYPT_KEYID_TYPE keyIDtype,
							   const void *keyID, const int keyIDlength )
	{
	PKCS15_INFO *pkcs15infoPtr;

	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) && \
			keysetInfo->type == KEYSET_FILE && \
			keysetInfo->subType == KEYSET_SUBTYPE_PKCS15 );
	assert( itemType > KEYMGMT_ITEM_NONE && \
			itemType < KEYMGMT_ITEM_LAST );
	assert( keyIDtype == CRYPT_KEYID_NAME || \
			keyIDtype == CRYPT_KEYID_URI || \
			keyIDtype == CRYPT_IKEYID_KEYID || \
			keyIDtype == CRYPT_IKEYID_ISSUERID );
	assert( isReadPtr( keyID, keyIDlength ) );

	/* Locate the appropriate object in the PKCS #15 collection */
	pkcs15infoPtr = findEntry( keysetInfo->keyData, 
							   keysetInfo->keyDataNoObjects, keyIDtype, 
							   keyID, keyIDlength, KEYMGMT_FLAG_NONE );
	if( pkcs15infoPtr == NULL )
		return( CRYPT_ERROR_NOTFOUND );

	/* Clear this entry */
	pkcs15freeEntry( pkcs15infoPtr );

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Keyset Access Routines							*
*																			*
****************************************************************************/

int setAccessMethodPKCS15( KEYSET_INFO *keysetInfo )
	{
	assert( isWritePtr( keysetInfo, sizeof( KEYSET_INFO ) ) );

	/* Set the access method pointers */
	keysetInfo->initFunction = initFunction;
	keysetInfo->shutdownFunction = shutdownFunction;
	keysetInfo->getItemFunction = getItemFunction;
	keysetInfo->getFirstItemFunction = getFirstItemFunction;
	keysetInfo->getNextItemFunction = getNextItemFunction;
	keysetInfo->setItemFunction = setItemFunction;
	keysetInfo->deleteItemFunction = deleteItemFunction;

	return( CRYPT_OK );
	}
#endif /* USE_PKCS15 */
