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