⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 certrev.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*						Certificate Revocation Routines						*
*						Copyright Peter Gutmann 1996-2007					*
*																			*
****************************************************************************/

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

/* The maximum length of ID that can be stored in a REVOCATION_INFO entry.
   Larger IDs require external storage */

#define MAX_ID_SIZE		128

/* Usually when we add revocation information we perform various checks such
   as making sure we're not adding duplicate information, however when
   processing the mega-CRLs from some CAs this becomes prohibitively
   expensive.  To solve this problem we perform checking up to a certain
   number of entries and after that just drop in any further entries as is
   in order to provide same-day service.  The following value defines the
   CRL threshold size in bytes at which we stop performing checks when we
   add new entries */

#define CRL_SORT_LIMIT	8192

/* Context-specific tags for OCSP certificate identifier types */

enum { CTAG_OI_CERTIFICATE, CTAG_OI_CERTIDWITHSIG, CTAG_OI_RTCS };

/* OCSP certificate status values */

enum { OCSP_STATUS_NOTREVOKED, OCSP_STATUS_REVOKED, OCSP_STATUS_UNKNOWN };

/****************************************************************************
*																			*
*					Add/Delete/Check Revocation Information					*
*																			*
****************************************************************************/

/* Find an entry in a revocation list.  This is done using a linear search,
   which isn't very optimal but anyone trying to do anything useful with
   mega-CRLs (or with CRLs in general) is in more trouble than basic search
   algorithm choice.  In other words it doesn't really make much difference
   whether we have an optimal or suboptimal implementation of a
   fundamentally broken mechanism like CRLs.

   The value is either a serialNumber or a hash of some form (issuerID,
   certHash), we don't bother distinguishing the exact type since the
   chances of a hash collision are virtually nonexistant */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int findRevocationEntry( const REVOCATION_INFO *listPtr,
								OUT_OPT_PTR REVOCATION_INFO **insertPoint,
								IN_BUFFER( valueLength ) const void *value, 
								IN_LENGTH_SHORT const int valueLength,
								const BOOLEAN sortEntries )
	{
	const REVOCATION_INFO *prevElement = NULL;
	const int idCheck = checksumData( value, valueLength );
	int iterationCount;

	assert( isReadPtr( listPtr, sizeof( REVOCATION_INFO ) ) );
	assert( insertPoint == NULL || \
			isWritePtr( insertPoint, sizeof( REVOCATION_INFO * ) ) );
	assert( isReadPtr( value, valueLength ) );

	REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );

	/* Clear the return value */
	if( insertPoint != NULL )
		*insertPoint = NULL;

	/* Find the correct place in the list to insert the new element and check
	   for duplicates.  If requested we sort the entries by serial number
	   (or more generally data value) for no adequately explored reason 
	   (some implementations can optimise the searching of CRLs based on
	   this but since there's no agreement on whether to do it or not you 
	   can't tell whether it's safe to rely on it).  In addition we bound 
	   the loop with FAILSAFE_ITERATIONS_MAX since CRLs can grow enormous */
	for( iterationCount = 0;
		 listPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
		 listPtr = listPtr->next, iterationCount++ )
		{
		if( ( sortEntries || idCheck == listPtr->idCheck ) && \
			listPtr->idLength == valueLength )
			{
			const int compareStatus = memcmp( listPtr->id,
											  value, valueLength );

			if( !compareStatus )
				{
				/* We found a matching entry, tell the caller which one it
				   is if required */
				if( insertPoint != NULL )
					*insertPoint = ( REVOCATION_INFO * ) listPtr;
				return( CRYPT_OK );
				}
			if( sortEntries && compareStatus > 0 )
				break;					/* Insert before this point */
			}
		else
			{
			if( sortEntries && listPtr->idLength > valueLength )
				break;					/* Insert before this point */
			}

		prevElement = listPtr;
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MAX );

	/* We can't find a matching entry, return the revocation entry after
	   which we should insert the new value */
	if( insertPoint != NULL )
		*insertPoint = ( REVOCATION_INFO * ) prevElement;
	return( CRYPT_ERROR_NOTFOUND );
	}

/* Check whether a certificate has been revoked */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int checkRevocation( const CERT_INFO *certInfoPtr, 
					 INOUT CERT_INFO *revocationInfoPtr )
	{
	CERT_REV_INFO *certRevInfo = revocationInfoPtr->cCertRev;
	REVOCATION_INFO *revocationEntry = DUMMY_INIT_PTR;
	int status;

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

	/* If there's no revocation information present then the certificate 
	   can't have been revoked */
	if( certRevInfo->revocations == NULL )
		return( CRYPT_OK );

	/* Check whether the certificate is present in the revocation list */
	if( revocationInfoPtr->type == CRYPT_CERTTYPE_CRL )
		{
		/* If the issuers differ then the certificate can't be in this CRL */
		if( ( revocationInfoPtr->issuerDNsize != certInfoPtr->issuerDNsize || \
			memcmp( revocationInfoPtr->issuerDNptr, certInfoPtr->issuerDNptr,
					revocationInfoPtr->issuerDNsize ) ) )
			return( CRYPT_OK );

		/* Check whether there's an entry for this certificate in the list */
		status = findRevocationEntry( certRevInfo->revocations,
									  &revocationEntry,
									  certInfoPtr->cCertCert->serialNumber,
									  certInfoPtr->cCertCert->serialNumberLength,
									  FALSE );
		if( cryptStatusError( status ) )
			{
			/* No CRL entry, the certificate is OK */
			return( CRYPT_OK );
			}
		}
	else
		{
		BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
		int certHashLength;

		ENSURES( revocationInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE );

		/* Get the certificate hash and use it to check whether there's an 
		   entry for this certificate in the list.  We read the certificate 
		   hash indirectly since it's computed on demand and may not have 
		   been evaluated yet */
		status = getCertComponent( ( CERT_INFO * ) certInfoPtr,
								   CRYPT_CERTINFO_FINGERPRINT_SHA,
								   certHash, CRYPT_MAX_HASHSIZE, 
								   &certHashLength );
		if( cryptStatusOK( status ) )
			{
			status = findRevocationEntry( certRevInfo->revocations,
										  &revocationEntry, certHash,
										  certHashLength, FALSE );
			}
		if( cryptStatusError( status ) )
			{
			/* No entry, either good or bad, we can't report anything about
			   the certificate */
			return( status );
			}
		}
	ENSURES( revocationEntry != NULL );

	/* Select the entry that contains the revocation information and return
	   the certificate's status.  For CRLs the presence of an entry means 
	   that the certificate is invalid, for OCSP the validity information is 
	   contained in the entry.  The unknown status is a bit difficult to 
	   report, the best that we can do is report notfound although the 
	   notfound occurred at the responder rather than here */
	certRevInfo->currentRevocation = revocationEntry;
	if( revocationInfoPtr->type == CRYPT_CERTTYPE_CRL )
		return( CRYPT_ERROR_INVALID );
	return( ( revocationEntry->status == CRYPT_OCSPSTATUS_NOTREVOKED ) ? \
				CRYPT_OK : \
			( revocationEntry->status == CRYPT_OCSPSTATUS_REVOKED ) ? \
				CRYPT_ERROR_INVALID : CRYPT_ERROR_NOTFOUND );
	}

/* Add an entry to a revocation list */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
int addRevocationEntry( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr,
						OUT_PTR REVOCATION_INFO **newEntryPosition,
						IN_KEYID const CRYPT_KEYID_TYPE valueType,
						IN_BUFFER( valueLength ) const void *value, 
						IN_LENGTH_SHORT const int valueLength,
						const BOOLEAN noCheck )
	{
	REVOCATION_INFO *newElement, *insertPoint;

	assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
	assert( isWritePtr( newEntryPosition, sizeof( REVOCATION_INFO * ) ) );
	assert( isReadPtr( value, valueLength ) );
	
	REQUIRES( valueType == CRYPT_KEYID_NONE || \
			  valueType == CRYPT_IKEYID_CERTID || \
			  valueType == CRYPT_IKEYID_ISSUERID || \
			  valueType == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
	REQUIRES( valueLength > 0 && valueLength < MAX_INTLENGTH_SHORT );

	/* Clear return value */
	*newEntryPosition = NULL;

	/* Find the insertion point for the new entry unless we're reading data
	   from a pre-encoded CRL, in which case we just drop it in at the start.
	   The absence of checking for data from an existing CRL is necessary in
	   order to provide same-day service for large CRLs */
	if( !noCheck && *listHeadPtrPtr != NULL && \
		cryptStatusOK( \
			findRevocationEntry( *listHeadPtrPtr, &insertPoint, value,
								  valueLength, TRUE ) ) )
		{
		/* If we get an OK status it means that we've found an existing
		   entry that matches the one being added, we can't add it again */
		return( CRYPT_ERROR_DUPLICATE );
		}
	else
		{
		/* It's an empty list, insert the new element at the start */
		insertPoint = NULL;
		}

	/* Allocate memory for the new element and copy the information across */
	if( ( newElement = ( REVOCATION_INFO * ) \
			clAlloc( "addRevocationEntry", sizeof( REVOCATION_INFO ) ) ) == NULL )
		return( CRYPT_ERROR_MEMORY );
	memset( newElement, 0, sizeof( REVOCATION_INFO ) );
	if( valueLength > MAX_ID_SIZE )
		{
		if( ( newElement->idPtr = clDynAlloc( "addRevocationEntry",
											  valueLength ) ) == NULL )
			{
			clFree( "addRevocationEntry", newElement );
			return( CRYPT_ERROR_MEMORY );
			}
		}
	else
		newElement->idPtr = newElement->id;
	newElement->idType = valueType;
	memcpy( newElement->idPtr, value, valueLength );
	newElement->idLength = valueLength;
	newElement->idCheck = checksumData( value, valueLength );

	/* Insert the new element into the list */
	if( noCheck )
		{
		/* If we're adding data from an existing CRL drop it in at the
		   quickest insert point.  This is necessary for quick operation
		   when handling mega-CRLs */
		newElement->next = *listHeadPtrPtr;
		*listHeadPtrPtr = newElement;
		}
	else
		insertSingleListElement( listHeadPtrPtr, insertPoint, newElement );
	*newEntryPosition = newElement;
	return( CRYPT_OK );
	}

/* Delete a revocation list */

STDC_NONNULL_ARG( ( 1 ) ) \
void deleteRevocationEntries( INOUT_PTR REVOCATION_INFO **listHeadPtrPtr )
	{
	REVOCATION_INFO *entryListPtr = *listHeadPtrPtr;
	int iterationCount;

	assert( isWritePtr( listHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );

	*listHeadPtrPtr = NULL;

	/* Destroy any remaining list items */
	for( iterationCount = 0;
		 entryListPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
		 iterationCount++ )
		{
		REVOCATION_INFO *itemToFree = entryListPtr;

		entryListPtr = entryListPtr->next;
		if( itemToFree->idPtr != itemToFree->id )
			{
			zeroise( itemToFree->idPtr, itemToFree->idLength );
			clFree( "deleteRevocationEntries", itemToFree->idPtr );
			}
		if( itemToFree->attributes != NULL )
			deleteAttributes( &itemToFree->attributes );
		zeroise( itemToFree, sizeof( REVOCATION_INFO ) );
		clFree( "deleteRevocationEntries", itemToFree );
		}
	}

/* Copy a revocation list */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int copyRevocationEntries( INOUT_PTR REVOCATION_INFO **destListHeadPtrPtr,
						   const REVOCATION_INFO *srcListPtr )
	{
	const REVOCATION_INFO *srcListCursor;
	REVOCATION_INFO *destListCursor = DUMMY_INIT_PTR;
	int iterationCount;

	assert( isWritePtr( destListHeadPtrPtr, sizeof( REVOCATION_INFO * ) ) );
	assert( *destListHeadPtrPtr == NULL );	/* Dest.should be empty */
	assert( isReadPtr( srcListPtr, sizeof( REVOCATION_INFO ) ) );

	/* Sanity check to make sure that the destination list doesn't already 
	   exist, which would cause the copy loop below to fail */
	REQUIRES( *destListHeadPtrPtr == NULL );

	/* Copy all revocation entries from source to destination */
	for( srcListCursor = srcListPtr, iterationCount = 0; 
		 srcListCursor != NULL && iterationCount < FAILSAFE_ITERATIONS_MAX;
		 srcListCursor = srcListCursor->next, iterationCount++ )
		{
		REVOCATION_INFO *newElement;

		/* Allocate the new entry and copy the data from the existing one
		   across.  We don't copy the attributes because there aren't any
		   that should be carried from request to response */
		if( ( newElement = ( REVOCATION_INFO * ) \
					clAlloc( "copyRevocationEntries",
							 sizeof( REVOCATION_INFO ) ) ) == NULL )
			return( CRYPT_ERROR_MEMORY );
		memcpy( newElement, srcListCursor, sizeof( REVOCATION_INFO ) );
		if( srcListCursor->idLength > MAX_ID_SIZE )
			{
			/* If the ID information doesn't fit into the fixed buffer,
			   allocate a variable-length one and copy it across */
			if( ( newElement->idPtr = \
					clDynAlloc( "copyRevocationEntries",
								srcListCursor->idLength ) ) == NULL )
				{
				clFree( "copyRevocationEntries", newElement );
				return( CRYPT_ERROR_MEMORY );
				}
			memcpy( newElement->idPtr, srcListCursor->id,
					srcListCursor->idLength );
			}
		else
			newElement->idPtr = newElement->id;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -