Merge remote-tracking branch 'restricted/pr/584' into baremetal-proposed
* restricted/pr/584: (140 commits)
Remove superfluous new line in x509.c
Add comment about X.509 name comparison of buffer with itself
[Fixup] Add missing PK release call in Cert Verify parsing
Fix guard controlling whether nested acquire calls are allowed
Add X.509 CRT test for nested calls for CRT frame / PK acquire
Don't return threading error on release()-without-acquire() calls
Don't allow nested CRT acquire()-calls if MBEDTLS_X509_ALWAYS_FLUSH
Make X.509 CRT cache reference counting unconditional
Remove memory buffer alloc from i386 test in all.sh
Don't mention pk_sign() in the context of public-key contexts
Don't use assertion for failures of mbedtls_x509_crt_x_acquire()
Fix copy pasta in x509_crt.h
Reference copy-less versions of X.509 CRT frame/PK getters
x509_crt.c: Add blank line to increase readability
[FIXUP] Fix bug in ASN.1 traversal of silently ignored tag
[FIXUP] Fix typo in declaration of mbedtls_x509_memcasecmp()
Move signature-info extraction out of MBEDTLS_X509_REMOVE_INFO
Fix certificate validity checking logic to work with !TIME_DATE
Simplify X.509 CRT version check in UID parsing
Remove unused variable warning in on-demand X.509 parsing
...
diff --git a/ChangeLog b/ChangeLog
index 49c3acf..e3c335e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -49,6 +49,13 @@
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 330b513..12fa136 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 96c1c9a..94990fe 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 @@
size_t *len );
/**
- * \brief Parses and splits an ASN.1 "SEQUENCE OF <tag>"
- * 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 <tag>"
+ * 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 2116521..796b0bb 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -1758,6 +1758,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
*
* If set, the X509 parser will not break-off when parsing an X509 certificate
diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h
index ff06d13..5d091bc 100644
--- a/include/mbedtls/x509.h
+++ b/include/mbedtls/x509.h
@@ -184,6 +184,15 @@
*/
/**
+ * 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.
*/
typedef mbedtls_asn1_buf mbedtls_x509_buf;
@@ -269,6 +278,29 @@
*/
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 @@
#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 @@
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 2bb95de..2950f30 100644
--- a/include/mbedtls/x509_crl.h
+++ b/include/mbedtls/x509_crl.h
@@ -75,7 +75,7 @@
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 09ba69f..3eee460 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 @@
* \{
*/
+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 @@
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 @@
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_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 0000000..6ca3db5
--- /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 171c340..aac253b 100644
--- a/library/asn1parse.c
+++ b/library/asn1parse.c
@@ -229,6 +229,103 @@
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 <tag>"
+ * 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 @@
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 @@
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 4194618..d94754a 100644
--- a/library/bignum.c
+++ b/library/bignum.c
@@ -127,7 +127,7 @@
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 @@
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 2739975..5821716 100644
--- a/library/cipher.c
+++ b/library/cipher.c
@@ -331,13 +331,13 @@
? 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 @@
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 @@
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 @@
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 6dd8c5d..54572ef 100644
--- a/library/cipher_wrap.c
+++ b/library/cipher_wrap.c
@@ -1987,7 +1987,7 @@
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 c6788b6..da4ceac 100644
--- a/library/debug.c
+++ b/library/debug.c
@@ -71,7 +71,7 @@
*/
#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 @@
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 @@
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 816b130..bbcf630 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 17611d6..0f75b1c 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -2334,7 +2334,15 @@
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 @@
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 @@
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 @@
}
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 ecde1b0..94b4d73 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -791,15 +791,58 @@
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 @@
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 @@
#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 @@
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 @@
*/
#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 @@
}
#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 @@
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 @@
if( 0 != ret )
{
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record" ), ret );
- return( ret );
+ goto exit;
}
ssl->state++;
@@ -4219,7 +4291,8 @@
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 @@
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 @@
{
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 @@
{
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 @@
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 @@
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 @@
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 @@
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 fff20ff..4c1a5c5 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -6456,6 +6456,7 @@
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 @@
/*
* 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 @@
&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 @@
#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 @@
* 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 @@
}
#endif /* MBEDTLS_DEBUG_C */
- return( ret );
+ return( verify_ret );
}
#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
@@ -6760,8 +6772,8 @@
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 5e9d923..8d3f2ad 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -534,6 +534,12 @@
#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 858dd90..89645a7 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 @@
* 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 );
+ ret = mbedtls_asn1_get_tag( p, end, &oid->len, MBEDTLS_ASN1_OID );
+ if( ret != 0 )
+ goto exit;
- 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 );
-
+ 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 @@
* 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 @@
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 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 @@
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 @@
/* 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 @@
/*
* 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 @@
}
#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 546e8fa..1639630 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 4f5507f..3113de4 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 @@
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 @@
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 e4a35f6..61e4a9e 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
*/
@@ -228,44 +608,18 @@
}
/*
- * 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 @@
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 );
}
@@ -290,74 +644,6 @@
}
/*
- * 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
*/
static void x509_crt_verify_chain_reset(
@@ -445,15 +731,13 @@
*/
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 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
*
* 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;
+ ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
+ goto err;
+ }
- 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;
-
+ /*
+ * 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;
+ }
- 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 );
+ /* 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;
+
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 @@
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 @@
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 @@
/*
* 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 @@
}
#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 @@
#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 @@
#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 @@
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 @@
/*
* 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 @@
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 @@
* 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 @@
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 @@
/*
* 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 @@
crl_list = crl_list->next;
}
+ mbedtls_x509_crt_pk_release( ca_crt );
return( flags );
}
#endif /* MBEDTLS_X509_CRL_PARSE_C */
@@ -1912,38 +2703,49 @@
/*
* 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 @@
*
* 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 @@
#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 @@
* - 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* - 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 @@
#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 @@
* 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 @@
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 @@
/* 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 @@
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 @@
#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 @@
/* 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 @@
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 @@
/*
* 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 @@
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 @@
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 @@
/* 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 @@
/*
* 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 aa519fb..9b58a86 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 @@
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 @@
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 10497e7..93cd82f 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 d70ba0e..85331b1 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 d45a663..e62341d 100644
--- a/programs/ssl/query_config.c
+++ b/programs/ssl/query_config.c
@@ -1466,6 +1466,22 @@
}
#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 5d751b6..8534dc2 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -1067,6 +1067,7 @@
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 @@
{
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 @@
* 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 497c337..521f25a 100644
--- a/programs/x509/cert_write.c
+++ b/programs/x509/cert_write.c
@@ -524,6 +524,8 @@
//
if( !opt.selfsign && strlen( opt.issuer_crt ) )
{
+ mbedtls_x509_name *subject;
+
/*
* 1.0.a. Load the certificates
*/
@@ -538,8 +540,17 @@
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 @@
opt.issuer_name = issuer_name;
+ mbedtls_x509_name_free( subject );
+
mbedtls_printf( " ok\n" );
}
@@ -627,12 +640,24 @@
//
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 a8e7523..5938a5f 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 @@
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 4ef7417..30fbafd 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -53,11 +53,20 @@
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 @@
-o ./TESTS/mbedtls/$*
generate-target-tests: $(EMBEDDED_TESTS)
-
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 2f1a1b5..7ae1bc5 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -728,7 +728,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
# 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 @@
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 @@
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 f8d7875..6536cc9 100644
--- a/tests/suites/test_suite_x509parse.data
+++ b/tests/suites/test_suite_x509parse.data
@@ -935,6 +935,10 @@
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 c9fe63f..25b0d7f 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 @@
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 @@
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 @@
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 @@
/* 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 @@
/* 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 @@
}
/* 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 0000000..7940b7f
--- /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 0000000..2728e96
--- /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 535807e..9237165 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 @@
)
{
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 @@
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 73c92bd..2ec9178 100644
--- a/visualc/VS2010/mbedTLS.vcxproj
+++ b/visualc/VS2010/mbedTLS.vcxproj
@@ -223,6 +223,7 @@
<ClInclude Include="..\..\include\mbedtls\x509_crl.h" />
<ClInclude Include="..\..\include\mbedtls\x509_crt.h" />
<ClInclude Include="..\..\include\mbedtls\x509_csr.h" />
+ <ClInclude Include="..\..\include\mbedtls\x509_internal.h" />
<ClInclude Include="..\..\include\mbedtls\xtea.h" />
</ItemGroup>
<ItemGroup>