Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[crypto] Generalise x509_parse_time() to asn1_generalized_time()
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed May 13, 2012
1 parent e5858c1 commit 88c09b3
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 116 deletions.
113 changes: 113 additions & 0 deletions src/crypto/asn1.c
Expand Up @@ -21,7 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <ipxe/tables.h>
#include <ipxe/asn1.h>

Expand Down Expand Up @@ -52,6 +54,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
__einfo_error ( EINFO_EINVAL_ASN1_INTEGER )
#define EINFO_EINVAL_ASN1_INTEGER \
__einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid integer" )
#define EINVAL_ASN1_TIME \
__einfo_error ( EINFO_EINVAL_ASN1_TIME )
#define EINFO_EINVAL_ASN1_TIME \
__einfo_uniqify ( EINFO_EINVAL, 0x05, "Invalid time" )

/**
* Invalidate ASN.1 object cursor
Expand Down Expand Up @@ -400,3 +406,110 @@ struct asn1_algorithm * asn1_algorithm ( const struct asn1_cursor *cursor ) {

return algorithm;
}

/**
* Parse ASN.1 GeneralizedTime
*
* @v cursor ASN.1 cursor
* @v time Time to fill in
* @ret rc Return status code
*
* RFC 5280 section 4.1.2.5 places several restrictions on the allowed
* formats for UTCTime and GeneralizedTime, and mandates the
* interpretation of centuryless year values.
*/
int asn1_generalized_time ( const struct asn1_cursor *cursor, time_t *time ) {
struct asn1_cursor contents;
unsigned int have_century;
unsigned int type;
union {
struct {
uint8_t century;
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
} __attribute__ (( packed )) named;
uint8_t raw[7];
} pairs;
struct tm tm;
const uint8_t *data;
size_t remaining;
unsigned int tens;
unsigned int units;
unsigned int i;
int rc;

/* Determine time format utcTime/generalizedTime */
memcpy ( &contents, cursor, sizeof ( contents ) );
type = asn1_type ( &contents );
switch ( type ) {
case ASN1_UTC_TIME:
have_century = 0;
break;
case ASN1_GENERALIZED_TIME:
have_century = 1;
break;
default:
DBGC ( cursor, "ASN1 %p invalid time type %02x\n",
cursor, type );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}

/* Enter utcTime/generalizedTime */
if ( ( rc = asn1_enter ( &contents, type ) ) != 0 ) {
DBGC ( cursor, "ASN1 %p cannot locate %s time:\n", cursor,
( ( type == ASN1_UTC_TIME ) ? "UTC" : "generalized" ) );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return rc;
}

/* Parse digit string a pair at a time */
data = contents.data;
remaining = contents.len;
for ( i = ( have_century ? 0 : 1 ) ; i < sizeof ( pairs.raw ) ; i++ ) {
if ( remaining < 2 ) {
DBGC ( cursor, "ASN1 %p invalid time:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}
tens = data[0];
units = data[1];
if ( ! ( isdigit ( tens ) && isdigit ( units ) ) ) {
DBGC ( cursor, "ASN1 %p invalid time:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}
pairs.raw[i] = ( ( 10 * ( tens - '0' ) ) + ( units - '0' ) );
data += 2;
remaining -= 2;
}

/* Determine century if applicable */
if ( ! have_century )
pairs.named.century = ( ( pairs.named.year >= 50 ) ? 19 : 20 );

/* Check for trailing "Z" */
if ( ( remaining != 1 ) || ( data[0] != 'Z' ) ) {
DBGC ( cursor, "ASN1 %p invalid time:\n", cursor );
DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
return -EINVAL_ASN1_TIME;
}

/* Fill in time */
tm.tm_year = ( ( ( pairs.named.century - 19 ) * 100 ) +
pairs.named.year );
tm.tm_mon = ( pairs.named.month - 1 );
tm.tm_mday = pairs.named.day;
tm.tm_hour = pairs.named.hour;
tm.tm_min = pairs.named.minute;
tm.tm_sec = pairs.named.second;

/* Convert to seconds since the Epoch */
*time = mktime ( &tm );

return 0;
}
126 changes: 10 additions & 116 deletions src/crypto/x509.c
Expand Up @@ -20,8 +20,6 @@ FILE_LICENCE ( GPL2_OR_LATER );

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/list.h>
Expand Down Expand Up @@ -60,10 +58,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
__einfo_error ( EINFO_EINVAL_BIT_STRING )
#define EINFO_EINVAL_BIT_STRING \
__einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid bit string" )
#define EINVAL_TIME \
__einfo_error ( EINFO_EINVAL_TIME )
#define EINFO_EINVAL_TIME \
__einfo_uniqify ( EINFO_EINVAL, 0x03, "Invalid time" )
#define EINVAL_ALGORITHM_MISMATCH \
__einfo_error ( EINFO_EINVAL_ALGORITHM_MISMATCH )
#define EINFO_EINVAL_ALGORITHM_MISMATCH \
Expand Down Expand Up @@ -301,114 +295,6 @@ static int x509_parse_integral_bit_string ( struct x509_certificate *cert,
return 0;
}

/**
* Parse X.509 certificate time
*
* @v cert X.509 certificate
* @v time Time to fill in
* @v raw ASN.1 cursor
* @ret rc Return status code
*
* RFC 5280 section 4.1.2.5 places several restrictions on the allowed
* formats for UTCTime and GeneralizedTime, and mandates the
* interpretation of centuryless year values.
*/
static int x509_parse_time ( struct x509_certificate *cert,
struct x509_time *time,
const struct asn1_cursor *raw ) {
struct asn1_cursor cursor;
unsigned int have_century;
unsigned int type;
union {
struct {
uint8_t century;
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
} __attribute__ (( packed )) named;
uint8_t raw[7];
} pairs;
struct tm tm;
const uint8_t *data;
size_t remaining;
unsigned int tens;
unsigned int units;
unsigned int i;
int rc;

/* Determine time format utcTime/generalizedTime */
memcpy ( &cursor, raw, sizeof ( cursor ) );
type = asn1_type ( &cursor );
switch ( type ) {
case ASN1_UTC_TIME:
have_century = 0;
break;
case ASN1_GENERALIZED_TIME:
have_century = 1;
break;
default:
DBGC ( cert, "X509 %p invalid time type %02x\n", cert, type );
DBGC_HDA ( cert, 0, raw->data, raw->len );
return -EINVAL_TIME;
}

/* Enter utcTime/generalizedTime */
if ( ( rc = asn1_enter ( &cursor, type ) ) != 0 ) {
DBGC ( cert, "X509 %p cannot locate %s time:\n", cert,
( ( type == ASN1_UTC_TIME ) ? "UTC" : "generalized" ) );
DBGC_HDA ( cert, 0, raw->data, raw->len );
return rc;
}

/* Parse digit string a pair at a time */
data = cursor.data;
remaining = cursor.len;
for ( i = ( have_century ? 0 : 1 ) ; i < sizeof ( pairs.raw ) ; i++ ) {
if ( remaining < 2 ) {
DBGC ( cert, "X509 %p invalid time:\n", cert );
DBGC_HDA ( cert, 0, raw->data, raw->len );
return -EINVAL_TIME;
}
tens = data[0];
units = data[1];
if ( ! ( isdigit ( tens ) && isdigit ( units ) ) ) {
DBGC ( cert, "X509 %p invalid time:\n", cert );
DBGC_HDA ( cert, 0, raw->data, raw->len );
return -EINVAL_TIME;
}
pairs.raw[i] = ( ( 10 * ( tens - '0' ) ) + ( units - '0' ) );
data += 2;
remaining -= 2;
}

/* Determine century if applicable */
if ( ! have_century )
pairs.named.century = ( ( pairs.named.year >= 50 ) ? 19 : 20 );

/* Check for trailing "Z" */
if ( ( remaining != 1 ) || ( data[0] != 'Z' ) ) {
DBGC ( cert, "X509 %p invalid time:\n", cert );
DBGC_HDA ( cert, 0, raw->data, raw->len );
return -EINVAL_TIME;
}

/* Fill in time */
tm.tm_year = ( ( ( pairs.named.century - 19 ) * 100 ) +
pairs.named.year );
tm.tm_mon = ( pairs.named.month - 1 );
tm.tm_mday = pairs.named.day;
tm.tm_hour = pairs.named.hour;
tm.tm_min = pairs.named.minute;
tm.tm_sec = pairs.named.second;

/* Convert to seconds since the Epoch */
time->time = mktime ( &tm );

return 0;
}

/**
* Parse X.509 certificate version
Expand Down Expand Up @@ -520,15 +406,23 @@ static int x509_parse_validity ( struct x509_certificate *cert,
asn1_enter ( &cursor, ASN1_SEQUENCE );

/* Parse notBefore */
if ( ( rc = x509_parse_time ( cert, not_before, &cursor ) ) != 0 )
if ( ( rc = asn1_generalized_time ( &cursor,
&not_before->time ) ) != 0 ) {
DBGC ( cert, "X509 %p cannot parse notBefore: %s\n",
cert, strerror ( rc ) );
return rc;
}
DBGC2 ( cert, "X509 %p valid from time %lld\n",
cert, not_before->time );
asn1_skip_any ( &cursor );

/* Parse notAfter */
if ( ( rc = x509_parse_time ( cert, not_after, &cursor ) ) != 0 )
if ( ( rc = asn1_generalized_time ( &cursor,
&not_after->time ) ) != 0 ) {
DBGC ( cert, "X509 %p cannot parse notAfter: %s\n",
cert, strerror ( rc ) );
return rc;
}
DBGC2 ( cert, "X509 %p valid until time %lld\n",
cert, not_after->time );

Expand Down
3 changes: 3 additions & 0 deletions src/include/ipxe/asn1.h
Expand Up @@ -10,6 +10,7 @@
FILE_LICENCE ( GPL2_OR_LATER );

#include <stdint.h>
#include <time.h>
#include <ipxe/tables.h>

/** An ASN.1 object cursor */
Expand Down Expand Up @@ -239,5 +240,7 @@ extern int asn1_compare ( const struct asn1_cursor *cursor1,
const struct asn1_cursor *cursor2 );
extern struct asn1_algorithm *
asn1_algorithm ( const struct asn1_cursor *cursor );
extern int asn1_generalized_time ( const struct asn1_cursor *cursor,
time_t *time );

#endif /* _IPXE_ASN1_H */

0 comments on commit 88c09b3

Please sign in to comment.