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

📄 asn1_chk.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 2 页
字号:
			/* Disallowed or unrecognised primitive */
			return( STATE_ERROR );
		}

	return( STATE_NONE );
	}

/* Check a single ASN.1 object.  checkASN1() and checkASN1Object() are 
   mutually recursive, the ...Object() version only exists to avoid a
   large if... else chain in checkASN1().  A typical checking run is
   as follows:

	30 nn			cASN1 -> cAObj -> cASN1
	   30 nn						  cASN1 -> cAObj -> cASN1
		  04 nn nn										cASN1 -> cPrim

	30 80			cASN1 -> cAObj -> cASN1
	   30 80						  cASN1 -> cAObj -> cASN1
		  04 nn nn										cASN1 -> cPrim
	   00 00						  cASN1 <- cAObj <- cASN1
	00 00			cASN1 <- cAObj <- cASN1

   The use of checkASN1Object() leads to an (apparently) excessively deep 
   call hierarchy, but that's mostly just an artifact of the way that it's 
   diagrammed here */

CHECK_RETVAL_ENUM( STATE ) STDC_NONNULL_ARG( ( 1, 2 ) ) \
static ASN1_STATE checkASN1Object( INOUT STREAM *stream, const ASN1_ITEM *item,
								   IN_RANGE( 1, MAX_NESTING_LEVEL ) \
									const int level, 
								   IN_ENUM_OPT( ASN1_STATE ) const ASN1_STATE state,
								   const BOOLEAN checkDataElements )
	{
	ASN1_STATE newState;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( item, sizeof( ASN1_ITEM ) ) );

	REQUIRES( level > 0 && level <= MAX_NESTING_LEVEL );
	REQUIRES( state >= STATE_NONE && state < STATE_ERROR );

	/* Make sure that we're not processing suspiciosly deeply nested data */
	if( level >= MAX_NESTING_LEVEL )
		return( STATE_ERROR );

	/* If we're checking data elements, check the contents for validity.  A
	   straight data-length check doesn't check nested elements since all it
	   cares about is finding the overall length with as little effort as
	   possible */
	if( checkDataElements && ( item->tag & BER_CLASS_MASK ) == BER_UNIVERSAL )
		{
		/* If it's constructed, parse the nested object(s) */
		if( ( item->tag & BER_CONSTRUCTED_MASK ) == BER_CONSTRUCTED )
			{
			/* Special-case for zero-length SEQUENCE/SET */
			if( item->length <= 0 && !item->indefinite )
				return( STATE_NONE );

			return( checkASN1( stream, item->length, item->indefinite,
							   level + 1, ( item->tag == BER_SEQUENCE ) ? \
									STATE_SEQUENCE : STATE_NONE, TRUE ) );
			}

		/* It's primitive, check the primitive element with optional state
		   update: SEQ + OID -> HOLE_OID; OID + { NULL | BOOLEAN } -> 
		   HOLE_BITSTRING/HOLE_OCTETSTRING */
		newState = checkPrimitive( stream, item, level + 1, state );
		if( newState < STATE_NONE || newState >= STATE_ERROR )
			return( STATE_ERROR );
		if( state == STATE_SEQUENCE && newState == STATE_OID )
			return( STATE_HOLE_OID );
		if( state == STATE_HOLE_OID )
			{
			if( newState == STATE_NULL )
				return( STATE_HOLE_BITSTRING );
			if( newState == STATE_BOOLEAN )
				return( STATE_HOLE_OCTETSTRING );
			}
		return( STATE_NONE );
		}

	/* Zero-length objects are usually an error, however PKCS #10 has an
	   attribute-encoding ambiguity that produces zero-length tagged 
	   extensions and OCSP has its braindamaged context-specific tagged 
	   NULLs so we don't complain about them if they have low-valued 
	   context-specific tags */
	if( item->length <= 0 && !item->indefinite )
		{
		return( ( ( item->tag & BER_CLASS_MASK ) == BER_CONTEXT_SPECIFIC && \
				  EXTRACT_CTAG( item->tag ) <= 3 ) ? \
				STATE_NONE : STATE_ERROR );
		}

	ENSURES( item->length > 0 || item->indefinite );

	/* If it's constructed, parse the nested object(s) */
	if( ( item->tag & BER_CONSTRUCTED_MASK ) == BER_CONSTRUCTED )
		{
		newState = checkASN1( stream, item->length, item->indefinite,
							  level + 1, STATE_NONE, checkDataElements );
		return( ( newState < STATE_NONE || newState >= STATE_ERROR ) ? \
				STATE_ERROR : STATE_NONE );
		}

	/* It's a context-specific tagged item that could contain anything, just 
	   skip it */
	if( ( item->tag & BER_CLASS_MASK ) != BER_CONTEXT_SPECIFIC || \
		item->length <= 0 )
		return( STATE_ERROR );
	return( cryptStatusError( sSkip( stream, item->length ) ) ? \
			STATE_ERROR : STATE_NONE );
	}

/* Check a complex ASN.1 object.  In order to handle huge CRLs with tens or 
   hundreds of thousands of individual entries we can't use a fixed loop 
   failsafe iteration count but have to vary it based on the size of the 
   input data.  Luckily this situation is relatively easy to check for, it 
   only occurs at a nesting level of 6 (when we find the CRL entries) and we 
   only have to enable it when the data length is more than 30K since the 
   default FAILSAFE_ITERATIONS_LARGE will handle anything smaller than that */

CHECK_RETVAL_ENUM( STATE ) STDC_NONNULL_ARG( ( 1 ) ) \
static ASN1_STATE checkASN1( INOUT STREAM *stream, 
							 IN_LENGTH const long length, 
							 const BOOLEAN isIndefinite, 
							 IN_RANGE( 0, MAX_NESTING_LEVEL ) const int level, 
							 IN_ENUM_OPT( ASN1_STATE ) const ASN1_STATE state, 
							 const BOOLEAN checkDataElements )
	{
	ASN1_ITEM item;
	ASN1_STATE newState;
	const long maxIterationCount = ( level == 6 && length > 30000 ) ? \
									length / 25 : FAILSAFE_ITERATIONS_LARGE;
	long localLength = length, lastPos = stell( stream );
	int iterationCount;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	REQUIRES( ( level > 0 && level <= MAX_NESTING_LEVEL ) || \
			  ( level == 0 && length == LENGTH_MAGIC ) );
	REQUIRES( state >= STATE_NONE && state < STATE_ERROR );
	REQUIRES( ( isIndefinite && length == 0 ) || \
			  ( !isIndefinite && length >= 0 && length < MAX_INTLENGTH ) );

	/* Make sure that we're not processing suspiciosly deeply nested data */
	if( level >= MAX_NESTING_LEVEL )
		return( STATE_ERROR );

	for( iterationCount = 0;
		 ( newState = getItem( stream, &item ) ) == STATE_NONE && \
			iterationCount < maxIterationCount;
		 iterationCount++ )
		{
		/* If this is the top level (for which the length isn't known in
		   advance) and the item has a definite length, set the length to 
		   the item's length */
		if( level <= 0 && !item.indefinite )
			localLength = item.headerSize + item.length;

		/* If this is an EOC (tag == BER_RESERVED) for an indefinite item, 
		   we're done */
		if( isIndefinite && item.tag == BER_RESERVED )
			return( STATE_NONE );

		/* Check the object */
		if( !checkDataElements && item.length > 0 )
			{
			/* Shortcut to save a level of recursion, if we're not 
			   interested in the data elements (i.e. if we're just doing a
			   length check) and the item has a definite length, just skip 
			   over it and continue */
			if( cryptStatusError( sSkip( stream, item.length ) ) )
				return( STATE_ERROR );
			}
		else
			{
			newState = checkASN1Object( stream, &item, level + 1, state, 
										checkDataElements );
			if( newState < STATE_NONE || newState >= STATE_ERROR )
				return( STATE_ERROR );
			}

		/* If it's an indefinite-length object, we have to keep going until 
		   we find the EOC octets */
		if( isIndefinite )
			continue;

		/* If the outermost object was of indefinite length and we've come 
		   back to the top level, exit.  The isIndefinite flag won't be set
		   at this point because we can't know the length status before we
		   start, but it's implicitly indicated by finding a length of
		   LENGTH_MAGIC at the topmost level */
		if( level == 0 && length == LENGTH_MAGIC )
			return( STATE_NONE );

		/* Check whether we've reached the end of the current (definite-
		   length) object */
		localLength -= stell( stream ) - lastPos;
		lastPos = stell( stream );
		if( localLength < 0 || localLength >= MAX_INTLENGTH )
			return( STATE_ERROR );
		if( localLength == 0 )
			{
			/* We've reached the end of the object, we're done */
			return( newState );
			}
		}
	ENSURES_S( iterationCount < maxIterationCount );

	return( ( newState == STATE_NONE ) ? STATE_NONE : STATE_ERROR );
	}

/* Check the encoding of a complete object and determine its length */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int checkObjectEncoding( IN_BUFFER( objectLength ) const void *objectPtr, 
						 IN_LENGTH const int objectLength )
	{
	STREAM stream;
	ASN1_STATE state;
	int length = DUMMY_INIT;

	assert( isReadPtr( objectPtr, objectLength ) );
	
	REQUIRES( objectLength > 0 && objectLength < MAX_INTLENGTH );

	sMemConnect( &stream, objectPtr, objectLength );
	state = checkASN1( &stream, LENGTH_MAGIC, FALSE, 0, STATE_NONE, TRUE );
	if( state >= STATE_NONE && state < STATE_ERROR )
		length = stell( &stream );
	sMemDisconnect( &stream );
	return( ( state < STATE_NONE ) ? CRYPT_ERROR_INTERNAL : \
			( state >= STATE_ERROR ) ? CRYPT_ERROR_BADDATA : length );
	}

/* Recursively dig into an ASN.1 object as far as we need to to determine 
   its length */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int findObjectLength( INOUT STREAM *stream, 
							 OUT_LENGTH_Z long *length, 
							 const BOOLEAN isLongObject )
	{
	const long startPos = stell( stream );
	long localLength;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( length, sizeof( long ) ) );

	/* Clear return value */
	*length = 0;

	/* Try for a definite length */
	if( isLongObject )
		status = readLongGenericHole( stream, &localLength, DEFAULT_TAG );
	else
		{
		int shortLength;

		status = readGenericHoleI( stream, &shortLength, 0, DEFAULT_TAG );
		localLength = shortLength;
		}
	if( cryptStatusError( status ) )
		return( status );

	/* If it's an indefinite-length object, burrow down into it to find its 
	   actual length */
	if( localLength == CRYPT_UNUSED )
		{
		ASN1_STATE state;

		/* We have to be a bit careful how we handle error reporting for 
		   this since we can run out of input and hit an underflow while 
		   we're in the process of burrowing through the data.  This is 
		   somewhat unfortunate since it leads to non-orthogonal behaviour 
		   because a definite length only requires checking a few bytes at 
		   the start of the data but an indefinite length requires 
		   processing the entire data quantity in order to determine where 
		   it ends */
		sseek( stream, startPos );
		state = checkASN1( stream, LENGTH_MAGIC, FALSE, 0, STATE_NONE, 
						   FALSE );
		if( state < STATE_NONE || state >= STATE_ERROR )
			{
			return( ( state < STATE_NONE ) ? \
						CRYPT_ERROR_INTERNAL : \
					( sGetStatus( stream ) == CRYPT_ERROR_UNDERFLOW ) ? \
						CRYPT_ERROR_UNDERFLOW : \
						CRYPT_ERROR_BADDATA );
			}
		localLength = stell( stream ) - startPos;
		}
	else
		{
		/* It's a definite-length object, add the size of the tag+length */
		localLength += stell( stream ) - startPos;
		}

	/* If it's not a long object, make sure that the length is within bounds.  
	   We have to do this explicitly here because indefinite-length objects
	   can be arbitrarily large so the length isn't checked as it is for
	   readGenericHoleI() */
	if( !isLongObject && localLength > 32767L )
		return( CRYPT_ERROR_OVERFLOW );
	*length = localLength;
	return( sseek( stream, startPos ) );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int getStreamObjectLength( INOUT STREAM *stream, OUT_LENGTH_Z int *length )
	{
	long localLength;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( length, sizeof( int ) ) );

	/* Clear return value */
	*length = 0;

	status = findObjectLength( stream, &localLength, FALSE );
	if( cryptStatusOK( status ) )
		*length = localLength;
	return( status );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int getLongStreamObjectLength( INOUT STREAM *stream, 
							   OUT_LENGTH_Z long *length )
	{
	long localLength;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isWritePtr( length, sizeof( long ) ) );

	/* Clear return value */
	*length = 0;

	status = findObjectLength( stream, &localLength, FALSE );
	if( cryptStatusOK( status ) )
		*length = localLength;
	return( status );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
int getObjectLength( IN_BUFFER( objectLength ) const void *objectPtr, 
					 IN_LENGTH const int objectLength, 
					 OUT_LENGTH_Z int *length )
	{
	STREAM stream;
	long localLength = DUMMY_INIT;
	int status;

	assert( isReadPtr( objectPtr, objectLength ) );
	assert( isWritePtr( length, sizeof( int ) ) );

	REQUIRES( objectLength > 0 && objectLength < MAX_INTLENGTH );

	/* Clear return value */
	*length = 0;

	sMemConnect( &stream, objectPtr, objectLength );
	if( peekTag( &stream ) == BER_INTEGER )
		{
		/* Sometimes we're asked to find the length of non-hole items that 
		   will be rejected by findObjectLength(), which calls down to 
		   readGenericHoleI().  Since these items are primitive and non-
		   constructed (in order to qualify as non-holes), we can process 
		   the item with readUniversal().
		   
		   An alternative processing mechanism would be to use peekTag() and
		   readGenericHole() in combination with the peekTag() results */
		status = readUniversal( &stream );
		if( cryptStatusOK( status ) )
			localLength = stell( &stream );
		}
	else
		{
		/* Quousque tandem? */
		status = findObjectLength( &stream, &localLength, FALSE );
		}
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	*length = localLength;
	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
int getLongObjectLength( IN_BUFFER( objectLength ) const void *objectPtr, 
						 IN_LENGTH const long objectLength,
						 OUT_LENGTH_Z long *length )
	{
	STREAM stream;
	long localLength;
	int status;

	assert( isReadPtr( objectPtr, objectLength ) );
	assert( isWritePtr( length, sizeof( long ) ) );

	REQUIRES( objectLength > 0 && objectLength < MAX_INTLENGTH );

	/* Clear return value */
	*length = 0;

	sMemConnect( &stream, objectPtr, objectLength );
	status = findObjectLength( &stream, &localLength, TRUE );
	sMemDisconnect( &stream );
	if( cryptStatusOK( status ) )
		*length = localLength;
	return( status );
	}

⌨️ 快捷键说明

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