From 9b0136dcb9a33673475f992cbf3e6f77ea5e014e Mon Sep 17 00:00:00 2001 From: Andres Amaya Garcia Date: Fri, 10 Nov 2017 19:39:43 +0000 Subject: [PATCH] Finish OCSP response issuer finding function The function follows RFC 6960 and uses either the responder's name or the responder's hash of their key as available in the OCSP response to locate the correct issuer certificate. To avoid code duplication, some functionality from x509_crt.c module was moved to x509.c and made public in x509.h. --- include/mbedtls/x509.h | 3 ++ library/x509.c | 97 +++++++++++++++++++++++++++++++++++++ library/x509_crt.c | 107 ++--------------------------------------- library/x509_ocsp.c | 59 ++++++++++++++++++++++- 4 files changed, 163 insertions(+), 103 deletions(-) diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h index b927e8d108..88fcddceae 100644 --- a/include/mbedtls/x509.h +++ b/include/mbedtls/x509.h @@ -287,6 +287,9 @@ int mbedtls_x509_self_test( int verbose ); * Internal module functions. You probably do not want to use these unless you * know you do. */ +int mbedtls_x509_serial_cmp( const mbedtls_x509_buf *a, const mbedtls_x509_buf *b ); +int mbedtls_x509_memcasecmp( const void *s1, const void *s2, size_t len ); +int mbedtls_x509_name_cmp( const mbedtls_x509_name *a, const mbedtls_x509_name *b ); int mbedtls_x509_get_name( unsigned char **p, const unsigned char *end, mbedtls_x509_name *cur ); int mbedtls_x509_get_alg_null( unsigned char **p, const unsigned char *end, diff --git a/library/x509.c b/library/x509.c index 371d6da1dc..1b3b684098 100644 --- a/library/x509.c +++ b/library/x509.c @@ -898,6 +898,103 @@ int mbedtls_x509_key_size_helper( char *buf, size_t buf_size, const char *name ) return( 0 ); } +/* + * Like memcmp, but case-insensitive and always returns -1 if different + */ +int mbedtls_x509_memcasecmp( const void *s1, const void *s2, size_t len ) +{ + size_t i; + unsigned char diff; + const unsigned char *n1 = s1, *n2 = s2; + + for( i = 0; i < len; i++ ) + { + diff = n1[i] ^ n2[i]; + + if( diff == 0 ) + continue; + + if( diff == 32 && + ( ( n1[i] >= 'a' && n1[i] <= 'z' ) || + ( n1[i] >= 'A' && n1[i] <= 'Z' ) ) ) + { + continue; + } + + return( -1 ); + } + + return( 0 ); +} + +/* + * Compare two X.509 strings, case-insensitive, and allowing for some encoding + * variations (but not all). + * + * Return 0 if equal, -1 otherwise. + */ +static int x509_string_cmp( const mbedtls_x509_buf *a, const mbedtls_x509_buf *b ) +{ + if( a->tag == b->tag && + a->len == b->len && + memcmp( a->p, b->p, b->len ) == 0 ) + { + return( 0 ); + } + + if( ( a->tag == MBEDTLS_ASN1_UTF8_STRING || a->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) && + ( b->tag == MBEDTLS_ASN1_UTF8_STRING || b->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) && + a->len == b->len && + mbedtls_x509_memcasecmp( a->p, b->p, b->len ) == 0 ) + { + return( 0 ); + } + + return( -1 ); +} + +/* + * Compare two X.509 Names (aka rdnSequence). + * + * See RFC 5280 section 7.1, though we don't implement the whole algorithm: + * we sometimes return unequal when the full algorithm would return equal, + * but never the other way. (In particular, we don't do Unicode normalisation + * or space folding.) + * + * Return 0 if equal, -1 otherwise. + */ +int mbedtls_x509_name_cmp( const mbedtls_x509_name *a, const mbedtls_x509_name *b ) +{ + /* Avoid recursion, it might not be optimised by the compiler */ + while( a != NULL || b != NULL ) + { + if( a == NULL || b == NULL ) + return( -1 ); + + /* type */ + if( a->oid.tag != b->oid.tag || + a->oid.len != b->oid.len || + memcmp( a->oid.p, b->oid.p, b->oid.len ) != 0 ) + { + return( -1 ); + } + + /* value */ + if( x509_string_cmp( &a->val, &b->val ) != 0 ) + return( -1 ); + + /* structure of the list of sets */ + if( a->next_merged != b->next_merged ) + return( -1 ); + + a = a->next; + b = b->next; + } + + /* a == NULL == b */ + return( 0 ); +} + #if defined(MBEDTLS_HAVE_TIME_DATE) /* * Set the time structure to the current time. diff --git a/library/x509_crt.c b/library/x509_crt.c index 056e46308b..67cf44574e 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -1943,35 +1943,6 @@ static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, } #endif /* MBEDTLS_X509_CRL_PARSE_C */ -/* - * Like memcmp, but case-insensitive and always returns -1 if different - */ -static int x509_memcasecmp( const void *s1, const void *s2, size_t len ) -{ - size_t i; - unsigned char diff; - const unsigned char *n1 = s1, *n2 = s2; - - for( i = 0; i < len; i++ ) - { - diff = n1[i] ^ n2[i]; - - if( diff == 0 ) - continue; - - if( diff == 32 && - ( ( n1[i] >= 'a' && n1[i] <= 'z' ) || - ( n1[i] >= 'A' && n1[i] <= 'Z' ) ) ) - { - continue; - } - - return( -1 ); - } - - return( 0 ); -} - /* * Return 0 if name matches wildcard, -1 otherwise */ @@ -1996,7 +1967,7 @@ static int x509_check_wildcard( const char *cn, mbedtls_x509_buf *name ) return( -1 ); if( cn_len - cn_idx == name->len - 1 && - x509_memcasecmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 ) + mbedtls_x509_memcasecmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 ) { return( 0 ); } @@ -2004,74 +1975,6 @@ static int x509_check_wildcard( const char *cn, mbedtls_x509_buf *name ) return( -1 ); } -/* - * Compare two X.509 strings, case-insensitive, and allowing for some encoding - * variations (but not all). - * - * Return 0 if equal, -1 otherwise. - */ -static int x509_string_cmp( const mbedtls_x509_buf *a, const mbedtls_x509_buf *b ) -{ - if( a->tag == b->tag && - a->len == b->len && - memcmp( a->p, b->p, b->len ) == 0 ) - { - return( 0 ); - } - - if( ( a->tag == MBEDTLS_ASN1_UTF8_STRING || a->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) && - ( b->tag == MBEDTLS_ASN1_UTF8_STRING || b->tag == MBEDTLS_ASN1_PRINTABLE_STRING ) && - a->len == b->len && - x509_memcasecmp( a->p, b->p, b->len ) == 0 ) - { - return( 0 ); - } - - return( -1 ); -} - -/* - * Compare two X.509 Names (aka rdnSequence). - * - * See RFC 5280 section 7.1, though we don't implement the whole algorithm: - * we sometimes return unequal when the full algorithm would return equal, - * but never the other way. (In particular, we don't do Unicode normalisation - * or space folding.) - * - * Return 0 if equal, -1 otherwise. - */ -static int x509_name_cmp( const mbedtls_x509_name *a, const mbedtls_x509_name *b ) -{ - /* Avoid recursion, it might not be optimised by the compiler */ - while( a != NULL || b != NULL ) - { - if( a == NULL || b == NULL ) - return( -1 ); - - /* type */ - if( a->oid.tag != b->oid.tag || - a->oid.len != b->oid.len || - memcmp( a->oid.p, b->oid.p, b->oid.len ) != 0 ) - { - return( -1 ); - } - - /* value */ - if( x509_string_cmp( &a->val, &b->val ) != 0 ) - return( -1 ); - - /* structure of the list of sets */ - if( a->next_merged != b->next_merged ) - return( -1 ); - - a = a->next; - b = b->next; - } - - /* a == NULL == b */ - return( 0 ); -} - /* * Check if 'parent' is a suitable parent (signing CA) for 'child'. * Return 0 if yes, -1 if not. @@ -2086,7 +1989,7 @@ static int x509_crt_check_parent( const mbedtls_x509_crt *child, int need_ca_bit; /* Parent must be the issuer */ - if( x509_name_cmp( &child->issuer, &parent->subject ) != 0 ) + if( mbedtls_x509_name_cmp( &child->issuer, &parent->subject ) != 0 ) return( -1 ); /* Parent must have the basicConstraints CA bit set as a general rule */ @@ -2276,7 +2179,7 @@ static int x509_crt_verify_child( const mbedtls_md_info_t *md_info; /* Counting intermediate self signed certificates */ - if( ( path_cnt != 0 ) && x509_name_cmp( &child->issuer, &child->subject ) == 0 ) + if( ( path_cnt != 0 ) && mbedtls_x509_name_cmp( &child->issuer, &child->subject ) == 0 ) self_cnt++; /* path_cnt is 0 for the first intermediate CA */ @@ -2447,7 +2350,7 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt, while( cur != NULL ) { if( cur->buf.len == cn_len && - x509_memcasecmp( cn, cur->buf.p, cn_len ) == 0 ) + mbedtls_x509_memcasecmp( cn, cur->buf.p, cn_len ) == 0 ) break; if( cur->buf.len > 2 && @@ -2470,7 +2373,7 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt, if( MBEDTLS_OID_CMP( MBEDTLS_OID_AT_CN, &name->oid ) == 0 ) { if( name->val.len == cn_len && - x509_memcasecmp( name->val.p, cn, cn_len ) == 0 ) + mbedtls_x509_memcasecmp( name->val.p, cn, cn_len ) == 0 ) break; if( name->val.len > 2 && diff --git a/library/x509_ocsp.c b/library/x509_ocsp.c index a21769f152..fb61a06ff4 100644 --- a/library/x509_ocsp.c +++ b/library/x509_ocsp.c @@ -1192,11 +1192,68 @@ static int x509_ocsp_verify_response_status( mbedtls_x509_ocsp_response *resp, } } +static int x509_ocsp_mdcmp( mbedtls_md_type_t md_alg, unsigned char *input, + size_t len, unsigned char *output ) +{ + int ret; + const mbedtls_md_info_t *md_info; + unsigned char *buf; + size_t md_len; + + if( ( md_info = mbedtls_md_info_from_type( md_alg ) ) == NULL ) + return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE ); + + md_len = mbedtls_md_get_size( md_info ); + + if( ( buf = mbedtls_calloc( md_len, sizeof( unsigned char ) ) ) == NULL ) + return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + + if( ( ret = mbedtls_md( md_info, input, len, buf ) ) != 0 ) + goto exit; + + /* Check whether the hash matches the expected value */ + ret = ( memcmp( buf, output, md_len ) != 0 ) ? 1 : 0; + +exit: + mbedtls_free( buf ); + + return( ret ); +} + static int x509_ocsp_is_issuer( mbedtls_x509_ocsp_responder_id *responder_id, mbedtls_x509_crt *crt, mbedtls_x509_crt **issuer ) { - return( 0 ) + int ret; + + switch( responder_id->type ) + { + case MBEDTLS_X509_OCSP_RESPONDER_ID_TYPE_NAME: + /* Compare the responderID with the candidate issuer's subject */ + if( mbedtls_x509_name_cmp( &responder_id->id.name, + &crt->subject ) == 0 ) + { + *issuer = crt; + } + + return( 0 ); + + case MBEDTLS_X509_OCSP_RESPONDER_ID_TYPE_KEY_HASH: + /* Check hash of the certificate issuer's public key matches */ + ret = x509_ocsp_mdcmp( MBEDTLS_MD_SHA1, crt->pk_raw.p, + crt->pk_raw.len, responder_id->id.key.p ); + if( ret < 0 ) + return( ret ); + else if( ret == 0 ) + *issuer = crt; + else + *issuer = NULL; + + return( 0 ); + + default: + return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); + } } /*