📄 asn1.c
字号:
if( data & mask )
value |= flag;
flag <<= 1;
data <<= 1;
}
if( bitString != NULL )
*bitString = value;
return( sGetStatus( stream ) );
}
/* Read a UTCTime and GeneralizedTime value */
static int getDigits( const BYTE *bufPtr )
{
int result, ch = *bufPtr++;
if( !isdigit( ch ) )
return( -1 );
result = ( ch - '0' ) * 10;
ch = *bufPtr++;
if( !isdigit( ch ) )
return( -1 );
return( result + ( ch - '0' ) );
}
static int readTime( STREAM *stream, time_t *timePtr, const BOOLEAN isUTCTime )
{
BYTE buffer[ 32 ], *bufPtr = buffer;
struct tm theTime, *gm_tm;
time_t utcTime, gmTime, localTime;
int value = 0, length, status;
/* Read the length field and make sure it's of the correct size. There's
only one encoding allowed although in theory the encoded value could
range in length from 11 to 17 bytes for UTCTime and 13 to 19 bytes for
GeneralizedTime. In practice we also have to allow 11-byte UTCTime's
since an obsolete encoding rule allowed the time to be encoded without
seconds, and Sweden Post haven't realised that this has changed yet */
length = sgetc( stream );
if( ( isUTCTime && length != 13 && length != 11 ) || \
( !isUTCTime && length != 15 ) )
{
sSetError( stream, CRYPT_ERROR_BADDATA );
return( sGetStatus( stream ) );
}
/* Read the encoded time data */
memset( buffer, 0, 32 );
status = sread( stream, buffer, length );
if( cryptStatusError( status ) )
return( status );
/* Decode the time fields. Ideally we should use sscanf(), but there
are too many dodgy versions of this around */
memset( &theTime, 0, sizeof( struct tm ) );
theTime.tm_isdst = -1; /* Get the system to adjust for DST */
if( !isUTCTime )
{
value = ( getDigits( bufPtr ) - 19 ) * 100; /* Read the century */
bufPtr += 2;
length -= 2;
}
theTime.tm_year = getDigits( bufPtr ) + value;
theTime.tm_mon = getDigits( bufPtr + 2 ) - 1;
theTime.tm_mday = getDigits( bufPtr + 4 );
theTime.tm_hour = getDigits( bufPtr + 6 );
theTime.tm_min = getDigits( bufPtr + 8 );
/* Read the seconds field if there's one present */
if( length == 13 )
{
theTime.tm_sec = getDigits( bufPtr + 10 );
if( bufPtr[ 12 ] != 'Z' )
status = CRYPT_ERROR_BADDATA;
}
else
if( length != 11 || bufPtr[ 10 ] != 'Z' )
status = CRYPT_ERROR_BADDATA;
/* Make sure there were no format errors */
if( cryptStatusOK( status ) && \
( theTime.tm_year | theTime.tm_mon | theTime.tm_mday | \
theTime.tm_hour | theTime.tm_min | theTime.tm_sec ) < 0 )
status = CRYPT_ERROR_BADDATA;
if( cryptStatusError( status ) )
{
sSetError( stream, status );
return( status );
}
/* Finally, convert it to the local time. Since the UTCTime format
doesn't take centuries into account (and you'd think that when the ISO
came up with the world's least efficient time encoding format they
could have spared another two bytes to fully specify the year), we
have to adjust by one century for years < 50 (and hope there aren't
any Y2K bugs in mktime()) if the format is UTCTime. Note that there
are some implementations which currently roll over a century from 1970
(the Unix/Posix epoch and sort-of ISO/ANSI C epoch although they never
come out and say it), but hopefully these will be fixed by 2050.
"The time is out of joint; o cursed spite,
That ever I was born to set it right" - Shakespeare, "Hamlet" */
if( isUTCTime && theTime.tm_year < 50 )
theTime.tm_year += 100;
utcTime = mktime( &theTime );
if( utcTime == -1 )
{
sSetError( stream, CRYPT_ERROR_BADDATA );
return( CRYPT_ERROR_BADDATA );
}
/* Convert the UTC time to local time. This is complicated by the fact
that although the C standard library can convert from local time ->
UTC, it can't convert the time back, so we calculate the local offset
from UTC and adjust the time as appropriate. Since we can't assume
that time_t is signed, we have to treat a negative and positive offset
separately. An extra complication is added by daylight savings time
adjustment, some systems adjust for DST by default, some don't, and
some allow you to set it in the Control Panel so it varies from
machine to machine (thanks Bill!), so we have to make it explicit as
part of the conversion process. Even this still isn't perfect
because it displays the time adjusted for DST now rather than DST
when the cert was created, however this problem is more or less
undecidable, the code used here has the property that the values for
Windows agree with those for Unix and everything else which is the
main thing */
localTime = time( NULL );
gm_tm = gmtime( &localTime );
gm_tm->tm_isdst = -1; /* Force correct DST adjustment */
gmTime = mktime( gm_tm );
if( timePtr != NULL )
if( localTime < gmTime )
*timePtr = utcTime - ( gmTime - localTime );
else
*timePtr = utcTime + ( localTime - gmTime );
return( CRYPT_OK );
}
int readUTCTimeTag( STREAM *stream, time_t *timeVal, const int tag )
{
/* Clear return value */
if( timeVal != NULL )
*timeVal = 0;
/* Read the identifier field if necessary */
if( tag != NO_TAG )
{
if( readTag( stream ) != selectTag( tag, BER_TIME_UTC ) )
{
sSetError( stream, CRYPT_ERROR_BADDATA );
return( CRYPT_ERROR_BADDATA );
}
}
/* Read the time fields */
return( readTime( stream, timeVal, TRUE ) );
}
int readGeneralizedTimeTag( STREAM *stream, time_t *timeVal, const int tag )
{
/* Clear return value */
if( timeVal != NULL )
*timeVal = 0;
/* Read the identifier field if necessary */
if( tag != NO_TAG )
{
if( readTag( stream ) != selectTag( tag, BER_TIME_GENERALIZED ) )
{
sSetError( stream, CRYPT_ERROR_BADDATA );
return( CRYPT_ERROR_BADDATA );
}
}
/* Read the time fields */
return( readTime( stream, timeVal, FALSE ) );
}
/****************************************************************************
* *
* Utility Routines for Constructed Objects *
* *
****************************************************************************/
/* Read the start of an encapsulating SEQUENCE or SET or BIT STRING/OCTET
STRING hole */
static int readObjectHeader( STREAM *stream, int *length, const int tag,
const BOOLEAN isBitString )
{
int tagValue, dataLength;
if( length != NULL )
*length = 0; /* Clear return value */
tagValue = readTag( stream );
if( cryptStatusError( tagValue ) )
return( tagValue );
if( tag != ANY_TAG && tagValue != tag )
{
sSetError( stream, CRYPT_ERROR_BADDATA );
return( sGetStatus( stream ) );
}
dataLength = readShortLength( stream );
if( cryptStatusError( dataLength ) )
return( dataLength );
if( length != NULL )
*length = dataLength - ( isBitString ? 1 : 0 );
if( isBitString )
sgetc( stream );
return( sGetStatus( stream ) );
}
int readSequence( STREAM *stream, int *length )
{
return( readObjectHeader( stream, length, BER_SEQUENCE, FALSE ) );
}
int readSet( STREAM *stream, int *length )
{
return( readObjectHeader( stream, length, BER_SET, FALSE ) );
}
int readConstructed( STREAM *stream, int *length, const int tag )
{
return( readObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
BER_SEQUENCE : MAKE_CTAG( tag ), FALSE ) );
}
int readOctetStringHole( STREAM *stream, int *length, const int tag )
{
return( readObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
BER_OCTETSTRING : MAKE_CTAG_PRIMITIVE( tag ),
FALSE ) );
}
int readBitStringHole( STREAM *stream, int *length, const int tag )
{
return( readObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
BER_BITSTRING : MAKE_CTAG_PRIMITIVE( tag ),
TRUE ) );
}
int readGenericHole( STREAM *stream, int *length )
{
return( readObjectHeader( stream, length, ANY_TAG, FALSE ) );
}
/* Write the start of an encapsulating SEQUENCE, SET, or generic tagged
constructed object. The default type for the generic object is assumed
to be a SEQUENCE */
int writeSequence( STREAM *stream, const int length )
{
writeTag( stream, BER_SEQUENCE );
writeLength( stream, length );
return( sGetStatus( stream ) );
}
int writeSet( STREAM *stream, const int length )
{
writeTag( stream, BER_SET );
writeLength( stream, length );
return( sGetStatus( stream ) );
}
int writeConstructed( STREAM *stream, const int length, const int tag )
{
writeTag( stream, ( tag == DEFAULT_TAG ) ? \
BER_SEQUENCE : MAKE_CTAG( tag ) );
writeLength( stream, length );
return( sGetStatus( stream ) );
}
/****************************************************************************
* *
* 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_NONE );
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_NONE );
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;
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 = readTag( stream ), innerLength;
/* 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} } */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -