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

📄 asn1.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 4 页
字号:
	if( ( isBitstring && state != STATE_HOLE_BITSTRING ) ||
		( !isBitstring && ( state != STATE_HOLE_OID && \
							state != STATE_HOLE_OCTETSTRING ) ) )
		{
		sungetc( stream );
		return( FALSE );
		}

	/* A BIT STRING which encapsulates something only ever contains
	   { SEQUENCE, sequence_length < length, INTEGER } */
	if( isBitstring )
		{
		/* Make sure there's a SEQUENCE of a vaguely correct length
		   present */
		innerLength = readShortLength( stream );
		if( tag != BER_SEQUENCE || \
			innerLength < length - 10 || innerLength > length + 10 )
			{
			sClearError( stream );
			sseek( stream, streamPos );
			return( FALSE );
			}
		
		/* Make sure that the first thing inside the SEQUENCE is an
		   INTEGER */
		if( readTag( stream ) != BER_INTEGER )
			isEncapsulated = FALSE;
		innerLength = readShortLength( stream );
		if( innerLength < length - 12 || innerLength > length + 8 )
			isEncapsulated = FALSE;

		sClearError( stream );
		sseek( stream, streamPos );
		return( isEncapsulated );
		}

	/* An OCTET STRING is more complex.  This could encapsulate any of:
		BIT STRING: keyUsage, crlReason, Netscape certType, must be
			<= 16 bits and a valid bitstring.
		GeneralisedTime: invalidityDate: too difficult to identify
			since the obvious check for a valid length will also fail
			invalid-length encodings, missing the very thing we usually
			want to check for.
		IA5String: Netscape extensions, also checked by the context-
			aware higher-level code which knows how long and in what
			format the string should be.
		INTEGER: deltaCRLIndicator, crlNumber, must be <= 16 bits).
		OCTET STRING: keyID, a blob which we don't check.
		OID: holdInstructionCode, which is difficult to identify and
			will be checked by the context-aware extension read code
			anyway.
		SEQUENCE: most extensions, a bit difficult to check but for
			now we make sure the length is roughly right */
	switch( tag )
		{
		case BER_BITSTRING:
			innerLength = readShortLength( stream );
			if( innerLength < 0 || innerLength > 2 )
				isEncapsulated = FALSE;
			else
				{
				int ch = sgetc( stream );

				if( ch < 0 || ch > 7 )
					isEncapsulated = FALSE;
				}
			break;

		case BER_INTEGER:
			innerLength = readShortLength( stream );
			if( innerLength < 0 || innerLength > 2 )
				isEncapsulated = FALSE;
			break;

		case BER_SEQUENCE:
			innerLength = readShortLength( stream );
			if( innerLength < length - 10 || innerLength > length + 10 )
				isEncapsulated = FALSE;
			break;

		default:
			isEncapsulated = FALSE;
		}
	sClearError( stream );
	sseek( stream, streamPos );
	return( isEncapsulated );
	}

/* Check a primitive ASN.1 object */

static ASN1_STATE checkASN1( STREAM *stream, long length,
							 const int isIndefinite, const int level,
							 ASN1_STATE state, 
							 const BOOLEAN checkDataElements );

static ASN1_STATE checkPrimitive( STREAM *stream, const ASN1_ITEM *item,
								  const int level, const ASN1_STATE state )
	{
	int length = ( int ) item->length, ch, i;

	/* In theory only NULL and EOC elements are allowed to have a zero 
	   length, but some broken implementations (Netscape, Van Dyke) encode
	   numeric zero values as a zero-length element so we have to accept 
	   these as well */
	if( !item->length && item->tag != BER_NULL && \
						 item->tag != BER_RESERVED && \
						 item->tag != BER_INTEGER )
		return( STATE_ERROR );

	/* Perform a general check that everything is OK.  We don't check for 
	   invalid content except where it would impede decoding of the data in
	   order to avoid failing on all of the broken certs out there */
	switch( item->tag )
		{
		case BER_BOOLEAN:
			sgetc( stream );
			return( STATE_BOOLEAN );

		case BER_INTEGER:
		case BER_ENUMERATED:
			if( length > 0 )	/* May be encoded as a zero-length value */
				sSkip( stream, length );
			return( STATE_NONE );

		case BER_BITSTRING:
			/* Check the number of unused bits */
			ch = sgetc( stream );
			length--;
			if( ch < 0 || ch > 7 )
				/* Invalid number of unused bits */
				return( STATE_ERROR );

			/* If it's short enough to be a bit flag, it's just a sequence 
			   of bits */
			if( length <= 4 )
				{
				if( length )
					sSkip( stream, length );
				return( STATE_NONE );
				}
			/* Fall through */

		case BER_OCTETSTRING:
			/* If it's something encapsulated inside the string, handle it
			   as a constructed item */
			if( checkEncapsulation( stream, length, ( BOOLEAN )	/* VC++ kludge */
					( ( item->tag == BER_BITSTRING ) ? TRUE : FALSE ), state ) )
				{
				ASN1_STATE state;

				state = checkASN1( stream, length, item->indefinite,
								   level + 1, STATE_NONE, TRUE );
				return( ( state == STATE_ERROR ) ? STATE_ERROR : STATE_NONE );
				}

			/* Skip the data */
			sSkip( stream, length );
			return( STATE_NONE );

		case BER_OBJECT_IDENTIFIER:
			if( length > MAX_OID_SIZE - 2 )
				/* Total OID size (including tag and length, since they're 
				   treated as a blob) should be less than a sane limit */
				return( STATE_ERROR );

			/* At this point we could check for obsolete and deprecated OIDs,
			   but this will be caught later on anyway */
			sSkip( stream, length );
			return( 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:
			sSkip( stream, length );
			return( 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; i++ )
				{
				ch = sgetc( stream );
				if( !isdigit( ch ) && ch != 'Z' )
					return( STATE_ERROR );
				}
			return( STATE_NONE );

		default:
			/* Disallowed or unrecognised primitive */
			return( STATE_ERROR );
		}

	return( STATE_NONE );
	}

/* Check a single ASN.1 object */

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

	/* Perform a sanity check */
	if( ( item->tag != BER_NULL ) && ( item->length < 0 ) )
		/* Object has a bad length field, usually because we've lost sync in 
		   the decoder or run into garbage */
		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 )
			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_OID */
		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 );
		}

	/* If we're interested in the data elements and the item has a definite
	   length, skip over it and continue */
	if( !checkDataElements && item->length )
		{
		sSkip( stream, item->length );
		return( STATE_NONE );
		}

	/* If it's constructed, check the various fields in it */
	if( item->length || item->indefinite )
		{
		/* If it's constructed, parse the nested object(s) */
		if( ( item->tag & BER_CONSTRUCTED_MASK ) == BER_CONSTRUCTED )
			{
			ASN1_STATE newState;

			newState = checkASN1( stream, item->length, item->indefinite,
								  level + 1, STATE_NONE, checkDataElements );
			return( ( newState == STATE_ERROR ) ? STATE_ERROR : STATE_NONE );
			}

		/* This could be anything */
		sSkip( stream, item->length );
		return( STATE_NONE );
		}

	/* At this point we have a zero-length object which should be an error,
	   however PKCS #10 has the attribute-encoding problem which produces
	   these objects so we can't complain about them */
	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 );
	BOOLEAN seenEOC = FALSE;
	ASN1_STATE status;

	/* Sanity-check the nesting level */
	if( level > MAX_NESTING_LEVEL )
		return( STATE_ERROR );

	/* Special-case for zero-length objects */
	if( !length && !isIndefinite )
		return( STATE_NONE );

	while( ( status = getItem( stream, &item ) ) == STATE_NONE )
		{
		/* If the length isn't known and the item has a definite length, set
		   the length to the item's length */
		if( length == LENGTH_MAGIC && !item.indefinite )
			length = item.headerSize + item.length;

		/* Check whether this is an EOC for an indefinite item */
		if( !item.indefinite && item.tag == BER_RESERVED )
			seenEOC = TRUE;
		else
			{
			state = checkASN1object( stream, &item, level + 1, state, 
									 checkDataElements );
			if( state == STATE_ERROR || sGetStatus( stream ) != CRYPT_OK )
				return( STATE_ERROR );
			}

		/* If it was an indefinite-length object (no length was ever set) and
		   we've come back to the top level, exit */
		if( length == LENGTH_MAGIC )
			return( 0 );

		length -= stell( stream ) - lastPos;
		lastPos = stell( stream );
		if( isIndefinite )
			{
			if( seenEOC )
				return( STATE_NONE );
			}
		else
			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( objectPtr != NULL && objectLength > 0 );

	sMemConnect( &stream, objectPtr, objectLength );
	state = checkASN1( &stream, LENGTH_MAGIC, FALSE, 1, STATE_NONE, TRUE );
	length = ( int ) 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 */

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

	assert( objectPtr != NULL && objectLength > 0 );

	sMemConnect( &stream, objectPtr, objectLength );
	readTag( &stream );
	length = readShortLength( &stream );
	if( !cryptStatusError( length ) )
		{
		if( length )
			/* Add the size of the tag+length */
			length += ( int ) stell( &stream );
		else
			{
			/* The object has an indefinite length, burrow down into it to
			   find its actual length */
			sseek( &stream, 0 );
			length = checkASN1( &stream, LENGTH_MAGIC, FALSE, 1, STATE_NONE, 
								FALSE );
			length = ( length == STATE_ERROR ) ? \
					 CRYPT_ERROR_BADDATA : ( int ) stell( &stream );
			}
		}
	sMemDisconnect( &stream );

	return( length );
	}

⌨️ 快捷键说明

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