📄 asn1_rd.c
字号:
REQUIRES_S( bufferMaxLength > 0 && \
bufferMaxLength < MAX_INTLENGTH_SHORT );
REQUIRES_S( length > 0 && length < MAX_INTLENGTH_SHORT );
/* Clear return value */
*bufferLength = dataLength;
/* If we don't care about the return value, skip it and exit */
if( buffer == NULL )
return( sSkip( stream, dataLength ) );
/* Read the object, limiting the size to the maximum buffer size */
if( dataLength > bufferMaxLength )
{
remainder = dataLength - bufferMaxLength;
dataLength = bufferMaxLength;
*bufferLength = dataLength;
}
status = sread( stream, buffer, dataLength );
if( cryptStatusError( status ) )
return( status );
/* If there's no data remaining, we're done */
if( remainder <= 0 )
return( CRYPT_OK );
/* Skip any remaining data */
return( sSkip( stream, remainder ) );
}
/****************************************************************************
* *
* Read Routines for Primitive Objects *
* *
****************************************************************************/
/* Read a tag and make sure that it's (approximately) valid */
CHECK_RETVAL_BOOL \
static BOOLEAN checkTag( IN_TAG_ENCODED const int tag )
{
/* Make sure that it's (approximately) valid: Not an EOC, and within the
allowed range */
if( tag <= 0 || tag > MAX_TAG )
return( FALSE );
/* Make sure that it's not an application-specific or private tag */
if( ( tag & BER_CLASS_MASK ) == BER_APPLICATION ||
( tag & BER_CLASS_MASK ) == BER_PRIVATE )
return( FALSE );
/* If its's a context-specific tag make sure that the tag value is
within the allowed range */
if( ( tag & BER_CLASS_MASK ) == BER_CONTEXT_SPECIFIC && \
( tag & BER_SHORT_ID_MASK ) > MAX_CTAG_VALUE )
return( FALSE );
return( TRUE );
}
RETVAL_RANGE( MAX_ERROR, 0xFF ) STDC_NONNULL_ARG( ( 1 ) ) \
int readTag( INOUT STREAM *stream )
{
int tag;
/* Read the tag */
tag = sgetc( stream );
if( cryptStatusError( tag ) )
return( tag );
if( !checkTag( tag ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
return( tag );
}
RETVAL_RANGE( MAX_ERROR, 0xFF ) STDC_NONNULL_ARG( ( 1 ) ) \
int peekTag( INOUT STREAM *stream )
{
int tag;
/* Peek at the tag value */
tag = sPeek( stream );
if( cryptStatusError( tag ) )
return( tag );
if( !checkTag( tag ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
return( tag );
}
/* Check for constructed data end-of-contents octets. Note that this
is a standard integer-return function that can return either a boolean
TRUE/FALSE or alternatively a stream error code if there's a problem, so
it's not a purely boolean function */
RETVAL_RANGE( MAX_ERROR, TRUE ) STDC_NONNULL_ARG( ( 1 ) ) \
int checkEOC( STREAM *stream )
{
BYTE eocBuffer[ 2 + 8 ];
int tag, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
/* Read the tag and check for an EOC octet pair. Note that we can't use
peekTag()/readTag() for this because an EOC isn't a valid tag */
tag = sPeek( stream );
if( cryptStatusError( tag ) )
return( tag );
if( tag != BER_EOC )
return( FALSE );
status = sread( stream, eocBuffer, 2 );
if( cryptStatusError( status ) )
return( status );
if( memcmp( eocBuffer, "\x00\x00", 2 ) )
{
/* After finding an EOC tag we need to have a length of zero */
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
}
return( TRUE );
}
/* Read a short (<= 256 bytes) raw object without decoding it. This is used
to read short data blocks like object identifiers, which are only ever
handled in encoded form */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
int readRawObject( INOUT STREAM *stream,
OUT_BUFFER( bufferMaxLength, bufferLength ) BYTE *buffer,
IN_LENGTH_SHORT_MIN( 3 ) const int bufferMaxLength,
OUT_LENGTH_SHORT_Z int *bufferLength,
IN_TAG_ENCODED const int tag )
{
int length, offset = 0;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( buffer, bufferMaxLength ) );
assert( isWritePtr( bufferLength, sizeof( int ) ) );
REQUIRES_S( bufferMaxLength >= 3 && \
bufferMaxLength < MAX_INTLENGTH_SHORT );
/* Need to be able to write at least the tag, length, and
one byte of content */
REQUIRES_S( tag == NO_TAG || \
( tag > 0 && tag <= MAX_TAG ) );
/* Note tag != 0 */
/* Clear return values */
memset( buffer, 0, min( 16, bufferMaxLength ) );
*bufferLength = 0;
/* Read the identifier field and length. We need to remember each byte
as it's read so we can't just call readLengthValue() for the length,
but since we only need to handle lengths that can be encoded in one
or two bytes this isn't a problem. Since this function reads a
complete encoded object, the tag (if known) must be specified, so
there's no capability to use DEFAULT_TAG */
if( tag != NO_TAG )
{
const int objectTag = readTag( stream );
if( cryptStatusError( objectTag ) )
return( objectTag );
if( tag != objectTag )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
buffer[ offset++ ] = objectTag;
}
length = sgetc( stream );
if( cryptStatusError( length ) )
return( length );
buffer[ offset++ ] = length;
if( length & 0x80 )
{
/* If the object is indefinite-length or longer than 256 bytes (i.e.
the length-of-length is anything other than 1), we don't want to
handle it */
if( length != 0x81 )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
length = sgetc( stream );
if( cryptStatusError( length ) )
return( length );
buffer[ offset++ ] = length;
}
if( length <= 0 )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
if( offset + length > bufferMaxLength )
{
/* We treat this as a stream error even though technically it's an
insufficient-buffer-space error because the data object has
violated the implicit format constraint of being larger than the
maximum size specified by the caller */
return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
}
/* Read in the rest of the data */
*bufferLength = offset + length;
return( sread( stream, buffer + offset, length ) );
}
/* Read a large integer value */
RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
int readIntegerTag( INOUT STREAM *stream,
OUT_BUFFER_OPT( integerMaxLength, \
*integerLength ) BYTE *integer,
IN_LENGTH_SHORT const int integerMaxLength,
OUT_LENGTH_SHORT_Z int *integerLength,
IN_TAG_EXT const int tag )
{
int length;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( integer == NULL || isWritePtr( integer, integerMaxLength ) );
assert( integerLength == NULL || \
isWritePtr( integerLength, sizeof( int ) ) );
REQUIRES_S( integerMaxLength > 0 && \
integerMaxLength < MAX_INTLENGTH_SHORT );
REQUIRES_S( tag == NO_TAG || tag == DEFAULT_TAG || \
( tag >= 0 && tag < MAX_TAG_VALUE ) );
/* Clear return values */
if( integer != NULL )
memset( integer, 0, min( 16, integerMaxLength ) );
if( integerLength != NULL )
*integerLength = 0;
/* Read the integer header info */
length = readIntegerHeader( stream, tag );
if( length <= 0 )
return( length ); /* Error or zero length */
/* Read in the numeric value, limiting the size to the maximum buffer
size. This is safe because the only situation where this can occur
is when we're reading some blob (whose value we don't care about)
dressed up as an integer rather than for any real integer */
return( readConstrainedData( stream, integer, integerMaxLength,
integerLength, length ) );
}
#ifdef USE_PKC
/* Read a bignum integer value */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readBignumInteger( INOUT STREAM *stream,
INOUT TYPECAST( BIGNUM * ) void *bignum,
IN_LENGTH_PKC const int minLength,
IN_LENGTH_PKC const int maxLength,
IN_OPT const void *maxRange,
IN_TAG_EXT const int tag,
const BOOLEAN checkShortKey )
{
BYTE buffer[ CRYPT_MAX_PKCSIZE + 8 ];
int length, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( bignum, sizeof( BIGNUM ) ) );
assert( maxRange == NULL || isReadPtr( maxRange, sizeof( BIGNUM ) ) );
REQUIRES_S( minLength > 0 && minLength <= maxLength && \
maxLength <= CRYPT_MAX_PKCSIZE );
REQUIRES_S( tag == NO_TAG || tag == DEFAULT_TAG || \
( tag >= 0 && tag < MAX_TAG_VALUE ) );
/* Read the integer header info */
length = readIntegerHeader( stream, tag );
if( cryptStatusError( length ) )
return( length );
if( length <= 0 )
{
/* It's a read of a zero value, make it explicit */
BN_zero( bignum );
return( CRYPT_OK );
}
/* Read the value into a fixed buffer */
if( length > CRYPT_MAX_PKCSIZE )
return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
status = sread( stream, buffer, length );
if( cryptStatusError( status ) )
return( status );
status = extractBignum( bignum, buffer, length, minLength, maxLength,
maxRange, checkShortKey );
if( cryptStatusError( status ) )
status = sSetError( stream, status );
zeroise( buffer, CRYPT_MAX_PKCSIZE );
return( status );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int readBignumTag( INOUT STREAM *stream,
INOUT TYPECAST( BIGNUM * ) void *bignum,
IN_LENGTH_PKC const int minLength,
IN_LENGTH_PKC const int maxLength,
IN_OPT const void *maxRange,
IN_TAG_EXT const int tag )
{
return( readBignumInteger( stream, bignum, minLength, maxLength,
maxRange, tag, FALSE ) );
}
/* Special-case bignum read routine that explicitly checks for a too-short
key and returns CRYPT_ERROR_NOSECURE rather than the CRYPT_ERROR_BADDATA
that'd otherwise be returned */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int readBignumChecked( INOUT STREAM *stream,
INOUT TYPECAST( BIGNUM * ) void *bignum,
IN_LENGTH_PKC const int minLength,
IN_LENGTH_PKC const int maxLength,
IN_OPT const void *maxRange )
{
return( readBignumInteger( stream, bignum, minLength, maxLength,
maxRange, DEFAULT_TAG, TRUE ) );
}
#endif /* USE_PKC */
/* Read a universal type and discard it (used to skip unknown or unwanted
types) */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readUniversalData( INOUT STREAM *stream )
{
long length;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
status = readLengthValue( stream, &length, READLENGTH_SHORT );
if( cryptStatusError( status ) )
return( status );
if( length <= 0 )
return( length ); /* Zero-length data */
return( sSkip( stream, length ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readUniversal( INOUT STREAM *stream )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
readTag( stream );
return( readUniversalData( stream ) );
}
/* Read a short integer value */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readShortIntegerTag( INOUT STREAM *stream,
OUT_OPT_INT_Z long *value,
IN_TAG_EXT const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( value == NULL || isWritePtr( value, sizeof( long ) ) );
REQUIRES_S( tag == NO_TAG || tag == DEFAULT_TAG || \
( tag >= 0 && tag < MAX_TAG_VALUE ) );
/* Clear return value */
if( value != NULL )
*value = 0L;
if( tag != NO_TAG && readTag( stream ) != selectTag( tag, BER_INTEGER ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
return( readNumeric( stream, value ) );
}
/* Read an enumerated value. This is encoded like an ASN.1 integer so we
just read it as such */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readEnumeratedTag( INOUT STREAM *stream,
OUT_OPT_INT_Z int *enumeration,
IN_TAG_EXT const int tag )
{
long value;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( enumeration == NULL || \
isWritePtr( enumeration, sizeof( int ) ) );
REQUIRES_S( tag == NO_TAG || tag == DEFAULT_TAG || \
( tag >= 0 && tag < MAX_TAG_VALUE ) );
/* Clear return value */
if( enumeration != NULL )
*enumeration = 0;
if( tag != NO_TAG && readTag( stream ) != selectTag( tag, BER_ENUMERATED ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
status = readNumeric( stream, &value );
if( cryptStatusOK( status ) )
{
if( value < 0 || value > 1000 )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
if( enumeration != NULL )
*enumeration = ( int ) value;
}
return( status );
}
/* Read a null value */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readNullTag( INOUT STREAM *stream, IN_TAG_EXT const int tag )
{
int value;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -