📄 asn1_rd.c
字号:
const BOOLEAN isUTCTime )
{
BYTE buffer[ 16 + 8 ], *bufPtr = buffer;
struct tm theTime, gmTimeInfo, *gmTimeInfoPtr = &gmTimeInfo;
time_t utcTime, gmTime;
int value = 0, length, i, iterationCount, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( timePtr, sizeof( time_t ) ) );
/* Clear return value */
*timePtr = 0;
/* Read the length field and make sure that 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. We formerly also allowed 11-byte
UTCTimes because an obsolete encoding rule allowed the time to be
encoded without seconds and Sweden Post hadn't realised that this had
changed yet, but these certs have now expired */
length = sgetc( stream );
if( cryptStatusError( length ) )
return( length );
if( ( isUTCTime && length != 13 ) || ( !isUTCTime && length != 15 ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
/* Read the encoded time data and make sure that the contents are
valid */
memset( buffer, 0, 16 );
status = sread( stream, buffer, length );
if( cryptStatusError( status ) )
return( status );
for( i = 0, iterationCount = 0;
i < length - 1 && iterationCount < FAILSAFE_ITERATIONS_MED;
i++, iterationCount++ )
{
if( !isDigit( buffer[ i ] ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
}
ENSURES_S( iterationCount < FAILSAFE_ITERATIONS_MED );
if( buffer[ length - 1 ] != 'Z' )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
/* Decode the time fields */
memset( &theTime, 0, sizeof( struct tm ) );
theTime.tm_isdst = -1; /* Get the system to adjust for DST */
if( !isUTCTime )
{
status = strGetNumeric( bufPtr, 2, &value, 19, 20 );
if( cryptStatusError( status ) )
return( status );
value = ( value - 19 ) * 100; /* Read the century */
bufPtr += 2;
length -= 2;
}
status = strGetNumeric( bufPtr, 2, &theTime.tm_year, 0, 99 );
if( cryptStatusOK( status ) )
{
theTime.tm_year += value;
status = strGetNumeric( bufPtr + 2, 2, &theTime.tm_mon, 1, 12 );
}
if( cryptStatusOK( status ) )
{
theTime.tm_mon--; /* Months are zero-based */
status = strGetNumeric( bufPtr + 4, 2, &theTime.tm_mday, 1, 31 );
}
if( cryptStatusOK( status ) )
status = strGetNumeric( bufPtr + 6, 2, &theTime.tm_hour, 0, 23 );
if( cryptStatusOK( status ) )
status = strGetNumeric( bufPtr + 8, 2, &theTime.tm_min, 0, 59 );
if( cryptStatusOK( status ) )
status = strGetNumeric( bufPtr + 10, 2, &theTime.tm_sec, 0, 59 );
if( cryptStatusError( 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 if the format is
UTCTime. Note that there are some implementations that 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.
In theory we could also check for an at least vaguely sane input
value range on the grounds that (a) some systems' mktime()s may be
broken and (b) some mktime()s may allow (and return) outrageous date
values that others don't. However it's probably better to simply be
consistent with what the system does rather than try and second-guess
the intent of the mktime() authors.
"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 < 0 )
{
/* Some Java-based apps with 64-bit times use ridiculous validity
dates (yes, we're going to be keeping the same key in active use
for *forty years*) that postdate the time_t range when time_t is
a signed 32-bit value. If we can't convert the time, we check
for a year after the time_t overflow (2038) and try again. In
theory we should just reject objects with such broken dates, but
since we otherwise accept all sorts of rubbish we at least try
and accept these as well */
if( theTime.tm_year >= 138 && theTime.tm_year < 180 )
{
theTime.tm_year = 136; /* 2036 */
utcTime = mktime( &theTime );
}
/* Some broken apps set dates to 1/1/1970, handling times this close
to the epoch is problematic because once any possible DST
adjustment is taken into account it's no longer possible to
represent the converted time as a time_t unless the system allows
it to be negative (Windows doesn't, many Unixen do, but having
cryptlib return a negative time value is probably a bad thing).
To handle this, if we find a date set anywhere during January 1970
we manually set the time to zero (the epoch) */
if( theTime.tm_year == 70 && theTime.tm_mon == 0 )
{
*timePtr = 0;
return( CRYPT_OK );
}
}
if( utcTime < MIN_STORED_TIME_VALUE )
return( sSetError( stream, 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 treat the UTC time as
local time (gmtime_s() always assumes that the input is local time)
and covert to GMT and back, which should give the offset from GMT.
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
other systems */
gmTimeInfoPtr = gmTime_s( &utcTime, gmTimeInfoPtr );
if( gmTimeInfoPtr == NULL )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
gmTimeInfoPtr->tm_isdst = -1; /* Force correct DST adjustment */
gmTime = mktime( gmTimeInfoPtr );
if( gmTime < MIN_STORED_TIME_VALUE )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
if( utcTime < gmTime )
*timePtr = utcTime - ( gmTime - utcTime );
else
*timePtr = utcTime + ( utcTime - gmTime );
/* This still isn't quite perfect since it can't handle the time at a
DST changeover. This is really a user problem ("Don't do that,
then") but if necessary can be corrected by converting back to GMT as
a sanity check and applying a +/- 1 hour correction if there's a
mismatch */
#if 0
gmTimeInfoPtr = gmTime_s( timePtr );
gmTimeInfoPtr->tm_isdst = -1;
gmTime = mktime( gmTimeInfoPtr );
if( gmTime != utcTime )
{
*timePtr += 3600; /* Try +1 first */
gmTimeInfoPtr = gmTime_s( timePtr, gmTimeInfoPtr );
gmTimeInfoPtr->tm_isdst = -1;
gmTime = mktime( gmTimeInfoPtr );
if( gmTime != utcTime )
*timePtr -= 7200; /* Nope, use -1 instead */
}
#endif /* 0 */
return( CRYPT_OK );
}
RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int readUTCTimeTag( STREAM *stream, OUT time_t *timeVal,
IN_TAG_EXT const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( timeVal, sizeof( time_t ) ) );
REQUIRES_S( tag == NO_TAG || tag == DEFAULT_TAG || \
( tag >= 0 && tag < MAX_TAG_VALUE ) );
/* Clear return value */
*timeVal = 0;
if( tag != NO_TAG && readTag( stream ) != selectTag( tag, BER_TIME_UTC ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
return( readTime( stream, timeVal, TRUE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int readGeneralizedTimeTag( INOUT STREAM *stream, OUT time_t *timeVal,
IN_TAG_EXT const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( timeVal, sizeof( time_t ) ) );
REQUIRES_S( tag == NO_TAG || tag == DEFAULT_TAG || \
( tag >= 0 && tag < MAX_TAG_VALUE ) );
/* Clear return value */
*timeVal = 0;
if( tag != NO_TAG && readTag( stream ) != selectTag( tag, BER_TIME_GENERALIZED ) )
return( sSetError( stream, CRYPT_ERROR_BADDATA ) );
return( readTime( stream, timeVal, FALSE ) );
}
/****************************************************************************
* *
* Read Routines for Constructed Objects *
* *
****************************************************************************/
/* Read an encapsulating SEQUENCE or SET or BIT STRING/OCTET STRING hole */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readSequence( INOUT STREAM *stream, OUT_OPT_LENGTH_Z int *length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
return( readObjectHeader( stream, length, 0, BER_SEQUENCE, FALSE, FALSE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readSequenceI( INOUT STREAM *stream, OUT_OPT_LENGTH_INDEF int *length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
return( readObjectHeader( stream, length, 0, BER_SEQUENCE, FALSE, TRUE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readSet( INOUT STREAM *stream, OUT_OPT_LENGTH_Z int *length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
return( readObjectHeader( stream, length, 0, BER_SET, FALSE, FALSE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readSetI( INOUT STREAM *stream, OUT_OPT_LENGTH_INDEF int *length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
return( readObjectHeader( stream, length, 0, BER_SET, FALSE, TRUE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readConstructed( INOUT STREAM *stream, OUT_OPT_LENGTH_Z int *length,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
REQUIRES_S( ( tag == DEFAULT_TAG ) || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
return( readObjectHeader( stream, length, 0, ( tag == DEFAULT_TAG ) ? \
BER_SEQUENCE : MAKE_CTAG( tag ), FALSE, FALSE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readConstructedI( INOUT STREAM *stream, OUT_OPT_LENGTH_INDEF int *length,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
REQUIRES_S( ( tag == DEFAULT_TAG ) || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
return( readObjectHeader( stream, length, 0, ( tag == DEFAULT_TAG ) ? \
BER_SEQUENCE : MAKE_CTAG( tag ), FALSE, TRUE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readOctetStringHole( INOUT STREAM *stream, OUT_OPT_LENGTH_Z int *length,
IN_LENGTH_SHORT const int minLength,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
REQUIRES_S( ( tag == DEFAULT_TAG ) || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
return( readObjectHeader( stream, length, minLength,
( tag == DEFAULT_TAG ) ? \
BER_OCTETSTRING : MAKE_CTAG_PRIMITIVE( tag ),
FALSE, FALSE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readBitStringHole( INOUT STREAM *stream, OUT_OPT_LENGTH_Z int *length,
IN_LENGTH_SHORT const int minLength,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
REQUIRES_S( ( tag == DEFAULT_TAG ) || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
return( readObjectHeader( stream, length, minLength,
( tag == DEFAULT_TAG ) ? \
BER_BITSTRING : MAKE_CTAG_PRIMITIVE( tag ),
TRUE, FALSE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readGenericHole( INOUT STREAM *stream, OUT_OPT_LENGTH_Z int *length,
IN_LENGTH_SHORT const int minLength,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
ENSURES_S( ( tag == DEFAULT_TAG ) || ( tag > 0 && tag < MAX_TAG ) );
/* In theory we should use MAX_TAG_VALUE but this function is
frequently used as part of the sequence 'read tag;
save tag; readGenericXYZ( tag );' so we have to allow all
tag values */
return( readObjectHeader( stream, length, minLength,
( tag == DEFAULT_TAG ) ? ANY_TAG : tag,
FALSE, FALSE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readGenericHoleI( INOUT STREAM *stream,
OUT_OPT_LENGTH_INDEF int *length,
IN_LENGTH_SHORT const int minLength,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( int ) ) );
ENSURES_S( ( tag == DEFAULT_TAG ) || ( tag > 0 && tag < MAX_TAG ) );
/* See comment for readGenericHole() */
return( readObjectHeader( stream, length, minLength,
( tag == DEFAULT_TAG ) ? ANY_TAG : tag,
FALSE, TRUE ) );
}
/* Read an abnormally-long encapsulating SEQUENCE or OCTET STRING hole.
This is used in place of the usual read in situations where potentially
huge data quantities would fail the sanity check enforced by the
standard read. This form always allows indefinite lengths, which are
likely for large objects */
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readLongSequence( INOUT STREAM *stream,
OUT_OPT_LENGTH_INDEF long *length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( long ) ) );
return( readLongObjectHeader( stream, length, BER_SEQUENCE ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readLongSet( INOUT STREAM *stream, OUT_OPT_LENGTH_INDEF long *length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( long ) ) );
return( readLongObjectHeader( stream, length, BER_SET ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readLongConstructed( INOUT STREAM *stream,
OUT_OPT_LENGTH_INDEF long *length,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( long ) ) );
REQUIRES_S( ( tag == DEFAULT_TAG ) || ( tag >= 0 && tag < MAX_TAG_VALUE ) );
return( readLongObjectHeader( stream, length, ( tag == DEFAULT_TAG ) ? \
BER_SEQUENCE : MAKE_CTAG( tag ) ) );
}
RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int readLongGenericHole( INOUT STREAM *stream,
OUT_OPT_LENGTH_INDEF long *length,
IN_TAG const int tag )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( length == NULL || isWritePtr( length, sizeof( long ) ) );
ENSURES_S( ( tag == DEFAULT_TAG ) || ( tag > 0 && tag < MAX_TAG ) );
/* See comment for readGenericHole() */
return( readLongObjectHeader( stream, length,
( tag == DEFAULT_TAG ) ? ANY_TAG : tag ) );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -