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

📄 asn1_chk.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 2 页
字号:
					STATE_ERROR : STATE_OID );

		case BER_RESERVED:
			break;					/* EOC */

		case BER_NULL:
			return( STATE_NULL );

		case BER_STRING_BMP:
		case BER_STRING_GENERAL:	/* Produced by Entrust software */
		case BER_STRING_IA5:
		case BER_STRING_ISO646:
		case BER_STRING_NUMERIC:
		case BER_STRING_PRINTABLE:
		case BER_STRING_T61:
		case BER_STRING_UTF8:
			return( cryptStatusError( sSkip( stream, length ) ) ? \
					STATE_ERROR : STATE_NONE );

		case BER_TIME_UTC:
		case BER_TIME_GENERALIZED:
			if( item->tag == BER_TIME_GENERALIZED )
				{
				if( length != 15 )
					return( STATE_ERROR );
				}
			else
				if( length != 11 && length != 13 )
					return( STATE_ERROR );
			for( i = 0; i < length - 1; i++ )
				{
				ch = sgetc( stream );
				if( ch < '0' || ch > '9' )
					return( STATE_ERROR );
				}
			if( sgetc( stream ) != 'Z' )
				return( STATE_ERROR );
			return( STATE_NONE );

		default:
			/* 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 an artifact of the way that it's 
   diagrammed here */

static ASN1_STATE checkASN1Object( STREAM *stream, const ASN1_ITEM *item,
								   const int level, const ASN1_STATE state,
								   const BOOLEAN checkDataElements )
	{
	ASN1_STATE newState;

	assert( state >= STATE_NONE && state <= STATE_ERROR );

	/* Perform a sanity check of input data */
	if( level >= MAX_NESTING_LEVEL || state == STATE_ERROR || \
		item->length < 0 )
		return( STATE_ERROR );

	/* If we're checking data elements, check the contents for validity */
	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( 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( ( newState == STATE_ERROR ) ? STATE_ERROR : 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 context-specific 
	   tags */
	if( item->length <= 0 && !item->indefinite )
		return( ( ( item->tag & BER_CLASS_MASK ) == BER_CONTEXT_SPECIFIC ) ? \
				STATE_NONE : STATE_ERROR );

	assert( 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_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 || \
		cryptStatusError( sSkip( stream, item->length ) ) )
		return( STATE_ERROR );
	return( STATE_NONE );
	}

/* Check a complex ASN.1 object */

static ASN1_STATE checkASN1( STREAM *stream, long length, const int isIndefinite,
							 const int level, ASN1_STATE state,
							 const BOOLEAN checkDataElements )
	{
	ASN1_ITEM item;
	long lastPos = stell( stream );
	ASN1_STATE status;

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

	/* Perform a sanity check of input data */
	if( level >= MAX_NESTING_LEVEL || state == STATE_ERROR || length < 0 )
		return( STATE_ERROR );

	while( ( status = getItem( stream, &item ) ) == STATE_NONE )
		{
		/* If this is the top level (for which the level 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 )
			length = 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 and the item has a definite 
			   length, just skip over it and continue */
			if( cryptStatusError( sSkip( stream, item.length ) ) )
				state = STATE_ERROR;
			}
		else
			state = checkASN1Object( stream, &item, level + 1, state, 
									 checkDataElements );
		if( state == STATE_ERROR || sGetStatus( stream ) != CRYPT_OK )
			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 */
		length -= stell( stream ) - lastPos;
		lastPos = stell( stream );
		if( length <= 0 )
			return( ( length < 0 ) ? STATE_ERROR : state );
		}

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

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

int checkObjectEncoding( const void *objectPtr, const int objectLength )
	{
	STREAM stream;
	ASN1_STATE state;
	int length;

	assert( isReadPtr( objectPtr, objectLength ) );
	assert( objectLength > 0 );

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

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

static long findObjectLength( STREAM *stream, const BOOLEAN isLongObject )
	{
	const long startPos = stell( stream );
	long length;
	int shortLength, status;

	/* Try for a definite length */
	if( isLongObject )
		status = readLongGenericHole( stream, &length, DEFAULT_TAG );
	else
		status = readGenericHoleI( stream, &shortLength, DEFAULT_TAG );
	if( cryptStatusError( status ) )
		return( status );
	if( !isLongObject )
		length = shortLength;

	/* If it's an indefinite-length object, burrow down into it to find its 
	   actual length */
	if( length == CRYPT_UNUSED )
		{
		sseek( stream, startPos );
		length = checkASN1( stream, LENGTH_MAGIC, FALSE, 0, STATE_NONE, FALSE );
		if( length == STATE_ERROR )
			return( CRYPT_ERROR_BADDATA );
		length = stell( stream ) - startPos;
		}
	else
		/* It's a definite-length object, add the size of the tag+length */
		length += stell( stream ) - startPos;
	sseek( stream, startPos );
	return( length );
	}

int getStreamObjectLength( STREAM *stream )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( findObjectLength( stream, FALSE ) );
	}

int getObjectLength( const void *objectPtr, const int objectLength )
	{
	STREAM stream;
	int length;

	assert( isReadPtr( objectPtr, objectLength ) );
	assert( objectLength > 0 );

	sMemConnect( &stream, objectPtr, objectLength );
	if( peekTag( &stream ) == BER_INTEGER )
		{
		int status;

		/* 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 = length = readUniversal( &stream );
		if( cryptStatusOK( status ) )
			length = stell( &stream );
		}
	else
		length = findObjectLength( &stream, FALSE );
	sMemDisconnect( &stream );

	return( length );
	}

long getLongObjectLength( const void *objectPtr, const long objectLength )
	{
	STREAM stream;
	int length;

	assert( isReadPtr( objectPtr, objectLength ) );
	assert( objectLength > 0 );

	sMemConnect( &stream, objectPtr, objectLength );
	length = findObjectLength( &stream, TRUE );
	sMemDisconnect( &stream );

	return( length );
	}

⌨️ 快捷键说明

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