📄 asn1_rd.c
字号:
/****************************************************************************
* *
* 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 + -