diff --git a/ChangeLog b/ChangeLog index 49c3acf5f..e3c335e23 100644 --- a/ChangeLog +++ b/ChangeLog @@ -49,6 +49,13 @@ API Changes always return NULL, and removes the peer_cert field from the mbedtls_ssl_session structure which otherwise stores the peer's certificate. + * Add a new compile-time option `MBEDTLS_X509_ON_DEMAND_PARSING`, + disabled by default, which allows to parse and cache X.509 CRTs + on demand only, at the benefit of lower RAM usage. Enabling + this option breaks the structure API of X.509 in that most + fields of `mbedtls_x509_crt` are removed, but it keeps the + X.509 function API. See the API changes section as well as + the documentation in `config.h` for more information. Bugfix * Server's RSA certificate in certs.c was SHA-1 signed. In the default diff --git a/configs/baremetal.h b/configs/baremetal.h index 330b513fc..12fa136dc 100644 --- a/configs/baremetal.h +++ b/configs/baremetal.h @@ -91,6 +91,8 @@ #define MBEDTLS_X509_CHECK_KEY_USAGE #define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE #define MBEDTLS_X509_REMOVE_INFO +#define MBEDTLS_X509_ON_DEMAND_PARSING +#define MBEDTLS_X509_ALWAYS_FLUSH #define MBEDTLS_ASN1_PARSE_C /* X.509 CSR writing */ diff --git a/include/mbedtls/asn1.h b/include/mbedtls/asn1.h index 96c1c9a8a..94990fe5e 100644 --- a/include/mbedtls/asn1.h +++ b/include/mbedtls/asn1.h @@ -89,6 +89,18 @@ #define MBEDTLS_ASN1_CONSTRUCTED 0x20 #define MBEDTLS_ASN1_CONTEXT_SPECIFIC 0x80 +/* Slightly smaller way to check if tag is a string tag + * compared to canonical implementation. */ +#define MBEDTLS_ASN1_IS_STRING_TAG( tag ) \ + ( ( tag ) < 32u && ( \ + ( ( 1u << ( tag ) ) & ( ( 1u << MBEDTLS_ASN1_BMP_STRING ) | \ + ( 1u << MBEDTLS_ASN1_UTF8_STRING ) | \ + ( 1u << MBEDTLS_ASN1_T61_STRING ) | \ + ( 1u << MBEDTLS_ASN1_IA5_STRING ) | \ + ( 1u << MBEDTLS_ASN1_UNIVERSAL_STRING ) | \ + ( 1u << MBEDTLS_ASN1_PRINTABLE_STRING ) | \ + ( 1u << MBEDTLS_ASN1_BIT_STRING ) ) ) != 0 ) ) + /* * Bit masks for each of the components of an ASN.1 tag as specified in * ITU X.690 (08/2015), section 8.1 "General rules for encoding", @@ -119,6 +131,10 @@ ( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf)->len ) || \ memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) != 0 ) +#define MBEDTLS_OID_CMP_RAW(oid_str, oid_buf, oid_buf_len) \ + ( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf_len) ) || \ + memcmp( (oid_str), (oid_buf), (oid_buf_len) ) != 0 ) + #ifdef __cplusplus extern "C" { #endif @@ -260,20 +276,97 @@ int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end size_t *len ); /** - * \brief Parses and splits an ASN.1 "SEQUENCE OF " - * Updated the pointer to immediately behind the full sequence tag. + * \brief Free a heap-allocated linked list presentation of + * an ASN.1 sequence, including the first element. * - * \param p The position in the ASN.1 data - * \param end End of data - * \param cur First variable in the chain to fill - * \param tag Type of sequence + * \param seq The address of the first sequence component. This may + * be \c NULL, in which case this functions returns + * immediately. + */ +void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq ); + +/** + * \brief This function parses and splits an ASN.1 "SEQUENCE OF " + * and updates the source buffer pointer to immediately behind + * the full sequence. + * + * \param p The address of the pointer to the beginning of the + * ASN.1 SEQUENCE OF structure, including ASN.1 tag+length header. + * On success, `*p` is advanced to point to the first byte + * following the parsed ASN.1 sequence. + * \param end The end of the ASN.1 input buffer starting at \p p. This is + * used for bounds checking. + * \param cur The address at which to store the first entry in the parsed + * sequence. Further entries are heap-allocated and referenced + * from \p cur. + * \param tag The common tag of the entries in the ASN.1 sequence. + * + * \note Ownership for the heap-allocated elements \c cur->next, + * \c cur->next->next, ..., is passed to the caller. It + * is hence the caller's responsibility to free them when + * no longer needed, and mbedtls_asn1_sequence_free() can + * be used for that, passing \c cur->next as the \c seq + * argument (or \p cur if \p cur itself was heap-allocated + * by the caller). * * \return 0 if successful or a specific ASN.1 error code. */ int mbedtls_asn1_get_sequence_of( unsigned char **p, const unsigned char *end, mbedtls_asn1_sequence *cur, - int tag); + int tag ); + +/** + * \brief Traverse an ASN.1 SEQUENCE container and + * call a callback for each entry. + * + * \warning This function is still experimental and may change + * at any time. + * + * \param p The address of the pointer to the beginning of + * the ASN.1 SEQUENCE header. This is updated to + * point to the end of the ASN.1 SEQUENCE container + * on a successful invocation. + * \param end The end of the ASN.1 SEQUENCE container. + * \param tag_must_mask A mask to be applied to the ASN.1 tags found within + * the SEQUENCE before comparing to \p tag_must_value. + * \param tag_must_val The required value of each ASN.1 tag found in the + * SEQUENCE, after masking with \p tag_must_mask. + * Mismatching tags lead to an error. + * For example, a value of \c 0 for both \p tag_must_mask + * and \p tag_must_val means that every tag is allowed, + * while a value of \c 0xFF for \p tag_must_mask means + * that \p tag_must_val is the only allowed tag. + * \param tag_may_mask A mask to be applied to the ASN.1 tags found within + * the SEQUENCE before comparing to \p tag_may_value. + * \param tag_may_val The desired value of each ASN.1 tag found in the + * SEQUENCE, after masking with \p tag_may_mask. + * Mismatching tags will be silently ignored. + * For example, a value of \c 0 for \p tag_may_mask and + * \p tag_may_val means that any tag will be considered, + * while a value of \c 0xFF for \p tag_may_mask means + * that all tags with value different from \p tag_may_val + * will be ignored. + * \param cb The callback to trigger for each component + * in the ASN.1 SEQUENCE. If the callback returns + * a non-zero value, the function stops immediately, + * forwarding the callback's return value. + * \param ctx The context to be passed to the callback \p cb. + * + * \return \c 0 if successful the entire ASN.1 SEQUENCE + * was traversed without parsing or callback errors. + * \return A negative ASN.1 error code on a parsing failure. + * \return A non-zero error code forwarded from the callback + * \p cb in case the latter returns a non-zero value. + */ +int mbedtls_asn1_traverse_sequence_of( + unsigned char **p, + const unsigned char *end, + uint8_t tag_must_mask, uint8_t tag_must_val, + uint8_t tag_may_mask, uint8_t tag_may_val, + int (*cb)( void *ctx, int tag, + unsigned char* start, size_t len ), + void *ctx ); #if defined(MBEDTLS_BIGNUM_C) /** diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h index 2116521dc..796b0bb2c 100644 --- a/include/mbedtls/config.h +++ b/include/mbedtls/config.h @@ -1757,6 +1757,54 @@ */ #define MBEDTLS_VERSION_FEATURES +/** + * \def MBEDTLS_X509_ON_DEMAND_PARSING + * + * Save RAM by reducing mbedtls_x509_crt to a pointer + * to the raw CRT data and parsing CRTs on demand only. + * + * \warning This option changes the API by removing most of + * the structure fields of mbedtls_x509_crt. + * + * \warning This option and its corresponding X.509 API are currently + * under development and may change at any time. + * + * Regardless of whether this option is enabled or not, direct access of + * structure fields of `mbedtls_x509_crt` should be replaced by calls to + * one of the following functions: + * - mbedtls_x509_crt_get_frame(), to obtain a CRT frame giving + * access to several basic CRT fields (such as the CRT version), + * as well as pointers to the raw ASN.1 data of more complex fields + * (such as the issuer). + * - mbedtls_x509_crt_get_pk(), to obtain a public key context + * for the public key contained in the certificate. + * - mbedtls_x509_crt_get_issuer(), to obtain the issuer name. + * - mbedtls_x509_crt_get_subject(), to obtain the subject name. + * - mbedtls_x509_crt_get_subject_alt_names(), to obtain the + * alternative names from the subject alternative names extension. + * - mbedtls_x509_crt_get_ext_key_usage(), to obtain the state of + * the extended key usage extension. + * + * Uncomment this to enable on-demand CRT parsing to save RAM. + */ +//#define MBEDTLS_X509_ON_DEMAND_PARSING + +/** + * \def MBEDTLS_X509_ALWAYS_FLUSH + * + * Save RAM by having Mbed TLS always flush caches for parsed X.509 + * structures after use: This means, firstly, that caches of X.509 + * structures used by an API call are flushed when the call returns, + * but it also encompasses immediate flushing of caches when Mbed TLS uses + * multiple structures in succession, thereby reducing the peak RAM usage. + * Setting this option leads to minimal RAM usage of the X.509 module at + * the cost of performance penalties when using X.509 structures multiple + * times (such as trusted CRTs on systems serving many connections). + * + * Uncomment this to always flush caches for unused X.509 structures. + */ +#define MBEDTLS_X509_ALWAYS_FLUSH + /** * \def MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3 * diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h index ff06d13b3..5d091bc2d 100644 --- a/include/mbedtls/x509.h +++ b/include/mbedtls/x509.h @@ -183,6 +183,15 @@ extern "C" { * \{ */ +/** + * Basic length-value buffer structure + */ +typedef struct mbedtls_x509_buf_raw +{ + unsigned char *p; /*!< The address of the first byte in the buffer. */ + size_t len; /*!< The number of Bytes in the buffer. */ +} mbedtls_x509_buf_raw; + /** * Type-length-value structure that allows for ASN1 using DER. */ @@ -269,6 +278,29 @@ int mbedtls_x509_time_is_past( const mbedtls_x509_time *to ); */ int mbedtls_x509_time_is_future( const mbedtls_x509_time *from ); +/** + * \brief Free a dynamic linked list presentation of an X.509 name + * as returned e.g. by mbedtls_x509_crt_get_subject(). + * + * \param name The address of the first name component. This may + * be \c NULL, in which case this functions returns + * immediately. + */ +void mbedtls_x509_name_free( mbedtls_x509_name *name ); + +/** + * \brief Free a dynamic linked list presentation of an X.509 sequence + * as returned e.g. by mbedtls_x509_crt_get_subject_alt_name(). + * + * \param seq The address of the first sequence component. This may + * be \c NULL, in which case this functions returns + * immediately. + */ +static inline void mbedtls_x509_sequence_free( mbedtls_x509_sequence *seq ) +{ + mbedtls_asn1_sequence_free( (mbedtls_asn1_sequence*) seq ); +} + #if defined(MBEDTLS_SELF_TEST) /** @@ -280,49 +312,6 @@ int mbedtls_x509_self_test( int verbose ); #endif /* MBEDTLS_SELF_TEST */ -/* - * Internal module functions. You probably do not want to use these unless you - * know you do. - */ -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, - mbedtls_x509_buf *alg ); -int mbedtls_x509_get_alg( unsigned char **p, const unsigned char *end, - mbedtls_x509_buf *alg, mbedtls_x509_buf *params ); -#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) -int mbedtls_x509_get_rsassa_pss_params( const mbedtls_x509_buf *params, - mbedtls_md_type_t *md_alg, mbedtls_md_type_t *mgf_md, - int *salt_len ); -#endif -int mbedtls_x509_get_sig( unsigned char **p, const unsigned char *end, mbedtls_x509_buf *sig ); -int mbedtls_x509_get_sig_alg( const mbedtls_x509_buf *sig_oid, const mbedtls_x509_buf *sig_params, - mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg, - void **sig_opts ); -int mbedtls_x509_get_time( unsigned char **p, const unsigned char *end, - mbedtls_x509_time *t ); -int mbedtls_x509_get_serial( unsigned char **p, const unsigned char *end, - mbedtls_x509_buf *serial ); -int mbedtls_x509_get_ext( unsigned char **p, const unsigned char *end, - mbedtls_x509_buf *ext, int tag ); -#if !defined(MBEDTLS_X509_REMOVE_INFO) -int mbedtls_x509_sig_alg_gets( char *buf, size_t size, const mbedtls_x509_buf *sig_oid, - mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg, - const void *sig_opts ); -#endif -int mbedtls_x509_key_size_helper( char *buf, size_t buf_size, const char *name ); -int mbedtls_x509_string_to_names( mbedtls_asn1_named_data **head, const char *name ); -int mbedtls_x509_set_extension( mbedtls_asn1_named_data **head, const char *oid, size_t oid_len, - int critical, const unsigned char *val, - size_t val_len ); -int mbedtls_x509_write_extensions( unsigned char **p, unsigned char *start, - mbedtls_asn1_named_data *first ); -int mbedtls_x509_write_names( unsigned char **p, unsigned char *start, - mbedtls_asn1_named_data *first ); -int mbedtls_x509_write_sig( unsigned char **p, unsigned char *start, - const char *oid, size_t oid_len, - unsigned char *sig, size_t size ); - #define MBEDTLS_X509_SAFE_SNPRINTF \ do { \ if( ret < 0 || (size_t) ret >= n ) \ @@ -332,6 +321,18 @@ int mbedtls_x509_write_sig( unsigned char **p, unsigned char *start, p += (size_t) ret; \ } while( 0 ) +#define MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP \ + do { \ + if( ret < 0 || (size_t) ret >= n ) \ + { \ + ret = MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; \ + goto cleanup; \ + } \ + \ + n -= (size_t) ret; \ + p += (size_t) ret; \ + } while( 0 ) + #ifdef __cplusplus } #endif diff --git a/include/mbedtls/x509_crl.h b/include/mbedtls/x509_crl.h index 2bb95de16..2950f302f 100644 --- a/include/mbedtls/x509_crl.h +++ b/include/mbedtls/x509_crl.h @@ -75,7 +75,7 @@ typedef struct mbedtls_x509_crl int version; /**< CRL version (1=v1, 2=v2) */ mbedtls_x509_buf sig_oid; /**< CRL signature type identifier */ - mbedtls_x509_buf issuer_raw; /**< The raw issuer data (DER). */ + mbedtls_x509_buf_raw issuer_raw; /**< The raw issuer data (DER). */ mbedtls_x509_name issuer; /**< The parsed issuer data (named information object). */ diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h index 09ba69f39..3eee460fb 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -32,6 +32,7 @@ #include "x509.h" #include "x509_crl.h" +#include "x509_internal.h" /** * \addtogroup x509_module @@ -47,14 +48,69 @@ extern "C" { * \{ */ +typedef struct mbedtls_x509_crt_frame +{ + /* Keep these 8-bit fields at the front of the structure to allow them to + * be fetched in a single instruction on Thumb2; putting them at the back + * requires an intermediate address calculation. */ + + uint8_t version; /**< The X.509 version. (1=v1, 2=v2, 3=v3) */ + uint8_t ca_istrue; /**< Optional Basic Constraint extension value: + * 1 if this certificate belongs to a CA, 0 otherwise. */ + uint8_t max_pathlen; /**< Optional Basic Constraint extension value: + * The maximum path length to the root certificate. + * Path length is 1 higher than RFC 5280 'meaning', so 1+ */ + uint8_t ns_cert_type; /**< Optional Netscape certificate type extension value: + * See the values in x509.h */ + + mbedtls_md_type_t sig_md; /**< The hash algorithm used to hash CRT before signing. */ + mbedtls_pk_type_t sig_pk; /**< The signature algorithm used to sign the CRT hash. */ + + uint16_t key_usage; /**< Optional key usage extension value: See the values in x509.h */ + uint32_t ext_types; /**< Bitfield indicating which extensions are present. + * See the values in x509.h. */ + + mbedtls_x509_time valid_from; /**< The start time of certificate validity. */ + mbedtls_x509_time valid_to; /**< The end time of certificate validity. */ + + mbedtls_x509_buf_raw raw; /**< The raw certificate data in DER. */ + mbedtls_x509_buf_raw tbs; /**< The part of the CRT that is [T]o [B]e [S]igned. */ + + mbedtls_x509_buf_raw serial; /**< The unique ID for certificate issued by a specific CA. */ + + mbedtls_x509_buf_raw pubkey_raw; /**< The raw public key data (DER). */ + + mbedtls_x509_buf_raw issuer_id; /**< Optional X.509 v2/v3 issuer unique identifier. */ + mbedtls_x509_buf_raw issuer_raw; /**< The raw issuer data (DER). Used for quick comparison. */ + + mbedtls_x509_buf_raw subject_id; /**< Optional X.509 v2/v3 subject unique identifier. */ + mbedtls_x509_buf_raw subject_raw; /**< The raw subject data (DER). Used for quick comparison. */ + + mbedtls_x509_buf_raw sig; /**< Signature: hash of the tbs part signed with the private key. */ + mbedtls_x509_buf_raw sig_alg; /**< The signature algorithm used for \p sig. */ + + mbedtls_x509_buf_raw v3_ext; /**< The raw data for the extension list in the certificate. + * Might be useful for manual inspection of extensions that + * Mbed TLS doesn't yet support. */ + mbedtls_x509_buf_raw subject_alt_raw; /**< The raw data for the SubjectAlternativeNames extension. */ + mbedtls_x509_buf_raw ext_key_usage_raw; /**< The raw data for the ExtendedKeyUsage extension. */ + +} mbedtls_x509_crt_frame; + /** * Container for an X.509 certificate. The certificate may be chained. */ typedef struct mbedtls_x509_crt { int own_buffer; /**< Indicates if \c raw is owned - * by the structure or not. */ - mbedtls_x509_buf raw; /**< The raw certificate data (DER). */ + * by the structure or not. */ + mbedtls_x509_buf raw; /**< The raw certificate data (DER). */ + mbedtls_x509_crt_cache *cache; /**< Internal parsing cache. */ + + struct mbedtls_x509_crt *next; /**< Next certificate in the CA-chain. */ + + /* Legacy fields */ +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) mbedtls_x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ int version; /**< The X.509 version. (1=v1, 2=v2, 3=v3) */ @@ -84,7 +140,7 @@ typedef struct mbedtls_x509_crt unsigned int key_usage; /**< Optional key usage extension value: See the values in x509.h */ - mbedtls_x509_sequence ext_key_usage; /**< Optional list of extended key usage OIDs. */ + mbedtls_x509_sequence ext_key_usage; /**< Optional list of extended key usage OIDs. */ unsigned char ns_cert_type; /**< Optional Netscape certificate type extension value: See the values in x509.h */ @@ -92,8 +148,7 @@ typedef struct mbedtls_x509_crt mbedtls_md_type_t sig_md; /**< Internal representation of the MD algorithm of the signature algorithm, e.g. MBEDTLS_MD_SHA256 */ mbedtls_pk_type_t sig_pk; /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. MBEDTLS_PK_RSA */ void *sig_opts; /**< Signature options to be passed to mbedtls_pk_verify_ext(), e.g. for RSASSA-PSS */ - - struct mbedtls_x509_crt *next; /**< Next certificate in the CA-chain. */ +#endif /* !MBEDTLS_X509_ON_DEMAND_PARSING */ } mbedtls_x509_crt; @@ -586,6 +641,366 @@ void mbedtls_x509_crt_restart_init( mbedtls_x509_crt_restart_ctx *ctx ); */ void mbedtls_x509_crt_restart_free( mbedtls_x509_crt_restart_ctx *ctx ); #endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief Request CRT frame giving access to basic CRT fields + * and raw ASN.1 data of complex fields. + * + * \param crt The CRT to use. This must be initialized and set up. + * \param dst The address of the destination frame structure. + * This need not be initialized. + * + * \note ::mbedtls_x509_crt_frame does not contain pointers to + * dynamically allocated memory, and hence need not be freed. + * Users may e.g. allocate an instance of + * ::mbedtls_x509_crt_frame on the stack and call this function + * on it, in which case no allocation/freeing has to be done. + * + * \note You may also use mbedtls_x509_crt_frame_acquire() and + * mbedtls_x509_crt_frame_release() for copy-less variants + * of this function. + * + * \return \c 0 on success. In this case, \p dst is updated + * to hold the frame for the given CRT. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_get_frame( mbedtls_x509_crt const *crt, + mbedtls_x509_crt_frame *dst ); + +/** + * \brief Set up a PK context with the public key in a certificate. + * + * \param crt The certificate to use. This must be initialized and set up. + * \param pk The address of the destination PK context to fill. + * This must be initialized via mbedtls_pk_init(). + * + * \note You may also use mbedtls_x509_crt_pk_acquire() and + * mbedtls_x509_crt_pk_release() for copy-less variants + * of this function. + * + * \return \c 0 on success. In this case, the user takes ownership + * of the destination PK context, and is responsible for + * calling mbedtls_pk_free() on it once it's no longer needed. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_get_pk( mbedtls_x509_crt const *crt, + mbedtls_pk_context *pk ); + +/** + * \brief Request the subject name of a CRT, presented + * as a dynamically allocated linked list. + * + * \param crt The CRT to use. This must be initialized and set up. + * \param subject The address at which to store the address of the + * first entry of the generated linked list holding + * the subject name. + * + * \note Depending on your use case, consider using the raw ASN.1 + * describing the subject name instead of the heap-allocated + * linked list generated by this call. The pointers to the + * raw ASN.1 data are part of the CRT frame that can be queried + * via mbedtls_x509_crt_get_frame(), and they can be traversed + * via mbedtls_asn1_traverse_sequence_of(). + * + * \return \c 0 on success. In this case, the user takes ownership + * of the name context, and is responsible for freeing it + * through a call to mbedtls_x509_name_free() once it's no + * longer needed. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_get_subject( mbedtls_x509_crt const *crt, + mbedtls_x509_name **subject ); + +/** + * \brief Request the subject name of a CRT, presented + * as a dynamically allocated linked list. + * + * \param crt The CRT to use. This must be initialized and set up. + * \param issuer The address at which to store the address of the + * first entry of the generated linked list holding + * the subject name. + * + * \note Depending on your use case, consider using the raw ASN.1 + * describing the issuer name instead of the heap-allocated + * linked list generated by this call. The pointers to the + * raw ASN.1 data are part of the CRT frame that can be queried + * via mbedtls_x509_crt_get_frame(), and they can be traversed + * via mbedtls_asn1_traverse_sequence_of(). + * + * \return \c 0 on success. In this case, the user takes ownership + * of the name context, and is responsible for freeing it + * through a call to mbedtls_x509_name_free() once it's no + * longer needed. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_get_issuer( mbedtls_x509_crt const *crt, + mbedtls_x509_name **issuer ); + +/** + * \brief Request the subject alternative name of a CRT, presented + * as a dynamically allocated linked list. + * + * \param crt The CRT to use. This must be initialized and set up. + * \param subj_alt The address at which to store the address of the + * first component of the subject alternative names list. + * + * \note Depending in your use case, consider using the raw ASN.1 + * describing the subject alternative names extension + * instead of the heap-allocated linked list generated by this + * call. The pointers to the raw ASN.1 data are part of the CRT + * frame that can be queried via mbedtls_x509_crt_get_frame(), + * and mbedtls_asn1_traverse_sequence_of() can be used to + * traverse the list of subject alternative names. + * + * \return \c 0 on success. In this case, the user takes ownership + * of the name context, and is responsible for freeing it + * through a call to mbedtls_x509_sequence_free() once it's + * no longer needed. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_get_subject_alt_names( mbedtls_x509_crt const *crt, + mbedtls_x509_sequence **subj_alt ); + +/** + * \brief Request the ExtendedKeyUsage extension of a CRT, + * presented as a dynamically allocated linked list. + * + * \param crt The CRT to use. This must be initialized and set up. + * \param ext_key_usage The address at which to store the address of the + * first entry of the ExtendedKeyUsage extension. + * + * \note Depending in your use case, consider using the raw ASN.1 + * describing the extended key usage extension instead of + * the heap-allocated linked list generated by this call. + * The pointers to the raw ASN.1 data are part of the CRT + * frame that can be queried via mbedtls_x509_crt_get_frame(), + * and mbedtls_asn1_traverse_sequence_of() can be used to + * traverse the entries in the extended key usage extension. + * + * \return \c 0 on success. In this case, the user takes ownership + * of the name context, and is responsible for freeing it + * through a call to mbedtls_x509_sequence_free() once it's + * no longer needed. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_get_ext_key_usage( mbedtls_x509_crt const *crt, + mbedtls_x509_sequence **ext_key_usage ); + +/** + * \brief Flush internal X.509 CRT parsing cache, if present. + * + * \param crt The CRT structure whose cache to flush. + * + * \note Calling this function frequently reduces RAM usage + * at the cost of performance. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_flush_cache( mbedtls_x509_crt const *crt ); + +/** + * \brief Request temporary read-access to a certificate frame + * for a given certificate. + * + * Once no longer needed, the frame must be released + * through a call to mbedtls_x509_crt_frame_release(). + * + * This is a copy-less version of mbedtls_x509_crt_get_frame(). + * See there for more information. + * + * \param crt The certificate to use. This must be initialized and set up. + * \param dst The address at which to store the address of a readable + * certificate frame for the input certificate \p crt which the + * caller can use until calling mbedtls_x509_crt_frame_release(). + * + * \note The certificate frame `**frame_ptr` returned by this function + * is owned by the X.509 module and must not be freed or modified + * by the caller. The X.509 module guarantees its validity as long + * as \p crt is valid and mbedtls_x509_crt_frame_release() hasn't + * been issued. + * + * \note In a single-threaded application using + * MBEDTLS_X509_ALWAYS_FLUSH, nested calls to this function + * are not allowed and will fail gracefully with + * MBEDTLS_ERR_X509_FATAL_ERROR. + * + * \return \c 0 on success. In this case, `*frame_ptr` is updated + * to hold the address of a frame for the given CRT. + * \return A negative error code on failure. + */ +static inline int mbedtls_x509_crt_frame_acquire( mbedtls_x509_crt const *crt, + mbedtls_x509_crt_frame const **dst ) +{ + int ret = 0; +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_lock( &crt->cache->frame_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif /* MBEDTLS_THREADING_C */ + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + if( crt->cache->frame_readers == 0 ) +#endif + ret = mbedtls_x509_crt_cache_provide_frame( crt ); + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + if( crt->cache->frame_readers == MBEDTLS_X509_CACHE_FRAME_READERS_MAX ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); + + crt->cache->frame_readers++; +#endif + +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_unlock( &crt->cache->frame_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif /* MBEDTLS_THREADING_C */ + + *dst = crt->cache->frame; + return( ret ); +} + +/** + * \brief Release access to a certificate frame acquired + * through a prior call to mbedtls_x509_crt_frame_acquire(). + * + * \param crt The certificate for which a certificate frame has + * previously been acquired. + */ +static inline int mbedtls_x509_crt_frame_release( mbedtls_x509_crt const *crt ) +{ +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_lock( &crt->cache->frame_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif /* MBEDTLS_THREADING_C */ + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + if( crt->cache->frame_readers == 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + crt->cache->frame_readers--; +#endif + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_unlock( &crt->cache->frame_mutex ); +#endif /* MBEDTLS_THREADING_C */ + +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) + (void) mbedtls_x509_crt_flush_cache_frame( crt ); +#endif /* MBEDTLS_X509_ALWAYS_FLUSH */ + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) && \ + !defined(MBEDTLS_THREADING_C) + ((void) crt); +#endif + + return( 0 ); +} + +/** + * \brief Request temporary access to a public key context + * for a given certificate. + * + * Once no longer needed, the frame must be released + * through a call to mbedtls_x509_crt_pk_release(). + * + * This is a copy-less version of mbedtls_x509_crt_get_pk(). + * See there for more information. + * + * \param crt The certificate to use. This must be initialized and set up. + * \param dst The address at which to store the address of a public key + * context for the public key in the input certificate \p crt. + * + * \warning The public key context `**pk_ptr` returned by this function + * is owned by the X.509 module and must be used by the caller + * in a thread-safe way. In particular, the caller must only + * use the context with functions which are `const` on the input + * context, or those which are known to be thread-safe. The latter + * for example includes mbedtls_pk_verify() for ECC or RSA public + * key contexts. + * + * \note In a single-threaded application using + * MBEDTLS_X509_ALWAYS_FLUSH, nested calls to this function + * are not allowed and will fail gracefully with + * MBEDTLS_ERR_X509_FATAL_ERROR. + * + * \return \c 0 on success. In this case, `*pk_ptr` is updated + * to hold the address of a public key context for the given + * certificate. + * \return A negative error code on failure. + */ +static inline int mbedtls_x509_crt_pk_acquire( mbedtls_x509_crt const *crt, + mbedtls_pk_context **dst ) +{ + int ret = 0; +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_lock( &crt->cache->pk_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif /* MBEDTLS_THREADING_C */ + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + if( crt->cache->pk_readers == 0 ) +#endif + ret = mbedtls_x509_crt_cache_provide_pk( crt ); + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + if( crt->cache->pk_readers == MBEDTLS_X509_CACHE_PK_READERS_MAX ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); + + crt->cache->pk_readers++; +#endif + +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_unlock( &crt->cache->pk_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif /* MBEDTLS_THREADING_C */ + + *dst = crt->cache->pk; + return( ret ); +} + +/** + * \brief Release access to a public key context acquired + * through a prior call to mbedtls_x509_crt_frame_acquire(). + * + * \param crt The certificate for which a certificate frame has + * previously been acquired. + */ +static inline int mbedtls_x509_crt_pk_release( mbedtls_x509_crt const *crt ) +{ +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_lock( &crt->cache->pk_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif /* MBEDTLS_THREADING_C */ + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + if( crt->cache->pk_readers == 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + crt->cache->pk_readers--; +#endif + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_unlock( &crt->cache->pk_mutex ); +#endif /* MBEDTLS_THREADING_C */ + +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) + (void) mbedtls_x509_crt_flush_cache_pk( crt ); +#endif /* MBEDTLS_X509_ALWAYS_FLUSH */ + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) && \ + !defined(MBEDTLS_THREADING_C) + ((void) crt); +#endif + + return( 0 ); +} + #endif /* MBEDTLS_X509_CRT_PARSE_C */ /* \} name */ diff --git a/include/mbedtls/x509_internal.h b/include/mbedtls/x509_internal.h new file mode 100644 index 000000000..6ca3db590 --- /dev/null +++ b/include/mbedtls/x509_internal.h @@ -0,0 +1,117 @@ +/** + * \file x509_internal.h + * + * \brief Internal X.509 functions + */ +/* + * Copyright (C) 2006-2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + * + */ +#ifndef MBEDTLS_X509_INTERNAL_H +#define MBEDTLS_X509_INTERNAL_H + +#include "x509.h" +#include "threading.h" + +/* Internal structure used for caching parsed data from an X.509 CRT. */ + +struct mbedtls_x509_crt; +struct mbedtls_pk_context; +struct mbedtls_x509_crt_frame; +#define MBEDTLS_X509_CACHE_PK_READERS_MAX ((uint32_t) -1) +#define MBEDTLS_X509_CACHE_FRAME_READERS_MAX ((uint32_t) -1) +typedef struct mbedtls_x509_crt_cache +{ +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + uint32_t frame_readers; + uint32_t pk_readers; +#endif /* !MBEDTLS_X509_ALWAYS_FLUSH || MBEDTLS_THREADING_C */ +#if defined(MBEDTLS_THREADING_C) + mbedtls_threading_mutex_t frame_mutex; + mbedtls_threading_mutex_t pk_mutex; +#endif + mbedtls_x509_buf_raw pk_raw; + struct mbedtls_x509_crt_frame *frame; + struct mbedtls_pk_context *pk; +} mbedtls_x509_crt_cache; + +/* Internal X.509 CRT cache handling functions. */ + +int mbedtls_x509_crt_flush_cache_frame( struct mbedtls_x509_crt const *crt ); +int mbedtls_x509_crt_flush_cache_pk( struct mbedtls_x509_crt const *crt ); + +int mbedtls_x509_crt_cache_provide_frame( struct mbedtls_x509_crt const *crt ); +int mbedtls_x509_crt_cache_provide_pk( struct mbedtls_x509_crt const *crt ); + +/* Uncategorized internal X.509 functions */ + +int mbedtls_x509_get_name( unsigned char *p, size_t len, + mbedtls_x509_name *cur ); +int mbedtls_x509_get_alg_null( unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *alg ); +int mbedtls_x509_get_alg( unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *alg, mbedtls_x509_buf *params ); +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) +int mbedtls_x509_get_rsassa_pss_params( const mbedtls_x509_buf *params, + mbedtls_md_type_t *md_alg, mbedtls_md_type_t *mgf_md, + int *salt_len ); +#endif +int mbedtls_x509_get_sig( unsigned char **p, const unsigned char *end, mbedtls_x509_buf *sig ); +int mbedtls_x509_get_sig_alg_raw( unsigned char **p, unsigned char const *end, + mbedtls_md_type_t *md_alg, + mbedtls_pk_type_t *pk_alg, + void **sig_opts ); +int mbedtls_x509_get_sig_alg( const mbedtls_x509_buf *sig_oid, const mbedtls_x509_buf *sig_params, + mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg, + void **sig_opts ); +int mbedtls_x509_get_time( unsigned char **p, const unsigned char *end, + mbedtls_x509_time *t ); +int mbedtls_x509_get_serial( unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *serial ); +int mbedtls_x509_name_cmp_raw( mbedtls_x509_buf_raw const *a, + mbedtls_x509_buf_raw const *b, + int (*check)( void *ctx, + mbedtls_x509_buf *oid, + mbedtls_x509_buf *val, + int next_merged ), + void *check_ctx ); +int mbedtls_x509_memcasecmp( const void *s1, const void *s2, + size_t len1, size_t len2 ); +int mbedtls_x509_get_ext( unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *ext, int tag ); + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +int mbedtls_x509_sig_alg_gets( char *buf, size_t size, + mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg, + const void *sig_opts ); +#endif +int mbedtls_x509_key_size_helper( char *buf, size_t buf_size, const char *name ); +int mbedtls_x509_string_to_names( mbedtls_asn1_named_data **head, const char *name ); +int mbedtls_x509_set_extension( mbedtls_asn1_named_data **head, const char *oid, size_t oid_len, + int critical, const unsigned char *val, + size_t val_len ); +int mbedtls_x509_write_extensions( unsigned char **p, unsigned char *start, + mbedtls_asn1_named_data *first ); +int mbedtls_x509_write_names( unsigned char **p, unsigned char *start, + mbedtls_asn1_named_data *first ); +int mbedtls_x509_write_sig( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + unsigned char *sig, size_t size ); + +#endif /* MBEDTLS_X509_INTERNAL_H */ diff --git a/library/asn1parse.c b/library/asn1parse.c index 171c340b8..aac253b01 100644 --- a/library/asn1parse.c +++ b/library/asn1parse.c @@ -229,6 +229,103 @@ int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end return( 0 ); } +void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq ) +{ + while( seq != NULL ) + { + mbedtls_asn1_sequence *next = seq->next; + mbedtls_platform_zeroize( seq, sizeof( *seq ) ); + mbedtls_free( seq ); + seq = next; + } +} + +/* + * Traverse an ASN.1 "SEQUENCE OF " + * and call a callback for each entry found. + */ +int mbedtls_asn1_traverse_sequence_of( + unsigned char **p, + const unsigned char *end, + uint8_t tag_must_mask, uint8_t tag_must_val, + uint8_t tag_may_mask, uint8_t tag_may_val, + int (*cb)( void *ctx, int tag, + unsigned char *start, size_t len ), + void *ctx ) +{ + int ret; + size_t len; + + /* Get main sequence tag */ + if( ( ret = mbedtls_asn1_get_tag( p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + { + return( ret ); + } + + if( *p + len != end ) + return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + + while( *p < end ) + { + unsigned char const tag = *(*p)++; + + if( ( tag & tag_must_mask ) != tag_must_val ) + return( MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); + + if( ( ret = mbedtls_asn1_get_len( p, end, &len ) ) != 0 ) + return( ret ); + + if( ( tag & tag_may_mask ) == tag_may_val ) + { + if( cb != NULL ) + { + ret = cb( ctx, tag, *p, len ); + if( ret != 0 ) + return( ret ); + } + } + + *p += len; + } + + return( 0 ); +} + +typedef struct +{ + int tag; + mbedtls_asn1_sequence *cur; +} asn1_get_sequence_of_cb_ctx_t; + +static int asn1_get_sequence_of_cb( void *ctx, + int tag, + unsigned char *start, + size_t len ) +{ + asn1_get_sequence_of_cb_ctx_t *cb_ctx = + (asn1_get_sequence_of_cb_ctx_t *) ctx; + mbedtls_asn1_sequence *cur = + cb_ctx->cur; + + if( cur->buf.p != NULL ) + { + cur->next = + mbedtls_calloc( 1, sizeof( mbedtls_asn1_sequence ) ); + + if( cur->next == NULL ) + return( MBEDTLS_ERR_ASN1_ALLOC_FAILED ); + + cur = cur->next; + } + + cur->buf.p = start; + cur->buf.len = len; + cur->buf.tag = tag; + + cb_ctx->cur = cur; + return( 0 ); +} /* @@ -239,49 +336,11 @@ int mbedtls_asn1_get_sequence_of( unsigned char **p, mbedtls_asn1_sequence *cur, int tag) { - int ret; - size_t len; - mbedtls_asn1_buf *buf; - - /* Get main sequence tag */ - if( ( ret = mbedtls_asn1_get_tag( p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) - return( ret ); - - if( *p + len != end ) - return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - - while( *p < end ) - { - buf = &(cur->buf); - buf->tag = **p; - - if( ( ret = mbedtls_asn1_get_tag( p, end, &buf->len, tag ) ) != 0 ) - return( ret ); - - buf->p = *p; - *p += buf->len; - - /* Allocate and assign next pointer */ - if( *p < end ) - { - cur->next = (mbedtls_asn1_sequence*)mbedtls_calloc( 1, - sizeof( mbedtls_asn1_sequence ) ); - - if( cur->next == NULL ) - return( MBEDTLS_ERR_ASN1_ALLOC_FAILED ); - - cur = cur->next; - } - } - - /* Set final sequence entry's next pointer to NULL */ - cur->next = NULL; - - if( *p != end ) - return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); + asn1_get_sequence_of_cb_ctx_t cb_ctx = { tag, cur }; + memset( cur, 0, sizeof( mbedtls_asn1_sequence ) ); + return( mbedtls_asn1_traverse_sequence_of( + p, end, 0xFF, tag, 0, 0, + asn1_get_sequence_of_cb, &cb_ctx ) ); } int mbedtls_asn1_get_alg( unsigned char **p, @@ -295,15 +354,12 @@ int mbedtls_asn1_get_alg( unsigned char **p, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) return( ret ); - if( ( end - *p ) < 1 ) - return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); - - alg->tag = **p; end = *p + len; if( ( ret = mbedtls_asn1_get_tag( p, end, &alg->len, MBEDTLS_ASN1_OID ) ) != 0 ) return( ret ); + alg->tag = MBEDTLS_ASN1_OID; alg->p = *p; *p += alg->len; diff --git a/library/bignum.c b/library/bignum.c index 41946183c..d94754a56 100644 --- a/library/bignum.c +++ b/library/bignum.c @@ -127,7 +127,7 @@ int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs ) if( X->n < nblimbs ) { - if( ( p = (mbedtls_mpi_uint*)mbedtls_calloc( nblimbs, ciL ) ) == NULL ) + if( ( p = (mbedtls_mpi_uint *)mbedtls_calloc( nblimbs, ciL ) ) == NULL ) return( MBEDTLS_ERR_MPI_ALLOC_FAILED ); if( X->p != NULL ) @@ -169,7 +169,7 @@ int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs ) if( i < nblimbs ) i = nblimbs; - if( ( p = (mbedtls_mpi_uint*)mbedtls_calloc( i, ciL ) ) == NULL ) + if( ( p = (mbedtls_mpi_uint *)mbedtls_calloc( i, ciL ) ) == NULL ) return( MBEDTLS_ERR_MPI_ALLOC_FAILED ); if( X->p != NULL ) diff --git a/library/cipher.c b/library/cipher.c index 273997577..58217163c 100644 --- a/library/cipher.c +++ b/library/cipher.c @@ -331,13 +331,13 @@ int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx, ? MBEDTLS_CHACHAPOLY_ENCRYPT : MBEDTLS_CHACHAPOLY_DECRYPT; - result = mbedtls_chachapoly_starts( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + result = mbedtls_chachapoly_starts( (mbedtls_chachapoly_context *) ctx->cipher_ctx, ctx->iv, mode ); if ( result != 0 ) return( result ); - return( mbedtls_chachapoly_update_aad( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + return( mbedtls_chachapoly_update_aad( (mbedtls_chachapoly_context *) ctx->cipher_ctx, ad, ad_len ) ); } #endif @@ -391,7 +391,7 @@ int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *i if ( ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 ) { *olen = ilen; - return( mbedtls_chachapoly_update( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + return( mbedtls_chachapoly_update( (mbedtls_chachapoly_context *) ctx->cipher_ctx, ilen, input, output ) ); } #endif @@ -924,7 +924,7 @@ int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx, if ( tag_len != 16U ) return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); - return( mbedtls_chachapoly_finish( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + return( mbedtls_chachapoly_finish( (mbedtls_chachapoly_context *) ctx->cipher_ctx, tag ) ); } #endif @@ -975,7 +975,7 @@ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx, if ( tag_len != sizeof( check_tag ) ) return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); - ret = mbedtls_chachapoly_finish( (mbedtls_chachapoly_context*) ctx->cipher_ctx, + ret = mbedtls_chachapoly_finish( (mbedtls_chachapoly_context *) ctx->cipher_ctx, check_tag ); if ( ret != 0 ) { diff --git a/library/cipher_wrap.c b/library/cipher_wrap.c index 6dd8c5d3a..54572efb9 100644 --- a/library/cipher_wrap.c +++ b/library/cipher_wrap.c @@ -1987,7 +1987,7 @@ static int chachapoly_setkey_wrap( void *ctx, if( key_bitlen != 256U ) return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); - if ( 0 != mbedtls_chachapoly_setkey( (mbedtls_chachapoly_context*)ctx, key ) ) + if ( 0 != mbedtls_chachapoly_setkey( (mbedtls_chachapoly_context *)ctx, key ) ) return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); return( 0 ); diff --git a/library/debug.c b/library/debug.c index c6788b62b..da4ceac2c 100644 --- a/library/debug.c +++ b/library/debug.c @@ -71,7 +71,7 @@ static inline void debug_send_line( const mbedtls_ssl_context *ssl, int level, */ #if defined(MBEDTLS_THREADING_C) char idstr[20 + DEBUG_BUF_SIZE]; /* 0x + 16 nibbles + ': ' */ - mbedtls_snprintf( idstr, sizeof( idstr ), "%p: %s", (void*)ssl, str ); + mbedtls_snprintf( idstr, sizeof( idstr ), "%p: %s", (void *)ssl, str ); ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, idstr ); #else ssl->conf->f_dbg( ssl->conf->p_dbg, level, file, line, str ); @@ -382,6 +382,8 @@ void mbedtls_debug_print_crt( const mbedtls_ssl_context *ssl, int level, while( crt != NULL ) { + int ret; + mbedtls_pk_context *pk; char buf[1024]; mbedtls_snprintf( str, sizeof( str ), "%s #%d:\n", text, ++i ); @@ -390,7 +392,17 @@ void mbedtls_debug_print_crt( const mbedtls_ssl_context *ssl, int level, mbedtls_x509_crt_info( buf, sizeof( buf ) - 1, "", crt ); debug_print_line_by_line( ssl, level, file, line, buf ); - debug_print_pk( ssl, level, file, line, "crt->", &crt->pk ); + ret = mbedtls_x509_crt_pk_acquire( crt, &pk ); + if( ret != 0 ) + { + mbedtls_snprintf( str, sizeof( str ), + "mbedtls_x509_crt_pk_acquire() failed with -%#04x\n", + -ret ); + debug_send_line( ssl, level, file, line, str ); + return; + } + debug_print_pk( ssl, level, file, line, "crt->", pk ); + mbedtls_x509_crt_pk_release( crt ); crt = crt->next; } diff --git a/library/net_sockets.c b/library/net_sockets.c index 816b1303d..bbcf630bc 100644 --- a/library/net_sockets.c +++ b/library/net_sockets.c @@ -72,8 +72,8 @@ #endif #endif /* _MSC_VER */ -#define read(fd,buf,len) recv( fd, (char*)( buf ), (int)( len ), 0 ) -#define write(fd,buf,len) send( fd, (char*)( buf ), (int)( len ), 0 ) +#define read(fd,buf,len) recv( fd, (char *)( buf ), (int)( len ), 0 ) +#define write(fd,buf,len) send( fd, (char *)( buf ), (int)( len ), 0 ) #define close(fd) closesocket(fd) static int wsa_init_done = 0; diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 17611d6fc..0f75b1c32 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c @@ -2334,7 +2334,15 @@ static int ssl_write_encrypted_pms( mbedtls_ssl_context *ssl, peer_pk = &ssl->handshake->peer_pubkey; #else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ if( ssl->session_negotiate->peer_cert != NULL ) - peer_pk = &ssl->session_negotiate->peer_cert->pk; + { + ret = mbedtls_x509_crt_pk_acquire( ssl->session_negotiate->peer_cert, + &peer_pk ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_pk_acquire", ret ); + return( ret ); + } + } #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ if( peer_pk == NULL ) @@ -2350,7 +2358,8 @@ static int ssl_write_encrypted_pms( mbedtls_ssl_context *ssl, if( ! mbedtls_pk_can_do( peer_pk, MBEDTLS_PK_RSA ) ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "certificate key type mismatch" ) ); - return( MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH ); + ret = MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + goto cleanup; } if( ( ret = mbedtls_pk_encrypt( peer_pk, @@ -2360,7 +2369,7 @@ static int ssl_write_encrypted_pms( mbedtls_ssl_context *ssl, ssl->conf->f_rng, ssl->conf->p_rng ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_rsa_pkcs1_encrypt", ret ); - return( ret ); + goto cleanup; } #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \ @@ -2373,11 +2382,16 @@ static int ssl_write_encrypted_pms( mbedtls_ssl_context *ssl, } #endif +cleanup: + #if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) /* We don't need the peer's public key anymore. Free it. */ mbedtls_pk_free( peer_pk ); -#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ - return( 0 ); +#else + mbedtls_x509_crt_pk_release( ssl->session_negotiate->peer_cert ); +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + return( ret ); } #endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED || MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ @@ -2463,13 +2477,21 @@ static int ssl_get_ecdh_params_from_cert( mbedtls_ssl_context *ssl ) MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } - peer_pk = &ssl->session_negotiate->peer_cert->pk; + + ret = mbedtls_x509_crt_pk_acquire( ssl->session_negotiate->peer_cert, + &peer_pk ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_pk_acquire", ret ); + return( ret ); + } #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ if( ! mbedtls_pk_can_do( peer_pk, MBEDTLS_PK_ECKEY ) ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "server key not ECDH capable" ) ); - return( MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH ); + ret = MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + goto cleanup; } peer_key = mbedtls_pk_ec( *peer_pk ); @@ -2478,21 +2500,26 @@ static int ssl_get_ecdh_params_from_cert( mbedtls_ssl_context *ssl ) MBEDTLS_ECDH_THEIRS ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ecdh_get_params" ), ret ); - return( ret ); + goto cleanup; } if( ssl_check_server_ecdh_params( ssl ) != 0 ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server certificate (ECDH curve)" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE; + goto cleanup; } +cleanup: + #if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) /* We don't need the peer's public key anymore. Free it, * so that more RAM is available for upcoming expensive * operations like ECDHE. */ mbedtls_pk_free( peer_pk ); -#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#else + mbedtls_x509_crt_pk_release( ssl->session_negotiate->peer_cert ); +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ return( ret ); } @@ -2799,7 +2826,14 @@ start_processing: MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) ); return( MBEDTLS_ERR_SSL_INTERNAL_ERROR ); } - peer_pk = &ssl->session_negotiate->peer_cert->pk; + + ret = mbedtls_x509_crt_pk_acquire( ssl->session_negotiate->peer_cert, + &peer_pk ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_pk_acquire", ret ); + return( ret ); + } #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ /* @@ -2810,6 +2844,9 @@ start_processing: MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE ); +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + mbedtls_x509_crt_pk_release( ssl->session_negotiate->peer_cert ); +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ return( MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH ); } @@ -2831,6 +2868,9 @@ start_processing: if( ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) ret = MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; #endif +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + mbedtls_x509_crt_pk_release( ssl->session_negotiate->peer_cert ); +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ return( ret ); } @@ -2839,7 +2879,9 @@ start_processing: * so that more RAM is available for upcoming expensive * operations like ECDHE. */ mbedtls_pk_free( peer_pk ); -#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#else + mbedtls_x509_crt_pk_release( ssl->session_negotiate->peer_cert ); +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ } #endif /* MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED */ diff --git a/library/ssl_srv.c b/library/ssl_srv.c index ecde1b0b5..94b4d7333 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c @@ -791,15 +791,58 @@ static int ssl_pick_cert( mbedtls_ssl_context *ssl, for( cur = list; cur != NULL; cur = cur->next ) { - MBEDTLS_SSL_DEBUG_CRT( 3, "candidate certificate chain, certificate", - cur->cert ); + int match = 1; + mbedtls_pk_context *pk; - if( ! mbedtls_pk_can_do( &cur->cert->pk, pk_alg ) ) + /* WARNING: With the current X.509 caching architecture, this MUST + * happen outside of the PK acquire/release block, because it moves + * the cached PK context. In a threading-enabled build, this would + * rightfully fail, but lead to a use-after-free otherwise. */ + MBEDTLS_SSL_DEBUG_CRT( 3, "candidate certificate chain, certificate", + cur->cert ); + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + /* ASYNC_PRIVATE may use a NULL entry for the opaque private key, so + * we have to use the public key context to infer the capabilities + * of the key. */ + { + int ret; + ret = mbedtls_x509_crt_pk_acquire( cur->cert, &pk ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_pk_acquire", ret ); + return( ret ); + } + } +#else + /* Outside of ASYNC_PRIVATE, use private key context directly + * instead of querying for the public key context from the + * certificate, so save a few bytes of code. */ + pk = cur->key; +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + if( ! mbedtls_pk_can_do( pk, pk_alg ) ) { MBEDTLS_SSL_DEBUG_MSG( 3, ( "certificate mismatch: key type" ) ); - continue; + match = 0; } +#if defined(MBEDTLS_ECDSA_C) + if( pk_alg == MBEDTLS_PK_ECDSA && + ssl_check_key_curve( pk, ssl->handshake->curves ) != 0 ) + { + MBEDTLS_SSL_DEBUG_MSG( 3, ( "certificate mismatch: elliptic curve" ) ); + match = 0; + } +#endif + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + mbedtls_x509_crt_pk_release( cur->cert ); +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + if( match == 0 ) + continue; + /* * This avoids sending the client a cert it'll reject based on * keyUsage or other extensions. @@ -816,31 +859,42 @@ static int ssl_pick_cert( mbedtls_ssl_context *ssl, continue; } -#if defined(MBEDTLS_ECDSA_C) - if( pk_alg == MBEDTLS_PK_ECDSA && - ssl_check_key_curve( &cur->cert->pk, ssl->handshake->curves ) != 0 ) - { - MBEDTLS_SSL_DEBUG_MSG( 3, ( "certificate mismatch: elliptic curve" ) ); - continue; - } -#endif - +#if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) || \ + defined(MBEDTLS_SSL_PROTO_TLS1_1) /* * Try to select a SHA-1 certificate for pre-1.2 clients, but still * present them a SHA-higher cert rather than failing if it's the only * one we got that satisfies the other conditions. */ - if( ssl->minor_ver < MBEDTLS_SSL_MINOR_VERSION_3 && - cur->cert->sig_md != MBEDTLS_MD_SHA1 ) + if( ssl->minor_ver < MBEDTLS_SSL_MINOR_VERSION_3 ) { - if( fallback == NULL ) - fallback = cur; + mbedtls_md_type_t sig_md; { + int ret; + mbedtls_x509_crt_frame const *frame; + ret = mbedtls_x509_crt_frame_acquire( cur->cert, &frame ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_frame_acquire", ret ); + return( ret ); + } + sig_md = frame->sig_md; + mbedtls_x509_crt_frame_release( cur->cert ); + } + + if( sig_md != MBEDTLS_MD_SHA1 ) + { + if( fallback == NULL ) + fallback = cur; + MBEDTLS_SSL_DEBUG_MSG( 3, ( "certificate not preferred: " - "sha-2 with pre-TLS 1.2 client" ) ); - continue; + "sha-2 with pre-TLS 1.2 client" ) ); + continue; } } +#endif /* MBEDTLS_SSL_PROTO_TLS1 || + MBEDTLS_SSL_PROTO_TLS1_1 || + MBEDTLS_SSL_PROTO_SSL3 */ /* If we get there, we got a winner */ break; @@ -2953,26 +3007,38 @@ static int ssl_write_certificate_request( mbedtls_ssl_context *ssl ) #endif crt = ssl->conf->ca_chain; - while( crt != NULL && crt->version != 0 ) + while( crt != NULL && crt->raw.p != NULL ) { - dn_size = crt->subject_raw.len; + mbedtls_x509_crt_frame const *frame; + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_frame_acquire", ret ); + return( ret ); + } + + dn_size = frame->subject_raw.len; if( end < p || (size_t)( end - p ) < dn_size || (size_t)( end - p ) < 2 + dn_size ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "skipping CAs: buffer too short" ) ); + mbedtls_x509_crt_frame_release( crt ); break; } *p++ = (unsigned char)( dn_size >> 8 ); *p++ = (unsigned char)( dn_size ); - memcpy( p, crt->subject_raw.p, dn_size ); + memcpy( p, frame->subject_raw.p, dn_size ); p += dn_size; MBEDTLS_SSL_DEBUG_BUF( 3, "requested DN", p - dn_size, dn_size ); total_dn_size += 2 + dn_size; + + mbedtls_x509_crt_frame_release( crt ); + crt = crt->next; } } @@ -3614,9 +3680,8 @@ static int ssl_decrypt_encrypted_pms( mbedtls_ssl_context *ssl, size_t peer_pmssize ) { int ret; + size_t len = (size_t)( end - p ); /* Cast is safe because p <= end. */ mbedtls_pk_context *private_key = mbedtls_ssl_own_key( ssl ); - mbedtls_pk_context *public_key = &mbedtls_ssl_own_cert( ssl )->pk; - size_t len = mbedtls_pk_get_len( public_key ); #if defined(MBEDTLS_SSL_ASYNC_PRIVATE) /* If we have already started decoding the message and there is an ongoing @@ -3634,12 +3699,17 @@ static int ssl_decrypt_encrypted_pms( mbedtls_ssl_context *ssl, */ #if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \ defined(MBEDTLS_SSL_PROTO_TLS1_2) +#if defined(MBEDTLS_SSL_PROTO_SSL3) if( ssl->minor_ver != MBEDTLS_SSL_MINOR_VERSION_0 ) +#endif /* MBEDTLS_SSL_PROTO_SSL3 */ { - if ( p + 2 > end ) { + if( len < 2 ) + { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); } + len -= 2; + if( *p++ != ( ( len >> 8 ) & 0xFF ) || *p++ != ( ( len ) & 0xFF ) ) { @@ -3649,12 +3719,6 @@ static int ssl_decrypt_encrypted_pms( mbedtls_ssl_context *ssl, } #endif - if( p + len != end ) - { - MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); - } - /* * Decrypt the premaster secret */ @@ -4194,7 +4258,15 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) peer_pk = &ssl->handshake->peer_pubkey; #else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ if( ssl->session_negotiate->peer_cert != NULL ) - peer_pk = &ssl->session_negotiate->peer_cert->pk; + { + ret = mbedtls_x509_crt_pk_acquire( ssl->session_negotiate->peer_cert, + &peer_pk ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_pk_acquire", ret ); + return( ret ); + } + } #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ if( peer_pk == NULL ) @@ -4209,7 +4281,7 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) if( 0 != ret ) { MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record" ), ret ); - return( ret ); + goto exit; } ssl->state++; @@ -4219,7 +4291,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) ssl->in_msg[0] != MBEDTLS_SSL_HS_CERTIFICATE_VERIFY ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY; + goto exit; } i = mbedtls_ssl_hs_hdr_len( ssl ); @@ -4254,7 +4327,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) if( i + 2 > ssl->in_hslen ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY; + goto exit; } /* @@ -4266,7 +4340,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "peer not adhering to requested sig_alg" " for verify message" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY; + goto exit; } #if !defined(MBEDTLS_MD_SHA1) @@ -4287,7 +4362,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "peer not adhering to requested sig_alg" " for verify message" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY; + goto exit; } /* @@ -4296,7 +4372,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) if( !mbedtls_pk_can_do( peer_pk, pk_alg ) ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "sig_alg doesn't match cert key" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY; + goto exit; } i++; @@ -4311,7 +4388,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) if( i + 2 > ssl->in_hslen ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY; + goto exit; } sig_len = ( ssl->in_msg[i] << 8 ) | ssl->in_msg[i+1]; @@ -4320,7 +4398,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) if( i + sig_len != ssl->in_hslen ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); - return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY; + goto exit; } /* Calculate hash and verify signature */ @@ -4334,13 +4413,19 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl ) ssl->in_msg + i, sig_len ) ) != 0 ) { MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_pk_verify", ret ); - return( ret ); + goto exit; } mbedtls_ssl_update_handshake_status( ssl ); MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= parse certificate verify" ) ); +exit: + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + mbedtls_x509_crt_pk_release( ssl->session_negotiate->peer_cert ); +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + return( ret ); } #endif /* MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED */ diff --git a/library/ssl_tls.c b/library/ssl_tls.c index fff20ff1b..4c1a5c52a 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -6456,6 +6456,7 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl, void *rs_ctx ) { int ret = 0; + int verify_ret; const mbedtls_ssl_ciphersuite_t *ciphersuite_info = ssl->handshake->ciphersuite_info; mbedtls_x509_crt *ca_chain; @@ -6480,7 +6481,7 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl, /* * Main check: verify certificate */ - ret = mbedtls_x509_crt_verify_restartable( + verify_ret = mbedtls_x509_crt_verify_restartable( chain, ca_chain, ca_crl, ssl->conf->cert_profile, @@ -6488,13 +6489,13 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl, &ssl->session_negotiate->verify_result, ssl->conf->f_vrfy, ssl->conf->p_vrfy, rs_ctx ); - if( ret != 0 ) + if( verify_ret != 0 ) { - MBEDTLS_SSL_DEBUG_RET( 1, "x509_verify_cert", ret ); + MBEDTLS_SSL_DEBUG_RET( 1, "x509_verify_cert", verify_ret ); } #if defined(MBEDTLS_SSL__ECP_RESTARTABLE) - if( ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) + if( verify_ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) return( MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS ); #endif @@ -6504,29 +6505,40 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl, #if defined(MBEDTLS_ECP_C) { - const mbedtls_pk_context *pk = &chain->pk; + mbedtls_pk_context *pk; + ret = mbedtls_x509_crt_pk_acquire( chain, &pk ); + if( ret != 0 ) + { + MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_x509_crt_pk_acquire", ret ); + return( ret ); + } /* If certificate uses an EC key, make sure the curve is OK */ - if( mbedtls_pk_can_do( pk, MBEDTLS_PK_ECKEY ) && - mbedtls_ssl_check_curve( ssl, mbedtls_pk_ec( *pk )->grp.id ) != 0 ) + if( mbedtls_pk_can_do( pk, MBEDTLS_PK_ECKEY ) ) + ret = mbedtls_ssl_check_curve( ssl, mbedtls_pk_ec( *pk )->grp.id ); + + mbedtls_x509_crt_pk_release( chain ); + + if( ret != 0 ) { ssl->session_negotiate->verify_result |= MBEDTLS_X509_BADCERT_BAD_KEY; MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate (EC key curve)" ) ); - if( ret == 0 ) - ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE; + if( verify_ret == 0 ) + verify_ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE; } } #endif /* MBEDTLS_ECP_C */ - if( mbedtls_ssl_check_cert_usage( chain, - ciphersuite_info, - ! ssl->conf->endpoint, - &ssl->session_negotiate->verify_result ) != 0 ) + ret = mbedtls_ssl_check_cert_usage( chain, + ciphersuite_info, + ! ssl->conf->endpoint, + &ssl->session_negotiate->verify_result ); + if( ret != 0 ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate (usage extensions)" ) ); - if( ret == 0 ) - ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE; + if( verify_ret == 0 ) + verify_ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE; } /* mbedtls_x509_crt_verify_with_profile is supposed to report a @@ -6536,19 +6548,19 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl, * functions, are treated as fatal and lead to a failure of * ssl_parse_certificate even if verification was optional. */ if( authmode == MBEDTLS_SSL_VERIFY_OPTIONAL && - ( ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED || - ret == MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE ) ) + ( verify_ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED || + verify_ret == MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE ) ) { - ret = 0; + verify_ret = 0; } if( ca_chain == NULL && authmode == MBEDTLS_SSL_VERIFY_REQUIRED ) { MBEDTLS_SSL_DEBUG_MSG( 1, ( "got no CA chain" ) ); - ret = MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED; + verify_ret = MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED; } - if( ret != 0 ) + if( verify_ret != 0 ) { uint8_t alert; @@ -6593,7 +6605,7 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl, } #endif /* MBEDTLS_DEBUG_C */ - return( ret ); + return( verify_ret ); } #if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) @@ -6760,8 +6772,8 @@ crt_verify: crt_len = chain->raw.len; #endif /* MBEDTLS_SSL_RENEGOTIATION */ - pk_start = chain->pk_raw.p; - pk_len = chain->pk_raw.len; + pk_start = chain->cache->pk_raw.p; + pk_len = chain->cache->pk_raw.len; /* Free the CRT structures before computing * digest and copying the peer's public key. */ diff --git a/library/version_features.c b/library/version_features.c index 5e9d9239b..8d3f2adb0 100644 --- a/library/version_features.c +++ b/library/version_features.c @@ -534,6 +534,12 @@ static const char *features[] = { #if defined(MBEDTLS_VERSION_FEATURES) "MBEDTLS_VERSION_FEATURES", #endif /* MBEDTLS_VERSION_FEATURES */ +#if defined(MBEDTLS_X509_ON_DEMAND_PARSING) + "MBEDTLS_X509_ON_DEMAND_PARSING", +#endif /* MBEDTLS_X509_ON_DEMAND_PARSING */ +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) + "MBEDTLS_X509_ALWAYS_FLUSH", +#endif /* MBEDTLS_X509_ALWAYS_FLUSH */ #if defined(MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3) "MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3", #endif /* MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3 */ diff --git a/library/x509.c b/library/x509.c index 858dd904e..89645a755 100644 --- a/library/x509.c +++ b/library/x509.c @@ -38,6 +38,7 @@ #if defined(MBEDTLS_X509_USE_C) #include "mbedtls/x509.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/asn1.h" #include "mbedtls/oid.h" @@ -347,64 +348,59 @@ int mbedtls_x509_get_rsassa_pss_params( const mbedtls_x509_buf *params, * AttributeType ::= OBJECT IDENTIFIER * * AttributeValue ::= ANY DEFINED BY AttributeType + * + * NOTE: This function returns an ASN.1 low-level error code. */ static int x509_get_attr_type_value( unsigned char **p, const unsigned char *end, - mbedtls_x509_name *cur ) + mbedtls_x509_buf *oid, + mbedtls_x509_buf *val ) { int ret; size_t len; - mbedtls_x509_buf *oid; - mbedtls_x509_buf *val; - if( ( ret = mbedtls_asn1_get_tag( p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_NAME + ret ); + ret = mbedtls_asn1_get_tag( p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE ); + if( ret != 0 ) + goto exit; end = *p + len; - if( ( end - *p ) < 1 ) - return( MBEDTLS_ERR_X509_INVALID_NAME + - MBEDTLS_ERR_ASN1_OUT_OF_DATA ); - - oid = &cur->oid; - oid->tag = **p; - - if( ( ret = mbedtls_asn1_get_tag( p, end, &oid->len, MBEDTLS_ASN1_OID ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_NAME + ret ); + ret = mbedtls_asn1_get_tag( p, end, &oid->len, MBEDTLS_ASN1_OID ); + if( ret != 0 ) + goto exit; + oid->tag = MBEDTLS_ASN1_OID; oid->p = *p; *p += oid->len; - if( ( end - *p ) < 1 ) - return( MBEDTLS_ERR_X509_INVALID_NAME + - MBEDTLS_ERR_ASN1_OUT_OF_DATA ); + if( *p == end ) + { + ret = MBEDTLS_ERR_ASN1_OUT_OF_DATA; + goto exit; + } - if( **p != MBEDTLS_ASN1_BMP_STRING && **p != MBEDTLS_ASN1_UTF8_STRING && - **p != MBEDTLS_ASN1_T61_STRING && **p != MBEDTLS_ASN1_PRINTABLE_STRING && - **p != MBEDTLS_ASN1_IA5_STRING && **p != MBEDTLS_ASN1_UNIVERSAL_STRING && - **p != MBEDTLS_ASN1_BIT_STRING ) - return( MBEDTLS_ERR_X509_INVALID_NAME + - MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); + if( !MBEDTLS_ASN1_IS_STRING_TAG( **p ) ) + { + ret = MBEDTLS_ERR_ASN1_UNEXPECTED_TAG; + goto exit; + } - val = &cur->val; val->tag = *(*p)++; - if( ( ret = mbedtls_asn1_get_len( p, end, &val->len ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_NAME + ret ); + ret = mbedtls_asn1_get_len( p, end, &val->len ); + if( ret != 0 ) + goto exit; val->p = *p; *p += val->len; if( *p != end ) - { - return( MBEDTLS_ERR_X509_INVALID_NAME + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - } + ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; - cur->next = NULL; - - return( 0 ); +exit: + return( ret ); } /* @@ -429,58 +425,235 @@ static int x509_get_attr_type_value( unsigned char **p, * For the general case we still use a flat list, but we mark elements of the * same set so that they are "merged" together in the functions that consume * this list, eg mbedtls_x509_dn_gets(). + * + * NOTE: This function returns an ASN.1 low-level error code. */ -int mbedtls_x509_get_name( unsigned char **p, const unsigned char *end, - mbedtls_x509_name *cur ) +static int x509_set_sequence_iterate( unsigned char **p, + unsigned char const **end_set, + unsigned char const *end, + mbedtls_x509_buf *oid, + mbedtls_x509_buf *val ) { int ret; size_t set_len; - const unsigned char *end_set; - /* don't use recursion, we'd risk stack overflow if not optimized */ - while( 1 ) + if( *p == *end_set ) { - /* - * parse SET - */ - if( ( ret = mbedtls_asn1_get_tag( p, end, &set_len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_NAME + ret ); + /* Parse next TLV of ASN.1 SET structure. */ + ret = mbedtls_asn1_get_tag( p, end, &set_len, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SET ); + if( ret != 0 ) + goto exit; - end_set = *p + set_len; + *end_set = *p + set_len; + } - while( 1 ) + /* x509_get_attr_type_value() returns ASN.1 low-level error codes. */ + ret = x509_get_attr_type_value( p, *end_set, oid, val ); + +exit: + return( ret ); +} + +/* + * Like memcmp, but case-insensitive and always returns -1 if different + */ +int mbedtls_x509_memcasecmp( const void *s1, const void *s2, + size_t len1, size_t len2 ) +{ + size_t i; + unsigned char diff; + const unsigned char *n1 = s1, *n2 = s2; + + if( len1 != len2 ) + return( -1 ); + + for( i = 0; i < len1; 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' ) ) ) { - if( ( ret = x509_get_attr_type_value( p, end_set, cur ) ) != 0 ) - return( ret ); - - if( *p == end_set ) - break; - - /* Mark this item as being no the only one in a set */ - cur->next_merged = 1; - - cur->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_name ) ); - - if( cur->next == NULL ) - return( MBEDTLS_ERR_X509_ALLOC_FAILED ); - - cur = cur->next; + continue; } - /* - * continue until end of SEQUENCE is reached - */ - if( *p == end ) - return( 0 ); + 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 ) && + mbedtls_x509_memcasecmp( a->p, b->p, + a->len, b->len ) == 0 ) + { + return( 0 ); + } + + return( -1 ); +} + +/* + * Compare two X.509 Names (aka rdnSequence) given as raw ASN.1 data. + * + * 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.) + * + * Further, this function allows to pass a callback to be triggered for every + * pair of well-formed and equal entries in the two input name lists. + * + * Returns: + * - 0 if both sequences are well-formed, present the same X.509 name, + * and the callback (if provided) hasn't returned a non-zero value + * on any of the name components. + * - 1 if a difference was detected in the name components. + * - A non-zero error code if the abort callback returns a non-zero value. + * In this case, the returned error code is the error code from the callback. + * - A negative error code if a parsing error occurred in either + * of the two buffers. + * + * This function can be used to verify that a buffer contains a well-formed + * ASN.1 encoded X.509 name by calling it with equal parameters. + */ +int mbedtls_x509_name_cmp_raw( mbedtls_x509_buf_raw const *a, + mbedtls_x509_buf_raw const *b, + int (*abort_check)( void *ctx, + mbedtls_x509_buf *oid, + mbedtls_x509_buf *val, + int next_merged ), + void *abort_check_ctx ) +{ + int ret; + size_t idx; + unsigned char *p[2], *end[2], *set[2]; + + p[0] = a->p; + p[1] = b->p; + end[0] = p[0] + a->len; + end[1] = p[1] + b->len; + + for( idx = 0; idx < 2; idx++ ) + { + size_t len; + ret = mbedtls_asn1_get_tag( &p[idx], end[idx], &len, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE ); + + if( end[idx] != p[idx] + len ) + { + return( MBEDTLS_ERR_X509_INVALID_NAME + + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + } + + set[idx] = p[idx]; + } + + while( 1 ) + { + int next_merged; + mbedtls_x509_buf oid[2], val[2]; + + ret = x509_set_sequence_iterate( &p[0], (const unsigned char **) &set[0], + end[0], &oid[0], &val[0] ); + if( ret != 0 ) + goto exit; + + ret = x509_set_sequence_iterate( &p[1], (const unsigned char **) &set[1], + end[1], &oid[1], &val[1] ); + if( ret != 0 ) + goto exit; + + if( oid[0].len != oid[1].len || + memcmp( oid[0].p, oid[1].p, oid[1].len ) != 0 ) + { + return( 1 ); + } + + if( x509_string_cmp( &val[0], &val[1] ) != 0 ) + return( 1 ); + + next_merged = ( set[0] != p[0] ); + if( next_merged != ( set[1] != p[1] ) ) + return( 1 ); + + if( abort_check != NULL ) + { + ret = abort_check( abort_check_ctx, &oid[0], &val[0], + next_merged ); + if( ret != 0 ) + return( ret ); + } + + if( p[0] == end[0] && p[1] == end[1] ) + break; + } + +exit: + if( ret < 0 ) + ret += MBEDTLS_ERR_X509_INVALID_NAME; + + return( ret ); +} + +static int x509_get_name_cb( void *ctx, + mbedtls_x509_buf *oid, + mbedtls_x509_buf *val, + int next_merged ) +{ + mbedtls_x509_name **cur_ptr = (mbedtls_x509_name**) ctx; + mbedtls_x509_name *cur = *cur_ptr; + + if( cur->oid.p != NULL ) + { cur->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_name ) ); - if( cur->next == NULL ) - return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + return( MBEDTLS_ERR_ASN1_ALLOC_FAILED ); cur = cur->next; } + + cur->oid = *oid; + cur->val = *val; + cur->next_merged = next_merged; + + *cur_ptr = cur; + return( 0 ); +} + +int mbedtls_x509_get_name( unsigned char *p, + size_t len, + mbedtls_x509_name *cur ) +{ + mbedtls_x509_buf_raw name_buf = { p, len }; + memset( cur, 0, sizeof( mbedtls_x509_name ) ); + return( mbedtls_x509_name_cmp_raw( &name_buf, &name_buf, + x509_get_name_cb, + &cur ) ); } static int x509_parse_int( unsigned char **p, size_t n, int *res ) @@ -655,6 +828,21 @@ int mbedtls_x509_get_sig( unsigned char **p, const unsigned char *end, mbedtls_x return( 0 ); } +int mbedtls_x509_get_sig_alg_raw( unsigned char **p, unsigned char const *end, + mbedtls_md_type_t *md_alg, + mbedtls_pk_type_t *pk_alg, + void **sig_opts ) +{ + int ret; + mbedtls_asn1_buf alg, params; + ret = mbedtls_asn1_get_alg( p, end, &alg, ¶ms ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_INVALID_ALG + ret ); + + return( mbedtls_x509_get_sig_alg( &alg, ¶ms, md_alg, + pk_alg, sig_opts ) ); +} + /* * Get signature algorithm from alg OID and optional parameters */ @@ -664,9 +852,6 @@ int mbedtls_x509_get_sig_alg( const mbedtls_x509_buf *sig_oid, const mbedtls_x50 { int ret; - if( *sig_opts != NULL ) - return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); - if( ( ret = mbedtls_oid_get_sig_alg( sig_oid, md_alg, pk_alg ) ) != 0 ) return( MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG + ret ); @@ -689,7 +874,10 @@ int mbedtls_x509_get_sig_alg( const mbedtls_x509_buf *sig_oid, const mbedtls_x50 return( ret ); } - *sig_opts = (void *) pss_opts; + if( sig_opts != NULL ) + *sig_opts = (void *) pss_opts; + else + mbedtls_free( pss_opts ); } else #endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ @@ -697,7 +885,10 @@ int mbedtls_x509_get_sig_alg( const mbedtls_x509_buf *sig_oid, const mbedtls_x50 /* Make sure parameters are absent or NULL */ if( ( sig_params->tag != MBEDTLS_ASN1_NULL && sig_params->tag != 0 ) || sig_params->len != 0 ) - return( MBEDTLS_ERR_X509_INVALID_ALG ); + return( MBEDTLS_ERR_X509_INVALID_ALG ); + + if( sig_opts != NULL ) + *sig_opts = NULL; } return( 0 ); @@ -840,20 +1031,34 @@ int mbedtls_x509_serial_gets( char *buf, size_t size, const mbedtls_x509_buf *se /* * Helper for writing signature algorithms */ -int mbedtls_x509_sig_alg_gets( char *buf, size_t size, const mbedtls_x509_buf *sig_oid, - mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg, - const void *sig_opts ) +int mbedtls_x509_sig_alg_gets( char *buf, size_t size, mbedtls_pk_type_t pk_alg, + mbedtls_md_type_t md_alg, const void *sig_opts ) { int ret; char *p = buf; size_t n = size; const char *desc = NULL; + mbedtls_x509_buf sig_oid; + mbedtls_md_type_t tmp_md_alg = md_alg; - ret = mbedtls_oid_get_sig_alg_desc( sig_oid, &desc ); - if( ret != 0 ) - ret = mbedtls_snprintf( p, n, "???" ); - else +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + /* The hash for RSASSA is determined by the algorithm parameters; + * in the OID list, the hash is set to MBEDTLS_MD_NONE. */ + if( pk_alg == MBEDTLS_PK_RSASSA_PSS ) + tmp_md_alg = MBEDTLS_MD_NONE; +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + + sig_oid.tag = MBEDTLS_ASN1_OID; + ret = mbedtls_oid_get_oid_by_sig_alg( pk_alg, tmp_md_alg, + (const char**) &sig_oid.p, + &sig_oid.len ); + if( ret == 0 && + mbedtls_oid_get_sig_alg_desc( &sig_oid, &desc ) == 0 ) + { ret = mbedtls_snprintf( p, n, "%s", desc ); + } + else + ret = mbedtls_snprintf( p, n, "???" ); MBEDTLS_X509_SAFE_SNPRINTF; #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) @@ -1003,6 +1208,17 @@ int mbedtls_x509_time_is_future( const mbedtls_x509_time *from ) } #endif /* MBEDTLS_HAVE_TIME_DATE */ +void mbedtls_x509_name_free( mbedtls_x509_name *name ) +{ + while( name != NULL ) + { + mbedtls_x509_name *next = name->next; + mbedtls_platform_zeroize( name, sizeof( *name ) ); + mbedtls_free( name ); + name = next; + } +} + #if defined(MBEDTLS_SELF_TEST) #include "mbedtls/x509_crt.h" diff --git a/library/x509_create.c b/library/x509_create.c index 546e8fa1a..1639630a2 100644 --- a/library/x509_create.c +++ b/library/x509_create.c @@ -28,6 +28,7 @@ #if defined(MBEDTLS_X509_CREATE_C) #include "mbedtls/x509.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/asn1write.h" #include "mbedtls/oid.h" diff --git a/library/x509_crl.c b/library/x509_crl.c index 4f5507f0e..3113de42c 100644 --- a/library/x509_crl.c +++ b/library/x509_crl.c @@ -38,6 +38,7 @@ #if defined(MBEDTLS_X509_CRL_PARSE_C) #include "mbedtls/x509_crl.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/oid.h" #include "mbedtls/platform_util.h" @@ -428,15 +429,17 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain, mbedtls_x509_crl_free( crl ); return( MBEDTLS_ERR_X509_INVALID_FORMAT + ret ); } + p += len; + crl->issuer_raw.len = p - crl->issuer_raw.p; - if( ( ret = mbedtls_x509_get_name( &p, p + len, &crl->issuer ) ) != 0 ) + if( ( ret = mbedtls_x509_get_name( crl->issuer_raw.p, + crl->issuer_raw.len, + &crl->issuer ) ) != 0 ) { mbedtls_x509_crl_free( crl ); return( ret ); } - crl->issuer_raw.len = p - crl->issuer_raw.p; - /* * thisUpdate Time * nextUpdate Time OPTIONAL @@ -690,8 +693,8 @@ int mbedtls_x509_crl_info( char *buf, size_t size, const char *prefix, ret = mbedtls_snprintf( p, n, "\n%ssigned using : ", prefix ); MBEDTLS_X509_SAFE_SNPRINTF; - ret = mbedtls_x509_sig_alg_gets( p, n, &crl->sig_oid, crl->sig_pk, crl->sig_md, - crl->sig_opts ); + ret = mbedtls_x509_sig_alg_gets( p, n, crl->sig_pk, + crl->sig_md, crl->sig_opts ); MBEDTLS_X509_SAFE_SNPRINTF; ret = mbedtls_snprintf( p, n, "\n" ); diff --git a/library/x509_crt.c b/library/x509_crt.c index e4a35f64d..61e4a9e22 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -40,6 +40,7 @@ #if defined(MBEDTLS_X509_CRT_PARSE_C) #include "mbedtls/x509_crt.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/oid.h" #include "mbedtls/platform_util.h" @@ -78,6 +79,385 @@ #endif /* !_WIN32 || EFIX64 || EFI32 */ #endif +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) +static void x509_buf_to_buf_raw( mbedtls_x509_buf_raw *dst, + mbedtls_x509_buf const *src ) +{ + dst->p = src->p; + dst->len = src->len; +} + +static void x509_buf_raw_to_buf( mbedtls_x509_buf *dst, + mbedtls_x509_buf_raw const *src ) +{ + dst->p = src->p; + dst->len = src->len; +} +#endif /* MBEDTLS_X509_ON_DEMAND_PARSING */ + +static int x509_crt_parse_frame( unsigned char *start, + unsigned char *end, + mbedtls_x509_crt_frame *frame ); +static int x509_crt_subject_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_name *subject ); +static int x509_crt_issuer_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_name *issuer ); +static int x509_crt_subject_alt_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_sequence *subject_alt ); +static int x509_crt_ext_key_usage_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_sequence *ext_key_usage ); + +int mbedtls_x509_crt_flush_cache_pk( mbedtls_x509_crt const *crt ) +{ +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_lock( &crt->cache->pk_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + /* Can only free the PK context if nobody is using it. + * If MBEDTLS_X509_ALWAYS_FLUSH is set, nested uses + * of xxx_acquire() are prohibited, and no reference + * counting is needed. Also, notice that the code-path + * below is safe if the cache isn't filled. */ + if( crt->cache->pk_readers == 0 ) +#endif /* !MBEDTLS_X509_ALWAYS_FLUSH || + MBEDTLS_THREADING_C */ + { +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) + /* The cache holds a shallow copy of the PK context + * in the legacy struct, so don't free PK context. */ + mbedtls_free( crt->cache->pk ); +#else + mbedtls_pk_free( crt->cache->pk ); + mbedtls_free( crt->cache->pk ); +#endif /* MBEDTLS_X509_ON_DEMAND_PARSING */ + crt->cache->pk = NULL; + } + +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_unlock( &crt->cache->pk_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif + return( 0 ); +} + +int mbedtls_x509_crt_flush_cache_frame( mbedtls_x509_crt const *crt ) +{ +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_lock( &crt->cache->frame_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif + +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + /* Can only free the PK context if nobody is using it. + * If MBEDTLS_X509_ALWAYS_FLUSH is set, nested uses + * of xxx_acquire() are prohibited, and no reference + * counting is needed. Also, notice that the code-path + * below is safe if the cache isn't filled. */ + if( crt->cache->frame_readers == 0 ) +#endif /* !MBEDTLS_X509_ALWAYS_FLUSH || + MBEDTLS_THREADING_C */ + { + mbedtls_free( crt->cache->frame ); + crt->cache->frame = NULL; + } + +#if defined(MBEDTLS_THREADING_C) + if( mbedtls_mutex_unlock( &crt->cache->frame_mutex ) != 0 ) + return( MBEDTLS_ERR_THREADING_MUTEX_ERROR ); +#endif + return( 0 ); +} + +int mbedtls_x509_crt_flush_cache( mbedtls_x509_crt const *crt ) +{ + int ret; + ret = mbedtls_x509_crt_flush_cache_frame( crt ); + if( ret != 0 ) + return( ret ); + ret = mbedtls_x509_crt_flush_cache_pk( crt ); + if( ret != 0 ) + return( ret ); + return( 0 ); +} + +static int x509_crt_frame_parse_ext( mbedtls_x509_crt_frame *frame ); + +int mbedtls_x509_crt_cache_provide_frame( mbedtls_x509_crt const *crt ) +{ + mbedtls_x509_crt_cache *cache = crt->cache; + mbedtls_x509_crt_frame *frame; + + if( cache->frame != NULL ) + { +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + return( 0 ); +#else + /* If MBEDTLS_X509_ALWAYS_FLUSH is set, we don't + * allow nested uses of acquire. */ + return( MBEDTLS_ERR_X509_FATAL_ERROR ); +#endif + } + + frame = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt_frame ) ); + if( frame == NULL ) + return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + cache->frame = frame; + +#if defined(MBEDTLS_X509_ON_DEMAND_PARSING) + /* This would work with !MBEDTLS_X509_ON_DEMAND_PARSING, too, + * but is inefficient compared to copying the respective fields + * from the legacy mbedtls_x509_crt. */ + return( x509_crt_parse_frame( crt->raw.p, + crt->raw.p + crt->raw.len, + frame ) ); +#else /* MBEDTLS_X509_ON_DEMAND_PARSING */ + /* Make sure all extension related fields are properly initialized. */ + frame->ca_istrue = 0; + frame->max_pathlen = 0; + frame->ext_types = 0; + frame->version = crt->version; + frame->sig_md = crt->sig_md; + frame->sig_pk = crt->sig_pk; + frame->valid_from = crt->valid_from; + frame->valid_to = crt->valid_to; + x509_buf_to_buf_raw( &frame->raw, &crt->raw ); + x509_buf_to_buf_raw( &frame->tbs, &crt->tbs ); + x509_buf_to_buf_raw( &frame->serial, &crt->serial ); + x509_buf_to_buf_raw( &frame->pubkey_raw, &crt->pk_raw ); + x509_buf_to_buf_raw( &frame->issuer_raw, &crt->issuer_raw ); + x509_buf_to_buf_raw( &frame->subject_raw, &crt->subject_raw ); + x509_buf_to_buf_raw( &frame->subject_id, &crt->subject_id ); + x509_buf_to_buf_raw( &frame->issuer_id, &crt->issuer_id ); + x509_buf_to_buf_raw( &frame->sig, &crt->sig ); + x509_buf_to_buf_raw( &frame->v3_ext, &crt->v3_ext ); + + /* The legacy CRT structure doesn't explicitly contain + * the `AlgorithmIdentifier` bounds; however, those can + * be inferred from the surrounding (mandatory) `SerialNumber` + * and `Issuer` fields. */ + frame->sig_alg.p = crt->serial.p + crt->serial.len; + frame->sig_alg.len = crt->issuer_raw.p - frame->sig_alg.p; + + return( x509_crt_frame_parse_ext( frame ) ); +#endif /* !MBEDTLS_X509_ON_DEMAND_PARSING */ +} + +int mbedtls_x509_crt_cache_provide_pk( mbedtls_x509_crt const *crt ) +{ + mbedtls_x509_crt_cache *cache = crt->cache; + mbedtls_pk_context *pk; + + if( cache->pk != NULL ) + { +#if !defined(MBEDTLS_X509_ALWAYS_FLUSH) || \ + defined(MBEDTLS_THREADING_C) + return( 0 ); +#else + /* If MBEDTLS_X509_ALWAYS_FLUSH is set, we don't + * allow nested uses of acquire. */ + return( MBEDTLS_ERR_X509_FATAL_ERROR ); +#endif + } + + pk = mbedtls_calloc( 1, sizeof( mbedtls_pk_context ) ); + if( pk == NULL ) + return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + cache->pk = pk; + +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) + *pk = crt->pk; + return( 0 ); +#else + { + mbedtls_x509_buf_raw pk_raw = cache->pk_raw; + return( mbedtls_pk_parse_subpubkey( &pk_raw.p, + pk_raw.p + pk_raw.len, + pk ) ); + } +#endif /* MBEDTLS_X509_ON_DEMAND_PARSING */ +} + +static void x509_crt_cache_init( mbedtls_x509_crt_cache *cache ) +{ + memset( cache, 0, sizeof( *cache ) ); +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init( &cache->frame_mutex ); + mbedtls_mutex_init( &cache->pk_mutex ); +#endif +} + +static void x509_crt_cache_clear_pk( mbedtls_x509_crt_cache *cache ) +{ +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) + /* The cache holds a shallow copy of the PK context + * in the legacy struct, so don't free PK context. */ + mbedtls_free( cache->pk ); +#else + mbedtls_pk_free( cache->pk ); + mbedtls_free( cache->pk ); +#endif /* MBEDTLS_X509_ON_DEMAND_PARSING */ + + cache->pk = NULL; +} + +static void x509_crt_cache_clear_frame( mbedtls_x509_crt_cache *cache ) +{ + mbedtls_free( cache->frame ); + cache->frame = NULL; +} + +static void x509_crt_cache_free( mbedtls_x509_crt_cache *cache ) +{ + if( cache == NULL ) + return; + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_free( &cache->frame_mutex ); + mbedtls_mutex_free( &cache->pk_mutex ); +#endif + + x509_crt_cache_clear_frame( cache ); + x509_crt_cache_clear_pk( cache ); + + memset( cache, 0, sizeof( *cache ) ); +} + +int mbedtls_x509_crt_get_subject_alt_names( mbedtls_x509_crt const *crt, + mbedtls_x509_sequence **subj_alt ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + mbedtls_x509_sequence *seq; + + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( ret ); + + seq = mbedtls_calloc( 1, sizeof( mbedtls_x509_sequence ) ); + if( seq == NULL ) + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + else + ret = x509_crt_subject_alt_from_frame( frame, seq ); + + mbedtls_x509_crt_frame_release( crt ); + + *subj_alt = seq; + return( ret ); +} + +int mbedtls_x509_crt_get_ext_key_usage( mbedtls_x509_crt const *crt, + mbedtls_x509_sequence **ext_key_usage ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + mbedtls_x509_sequence *seq; + + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( ret ); + + seq = mbedtls_calloc( 1, sizeof( mbedtls_x509_sequence ) ); + if( seq == NULL ) + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + else + ret = x509_crt_ext_key_usage_from_frame( frame, seq ); + + mbedtls_x509_crt_frame_release( crt ); + + *ext_key_usage = seq; + return( ret ); +} + +int mbedtls_x509_crt_get_subject( mbedtls_x509_crt const *crt, + mbedtls_x509_name **subject ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + mbedtls_x509_name *name; + + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( ret ); + + name = mbedtls_calloc( 1, sizeof( mbedtls_x509_name ) ); + if( name == NULL ) + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + else + ret = x509_crt_subject_from_frame( frame, name ); + + mbedtls_x509_crt_frame_release( crt ); + + *subject = name; + return( ret ); +} + +int mbedtls_x509_crt_get_issuer( mbedtls_x509_crt const *crt, + mbedtls_x509_name **issuer ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + mbedtls_x509_name *name; + + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( ret ); + + name = mbedtls_calloc( 1, sizeof( mbedtls_x509_name ) ); + if( name == NULL ) + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + else + ret = x509_crt_issuer_from_frame( frame, name ); + + mbedtls_x509_crt_frame_release( crt ); + + *issuer = name; + return( ret ); +} + +int mbedtls_x509_crt_get_frame( mbedtls_x509_crt const *crt, + mbedtls_x509_crt_frame *dst ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( ret ); + *dst = *frame; + mbedtls_x509_crt_frame_release( crt ); + return( 0 ); +} + +int mbedtls_x509_crt_get_pk( mbedtls_x509_crt const *crt, + mbedtls_pk_context *dst ) +{ +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) + mbedtls_x509_buf_raw pk_raw = crt->cache->pk_raw; + return( mbedtls_pk_parse_subpubkey( &pk_raw.p, + pk_raw.p + pk_raw.len, + dst ) ); +#else /* !MBEDTLS_X509_ON_DEMAND_PARSING */ + int ret; + mbedtls_pk_context *pk; + ret = mbedtls_x509_crt_pk_acquire( crt, &pk ); + if( ret != 0 ) + return( ret ); + + /* Move PK from CRT cache to destination pointer + * to avoid a copy. */ + *dst = *pk; + mbedtls_free( crt->cache->pk ); + crt->cache->pk = NULL; + + mbedtls_x509_crt_pk_release( crt ); + return( 0 ); +#endif /* MBEDTLS_X509_ON_DEMAND_PARSING */ +} + /* * Item in a verification chain: cert and flags for it */ @@ -227,45 +607,19 @@ static int x509_profile_check_key( const mbedtls_x509_crt_profile *profile, return( -1 ); } -/* - * 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 */ -static int x509_check_wildcard( const char *cn, const mbedtls_x509_buf *name ) +static int x509_check_wildcard( char const *cn, + size_t cn_len, + unsigned char const *buf, + size_t buf_len ) { size_t i; - size_t cn_idx = 0, cn_len = strlen( cn ); + size_t cn_idx = 0; /* We can't have a match if there is no wildcard to match */ - if( name->len < 3 || name->p[0] != '*' || name->p[1] != '.' ) + if( buf_len < 3 || buf[0] != '*' || buf[1] != '.' ) return( -1 ); for( i = 0; i < cn_len; ++i ) @@ -280,8 +634,8 @@ static int x509_check_wildcard( const char *cn, const mbedtls_x509_buf *name ) if( cn_idx == 0 ) return( -1 ); - if( cn_len - cn_idx == name->len - 1 && - x509_memcasecmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 ) + if( mbedtls_x509_memcasecmp( buf + 1, cn + cn_idx, + buf_len - 1, cn_len - cn_idx ) == 0 ) { return( 0 ); } @@ -289,74 +643,6 @@ static int x509_check_wildcard( const char *cn, const 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 ); -} - /* * Reset (init or clear) a verify_chain */ @@ -445,15 +731,13 @@ static int x509_get_dates( unsigned char **p, */ static int x509_get_uid( unsigned char **p, const unsigned char *end, - mbedtls_x509_buf *uid, int n ) + mbedtls_x509_buf_raw *uid, int n ) { int ret; if( *p == end ) return( 0 ); - uid->tag = **p; - if( ( ret = mbedtls_asn1_get_tag( p, end, &uid->len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | n ) ) != 0 ) { @@ -487,7 +771,7 @@ static int x509_get_basic_constraints( unsigned char **p, if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + return( ret ); if( *p == end ) return( 0 ); @@ -498,7 +782,7 @@ static int x509_get_basic_constraints( unsigned char **p, ret = mbedtls_asn1_get_int( p, end, ca_istrue ); if( ret != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + return( ret ); if( *ca_istrue != 0 ) *ca_istrue = 1; @@ -508,11 +792,10 @@ static int x509_get_basic_constraints( unsigned char **p, return( 0 ); if( ( ret = mbedtls_asn1_get_int( p, end, max_pathlen ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + return( ret ); if( *p != end ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); (*max_pathlen)++; @@ -527,11 +810,10 @@ static int x509_get_ns_cert_type( unsigned char **p, mbedtls_x509_bitstring bs = { 0, 0, NULL }; if( ( ret = mbedtls_asn1_get_bitstring( p, end, &bs ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + return( ret ); if( bs.len != 1 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); /* Get actual bitstring */ *ns_cert_type = *bs.p; @@ -540,29 +822,53 @@ static int x509_get_ns_cert_type( unsigned char **p, static int x509_get_key_usage( unsigned char **p, const unsigned char *end, - unsigned int *key_usage) + uint16_t *key_usage) { int ret; size_t i; mbedtls_x509_bitstring bs = { 0, 0, NULL }; if( ( ret = mbedtls_asn1_get_bitstring( p, end, &bs ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + return( ret ); if( bs.len < 1 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_INVALID_LENGTH ); + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); /* Get actual bitstring */ *key_usage = 0; - for( i = 0; i < bs.len && i < sizeof( unsigned int ); i++ ) + for( i = 0; i < bs.len && i < sizeof( *key_usage ); i++ ) { - *key_usage |= (unsigned int) bs.p[i] << (8*i); + *key_usage |= (uint16_t) bs.p[i] << ( 8*i ); } return( 0 ); } +static int asn1_build_sequence_cb( void *ctx, + int tag, + unsigned char *data, + size_t data_len ) +{ + mbedtls_asn1_sequence **cur_ptr = (mbedtls_asn1_sequence **) ctx; + mbedtls_asn1_sequence *cur = *cur_ptr; + + /* Allocate and assign next pointer */ + if( cur->buf.p != NULL ) + { + cur->next = mbedtls_calloc( 1, sizeof( mbedtls_asn1_sequence ) ); + if( cur->next == NULL ) + return( MBEDTLS_ERR_ASN1_ALLOC_FAILED ); + cur = cur->next; + } + + cur->buf.tag = tag; + cur->buf.p = data; + cur->buf.len = data_len; + + *cur_ptr = cur; + return( 0 ); +} + /* * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId * @@ -572,17 +878,11 @@ static int x509_get_ext_key_usage( unsigned char **p, const unsigned char *end, mbedtls_x509_sequence *ext_key_usage) { - int ret; - - if( ( ret = mbedtls_asn1_get_sequence_of( p, end, ext_key_usage, MBEDTLS_ASN1_OID ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - - /* Sequence length must be >= 1 */ - if( ext_key_usage->buf.p == NULL ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_INVALID_LENGTH ); - - return( 0 ); + return( mbedtls_asn1_traverse_sequence_of( p, end, + 0xFF, MBEDTLS_ASN1_OID, + 0, 0, + asn1_build_sequence_cb, + (void *) &ext_key_usage ) ); } /* @@ -611,220 +911,551 @@ static int x509_get_ext_key_usage( unsigned char **p, * * NOTE: we only parse and use dNSName at this point. */ -static int x509_get_subject_alt_name( unsigned char **p, +static int x509_get_subject_alt_name( unsigned char *p, const unsigned char *end, mbedtls_x509_sequence *subject_alt_name ) { - int ret; - size_t len, tag_len; - mbedtls_asn1_buf *buf; - unsigned char tag; - mbedtls_asn1_sequence *cur = subject_alt_name; - - /* Get main sequence tag */ - if( ( ret = mbedtls_asn1_get_tag( p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - - if( *p + len != end ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - - while( *p < end ) - { - if( ( end - *p ) < 1 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_OUT_OF_DATA ); - - tag = **p; - (*p)++; - if( ( ret = mbedtls_asn1_get_len( p, end, &tag_len ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - - if( ( tag & MBEDTLS_ASN1_TAG_CLASS_MASK ) != - MBEDTLS_ASN1_CONTEXT_SPECIFIC ) - { - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); - } - - /* Skip everything but DNS name */ - if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) ) - { - *p += tag_len; - continue; - } - - /* Allocate and assign next pointer */ - if( cur->buf.p != NULL ) - { - if( cur->next != NULL ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS ); - - cur->next = mbedtls_calloc( 1, sizeof( mbedtls_asn1_sequence ) ); - - if( cur->next == NULL ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_ALLOC_FAILED ); - - cur = cur->next; - } - - buf = &(cur->buf); - buf->tag = tag; - buf->p = *p; - buf->len = tag_len; - *p += buf->len; - } - - /* Set final sequence entry's next pointer to NULL */ - cur->next = NULL; - - if( *p != end ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - - return( 0 ); + return( mbedtls_asn1_traverse_sequence_of( &p, end, + MBEDTLS_ASN1_TAG_CLASS_MASK, + MBEDTLS_ASN1_CONTEXT_SPECIFIC, + MBEDTLS_ASN1_TAG_VALUE_MASK, + 2 /* SubjectAlt DNS */, + asn1_build_sequence_cb, + (void *) &subject_alt_name ) ); } /* * X.509 v3 extensions * */ -static int x509_get_crt_ext( unsigned char **p, - const unsigned char *end, - mbedtls_x509_crt *crt ) +static int x509_crt_get_ext_cb( void *ctx, + int tag, + unsigned char *p, + size_t ext_len ) { int ret; + mbedtls_x509_crt_frame *frame = (mbedtls_x509_crt_frame *) ctx; size_t len; - unsigned char *end_ext_data, *end_ext_octet; + unsigned char *end, *end_ext_octet; + mbedtls_x509_buf extn_oid = { 0, 0, NULL }; + int is_critical = 0; /* DEFAULT FALSE */ + int ext_type = 0; - if( *p == end ) - return( 0 ); + ((void) tag); - if( ( ret = mbedtls_x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 ) - return( ret ); + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ - end = crt->v3_ext.p + crt->v3_ext.len; - while( *p < end ) + end = p + ext_len; + + /* Get extension ID */ + if( ( ret = mbedtls_asn1_get_tag( &p, end, &extn_oid.len, + MBEDTLS_ASN1_OID ) ) != 0 ) + goto err; + + extn_oid.tag = MBEDTLS_ASN1_OID; + extn_oid.p = p; + p += extn_oid.len; + + /* Get optional critical */ + if( ( ret = mbedtls_asn1_get_bool( &p, end, &is_critical ) ) != 0 && + ( ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) ) + goto err; + + /* Data should be octet string type */ + if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_OCTET_STRING ) ) != 0 ) + goto err; + + end_ext_octet = p + len; + if( end_ext_octet != end ) { - /* - * Extension ::= SEQUENCE { - * extnID OBJECT IDENTIFIER, - * critical BOOLEAN DEFAULT FALSE, - * extnValue OCTET STRING } - */ - mbedtls_x509_buf extn_oid = {0, 0, NULL}; - int is_critical = 0; /* DEFAULT FALSE */ - int ext_type = 0; - - if( ( ret = mbedtls_asn1_get_tag( p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - - end_ext_data = *p + len; - - /* Get extension ID */ - if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &extn_oid.len, - MBEDTLS_ASN1_OID ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - - extn_oid.tag = MBEDTLS_ASN1_OID; - extn_oid.p = *p; - *p += extn_oid.len; - - /* Get optional critical */ - if( ( ret = mbedtls_asn1_get_bool( p, end_ext_data, &is_critical ) ) != 0 && - ( ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - - /* Data should be octet string type */ - if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len, - MBEDTLS_ASN1_OCTET_STRING ) ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); - - end_ext_octet = *p + len; - - if( end_ext_octet != end_ext_data ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - - /* - * Detect supported extensions - */ - ret = mbedtls_oid_get_x509_ext_type( &extn_oid, &ext_type ); - - if( ret != 0 ) - { - /* No parser found, skip extension */ - *p = end_ext_octet; + ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + goto err; + } + /* + * Detect supported extensions + */ + ret = mbedtls_oid_get_x509_ext_type( &extn_oid, &ext_type ); + if( ret != 0 ) + { #if !defined(MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) - if( is_critical ) - { - /* Data is marked as critical: fail */ - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + - MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); - } -#endif - continue; - } - - /* Forbid repeated extensions */ - if( ( crt->ext_types & ext_type ) != 0 ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS ); - - crt->ext_types |= ext_type; - - switch( ext_type ) + if( is_critical ) { + /* Data is marked as critical: fail */ + ret = MBEDTLS_ERR_ASN1_UNEXPECTED_TAG; + goto err; + } +#endif /* MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION */ + return( 0 ); + } + + /* Forbid repeated extensions */ + if( ( frame->ext_types & ext_type ) != 0 ) + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS ); + + frame->ext_types |= ext_type; + switch( ext_type ) + { case MBEDTLS_X509_EXT_BASIC_CONSTRAINTS: + { + int ca_istrue; + int max_pathlen; + /* Parse basic constraints */ - if( ( ret = x509_get_basic_constraints( p, end_ext_octet, - &crt->ca_istrue, &crt->max_pathlen ) ) != 0 ) - return( ret ); + ret = x509_get_basic_constraints( &p, end_ext_octet, + &ca_istrue, + &max_pathlen ); + if( ret != 0 ) + goto err; + + frame->ca_istrue = ca_istrue; + frame->max_pathlen = max_pathlen; break; + } case MBEDTLS_X509_EXT_KEY_USAGE: /* Parse key usage */ - if( ( ret = x509_get_key_usage( p, end_ext_octet, - &crt->key_usage ) ) != 0 ) - return( ret ); + ret = x509_get_key_usage( &p, end_ext_octet, + &frame->key_usage ); + if( ret != 0 ) + goto err; + break; + + case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME: + /* Copy reference to raw subject alt name data. */ + frame->subject_alt_raw.p = p; + frame->subject_alt_raw.len = end_ext_octet - p; + + ret = mbedtls_asn1_traverse_sequence_of( &p, end_ext_octet, + MBEDTLS_ASN1_TAG_CLASS_MASK, + MBEDTLS_ASN1_CONTEXT_SPECIFIC, + MBEDTLS_ASN1_TAG_VALUE_MASK, + 2 /* SubjectAlt DNS */, + NULL, NULL ); + if( ret != 0 ) + goto err; break; case MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE: /* Parse extended key usage */ - if( ( ret = x509_get_ext_key_usage( p, end_ext_octet, - &crt->ext_key_usage ) ) != 0 ) - return( ret ); - break; + frame->ext_key_usage_raw.p = p; + frame->ext_key_usage_raw.len = end_ext_octet - p; + if( frame->ext_key_usage_raw.len == 0 ) + { + ret = MBEDTLS_ERR_ASN1_INVALID_LENGTH; + goto err; + } + + /* Check structural sanity of extension. */ + ret = mbedtls_asn1_traverse_sequence_of( &p, end_ext_octet, + 0xFF, MBEDTLS_ASN1_OID, + 0, 0, NULL, NULL ); + if( ret != 0 ) + goto err; - case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME: - /* Parse subject alt name */ - if( ( ret = x509_get_subject_alt_name( p, end_ext_octet, - &crt->subject_alt_names ) ) != 0 ) - return( ret ); break; case MBEDTLS_X509_EXT_NS_CERT_TYPE: /* Parse netscape certificate type */ - if( ( ret = x509_get_ns_cert_type( p, end_ext_octet, - &crt->ns_cert_type ) ) != 0 ) - return( ret ); + ret = x509_get_ns_cert_type( &p, end_ext_octet, + &frame->ns_cert_type ); + if( ret != 0 ) + goto err; break; default: - return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE ); - } + /* + * If this is a non-critical extension, which the oid layer + * supports, but there isn't an X.509 parser for it, + * skip the extension. + */ +#if !defined(MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) + if( is_critical ) + return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE ); +#endif + p = end_ext_octet; } - if( *p != end ) - return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + + return( 0 ); + +err: + return( ret ); +} + +static int x509_crt_frame_parse_ext( mbedtls_x509_crt_frame *frame ) +{ + int ret; + unsigned char *p = frame->v3_ext.p; + unsigned char *end = p + frame->v3_ext.len; + + if( p == end ) + return( 0 ); + + ret = mbedtls_asn1_traverse_sequence_of( &p, end, + 0xFF, MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED, + 0, 0, x509_crt_get_ext_cb, frame ); + + if( ret == MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE ) + return( ret ); + if( ret == MBEDTLS_ERR_X509_INVALID_EXTENSIONS ) + return( ret ); + + if( ret != 0 ) + ret += MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + + return( ret ); +} + +static int x509_crt_parse_frame( unsigned char *start, + unsigned char *end, + mbedtls_x509_crt_frame *frame ) +{ + int ret; + unsigned char *p; + size_t len; + + mbedtls_x509_buf tmp; + unsigned char *tbs_start; + + mbedtls_x509_buf outer_sig_alg; + size_t inner_sig_alg_len; + unsigned char *inner_sig_alg_start; + + memset( frame, 0, sizeof( *frame ) ); + + /* + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + * } + * + */ + p = start; + + frame->raw.p = p; + if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + { + return( MBEDTLS_ERR_X509_INVALID_FORMAT ); + } + + /* NOTE: We are currently not checking that the `Certificate` + * structure spans the entire buffer. */ + end = p + len; + frame->raw.len = end - frame->raw.p; + + /* + * TBSCertificate ::= SEQUENCE { ... + */ + frame->tbs.p = p; + if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + { + return( ret + MBEDTLS_ERR_X509_INVALID_FORMAT ); + } + tbs_start = p; + + /* Breadth-first parsing: Jump over TBS for now. */ + p += len; + frame->tbs.len = p - frame->tbs.p; + + /* + * AlgorithmIdentifier ::= SEQUENCE { ... + */ + outer_sig_alg.p = p; + if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + { + return( MBEDTLS_ERR_X509_INVALID_ALG + ret ); + } + p += len; + outer_sig_alg.len = p - outer_sig_alg.p; + + /* + * signatureValue BIT STRING + */ + ret = mbedtls_x509_get_sig( &p, end, &tmp ); + if( ret != 0 ) + return( ret ); + frame->sig.p = tmp.p; + frame->sig.len = tmp.len; + + /* Check that we consumed the entire `Certificate` structure. */ + if( p != end ) + { + return( MBEDTLS_ERR_X509_INVALID_FORMAT + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + } + + /* Parse TBSCertificate structure + * + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version MUST be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version MUST be v2 or v3 + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version MUST be v3 + * } + */ + end = frame->tbs.p + frame->tbs.len; + p = tbs_start; + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + { + int version; + ret = x509_get_version( &p, end, &version ); + if( ret != 0 ) + return( ret ); + + if( version < 0 || version > 2 ) + return( MBEDTLS_ERR_X509_UNKNOWN_VERSION ); + + frame->version = version + 1; + } + + /* + * CertificateSerialNumber ::= INTEGER + */ + ret = mbedtls_x509_get_serial( &p, end, &tmp ); + if( ret != 0 ) + return( ret ); + + frame->serial.p = tmp.p; + frame->serial.len = tmp.len; + + /* + * signature AlgorithmIdentifier + */ + inner_sig_alg_start = p; + ret = mbedtls_x509_get_sig_alg_raw( &p, end, &frame->sig_md, + &frame->sig_pk, NULL ); + if( ret != 0 ) + return( ret ); + inner_sig_alg_len = p - inner_sig_alg_start; + + frame->sig_alg.p = inner_sig_alg_start; + frame->sig_alg.len = inner_sig_alg_len; + + /* Consistency check: + * Inner and outer AlgorithmIdentifier structures must coincide: + * + * Quoting RFC 5280, Section 4.1.1.2: + * This field MUST contain the same algorithm identifier as the + * signature field in the sequence tbsCertificate (Section 4.1.2.3). + */ + if( outer_sig_alg.len != inner_sig_alg_len || + memcmp( outer_sig_alg.p, inner_sig_alg_start, inner_sig_alg_len ) != 0 ) + { + return( MBEDTLS_ERR_X509_SIG_MISMATCH ); + } + + /* + * issuer Name + * + * Name ::= CHOICE { -- only one possibility for now -- + * rdnSequence RDNSequence } + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + */ + frame->issuer_raw.p = p; + + ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); + if( ret != 0 ) + return( ret + MBEDTLS_ERR_X509_INVALID_FORMAT ); + p += len; + frame->issuer_raw.len = p - frame->issuer_raw.p; + + /* Comparing the raw buffer to itself amounts to structural validation. */ + ret = mbedtls_x509_name_cmp_raw( &frame->issuer_raw, + &frame->issuer_raw, + NULL, NULL ); + if( ret != 0 ) + return( ret ); + + /* + * Validity ::= SEQUENCE { ... + */ + ret = x509_get_dates( &p, end, &frame->valid_from, &frame->valid_to ); + if( ret != 0 ) + return( ret ); + + /* + * subject Name + * + * Name ::= CHOICE { -- only one possibility for now -- + * rdnSequence RDNSequence } + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + */ + frame->subject_raw.p = p; + + ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); + if( ret != 0 ) + return( ret + MBEDTLS_ERR_X509_INVALID_FORMAT ); + p += len; + frame->subject_raw.len = p - frame->subject_raw.p; + + /* Comparing the raw buffer to itself amounts to structural validation. */ + ret = mbedtls_x509_name_cmp_raw( &frame->subject_raw, + &frame->subject_raw, + NULL, NULL ); + if( ret != 0 ) + return( ret ); + + /* + * SubjectPublicKeyInfo + */ + frame->pubkey_raw.p = p; + ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ); + if( ret != 0 ) + return( ret + MBEDTLS_ERR_PK_KEY_INVALID_FORMAT ); + p += len; + frame->pubkey_raw.len = p - frame->pubkey_raw.p; + + if( frame->version != 1 ) + { + /* + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + */ + ret = x509_get_uid( &p, end, &frame->issuer_id, 1 /* implicit tag */ ); + if( ret != 0 ) + return( ret ); + + /* + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + */ + ret = x509_get_uid( &p, end, &frame->subject_id, 2 /* implicit tag */ ); + if( ret != 0 ) + return( ret ); + } + + /* + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version shall be v3 + */ +#if !defined(MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3) + if( frame->version == 3 ) +#endif + { + if( p != end ) + { + ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | 3 ); + if( len == 0 ) + ret = MBEDTLS_ERR_ASN1_OUT_OF_DATA; + if( ret != 0 ) + return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); + + frame->v3_ext.p = p; + frame->v3_ext.len = len; + + p += len; + } + + ret = x509_crt_frame_parse_ext( frame ); + if( ret != 0 ) + return( ret ); + } + + /* Wrapup: Check that we consumed the entire `TBSCertificate` structure. */ + if( p != end ) + { + return( MBEDTLS_ERR_X509_INVALID_FORMAT + + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); + } return( 0 ); } +static int x509_crt_subject_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_name *subject ) +{ + return( mbedtls_x509_get_name( frame->subject_raw.p, + frame->subject_raw.len, + subject ) ); +} + +static int x509_crt_issuer_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_name *issuer ) +{ + return( mbedtls_x509_get_name( frame->issuer_raw.p, + frame->issuer_raw.len, + issuer ) ); +} + +static int x509_crt_subject_alt_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_sequence *subject_alt ) +{ + int ret; + unsigned char *p = frame->subject_alt_raw.p; + unsigned char *end = p + frame->subject_alt_raw.len; + + memset( subject_alt, 0, sizeof( *subject_alt ) ); + + if( ( frame->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ) == 0 ) + return( 0 ); + + ret = x509_get_subject_alt_name( p, end, subject_alt ); + if( ret != 0 ) + ret += MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + return( ret ); +} + +static int x509_crt_ext_key_usage_from_frame( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_sequence *ext_key_usage ) +{ + int ret; + unsigned char *p = frame->ext_key_usage_raw.p; + unsigned char *end = p + frame->ext_key_usage_raw.len; + + memset( ext_key_usage, 0, sizeof( *ext_key_usage ) ); + + if( ( frame->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE ) == 0 ) + return( 0 ); + + ret = x509_get_ext_key_usage( &p, end, ext_key_usage ); + if( ret != 0 ) + { + ret += MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + return( ret ); + } + + return( 0 ); +} + +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) +static int x509_crt_pk_from_frame( mbedtls_x509_crt_frame *frame, + mbedtls_pk_context *pk ) +{ + unsigned char *p = frame->pubkey_raw.p; + unsigned char *end = p + frame->pubkey_raw.len; + return( mbedtls_pk_parse_subpubkey( &p, end, pk ) ); +} +#endif /* !MBEDTLS_X509_ON_DEMAND_PARSING */ + /* * Parse and fill a single X.509 certificate in DER format */ @@ -834,256 +1465,178 @@ static int x509_crt_parse_der_core( mbedtls_x509_crt *crt, int make_copy ) { int ret; - size_t len; - unsigned char *p, *end, *crt_end; - mbedtls_x509_buf sig_params1, sig_params2, sig_oid2; + mbedtls_x509_crt_frame *frame; + mbedtls_x509_crt_cache *cache; - memset( &sig_params1, 0, sizeof( mbedtls_x509_buf ) ); - memset( &sig_params2, 0, sizeof( mbedtls_x509_buf ) ); - memset( &sig_oid2, 0, sizeof( mbedtls_x509_buf ) ); - - /* - * Check for valid input - */ if( crt == NULL || buf == NULL ) return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); - /* Use the original buffer until we figure out actual length. */ - p = (unsigned char*) buf; - len = buflen; - end = p + len; - - /* - * Certificate ::= SEQUENCE { - * tbsCertificate TBSCertificate, - * signatureAlgorithm AlgorithmIdentifier, - * signatureValue BIT STRING } - */ - if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + if( make_copy == 0 ) { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_INVALID_FORMAT ); - } - - end = crt_end = p + len; - crt->raw.len = crt_end - buf; - if( make_copy != 0 ) - { - /* Create and populate a new buffer for the raw field. */ - crt->raw.p = p = mbedtls_calloc( 1, crt->raw.len ); - if( crt->raw.p == NULL ) - return( MBEDTLS_ERR_X509_ALLOC_FAILED ); - - memcpy( crt->raw.p, buf, crt->raw.len ); - crt->own_buffer = 1; - - p += crt->raw.len - len; - end = crt_end = p + len; + crt->raw.p = (unsigned char*) buf; + crt->raw.len = buflen; + crt->own_buffer = 0; } else { - crt->raw.p = (unsigned char*) buf; - crt->own_buffer = 0; + /* Call mbedtls_calloc with buflen + 1 in order to avoid potential + * return of NULL in case of length 0 certificates, which we want + * to cleanly fail with MBEDTLS_ERR_X509_INVALID_FORMAT in the + * core parsing routine, but not here. */ + crt->raw.p = mbedtls_calloc( 1, buflen + 1 ); + if( crt->raw.p == NULL ) + return( MBEDTLS_ERR_X509_ALLOC_FAILED ); + crt->raw.len = buflen; + memcpy( crt->raw.p, buf, buflen ); + + crt->own_buffer = 1; } + cache = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt_cache ) ); + if( cache == NULL ) + { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto exit; + } + crt->cache = cache; + x509_crt_cache_init( cache ); + +#if defined(MBEDTLS_X509_ON_DEMAND_PARSING) + + ret = mbedtls_x509_crt_cache_provide_frame( crt ); + if( ret != 0 ) + goto exit; + + frame = crt->cache->frame; + +#else /* MBEDTLS_X509_ON_DEMAND_PARSING */ + + frame = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt_frame ) ); + if( frame == NULL ) + { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto exit; + } + cache->frame = frame; + + ret = x509_crt_parse_frame( crt->raw.p, + crt->raw.p + crt->raw.len, + frame ); + if( ret != 0 ) + goto exit; + + /* Copy frame to legacy CRT structure -- that's inefficient, but if + * memory matters, the new CRT structure should be used anyway. */ + x509_buf_raw_to_buf( &crt->tbs, &frame->tbs ); + x509_buf_raw_to_buf( &crt->serial, &frame->serial ); + x509_buf_raw_to_buf( &crt->issuer_raw, &frame->issuer_raw ); + x509_buf_raw_to_buf( &crt->subject_raw, &frame->subject_raw ); + x509_buf_raw_to_buf( &crt->issuer_id, &frame->issuer_id ); + x509_buf_raw_to_buf( &crt->subject_id, &frame->subject_id ); + x509_buf_raw_to_buf( &crt->pk_raw, &frame->pubkey_raw ); + x509_buf_raw_to_buf( &crt->sig, &frame->sig ); + x509_buf_raw_to_buf( &crt->v3_ext, &frame->v3_ext ); + crt->valid_from = frame->valid_from; + crt->valid_to = frame->valid_to; + crt->version = frame->version; + crt->ca_istrue = frame->ca_istrue; + crt->max_pathlen = frame->max_pathlen; + crt->ext_types = frame->ext_types; + crt->key_usage = frame->key_usage; + crt->ns_cert_type = frame->ns_cert_type; + /* - * TBSCertificate ::= SEQUENCE { + * Obtain the remaining fields from the frame. */ - crt->tbs.p = p; - if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_INVALID_FORMAT + ret ); - } + /* sig_oid: Previously, needed for convenience in + * mbedtls_x509_crt_info(), now pure legacy burden. */ + unsigned char *tmp = frame->sig_alg.p; + unsigned char *end = tmp + frame->sig_alg.len; + mbedtls_x509_buf sig_oid, sig_params; - end = p + len; - crt->tbs.len = end - crt->tbs.p; - - /* - * Version ::= INTEGER { v1(0), v2(1), v3(2) } - * - * CertificateSerialNumber ::= INTEGER - * - * signature AlgorithmIdentifier - */ - if( ( ret = x509_get_version( &p, end, &crt->version ) ) != 0 || - ( ret = mbedtls_x509_get_serial( &p, end, &crt->serial ) ) != 0 || - ( ret = mbedtls_x509_get_alg( &p, end, &crt->sig_oid, - &sig_params1 ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - - if( crt->version < 0 || crt->version > 2 ) - { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_UNKNOWN_VERSION ); - } - - crt->version++; - - if( ( ret = mbedtls_x509_get_sig_alg( &crt->sig_oid, &sig_params1, - &crt->sig_md, &crt->sig_pk, - &crt->sig_opts ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - - /* - * issuer Name - */ - crt->issuer_raw.p = p; - - if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_INVALID_FORMAT + ret ); - } - - if( ( ret = mbedtls_x509_get_name( &p, p + len, &crt->issuer ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - - crt->issuer_raw.len = p - crt->issuer_raw.p; - - /* - * Validity ::= SEQUENCE { - * notBefore Time, - * notAfter Time } - * - */ - if( ( ret = x509_get_dates( &p, end, &crt->valid_from, - &crt->valid_to ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - - /* - * subject Name - */ - crt->subject_raw.p = p; - - if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_INVALID_FORMAT + ret ); - } - - if( len && ( ret = mbedtls_x509_get_name( &p, p + len, &crt->subject ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - - crt->subject_raw.len = p - crt->subject_raw.p; - - /* - * SubjectPublicKeyInfo - */ - crt->pk_raw.p = p; - if( ( ret = mbedtls_pk_parse_subpubkey( &p, end, &crt->pk ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - crt->pk_raw.len = p - crt->pk_raw.p; - - /* - * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, - * -- If present, version shall be v2 or v3 - * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, - * -- If present, version shall be v2 or v3 - * extensions [3] EXPLICIT Extensions OPTIONAL - * -- If present, version shall be v3 - */ - if( crt->version == 2 || crt->version == 3 ) - { - ret = x509_get_uid( &p, end, &crt->issuer_id, 1 ); + ret = mbedtls_x509_get_alg( &tmp, end, + &sig_oid, &sig_params ); if( ret != 0 ) { - mbedtls_x509_crt_free( crt ); - return( ret ); + /* This should never happen, because we check + * the sanity of the AlgorithmIdentifier structure + * during frame parsing. */ + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto exit; + } + crt->sig_oid = sig_oid; + + /* Signature parameters */ + tmp = frame->sig_alg.p; + ret = mbedtls_x509_get_sig_alg_raw( &tmp, end, + &crt->sig_md, &crt->sig_pk, + &crt->sig_opts ); + if( ret != 0 ) + { + /* Again, this should never happen. */ + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto exit; } } - if( crt->version == 2 || crt->version == 3 ) - { - ret = x509_get_uid( &p, end, &crt->subject_id, 2 ); - if( ret != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - } + ret = x509_crt_pk_from_frame( frame, &crt->pk ); + if( ret != 0 ) + goto exit; -#if !defined(MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3) - if( crt->version == 3 ) -#endif - { - ret = x509_get_crt_ext( &p, end, crt ); - if( ret != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } - } + ret = x509_crt_subject_from_frame( frame, &crt->subject ); + if( ret != 0 ) + goto exit; - if( p != end ) - { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_INVALID_FORMAT + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - } + ret = x509_crt_issuer_from_frame( frame, &crt->issuer ); + if( ret != 0 ) + goto exit; - end = crt_end; + ret = x509_crt_subject_alt_from_frame( frame, &crt->subject_alt_names ); + if( ret != 0 ) + goto exit; - /* - * } - * -- end of TBSCertificate + ret = x509_crt_ext_key_usage_from_frame( frame, &crt->ext_key_usage ); + if( ret != 0 ) + goto exit; +#endif /* !MBEDTLS_X509_ON_DEMAND_PARSING */ + + /* Currently, we accept DER encoded CRTs with trailing garbage + * and promise to not account for the garbage in the `raw` field. * - * signatureAlgorithm AlgorithmIdentifier, - * signatureValue BIT STRING - */ - if( ( ret = mbedtls_x509_get_alg( &p, end, &sig_oid2, &sig_params2 ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } + * Note that this means that `crt->raw.len` is not necessarily the + * full size of the heap buffer allocated at `crt->raw.p` in case + * of copy-mode, but this is not a problem: freeing the buffer doesn't + * need the size, and the garbage data doesn't need zeroization. */ + crt->raw.len = frame->raw.len; - if( crt->sig_oid.len != sig_oid2.len || - memcmp( crt->sig_oid.p, sig_oid2.p, crt->sig_oid.len ) != 0 || - sig_params1.len != sig_params2.len || - ( sig_params1.len != 0 && - memcmp( sig_params1.p, sig_params2.p, sig_params1.len ) != 0 ) ) - { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_SIG_MISMATCH ); - } + cache->pk_raw = frame->pubkey_raw; - if( ( ret = mbedtls_x509_get_sig( &p, end, &crt->sig ) ) != 0 ) - { - mbedtls_x509_crt_free( crt ); - return( ret ); - } + /* Free the frame before parsing the public key to + * keep peak RAM usage low. This is slightly inefficient + * because the frame will need to be parsed again on the + * first usage of the CRT, but that seems acceptable. + * As soon as the frame gets used multiple times, it + * will be cached by default. */ + x509_crt_cache_clear_frame( crt->cache ); - if( p != end ) - { - mbedtls_x509_crt_free( crt ); - return( MBEDTLS_ERR_X509_INVALID_FORMAT + - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); - } + /* The cache just references the PK structure from the legacy + * implementation, so set up the latter first before setting up + * the cache. + * + * We're not actually using the parsed PK context here; + * we just parse it to check that it's well-formed. */ + ret = mbedtls_x509_crt_cache_provide_pk( crt ); + if( ret != 0 ) + goto exit; + x509_crt_cache_clear_pk( crt->cache ); - return( 0 ); +exit: + if( ret != 0 ) + mbedtls_x509_crt_free( crt ); + + return( ret ); } /* @@ -1104,7 +1657,7 @@ static int mbedtls_x509_crt_parse_der_internal( mbedtls_x509_crt *chain, if( crt == NULL || buf == NULL ) return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); - while( crt->version != 0 && crt->next != NULL ) + while( crt->raw.p != NULL && crt->next != NULL ) { prev = crt; crt = crt->next; @@ -1113,7 +1666,7 @@ static int mbedtls_x509_crt_parse_der_internal( mbedtls_x509_crt *chain, /* * Add new certificate on the end of the chain if needed. */ - if( crt->version != 0 && crt->next == NULL ) + if( crt->raw.p != NULL && crt->next == NULL ) { crt->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) ); @@ -1415,6 +1968,71 @@ cleanup: } #endif /* MBEDTLS_FS_IO */ +typedef struct mbedtls_x509_crt_sig_info +{ + mbedtls_md_type_t sig_md; + mbedtls_pk_type_t sig_pk; + void *sig_opts; + uint8_t crt_hash[MBEDTLS_MD_MAX_SIZE]; + size_t crt_hash_len; + mbedtls_x509_buf_raw sig; + mbedtls_x509_buf_raw issuer_raw; +} mbedtls_x509_crt_sig_info; + +static void x509_crt_free_sig_info( mbedtls_x509_crt_sig_info *info ) +{ +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + mbedtls_free( info->sig_opts ); +#else + ((void) info); +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ +} + +static int x509_crt_get_sig_info( mbedtls_x509_crt_frame const *frame, + mbedtls_x509_crt_sig_info *info ) +{ + const mbedtls_md_info_t *md_info; + + md_info = mbedtls_md_info_from_type( frame->sig_md ); + if( mbedtls_md( md_info, frame->tbs.p, frame->tbs.len, + info->crt_hash ) != 0 ) + { + /* Note: this can't happen except after an internal error */ + return( -1 ); + } + + info->crt_hash_len = mbedtls_md_get_size( md_info ); + + /* Make sure that this function leaves the target structure + * ready to be freed, regardless of success of failure. */ + info->sig_opts = NULL; + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + { + int ret; + unsigned char *alg_start = frame->sig_alg.p; + unsigned char *alg_end = alg_start + frame->sig_alg.len; + + /* Get signature options -- currently only + * necessary for RSASSA-PSS. */ + ret = mbedtls_x509_get_sig_alg_raw( &alg_start, alg_end, &info->sig_md, + &info->sig_pk, &info->sig_opts ); + if( ret != 0 ) + { + /* Note: this can't happen except after an internal error */ + return( -1 ); + } + } +#else /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + info->sig_md = frame->sig_md; + info->sig_pk = frame->sig_pk; +#endif /* !MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + + info->issuer_raw = frame->issuer_raw; + info->sig = frame->sig; + return( 0 ); +} + #if !defined(MBEDTLS_X509_REMOVE_INFO) static int x509_info_subject_alt_name( char **buf, size_t *size, const mbedtls_x509_sequence *subject_alt_name ) @@ -1551,135 +2169,209 @@ static int x509_info_ext_key_usage( char **buf, size_t *size, #define BEFORE_COLON 18 #define BC "18" int mbedtls_x509_crt_info( char *buf, size_t size, const char *prefix, - const mbedtls_x509_crt *crt ) + const mbedtls_x509_crt *crt ) { int ret; size_t n; char *p; char key_size_str[BEFORE_COLON]; + mbedtls_x509_crt_frame frame; + mbedtls_pk_context pk; + + mbedtls_x509_name *issuer = NULL, *subject = NULL; + mbedtls_x509_sequence *ext_key_usage = NULL, *subject_alt_names = NULL; + mbedtls_x509_crt_sig_info sig_info; p = buf; n = size; + memset( &sig_info, 0, sizeof( mbedtls_x509_crt_sig_info ) ); + mbedtls_pk_init( &pk ); + if( NULL == crt ) { ret = mbedtls_snprintf( p, n, "\nCertificate is uninitialised!\n" ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; return( (int) ( size - n ) ); } - ret = mbedtls_snprintf( p, n, "%scert. version : %d\n", - prefix, crt->version ); - MBEDTLS_X509_SAFE_SNPRINTF; - ret = mbedtls_snprintf( p, n, "%sserial number : ", - prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_crt_get_frame( crt, &frame ); + if( ret != 0 ) + { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto cleanup; + } - ret = mbedtls_x509_serial_gets( p, n, &crt->serial ); - MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_crt_get_subject( crt, &subject ); + if( ret != 0 ) + { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto cleanup; + } + + ret = mbedtls_x509_crt_get_issuer( crt, &issuer ); + if( ret != 0 ) + { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto cleanup; + } + + ret = mbedtls_x509_crt_get_subject_alt_names( crt, &subject_alt_names ); + if( ret != 0 ) + { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto cleanup; + } + + ret = mbedtls_x509_crt_get_ext_key_usage( crt, &ext_key_usage ); + if( ret != 0 ) + { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto cleanup; + } + + ret = mbedtls_x509_crt_get_pk( crt, &pk ); + if( ret != 0 ) + { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto cleanup; + } + + ret = x509_crt_get_sig_info( &frame, &sig_info ); + if( ret != 0 ) + { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + goto cleanup; + } + + ret = mbedtls_snprintf( p, n, "%scert. version : %d\n", + prefix, frame.version ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; + + { + mbedtls_x509_buf serial; + serial.p = frame.serial.p; + serial.len = frame.serial.len; + ret = mbedtls_snprintf( p, n, "%sserial number : ", + prefix ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; + ret = mbedtls_x509_serial_gets( p, n, &serial ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; + } ret = mbedtls_snprintf( p, n, "\n%sissuer name : ", prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; - ret = mbedtls_x509_dn_gets( p, n, &crt->issuer ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; + ret = mbedtls_x509_dn_gets( p, n, issuer ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; ret = mbedtls_snprintf( p, n, "\n%ssubject name : ", prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; - ret = mbedtls_x509_dn_gets( p, n, &crt->subject ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; + ret = mbedtls_x509_dn_gets( p, n, subject ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; ret = mbedtls_snprintf( p, n, "\n%sissued on : " \ "%04d-%02d-%02d %02d:%02d:%02d", prefix, - crt->valid_from.year, crt->valid_from.mon, - crt->valid_from.day, crt->valid_from.hour, - crt->valid_from.min, crt->valid_from.sec ); - MBEDTLS_X509_SAFE_SNPRINTF; + frame.valid_from.year, frame.valid_from.mon, + frame.valid_from.day, frame.valid_from.hour, + frame.valid_from.min, frame.valid_from.sec ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; ret = mbedtls_snprintf( p, n, "\n%sexpires on : " \ "%04d-%02d-%02d %02d:%02d:%02d", prefix, - crt->valid_to.year, crt->valid_to.mon, - crt->valid_to.day, crt->valid_to.hour, - crt->valid_to.min, crt->valid_to.sec ); - MBEDTLS_X509_SAFE_SNPRINTF; + frame.valid_to.year, frame.valid_to.mon, + frame.valid_to.day, frame.valid_to.hour, + frame.valid_to.min, frame.valid_to.sec ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; ret = mbedtls_snprintf( p, n, "\n%ssigned using : ", prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; - ret = mbedtls_x509_sig_alg_gets( p, n, &crt->sig_oid, crt->sig_pk, - crt->sig_md, crt->sig_opts ); - MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_sig_alg_gets( p, n, sig_info.sig_pk, + sig_info.sig_md, sig_info.sig_opts ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; /* Key size */ if( ( ret = mbedtls_x509_key_size_helper( key_size_str, BEFORE_COLON, - mbedtls_pk_get_name( &crt->pk ) ) ) != 0 ) + mbedtls_pk_get_name( &pk ) ) ) != 0 ) { return( ret ); } ret = mbedtls_snprintf( p, n, "\n%s%-" BC "s: %d bits", prefix, key_size_str, - (int) mbedtls_pk_get_bitlen( &crt->pk ) ); - MBEDTLS_X509_SAFE_SNPRINTF; + (int) mbedtls_pk_get_bitlen( &pk ) ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; /* * Optional extensions */ - if( crt->ext_types & MBEDTLS_X509_EXT_BASIC_CONSTRAINTS ) + if( frame.ext_types & MBEDTLS_X509_EXT_BASIC_CONSTRAINTS ) { ret = mbedtls_snprintf( p, n, "\n%sbasic constraints : CA=%s", prefix, - crt->ca_istrue ? "true" : "false" ); - MBEDTLS_X509_SAFE_SNPRINTF; + frame.ca_istrue ? "true" : "false" ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; - if( crt->max_pathlen > 0 ) + if( frame.max_pathlen > 0 ) { - ret = mbedtls_snprintf( p, n, ", max_pathlen=%d", crt->max_pathlen - 1 ); - MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_snprintf( p, n, ", max_pathlen=%d", frame.max_pathlen - 1 ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; } } - if( crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ) + if( frame.ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ) { ret = mbedtls_snprintf( p, n, "\n%ssubject alt name : ", prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; if( ( ret = x509_info_subject_alt_name( &p, &n, - &crt->subject_alt_names ) ) != 0 ) + subject_alt_names ) ) != 0 ) return( ret ); } - if( crt->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE ) + if( frame.ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE ) { ret = mbedtls_snprintf( p, n, "\n%scert. type : ", prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; - if( ( ret = x509_info_cert_type( &p, &n, crt->ns_cert_type ) ) != 0 ) + if( ( ret = x509_info_cert_type( &p, &n, frame.ns_cert_type ) ) != 0 ) return( ret ); } - if( crt->ext_types & MBEDTLS_X509_EXT_KEY_USAGE ) + if( frame.ext_types & MBEDTLS_X509_EXT_KEY_USAGE ) { ret = mbedtls_snprintf( p, n, "\n%skey usage : ", prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; - if( ( ret = x509_info_key_usage( &p, &n, crt->key_usage ) ) != 0 ) + if( ( ret = x509_info_key_usage( &p, &n, frame.key_usage ) ) != 0 ) return( ret ); } - if( crt->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE ) + if( frame.ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE ) { ret = mbedtls_snprintf( p, n, "\n%sext key usage : ", prefix ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; if( ( ret = x509_info_ext_key_usage( &p, &n, - &crt->ext_key_usage ) ) != 0 ) + ext_key_usage ) ) != 0 ) return( ret ); } ret = mbedtls_snprintf( p, n, "\n" ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; - return( (int) ( size - n ) ); + ret = (int) ( size - n ); + +cleanup: + + x509_crt_free_sig_info( &sig_info ); + mbedtls_pk_free( &pk ); + mbedtls_x509_name_free( issuer ); + mbedtls_x509_name_free( subject ); + mbedtls_x509_sequence_free( ext_key_usage ); + mbedtls_x509_sequence_free( subject_alt_names ); + + return( ret ); } struct x509_crt_verify_string { @@ -1741,8 +2433,8 @@ int mbedtls_x509_crt_verify_info( char *buf, size_t size, const char *prefix, #endif /* !MBEDTLS_X509_REMOVE_INFO */ #if defined(MBEDTLS_X509_CHECK_KEY_USAGE) -int mbedtls_x509_crt_check_key_usage( const mbedtls_x509_crt *crt, - unsigned int usage ) +static int x509_crt_check_key_usage_frame( const mbedtls_x509_crt_frame *crt, + unsigned int usage ) { unsigned int usage_must, usage_may; unsigned int may_mask = MBEDTLS_X509_KU_ENCIPHER_ONLY @@ -1763,37 +2455,87 @@ int mbedtls_x509_crt_check_key_usage( const mbedtls_x509_crt *crt, return( 0 ); } + +int mbedtls_x509_crt_check_key_usage( const mbedtls_x509_crt *crt, + unsigned int usage ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + ret = x509_crt_check_key_usage_frame( frame, usage ); + mbedtls_x509_crt_frame_release( crt ); + + return( ret ); +} #endif #if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE) -int mbedtls_x509_crt_check_extended_key_usage( const mbedtls_x509_crt *crt, - const char *usage_oid, - size_t usage_len ) +typedef struct { - const mbedtls_x509_sequence *cur; + const char *oid; + size_t oid_len; +} x509_crt_check_ext_key_usage_cb_ctx_t; - /* Extension is not mandatory, absent means no restriction */ - if( ( crt->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE ) == 0 ) - return( 0 ); +static int x509_crt_check_ext_key_usage_cb( void *ctx, + int tag, + unsigned char *data, + size_t data_len ) +{ + x509_crt_check_ext_key_usage_cb_ctx_t *cb_ctx = + (x509_crt_check_ext_key_usage_cb_ctx_t *) ctx; + ((void) tag); - /* - * Look for the requested usage (or wildcard ANY) in our list - */ - for( cur = &crt->ext_key_usage; cur != NULL; cur = cur->next ) + if( MBEDTLS_OID_CMP_RAW( MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE, + data, data_len ) == 0 ) { - const mbedtls_x509_buf *cur_oid = &cur->buf; - - if( cur_oid->len == usage_len && - memcmp( cur_oid->p, usage_oid, usage_len ) == 0 ) - { - return( 0 ); - } - - if( MBEDTLS_OID_CMP( MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE, cur_oid ) == 0 ) - return( 0 ); + return( 1 ); } - return( MBEDTLS_ERR_X509_BAD_INPUT_DATA ); + if( data_len == cb_ctx->oid_len && memcmp( data, cb_ctx->oid, + data_len ) == 0 ) + { + return( 1 ); + } + + return( 0 ); +} + +int mbedtls_x509_crt_check_extended_key_usage( const mbedtls_x509_crt *crt, + const char *usage_oid, + size_t usage_len ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + unsigned ext_types; + unsigned char *p, *end; + x509_crt_check_ext_key_usage_cb_ctx_t cb_ctx = { usage_oid, usage_len }; + + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + /* Extension is not mandatory, absent means no restriction */ + ext_types = frame->ext_types; + if( ( ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE ) != 0 ) + { + p = frame->ext_key_usage_raw.p; + end = p + frame->ext_key_usage_raw.len; + + ret = mbedtls_asn1_traverse_sequence_of( &p, end, + 0xFF, MBEDTLS_ASN1_OID, 0, 0, + x509_crt_check_ext_key_usage_cb, + &cb_ctx ); + if( ret == 1 ) + ret = 0; + else + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + mbedtls_x509_crt_frame_release( crt ); + return( ret ); } #endif /* MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE */ @@ -1801,14 +2543,16 @@ int mbedtls_x509_crt_check_extended_key_usage( const mbedtls_x509_crt *crt, /* * Return 1 if the certificate is revoked, or 0 otherwise. */ -int mbedtls_x509_crt_is_revoked( const mbedtls_x509_crt *crt, const mbedtls_x509_crl *crl ) +static int x509_serial_is_revoked( unsigned char const *serial, + size_t serial_len, + const mbedtls_x509_crl *crl ) { const mbedtls_x509_crl_entry *cur = &crl->entry; while( cur != NULL && cur->serial.len != 0 ) { - if( crt->serial.len == cur->serial.len && - memcmp( crt->serial.p, cur->serial.p, crt->serial.len ) == 0 ) + if( serial_len == cur->serial.len && + memcmp( serial, cur->serial.p, serial_len ) == 0 ) { if( mbedtls_x509_time_is_past( &cur->revocation_date ) ) return( 1 ); @@ -1820,25 +2564,71 @@ int mbedtls_x509_crt_is_revoked( const mbedtls_x509_crt *crt, const mbedtls_x509 return( 0 ); } +int mbedtls_x509_crt_is_revoked( const mbedtls_x509_crt *crt, + const mbedtls_x509_crl *crl ) +{ + int ret; + mbedtls_x509_crt_frame const *frame; + + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + ret = x509_serial_is_revoked( frame->serial.p, + frame->serial.len, + crl ); + mbedtls_x509_crt_frame_release( crt ); + return( ret ); +} + /* * Check that the given certificate is not revoked according to the CRL. * Skip validation if no CRL for the given CA is present. */ -static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, +static int x509_crt_verifycrl( unsigned char *crt_serial, + size_t crt_serial_len, + mbedtls_x509_crt *ca_crt, mbedtls_x509_crl *crl_list, const mbedtls_x509_crt_profile *profile ) { + int ret; int flags = 0; unsigned char hash[MBEDTLS_MD_MAX_SIZE]; const mbedtls_md_info_t *md_info; + mbedtls_x509_buf_raw ca_subject; + mbedtls_pk_context *pk; + int can_sign; - if( ca == NULL ) + if( ca_crt == NULL ) return( flags ); + { + mbedtls_x509_crt_frame const *ca; + ret = mbedtls_x509_crt_frame_acquire( ca_crt, &ca ); + if( ret != 0 ) + return( MBEDTLS_X509_BADCRL_NOT_TRUSTED ); + + ca_subject = ca->subject_raw; + + can_sign = 0; + if( x509_crt_check_key_usage_frame( ca, + MBEDTLS_X509_KU_CRL_SIGN ) == 0 ) + { + can_sign = 1; + } + + mbedtls_x509_crt_frame_release( ca_crt ); + } + + ret = mbedtls_x509_crt_pk_acquire( ca_crt, &pk ); + if( ret != 0 ) + return( MBEDTLS_X509_BADCRL_NOT_TRUSTED ); + while( crl_list != NULL ) { if( crl_list->version == 0 || - x509_name_cmp( &crl_list->issuer, &ca->subject ) != 0 ) + mbedtls_x509_name_cmp_raw( &crl_list->issuer_raw, + &ca_subject, NULL, NULL ) != 0 ) { crl_list = crl_list->next; continue; @@ -1848,8 +2638,7 @@ static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, * Check if the CA is configured to sign CRLs */ #if defined(MBEDTLS_X509_CHECK_KEY_USAGE) - if( mbedtls_x509_crt_check_key_usage( ca, - MBEDTLS_X509_KU_CRL_SIGN ) != 0 ) + if( !can_sign ) { flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED; break; @@ -1873,10 +2662,10 @@ static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, break; } - if( x509_profile_check_key( profile, &ca->pk ) != 0 ) + if( x509_profile_check_key( profile, pk ) != 0 ) flags |= MBEDTLS_X509_BADCERT_BAD_KEY; - if( mbedtls_pk_verify_ext( crl_list->sig_pk, crl_list->sig_opts, &ca->pk, + if( mbedtls_pk_verify_ext( crl_list->sig_pk, crl_list->sig_opts, pk, crl_list->sig_md, hash, mbedtls_md_get_size( md_info ), crl_list->sig.p, crl_list->sig.len ) != 0 ) { @@ -1896,7 +2685,8 @@ static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, /* * Check if certificate is revoked */ - if( mbedtls_x509_crt_is_revoked( crt, crl_list ) ) + if( x509_serial_is_revoked( crt_serial, crt_serial_len, + crl_list ) ) { flags |= MBEDTLS_X509_BADCERT_REVOKED; break; @@ -1905,6 +2695,7 @@ static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, crl_list = crl_list->next; } + mbedtls_x509_crt_pk_release( ca_crt ); return( flags ); } #endif /* MBEDTLS_X509_CRL_PARSE_C */ @@ -1912,38 +2703,49 @@ static int x509_crt_verifycrl( mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, /* * Check the signature of a certificate by its parent */ -static int x509_crt_check_signature( const mbedtls_x509_crt *child, +static int x509_crt_check_signature( const mbedtls_x509_crt_sig_info *sig_info, mbedtls_x509_crt *parent, mbedtls_x509_crt_restart_ctx *rs_ctx ) { - const mbedtls_md_info_t *md_info; - unsigned char hash[MBEDTLS_MD_MAX_SIZE]; + int ret; + mbedtls_pk_context *pk; - md_info = mbedtls_md_info_from_type( child->sig_md ); - if( mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash ) != 0 ) - { - /* Note: this can't happen except after an internal error */ - return( -1 ); - } + ret = mbedtls_x509_crt_pk_acquire( parent, &pk ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); /* Skip expensive computation on obvious mismatch */ - if( ! mbedtls_pk_can_do( &parent->pk, child->sig_pk ) ) - return( -1 ); - -#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) - if( rs_ctx != NULL && child->sig_pk == MBEDTLS_PK_ECDSA ) + if( ! mbedtls_pk_can_do( pk, sig_info->sig_pk ) ) { - return( mbedtls_pk_verify_restartable( &parent->pk, - child->sig_md, hash, mbedtls_md_get_size( md_info ), - child->sig.p, child->sig.len, &rs_ctx->pk ) ); + ret = -1; + goto exit; } -#else - (void) rs_ctx; -#endif - return( mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent->pk, - child->sig_md, hash, mbedtls_md_get_size( md_info ), - child->sig.p, child->sig.len ) ); +#if !( defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) ) + ((void) rs_ctx); +#else + if( rs_ctx != NULL && sig_info->sig_pk == MBEDTLS_PK_ECDSA ) + { + ret = mbedtls_pk_verify_restartable( pk, + sig_info->sig_md, + sig_info->crt_hash, sig_info->crt_hash_len, + sig_info->sig.p, sig_info->sig.len, + &rs_ctx->pk ); + } + else +#endif + { + ret = mbedtls_pk_verify_ext( sig_info->sig_pk, + sig_info->sig_opts, + pk, + sig_info->sig_md, + sig_info->crt_hash, sig_info->crt_hash_len, + sig_info->sig.p, sig_info->sig.len ); + } + +exit: + mbedtls_x509_crt_pk_release( parent ); + return( ret ); } /* @@ -1952,15 +2754,19 @@ static int x509_crt_check_signature( const mbedtls_x509_crt *child, * * top means parent is a locally-trusted certificate */ -static int x509_crt_check_parent( const mbedtls_x509_crt *child, - const mbedtls_x509_crt *parent, +static int x509_crt_check_parent( const mbedtls_x509_crt_sig_info *sig_info, + const mbedtls_x509_crt_frame *parent, int top ) { int need_ca_bit; /* Parent must be the issuer */ - if( x509_name_cmp( &child->issuer, &parent->subject ) != 0 ) + if( mbedtls_x509_name_cmp_raw( &sig_info->issuer_raw, + &parent->subject_raw, + NULL, NULL ) != 0 ) + { return( -1 ); + } /* Parent must have the basicConstraints CA bit set as a general rule */ need_ca_bit = 1; @@ -1974,7 +2780,8 @@ static int x509_crt_check_parent( const mbedtls_x509_crt *child, #if defined(MBEDTLS_X509_CHECK_KEY_USAGE) if( need_ca_bit && - mbedtls_x509_crt_check_key_usage( parent, MBEDTLS_X509_KU_KEY_CERT_SIGN ) != 0 ) + x509_crt_check_key_usage_frame( parent, + MBEDTLS_X509_KU_KEY_CERT_SIGN ) != 0 ) { return( -1 ); } @@ -2027,7 +2834,7 @@ static int x509_crt_check_parent( const mbedtls_x509_crt *child, * - MBEDTLS_ERR_ECP_IN_PROGRESS otherwise */ static int x509_crt_find_parent_in( - mbedtls_x509_crt *child, + mbedtls_x509_crt_sig_info const *child_sig, mbedtls_x509_crt *candidates, mbedtls_x509_crt **r_parent, int *r_signature_is_good, @@ -2037,7 +2844,7 @@ static int x509_crt_find_parent_in( mbedtls_x509_crt_restart_ctx *rs_ctx ) { int ret; - mbedtls_x509_crt *parent, *fallback_parent; + mbedtls_x509_crt *parent_crt, *fallback_parent; int signature_is_good, fallback_signature_is_good; #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) @@ -2045,7 +2852,7 @@ static int x509_crt_find_parent_in( if( rs_ctx != NULL && rs_ctx->parent != NULL ) { /* restore saved state */ - parent = rs_ctx->parent; + parent_crt = rs_ctx->parent; fallback_parent = rs_ctx->fallback_parent; fallback_signature_is_good = rs_ctx->fallback_signature_is_good; @@ -2062,30 +2869,55 @@ static int x509_crt_find_parent_in( fallback_parent = NULL; fallback_signature_is_good = 0; - for( parent = candidates; parent != NULL; parent = parent->next ) + for( parent_crt = candidates; parent_crt != NULL; + parent_crt = parent_crt->next ) { - /* basic parenting skills (name, CA bit, key usage) */ - if( x509_crt_check_parent( child, parent, top ) != 0 ) - continue; + int parent_valid, parent_match, path_len_ok; - /* +1 because stored max_pathlen is 1 higher that the actual value */ - if( parent->max_pathlen > 0 && - (size_t) parent->max_pathlen < 1 + path_cnt - self_cnt ) - { - continue; - } - - /* Signature */ #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) check_signature: #endif - ret = x509_crt_check_signature( child, parent, rs_ctx ); + + parent_valid = parent_match = path_len_ok = 0; + { + mbedtls_x509_crt_frame const *parent; + + ret = mbedtls_x509_crt_frame_acquire( parent_crt, &parent ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + if( !mbedtls_x509_time_is_past( &parent->valid_to ) && + !mbedtls_x509_time_is_future( &parent->valid_from ) ) + { + parent_valid = 1; + } + + /* basic parenting skills (name, CA bit, key usage) */ + if( x509_crt_check_parent( child_sig, parent, top ) == 0 ) + parent_match = 1; + + /* +1 because the stored max_pathlen is 1 higher + * than the actual value */ + if( !( parent->max_pathlen > 0 && + (size_t) parent->max_pathlen < 1 + path_cnt - self_cnt ) ) + { + path_len_ok = 1; + } + + mbedtls_x509_crt_frame_release( parent_crt ); + } + + if( parent_match == 0 || path_len_ok == 0 ) + continue; + + /* Signature */ + ret = x509_crt_check_signature( child_sig, parent_crt, rs_ctx ); #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) if( rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) { /* save state */ - rs_ctx->parent = parent; + rs_ctx->parent = parent_crt; rs_ctx->fallback_parent = fallback_parent; rs_ctx->fallback_signature_is_good = fallback_signature_is_good; @@ -2100,12 +2932,11 @@ check_signature: continue; /* optional time check */ - if( mbedtls_x509_time_is_past( &parent->valid_to ) || - mbedtls_x509_time_is_future( &parent->valid_from ) ) + if( !parent_valid ) { if( fallback_parent == NULL ) { - fallback_parent = parent; + fallback_parent = parent_crt; fallback_signature_is_good = signature_is_good; } @@ -2115,9 +2946,9 @@ check_signature: break; } - if( parent != NULL ) + if( parent_crt != NULL ) { - *r_parent = parent; + *r_parent = parent_crt; *r_signature_is_good = signature_is_good; } else @@ -2152,7 +2983,8 @@ check_signature: * - MBEDTLS_ERR_ECP_IN_PROGRESS otherwise */ static int x509_crt_find_parent( - mbedtls_x509_crt *child, + mbedtls_x509_crt_sig_info const *child_sig, + mbedtls_x509_crt *rest, mbedtls_x509_crt *trust_ca, mbedtls_x509_crt **parent, int *parent_is_trusted, @@ -2176,9 +3008,9 @@ static int x509_crt_find_parent( #endif while( 1 ) { - search_list = *parent_is_trusted ? trust_ca : child->next; + search_list = *parent_is_trusted ? trust_ca : rest; - ret = x509_crt_find_parent_in( child, search_list, + ret = x509_crt_find_parent_in( child_sig, search_list, parent, signature_is_good, *parent_is_trusted, path_cnt, self_cnt, rs_ctx ); @@ -2219,14 +3051,10 @@ static int x509_crt_find_parent( * check for self-issued as self-signatures are not checked) */ static int x509_crt_check_ee_locally_trusted( - mbedtls_x509_crt *crt, - mbedtls_x509_crt *trust_ca ) + mbedtls_x509_crt_frame const *crt, + mbedtls_x509_crt const *trust_ca ) { - mbedtls_x509_crt *cur; - - /* must be self-issued */ - if( x509_name_cmp( &crt->issuer, &crt->subject ) != 0 ) - return( -1 ); + mbedtls_x509_crt const *cur; /* look for an exact match with trusted cert */ for( cur = trust_ca; cur != NULL; cur = cur->next ) @@ -2295,8 +3123,8 @@ static int x509_crt_verify_chain( int ret; uint32_t *flags; mbedtls_x509_crt_verify_chain_item *cur; - mbedtls_x509_crt *child; - mbedtls_x509_crt *parent; + mbedtls_x509_crt *child_crt; + mbedtls_x509_crt *parent_crt; int parent_is_trusted; int child_is_trusted; int signature_is_good; @@ -2312,58 +3140,100 @@ static int x509_crt_verify_chain( /* restore derived state */ cur = &ver_chain->items[ver_chain->len - 1]; - child = cur->crt; - flags = &cur->flags; + child_crt = cur->crt; + child_is_trusted = 0; goto find_parent; } #endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ - child = crt; + child_crt = crt; self_cnt = 0; parent_is_trusted = 0; child_is_trusted = 0; while( 1 ) { +#if defined(MBEDTLS_X509_CRL_PARSE_C) + mbedtls_x509_buf_raw child_serial; +#endif /* MBEDTLS_X509_CRL_PARSE_C */ + int self_issued; + /* Add certificate to the verification chain */ cur = &ver_chain->items[ver_chain->len]; - cur->crt = child; + cur->crt = child_crt; cur->flags = 0; ver_chain->len++; - flags = &cur->flags; - - /* Check time-validity (all certificates) */ - if( mbedtls_x509_time_is_past( &child->valid_to ) ) - *flags |= MBEDTLS_X509_BADCERT_EXPIRED; - - if( mbedtls_x509_time_is_future( &child->valid_from ) ) - *flags |= MBEDTLS_X509_BADCERT_FUTURE; - - /* Stop here for trusted roots (but not for trusted EE certs) */ - if( child_is_trusted ) - return( 0 ); - - /* Check signature algorithm: MD & PK algs */ - if( x509_profile_check_md_alg( profile, child->sig_md ) != 0 ) - *flags |= MBEDTLS_X509_BADCERT_BAD_MD; - - if( x509_profile_check_pk_alg( profile, child->sig_pk ) != 0 ) - *flags |= MBEDTLS_X509_BADCERT_BAD_PK; - - /* Special case: EE certs that are locally trusted */ - if( ver_chain->len == 1 && - x509_crt_check_ee_locally_trusted( child, trust_ca ) == 0 ) - { - return( 0 ); - } #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) find_parent: #endif - /* Look for a parent in trusted CAs or up the chain */ - ret = x509_crt_find_parent( child, trust_ca, &parent, - &parent_is_trusted, &signature_is_good, - ver_chain->len - 1, self_cnt, rs_ctx ); + + flags = &cur->flags; + + { + mbedtls_x509_crt_sig_info child_sig; + { + mbedtls_x509_crt_frame const *child; + + ret = mbedtls_x509_crt_frame_acquire( child_crt, &child ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + /* Check time-validity (all certificates) */ + if( mbedtls_x509_time_is_past( &child->valid_to ) ) + *flags |= MBEDTLS_X509_BADCERT_EXPIRED; + if( mbedtls_x509_time_is_future( &child->valid_from ) ) + *flags |= MBEDTLS_X509_BADCERT_FUTURE; + + /* Stop here for trusted roots (but not for trusted EE certs) */ + if( child_is_trusted ) + { + mbedtls_x509_crt_frame_release( child_crt ); + return( 0 ); + } + + self_issued = 0; + if( mbedtls_x509_name_cmp_raw( &child->issuer_raw, + &child->subject_raw, + NULL, NULL ) == 0 ) + { + self_issued = 1; + } + + /* Check signature algorithm: MD & PK algs */ + if( x509_profile_check_md_alg( profile, child->sig_md ) != 0 ) + *flags |= MBEDTLS_X509_BADCERT_BAD_MD; + + if( x509_profile_check_pk_alg( profile, child->sig_pk ) != 0 ) + *flags |= MBEDTLS_X509_BADCERT_BAD_PK; + + /* Special case: EE certs that are locally trusted */ + if( ver_chain->len == 1 && self_issued && + x509_crt_check_ee_locally_trusted( child, trust_ca ) == 0 ) + { + mbedtls_x509_crt_frame_release( child_crt ); + return( 0 ); + } + +#if defined(MBEDTLS_X509_CRL_PARSE_C) + child_serial = child->serial; +#endif /* MBEDTLS_X509_CRL_PARSE_C */ + + ret = x509_crt_get_sig_info( child, &child_sig ); + mbedtls_x509_crt_frame_release( child_crt ); + + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + } + + /* Look for a parent in trusted CAs or up the chain */ + ret = x509_crt_find_parent( &child_sig, child_crt->next, + trust_ca, &parent_crt, + &parent_is_trusted, &signature_is_good, + ver_chain->len - 1, self_cnt, rs_ctx ); + + x509_crt_free_sig_info( &child_sig ); + } #if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) if( rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS ) @@ -2372,7 +3242,6 @@ find_parent: rs_ctx->in_progress = x509_crt_rs_find_parent; rs_ctx->self_cnt = self_cnt; rs_ctx->ver_chain = *ver_chain; /* struct copy */ - return( ret ); } #else @@ -2380,7 +3249,7 @@ find_parent: #endif /* No parent? We're done here */ - if( parent == NULL ) + if( parent_crt == NULL ) { *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED; return( 0 ); @@ -2389,11 +3258,8 @@ find_parent: /* Count intermediate self-issued (not necessarily self-signed) certs. * These can occur with some strategies for key rollover, see [SIRO], * and should be excluded from max_pathlen checks. */ - if( ver_chain->len != 1 && - x509_name_cmp( &child->issuer, &child->subject ) == 0 ) - { + if( ver_chain->len != 1 && self_issued ) self_cnt++; - } /* path_cnt is 0 for the first intermediate CA, * and if parent is trusted it's not an intermediate CA */ @@ -2408,20 +3274,31 @@ find_parent: if( ! signature_is_good ) *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED; - /* check size of signing key */ - if( x509_profile_check_key( profile, &parent->pk ) != 0 ) - *flags |= MBEDTLS_X509_BADCERT_BAD_KEY; + { + mbedtls_pk_context *parent_pk; + ret = mbedtls_x509_crt_pk_acquire( parent_crt, &parent_pk ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + /* check size of signing key */ + if( x509_profile_check_key( profile, parent_pk ) != 0 ) + *flags |= MBEDTLS_X509_BADCERT_BAD_KEY; + + mbedtls_x509_crt_pk_release( parent_crt ); + } #if defined(MBEDTLS_X509_CRL_PARSE_C) /* Check trusted CA's CRL for the given crt */ - *flags |= x509_crt_verifycrl( child, parent, ca_crl, profile ); + *flags |= x509_crt_verifycrl( child_serial.p, + child_serial.len, + parent_crt, ca_crl, profile ); #else (void) ca_crl; #endif /* prepare for next iteration */ - child = parent; - parent = NULL; + child_crt = parent_crt; + parent_crt = NULL; child_is_trusted = parent_is_trusted; signature_is_good = 0; } @@ -2430,18 +3307,17 @@ find_parent: /* * Check for CN match */ -static int x509_crt_check_cn( const mbedtls_x509_buf *name, - const char *cn, size_t cn_len ) +static int x509_crt_check_cn( unsigned char const *buf, + size_t buflen, + const char *cn, + size_t cn_len ) { - /* try exact match */ - if( name->len == cn_len && - x509_memcasecmp( cn, name->p, cn_len ) == 0 ) - { + /* Try exact match */ + if( mbedtls_x509_memcasecmp( cn, buf, buflen, cn_len ) == 0 ) return( 0 ); - } /* try wildcard match */ - if( x509_check_wildcard( cn, name ) == 0 ) + if( x509_check_wildcard( cn, cn_len, buf, buflen ) == 0 ) { return( 0 ); } @@ -2449,42 +3325,95 @@ static int x509_crt_check_cn( const mbedtls_x509_buf *name, return( -1 ); } +/* Returns 1 on a match and 0 on a mismatch. + * This is because this function is used as a callback for + * mbedtls_x509_name_cmp_raw(), which continues the name + * traversal as long as the callback returns 0. */ +static int x509_crt_check_name( void *ctx, + mbedtls_x509_buf *oid, + mbedtls_x509_buf *val, + int next_merged ) +{ + char const *cn = (char const*) ctx; + size_t cn_len = strlen( cn ); + ((void) next_merged); + + if( MBEDTLS_OID_CMP( MBEDTLS_OID_AT_CN, oid ) == 0 && + x509_crt_check_cn( val->p, val->len, cn, cn_len ) == 0 ) + { + return( 1 ); + } + + return( 0 ); +} + +/* Returns 1 on a match and 0 on a mismatch. + * This is because this function is used as a callback for + * mbedtls_asn1_traverse_sequence_of(), which continues the + * traversal as long as the callback returns 0. */ +static int x509_crt_subject_alt_check_name( void *ctx, + int tag, + unsigned char *data, + size_t data_len ) +{ + char const *cn = (char const*) ctx; + size_t cn_len = strlen( cn ); + ((void) tag); + + if( x509_crt_check_cn( data, data_len, cn, cn_len ) == 0 ) + return( 1 ); + + return( 0 ); +} + /* * Verify the requested CN - only call this if cn is not NULL! */ -static void x509_crt_verify_name( const mbedtls_x509_crt *crt, - const char *cn, - uint32_t *flags ) +static int x509_crt_verify_name( const mbedtls_x509_crt *crt, + const char *cn, + uint32_t *flags ) { - const mbedtls_x509_name *name; - const mbedtls_x509_sequence *cur; - size_t cn_len = strlen( cn ); + int ret; + mbedtls_x509_crt_frame const *frame; - if( crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ) + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); + + if( frame->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME ) { - for( cur = &crt->subject_alt_names; cur != NULL; cur = cur->next ) - { - if( x509_crt_check_cn( &cur->buf, cn, cn_len ) == 0 ) - break; - } + unsigned char *p = + frame->subject_alt_raw.p; + const unsigned char *end = + frame->subject_alt_raw.p + frame->subject_alt_raw.len; - if( cur == NULL ) - *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; + ret = mbedtls_asn1_traverse_sequence_of( &p, end, + MBEDTLS_ASN1_TAG_CLASS_MASK, + MBEDTLS_ASN1_CONTEXT_SPECIFIC, + MBEDTLS_ASN1_TAG_VALUE_MASK, + 2 /* SubjectAlt DNS */, + x509_crt_subject_alt_check_name, + (void *) cn ); } else { - for( name = &crt->subject; name != NULL; name = name->next ) - { - if( MBEDTLS_OID_CMP( MBEDTLS_OID_AT_CN, &name->oid ) == 0 && - x509_crt_check_cn( &name->val, cn, cn_len ) == 0 ) - { - break; - } - } - - if( name == NULL ) - *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; + ret = mbedtls_x509_name_cmp_raw( &frame->subject_raw, + &frame->subject_raw, + x509_crt_check_name, (void *) cn ); } + + mbedtls_x509_crt_frame_release( crt ); + + /* x509_crt_check_name() and x509_crt_subject_alt_check_name() + * return 1 when finding a name component matching `cn`. */ + if( ret == 1 ) + return( 0 ); + + if( ret != 0 ) + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + + *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; + return( ret ); } /* @@ -2566,7 +3495,6 @@ int mbedtls_x509_crt_verify_restartable( mbedtls_x509_crt *crt, mbedtls_x509_crt_restart_ctx *rs_ctx ) { int ret; - mbedtls_pk_type_t pk_type; mbedtls_x509_crt_verify_chain ver_chain; uint32_t ee_flags; @@ -2582,16 +3510,31 @@ int mbedtls_x509_crt_verify_restartable( mbedtls_x509_crt *crt, /* check name if requested */ if( cn != NULL ) - x509_crt_verify_name( crt, cn, &ee_flags ); + { + ret = x509_crt_verify_name( crt, cn, &ee_flags ); + if( ret != 0 ) + return( ret ); + } - /* Check the type and size of the key */ - pk_type = mbedtls_pk_get_type( &crt->pk ); + { + mbedtls_pk_context *pk; + mbedtls_pk_type_t pk_type; - if( x509_profile_check_pk_alg( profile, pk_type ) != 0 ) - ee_flags |= MBEDTLS_X509_BADCERT_BAD_PK; + ret = mbedtls_x509_crt_pk_acquire( crt, &pk ); + if( ret != 0 ) + return( MBEDTLS_ERR_X509_FATAL_ERROR ); - if( x509_profile_check_key( profile, &crt->pk ) != 0 ) - ee_flags |= MBEDTLS_X509_BADCERT_BAD_KEY; + /* Check the type and size of the key */ + pk_type = mbedtls_pk_get_type( pk ); + + if( x509_profile_check_pk_alg( profile, pk_type ) != 0 ) + ee_flags |= MBEDTLS_X509_BADCERT_BAD_PK; + + if( x509_profile_check_key( profile, pk ) != 0 ) + ee_flags |= MBEDTLS_X509_BADCERT_BAD_KEY; + + mbedtls_x509_crt_pk_release( crt ); + } /* Check the chain */ ret = x509_crt_verify_chain( crt, trust_ca, ca_crl, profile, @@ -2641,63 +3584,32 @@ void mbedtls_x509_crt_init( mbedtls_x509_crt *crt ) /* * Unallocate all certificate data */ + void mbedtls_x509_crt_free( mbedtls_x509_crt *crt ) { mbedtls_x509_crt *cert_cur = crt; mbedtls_x509_crt *cert_prv; - mbedtls_x509_name *name_cur; - mbedtls_x509_name *name_prv; - mbedtls_x509_sequence *seq_cur; - mbedtls_x509_sequence *seq_prv; if( crt == NULL ) return; do { + x509_crt_cache_free( cert_cur->cache ); + mbedtls_free( cert_cur->cache ); + +#if !defined(MBEDTLS_X509_ON_DEMAND_PARSING) mbedtls_pk_free( &cert_cur->pk ); #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) mbedtls_free( cert_cur->sig_opts ); #endif - name_cur = cert_cur->issuer.next; - while( name_cur != NULL ) - { - name_prv = name_cur; - name_cur = name_cur->next; - mbedtls_platform_zeroize( name_prv, sizeof( mbedtls_x509_name ) ); - mbedtls_free( name_prv ); - } - - name_cur = cert_cur->subject.next; - while( name_cur != NULL ) - { - name_prv = name_cur; - name_cur = name_cur->next; - mbedtls_platform_zeroize( name_prv, sizeof( mbedtls_x509_name ) ); - mbedtls_free( name_prv ); - } - - seq_cur = cert_cur->ext_key_usage.next; - while( seq_cur != NULL ) - { - seq_prv = seq_cur; - seq_cur = seq_cur->next; - mbedtls_platform_zeroize( seq_prv, - sizeof( mbedtls_x509_sequence ) ); - mbedtls_free( seq_prv ); - } - - seq_cur = cert_cur->subject_alt_names.next; - while( seq_cur != NULL ) - { - seq_prv = seq_cur; - seq_cur = seq_cur->next; - mbedtls_platform_zeroize( seq_prv, - sizeof( mbedtls_x509_sequence ) ); - mbedtls_free( seq_prv ); - } + mbedtls_x509_name_free( cert_cur->issuer.next ); + mbedtls_x509_name_free( cert_cur->subject.next ); + mbedtls_x509_sequence_free( cert_cur->ext_key_usage.next ); + mbedtls_x509_sequence_free( cert_cur->subject_alt_names.next ); +#endif /* !MBEDTLS_X509_ON_DEMAND_PARSING */ if( cert_cur->raw.p != NULL && cert_cur->own_buffer ) { diff --git a/library/x509_csr.c b/library/x509_csr.c index aa519fb93..9b58a86fe 100644 --- a/library/x509_csr.c +++ b/library/x509_csr.c @@ -38,6 +38,7 @@ #if defined(MBEDTLS_X509_CSR_PARSE_C) #include "mbedtls/x509_csr.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/oid.h" #include "mbedtls/platform_util.h" @@ -183,15 +184,17 @@ int mbedtls_x509_csr_parse_der( mbedtls_x509_csr *csr, mbedtls_x509_csr_free( csr ); return( MBEDTLS_ERR_X509_INVALID_FORMAT + ret ); } + p += len; + csr->subject_raw.len = p - csr->subject_raw.p; - if( ( ret = mbedtls_x509_get_name( &p, p + len, &csr->subject ) ) != 0 ) + if( ( ret = mbedtls_x509_get_name( csr->subject_raw.p, + csr->subject_raw.len, + &csr->subject ) ) != 0 ) { mbedtls_x509_csr_free( csr ); return( ret ); } - csr->subject_raw.len = p - csr->subject_raw.p; - /* * subjectPKInfo SubjectPublicKeyInfo */ @@ -357,8 +360,8 @@ int mbedtls_x509_csr_info( char *buf, size_t size, const char *prefix, ret = mbedtls_snprintf( p, n, "\n%ssigned using : ", prefix ); MBEDTLS_X509_SAFE_SNPRINTF; - ret = mbedtls_x509_sig_alg_gets( p, n, &csr->sig_oid, csr->sig_pk, csr->sig_md, - csr->sig_opts ); + ret = mbedtls_x509_sig_alg_gets( p, n, csr->sig_pk, + csr->sig_md, csr->sig_opts ); MBEDTLS_X509_SAFE_SNPRINTF; if( ( ret = mbedtls_x509_key_size_helper( key_size_str, BEFORE_COLON, diff --git a/library/x509write_crt.c b/library/x509write_crt.c index 10497e752..93cd82f7a 100644 --- a/library/x509write_crt.c +++ b/library/x509write_crt.c @@ -34,6 +34,7 @@ #if defined(MBEDTLS_X509_CRT_WRITE_C) #include "mbedtls/x509_crt.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/oid.h" #include "mbedtls/asn1write.h" #include "mbedtls/sha1.h" diff --git a/library/x509write_csr.c b/library/x509write_csr.c index d70ba0ed9..85331b163 100644 --- a/library/x509write_csr.c +++ b/library/x509write_csr.c @@ -33,6 +33,7 @@ #if defined(MBEDTLS_X509_CSR_WRITE_C) #include "mbedtls/x509_csr.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/oid.h" #include "mbedtls/asn1write.h" #include "mbedtls/platform_util.h" diff --git a/programs/ssl/query_config.c b/programs/ssl/query_config.c index d45a6634f..e62341d49 100644 --- a/programs/ssl/query_config.c +++ b/programs/ssl/query_config.c @@ -1466,6 +1466,22 @@ int query_config( const char *config ) } #endif /* MBEDTLS_VERSION_FEATURES */ +#if defined(MBEDTLS_X509_ON_DEMAND_PARSING) + if( strcmp( "MBEDTLS_X509_ON_DEMAND_PARSING", config ) == 0 ) + { + MACRO_EXPANSION_TO_STR( MBEDTLS_X509_ON_DEMAND_PARSING ); + return( 0 ); + } +#endif /* MBEDTLS_X509_ON_DEMAND_PARSING */ + +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) + if( strcmp( "MBEDTLS_X509_ALWAYS_FLUSH", config ) == 0 ) + { + MACRO_EXPANSION_TO_STR( MBEDTLS_X509_ALWAYS_FLUSH ); + return( 0 ); + } +#endif /* MBEDTLS_X509_ALWAYS_FLUSH */ + #if defined(MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3) if( strcmp( "MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3", config ) == 0 ) { diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index 5d751b6a7..8534dc235 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -1067,6 +1067,7 @@ static int ssl_async_start( mbedtls_ssl_context *ssl, const unsigned char *input, size_t input_len ) { + int ret; ssl_async_key_context_t *config_data = mbedtls_ssl_conf_get_async_config_data( ssl->conf ); unsigned slot; @@ -1075,9 +1076,17 @@ static int ssl_async_start( mbedtls_ssl_context *ssl, { char dn[100]; - if( mbedtls_x509_dn_gets( dn, sizeof( dn ), &cert->subject ) > 0 ) + mbedtls_x509_name *subject; + + ret = mbedtls_x509_crt_get_subject( cert, &subject ); + if( ret != 0 ) + return( ret ); + + if( mbedtls_x509_dn_gets( dn, sizeof( dn ), subject ) > 0 ) mbedtls_printf( "Async %s callback: looking for DN=%s\n", op_name, dn ); + + mbedtls_x509_name_free( subject ); } /* Look for a private key that matches the public key in cert. @@ -1086,8 +1095,14 @@ static int ssl_async_start( mbedtls_ssl_context *ssl, * public key. */ for( slot = 0; slot < config_data->slots_used; slot++ ) { - if( mbedtls_pk_check_pair( &cert->pk, - config_data->slots[slot].pk ) == 0 ) + mbedtls_pk_context *pk; + int match; + ret = mbedtls_x509_crt_pk_acquire( cert, &pk ); + if( ret != 0 ) + return( ret ); + match = mbedtls_pk_check_pair( pk, config_data->slots[slot].pk ); + mbedtls_x509_crt_pk_release( cert ); + if( match == 0 ) break; } if( slot == config_data->slots_used ) diff --git a/programs/x509/cert_write.c b/programs/x509/cert_write.c index 497c3376b..521f25a93 100644 --- a/programs/x509/cert_write.c +++ b/programs/x509/cert_write.c @@ -524,6 +524,8 @@ int main( int argc, char *argv[] ) // if( !opt.selfsign && strlen( opt.issuer_crt ) ) { + mbedtls_x509_name *subject; + /* * 1.0.a. Load the certificates */ @@ -538,8 +540,17 @@ int main( int argc, char *argv[] ) goto exit; } + ret = mbedtls_x509_crt_get_subject( &issuer_crt, &subject ); + if( ret != 0 ) + { + mbedtls_strerror( ret, buf, 1024 ); + mbedtls_printf( " failed\n ! mbedtls_x509_crt_get_subject " + "returned -0x%04x - %s\n\n", -ret, buf ); + goto exit; + } + ret = mbedtls_x509_dn_gets( issuer_name, sizeof(issuer_name), - &issuer_crt.subject ); + subject ); if( ret < 0 ) { mbedtls_strerror( ret, buf, 1024 ); @@ -550,6 +561,8 @@ int main( int argc, char *argv[] ) opt.issuer_name = issuer_name; + mbedtls_x509_name_free( subject ); + mbedtls_printf( " ok\n" ); } @@ -627,12 +640,24 @@ int main( int argc, char *argv[] ) // if( strlen( opt.issuer_crt ) ) { - if( mbedtls_pk_check_pair( &issuer_crt.pk, issuer_key ) != 0 ) + mbedtls_pk_context pk; + ret = mbedtls_x509_crt_get_pk( &issuer_crt, &pk ); + if( ret != 0 ) + { + mbedtls_strerror( ret, buf, 1024 ); + mbedtls_printf( " failed\n ! mbedtls_x509_crt_get_pk " + "returned -0x%04x - %s\n\n", -ret, buf ); + goto exit; + } + + if( mbedtls_pk_check_pair( &pk, issuer_key ) != 0 ) { mbedtls_printf( " failed\n ! issuer_key does not match " "issuer certificate\n\n" ); goto exit; } + + mbedtls_pk_free( &pk ); } mbedtls_printf( " ok\n" ); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a8e7523e5..5938a5fc8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,5 @@ +option(LINK_WITH_PTHREAD "Explicitly link mbed TLS library to pthread." OFF) + set(libs mbedtls ) @@ -10,6 +12,10 @@ if(ENABLE_ZLIB_SUPPORT) set(libs ${libs} ${ZLIB_LIBRARIES}) endif(ENABLE_ZLIB_SUPPORT) +if(LINK_WITH_PTHREAD) + set(libs ${libs} pthread) +endif(LINK_WITH_PTHREAD) + find_package(Perl) if(NOT PERL_FOUND) message(FATAL_ERROR "Cannot build test suites without Perl") diff --git a/tests/Makefile b/tests/Makefile index 4ef74177b..30fbafdff 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -53,11 +53,20 @@ ifdef ZLIB LOCAL_LDFLAGS += -lz endif +# Pthread shared library extension +ifdef PTHREAD +LOCAL_LDFLAGS += -lpthread +endif + # A test application is built for each suites/test_suite_*.data file. # Application name is same as .data file's base name and can be # constructed by stripping path 'suites/' and extension .data. APPS = $(basename $(subst suites/,,$(wildcard suites/test_suite_*.data))) +ifndef PTHREAD +APPS := $(filter-out test_suite_x509parse_pthread, $(APPS)) +endif + # Construct executable name by adding OS specific suffix $(EXEXT). BINARIES := $(addsuffix $(EXEXT),$(APPS)) @@ -136,4 +145,3 @@ $(EMBEDDED_TESTS): embedded_%: suites/$$(firstword $$(subst ., ,$$*)).function s -o ./TESTS/mbedtls/$* generate-target-tests: $(EMBEDDED_TESTS) - diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 2f1a1b54e..7ae1bc5c0 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -728,7 +728,7 @@ component_test_full_cmake_clang () { msg "build: cmake, full config, clang" # ~ 50s scripts/config.pl full scripts/config.pl unset MBEDTLS_MEMORY_BACKTRACE # too slow for tests - CC=clang cmake -D CMAKE_BUILD_TYPE:String=Check -D ENABLE_TESTING=On . + CC=clang cmake -D LINK_WITH_PTHREAD=1 -D CMAKE_BUILD_TYPE:String=Check -D ENABLE_TESTING=On . make msg "test: main suites (full config)" # ~ 5s @@ -750,7 +750,7 @@ component_build_deprecated () { scripts/config.pl set MBEDTLS_DEPRECATED_WARNING # Build with -O -Wextra to catch a maximum of issues. make CC=gcc CFLAGS='-O -Werror -Wall -Wextra' lib programs - make CC=gcc CFLAGS='-O -Werror -Wall -Wextra -Wno-unused-function' tests + make PTHREAD=1 CC=gcc CFLAGS='-O -Werror -Wall -Wextra -Wno-unused-function' tests msg "build: make, full config + DEPRECATED_REMOVED, clang -O" # ~ 30s # No cleanup, just tweak the configuration and rebuild @@ -759,7 +759,7 @@ component_build_deprecated () { scripts/config.pl set MBEDTLS_DEPRECATED_REMOVED # Build with -O -Wextra to catch a maximum of issues. make CC=clang CFLAGS='-O -Werror -Wall -Wextra' lib programs - make CC=clang CFLAGS='-O -Werror -Wall -Wextra -Wno-unused-function' tests + make PTHREAD=1 CC=clang CFLAGS='-O -Werror -Wall -Wextra -Wno-unused-function' tests } @@ -807,7 +807,7 @@ component_test_check_params_without_platform () { scripts/config.pl unset MBEDTLS_PLATFORM_SNPRINTF_ALT scripts/config.pl unset MBEDTLS_ENTROPY_NV_SEED scripts/config.pl unset MBEDTLS_PLATFORM_C - make CC=gcc CFLAGS='-Werror -O1' all test + make CC=gcc PTHREAD=1 CFLAGS='-Werror -O1' all test } component_test_check_params_silent () { @@ -815,7 +815,7 @@ component_test_check_params_silent () { scripts/config.pl full # includes CHECK_PARAMS scripts/config.pl unset MBEDTLS_MEMORY_BACKTRACE # too slow for tests sed -i 's/.*\(#define MBEDTLS_PARAM_FAILED( cond )\).*/\1/' "$CONFIG_H" - make CC=gcc CFLAGS='-Werror -O1' all test + make CC=gcc PTHREAD=1 CFLAGS='-Werror -O1' all test } component_test_no_platform () { @@ -837,8 +837,8 @@ component_test_no_platform () { scripts/config.pl unset MBEDTLS_FS_IO # Note, _DEFAULT_SOURCE needs to be defined for platforms using glibc version >2.19, # to re-enable platform integration features otherwise disabled in C99 builds - make CC=gcc CFLAGS='-Werror -Wall -Wextra -std=c99 -pedantic -O0 -D_DEFAULT_SOURCE' lib programs - make CC=gcc CFLAGS='-Werror -Wall -Wextra -O0' test + make CC=gcc PTHREAD=1 CFLAGS='-Werror -Wall -Wextra -std=c99 -pedantic -O0 -D_DEFAULT_SOURCE' lib programs + make CC=gcc PTHREAD=1 CFLAGS='-Werror -Wall -Wextra -O0' test } component_build_no_std_function () { @@ -847,21 +847,21 @@ component_build_no_std_function () { scripts/config.pl full scripts/config.pl set MBEDTLS_PLATFORM_NO_STD_FUNCTIONS scripts/config.pl unset MBEDTLS_ENTROPY_NV_SEED - make CC=gcc CFLAGS='-Werror -Wall -Wextra -O0' + make CC=gcc PTHREAD=1 CFLAGS='-Werror -Wall -Wextra -O0' } component_build_no_ssl_srv () { msg "build: full config except ssl_srv.c, make, gcc" # ~ 30s scripts/config.pl full scripts/config.pl unset MBEDTLS_SSL_SRV_C - make CC=gcc CFLAGS='-Werror -Wall -Wextra -O0' + make CC=gcc PTHREAD=1 CFLAGS='-Werror -Wall -Wextra -O0' } component_build_no_ssl_cli () { msg "build: full config except ssl_cli.c, make, gcc" # ~ 30s scripts/config.pl full scripts/config.pl unset MBEDTLS_SSL_CLI_C - make CC=gcc CFLAGS='-Werror -Wall -Wextra -O0' + make CC=gcc PTHREAD=1 CFLAGS='-Werror -Wall -Wextra -O0' } component_build_no_sockets () { @@ -871,7 +871,7 @@ component_build_no_sockets () { scripts/config.pl full scripts/config.pl unset MBEDTLS_NET_C # getaddrinfo() undeclared, etc. scripts/config.pl set MBEDTLS_NO_PLATFORM_ENTROPY # uses syscall() on GNU/Linux - make CC=gcc CFLAGS='-Werror -Wall -Wextra -O0 -std=c99 -pedantic' lib + make CC=gcc PTHREAD=1 CFLAGS='-Werror -Wall -Wextra -O0 -std=c99 -pedantic' lib } component_test_no_max_fragment_length () { @@ -918,6 +918,22 @@ component_test_asan_remove_peer_certificate_no_renego () { if_build_succeeded tests/compat.sh } +component_test_asan_on_demand_parsing_remove_peer_cert () { + msg "build: default config, no peer CRT, on-demand CRT parsing (ASan build)" + scripts/config.pl unset MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + scripts/config.pl set MBEDTLS_X509_ON_DEMAND_PARSING + scripts/config.pl set MBEDTLS_THREADING_C + scripts/config.pl set MBEDTLS_THREADING_PTHREAD + CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan -D LINK_WITH_PTHREAD=1 . + make + + msg "test: !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE, MBEDTLS_X509_ON_DEMAND_PARSING" + make test + + msg "test: ssl-opt.sh, !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE, MBEDTLS_X509_ON_DEMAND_PARSING" + if_build_succeeded tests/ssl-opt.sh +} + component_test_no_max_fragment_length_small_ssl_out_content_len () { msg "build: no MFL extension, small SSL_OUT_CONTENT_LEN (ASan build)" scripts/config.pl unset MBEDTLS_SSL_MAX_FRAGMENT_LENGTH @@ -1008,7 +1024,10 @@ component_test_m32_o0 () { # Build once with -O0, to compile out the i386 specific inline assembly msg "build: i386, make, gcc -O0 (ASan build)" # ~ 30s scripts/config.pl full - make CC=gcc CFLAGS='-O0 -Werror -Wall -Wextra -m32 -fsanitize=address' + scripts/config.pl unset MBEDTLS_MEMORY_BACKTRACE + scripts/config.pl unset MBEDTLS_MEMORY_BUFFER_ALLOC_C + scripts/config.pl unset MBEDTLS_MEMORY_DEBUG + make CC=gcc PTHREAD=1 CFLAGS='-O0 -Werror -Wall -Wextra -m32 -fsanitize=address' msg "test: i386, make, gcc -O0 (ASan build)" make test @@ -1027,7 +1046,7 @@ component_test_m32_o1 () { scripts/config.pl unset MBEDTLS_MEMORY_BACKTRACE scripts/config.pl unset MBEDTLS_MEMORY_BUFFER_ALLOC_C scripts/config.pl unset MBEDTLS_MEMORY_DEBUG - make CC=gcc CFLAGS='-O1 -Werror -Wall -Wextra -m32 -fsanitize=address' + make CC=gcc PTHREAD=1 CFLAGS='-O1 -Werror -Wall -Wextra -m32 -fsanitize=address' msg "test: i386, make, gcc -O1 (ASan build)" make test @@ -1042,7 +1061,7 @@ support_test_m32_o1 () { component_test_mx32 () { msg "build: 64-bit ILP32, make, gcc" # ~ 30s scripts/config.pl full - make CC=gcc CFLAGS='-Werror -Wall -Wextra -mx32' + make CC=gcc PTHREAD=1 CFLAGS='-Werror -Wall -Wextra -mx32' msg "test: 64-bit ILP32, make, gcc" make test diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index f8d787533..6536cc9c7 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -935,6 +935,10 @@ X509 Parse Selftest depends_on:MBEDTLS_SHA1_C:MBEDTLS_PEM_PARSE_C:MBEDTLS_CERTS_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15 x509_selftest: +X509 nested acquire +depends_on:MBEDTLS_RSA_C:MBEDTLS_SHA256_C +x509_nested_acquire:"308196308180a0030201008204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffff300d06092a864886f70d01010b0500030200ff" + X509 CRT ASN1 (Empty Certificate) x509parse_crt:"":"":MBEDTLS_ERR_X509_INVALID_FORMAT diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function index c9fe63f09..25b0d7f8f 100644 --- a/tests/suites/test_suite_x509parse.function +++ b/tests/suites/test_suite_x509parse.function @@ -4,6 +4,7 @@ #include "mbedtls/x509_crt.h" #include "mbedtls/x509_crl.h" #include "mbedtls/x509_csr.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/pem.h" #include "mbedtls/oid.h" #include "mbedtls/base64.h" @@ -142,25 +143,48 @@ int verify_print( void *data, mbedtls_x509_crt *crt, int certificate_depth, uint verify_print_context *ctx = (verify_print_context *) data; char *p = ctx->p; size_t n = ctx->buf + sizeof( ctx->buf ) - ctx->p; + mbedtls_x509_crt_frame const *frame; + mbedtls_x509_name *subject; ((void) flags); - ret = mbedtls_snprintf( p, n, "depth %d - serial ", certificate_depth ); - MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_crt_get_subject( crt, &subject ); + if( ret != 0 ) + return( ret ); - ret = mbedtls_x509_serial_gets( p, n, &crt->serial ); - MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_crt_frame_acquire( crt, &frame ); + if( ret != 0 ) + return( ret ); + + ret = mbedtls_snprintf( p, n, "depth %d - serial ", certificate_depth ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; + + { + mbedtls_x509_buf serial; + serial.p = frame->serial.p; + serial.len = frame->serial.len; + ret = mbedtls_x509_serial_gets( p, n, &serial ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; + } ret = mbedtls_snprintf( p, n, " - subject " ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; - ret = mbedtls_x509_dn_gets( p, n, &crt->subject ); - MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_dn_gets( p, n, subject ); + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; ret = mbedtls_snprintf( p, n, " - flags 0x%08x\n", *flags ); - MBEDTLS_X509_SAFE_SNPRINTF; + MBEDTLS_X509_SAFE_SNPRINTF_WITH_CLEANUP; ctx->p = p; +cleanup: + + mbedtls_x509_name_free( subject ); + mbedtls_x509_crt_frame_release( crt ); + + if( ret < 0 ) + return( ret ); + return( 0 ); } #endif /* MBEDTLS_X509_CRT_PARSE_C */ @@ -428,15 +452,19 @@ void mbedtls_x509_dn_gets( char * crt_file, char * entity, char * result_str ) mbedtls_x509_crt crt; char buf[2000]; int res = 0; + mbedtls_x509_name *subject = NULL, *issuer = NULL; mbedtls_x509_crt_init( &crt ); memset( buf, 0, 2000 ); TEST_ASSERT( mbedtls_x509_crt_parse_file( &crt, crt_file ) == 0 ); + TEST_ASSERT( mbedtls_x509_crt_get_subject( &crt, &subject ) == 0 ); + TEST_ASSERT( mbedtls_x509_crt_get_issuer( &crt, &issuer ) == 0 ); + if( strcmp( entity, "subject" ) == 0 ) - res = mbedtls_x509_dn_gets( buf, 2000, &crt.subject ); + res = mbedtls_x509_dn_gets( buf, 2000, subject ); else if( strcmp( entity, "issuer" ) == 0 ) - res = mbedtls_x509_dn_gets( buf, 2000, &crt.issuer ); + res = mbedtls_x509_dn_gets( buf, 2000, issuer ); else TEST_ASSERT( "Unknown entity" == 0 ); @@ -446,6 +474,8 @@ void mbedtls_x509_dn_gets( char * crt_file, char * entity, char * result_str ) TEST_ASSERT( strcmp( buf, result_str ) == 0 ); exit: + mbedtls_x509_name_free( issuer ); + mbedtls_x509_name_free( subject ); mbedtls_x509_crt_free( &crt ); } /* END_CASE */ @@ -453,16 +483,18 @@ exit: /* BEGIN_CASE depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CRT_PARSE_C */ void mbedtls_x509_time_is_past( char * crt_file, char * entity, int result ) { - mbedtls_x509_crt crt; + mbedtls_x509_crt crt; + mbedtls_x509_crt_frame frame; mbedtls_x509_crt_init( &crt ); TEST_ASSERT( mbedtls_x509_crt_parse_file( &crt, crt_file ) == 0 ); + TEST_ASSERT( mbedtls_x509_crt_get_frame( &crt, &frame ) == 0 ); if( strcmp( entity, "valid_from" ) == 0 ) - TEST_ASSERT( mbedtls_x509_time_is_past( &crt.valid_from ) == result ); + TEST_ASSERT( mbedtls_x509_time_is_past( &frame.valid_from ) == result ); else if( strcmp( entity, "valid_to" ) == 0 ) - TEST_ASSERT( mbedtls_x509_time_is_past( &crt.valid_to ) == result ); + TEST_ASSERT( mbedtls_x509_time_is_past( &frame.valid_to ) == result ); else TEST_ASSERT( "Unknown entity" == 0 ); @@ -474,16 +506,18 @@ exit: /* BEGIN_CASE depends_on:MBEDTLS_FS_IO:MBEDTLS_X509_CRT_PARSE_C */ void mbedtls_x509_time_is_future( char * crt_file, char * entity, int result ) { - mbedtls_x509_crt crt; + mbedtls_x509_crt crt; + mbedtls_x509_crt_frame frame; mbedtls_x509_crt_init( &crt ); TEST_ASSERT( mbedtls_x509_crt_parse_file( &crt, crt_file ) == 0 ); + TEST_ASSERT( mbedtls_x509_crt_get_frame( &crt, &frame ) == 0 ); if( strcmp( entity, "valid_from" ) == 0 ) - TEST_ASSERT( mbedtls_x509_time_is_future( &crt.valid_from ) == result ); + TEST_ASSERT( mbedtls_x509_time_is_future( &frame.valid_from ) == result ); else if( strcmp( entity, "valid_to" ) == 0 ) - TEST_ASSERT( mbedtls_x509_time_is_future( &crt.valid_to ) == result ); + TEST_ASSERT( mbedtls_x509_time_is_future( &frame.valid_to ) == result ); else TEST_ASSERT( "Unknown entity" == 0 ); @@ -563,6 +597,99 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_X509_CRT_PARSE_C */ +void x509_nested_acquire( data_t * buf ) +{ + /* This tests exercises the behavior of the library when + * facing nested calls to mbedtls_x509_crt_xxx_acquire(). + * This is allowed if !MBEDTLS_X509_ALWAYS_FLUSH or + * MBEDTLS_THREADING_C, but forbidden otherwise. */ + + mbedtls_x509_crt crt; + mbedtls_x509_crt_init( &crt ); + TEST_ASSERT( mbedtls_x509_crt_parse_der( &crt, buf->x, buf->len ) == 0 ); + + /* Nested aquire for CRT frames */ + { + int ret; + mbedtls_x509_crt_frame const *frame1; + mbedtls_x509_crt_frame const *frame2; + + /* Perform a (hopefully) innocent acquire-release pair first. */ + + TEST_ASSERT( mbedtls_x509_crt_frame_acquire( &crt, &frame1 ) == 0 ); + TEST_ASSERT( mbedtls_x509_crt_frame_release( &crt ) == 0 ); + + /* Perform two nested acquire calls. */ + + TEST_ASSERT( mbedtls_x509_crt_frame_acquire( &crt, &frame1 ) == 0 ); + + ret = mbedtls_x509_crt_frame_acquire( &crt, &frame2 ); +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) && \ + !defined(MBEDTLS_THREADING_C) + TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR ); +#else + TEST_ASSERT( ret == 0 ); + TEST_ASSERT( mbedtls_x509_crt_frame_release( &crt ) == 0 ); +#endif + + TEST_ASSERT( mbedtls_x509_crt_frame_release( &crt ) == 0 ); + + ret = mbedtls_x509_crt_frame_release( &crt ); + + /* In contexts which use resource counting, we expect an + * error on an attempted release() without prior acquire(). */ +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) && \ + !defined(MBEDTLS_THREADING_C) + TEST_ASSERT( ret == 0 ); +#else + TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR ); +#endif + } + + /* Nested aquire for PK contexts */ + { + int ret; + mbedtls_pk_context *pk1; + mbedtls_pk_context *pk2; + + /* Perform a (hopefully) innocent acquire-release pair first. */ + + TEST_ASSERT( mbedtls_x509_crt_pk_acquire( &crt, &pk1 ) == 0 ); + TEST_ASSERT( mbedtls_x509_crt_pk_release( &crt ) == 0 ); + + /* Perform two nested acquire calls. */ + + TEST_ASSERT( mbedtls_x509_crt_pk_acquire( &crt, &pk1 ) == 0 ); + + ret = mbedtls_x509_crt_pk_acquire( &crt, &pk2 ); +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) && \ + !defined(MBEDTLS_THREADING_C) + TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR ); +#else + TEST_ASSERT( ret == 0 ); + TEST_ASSERT( mbedtls_x509_crt_pk_release( &crt ) == 0 ); +#endif + + TEST_ASSERT( mbedtls_x509_crt_pk_release( &crt ) == 0 ); + + ret = mbedtls_x509_crt_pk_release( &crt ); + + /* In contexts which use resource counting, we expect an + * error on an attempted release() without prior acquire(). */ +#if defined(MBEDTLS_X509_ALWAYS_FLUSH) && \ + !defined(MBEDTLS_THREADING_C) + TEST_ASSERT( ret == 0 ); +#else + TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR ); +#endif + } + +exit: + mbedtls_x509_crt_free( &crt ); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_X509_CRL_PARSE_C:!MBEDTLS_X509_REMOVE_INFO */ void x509parse_crl( data_t * buf, char * result_str, int result ) { diff --git a/tests/suites/test_suite_x509parse_pthread.data b/tests/suites/test_suite_x509parse_pthread.data new file mode 100644 index 000000000..7940b7f57 --- /dev/null +++ b/tests/suites/test_suite_x509parse_pthread.data @@ -0,0 +1,19 @@ +X509 CRT concurrent verification #1 (RSA cert, RSA CA) +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP192R1_ENABLED:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_SHA1_C +x509_verify_thread:"data_files/server1.crt":"data_files/test-ca.crt":0:0:25:50 + +X509 CRT concurrent verification #2 (EC cert, RSA CA) +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP192R1_ENABLED:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_SHA1_C +x509_verify_thread:"data_files/server3.crt":"data_files/test-ca.crt":0:0:25:50 + +X509 CRT concurrent verification #3 (RSA cert, EC CA) +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_ECDSA_C:MBEDTLS_SHA256_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_ECP_DP_SECP384R1_ENABLED +x509_verify_thread:"data_files/server4.crt":"data_files/test-ca2.crt":0:0:25:50 + +X509 CRT concurrent verification #4 (EC cert, EC CA) +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_SHA256_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_ECP_DP_SECP384R1_ENABLED +x509_verify_thread:"data_files/server5.crt":"data_files/test-ca2.crt":0:0:25:50 + +X509 CRT concurrent verification #5 (RSA cert, RSA CA, RSASSA-PSS) +depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_X509_RSASSA_PSS_SUPPORT:MBEDTLS_SHA1_C +x509_verify_thread:"data_files/server9-with-ca.crt":"data_files/test-ca.crt":0:0:25:50 diff --git a/tests/suites/test_suite_x509parse_pthread.function b/tests/suites/test_suite_x509parse_pthread.function new file mode 100644 index 000000000..2728e9617 --- /dev/null +++ b/tests/suites/test_suite_x509parse_pthread.function @@ -0,0 +1,125 @@ +/* BEGIN_HEADER */ +#include "mbedtls/bignum.h" +#include "mbedtls/x509.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/x509_crl.h" +#include "mbedtls/x509_csr.h" +#include "mbedtls/x509_internal.h" +#include "mbedtls/pem.h" +#include "mbedtls/oid.h" +#include "mbedtls/base64.h" +#include "string.h" + +/* Profile for backward compatibility. Allows SHA-1, unlike the default + profile. */ +const mbedtls_x509_crt_profile compat_profile = +{ + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ) | + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_RIPEMD160 ) | + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) | + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) | + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) | + MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ), + 0xFFFFFFF, /* Any PK alg */ + 0xFFFFFFF, /* Any curve */ + 1024, +}; + +typedef struct +{ + mbedtls_x509_crt *crt; + mbedtls_x509_crt *ca; + uint32_t expected_flags; + unsigned id; + int expected_result; + int iter_total; + int result; +} x509_verify_thread_ctx; + +void* x509_verify_thread_worker( void *p ) +{ + unsigned iter_cnt; + x509_verify_thread_ctx *ctx = (x509_verify_thread_ctx *) p; + + for( iter_cnt=0; iter_cnt < (unsigned) ctx->iter_total; iter_cnt++ ) + { + uint32_t flags; + int res; + + res = mbedtls_x509_crt_verify_with_profile( ctx->crt, ctx->ca, + NULL, &compat_profile, + NULL, &flags, NULL, NULL ); + if( res != ctx->expected_result || + flags != ctx->expected_flags ) + { + ctx->result = 1; + pthread_exit( NULL ); + } + } + + ctx->result = 0; + pthread_exit( NULL ); + return( NULL ); +} +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_THREADING_PTHREAD:MBEDTLS_X509_CRT_PARSE_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE depends_on:MBEDTLS_FS_IO */ +void x509_verify_thread( char *crt_file, char *ca_file, + int result, int flags_result, + int thread_total, + int iterations_per_thread ) +{ + x509_verify_thread_ctx *thread_ctx; + pthread_t *threads; + int cur_thread; + + mbedtls_x509_crt crt; + mbedtls_x509_crt ca; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + TEST_ASSERT( psa_crypto_init() == 0 ); +#endif + + mbedtls_x509_crt_init( &crt ); + mbedtls_x509_crt_init( &ca ); + threads = mbedtls_calloc( thread_total, sizeof( pthread_t ) ); + thread_ctx = mbedtls_calloc( thread_total, sizeof( x509_verify_thread_ctx ) ); + + TEST_ASSERT( mbedtls_x509_crt_parse_file( &crt, crt_file ) == 0 ); + TEST_ASSERT( mbedtls_x509_crt_parse_file( &ca, ca_file ) == 0 ); + TEST_ASSERT( threads != NULL ); + + /* Start all verify threads */ + for( cur_thread = 0; cur_thread < thread_total; cur_thread++ ) + { + thread_ctx[ cur_thread ].id = (unsigned) cur_thread; + thread_ctx[ cur_thread ].ca = &ca; + thread_ctx[ cur_thread ].crt = &crt; + thread_ctx[ cur_thread ].expected_result = result; + thread_ctx[ cur_thread ].expected_flags = flags_result; + thread_ctx[ cur_thread ].iter_total = iterations_per_thread; + TEST_ASSERT( pthread_create( &threads[ cur_thread ], NULL, + &x509_verify_thread_worker, + &thread_ctx[ cur_thread ] ) == 0 ); + } + + /* Wait for all threads to complete */ + for( cur_thread = 0; cur_thread < thread_total; cur_thread++ ) + TEST_ASSERT( pthread_join( threads[ cur_thread ], NULL ) == 0 ); + + /* Check their results */ + for( cur_thread = 0; cur_thread < thread_total; cur_thread++ ) + TEST_ASSERT( thread_ctx[ cur_thread ].result == 0 ); + +exit: + mbedtls_free( threads ); + mbedtls_free( thread_ctx ); + mbedtls_x509_crt_free( &crt ); + mbedtls_x509_crt_free( &ca ); +} +/* END_CASE */ diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function index 535807e3a..923716594 100644 --- a/tests/suites/test_suite_x509write.function +++ b/tests/suites/test_suite_x509write.function @@ -2,6 +2,7 @@ #include "mbedtls/bignum.h" #include "mbedtls/x509_crt.h" #include "mbedtls/x509_csr.h" +#include "mbedtls/x509_internal.h" #include "mbedtls/pem.h" #include "mbedtls/oid.h" #include "mbedtls/rsa.h" @@ -216,7 +217,7 @@ void mbedtls_x509_string_to_names( char * name, char * parsed_name, int result ) { int ret; - size_t len = 0; + size_t len; mbedtls_asn1_named_data *names = NULL; mbedtls_x509_name parsed, *parsed_cur, *parsed_prv; unsigned char buf[1024], out[1024], *c; @@ -234,10 +235,9 @@ void mbedtls_x509_string_to_names( char * name, char * parsed_name, int result ret = mbedtls_x509_write_names( &c, buf, names ); TEST_ASSERT( ret > 0 ); + len = (size_t) ret; - TEST_ASSERT( mbedtls_asn1_get_tag( &c, buf + sizeof( buf ), &len, - MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) == 0 ); - TEST_ASSERT( mbedtls_x509_get_name( &c, buf + sizeof( buf ), &parsed ) == 0 ); + TEST_ASSERT( mbedtls_x509_get_name( c, len, &parsed ) == 0 ); ret = mbedtls_x509_dn_gets( (char *) out, sizeof( out ), &parsed ); TEST_ASSERT( ret > 0 ); diff --git a/visualc/VS2010/mbedTLS.vcxproj b/visualc/VS2010/mbedTLS.vcxproj index 73c92bda5..2ec9178af 100644 --- a/visualc/VS2010/mbedTLS.vcxproj +++ b/visualc/VS2010/mbedTLS.vcxproj @@ -223,6 +223,7 @@ +