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

📄 asn1_rw.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************
*																			*
*					Utility Routines for Constructed Objects				*
*																			*
****************************************************************************/

/* Check that a tag for one of the hole types is valid: BIT STRING,
   primtive or constructed OCTET STRING, SEQUENCE, or SET */

#define isValidHoleTag( tag ) \
		( ( tagValue & BER_CLASS_MASK ) != BER_UNIVERSAL || \
		  ( tagValue == BER_BITSTRING || tagValue == BER_OCTETSTRING || \
			tagValue == ( BER_OCTETSTRING | BER_CONSTRUCTED ) || \
			tagValue == BER_SEQUENCE || tagValue == BER_SET ) )

/* Read an encapsulating SEQUENCE or SET or BIT STRING/OCTET STRING hole */

static int readObjectHeader( STREAM *stream, int *length, const int tag,
							 const BOOLEAN isBitString, 
							 const BOOLEAN indefOK )
	{
	int tagValue, dataLength;

	/* Clear return value */
	if( length != NULL )
		*length = 0;

	/* Read the object tag */
	tagValue = readTag( stream );
	if( cryptStatusError( tagValue ) )
		return( tagValue );
	if( tag == ANY_TAG )
		{
		/* Even if we're prepared to accept (almost) any tag, we still have 
		   to check for valid universal tags */
		if( !isValidHoleTag( tagValue ) )
			{
			sSetError( stream, CRYPT_ERROR_BADDATA );
			return( CRYPT_ERROR_BADDATA );
			}
		}
	else
		if( tagValue != tag )
			{
			sSetError( stream, CRYPT_ERROR_BADDATA );
			return( CRYPT_ERROR_BADDATA );
			}
	
	/* Read the length.  If the indefiniteOK flag is set of the length is 
	   being ignored by the caller we allow indefinite lengths.  The latter
	   is because it makes handling of infinitely-nested SEQUENCEs and 
	   whatnot easier if we don't have to worry about definite vs. 
	   indefinite-length encoding, and if indefinite lengths really aren't 
	   OK then they'll be picked up when the caller runs into the EOC at the
	   end of the object */
	dataLength = readLengthValue( stream, ( indefOK || length == NULL ) ? \
								  READLENGTH_SHORT_INDEF : READLENGTH_SHORT );
	if( cryptStatusError( dataLength ) )
		{
		/* If we've asked for an indication of indefinite-length values and we
		   got one, convert the length to CRYPT_UNUSED */
		if( indefOK && dataLength == OK_SPECIAL )
			dataLength = CRYPT_UNUSED;
		else
			return( dataLength );
		}

	/* If it's a bit string there's an extra unused-bits count */
	if( isBitString )
		{
		if( dataLength != CRYPT_UNUSED )
			dataLength--;
		if( length != NULL )
			*length = dataLength;
		return( sgetc( stream ) );
		}

	if( length != NULL )
		*length = dataLength;
	return( CRYPT_OK );
	}

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

	return( readObjectHeader( stream, length, BER_SEQUENCE, FALSE, FALSE ) );
	}

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

	return( readObjectHeader( stream, length, BER_SEQUENCE, FALSE, TRUE ) );
	}

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

	return( readObjectHeader( stream, length, BER_SET, FALSE, FALSE ) );
	}

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

	return( readObjectHeader( stream, length, BER_SET, FALSE, TRUE ) );
	}

int readConstructed( STREAM *stream, int *length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
							  BER_SEQUENCE : MAKE_CTAG( tag ), FALSE, FALSE ) );
	}

int readConstructedI( STREAM *stream, int *length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
							  BER_SEQUENCE : MAKE_CTAG( tag ), FALSE, TRUE ) );
	}

int readOctetStringHole( STREAM *stream, int *length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
							  BER_OCTETSTRING : MAKE_CTAG_PRIMITIVE( tag ),
							  FALSE, FALSE ) );
	}

int readBitStringHole( STREAM *stream, int *length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
							  BER_BITSTRING : MAKE_CTAG_PRIMITIVE( tag ),
							  TRUE, FALSE ) );
	}

int readGenericHole( STREAM *stream, int *length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readObjectHeader( stream, length, 
							  ( tag == DEFAULT_TAG ) ? ANY_TAG : tag, 
							  FALSE, FALSE ) );
	}

/* Read an abnormally-long encapsulating SEQUENCE or OCTET STRING hole.  
   This is used in place of the usual read in places where potentially huge 
   data quantities would fail the sanity check enforced by standard read.
   This form always allows indefinite lengths, which are likely for large
   objects */

static int readLongObjectHeader( STREAM *stream, long *length, const int tag )
	{
	long dataLength;
	int tagValue;

	if( length != NULL )
		*length = 0;	/* Clear return value */
	tagValue = readTag( stream );
	if( cryptStatusError( tagValue ) )
		return( tagValue );
	if( tag == ANY_TAG )
		{
		/* Even if we're prepared to accept (almost) any tag, we still have 
		   to check for valid universal tags */
		if( !isValidHoleTag( tagValue ) )
			{
			sSetError( stream, CRYPT_ERROR_BADDATA );
			return( CRYPT_ERROR_BADDATA );
			}
		}
	else
		if( tagValue != tag )
			{
			sSetError( stream, CRYPT_ERROR_BADDATA );
			return( CRYPT_ERROR_BADDATA );
			}
	dataLength = readLengthValue( stream, READLENGTH_LONG_INDEF );
	if( cryptStatusError( dataLength ) )
		{
		/* We've asked for an indication of indefinite-length values, if we
		   got one convert the length to CRYPT_UNUSED */
		if( dataLength == OK_SPECIAL )
			dataLength = CRYPT_UNUSED;
		else
			return( dataLength );
		}
	if( length != NULL )
		*length = dataLength;

	return( CRYPT_OK );
	}

long readLongSequence( STREAM *stream, long *length )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readLongObjectHeader( stream, length, BER_SEQUENCE ) );
	}

long readLongConstructed( STREAM *stream, long *length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readLongObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
								  BER_SEQUENCE : MAKE_CTAG( tag ) ) );
	}

long readLongGenericHole( STREAM *stream, long *length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	return( readLongObjectHeader( stream, length, 							  
								  ( tag == DEFAULT_TAG ) ? ANY_TAG : tag ) );
	}

/* Write the start of an encapsulating SEQUENCE, SET, or generic tagged
   constructed object.  The difference between writeOctet/BitStringHole() and
   writeGenericHole() is that the octet/bit-string versions create a normal
   or context-specific-tagged string while the generic version creates a 
   pure hole with no processing of tags */

int writeSequence( STREAM *stream, const int length )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length >= 0 );

	writeTag( stream, BER_SEQUENCE );
	return( writeLength( stream, length ) );
	}

int writeSet( STREAM *stream, const int length )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length >= 0 );

	writeTag( stream, BER_SET );
	return( writeLength( stream, length ) );
	}

int writeConstructed( STREAM *stream, const int length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length >= 0 );

	writeTag( stream, ( tag == DEFAULT_TAG ) ? \
			  BER_SEQUENCE : MAKE_CTAG( tag ) );
	return( writeLength( stream, length ) );
	}

int writeOctetStringHole( STREAM *stream, const int length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length >= 0 );

	writeTag( stream, ( tag == DEFAULT_TAG ) ? \
			  BER_OCTETSTRING : MAKE_CTAG_PRIMITIVE( tag ) );
	return( writeLength( stream, length ) );
	}

int writeBitStringHole( STREAM *stream, const int length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length >= 0 );

	writeTag( stream, ( tag == DEFAULT_TAG ) ? \
			  BER_BITSTRING : MAKE_CTAG_PRIMITIVE( tag ) );
	writeLength( stream, length + 1 );	/* +1 for bit count */
	return( sputc( stream, 0 ) );
	}

int writeGenericHole( STREAM *stream, const int length, const int tag )
	{
	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length >= 0 );

	writeTag( stream, tag );
	return( writeLength( stream, length ) );
	}

/****************************************************************************
*																			*
*						ASN.1 Encoding/Length Checks						*
*																			*
****************************************************************************/

/* The maximum nesting level for constructed or encapsulated objects (this
   can get surprisingly high for some of the more complex attributes).  This
   value is chosen to pass all normal certs while avoiding stack overflows
   for artificial bad data */

#define MAX_NESTING_LEVEL	50

/* When we parse a nested data object encapsulated within a larger object,
   the length is initially set to a magic value which is adjusted to the
   actual length once we start parsing the object */

#define LENGTH_MAGIC		177545L

/* Current parse state.  This is used to check for potential BIT STRING and
   OCTET STRING targets for OCTET/BIT STRING holes, which are always
   preceded by an AlgorithmIdentifier.  In order to detect these without
   having to know every imaginable AlgorithmIdentifier OID, we check for the
   following sequence of events:

	checkASN1Object								-- SEQUENCE
		checkASN1
			checkASN1Object
				checkPrimitive					-- OID
			checkASN1Object
				checkPrimitive					-- opt.BOOLEAN	  OCTET STRING
				checkPrimitive					-- NULL, or		|
				checkASN1Object					-- SEQUENCE		| BIT STRING
	checkASN1Object
		checkPrimitive							-- OCTET/BIT STRING

   This type of checking is rather awkward in the (otherwise stateless) code,
   but is the only way to be sure that it's safe to try burrowing into an
   OCTET STRING or BIT STRING to try to find encapsulated data, since
   otherwise even with relatively strict checking there's still a very small
   chance that random data will look like a nested object */

typedef enum {
	/* Generic non-state */
	STATE_NONE,

	/* States corresponding to ASN.1 primitives */
	STATE_BOOLEAN, STATE_NULL, STATE_OID, STATE_SEQUENCE,

	/* States corresponding to different parts of a SEQUENCE { OID, optional,
	   OCTET/BIT STRING } sequence */
	STATE_HOLE_OID, STATE_HOLE_BITSTRING, STATE_HOLE_OCTETSTRING,

	/* Error state */
	STATE_ERROR
	} ASN1_STATE;

/* Structure to hold info on an ASN.1 item */

typedef struct {
	int tag;			/* Tag */
	long length;		/* Data length */
	BOOLEAN indefinite;	/* Item has indefinite length */
	int headerSize;		/* Size of tag+length */
	} ASN1_ITEM;

/* Get an ASN.1 object's tag and length */

static int getItem( STREAM *stream, ASN1_ITEM *item )
	{
	int length;

	memset( item, 0, sizeof( ASN1_ITEM ) );
	item->headerSize = 2;
	item->tag = sgetc( stream );
	length = sgetc( stream );
	if( cryptStatusError( length ) )
		return( STATE_ERROR );
	if( length & 0x80 )
		{
		int i;

		length &= 0x7F;
		if( length > 4 )
			/* Object has a bad length field, usually because we've lost sync
			   in the decoder or run into garbage */
			return( STATE_ERROR );
		item->headerSize += length;
		item->length = 0;
		if( !length )
			item->indefinite = TRUE;
		for( i = 0; i < length; i++ )
			item->length = ( item->length << 8 ) | sgetc( stream );
		}
	else
		item->length = length;
	if( item->headerSize < 2 || item->length < 0 )
		return( STATE_ERROR );

	return( sStatusOK( stream ) ? STATE_NONE : STATE_ERROR );
	}

/* Check whether an ASN.1 object is encapsulated inside an OCTET STRING or
   BIT STRING.  After performing the various checks we have to explicitly
   clear the stream error state since the probing for valid data could have
   set the error indicator if nothing valid was found */

static BOOLEAN checkEncapsulation( STREAM *stream, const int length,
								   const BOOLEAN isBitstring,
								   const ASN1_STATE state )
	{
	BOOLEAN isEncapsulated = TRUE;
	long streamPos = stell( stream );
	int tag = peekTag( stream ), innerLength, status;

	/* Perform a quick check to see whether an OCTET STRING or BIT STRING hole
	   is allowed at this point.  A BIT STRING must be preceded by { SEQ, OID,
	   NULL }.  An OCTET STRING must be preceded by { SEQ, OID, {BOOLEAN} } */
	if( ( isBitstring && state != STATE_HOLE_BITSTRING ) ||
		( !isBitstring && ( state != STATE_HOLE_OID && \
							state != STATE_HOLE_OCTETSTRING ) ) )
		return( FALSE );

	/* A BIT STRING that encapsulates something only ever contains
	   { SEQUENCE, sequence_length < length, INTEGER } */
	if( isBitstring )
		{
		/* Make sure that there's a SEQUENCE of a vaguely correct length
		   present */
		status = readSequence( stream, &innerLength );

⌨️ 快捷键说明

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