📄 asn1_chk.c
字号:
STATE_ERROR : 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:
return( cryptStatusError( sSkip( stream, length ) ) ? \
STATE_ERROR : 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 - 1; i++ )
{
ch = sgetc( stream );
if( ch < '0' || ch > '9' )
return( STATE_ERROR );
}
if( sgetc( stream ) != 'Z' )
return( STATE_ERROR );
return( STATE_NONE );
default:
/* Disallowed or unrecognised primitive */
return( STATE_ERROR );
}
return( STATE_NONE );
}
/* Check a single ASN.1 object. checkASN1() and checkASN1Object() are
mutually recursive, the ...Object() version only exists to avoid a
large if... else chain in checkASN1(). A typical checking run is
as follows:
30 nn cASN1 -> cAObj -> cASN1
30 nn cASN1 -> cAObj -> cASN1
04 nn nn cASN1 -> cPrim
30 80 cASN1 -> cAObj -> cASN1
30 80 cASN1 -> cAObj -> cASN1
04 nn nn cASN1 -> cPrim
00 00 cASN1 <- cAObj <- cASN1
00 00 cASN1 <- cAObj <- cASN1
The use of checkASN1Object() leads to an (apparently) excessively deep
call hierarchy, but that's mostly an artifact of the way that it's
diagrammed here */
static ASN1_STATE checkASN1Object( STREAM *stream, const ASN1_ITEM *item,
const int level, const ASN1_STATE state,
const BOOLEAN checkDataElements )
{
ASN1_STATE newState;
assert( state >= STATE_NONE && state <= STATE_ERROR );
/* Perform a sanity check of input data */
if( level >= MAX_NESTING_LEVEL || state == STATE_ERROR || \
item->length < 0 )
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 )
{
/* Special-case for zero-length SEQUENCE/SET */
if( item->length <= 0 && !item->indefinite )
return( STATE_NONE );
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_BITSTRING/HOLE_OCTETSTRING */
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 );
}
/* Zero-length objects are usually an error, however PKCS #10 has an
attribute-encoding ambiguity that produces zero-length tagged
extensions and OCSP has its braindamaged context-specific tagged
NULLs so we don't complain about them if they have context-specific
tags */
if( item->length <= 0 && !item->indefinite )
return( ( ( item->tag & BER_CLASS_MASK ) == BER_CONTEXT_SPECIFIC ) ? \
STATE_NONE : STATE_ERROR );
assert( item->length > 0 || item->indefinite );
/* If it's constructed, parse the nested object(s) */
if( ( item->tag & BER_CONSTRUCTED_MASK ) == BER_CONSTRUCTED )
{
newState = checkASN1( stream, item->length, item->indefinite,
level + 1, STATE_NONE, checkDataElements );
return( ( newState == STATE_ERROR ) ? \
STATE_ERROR : STATE_NONE );
}
/* It's a context-specific tagged item that could contain anything, just
skip it */
if( ( item->tag & BER_CLASS_MASK ) != BER_CONTEXT_SPECIFIC || \
item->length <= 0 || \
cryptStatusError( sSkip( stream, item->length ) ) )
return( STATE_ERROR );
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 );
ASN1_STATE status;
assert( state >= STATE_NONE && state <= STATE_ERROR );
assert( level > 0 || length == LENGTH_MAGIC );
assert( ( isIndefinite && length == 0 ) || \
( !isIndefinite && length >= 0 ) );
/* Perform a sanity check of input data */
if( level >= MAX_NESTING_LEVEL || state == STATE_ERROR || length < 0 )
return( STATE_ERROR );
while( ( status = getItem( stream, &item ) ) == STATE_NONE )
{
/* If this is the top level (for which the level isn't known in
advance) and the item has a definite length, set the length to
the item's length */
if( level == 0 && !item.indefinite )
length = item.headerSize + item.length;
/* If this is an EOC (tag == BER_RESERVED) for an indefinite item,
we're done */
if( isIndefinite && item.tag == BER_RESERVED )
return( STATE_NONE );
/* Check the object */
if( !checkDataElements && item.length > 0 )
{
/* Shortcut to save a level of recursion, if we're not
interested in the data elements and the item has a definite
length, just skip over it and continue */
if( cryptStatusError( sSkip( stream, item.length ) ) )
state = STATE_ERROR;
}
else
state = checkASN1Object( stream, &item, level + 1, state,
checkDataElements );
if( state == STATE_ERROR || sGetStatus( stream ) != CRYPT_OK )
return( STATE_ERROR );
/* If it's an indefinite-length object, we have to keep going until
we find the EOC octets */
if( isIndefinite )
continue;
/* If the outermost object was of indefinite length and we've come
back to the top level, exit. The isIndefinite flag won't be set
at this point because we can't know the length status before we
start, but it's implicitly indicated by finding a length of
LENGTH_MAGIC at the topmost level */
if( level == 0 && length == LENGTH_MAGIC )
return( STATE_NONE );
/* Check whether we've reached the end of the current (definite-
length) object */
length -= stell( stream ) - lastPos;
lastPos = stell( stream );
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( isReadPtr( objectPtr, objectLength ) );
assert( objectLength > 0 );
sMemConnect( &stream, objectPtr, objectLength );
state = checkASN1( &stream, LENGTH_MAGIC, FALSE, 0, STATE_NONE, TRUE );
length = 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 */
static long findObjectLength( STREAM *stream, const BOOLEAN isLongObject )
{
const long startPos = stell( stream );
long length;
int shortLength, status;
/* Try for a definite length */
if( isLongObject )
status = readLongGenericHole( stream, &length, DEFAULT_TAG );
else
status = readGenericHoleI( stream, &shortLength, DEFAULT_TAG );
if( cryptStatusError( status ) )
return( status );
if( !isLongObject )
length = shortLength;
/* If it's an indefinite-length object, burrow down into it to find its
actual length */
if( length == CRYPT_UNUSED )
{
sseek( stream, startPos );
length = checkASN1( stream, LENGTH_MAGIC, FALSE, 0, STATE_NONE, FALSE );
if( length == STATE_ERROR )
return( CRYPT_ERROR_BADDATA );
length = stell( stream ) - startPos;
}
else
/* It's a definite-length object, add the size of the tag+length */
length += stell( stream ) - startPos;
sseek( stream, startPos );
return( length );
}
int getStreamObjectLength( STREAM *stream )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
return( findObjectLength( stream, FALSE ) );
}
int getObjectLength( const void *objectPtr, const int objectLength )
{
STREAM stream;
int length;
assert( isReadPtr( objectPtr, objectLength ) );
assert( objectLength > 0 );
sMemConnect( &stream, objectPtr, objectLength );
if( peekTag( &stream ) == BER_INTEGER )
{
int status;
/* Sometimes we're asked to find the length of non-hole items that
will be rejected by findObjectLength(), which calls down to
readGenericHoleI(). Since these items are primitive and non-
constructed (in order to qualify as non-holes), we can process
the item with readUniversal().
An alternative processing mechanism would be to use peekTag() and
readGenericHole() in combination with the peekTag() results */
status = length = readUniversal( &stream );
if( cryptStatusOK( status ) )
length = stell( &stream );
}
else
length = findObjectLength( &stream, FALSE );
sMemDisconnect( &stream );
return( length );
}
long getLongObjectLength( const void *objectPtr, const long objectLength )
{
STREAM stream;
int length;
assert( isReadPtr( objectPtr, objectLength ) );
assert( objectLength > 0 );
sMemConnect( &stream, objectPtr, objectLength );
length = findObjectLength( &stream, TRUE );
sMemDisconnect( &stream );
return( length );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -