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

📄 asn1_rd.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 4 页
字号:
/****************************************************************************
*																			*
*								ASN.1 Read Routines							*
*						Copyright Peter Gutmann 1992-2008					*
*																			*
****************************************************************************/

#include <ctype.h>
#if defined( INC_ALL )	/* bn.h must be included before crypt.h */
  #include "bn.h"
#else
  #include "bn/bn.h"
#endif /* Compiler-specific includes */
#if defined( INC_ALL )
  #include "crypt.h"
  #include "asn1.h"
#else
  #include "crypt.h"
  #include "misc/asn1.h"
#endif /* Compiler-specific includes */

/****************************************************************************
*																			*
*								Utility Routines							*
*																			*
****************************************************************************/

/* When specifying a tag we can use either the default tag for the object
   (indicated with the value DEFAULT_TAG) or a special-case tag.  The 
   following macro selects the correct value.  Since these are all primitive 
   objects, we force the tag type to a primitive tag */

#define selectTag( tag, defaultTag )	\
		( ( ( tag ) == DEFAULT_TAG ) ? ( defaultTag ) : \
									   ( MAKE_CTAG_PRIMITIVE( tag ) ) )

/* Read the length octets for an ASN.1 data type, with special-case handling
   for long and short lengths and indefinite-length encodings.  The short-
   length read is limited to 32K, which is a sane limit for most PKI data 
   and one that doesn't cause type conversion problems on systems where 
   sizeof( int ) != sizeof( long ).  If the caller indicates that indefinite 
   lengths are OK, we return OK_SPECIAL if we encounter one.  Long length 
   reads always allow indefinite lengths since these are quite likely for 
   large objects */

typedef enum {
	READLENGTH_NONE,		/* No length read behaviour */
	READLENGTH_SHORT,		/* Short length, no indef.allowed */
	READLENGTH_SHORT_INDEF,	/* Short length, indef.to OK_SPECIAL */
	READLENGTH_LONG_INDEF,	/* Long length, indef.to OK_SPECIAL */
	READLENGTH_LAST			/* Last possible read type */
	} READLENGTH_TYPE;

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readLengthValue( INOUT STREAM *stream, 
							OUT_LENGTH_Z long *length,
							IN_ENUM( READLENGTH ) const READLENGTH_TYPE readType )
	{
	BYTE buffer[ 8 + 8 ], *bufPtr = buffer;
	BOOLEAN shortLen = ( readType == READLENGTH_SHORT || \
						 readType == READLENGTH_SHORT_INDEF );
	long dataLength;
	int noLengthOctets, status;

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

	REQUIRES_S( readType > READLENGTH_NONE && readType < READLENGTH_LAST );

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

	/* Read the first byte of length data.  If it's a short length, we're
	   done */
	status = dataLength = sgetc( stream );
	if( cryptStatusError( status ) )
		return( status );
	if( !( dataLength & 0x80 ) )
		{
		*length = dataLength;
		return( CRYPT_OK );
		}

	/* Read the actual length octets */
	noLengthOctets = dataLength & 0x7F;
	if( noLengthOctets <= 0 )
		{
		/* If indefinite lengths aren't allowed, signal an error */
		if( readType != READLENGTH_SHORT_INDEF && \
			readType != READLENGTH_LONG_INDEF )
			return( sSetError( stream, CRYPT_ERROR_BADDATA ) );

		/* It's an indefinite length encoding, we're done */
		*length = CRYPT_UNUSED;

		return( CRYPT_OK );
		}
	if( noLengthOctets > 8 )
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
	status = sread( stream, buffer, noLengthOctets );
	if( cryptStatusError( status ) )
		return( sSetError( stream, status ) );

	/* Handle leading zero octets.  Since BER lengths can be encoded in 
	   peculiar ways (at least one text uses a big-endian 32-bit encoding 
	   for everything) we allow up to 8 bytes of non-DER length data, but 
	   only the last 2 or 4 of these (for short or long lengths 
	   respectively) can be nonzero */
	if( buffer[ 0 ] == 0 )
		{
		int i;

		/* Oddball length encoding with leading zero(es) */
		for( i = 0; i < noLengthOctets && buffer[ i ] == 0; i++ );
		noLengthOctets -= i;
		if( noLengthOctets <= 0 )
			return( 0 );		/* Very broken encoding of a zero length */
		bufPtr += i;			/* Skip leading zero(es) */
		}

	/* Make sure that the length size is reasonable */
	if( noLengthOctets > 4 )
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
	if( shortLen && noLengthOctets > 2 )
		return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );

	/* Read and check the length value */
	dataLength = 0;
	while( noLengthOctets-- > 0 )
		{
		dataLength = ( dataLength << 8 ) | *bufPtr++;
		if( dataLength <= 0 || dataLength >= MAX_INTLENGTH )
			{
			/* Integer overflow */
			return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
			}
		}
	if( shortLen )
		{
		if( dataLength & 0xFFFF8000UL || dataLength > 32767L )
			{
			/* Length must be < 32K for short lengths */
			return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
			}
		}
	else
		{
		if( ( dataLength & 0x80000000UL ) || dataLength >= MAX_INTLENGTH )
			{
			/* Length must be < MAX_INTLENGTH for standard data */
			return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
			}
		}
	if( dataLength <= 0 )
		{
		/* Shouldn't happen since the overflow check above catches it, but we 
		   check again just to be safe */
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
		}
	ENSURES_S( dataLength > 0 && dataLength < MAX_INTLENGTH );
	*length = dataLength;

	return( CRYPT_OK );
	}

/* Read the header for a (signed) integer value */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int readIntegerHeader( INOUT STREAM *stream, 
							  IN_TAG_EXT const int tag )
	{
	long length;
	int iterationCount, status;

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

	REQUIRES_S( tag == NO_TAG || tag == DEFAULT_TAG || \
				( tag >= 0 && tag < MAX_TAG_VALUE ) );

	/* Read the identifier field if necessary and the length */
	if( tag != NO_TAG && readTag( stream ) != selectTag( tag, BER_INTEGER ) )
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
	status = readLengthValue( stream, &length, READLENGTH_SHORT );
	if( cryptStatusError( status ) )
		return( status );
	if( length <= 0 )
		return( length );	/* Zero-length data */

	/* ASN.1 encoded values are signed while the internal representation is
	   unsigned so we skip any leading zero bytes needed to encode a value
	   that has the high bit set.  If we get a value with the (supposed) 
	   sign bit set we treat it as an unsigned value since a number of 
	   implementations get this wrong */
	for( iterationCount = 0;
		 length > 0 && sPeek( stream ) == 0 && \
			iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ )
		{
		status = sgetc( stream );
		if( cryptStatusError( status ) )
			return( status );
		length--;
		ENSURES_S( status == 0 );
		}
	ENSURES_S( iterationCount < FAILSAFE_ITERATIONS_MED );

	return( length );
	}

/* Read the header for a constructed object */

CHECK_RETVAL \
static int checkMatchAnyTag( IN_TAG_ENCODED const int tag )
	{
	REQUIRES( tag > 0 && tag <= MAX_TAG );

	/* Even if we're prepared to accept (almost) any tag, we still have to 
	   check for valid universal tags: BIT STRING, primitive or constructed 
	   OCTET STRING, SEQUENCE, or SET */
	if( tag == BER_BITSTRING || tag == BER_OCTETSTRING || \
		tag == ( BER_OCTETSTRING | BER_CONSTRUCTED ) || \
		tag == BER_SEQUENCE || tag == BER_SET )
		return( ANY_TAG );

	/* In addition we can accept context-specific tagged items up to [10] */
	if( ( tag & BER_CLASS_MASK ) == BER_CONTEXT_SPECIFIC && \
		( tag & BER_SHORT_ID_MASK ) <= MAX_CTAG_VALUE )
		return( ANY_TAG );

	/* Anything else is invalid */
	return( 0 );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int readObjectHeader( INOUT STREAM *stream, 
							 OUT_OPT_LENGTH_Z int *length, 
							 IN_LENGTH_SHORT const int minLength, 
							 IN_TAG_ENCODED_EXT const int tag, 
							 const BOOLEAN isBitString, 
							 const BOOLEAN indefOK )
	{
	long dataLength;
	int tagValue, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length == NULL || isWritePtr( length, sizeof( int ) ) );

	REQUIRES_S( minLength >= 0 && minLength < MAX_INTLENGTH_SHORT );
	REQUIRES_S( tag == NO_TAG || tag == ANY_TAG || \
				( tag >= 1 && tag < MAX_TAG ) );
				/* Note tag != 0 */

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

	/* Read the identifier field */
	tagValue = readTag( stream );
	if( cryptStatusError( tagValue ) )
		return( tagValue );
	if( tag == ANY_TAG )
		{
		/* If we're prepared to accept any (valid) tag beyond the filtering 
		   already performed by readTag(), make sure that it's a potentially 
		   valid value for this particular context */
		tagValue = checkMatchAnyTag( tagValue );
		}
	if( tagValue != tag )
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );

	/* Read the length.  If the indefiniteOK flag is set or the length is 
	   being ignored by the caller then 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 encodings, 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 */
	status = readLengthValue( stream, &dataLength,
							  ( indefOK || length == NULL ) ? \
								READLENGTH_SHORT_INDEF : READLENGTH_SHORT );
	if( cryptStatusError( status ) )
		return( status );

	/* If it's a bit string there's an extra unused-bits count.  Since this 
	   is a hole encoding we don't bother about the actual value except to
	   check that it has a sensible value */
	if( isBitString )
		{
		int value;

		if( dataLength != CRYPT_UNUSED )
			{
			dataLength--;
			if( dataLength < 0 || dataLength >= MAX_INTLENGTH )
				return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
			}
		value = sgetc( stream );
		if( cryptStatusError( value ) )
			return( value );
		if( value < 0 || value > 7 )
			return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
		}

	/* Make sure that the length is in order and return it to the caller if 
	   necessary */
	if( ( dataLength != CRYPT_UNUSED ) && ( dataLength < minLength ) )
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
	if( length != NULL )
		*length = dataLength;
	return( CRYPT_OK );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int readLongObjectHeader( INOUT STREAM *stream, 
								 OUT_OPT_LENGTH_Z long *length, 
								 IN_TAG_ENCODED_EXT const int tag )
	{
	long dataLength;
	int tagValue, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( length == NULL || isWritePtr( length, sizeof( long ) ) );

	REQUIRES_S( tag == NO_TAG || tag == ANY_TAG || \
				( tag >= 1 && tag < MAX_TAG ) );
				/* Note tag != 0 */

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

	/* Read the identifier field */
	tagValue = readTag( stream );
	if( cryptStatusError( tagValue ) )
		return( tagValue );
	if( tag == ANY_TAG )
		{
		/* If we're prepared to accept any (valid) tag beyond the filtering 
		   already performed by readTag(), make sure that it's a potentially 
		   valid value for this particular context */
		tagValue = checkMatchAnyTag( tagValue );
		}
	if( tagValue != tag )
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );

	/* Read the length */
	status = readLengthValue( stream, &dataLength, READLENGTH_LONG_INDEF );
	if( cryptStatusError( status ) )
		return( status );
	if( length != NULL )
		*length = dataLength;

	return( CRYPT_OK );
	}

/* Read a (non-bignum) numeric value, used by several routines */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int readNumeric( INOUT STREAM *stream, OUT_OPT_LENGTH_Z long *value )
	{
	BYTE buffer[ 4 + 8 ], *bufPtr = buffer;
	long localValue = 0;
	int length, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( value == NULL || isWritePtr( value, sizeof( long ) ) );

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

	/* Read the length field and make sure that it's a non-bignum value */
	length = readIntegerHeader( stream, NO_TAG );
	if( length <= 0 )
		return( length );	/* Error or zero length */
	if( length > 4 )
		return( sSetError( stream, CRYPT_ERROR_BADDATA ) );

	/* Read the data */
	status = sread( stream, buffer, length );
	if( cryptStatusError( status ) || value == NULL )
		return( status );
	while( length-- > 0 )
		{
		localValue = ( localValue << 8 ) | *bufPtr++;
		if( localValue < 0 || length >= MAX_INTLENGTH )
			{
			/* Integer overflow */
			return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
			}
		}
	*value = localValue;
	return( CRYPT_OK );
	}

/* Read a constrained-length data value, used by several routines */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
static int readConstrainedData( INOUT STREAM *stream, 
								OUT_BUFFER_OPT( bufferMaxLength, \
												*bufferLength ) BYTE *buffer, 
								IN_LENGTH_SHORT const int bufferMaxLength,
								OUT_LENGTH_SHORT_Z int *bufferLength, 
								IN_LENGTH const int length )
	{
	int dataLength = length, remainder = 0, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( buffer == NULL || isWritePtr( buffer, length ) );
	assert( isWritePtr( bufferLength, sizeof( int ) ) );

⌨️ 快捷键说明

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