/****************************************************************************
*																			*
*							Set Certificate Components						*
*						Copyright Peter Gutmann 1997-2025					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "cert.h"
  #include "certattr.h"
  #include "asn1_ext.h"
#else
  #include "cert/cert.h"
  #include "cert/certattr.h"
  #include "enc_dec/asn1_ext.h"
#endif /* Compiler-specific includes */

#ifdef USE_CERTIFICATES

/* Check whether anyone ever uses the higher compliance levels, this will
   produce a (harmless) compile error if built with 
   USE_CERTLEVEL_PKIX_FULL.  This check was added in cryptlib 3.4.3, 
   February 2016, and has never resulted in a problem report */

#if defined( USE_CERTLEVEL_PKIX_FULL ) && !defined( CONFIG_FUZZ )
  #if !( defined( _MSC_VER ) && \
		 ( ( _MSC_VER == 1500 ) || \
		   ( _MSC_VER == VS_LATEST_VERSION && defined( CRYPTLIB_BUILD ) ) ) )
	#error If you see this message, please let the cryptlib developers know, then remove the error directive that caused it and recompile as normal.
  #endif /* Debug build */
#endif /* USE_CERTLEVEL_PKIX_FULL */

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

#if defined( USE_CERTREQ ) || defined( USE_CERTREV )

/* Copy the encoded issuer DN */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyIssuerDnData( INOUT_PTR CERT_INFO *destCertInfoPtr,
							 const CERT_INFO *srcCertInfoPtr )
	{
	void *dnDataPtr;

	assert( isWritePtr( destCertInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isReadPtr( srcCertInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( destCertInfoPtr->issuerDNptr == NULL );
	REQUIRES( srcCertInfoPtr->issuerDNptr != NULL );

	REQUIRES( isShortIntegerRangeNZ( srcCertInfoPtr->issuerDNsize ) );
	if( ( dnDataPtr = clAlloc( "copyIssuerDnData",
							   srcCertInfoPtr->issuerDNsize ) ) == NULL )
		return( CRYPT_ERROR_MEMORY );
	memcpy( dnDataPtr, srcCertInfoPtr->issuerDNptr,
			srcCertInfoPtr->issuerDNsize );
	destCertInfoPtr->issuerDNptr = destCertInfoPtr->issuerDNdata = dnDataPtr;
	destCertInfoPtr->issuerDNsize = srcCertInfoPtr->issuerDNsize;

	return( CRYPT_OK );
	}

/* Copy revocation information into a CRL */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyRevocationInfo( INOUT_PTR CERT_INFO *certInfoPtr,
							   const CERT_INFO *revInfoPtr )
	{
	REVOCATION_INFO *revocationInfoPtr;
	const void *serialNumberPtr;
	int serialNumberLength, status = CRYPT_OK;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isReadPtr( revInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isReadPtrDynamic( revInfoPtr->issuerDNptr, 
							  revInfoPtr->issuerDNsize ) );

	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CRL );
	REQUIRES( revInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  revInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN || \
			  revInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION );
	REQUIRES( revInfoPtr->issuerDNptr != NULL );

	/* If there's an issuer name recorded, make sure that it matches the one 
	   in the certificate that's being added */
	if( certInfoPtr->issuerDNptr != NULL )
		{
		if( certInfoPtr->issuerDNsize != revInfoPtr->issuerDNsize || \
			memcmp( certInfoPtr->issuerDNptr, revInfoPtr->issuerDNptr,
					certInfoPtr->issuerDNsize ) )
			{
			setObjectErrorInfo( certInfoPtr, CRYPT_CERTINFO_ISSUERNAME,
								CRYPT_ERRTYPE_ATTR_VALUE );
			retExt( CRYPT_ERROR_INVALID,
					( CRYPT_ERROR_INVALID, CERTIFICATE_ERRINFO,
					  "%s issuer name doesn't match %s issuer name",
					  getCertTypeNameLC( certInfoPtr->type ),
					  getCertTypeName( revInfoPtr->type ) ) );
			}
		}
	else
		{
		/* There's no issuer name present yet, set the CRL issuer name to 
		   the certificate's issuer.  This is to make sure that we can't add 
		   certificates or sign the CRL with a different issuer.  We do this 
		   here rather than after setting the revocation list entry because 
		   of the difficulty of undoing the revocation entry addition */
		status = copyIssuerDnData( certInfoPtr, revInfoPtr );
		if( cryptStatusError( status ) )
			{
			retExt( status,
					( status, CERTIFICATE_ERRINFO,
					  "Couldn't copy issuer name to %s",
					  getCertTypeNameLC( certInfoPtr->type ) ) );
			}
		}

	/* Add the certificate information to the revocation list and make it 
	   the currently selected entry.  The ID type isn't quite an
	   issuerAndSerialNumber but the checking code eventually converts it 
	   into this form using the supplied issuer certificate DN */
	if( revInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION )
		{
		serialNumberPtr = revInfoPtr->cCertReq->serialNumber;
		serialNumberLength = revInfoPtr->cCertReq->serialNumberLength;
		}
	else
		{
		serialNumberPtr = revInfoPtr->cCertCert->serialNumber;
		serialNumberLength = revInfoPtr->cCertCert->serialNumberLength;
		}
	status = addRevocationEntry( &certInfoPtr->cCertRev->revocations,
								 &revocationInfoPtr, 
								 CRYPT_IKEYID_ISSUERANDSERIALNUMBER,
								 serialNumberPtr, serialNumberLength, FALSE );
	if( cryptStatusError( status ) )
		{
		if( status == CRYPT_ERROR_DUPLICATE )
			{
			/* If this certificate is already present in the list set the 
			   extended error code for it */
			setObjectErrorInfo( certInfoPtr, CRYPT_CERTINFO_CERTIFICATE,
								CRYPT_ERRTYPE_ATTR_PRESENT );
			retExt( status,
					( status, CERTIFICATE_ERRINFO,
					  "Certificate is already present in %s",
					  getCertTypeNameLC( certInfoPtr->type ) ) );
			}
		return( status );
		}
	DATAPTR_SET( certInfoPtr->cCertRev->currentRevocation, 
				 revocationInfoPtr );

	return( CRYPT_OK );
	}
#endif /* USE_CERTREQ || USE_CERTREV */

#if defined( USE_CERTREV ) || defined( USE_CERTVAL )

/* Copy an RTCS or OCSP responder URL from a certificate into a request */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyResponderURL( INOUT_PTR CERT_INFO *requestInfoPtr,
							 INOUT_PTR CERT_INFO *userCertInfoPtr )
	{
	const CRYPT_ATTRIBUTE_TYPE aiaAttribute = \
				( requestInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST ) ? \
				CRYPT_CERTINFO_AUTHORITYINFO_RTCS : \
				CRYPT_CERTINFO_AUTHORITYINFO_OCSP;
	SELECTION_STATE savedState;
	void *responderUrl;
	int urlSize DUMMY_INIT, status;

	REQUIRES( requestInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST || \
			  requestInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST );
	REQUIRES( userCertInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  userCertInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );

	/* There's no responder URL set, check whether the user certificate 
	   contains a responder URL in the RTCS/OCSP authorityInfoAccess 
	   GeneralName */
	saveSelectionState( savedState, userCertInfoPtr );
	status = selectGeneralName( userCertInfoPtr, aiaAttribute,
								MAY_BE_ABSENT );
	if( cryptStatusOK( status ) )
		{
		status = selectGeneralName( userCertInfoPtr,
									CRYPT_ATTRIBUTE_NONE,
									MUST_BE_PRESENT );
		}
	if( cryptStatusOK( status ) )
		{
		status = getCertComponentString( userCertInfoPtr,
								CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER,
								NULL, 0, &urlSize );
		}
	if( cryptStatusError( status ) )
		{
		/* If there's no responder URL present then it's not a (fatal) 
		   error */
		restoreSelectionState( savedState, userCertInfoPtr );
		return( CRYPT_OK );
		}

	/* There's a responder URL present, copy it to the request */
	REQUIRES( isShortIntegerRangeNZ( urlSize ) );
	if( ( responderUrl = clAlloc( "copyResponderURL", urlSize ) ) == NULL )
		{
		restoreSelectionState( savedState, userCertInfoPtr );
		return( CRYPT_ERROR_MEMORY );
		}
	status = getCertComponentString( userCertInfoPtr,
									 CRYPT_CERTINFO_UNIFORMRESOURCEIDENTIFIER,
									 responderUrl, urlSize, &urlSize );
	if( cryptStatusOK( status ) )
		{
		if( requestInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST )
			{
			REQUIRES( requestInfoPtr->cCertVal->responderUrl == NULL );

			requestInfoPtr->cCertVal->responderUrl = responderUrl;
			requestInfoPtr->cCertVal->responderUrlSize = urlSize;
			}
		else
			{
			REQUIRES( requestInfoPtr->cCertRev->responderUrl == NULL );

			requestInfoPtr->cCertRev->responderUrl = responderUrl;
			requestInfoPtr->cCertRev->responderUrlSize = urlSize;
			}
		}
	else
		{
		clFree( "copyResponderURL", responderUrl );
		}
	restoreSelectionState( savedState, userCertInfoPtr );

	return( status );
	}
#endif /* USE_CERTREV || USE_CERTVAL */

/****************************************************************************
*																			*
*							Copy Certificate Data							*
*																			*
****************************************************************************/

/* Check that the key in a XYZZY certificate is signature-capable (or at 
   least sig-check capable, since it may be just the public key that's being 
   used), since XYZZY certificates are self-signed.  Then set the 
   appropriate signature keyUsage and optional encryption usage if the key 
   can also do that */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int setXyzzyKeyUsage( INOUT_PTR CERT_INFO *certInfoPtr,
							 IN_HANDLE const CRYPT_CONTEXT iCryptContext )
	{
	int keyUsage = KEYUSAGE_SIGN | KEYUSAGE_CA;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( isHandleRangeValid( iCryptContext ) );

	/* Make sure that the key is signature-capable.  We have to check for a 
	   capability to either sign or sig-check since a pure public key will 
	   only have a sig-check capability while a private key held in a device 
	   (from which we're going to extract the public-key components) may be 
	   only signature-capable */
	if( !checkContextCapability( iCryptContext, 
								 MESSAGE_CHECK_PKC_SIGCHECK ) && \
		!checkContextCapability( iCryptContext, 
								 MESSAGE_CHECK_PKC_SIGN ) )
		{
		setObjectErrorInfo( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE,
							CRYPT_ERRTYPE_ATTR_VALUE );
		retExt( CRYPT_ERROR_INVALID,
				( CRYPT_ERROR_INVALID, CERTIFICATE_ERRINFO,
				  "XYZZY %s must be signature-capable to create a "
				  "self-signed certificate",
				  getCertTypeNameLC( certInfoPtr->type ) ) );
		}

	/* If the key is encryption-capable (with the same caveat as for the 
	   signature check above), enable that usage as well */
	if( checkContextCapability( iCryptContext, 
								MESSAGE_CHECK_PKC_ENCRYPT ) || \
		checkContextCapability( iCryptContext, 
								MESSAGE_CHECK_PKC_DECRYPT ) )
		keyUsage |= CRYPT_KEYUSAGE_KEYENCIPHERMENT;

	/* Clear the existing usage and replace it with our usage.  See the 
	   comment in setXyzzyInfo() for why it's done this way */
	( void ) deleteCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE );
	return( addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE, 
							  keyUsage ) );
	}

/* Copy public key data into a certificate object */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int copyKeyFromCertificate( INOUT_PTR CERT_INFO *destCertInfoPtr,
								   const CERT_INFO *srcCertInfoPtr )
	{
	void *publicKeyInfoPtr;

	assert( isWritePtr( destCertInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isReadPtr( srcCertInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( destCertInfoPtr->publicKeyInfo == NULL );
	REQUIRES( srcCertInfoPtr->publicKeyInfo != NULL );

	REQUIRES( isShortIntegerRangeNZ( srcCertInfoPtr->publicKeyInfoSize ) );
	if( ( publicKeyInfoPtr = \
				clAlloc( "copyKeyFromCertificate", 
						 srcCertInfoPtr->publicKeyInfoSize ) ) == NULL )
		return( CRYPT_ERROR_MEMORY );
	memcpy( publicKeyInfoPtr, srcCertInfoPtr->publicKeyInfo, 
			srcCertInfoPtr->publicKeyInfoSize );
	destCertInfoPtr->publicKeyData = \
		destCertInfoPtr->publicKeyInfo = publicKeyInfoPtr;
	destCertInfoPtr->publicKeyInfoSize = srcCertInfoPtr->publicKeyInfoSize;
	destCertInfoPtr->publicKeyAlgo = srcCertInfoPtr->publicKeyAlgo;
	destCertInfoPtr->publicKeyFeatures = srcCertInfoPtr->publicKeyFeatures;
	memcpy( destCertInfoPtr->publicKeyID, srcCertInfoPtr->publicKeyID, 
			KEYID_SIZE );
	SET_FLAG( destCertInfoPtr->flags, CERT_FLAG_DATAONLY );

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int copyKeyFromContext( INOUT_PTR CERT_INFO *destCertInfoPtr,
							   IN_HANDLE const CRYPT_CONTEXT iCryptContext )
	{
	MESSAGE_DATA msgData;
	void *publicKeyInfoPtr;
	int length, status;

	assert( isWritePtr( destCertInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( destCertInfoPtr->publicKeyInfo == NULL );
	REQUIRES( isHandleRangeValid( iCryptContext ) );

	/* Get the key metadata from the context */
	status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
							  &destCertInfoPtr->publicKeyAlgo,
							  CRYPT_CTXINFO_ALGO );
	if( cryptStatusOK( status ) )
		{
		status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
								  &destCertInfoPtr->publicKeyFeatures,
								  CRYPT_IATTRIBUTE_KEYFEATURES );
		}
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, destCertInfoPtr->publicKeyID, KEYID_SIZE );
		status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
								  &msgData, CRYPT_IATTRIBUTE_KEYID );
		}
	if( cryptStatusError( status ) )
		return( status );

	/* Copy over the public-key data.  We perform a copy rather than keeping 
	   a reference to the context for two reasons.  Firstly, when the 
	   certificate is transitioned into the high state it will constrain the 
	   attached context so a context shared between two certificates could 
	   be constrained in unexpected ways.  Secondly, the context could be a 
	   private-key context and attaching that to a certificate would be 
	   rather inappropriate.  Furthermore, the constraint issue is even more 
	   problematic in that a context constrained by an encryption-only 
	   request could then no longer be used to sign the request or a PKI 
	   protocol message containing the request */
	setMessageData( &msgData, NULL, 0 );
	status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
							  &msgData, CRYPT_IATTRIBUTE_KEY_SPKI );
	if( cryptStatusError( status ) )
		return( status );
	length = msgData.length;
	REQUIRES( isShortIntegerRangeNZ( length ) );
	if( ( publicKeyInfoPtr = clAlloc( "copyKeyFromContext", 
									  length ) ) == NULL )
		return( CRYPT_ERROR_MEMORY );
	setMessageData( &msgData, publicKeyInfoPtr, length );
	status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
							  &msgData, CRYPT_IATTRIBUTE_KEY_SPKI );
	if( cryptStatusError( status ) )
		{
		clFree( "copyKeyFromContext", publicKeyInfoPtr );
		return( status );
		}
	destCertInfoPtr->publicKeyData = \
		destCertInfoPtr->publicKeyInfo = publicKeyInfoPtr;
	destCertInfoPtr->publicKeyInfoSize = length;
	SET_FLAG( destCertInfoPtr->flags, CERT_FLAG_DATAONLY );

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int copyPublicKeyInfo( INOUT_PTR CERT_INFO *certInfoPtr,
					   IN_HANDLE_OPT const CRYPT_HANDLE cryptHandle,
					   IN_PTR_OPT const CERT_INFO *srcCertInfoPtr )
	{
	CRYPT_CONTEXT iCryptContext;
	BOOLEAN_INT isXyzzyCert;
	int status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( ( isHandleRangeValid( cryptHandle ) && \
			  srcCertInfoPtr == NULL ) || \
			( cryptHandle == CRYPT_UNUSED && \
			  isReadPtr( srcCertInfoPtr, sizeof( CERT_INFO ) ) ) );

	REQUIRES( sanityCheckCert( certInfoPtr ) );
	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \
			  certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT );
	REQUIRES( ( isHandleRangeValid( cryptHandle ) && \
				srcCertInfoPtr == NULL ) || \
			  ( cryptHandle == CRYPT_UNUSED && \
			    srcCertInfoPtr != NULL ) );
	REQUIRES( srcCertInfoPtr == NULL || \
			  sanityCheckCert( srcCertInfoPtr ) );
	REQUIRES( srcCertInfoPtr == NULL || \
			  srcCertInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \
			  srcCertInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT );

	/* Make sure that we haven't already got a public key present */
	if( certInfoPtr->iPubkeyContext != CRYPT_ERROR || \
		certInfoPtr->publicKeyInfo != NULL )
		{
		setObjectErrorInfo( certInfoPtr, 
							CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO,
							CRYPT_ERRTYPE_ATTR_PRESENT );
		retExt( CRYPT_ERROR_INITED,
				( CRYPT_ERROR_INITED, CERTIFICATE_ERRINFO,
				  "%s already contains a public key",
				  getCertTypeName( certInfoPtr->type ) ) );
		}

	/* If we've been given a data-only certificate then all that we need to 
	   do is copy over the public key data */
	if( srcCertInfoPtr != NULL )
		return( copyKeyFromCertificate( certInfoPtr, srcCertInfoPtr ) );

	/* Get the context handle.  All other checking has already been 
	   performed by the kernel */
	status = krnlSendMessage( cryptHandle, IMESSAGE_GETDEPENDENT, 
							  &iCryptContext, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		{
		setObjectErrorInfo( certInfoPtr, 
							CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO,
							CRYPT_ERRTYPE_ATTR_VALUE );
		return( status );
		}

	/* If it's a XYZZY certificate then we need to perform additional
	   processing for XYZZY-specific key usage requirements */
	status = getCertComponent( certInfoPtr, CRYPT_CERTINFO_XYZZY, 
							   &isXyzzyCert );
	if( cryptStatusOK( status ) && isXyzzyCert == TRUE )
		{
		status = setXyzzyKeyUsage( certInfoPtr, iCryptContext);
		if( cryptStatusError( status ) )
			return( status );
		}

	/* Copy the public-key data from the encryption context */
	return( copyKeyFromContext( certInfoPtr, iCryptContext ) );
	}

#ifdef USE_DBMS	/* Only used by CA code */

/* Sanitise certificate attributes based on a user-supplied template.  This 
   is used to prevent a user from supplying potentially dangerous attributes 
   in a certificate request, for example to request a CA certificate by 
   setting the basicConstraints/keyUsage = CA extensions in the request in a 
   manner that would result in the creation of a CA certificate when the 
   request is processed.  
   
   There are two ways to do this, either with a whitelist or with a 
   blocklist.  Obviously the preferred option is the whitelist one, but the
   problem with this is that without any knowledge of the environment into
   which it's being deployed we have no idea what should be permitted.  For
   example an RA, which acts as a trusted agent for the CA, probably wants
   fuller control over the form of the certificate with a wider selection of 
   attributes permitted while a CA dealing directly with the public probably 
   wants few or no attributes permitted.  This is a variation of the 
   firewall configuration problem, lock it down by default and users will 
   complain, leave it open by default and users are happy but it's not secure.

   What we use here is in effect a blend of whitelist and blocklist.  On
   the whitelist side the only attributes that are permitted in certificate 
   requests are:

	CRYPT_CERTINFO_CHALLENGEPASSWORD: Needed for SCEP.
	CRYPT_CERTINFO_KEYFEATURES: Only used by cryptlib for its own purposes.
	CRYPT_CERTINFO_KEYUSAGE
	CRYPT_CERTINFO_SUBJECTALTNAME
	CRYPT_CERTINFO_EXTKEYUSAGE

   For revocation requests we allow rather more values since they're 
   required by the client to inform the CA why the certificate is being
   revoked.  Except for the invalidity date they're not so much of a 
   security threat, and even for the date the consistency vs. accuracy issue 
   for CRLs debate means the CA will put who knows what date in the CRL 
   anyway:

	CRYPT_CERTINFO_CRLEXTREASON
	CRYPT_CERTINFO_CRLREASON
	CRYPT_CERTINFO_HOLDINSTRUCTIONCODE
	CRYPT_CERTINFO_INVALIDITYDATE
   
   Everything else, including the all-important basicConstraints, has to be 
   set explicitly by the CA and can't be specified in a request.

   On the blocklist side, multi-AVA RDNs are disallowed in order to prevent
   an attacker from playing games with DN forms.  This should be safe 
   because the only things that create such weird DNs tend to be European
   in-house CAs following (or justifying their bugs through) peculiar 
   requirements in digital signature legislation, and those guys will be 
   creating their own certificates from scratch themselves rather than using 
   cryptlib for it */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkCertWellFormed( INOUT_PTR CERT_INFO *certInfoPtr )
	{
	DATAPTR_ATTRIBUTE attributePtr;
	int status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	/* Check that the subject DN is well-formed */
	status = checkDN( certInfoPtr->subjectName, 
					  CHECKDN_FLAG_COUNTRY | CHECKDN_FLAG_COMMONNAME | \
							CHECKDN_FLAG_WELLFORMED,
					  &certInfoPtr->errorLocus,
					  &certInfoPtr->errorType );
	if( cryptStatusError( status ) )
		{
		retExt( status,
				( status, CERTIFICATE_ERRINFO,
				  "%s has a malformed subject DN",
				  getCertTypeName( certInfoPtr->type ) ) );
		}

	/* There can potentially be any number of other DNs in a certificate 
	   (typically hidden in GeneralName fields), it's not really certain 
	   what we should do with these or even how we can effectively track 
	   them all down.  Since the reason why we're doing this in the first 
	   place is to avoid shenanigans due to the more or less arbitrary 
	   handling of complex DNs by implementations, and the only one that 
	   anyone really pays any attention to is the subject name, we check 
	   the subject name and at least have a go at the subjectAltName, but 
	   leave the rest (it's not even certain what a DN in, say, an AIA
	   would mean, let alone how to check it) */
	attributePtr = findAttribute( certInfoPtr->attributes,
								  CRYPT_CERTINFO_SUBJECTALTNAME, TRUE );
	if( DATAPTR_ISSET( attributePtr ) )
		{
		attributePtr = findAttributeField( attributePtr, 
										   CRYPT_CERTINFO_SUBJECTALTNAME,
										   CRYPT_CERTINFO_DIRECTORYNAME );
		}
	if( DATAPTR_ISSET( attributePtr ) )
		{
		CRYPT_ATTRIBUTE_TYPE dummy1;
		CRYPT_ERRTYPE_TYPE dummy2;
		DATAPTR_DN dnPtr;

		status = getAttributeDataDN( attributePtr, &dnPtr );
		if( cryptStatusOK( status ) )
			{
			status = checkDN( dnPtr, CHECKDN_FLAG_COUNTRY | \
									 CHECKDN_FLAG_COMMONNAME | \
									 CHECKDN_FLAG_WELLFORMED,
							  &dummy1, &dummy2 );
			if( cryptStatusError( status ) )
				{
				/* Reporting this one is a bit complicated because we're 
				   dealing with a complex nested attribute, the best that we 
				   can do is report a general problem with the altName */
				setObjectErrorInfo( certInfoPtr, 
									CRYPT_CERTINFO_SUBJECTALTNAME,
									CRYPT_ERRTYPE_ATTR_VALUE );
				retExt( status,
						( status, CERTIFICATE_ERRINFO,
						  "%s has a malformed altName DN",
						  getCertTypeName( certInfoPtr->type ) ) );
				}
			}
		}

	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int sanitiseCertAttributes( INOUT_PTR CERT_INFO *certInfoPtr,
								   const DATAPTR_ATTRIBUTE templateListPtr )
	{
	LOOP_INDEX_PTR DATAPTR_ATTRIBUTE templateAttributeCursor;
	ATTRIBUTE_ENUM_INFO attrEnumInfo;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
	REQUIRES( DATAPTR_ISVALID( templateListPtr ) );

	/* If there are no attributes present or there's no disallowed-attribute 
	   template, we just need to apply the well-formedness check on the way
	   out */
	if( DATAPTR_ISNULL( certInfoPtr->attributes ) || \
		DATAPTR_ISNULL( templateListPtr ) )
		return( checkCertWellFormed( certInfoPtr ) );

	/* Walk down the template attribute list applying each one in turn to
	   the certificate attributes */
	LOOP_MAX( templateAttributeCursor = \
					getFirstAttribute( &attrEnumInfo, templateListPtr, \
									   ATTRIBUTE_ENUM_NONBLOB ), 
			  DATAPTR_ISSET( templateAttributeCursor ),
			  templateAttributeCursor = getNextAttribute( &attrEnumInfo ) )
		{
		CRYPT_ATTRIBUTE_TYPE fieldID, subFieldID;
		DATAPTR_ATTRIBUTE certAttributePtr;
		int status;

		ENSURES( LOOP_INVARIANT_MAX_GENERIC() );

		/* Check to see whether there's a constrained attribute present in
		   the certificate attributes and if it is, whether it conflicts 
		   with the constraining attribute */
		status = getAttributeIdInfo( templateAttributeCursor, NULL, 
									 &fieldID, &subFieldID );
		if( cryptStatusError( status ) )
			return( status );
		certAttributePtr = findAttributeField( certInfoPtr->attributes,
											   fieldID, subFieldID );
		if( DATAPTR_ISNULL( certAttributePtr ) )
			{
			/* There's nothing to constrain present in the certificate, 
			   continue */
			continue;
			}

		/* If the certificate attribute was provided through the application 
		   of PKI user data (indicated by it having the locked flag set), 
		   allow it even if it conflicts with the constraining attribute.  
		   This is permitted because the PKI user data was explicitly set by 
		   the issuing CA rather than being user-supplied in the certificate 
		   request so it has to be OK, or at least CA-approved */
		if( checkAttributeProperty( certAttributePtr, 
									ATTRIBUTE_PROPERTY_LOCKED ) )
			continue;

		/* There are conflicting attributes present, disallow the 
		   certificate issue */
		status = getAttributeIdInfo( certAttributePtr, NULL, &fieldID, 
									 NULL );
		if( cryptStatusError( status ) )
			return( status );
		if( fieldID == CRYPT_CERTINFO_KEYUSAGE )
			{
			int constrainedAttributeValue, constrainingAttributeValue;

			/* There is one special case in which conflicting attributes 
			   are allowed and that's for keyUsage (or more generally 
			   bitfields), since we can selectively disallow dangerous bit 
			   values while allowing safe ones */
			status = getAttributeDataValue( certAttributePtr, 
											&constrainedAttributeValue );
			if( cryptStatusError( status ) )
				return( status );
			status = getAttributeDataValue( templateAttributeCursor, 
											&constrainingAttributeValue );
			if( cryptStatusError( status ) )
				return( status );
			if( !( constrainedAttributeValue & constrainingAttributeValue ) )
				continue;
			}
		certInfoPtr->errorLocus = fieldID;
		certInfoPtr->errorType = CRYPT_ERRTYPE_ATTR_VALUE;

		retExt( CRYPT_ERROR_INVALID,
				( CRYPT_ERROR_INVALID, CERTIFICATE_ERRINFO,
				  "%s contains conflicting attributes",
				  getCertTypeName( certInfoPtr->type ) ) );
		}
	ENSURES( LOOP_BOUND_OK );

	return( checkCertWellFormed( certInfoPtr ) );
	}
#endif /* USE_DBMS*/

/****************************************************************************
*																			*
*						Copy Certificate Request Data						*
*																			*
****************************************************************************/

#ifdef USE_CERTREQ

/* Copy certificate request information into a certificate object.  This 
   copies the public key context, the DN, any valid attributes, and any other 
   relevant bits and pieces if it's a CRMF request */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyCertReqToCert( INOUT_PTR CERT_INFO *certInfoPtr,
							  INOUT_PTR CERT_INFO *certRequestInfoPtr )
	{
	int status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( certRequestInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
	REQUIRES( DATAPTR_ISNULL( certInfoPtr->subjectName ) );
	REQUIRES( certRequestInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \
			  certRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT );

	/* Copy the public key context, the DN, and the attributes.  Type
	   checking has already been performed by the kernel.  We copy the
	   attributes across after the DN because that copy is the hardest to
	   undo: If there are already attributes present then the copied 
	   attributes would be mixed in among them so it's not really possible 
	   to undo the copy later without performing a complex selective 
	   delete */
	status = copyDN( &certInfoPtr->subjectName, 
					 certRequestInfoPtr->subjectName );
	if( cryptStatusError( status ) )
		return( status );
	if( TEST_FLAG( certRequestInfoPtr->flags, CERT_FLAG_DATAONLY ) )
		{
		status = copyPublicKeyInfo( certInfoPtr, CRYPT_UNUSED,
									certRequestInfoPtr );
		}
	else
		{
		status = copyPublicKeyInfo( certInfoPtr,
									certRequestInfoPtr->iPubkeyContext,
									NULL );
		}
	if( cryptStatusOK( status ) && \
		DATAPTR_ISSET( certRequestInfoPtr->attributes ) )
		{
		status = copyAttributes( &certInfoPtr->attributes,
								 certRequestInfoPtr->attributes,
								 &certInfoPtr->errorLocus,
								 &certInfoPtr->errorType );
		}
	if( cryptStatusError( status ) )
		{
		deleteDN( &certInfoPtr->subjectName );
		return( status );
		}

	/* If it's a CRMF request there could also be a validity period
	   specified */
	if( certRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT )
		{
		const time_t currentTime = getTime( GETTIME_NOFAIL );

		/* If there's a valid time present in the request and it meets some 
		   basic conditions, copy it across.  Since these are trivial things 
		   we don't abort if there's a problem but leave the value unset so 
		   it'll be filled in when the certificate is issued, although we do
		   issue a diagnostic in debug mode to warn the caller that something
		   is slightly wrong */
		if( certRequestInfoPtr->startTime > MIN_TIME_VALUE )
			{
			/* We don't allow start times back- or forward-dated by more 
			   than a year */
			if( certRequestInfoPtr->startTime > currentTime - ( 86400L * 365 ) && \
				certRequestInfoPtr->startTime < currentTime + ( 86400L * 365 ) ) 
				{
				certInfoPtr->startTime = certRequestInfoPtr->startTime;
				}
			else
				{
				DEBUG_DIAG(( "Skipping setting start time %ld vs. current "
							 "time %ld", 
							 ( long ) certRequestInfoPtr->startTime, 
							 ( long ) currentTime ));
				}
			}
		if( certRequestInfoPtr->endTime > MIN_TIME_VALUE )
			{
			/* We don't allow end times before the current time or before 
			   the start time */
			if( certRequestInfoPtr->endTime > currentTime && \
				certRequestInfoPtr->endTime > certInfoPtr->startTime )
				{
				certInfoPtr->endTime = certRequestInfoPtr->endTime;
				}
			else
				{
				DEBUG_DIAG(( "Skipping setting end time %ld vs. current "
							 "time %ld", 
							 ( long ) certRequestInfoPtr->endTime, 
							 ( long ) currentTime ));
				}
			}
		}

	return( CRYPT_OK );
	}

/* Copy what we need to identify the certificate to be revoked and any 
   revocation information into a certificate object */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyRevReqToCert( INOUT_PTR CERT_INFO *certInfoPtr,
							 INOUT_PTR CERT_INFO *revRequestInfoPtr )
	{
	int status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( revRequestInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CRL );
	REQUIRES( revRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION );

	status = copyRevocationInfo( certInfoPtr, revRequestInfoPtr );
	if( cryptStatusError( status ) || \
		DATAPTR_ISNULL( revRequestInfoPtr->attributes ) )
		return( status );
	return( copyRevocationAttributes( &certInfoPtr->attributes,
									  revRequestInfoPtr->attributes ) );
	}

/* Copy the public key, DN, and any attributes that need to be copied across.  
   We copy the full DN rather than just the encoded form in case the user 
   wants to query the request details after creating it */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyCertToRequest( INOUT_PTR CERT_INFO *crmfRequestInfoPtr,
							  INOUT_PTR CERT_INFO *certInfoPtr,
							  const CRYPT_HANDLE iCryptHandle )
	{
	int status;

	assert( isWritePtr( crmfRequestInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( crmfRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT );
	REQUIRES( DATAPTR_ISNULL( crmfRequestInfoPtr->subjectName ) );
	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );

	status = copyDN( &crmfRequestInfoPtr->subjectName,
					 certInfoPtr->subjectName );
	if( cryptStatusError( status ) )
		return( status );
	if( crmfRequestInfoPtr->iPubkeyContext == CRYPT_ERROR && \
		crmfRequestInfoPtr->publicKeyInfo == NULL )
		{
		/* Only copy the key across if a key hasn't already been added 
		   earlier as CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO.  Checking for 
		   this special case (rather than returning an error) allows the DN 
		   information from an existing certificate to be copied into a 
		   request for a new key */
		status = copyPublicKeyInfo( crmfRequestInfoPtr, iCryptHandle, NULL );
		}
	if( cryptStatusOK( status ) )
		{
		/* We copy the attributes across after the DN because that copy is 
		   the hardest to undo: If there are already attributes present then 
		   the copied attributes will be mixed in among them so it's not 
		   really possible to undo the copy later without performing a 
		   complex selective delete */
		status = copyCRMFRequestAttributes( &crmfRequestInfoPtr->attributes,
											certInfoPtr->attributes );
		}
	if( cryptStatusError( status ) )
		deleteDN( &crmfRequestInfoPtr->subjectName );

	return( status );
	}

/* Copy across the issuer and subject DN and serial number */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyCertToRevRequest( INOUT_PTR CERT_INFO *crmfRevRequestInfoPtr,
								 INOUT_PTR CERT_INFO *certInfoPtr )
	{
	int status;

	assert( isWritePtr( crmfRevRequestInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( crmfRevRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION );
	REQUIRES( DATAPTR_ISNULL( crmfRevRequestInfoPtr->subjectName ) );
	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );

	/* If the information is already present then we can't add it again */
	if( DATAPTR_ISSET( crmfRevRequestInfoPtr->issuerName ) )
		{
		setObjectErrorInfo( crmfRevRequestInfoPtr,
							CRYPT_CERTINFO_CERTIFICATE,
							CRYPT_ERRTYPE_ATTR_PRESENT );
		retExt( CRYPT_ERROR_INITED,
				( CRYPT_ERROR_INITED, CERTIFICATE_ERRINFO,
				  "%s already contains an issuer name",
				  getCertTypeName( crmfRevRequestInfoPtr->type ) ) );
		}

	/* Copy across the issuer name and allocate the storage that we need to 
	   copy the subject name.  We don't care about any internal structure of 
	   the DNs so we just copy the pre-encoded form, we could in theory copy 
	   the full DN but it isn't really the issuer (creator) of the object so 
	   it's better if it appears to have no issuer DN than a misleading one */
	status = copyIssuerDnData( crmfRevRequestInfoPtr, certInfoPtr );
	if( cryptStatusError( status ) )
		return( status );
	status = setSerialNumber( crmfRevRequestInfoPtr,
							  certInfoPtr->cCertCert->serialNumber,
							  certInfoPtr->cCertCert->serialNumberLength );
	if( cryptStatusOK( status ) )
		{
		REQUIRES( isShortIntegerRangeNZ( certInfoPtr->subjectDNsize ) );
		crmfRevRequestInfoPtr->subjectDNdata = \
									clAlloc( "copyCertToRevRequest",
											 certInfoPtr->subjectDNsize );
		if( crmfRevRequestInfoPtr->subjectDNdata == NULL )
			status = CRYPT_ERROR_MEMORY;
		}
	if( cryptStatusError( status ) )
		{
		clFree( "copyCertToRevRequest", 
				crmfRevRequestInfoPtr->issuerDNdata );
		crmfRevRequestInfoPtr->issuerDNdata = \
			crmfRevRequestInfoPtr->issuerDNptr = NULL;
		crmfRevRequestInfoPtr->issuerDNsize = 0;
		if( crmfRevRequestInfoPtr->cCertCert->serialNumber != NULL && \
			crmfRevRequestInfoPtr->cCertCert->serialNumber != \
				crmfRevRequestInfoPtr->cCertCert->serialNumberBuffer )
			{
			clFree( "copyCertToRevRequest",
					crmfRevRequestInfoPtr->cCertCert->serialNumber );
			}
		crmfRevRequestInfoPtr->cCertCert->serialNumber = NULL;
		crmfRevRequestInfoPtr->cCertCert->serialNumberLength = 0;

		return( status );
		}

	/* Copy the certificate's subject DN for use in CMP.  This isn't used in 
	   the CRMF/certificate code itself, but it's required by the CMP 
	   protocol to fill a message header field that requires a DN.  Since 
	   the CMP client that's sending the CRMF request only has access to the 
	   revocation request but not the original certificate that was used to 
	   create it, we have to save this metadata in the request so that it 
	   can be read back later when it's required for CMP */
	ENSURES( rangeCheck( certInfoPtr->subjectDNsize, 
						 1, MAX_INTLENGTH_SHORT ) );
	memcpy( crmfRevRequestInfoPtr->subjectDNdata, certInfoPtr->subjectDNptr,
			certInfoPtr->subjectDNsize );
	crmfRevRequestInfoPtr->subjectDNptr = crmfRevRequestInfoPtr->subjectDNdata;
	crmfRevRequestInfoPtr->subjectDNsize = certInfoPtr->subjectDNsize;

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

/****************************************************************************
*																			*
*						Copy Certificate Revocation Data					*
*																			*
****************************************************************************/

#ifdef USE_CERTREV

/* The OCSP ID doesn't contain any usable fields so we pre-encode it when 
   the certificate is added to the OCSP request and treat it as a blob 
   thereafter */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int writeOCSPID( INOUT_PTR STREAM *stream, 
						const CERT_INFO *certInfoPtr,
						IN_BUFFER( issuerKeyHashLength ) \
							const void *issuerKeyHash,
						IN_LENGTH_FIXED( KEYID_SIZE ) \
							const int issuerKeyHashLength )
	{
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	BYTE hashBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
	int hashSize;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isReadPtrDynamic( issuerKeyHash, issuerKeyHashLength ) );

	REQUIRES( issuerKeyHashLength == KEYID_SIZE );
	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
	REQUIRES( certInfoPtr->issuerDNptr != NULL );
	REQUIRES( certInfoPtr->cCertCert->serialNumber != NULL );

	/* Get the issuerName hash */
	getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, 
							 &hashSize );
	hashFunctionAtomic( hashBuffer, CRYPT_MAX_HASHSIZE,
						certInfoPtr->issuerDNptr,
						certInfoPtr->issuerDNsize );

	/* Write the request data */
	writeSequence( stream,
			sizeofAlgoID( CRYPT_ALGO_SHA1 ) + \
			sizeofShortObject( hashSize ) + sizeofShortObject( hashSize ) + \
			sizeofInteger( certInfoPtr->cCertCert->serialNumber,
						   certInfoPtr->cCertCert->serialNumberLength ) );
	writeAlgoID( stream, CRYPT_ALGO_SHA1, DEFAULT_TAG );
	writeOctetString( stream, hashBuffer, hashSize, DEFAULT_TAG );
	writeOctetString( stream, issuerKeyHash, issuerKeyHashLength, 
					  DEFAULT_TAG );
	return( writeInteger( stream, certInfoPtr->cCertCert->serialNumber,
						  certInfoPtr->cCertCert->serialNumberLength,
						  DEFAULT_TAG ) );
	}

/* Copy revocation information from an OCSP request to a response */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyOcspReqToResp( INOUT_PTR CERT_INFO *certInfoPtr,
							  INOUT_PTR CERT_INFO *ocspRequestInfoPtr )
	{
	CERT_REV_INFO *certRevInfo = certInfoPtr->cCertRev;
	const CERT_REV_INFO *requestRevInfo = ocspRequestInfoPtr->cCertRev;
	int status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( ocspRequestInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE );
	REQUIRES( DATAPTR_ISNULL( certRevInfo->revocations ) );
	REQUIRES( ocspRequestInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST );

	/* Copy the revocation information and extensions */
	status = copyRevocationEntries( &certRevInfo->revocations,
									requestRevInfo->revocations );
	if( cryptStatusOK( status ) )
		{
		status = copyOCSPRequestAttributes( &certInfoPtr->attributes,
											ocspRequestInfoPtr->attributes );
		}
	if( cryptStatusError( status ) )
		return( status );

	return( CRYPT_OK );
	}

/* Copy the certificate information to the revocation list.  First we make 
   sure that the CA certificate hash (needed for OCSP's weird certificate 
   ID) is present.  We add the necessary information as a pre-encoded blob 
   since we can't do much with the ID fields */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyCertToOCSPRequest( INOUT_PTR CERT_INFO *ocspRequestInfoPtr,
								  INOUT_PTR CERT_INFO *certInfoPtr )
	{
	CERT_REV_INFO *certRevInfo = ocspRequestInfoPtr->cCertRev;
	REVOCATION_INFO *revInfoPtr DUMMY_INIT_PTR;
	STREAM stream;
	DYNBUF essCertDB;
	BYTE idBuffer[ 256 + 8 ], *idBufPtr = idBuffer;
	int idLength DUMMY_INIT, status;

	assert( isWritePtr( ocspRequestInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( ocspRequestInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST );
	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );

	/* Make sure that there's a CA certificate hash present */
	if( !ocspRequestInfoPtr->certHashSet )
		{
		setObjectErrorInfo( ocspRequestInfoPtr, CRYPT_CERTINFO_CACERTIFICATE,
							CRYPT_ERRTYPE_ATTR_ABSENT );
		retExt( CRYPT_ERROR_NOTINITED,
				( CRYPT_ERROR_NOTINITED, OCSPREQ_ERRINFO,
				  "%s doesn't contain CA certificate information",
				  getCertTypeName( ocspRequestInfoPtr->type ) ) );
		}

	/* Generate the OCSP certificate ID */
	sMemNullOpen( &stream );
	status = writeOCSPID( &stream, certInfoPtr, 
						  ocspRequestInfoPtr->certHash, KEYID_SIZE );
	if( cryptStatusOK( status ) )
		idLength = stell( &stream );
	sMemClose( &stream );
	if( cryptStatusError( status ) )
		return( status );
	if( idLength > 256 )
		{
		/* Allocate a buffer for an oversize ID */
		REQUIRES( isShortIntegerRangeNZ( idLength ) );
		if( ( idBufPtr = clDynAlloc( "copyCertToOCSPRequest", \
									 idLength ) ) == NULL )
			return( CRYPT_ERROR_MEMORY );
		}
	sMemOpen( &stream, idBufPtr, idLength );
	status = writeOCSPID( &stream, certInfoPtr, 
						  ocspRequestInfoPtr->certHash, KEYID_SIZE );
	sMemDisconnect( &stream );
	if( cryptStatusOK( status ) )
		{
		status = addRevocationEntry( &certRevInfo->revocations,
									 &revInfoPtr, CRYPT_KEYID_NONE, 
									 idBufPtr, idLength, FALSE );
		}
	if( idBufPtr != idBuffer )
		clFree( "copyCertToOCSPRequest", idBufPtr );
	if( cryptStatusError( status ) )
		{
		if( status == CRYPT_ERROR_DUPLICATE )
			{
			/* If this certificate is already present in the list, set the 
			   extended error code for it */
			setObjectErrorInfo( ocspRequestInfoPtr, 
								CRYPT_CERTINFO_CERTIFICATE,
								CRYPT_ERRTYPE_ATTR_PRESENT );
			retExt( status,
					( status, OCSPREQ_ERRINFO,
					  "%s is already present in %s",
					  getCertTypeName( certInfoPtr->type ),
					  getCertTypeNameLC( ocspRequestInfoPtr->type ) ) );
			}
		return( status );
		}
	DATAPTR_SET( certRevInfo->currentRevocation, revInfoPtr );

	/* Add the certificate information again as an ESSCertID extension to 
	   work around the problems inherent in OCSP IDs */
	status = dynCreate( &essCertDB, certInfoPtr->objectHandle, 
						CRYPT_IATTRIBUTE_ESSCERTID );
	if( cryptStatusOK( status ) )
		{
		CRYPT_ATTRIBUTE_TYPE dummy1;
		CRYPT_ERRTYPE_TYPE dummy2;
		ERROR_INFO localErrorInfo;

		/* Since this isn't a critical extension (the ESSCertID is just a 
		   backup for the main, albeit not very useful, ID) we continue if 
		   there's a problem adding it, and ignore any additional error 
		   information */
		clearErrorInfo( &localErrorInfo );
		revInfoPtr = DATAPTR_GET( certRevInfo->currentRevocation );
		ENSURES( revInfoPtr != NULL );
		( void ) addAttributeFieldString( &revInfoPtr->attributes,
								CRYPT_CERTINFO_CMS_SIGNINGCERT_ESSCERTID, 
								CRYPT_ATTRIBUTE_NONE, dynData( essCertDB ), 
								dynLength( essCertDB ), ATTR_FLAG_NONE, 
								FALSE, &localErrorInfo, &dummy1, &dummy2 );
		dynDestroy( &essCertDB );
		}
	return( CRYPT_OK );
	}

/* Get the hash of the public key (for an OCSP request), possibly
   overwriting a previous hash if there are multiple entries in the
   request */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyCaCertToOCSPReq( INOUT_PTR CERT_INFO *certInfoPtr,
								INOUT_PTR CERT_INFO *caCertInfoPtr )
	{
	HASH_FUNCTION_ATOMIC hashFunctionAtomic;
	STREAM stream;
	void *dataPtr DUMMY_INIT_PTR;
	int length, status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( caCertInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST );
	REQUIRES( caCertInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  caCertInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
	REQUIRES( caCertInfoPtr->publicKeyInfo != NULL );

	getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, NULL );

	/* Dig down into the encoded key data to find the weird bits of key that
	   OCSP requires us to hash.  We store the result as the certificate 
	   hash, this is safe because this value isn't used for an OCSP request 
	   so it can't be accessed externally */
	sMemConnect( &stream, caCertInfoPtr->publicKeyInfo,
				 caCertInfoPtr->publicKeyInfoSize );
	readSequence( &stream, NULL );	/* Wrapper */
	readUniversal( &stream );		/* AlgoID */
	status = readBitStringHole( &stream, &length, 16, DEFAULT_TAG );
	if( cryptStatusOK( status ) )	/* BIT STRING wrapper */
		status = sMemGetDataBlock( &stream, &dataPtr, length );
	if( cryptStatusError( status ) )
		{
		/* There's a problem with the format of the key */
		DEBUG_DIAG(( "Invalid certificate data format when hashing "
					 "certificate to create OCSP ID" ));
		assert( DEBUG_WARN );
		sMemDisconnect( &stream );
		setObjectErrorInfo( certInfoPtr, CRYPT_CERTINFO_CACERTIFICATE,
							CRYPT_ERRTYPE_ATTR_VALUE );
		return( CRYPT_ERROR_INVALID );
		}
	hashFunctionAtomic( certInfoPtr->certHash, KEYID_SIZE, dataPtr, length );
	certInfoPtr->certHashSet = TRUE;
	sMemDisconnect( &stream );

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

/****************************************************************************
*																			*
*						Copy Certificate Validation Data					*
*																			*
****************************************************************************/

#ifdef USE_CERTVAL

/* Copy validity information from an RTCS request to a response */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyRtcsReqToResp( INOUT_PTR CERT_INFO *certInfoPtr,
							  IN_PTR const CERT_INFO *rtcsRequestInfoPtr )
	{
	CERT_VAL_INFO *certValInfo = certInfoPtr->cCertVal;
	const CERT_VAL_INFO *requestValInfo = rtcsRequestInfoPtr->cCertVal;
	int status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isReadPtr( rtcsRequestInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_RESPONSE );
	REQUIRES( DATAPTR_ISNULL( certValInfo->validityInfo ) );
	REQUIRES( rtcsRequestInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST );

	/* Copy the certificate validity information and extensions */
	status = copyValidityEntries( &certValInfo->validityInfo,
								  requestValInfo->validityInfo );
	if( cryptStatusOK( status ) )
		{
		status = copyRTCSRequestAttributes( &certInfoPtr->attributes,
											rtcsRequestInfoPtr->attributes );
		}
	return( status );
	}

/* Copy the certificate hash.  We read the value indirectly since it's 
   computed on demand and may not have been evaluated yet */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyCertToRTCSRequest( INOUT_PTR CERT_INFO *rtcsRequestInfoPtr,
								  INOUT_PTR CERT_INFO *certInfoPtr )
	{
	CERT_VAL_INFO *valInfo = rtcsRequestInfoPtr->cCertVal;
	VALIDITY_INFO *validityInfoPtr DUMMY_INIT_PTR;
	BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
	int certHashLength, status;

	assert( isWritePtr( rtcsRequestInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( rtcsRequestInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST );
	REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
			  certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );

	status = getCertComponentString( certInfoPtr,
									 CRYPT_CERTINFO_FINGERPRINT_SHA1, 
									 certHash, CRYPT_MAX_HASHSIZE, 
									 &certHashLength );
	if( cryptStatusOK( status ) )
		{
		status = addValidityEntry( &valInfo->validityInfo,
								   &validityInfoPtr, certHash, 
								   certHashLength );
		}
	if( cryptStatusError( status ) )
		{
		if( status == CRYPT_ERROR_DUPLICATE )
			{
			/* If this certificate is already present in the list, set the 
			   extended error code for it */
			setObjectErrorInfo( rtcsRequestInfoPtr, 
								CRYPT_CERTINFO_CERTIFICATE,
								CRYPT_ERRTYPE_ATTR_PRESENT );
			retExt( status,
					( status, RTCSREQ_ERRINFO,
					  "%s is already present in %s",
					  getCertTypeName( certInfoPtr->type ),
					  getCertTypeNameLC( rtcsRequestInfoPtr->type ) ) );
			}
		return( status );
		}
	DATAPTR_SET( valInfo->currentValidity, validityInfoPtr );

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

/****************************************************************************
*																			*
*				Certificate Information Copy External Interface				*
*																			*
****************************************************************************/

/* Copy user certificate information into a certificate object, either a
   CRL, a certification/revocation request, or an RTCS/OCSP request */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int copyUserCertInfo( INOUT_PTR CERT_INFO *certInfoPtr,
							 INOUT_PTR CERT_INFO *userCertInfoPtr,
							 IN_HANDLE const CRYPT_HANDLE iCryptHandle )
	{
	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
	assert( isWritePtr( userCertInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( isHandleRangeValid( iCryptHandle ) );
	REQUIRES( userCertInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
			  userCertInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
	REQUIRES( userCertInfoPtr->certificate != NULL );

	/* If it's an RTCS or OCSP request, remember the responder URL if there's
	   one present.  We can't leave it to be read out of the certificate 
	   because authorityInfoAccess isn't a valid attribute for RTCS/OCSP 
	   requests */
#ifdef USE_CERTREV
	if( certInfoPtr->type == CRYPT_CERTTYPE_OCSP_REQUEST && \
		certInfoPtr->cCertRev->responderUrl == NULL )
		{
		int status;

		status = copyResponderURL( certInfoPtr, userCertInfoPtr );
		if( cryptStatusError( status ) )
			return( status );
		}
#endif /* USE_CERTREV */
#ifdef USE_CERTVAL
	if( certInfoPtr->type == CRYPT_CERTTYPE_RTCS_REQUEST && \
		certInfoPtr->cCertVal->responderUrl == NULL )
		{
		int status;

		status = copyResponderURL( certInfoPtr, userCertInfoPtr );
		if( cryptStatusError( status ) )
			return( status );
		}
#endif /* USE_CERTVAL */

	/* Copy the required information across to the certificate */
	switch( certInfoPtr->type )
		{
#ifdef USE_CERTREQ
		case CRYPT_CERTTYPE_REQUEST_CERT:
			return( copyCertToRequest( certInfoPtr, userCertInfoPtr,
									   iCryptHandle ) );

		case CRYPT_CERTTYPE_REQUEST_REVOCATION:
			return( copyCertToRevRequest( certInfoPtr, userCertInfoPtr ) );
#endif /* USE_CERTREQ */

#ifdef USE_CERTREV
		case CRYPT_CERTTYPE_CRL:
			return( copyRevocationInfo( certInfoPtr, userCertInfoPtr ) );

		case CRYPT_CERTTYPE_OCSP_REQUEST:
			return( copyCertToOCSPRequest( certInfoPtr, userCertInfoPtr ) );
#endif /* USE_CERTREV */

#ifdef USE_CERTVAL
		case CRYPT_CERTTYPE_RTCS_REQUEST:
			return( copyCertToRTCSRequest( certInfoPtr, userCertInfoPtr ) );
#endif /* USE_CERTVAL */
		}

	retIntError();
	}

/* A general dispatch function for copying information from one certificate 
   object to another.  This is a wrapper that acquires the source object's 
   data and then passes a pointer to it to the actual copy function that
   copies any necessary information to the destination object, releasing the
   source object after the copy */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int copyCertObject( INOUT_PTR CERT_INFO *certInfoPtr,
					IN_HANDLE const CRYPT_CERTIFICATE addedCert,
					IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE certInfoType,
					const int certInfo )
	{
	CERT_INFO *addedCertInfoPtr;
	int status;

	assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );

	REQUIRES( sanityCheckCert( certInfoPtr ) );
	REQUIRES( isAttribute( certInfoType ) || \
			  isInternalAttribute( certInfoType ) );
	REQUIRES( isHandleRangeValid( addedCert ) );

	status = krnlAcquireObject( addedCert, OBJECT_TYPE_CERTIFICATE,
								( MESSAGE_PTR_CAST ) &addedCertInfoPtr,
								CRYPT_ARGERROR_NUM1 );
	if( cryptStatusError( status ) )
		return( status );
	REQUIRES_OBJECT( sanityCheckCert( addedCertInfoPtr ), 
					 addedCertInfoPtr->objectHandle );
	switch( certInfoType )
		{
		case CRYPT_CERTINFO_CERTIFICATE:
			status = copyUserCertInfo( certInfoPtr, addedCertInfoPtr, 
									   certInfo );
			break;

#ifdef USE_CERTREV
		case CRYPT_CERTINFO_CACERTIFICATE:
			status = copyCaCertToOCSPReq( certInfoPtr, addedCertInfoPtr );
			break;
#endif /* USE_CERTREV */

#ifdef USE_CERTREQ
		case CRYPT_CERTINFO_CERTREQUEST:
			status = copyCertReqToCert( certInfoPtr, addedCertInfoPtr );
			break;
#endif /* USE_CERTREQ */

#ifdef USE_CERTVAL
		case CRYPT_IATTRIBUTE_RTCSREQUEST:
			status = copyRtcsReqToResp( certInfoPtr, addedCertInfoPtr );
			break;
#endif /* USE_CERTVAL */

#ifdef USE_CERTREV
		case CRYPT_IATTRIBUTE_OCSPREQUEST:
			status = copyOcspReqToResp( certInfoPtr, addedCertInfoPtr );
			break;

		case CRYPT_IATTRIBUTE_REVREQUEST:
			status = copyRevReqToCert( certInfoPtr, addedCertInfoPtr );
			break;
#endif /* USE_CERTREV */

#ifdef USE_PKIUSER
		case CRYPT_IATTRIBUTE_PKIUSERINFO:
			status = copyPkiUserToCertReq( certInfoPtr, addedCertInfoPtr );
			break;
#endif /* USE_PKIUSER */

#ifdef USE_DBMS	/* Only used by CA code */
		case CRYPT_IATTRIBUTE_BLOCKEDATTRS:
			status = sanitiseCertAttributes( certInfoPtr,
											 addedCertInfoPtr->attributes );
			break;
#endif /* USE_DBMS */

		default:
			retIntError_Obj( addedCertInfoPtr->objectHandle );
		}
	krnlReleaseObject( addedCertInfoPtr->objectHandle );

	return( status );
	}
#endif /* USE_CERTIFICATES */
