/****************************************************************************
*																			*
*					  Certificate Trust Management Routines					*
*						Copyright Peter Gutmann 1998-2025					*
*																			*
****************************************************************************/

/* The following code is actually part of the user rather than certificate
   routines but it pertains to certificates so we include it here.  Trust
   information mutex handling is done in the user object so there are no 
   mutexes required here.

   The interpretation of what represents a "trusted certificate" is somewhat 
   complex and open-ended, it's not clear whether what's being trusted is 
   the key in the certificate, the certificate, or the owner of the 
   certificate (corresponding to subjectKeyIdentifier, issuerAndSerialNumber/
   certHash, or subject DN).  The generally accepted form is to trust the 
   subject so we check for this in the certificate.  The modification for 
   trusting the key is fairly simple to make if required.
   
   Since the trust data sits in memory for extended periods of time, we're
   especially careful to defend against memory corruption issues */

#if defined( INC_ALL )
  #include "cert.h"
  #include "trustmgr_int.h"
  #include "trustmgr.h"
#else
  #include "cert/cert.h"
  #include "cert/trustmgr_int.h"
  #include "cert/trustmgr.h"
#endif /* Compiler-specific includes */

/* The maximum number of entries in each hash table chain.  This is somewhat
   arbitrary and is designed to allow a reasonable number of trusted certs
   while disallowing complexity attacks, the typical user will have one or
   two trusted CAs rather than every CA on the planet like web browsers do */

#define MAX_TRUSTLIST_ENTRIES	16

#ifdef USE_CERTIFICATES

/****************************************************************************
*																			*
*								Utility Routines							*
*																			*
****************************************************************************/

/* Sanity-check trust information */

#ifndef CONFIG_CONSERVE_MEMORY_EXTRA

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN sanityCheckTrustInfo( const TRUST_INFO *trustInfo )
	{
	assert( isReadPtr( trustInfo, sizeof( TRUST_INFO ) ) );

	/* Check safe pointers */
	if( !DATAPTR_ISVALID( trustInfo->certObject ) || \
		!DATAPTR_ISVALID( trustInfo->next ) || \
		!DATAPTR_ISVALID( trustInfo->prev ) )
		{
		DEBUG_PUTS(( "sanityCheckTrustInfo: Safe pointers" ));
		return( FALSE );
		}

	/* Check certificate data */
	if( trustInfo->iCryptCert != CRYPT_ERROR && \
		!isHandleRangeValid( trustInfo->iCryptCert ) )
		{
		DEBUG_PUTS(( "sanityCheckTrustInfo: Certificate object" ));
		return( FALSE );
		}
	if( DATAPTR_ISSET( trustInfo->certObject ) )
		{
		const void *certObjectPtr;

		certObjectPtr = DATAPTR_GET( trustInfo->certObject );
		ENSURES_B( certObjectPtr != NULL );

		if( !isShortIntegerRangeMin( trustInfo->certObjectLength, 
									 MIN_CERTSIZE ) )
			{
			DEBUG_PUTS(( "sanityCheckTrustInfo: Certificate data" ));
			return( FALSE );
			}
		if( checksumData( certObjectPtr, \
						  trustInfo->certObjectLength ) != trustInfo->certChecksum )
			{
			DEBUG_PUTS(( "sanityCheckTrustInfo: Certificate data corruption" ));
			return( FALSE );
			}
		}
	else
		{
		if( !DATAPTR_ISNULL( trustInfo->certObject ) || \
			trustInfo->certObjectLength != 0 || \
			trustInfo->certChecksum != 0 )
			{
			DEBUG_PUTS(( "sanityCheckTrustInfo: Spurious certificate data" ));
			return( FALSE );
			}
		}

	return( TRUE );
	}
#endif /* !CONFIG_CONSERVE_MEMORY_EXTRA */

/* Get a pointer to the trust information from a generic DATAPTR after 
   checking that it's valid */

CHECK_RETVAL_PTR \
static const DATAPTR *getCheckTrustInfo( IN_PTR const DATAPTR trustInfo )
	{
	const TRUST_INFO_CONTAINER *trustInfoContainer;

	/* Get a pointer to the trust information */
	trustInfoContainer = DATAPTR_GET( trustInfo );
	ENSURES_N( trustInfoContainer != NULL );

	/* Make sure that it's valid */
	if( checksumData( trustInfoContainer->trustInfo, 
					  sizeof( DATAPTR ) * TRUSTINFO_SIZE ) != \
		trustInfoContainer->trustInfoChecksum )
		return( NULL );

	return( trustInfoContainer->trustInfo );
	}

/* Update the checksum on the trust information */

static void updateTrustInfoChecksum( IN_DATAPTR const DATAPTR trustInfo )
	{
	TRUST_INFO_CONTAINER *trustInfoContainer;

	/* Get a pointer to the trust information index.  We can't use 
	   getCheckTrustInfo() because we've changed it so the checksum won't
	   validate */
	trustInfoContainer = DATAPTR_GET( trustInfo );
	ENSURES_V( trustInfoContainer != NULL );

	/* Update the trust information checksum */
	trustInfoContainer->trustInfoChecksum = \
					checksumData( trustInfoContainer->trustInfo, 
								  sizeof( DATAPTR ) * TRUSTINFO_SIZE );
	}

/****************************************************************************
*																			*
*					Retrieve Trusted Certificate Information				*
*																			*
****************************************************************************/

/* Find the trust information entry for a given certificate.  This isn't 
   used within this module but is called from cryptusr.c */

CHECK_RETVAL_PTR \
void *findTrustEntry( IN_DATAPTR const DATAPTR trustInfo, 
					  IN_HANDLE const CRYPT_CERTIFICATE iCryptCert,
					  IN_BOOL const BOOLEAN getIssuerEntry )
	{
	const DATAPTR *trustInfoIndex;
	LOOP_INDEX_PTR const TRUST_INFO *trustInfoCursor;
	DYNBUF nameDB;
	BYTE sHash[ HASH_DATA_SIZE + 8 ];
	BOOLEAN nameHashed = FALSE;
	int sCheck, trustInfoEntry, status;

	REQUIRES_N( DATAPTR_ISSET( trustInfo ) );
	REQUIRES_N( isHandleRangeValid( iCryptCert ) );
	REQUIRES_N( isBooleanValue( getIssuerEntry ) );

	/* Check that the trust information is valid and get a pointer to the
	   trust information index */
	trustInfoIndex = getCheckTrustInfo( trustInfo );
	ENSURES_N( trustInfoIndex != NULL );

	/* If we're trying to get a trusted issuer certificate and we're already 
	   at a self-signed (CA root) certificate, don't return it.  This check 
	   is necessary because self-signed certificates have issuer name == 
	   subject name so once we get to a self-signed certificate's subject DN 
	   an attempt to fetch its issuer would just repeatedly fetch the same 
	   certificate */
	if( getIssuerEntry )
		{
		BOOLEAN_INT selfSigned;

		status = krnlSendMessage( iCryptCert, IMESSAGE_GETATTRIBUTE, 
								  &selfSigned, CRYPT_CERTINFO_SELFSIGNED );
		if( cryptStatusError( status ) || selfSigned == TRUE )
			return( NULL );
		}

	/* Set up the information needed to find the trusted certificate */
	status = dynCreate( &nameDB, iCryptCert, getIssuerEntry ? \
						CRYPT_IATTRIBUTE_ISSUER : CRYPT_IATTRIBUTE_SUBJECT );
	if( cryptStatusError( status ) )
		return( NULL );
	sCheck = checksumData( dynData( nameDB ), dynLength( nameDB ) );
	trustInfoEntry = sCheck & ( TRUSTINFO_SIZE - 1 );
	ENSURES_N( trustInfoEntry >= 0 && trustInfoEntry < TRUSTINFO_SIZE );

	/* Check to see whether something with the requested DN is present */
	LOOP_MED( trustInfoCursor = DATAPTR_GET( trustInfoIndex[ trustInfoEntry ] ), 
			  trustInfoCursor != NULL,
			  trustInfoCursor = DATAPTR_GET( trustInfoCursor->next ) )
		{
		REQUIRES_N( sanityCheckTrustInfo( trustInfoCursor ) );

		ENSURES_N( LOOP_INVARIANT_MED_GENERIC() );

		/* Perform a quick check using a checksum of the name to weed out
		   most entries */
		if( trustInfoCursor->sCheck == sCheck )
			{
			if( !nameHashed )
				{
				hashData( sHash, HASH_DATA_SIZE, dynData( nameDB ), 
						  dynLength( nameDB ) );
				nameHashed = TRUE;
				}
			if( !memcmp( trustInfoCursor->sHash, sHash, HASH_DATA_SIZE ) )
				{
				dynDestroy( &nameDB );
				return( ( TRUST_INFO * ) trustInfoCursor );
				}
			}
		}
	ENSURES_N( LOOP_BOUND_OK );
	dynDestroy( &nameDB );

	return( NULL );
	}

/* Retrieve trusted certificates */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int getTrustedCert( INOUT_PTR TYPECAST( TRUST_INFO * ) struct TI *trustInfoPtr,
					OUT_HANDLE_OPT CRYPT_CERTIFICATE *iCertificate )
	{
	TRUST_INFO *trustInfo = trustInfoPtr;
	int status;

	assert( isWritePtr( trustInfoPtr, sizeof( TRUST_INFO ) ) );

	REQUIRES( sanityCheckTrustInfo( trustInfo ) );

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

	/* If the certificate hasn't already been instantiated yet, do so now */
	if( trustInfo->iCryptCert == CRYPT_ERROR )
		{
		MESSAGE_CREATEOBJECT_INFO createInfo;
		ERROR_INFO localErrorInfo;
		void *certObjectPtr;

		certObjectPtr = DATAPTR_GET( trustInfo->certObject );
		ENSURES( certObjectPtr != NULL );

		/* Instantiate the certificate.  Since there's nothing to return the 
		   error information through we don't do anything with it */
		clearErrorInfo( &localErrorInfo );
		setMessageCreateObjectIndirectInfo( &createInfo, certObjectPtr,
											trustInfo->certObjectLength,
											CRYPT_CERTTYPE_CERTIFICATE,
											&localErrorInfo );
		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
								  IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
								  &createInfo, OBJECT_TYPE_CERTIFICATE );
		if( cryptStatusError( status ) )
			{
			/* If we couldn't instantiate the certificate and it's due to a
			   problem with the certificate rather than circumstances beyond
			   our control, warn about the issue since a (supposedly) known-
			   good certificate shouldn't cause problems on import */
			if( status != CRYPT_ERROR_MEMORY )
				{
				DEBUG_DIAG(( "Couldn't instantiate trusted certificate" ));
				assert( DEBUG_WARN );
				}
			return( status );
			}

		/* The certificate was successfully instantiated, free its stored 
		   encoded form */
		REQUIRES( isShortIntegerRangeNZ( trustInfo->certObjectLength ) ); 
		zeroise( certObjectPtr, trustInfo->certObjectLength );
		clFree( "getTrustedCert", certObjectPtr );
		certObjectPtr = NULL;
		DATAPTR_SET( trustInfo->certObject, NULL );
		trustInfo->certObjectLength = 0;
		trustInfo->certChecksum = 0;
		trustInfo->iCryptCert = createInfo.cryptHandle;
		}

	/* Return the trusted certificate */
	*iCertificate = trustInfo->iCryptCert;

	return( CRYPT_OK );
	}

CHECK_RETVAL_BOOL \
BOOLEAN trustedCertsPresent( IN_DATAPTR const DATAPTR trustInfo )
	{
	const DATAPTR *trustInfoIndex;
	LOOP_INDEX i;

	REQUIRES_B( DATAPTR_ISSET( trustInfo ) );

	/* Check that the trust information is valid and get a pointer to the
	   trust information index */
	trustInfoIndex = getCheckTrustInfo( trustInfo );
	ENSURES_B( trustInfoIndex != NULL );

	LOOP_EXT( i = 0, i < TRUSTINFO_SIZE, i++, TRUSTINFO_SIZE + 1 )
		{
		ENSURES_B( LOOP_INVARIANT_EXT( i, 0, TRUSTINFO_SIZE - 1,
									   TRUSTINFO_SIZE + 1 ) );

		if( DATAPTR_ISSET( trustInfoIndex[ i ] ) )
			return( TRUE );
		}
	ENSURES_B( LOOP_BOUND_OK );

	return( FALSE );
	}

CHECK_RETVAL \
int enumTrustedCerts( IN_DATAPTR const DATAPTR trustInfo, 
					  IN_HANDLE_OPT const CRYPT_CERTIFICATE iCryptCtl,
					  IN_HANDLE_OPT const CRYPT_KEYSET iCryptKeyset )
	{
	const DATAPTR *trustInfoIndex;
	LOOP_INDEX i;

	REQUIRES( DATAPTR_ISSET( trustInfo ) );
	REQUIRES( ( iCryptCtl == CRYPT_UNUSED && \
				isHandleRangeValid( iCryptKeyset ) ) || \
			  ( isHandleRangeValid( iCryptCtl ) && \
				iCryptKeyset == CRYPT_UNUSED ) );

	/* Check that the trust information is valid and get a pointer to the
	   trust information index */
	trustInfoIndex = getCheckTrustInfo( trustInfo );
	ENSURES( trustInfoIndex != NULL );

	/* Send each trusted certificate to the given object, either a 
	   certificate trust list or a keyset */
	LOOP_EXT( i = 0, i < TRUSTINFO_SIZE, i++, TRUSTINFO_SIZE + 1 )
		{
		LOOP_INDEX_PTR_ALT TRUST_INFO *trustInfoCursor;

		ENSURES( LOOP_INVARIANT_EXT( i, 0, TRUSTINFO_SIZE - 1,
									  TRUSTINFO_SIZE + 1 ) );

		LOOP_MED_ALT( trustInfoCursor = DATAPTR_GET( trustInfoIndex[ i ] ), 
					  trustInfoCursor != NULL,
					  trustInfoCursor = DATAPTR_GET( trustInfoCursor->next ) )	
			{
			CRYPT_CERTIFICATE iCryptCert;
			int status;

			REQUIRES( sanityCheckTrustInfo( trustInfoCursor ) );

			ENSURES( LOOP_INVARIANT_MED_ALT_GENERIC() );

			status = getTrustedCert( trustInfoCursor, &iCryptCert );
			if( cryptStatusError( status ) )
				return( status );
			if( iCryptCtl != CRYPT_UNUSED )
				{
				/* We're sending trusted certificates to a certificate trust 
				   list */
				status = krnlSendMessage( iCryptCtl, IMESSAGE_SETATTRIBUTE,
										  ( MESSAGE_CAST ) &iCryptCert,
										  CRYPT_IATTRIBUTE_CERTCOLLECTION );
				}
			else
				{
				MESSAGE_KEYMGMT_INFO setkeyInfo;

				/* We're sending trusted certificates to a keyset */
				setMessageKeymgmtInfo( &setkeyInfo, CRYPT_KEYID_NONE, NULL, 0,
									   NULL, 0, KEYMGMT_FLAG_NONE );
				setkeyInfo.cryptHandle = iCryptCert;
				status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_SETKEY, 
										  &setkeyInfo, 
										  KEYMGMT_ITEM_PUBLICKEY );
				}
			if( cryptStatusError( status ) )
				return( status );
			}
		ENSURES( LOOP_BOUND_OK_ALT );
		}
	ENSURES( LOOP_BOUND_OK );
	
	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*					Add/Update Trusted Certificate Information				*
*																			*
****************************************************************************/

/* Find the insertion point for a new trusted certificate */

CHECK_RETVAL \
static int findInsertionPoint( IN_DATAPTR const DATAPTR *trustInfoIndex, 
							   const int sCheck, 
							   IN_BUFFER( sHashLen ) \
									const void *sHash, 
							   IN_LENGTH_FIXED( HASH_DATA_SIZE ) \
									const int sHashLen,
							   OUT_DATAPTR DATAPTR **trustInfoEntryPtr,
							   TRUST_INFO **trustInfoEndPtrPtr )
	{
	TRUST_INFO *trustInfoCursor, *trustInfoLast DUMMY_INIT_PTR;
	LOOP_INDEX noEntries;
	int trustInfoIndexPos;

	assert( isReadPtr( trustInfoIndex, sizeof( DATAPTR * ) ) );
	assert( isReadPtr( sHash, sHashLen ) );
	assert( isWritePtr( trustInfoEndPtrPtr, sizeof( TRUST_INFO * ) ) );

	REQUIRES( sHash != NULL && sHashLen == HASH_DATA_SIZE );

	/* Clear return values */
	*trustInfoEntryPtr = NULL;
	*trustInfoEndPtrPtr = NULL;

	/* Find the hash table entry for this chain.  We cast the returned 
	   pointer to non-const because the caller will be modifying it */
	trustInfoIndexPos = sCheck & ( TRUSTINFO_SIZE - 1 );
	ENSURES( trustInfoIndexPos >= 0 && trustInfoIndexPos < TRUSTINFO_SIZE );
	*trustInfoEntryPtr = ( DATAPTR * ) &trustInfoIndex[ trustInfoIndexPos ];

	/* If there's nothing there yet, we're done */
	trustInfoCursor = DATAPTR_GET( trustInfoIndex[ trustInfoIndexPos ] );
	if( trustInfoCursor == NULL )
		{
		/* Nothing present yet, we're done */
		return( CRYPT_OK );
		}

	/* Walk the chain making sure that this entry isn't already present */
	LOOP_MED( noEntries = 0, noEntries < MAX_TRUSTLIST_ENTRIES, noEntries++ )
		{
		REQUIRES( sanityCheckTrustInfo( trustInfoCursor ) );

		ENSURES( LOOP_INVARIANT_MED_GENERIC() );

		/* Perform a quick check using a checksum of the name to weed out
		   most entries */
		if( trustInfoCursor->sCheck == sCheck && \
			!memcmp( trustInfoCursor->sHash, sHash, HASH_DATA_SIZE ) )
			return( CRYPT_ERROR_DUPLICATE );
		trustInfoLast = trustInfoCursor;

		/* Move on to the next entry */
		trustInfoCursor = DATAPTR_GET( trustInfoCursor->next );
		if( trustInfoCursor == NULL )
			break;
		}
	ENSURES( LOOP_BOUND_OK );
	if( noEntries >= MAX_TRUSTLIST_ENTRIES )
		return( CRYPT_ERROR_OVERFLOW );
	*trustInfoEndPtrPtr = trustInfoLast;

	return( CRYPT_OK );
	}

/* Add and delete a trust entry */

CHECK_RETVAL \
static int addEntry( IN_DATAPTR const DATAPTR trustInfo, 
					 IN_HANDLE_OPT const CRYPT_CERTIFICATE iCryptCert, 
					 IN_BUFFER_OPT( certObjectLength ) const void *certObject, 
					 IN_LENGTH_SHORT_Z const int certObjectLength )
	{
	CRYPT_CERTIFICATE iLocalCert;
	DYNBUF subjectDB;
	const DATAPTR *trustInfoIndex;
	DATAPTR *trustInfoIndexEntryPtr;
	TRUST_INFO *trustInfoListEnd, *newElement;
	BYTE sHash[ HASH_DATA_SIZE + 8 ];
	BOOLEAN recreateCert = FALSE;
	int sCheck, status;

	assert( ( certObject == NULL && certObjectLength == 0 && \
			  isHandleRangeValid( iCryptCert ) ) || \
			( isReadPtrDynamic( certObject, certObjectLength ) && \
			  iCryptCert == CRYPT_UNUSED ) );

	REQUIRES( ( certObject == NULL && certObjectLength == 0 && \
				isHandleRangeValid( iCryptCert ) ) || \
			  ( certObject != NULL && \
			    isShortIntegerRangeMin( certObjectLength, 
										MIN_CRYPT_OBJECTSIZE ) && \
				iCryptCert == CRYPT_UNUSED ) );

	/* If we're fuzzing then we exit now since all the following will end up
	   doing is fuzzing certificates by proxy */
#ifdef CONFIG_FUZZ
	return( CRYPT_OK );
#endif /* CONFIG_FUZZ */

	/* Check that the trust information is valid and get a pointer to the
	   trust information index */
	trustInfoIndex = getCheckTrustInfo( trustInfo );
	ENSURES( trustInfoIndex != NULL );

	/* If we're being fed certificate data, make sure that it's valid as a
	   certificate.  We could in theory just parse out the subject DN but 
	   this means that when we later try and instantiate it we'll get an
	   error if it's not valid certificate data, so this serves as both a
	   check that it at least looks like a certificate and a means of 
	   getting at the identification information without using a lot of 
	   custom code */
	if( certObject != NULL )
		{
		MESSAGE_CREATEOBJECT_INFO createInfo;
		ERROR_INFO localErrorInfo;

		clearErrorInfo( &localErrorInfo );
		setMessageCreateObjectIndirectInfoEx( &createInfo, certObject,
											  certObjectLength,
											  CRYPT_CERTTYPE_CERTIFICATE,
											  KEYMGMT_FLAG_DATAONLY_CERT, 
											  &localErrorInfo );
		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
								  IMESSAGE_DEV_CREATEOBJECT_INDIRECT, 
								  &createInfo, OBJECT_TYPE_CERTIFICATE );
		if( cryptStatusError( status ) )
			{
			DEBUG_DIAG(( "Trusted certificate data is invalid" ));
			return( status );
			}
		iLocalCert = createInfo.cryptHandle;
		}
	else
		{
		CRYPT_CONTEXT iCryptContext;

		/* We're adding a certificate, check whether it has a context 
		   attached and if it does, whether it's a public-key context.  
		   If there's no context attached (it's a data-only certificate) or 
		   the attached context is a private-key context (which we don't 
		   want to leave hanging around in memory, or which could be in a 
		   removable crypto device) we don't try and use the certificate but 
		   instead add the certificate data and then later re-instantiate a 
		   new certificate with attached public-key context if required */
		status = krnlSendMessage( iCryptCert, IMESSAGE_GETDEPENDENT,
								  &iCryptContext, OBJECT_TYPE_CONTEXT );
		if( cryptStatusError( status ) )
			{
			/* There's no context associated with this certificate, we'll 
			   have to recreate it later */
			recreateCert = TRUE;
			}
		else
			{
			if( checkContextCapability( iCryptContext, 
										MESSAGE_CHECK_PKC_PRIVATE ) )
				{
				/* The context associated with the certificate is a private-
				   key context, recreate it later as a public-key context */
				recreateCert = TRUE;
				}
			}
		iLocalCert = iCryptCert;
		}

	/* Get the ID information for the certificate by generating the checksum 
	   and hash of the certificate object's subject name and key ID */
	status = dynCreate( &subjectDB, iLocalCert, CRYPT_IATTRIBUTE_SUBJECT );
	if( cryptStatusError( status ) )
		{
		if( certObject != NULL )
			krnlSendNotifier( iLocalCert, IMESSAGE_DECREFCOUNT );
		return( status );
		}
	sCheck = checksumData( dynData( subjectDB ), dynLength( subjectDB ) );
	hashData( sHash, HASH_DATA_SIZE, dynData( subjectDB ), 
			  dynLength( subjectDB ) );
	dynDestroy( &subjectDB );
#if 0	/* sKID lookup isn't used at present */
	DYNBUF subjectKeyDB;
	status = dynCreate( &subjectKeyDB, iLocalCert, 
						CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER );
	if( cryptStatusOK( status ) )
		{
		kCheck = checksumData( dynData( subjectKeyDB ), 
							   dynLength( subjectKeyDB ) );
		hashData( kHash, HASH_DATA_SIZE, dynData( subjectKeyDB ), 
				  dynLength( subjectKeyDB ) );
		dynDestroy( &subjectKeyDB );
		}
#endif /* 0 */
	if( certObject != NULL )
		krnlSendNotifier( iLocalCert, IMESSAGE_DECREFCOUNT );

	/* Find the insertion point */
	status = findInsertionPoint( trustInfoIndex, sCheck, 
								 sHash, HASH_DATA_SIZE, 
								 &trustInfoIndexEntryPtr, 
								 &trustInfoListEnd );
	if( cryptStatusError( status ) )
		return( status );

	/* Allocate memory for the new element and copy the information across */
	REQUIRES( isShortIntegerRangeNZ( sizeof( TRUST_INFO ) ) );
	if( ( newElement  = ( TRUST_INFO * ) \
				clAlloc( "addEntry", sizeof( TRUST_INFO ) ) ) == NULL )
		return( CRYPT_ERROR_MEMORY );
	memset( newElement, 0, sizeof( TRUST_INFO ) );
	newElement->sCheck = sCheck;
	memcpy( newElement->sHash, sHash, HASH_DATA_SIZE );
	DATAPTR_SET( newElement->certObject, NULL );
	DATAPTR_SET( newElement->prev, NULL );
	DATAPTR_SET( newElement->next, NULL );
	if( certObject != NULL || recreateCert )
		{
		DYNBUF certDB;
		void *objectPtr;
		int objectLength = certObjectLength;

		/* If we're using the data from an existing certificate object we 
		   need to extract the encoded data */
		if( recreateCert )
			{
			/* Get the encoded certificate */
			status = dynCreateCert( &certDB, iLocalCert, 
									CRYPT_CERTFORMAT_CERTIFICATE );
			if( cryptStatusError( status ) )
				{
				clFree( "addEntry", newElement );
				return( status );
				}
			certObject = dynData( certDB );
			objectLength = dynLength( certDB );
			}

		/* Remember the trusted certificate data for later use */
		REQUIRES_PTR( isShortIntegerRangeNZ( objectLength ),
					  newElement );
		if( ( objectPtr = clAlloc( "addEntry", objectLength ) ) == NULL )
			{
			clFree( "addEntry", newElement );
			if( recreateCert )
				dynDestroy( &certDB );
			return( CRYPT_ERROR_MEMORY );
			}
		memcpy( objectPtr, certObject, objectLength );
		DATAPTR_SET( newElement->certObject, objectPtr );
		newElement->certObjectLength = objectLength;
		newElement->certChecksum = checksumData( objectPtr, objectLength );
		newElement->iCryptCert = CRYPT_ERROR;

		/* Clean up */
		if( recreateCert )
			dynDestroy( &certDB );
		}
	else
		{
		/* The trusted key exists as a standard certificate with a public-
		   key context attached, remember it for later */
		krnlSendNotifier( iLocalCert, IMESSAGE_INCREFCOUNT );
		newElement->iCryptCert = iLocalCert;
		}
	ENSURES_PTR( sanityCheckTrustInfo( newElement ),
				 newElement );
				 /* This doesn't free newElement->certObject, but then it's 
				    also a should-never-occur failure condition that would
				    trigger a general exit */

	/* Add the new entry to the list */
	insertDoubleListElement( trustInfoIndexEntryPtr, trustInfoListEnd, 
							 newElement, TRUST_INFO );
	updateTrustInfoChecksum( trustInfo );

	return( CRYPT_OK );
	}

CHECK_RETVAL \
int addTrustEntry( IN_DATAPTR const DATAPTR trustInfo, 
				   IN_HANDLE_OPT const CRYPT_CERTIFICATE iCryptCert,
				   IN_BUFFER_OPT( certObjectLength ) const void *certObject, 
				   IN_LENGTH_SHORT_Z const int certObjectLength,
				   IN_BOOL const BOOLEAN addSingleCert )
	{
	BOOLEAN itemAdded = FALSE;
	int status;

	assert( ( certObject == NULL && certObjectLength == 0 && \
			  isHandleRangeValid( iCryptCert ) ) || \
			( isReadPtrDynamic( certObject, certObjectLength ) && \
			  iCryptCert == CRYPT_UNUSED ) );

	REQUIRES( DATAPTR_ISSET( trustInfo ) );
	REQUIRES( ( certObject == NULL && certObjectLength == 0 && \
				isHandleRangeValid( iCryptCert ) ) || \
			  ( certObject != NULL && \
			    isShortIntegerRangeMin( certObjectLength, 
										MIN_CRYPT_OBJECTSIZE ) && \
				iCryptCert == CRYPT_UNUSED ) );
	REQUIRES( isBooleanValue( addSingleCert ) );

	/* If we're adding encoded certificate data then we can add it 
	   directly */
	if( certObject != NULL )
		{
		return( addEntry( trustInfo, CRYPT_UNUSED, certObject, 
						  certObjectLength ) );
		}

	/* Add the certificate/each certificate in the trust list */
	status = krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE,
							  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
	if( cryptStatusError( status ) )
		return( status );
	if( addSingleCert )
		{
		status = addEntry( trustInfo, iCryptCert, NULL, 0 );
		if( cryptStatusOK( status ) )
			itemAdded = TRUE;
		}
	else
		{
		int loopStatus, LOOP_ITERATOR;

		/* It's a trust list, move to the start of the list and add each
		   certificate in it */
		status = krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE,
								  MESSAGE_VALUE_CURSORFIRST,
								  CRYPT_CERTINFO_CURRENT_CERTIFICATE );
		if( cryptStatusError( status ) )
			{
			( void ) krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE, 
									  MESSAGE_VALUE_FALSE, 
									  CRYPT_IATTRIBUTE_LOCKED );
			return( status );
			}
		LOOP_MED( loopStatus = CRYPT_OK, cryptStatusOK( loopStatus ),
				  loopStatus = krnlSendMessage( iCryptCert, 
									IMESSAGE_SETATTRIBUTE,
									MESSAGE_VALUE_CURSORNEXT,
									CRYPT_CERTINFO_CURRENT_CERTIFICATE ) )
			{
			ENSURES( LOOP_INVARIANT_MED_GENERIC() );

			/* An item being added may already be present, however we can't 
			   fail immediately because what's being added may be a chain 
			   containing further certificates so we keep track of whether 
			   we've successfully added at least one item and clear data 
			   duplicate errors */
			status = addEntry( trustInfo, iCryptCert, NULL, 0 );
			if( cryptStatusError( status ) )
				{
				if( status != CRYPT_ERROR_DUPLICATE )
					break;
				status = CRYPT_OK;
				}
			else
				itemAdded = TRUE;
			}
		ENSURES( LOOP_BOUND_OK );
		}
	( void ) krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE, 
							  MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
	if( cryptStatusError( status ) )
		return( status );
	if( !itemAdded )
		{
		/* We reached the end of the certificate chain without finding 
		   anything that we could add, return a data duplicate error */
		return( CRYPT_ERROR_DUPLICATE ); 
		}

	return( CRYPT_OK );
	}

RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
int deleteTrustEntry( IN_DATAPTR const DATAPTR trustInfo, 
					  IN_PTR TYPECAST( TRUST_INFO * ) \
						struct TI *entryToDeletePtr )
	{
	DATAPTR *trustInfoIndex;
	TRUST_INFO *entryToDelete = ( TRUST_INFO * ) entryToDeletePtr;
	int trustInfoEntry;

	assert( isWritePtr( entryToDeletePtr, sizeof( TRUST_INFO ) ) );

	REQUIRES( DATAPTR_ISSET( trustInfo ) );
	REQUIRES( sanityCheckTrustInfo( entryToDelete ) );

	/* Check that the trust information is valid and get a pointer to the
	   trust information index */
	trustInfoIndex = ( DATAPTR * ) getCheckTrustInfo( trustInfo );
	ENSURES( trustInfoIndex != NULL );

	trustInfoEntry = entryToDelete->sCheck & ( TRUSTINFO_SIZE - 1 );
	REQUIRES( trustInfoEntry >= 0 && trustInfoEntry < TRUSTINFO_SIZE );

	/* Free the trust information entry contents */
	if( entryToDelete->iCryptCert != CRYPT_ERROR )
		krnlSendNotifier( entryToDelete->iCryptCert, IMESSAGE_DECREFCOUNT );
	if( DATAPTR_ISSET( entryToDelete->certObject ) )
		{
		void *certObjectPtr = DATAPTR_GET( entryToDelete->certObject );

		ENSURES( certObjectPtr != NULL );
		REQUIRES( isShortIntegerRangeNZ( entryToDelete->certObjectLength ) ); 
		zeroise( certObjectPtr, entryToDelete->certObjectLength );
		clFree( "deleteTrustEntry", certObjectPtr );
		certObjectPtr = NULL;
		}

	/* Remove the trust entry from the list */
	deleteDoubleListElement( &trustInfoIndex[ trustInfoEntry ], entryToDelete, 
							 TRUST_INFO );
	updateTrustInfoChecksum( trustInfo );

	/* Clear all data in the trust entry and free the memory */
	zeroise( entryToDelete, sizeof( TRUST_INFO ) );
	clFree( "deleteTrustEntry", entryToDelete );

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*				Init/Shut down Trusted Certificate Information				*
*																			*
****************************************************************************/

/* Initialise and shut down the trust information */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int initTrustInfo( OUT_DATAPTR DATAPTR *trustInfoPtr )
	{
	TRUST_INFO_CONTAINER *trustInfoContainer = \
							getBuiltinStorage( BUILTIN_STORAGE_TRUSTMGR );
	LOOP_INDEX i;

	assert( isWritePtr( trustInfoPtr, sizeof( DATAPTR ) ) );

	REQUIRES( checkBuiltinStorage( BUILTIN_STORAGE_TRUSTMGR ) );

	/* Clear return value */
	DATAPTR_SET_PTR( trustInfoPtr, NULL );

	/* Initialise the trust information table */
	memset( trustInfoContainer, 0, sizeof( TRUST_INFO_CONTAINER ) );
	LOOP_EXT( i = 0, i < TRUSTINFO_SIZE, i++, TRUSTINFO_SIZE + 1 )
		{
		ENSURES( LOOP_INVARIANT_EXT( i, 0, TRUSTINFO_SIZE - 1,
									 TRUSTINFO_SIZE + 1 ) );

		DATAPTR_SET( trustInfoContainer->trustInfo[ i ], NULL );
		}
	ENSURES( LOOP_BOUND_OK );
	DATAPTR_SET_PTR( trustInfoPtr, trustInfoContainer );
	updateTrustInfoChecksum( *trustInfoPtr );

	ENSURES( getCheckTrustInfo( *trustInfoPtr ) != NULL );

	ENSURES( checkBuiltinStorage( BUILTIN_STORAGE_TRUSTMGR ) );

	return( CRYPT_OK );
	}

void endTrustInfo( IN_DATAPTR const DATAPTR trustInfo )
	{
	DATAPTR *trustInfoIndex;
	LOOP_INDEX i;

	REQUIRES_V( DATAPTR_ISSET( trustInfo ) );

	/* Check that the trust information is valid and get a pointer to the
	   trust information index */
	trustInfoIndex = ( DATAPTR * ) getCheckTrustInfo( trustInfo );
	ENSURES_V( trustInfoIndex != NULL );

	/* Destroy the chain of items at each table position */
	LOOP_EXT( i = 0, i < TRUSTINFO_SIZE, i++, TRUSTINFO_SIZE + 1 )
		{
		LOOP_INDEX_PTR_ALT TRUST_INFO *trustInfoCursor;

		ENSURES_V( LOOP_INVARIANT_EXT( i, 0, TRUSTINFO_SIZE - 1,
									   TRUSTINFO_SIZE + 1 ) );

		/* Destroy any items in the list */
		LOOP_MED_INITCHECK_ALT( trustInfoCursor = DATAPTR_GET( trustInfoIndex[ i ] ), 
								trustInfoCursor != NULL )
			{
			TRUST_INFO *itemToFree = trustInfoCursor;

			ENSURES_V( LOOP_INVARIANT_MED_ALT_GENERIC() );

			trustInfoCursor = DATAPTR_GET( trustInfoCursor->next );
			deleteTrustEntry( trustInfo, itemToFree );
			}
		ENSURES_V( LOOP_BOUND_OK_ALT );

		/* Clear the list head */
		DATAPTR_SET( trustInfoIndex[ i ], NULL );
		}
	ENSURES_V( LOOP_BOUND_OK );

	updateTrustInfoChecksum( trustInfo );
	}
#endif /* USE_CERTIFICATES */
