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

📄 ext_chk.c

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

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

/****************************************************************************
*																			*
*							Attribute Field Type Checking					*
*																			*
****************************************************************************/

/* Validate and preprocess a set of attributes in preparation for writing
   them to a certificate or CRL and set up links to the information in the
   attribute information table prior to encoding the attributes.  This is a
   rather complex process that relies on stepping through the list of
   attribute fields and the attribute information table in sync and making
   sure that the list of fields is consistent with the attribute information
   table.  In addition we set up synchronisation points between the list and 
   table that are used during the encoding process.  For example assume that 
   we have the following attribute:

	attribute ::= SEQUENCE {
		foo		BOOLEAN DEFAULT TRUE,
		bar		SEQUENCE OF OBJECT IDENTIFIER
		}

   The attribute information table would encode this attribute as:

	t1:	OID	SEQUENCE	MORE
	t2:		BOOLEAN		MORE	OPTIONAL
	t3:		SEQUENCE	MORE
	t4:		OID

   The first table entry t1 contains the OID, the SEQUENCE wrapper, and a
   continuation flag.  For the purposes of comparison with the list this is
   a no-op and can be skipped since it's only used for encoding purposes.  
   The next table entry t2 contains the first attribute field, an optional
   boolean and a continuation flag.  The next table entry t3 contains another
   SEQUENCE wrapper that again is only used for encoding and can be skipped
   for comparing with the list, and a continuation flag.  Finally, the last
   table entry t4 contains the second attribute field, an OID.

   Assuming that the attribute list contains the following:

	BOOLEAN	FALSE	-> t1
	OID		xxx		-> t3

   The attribute validation process sets the synchronisation point for the 
   first attribute list entry to point to t1 and the second one to point to 
   t3.  When we encode the attribute we encode t1 (the OID, critical flag, 
   and SEQUENCE wrapper) since the field IDs won't match we step to t2 and 
   use that to encode the boolean.  We then do the same for t3 with the 
   SEQUENCE and OID.

   If the attribute list instead contained only:

	OID		xxx		-> t1

   then this time the attribute validation process sets the synchronisation 
   point to t1.  When encoding we encode t1 as before, step to t2, the field 
   IDs won't match but t2 is optional so we skip it, then encode t3 as for 
   t1 and finally encode the OID using t4.

   At this point we also evaluate the encoded size of each attribute.  For
   invidual fields we just store their encoded size.  For constructed 
   objects we stack the attribute list entry where the constructed object
   starts and, until we reach the end of the constructed object, accumulate
   the total size of the fields that make up the object.  When we reach the
   end of the object we unstack the pointer to the attribute list and store
   the total size in it.

   To handle nested constructed objects we only update the size of the
   topmost item on the stack.  When this is unstacked we add the size of 
   that entry plus the size of its tag and length information to the next
   entry on the stack.

   In addition to updating the size we also record the sequence of table
   entries that are required to encode the constructed item.  A worst-case
   sequence of entries would be:

	SEQUENCE {
		SEQUENCE OPTIONAL { ... }		| Not encoded
		SEQUENCE {
			SEQUENCE OPTIONAL { ... }	| Not encoded
			SEQUENCE {
				value
				}
			}
		}

   which contains an alternating sequence of encoded and non-encoded fields.
   Because of this the validation check performs the complex task of
   recording which table entries are used for the encoding by stacking and
   unstacking them and discarding the ones that evaluate to a zero size
   during the unstacking process.

   Each entry in the stack contains the list item that it applies to, the 
   table entry which is used to encode the stacked item, and the size of the 
   item */

#define ATTRIBUTE_STACKSIZE		10

typedef struct {
	ATTRIBUTE_LIST *attributeListPtr;	/* List entry that this applies to */
	ATTRIBUTE_INFO *attributeInfoPtr;	/* Encoding point for sequence */
	int size;							/* Size of sequence */
	} ATTRIBUTE_STACK;

/* Once we reach the end of the constructed item we need to unwind the stack
   and update everything that we've gone past.  If it's an optional item (so 
   that nothing gets encoded) we don't do anything.  The count argument 
   specifies the level of unwinding to perform, this can be relative 
   (isRelative = TRUE, in which case we undo 'count' levels of nesting, which 
   may be more than count stack positions if non-nested data was stacked) or 
   absolute (isRelative = FALSE, in which case we undo 'count' stack 
   positions */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int updateStackedInfo( INOUT_ARRAY( ATTRIBUTE_STACKSIZE ) \
								ATTRIBUTE_STACK *stack, 
							  IN_RANGE( 0, ATTRIBUTE_STACKSIZE - 1 ) \
								const int stackPos,
							  OUT_RANGE( 0, ATTRIBUTE_STACKSIZE - 1 ) \
								int *newStackPosPtr,
							  IN_RANGE( 0, ATTRIBUTE_STACKSIZE - 1 ) int count, 
							  const BOOLEAN isRelative )
	{
	int currentStackPos = stackPos;

	assert( isWritePtr( stack, sizeof( ATTRIBUTE_STACK ) * \
							   ATTRIBUTE_STACKSIZE ) );
	assert( isWritePtr( newStackPosPtr, sizeof( int ) ) );
	
	REQUIRES( stackPos >= 0 && stackPos < ATTRIBUTE_STACKSIZE );
	REQUIRES( count >= 0 && count <= stackPos );

	while( count-- > 0 )
		{
		ATTRIBUTE_LIST *attributeFifoPtr = stack[ --currentStackPos ].attributeListPtr;
		const ATTRIBUTE_INFO *attributeInfoPtr = stack[ currentStackPos ].attributeInfoPtr;
		const int size = stack[ currentStackPos ].size;

		assert( isReadPtr( attributeInfoPtr, sizeof( ATTRIBUTE_INFO ) ) );

		ENSURES( count >= 0 && count <= currentStackPos );
		ENSURES( currentStackPos >= 0 && \
				 currentStackPos < ATTRIBUTE_STACKSIZE );
		ENSURES( size >= 0 && size < MAX_INTLENGTH );

		/* If there's nothing to encode, continue.  There are a few special
		   cases here where even if the sequence is of zero length we may
		   have to encode something.  Firstly, if there's a member with a
		   default value present (resulting in nothing being encoded) we still
		   have to encode a zero-length sequence.  In addition if all of the
		   members have non-encoding values (e.g. OIDs and fixed attributes,
		   none of which are specified by the user) we have to encode these
		   even though there's no actual value associated with them since
		   their mere presence conveys the necessary information.

		   In addition sometimes we can reach the end of the attribute list
		   but there are further actions defined in the encoding table (for
		   example cleanup actions in nested sequences).  In this case the
		   stacked attributeFifoPtr is NULL and the size is zero so we 
		   perform an additional check to make sure that the pointer is 
		   non-null */
		if( !( size > 0 || \
			   ( attributeFifoPtr != NULL && \
				 ( ( attributeFifoPtr->flags & ATTR_FLAG_DEFAULTVALUE ) || \
				   ( attributeInfoPtr->flags & FL_NONENCODING ) ) ) ) )
			continue;

		assert( isWritePtr( attributeFifoPtr, sizeof( ATTRIBUTE_LIST ) ) );
		assert( sizeof( attributeFifoPtr->sizeFifo ) / sizeof( int ) >= \
														ATTRIBUTE_STACKSIZE );

		/* Remember the size and table entry used to encode this stack 
		   entry */
		attributeFifoPtr->sizeFifo[ attributeFifoPtr->fifoEnd ] = size;
		attributeFifoPtr->encodingFifo[ attributeFifoPtr->fifoEnd++ ] = \
								stack[ currentStackPos ].attributeInfoPtr;

		/* If there are no further items on the stack, continue */
		if( currentStackPos <= 0 )
			continue;

		ENSURES( currentStackPos > 0 && \
				 currentStackPos < ATTRIBUTE_STACKSIZE );

		/* If it's a non-constructed field, add the length of the existing and
		   new fields */
		if( attributeInfoPtr->fieldType != BER_SEQUENCE && \
			attributeInfoPtr->fieldType != BER_SET )
			{
			const int newLength = \
					( attributeInfoPtr->fieldType == FIELDTYPE_IDENTIFIER ) ? \
						sizeofOID( attributeInfoPtr->oid ) : \
						( int ) attributeInfoPtr->defaultValue;

			/* Add the new length to the existing data size.  Since this is a
			   non-constructed field it doesn't count as a reduction in the
			   nesting level so if we're unnesting by a relative amount we 
			   adjust the nesting count to give a net change of zero for this
			   item */
			stack[ currentStackPos - 1 ].size += size + newLength;
			if( isRelative )
				count++;
			}
		else
			{
			/* It's a constructed field, percolate the encapsulated content
			   size up the stack */
			stack[ currentStackPos - 1 ].size += ( int ) sizeofObject( size );
			}
		}

	*newStackPosPtr = currentStackPos;

	return( CRYPT_OK );
	}

/* Some attributes contain a sequence of items of the attributeTypeAndValue
   form (i.e. OID, ANY DEFINED BY OID).  To process these a check is made to
   determine whether the named value component in the attribute list is
   present in the current attributeTypeAndValue definition.  If it isn't the
   item is given a zero length, which means that it's never encoded since the 
   field is marked as optional.  The following function checks whether a 
   named value component is present in the item */

CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
static int checkComponentPresent( IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE fieldID,
								  /*?*/ ATTRIBUTE_INFO **attributeInfoPtrPtr )
	{
	const ATTRIBUTE_INFO *attributeInfoPtr = *attributeInfoPtrPtr;
	int nestLevel = 0, iterationCount;

	assert( isWritePtr( attributeInfoPtrPtr, sizeof( ATTRIBUTE_INFO * ) ) );
	assert( isReadPtr( *attributeInfoPtrPtr, sizeof( ATTRIBUTE_INFO ) ) );

	REQUIRES( ( fieldID > CRYPT_ATTRIBUTE_NONE && \
				fieldID < CRYPT_ATTRIBUTE_LAST ) || \
			  ( fieldID == CRYPT_IATTRIBUTE_LAST ) );
			  /* The latter is used when we've run out of values to match 
			     and just want to skip to the end of the 
				 attributeTypeAndValue */

	/* Check each field we find until we find the end of the
	   attributeTypeAndValue.  Unfortunately we don't know how many 
	   attribute table entries are left so we have to use the generic
	   FAILSAFE_ITERATIONS_LARGE for the bounds check */
	for( iterationCount = 0; iterationCount < FAILSAFE_ITERATIONS_LARGE; \
		 iterationCount++ )
		{
		/* Sanity check to make sure that we don't fall off the end of the 
		   table */
		ENSURES( attributeInfoPtr->fieldID != CRYPT_ERROR );

		/* Adjust the nesting level depending on whether we're entering or
		   leaving a sequence */
		if( attributeInfoPtr->fieldType == BER_SEQUENCE )
			nestLevel++;
		nestLevel -= decodeNestingLevel( attributeInfoPtr->flags );

⌨️ 快捷键说明

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