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

📄 certrev.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*						Certificate Revocation Routines						*
*						Copyright Peter Gutmann 1996-2003					*
*																			*
****************************************************************************/

#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) ||  defined( INC_CHILD )
  #include "cert.h"
  #include "../misc/asn1_rw.h"
  #include "../misc/asn1s_rw.h"
#else
  #include "cert/cert.h"
  #include "misc/asn1_rw.h"
  #include "misc/asn1s_rw.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 cert 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 */

static int findRevocationEntry( const REVOCATION_INFO *listPtr, 
								REVOCATION_INFO **insertPoint,
								const void *value, const int valueLength,
								const BOOLEAN sortEntries )
	{
	const REVOCATION_INFO *prevElement = NULL;
	const int dCheck = checksumData( value, valueLength );

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

	/* 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) */
	while( listPtr != NULL )
		{
		if( ( sortEntries || dCheck == listPtr->dCheck ) && \
			listPtr->dataLength == valueLength )
			{
			const int compareStatus = memcmp( listPtr->data,
											  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->dataLength > valueLength )
				break;					/* Insert before this point */

		prevElement = listPtr;
		listPtr = listPtr->next;
		}

	/* 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 cert has been revoked */

int checkRevocation( const CERT_INFO *certInfoPtr, 
					 CERT_INFO *revocationInfoPtr )
	{
	REVOCATION_INFO *revocationEntry;
	int status;

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

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

	/* Check whether the cert is present in the revocation list */
	if( revocationInfoPtr->type == CRYPT_CERTTYPE_CRL )
		{
		/* If the issuers differ, the cert 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 cert in the list */
		status = findRevocationEntry( revocationInfoPtr->revocations, 
									  &revocationEntry, 
									  certInfoPtr->serialNumber, 
									  certInfoPtr->serialNumberLength,
									  FALSE );
		if( status == CRYPT_ERROR_NOTFOUND )
			/* No CRL entry, the certificate is OK */
			return( CRYPT_OK );
		}
	else
		{
		BYTE certHash[ CRYPT_MAX_HASHSIZE ];
		int certHashLength;

		assert( revocationInfoPtr->type == CRYPT_CERTTYPE_OCSP_RESPONSE );

		/* Get the cert hash and use it to check whether there's an entry
		   for this cert in the list.  We read the cert 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, &certHashLength );
		if( cryptStatusOK( status ) )
			status = findRevocationEntry( revocationInfoPtr->revocations, 
										  &revocationEntry, certHash, 
										  certHashLength, FALSE );
		if( cryptStatusError( status ) )
			/* No entry, either good or bad, we can't report anything about
			   the cert */
			return( status );
		}

	/* Select the entry that contains the revocation information and return 
	   the cert's status.  For CRLs the presence of an entry means that the 
	   cert is invalid, for OCSP the validity information is contained in 
	   the entry.  The unknown status is a bit difficult to report, the best 
	   we can do is report notfound, although the notfound occurred at the 
	   responder rather than here */
	revocationInfoPtr->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 */

int addRevocationEntry( REVOCATION_INFO **listHeadPtr, 
						REVOCATION_INFO **newEntryPosition,
						const CRYPT_KEYID_TYPE valueType,
						const void *value, const int valueLength,
						const BOOLEAN noCheck )
	{
	REVOCATION_INFO *newElement, *insertPoint;

	assert( isWritePtr( listHeadPtr, REVOCATION_INFO * ) );
	assert( isWritePtr( newEntryPosition, REVOCATION_INFO * ) );
	assert( sizeof( newElement->data ) == MAX_ID_SIZE );
	assert( valueType == CRYPT_KEYID_NONE || \
			valueType == CRYPT_IKEYID_CERTID || \
			valueType == CRYPT_IKEYID_ISSUERID || \
			valueType == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
	assert( isReadPtr( value, valueLength ) );

	/* 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 && *listHeadPtr != NULL && \
		cryptStatusOK( \
			findRevocationEntry( *listHeadPtr, &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 );

	/* 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 > 128 )
		{
		if( ( newElement->dataPtr = clDynAlloc( "addRevocationEntry", 
												valueLength ) ) == NULL )
			{
			clFree( "addRevocationEntry", newElement );
			return( CRYPT_ERROR_MEMORY );
			}
		}
	else
		newElement->dataPtr = newElement->data;
	newElement->type = valueType;
	memcpy( newElement->dataPtr, value, valueLength );
	newElement->dataLength = valueLength;
	newElement->dCheck = 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 = *listHeadPtr;
		*listHeadPtr = newElement;
		}
	else
		insertSingleListElement( listHeadPtr, insertPoint, newElement );
	*newEntryPosition = newElement;
	return( CRYPT_OK );
	}

/* Delete a revocation list */

void deleteRevocationEntries( REVOCATION_INFO **listHeadPtr )
	{
	REVOCATION_INFO *entryListPtr = *listHeadPtr;

	assert( isWritePtr( listHeadPtr, REVOCATION_INFO * ) );

	*listHeadPtr = NULL;

	/* Destroy any remaining list items */
	while( entryListPtr != NULL )
		{
		REVOCATION_INFO *itemToFree = entryListPtr;

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

/* Copy a revocation list */

int copyRevocationEntries( REVOCATION_INFO **destListHeadPtr,
						   const REVOCATION_INFO *srcListPtr,
						   CRYPT_ATTRIBUTE_TYPE *errorLocus, 
						   CRYPT_ERRTYPE_TYPE *errorType )
	{
	const REVOCATION_INFO *srcListCursor;
	REVOCATION_INFO *destListCursor;

	assert( isWritePtr( destListHeadPtr, REVOCATION_INFO * ) );
	assert( *destListHeadPtr == NULL );	/* Dest.should be empty */

	/* Copy all revocation entries from source to destination */
	for( srcListCursor = srcListPtr; srcListCursor != NULL;
		 srcListCursor = srcListCursor->next )
		{
		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->dataLength > 128 )
			{
			/* If the ID information doesn't fit into the fixed buffer, 
			   allocate a variable-length one and copy it across */
			if( ( newElement->dataPtr = \
					clDynAlloc( "copyRevocationEntries",
								srcListCursor->dataLength ) ) == NULL )
				{
				clFree( "copyRevocationEntries", newElement );
				return( CRYPT_ERROR_MEMORY );
				}
			memcpy( newElement->dataPtr, srcListCursor->data,
					srcListCursor->dataLength );
			}
		else
			newElement->dataPtr = newElement->data;
		newElement->attributes = NULL;
		newElement->next = NULL;

		/* Set the status to 'unknown' by default, this means that any 
		   entries that we can't do anything with automatically get the
		   correct status associated with them */
		newElement->status = CRYPT_OCSPSTATUS_UNKNOWN;

		/* Link the new element into the list */
		if( *destListHeadPtr == NULL )
			*destListHeadPtr = destListCursor = newElement;
		else
			{
			destListCursor->next = newElement;
			destListCursor = newElement;
			}
		}

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Read/write CRL Information						*
*																			*
****************************************************************************/

/* Read/write CRL entries:

	RevokedCert ::= SEQUENCE {
			userCertificate		CertificalSerialNumber,
			revocationDate		UTCTime
			extensions			Extensions OPTIONAL,
			} */

int sizeofCRLentry( REVOCATION_INFO *crlEntry )
	{
	assert( isWritePtr( crlEntry, REVOCATION_INFO ) );

	/* Remember the encoded attribute size for later when we write the
	   attributes */
	crlEntry->attributeSize = sizeofAttributes( crlEntry->attributes );

	return( ( int ) sizeofObject( \
						sizeofInteger( crlEntry->data, crlEntry->dataLength ) + \
						sizeofUTCTime() + \
						( ( crlEntry->attributeSize > 0 ) ? \
							( int ) sizeofObject( crlEntry->attributeSize ) : 0 ) ) );
	}

int readCRLentry( STREAM *stream, REVOCATION_INFO **listHeadPtr,
				  CRYPT_ATTRIBUTE_TYPE *errorLocus, 
				  CRYPT_ERRTYPE_TYPE *errorType )
	{
	REVOCATION_INFO *currentEntry;
	BYTE serialNumber[ MAX_SERIALNO_SIZE ];
	int serialNumberLength, endPos, length, status;
	time_t revocationTime;

	assert( isWritePtr( listHeadPtr, REVOCATION_INFO * ) );

	/* Determine the overall size of the entry */
	readSequence( stream, &length );
	endPos = stell( stream ) + length;

	/* Read the integer component of the serial number (limited to a sane
	   length) and the revocation time */
	readInteger( stream, serialNumber, &serialNumberLength, 
				 MAX_SERIALNO_SIZE );
	status = readUTCTime( stream, &revocationTime );
	if( cryptStatusError( status ) )
		return( status );

	/* Add the entry to the revocation information list.  The ID type isn't 
	   quite an issueAndSerialNumber, but the checking code eventually
	   converts it into this form using the supplied issuer cert DN */
	status = addRevocationEntry( listHeadPtr, &currentEntry, 
								 CRYPT_IKEYID_ISSUERANDSERIALNUMBER,
								 serialNumber, serialNumberLength, 
								 ( endPos > CRL_SORT_LIMIT ) ? TRUE : FALSE );
	if( cryptStatusError( status ) )
		return( status );
	currentEntry->revocationTime = revocationTime;

⌨️ 快捷键说明

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