Merge pull request #583 from ARMmbed/remove_peer_crt_after_handshake-baremetal
[Baremetal] Allow removal of peer certificate to reduce RAM usage
diff --git a/ChangeLog b/ChangeLog
index b67c2ed..49c3acf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -42,6 +42,13 @@
API Changes
* Add a new X.509 API call `mbedtls_x509_parse_der_nocopy()`.
See the Features section for more information.
+ * Allow to opt in to the removal the API mbedtls_ssl_get_peer_cert()
+ for the benefit of saving RAM, by disabling the new compile-time
+ option MBEDTLS_SSL_KEEP_PEER_CERTIFICATE (enabled by default for
+ API stability). Disabling this option makes mbedtls_ssl_get_peer_cert()
+ always return NULL, and removes the peer_cert field from the
+ mbedtls_ssl_session structure which otherwise stores the peer's
+ certificate.
Bugfix
* Server's RSA certificate in certs.c was SHA-1 signed. In the default
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index 00fb9b3..67cb778 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -278,6 +278,14 @@
#error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites"
#endif
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) && \
+ !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) && \
+ ( !defined(MBEDTLS_SHA256_C) && \
+ !defined(MBEDTLS_SHA512_C) && \
+ !defined(MBEDTLS_SHA1_C) )
+#error "MBEDTLS_SSL_KEEP_PEER_CERTIFICATE defined, but not all prerequesites"
+#endif
+
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \
( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) )
#error "MBEDTLS_MEMORY_BUFFER_ALLOC_C defined, but not all prerequisites"
diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index 9fc512e..e5d5933 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -1405,6 +1405,28 @@
#define MBEDTLS_SSL_FALLBACK_SCSV
/**
+ * \def MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
+ *
+ * This option controls the availability of the API mbedtls_ssl_get_peer_cert()
+ * giving access to the peer's certificate after completion of the handshake.
+ *
+ * Unless you need mbedtls_ssl_peer_cert() in your application, it is
+ * recommended to disable this option for reduced RAM usage.
+ *
+ * \note If this option is disabled, mbedtls_ssl_get_peer_cert() is still
+ * defined, but always returns \c NULL.
+ *
+ * \note This option has no influence on the protection against the
+ * triple handshake attack. Even if it is disabled, Mbed TLS will
+ * still ensure that certificates do not change during renegotiation,
+ * for exaple by keeping a hash of the peer's certificate.
+ *
+ * Comment this macro to disable storing the peer's certificate
+ * after the handshake.
+ */
+#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
+
+/**
* \def MBEDTLS_SSL_HW_RECORD_ACCEL
*
* Enable hooking functions in SSL module for hardware acceleration of
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 6e1bb3a..9a36009 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -812,6 +812,25 @@
typedef void mbedtls_ssl_async_cancel_t( mbedtls_ssl_context *ssl );
#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) && \
+ !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+#define MBEDTLS_SSL_PEER_CERT_DIGEST_MAX_LEN 48
+#if defined(MBEDTLS_SHA256_C)
+#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE MBEDTLS_MD_SHA256
+#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN 32
+#elif defined(MBEDTLS_SHA512_C)
+#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE MBEDTLS_MD_SHA384
+#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN 48
+#elif defined(MBEDTLS_SHA1_C)
+#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE MBEDTLS_MD_SHA1
+#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN 20
+#else
+/* This is already checked in check_config.h, but be sure. */
+#error "Bad configuration - need SHA-1, SHA-256 or SHA-512 enabled to compute digest of peer CRT."
+#endif
+#endif /* MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED &&
+ !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
/*
* This structure is used for storing current session data.
*
@@ -835,7 +854,15 @@
unsigned char master[48]; /*!< the master secret */
#if defined(MBEDTLS_X509_CRT_PARSE_C)
- mbedtls_x509_crt *peer_cert; /*!< peer X.509 cert chain */
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ mbedtls_x509_crt *peer_cert; /*!< peer X.509 cert chain */
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ /*! The digest of the peer's end-CRT. This must be kept to detect CRT
+ * changes during renegotiation, mitigating the triple handshake attack. */
+ unsigned char *peer_cert_digest;
+ size_t peer_cert_digest_len;
+ mbedtls_md_type_t peer_cert_digest_type;
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
#endif /* MBEDTLS_X509_CRT_PARSE_C */
uint32_t verify_result; /*!< verification result */
@@ -3230,18 +3257,34 @@
#if defined(MBEDTLS_X509_CRT_PARSE_C)
/**
- * \brief Return the peer certificate from the current connection
+ * \brief Return the peer certificate from the current connection.
*
- * Note: Can be NULL in case no certificate was sent during
- * the handshake. Different calls for the same connection can
- * return the same or different pointers for the same
- * certificate and even a different certificate altogether.
- * The peer cert CAN change in a single connection if
- * renegotiation is performed.
+ * \param ssl The SSL context to use. This must be initialized and setup.
*
- * \param ssl SSL context
+ * \return The current peer certificate, if available.
+ * The returned certificate is owned by the SSL context and
+ * is valid only until the next call to the SSL API.
+ * \return \c NULL if no peer certificate is available. This might
+ * be because the chosen ciphersuite doesn't use CRTs
+ * (PSK-based ciphersuites, for example), or because
+ * #MBEDTLS_SSL_KEEP_PEER_CERTIFICATE has been disabled,
+ * allowing the stack to free the peer's CRT to save memory.
*
- * \return the current peer certificate
+ * \note For one-time inspection of the peer's certificate during
+ * the handshake, consider registering an X.509 CRT verification
+ * callback through mbedtls_ssl_conf_verify() instead of calling
+ * this function. Using mbedtls_ssl_conf_verify() also comes at
+ * the benefit of allowing you to influence the verification
+ * process, for example by masking expected and tolerated
+ * verification failures.
+ *
+ * \warning You must not use the pointer returned by this function
+ * after any further call to the SSL API, including
+ * mbedtls_ssl_read() and mbedtls_ssl_write(); this is
+ * because the pointer might change during renegotiation,
+ * which happens transparently to the user.
+ * If you want to use the certificate across API calls,
+ * you must make a copy.
*/
const mbedtls_x509_crt *mbedtls_ssl_get_peer_cert( const mbedtls_ssl_context *ssl );
#endif /* MBEDTLS_X509_CRT_PARSE_C */
diff --git a/include/mbedtls/ssl_cache.h b/include/mbedtls/ssl_cache.h
index 52ba094..84254d3 100644
--- a/include/mbedtls/ssl_cache.h
+++ b/include/mbedtls/ssl_cache.h
@@ -70,7 +70,8 @@
mbedtls_time_t timestamp; /*!< entry timestamp */
#endif
mbedtls_ssl_session session; /*!< entry session */
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
mbedtls_x509_buf peer_cert; /*!< entry peer_cert */
#endif
mbedtls_ssl_cache_entry *next; /*!< chain pointer */
diff --git a/include/mbedtls/ssl_ciphersuites.h b/include/mbedtls/ssl_ciphersuites.h
index 71053e5..7126783 100644
--- a/include/mbedtls/ssl_ciphersuites.h
+++ b/include/mbedtls/ssl_ciphersuites.h
@@ -486,6 +486,24 @@
}
}
+static inline int mbedtls_ssl_ciphersuite_uses_srv_cert( const mbedtls_ssl_ciphersuite_t *info )
+{
+ switch( info->key_exchange )
+ {
+ case MBEDTLS_KEY_EXCHANGE_RSA:
+ case MBEDTLS_KEY_EXCHANGE_RSA_PSK:
+ case MBEDTLS_KEY_EXCHANGE_DHE_RSA:
+ case MBEDTLS_KEY_EXCHANGE_ECDH_RSA:
+ case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA:
+ case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA:
+ case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA:
+ return( 1 );
+
+ default:
+ return( 0 );
+ }
+}
+
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__DHE_ENABLED)
static inline int mbedtls_ssl_ciphersuite_uses_dhe( const mbedtls_ssl_ciphersuite_t *info )
{
diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index 8089441..3eb37b8 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -396,8 +396,13 @@
ssl_ecrs_cke_ecdh_calc_secret, /*!< ClientKeyExchange: ECDH step 2 */
ssl_ecrs_crt_vrfy_sign, /*!< CertificateVerify: pk_sign() */
} ecrs_state; /*!< current (or last) operation */
+ mbedtls_x509_crt *ecrs_peer_cert; /*!< The peer's CRT chain. */
size_t ecrs_n; /*!< place for saving a length */
#endif
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ mbedtls_pk_context peer_pubkey; /*!< The public key from the peer. */
+#endif /* MBEDTLS_X509_CRT_PARSE_C && !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
unsigned int out_msg_seq; /*!< Outgoing handshake sequence number */
unsigned int in_msg_seq; /*!< Incoming handshake sequence number */
@@ -989,6 +994,9 @@
void mbedtls_ssl_dtls_replay_update( mbedtls_ssl_context *ssl );
#endif
+int mbedtls_ssl_session_copy( mbedtls_ssl_session *dst,
+ const mbedtls_ssl_session *src );
+
/* constant-time buffer comparison */
static inline int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n )
{
diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h
index dc3260b..09ba69f 100644
--- a/include/mbedtls/x509_crt.h
+++ b/include/mbedtls/x509_crt.h
@@ -70,6 +70,7 @@
mbedtls_x509_time valid_from; /**< Start time of certificate validity. */
mbedtls_x509_time valid_to; /**< End time of certificate validity. */
+ mbedtls_x509_buf pk_raw;
mbedtls_pk_context pk; /**< Container for the public key context. */
mbedtls_x509_buf issuer_id; /**< Optional X.509 v2/v3 issuer unique identifier. */
diff --git a/library/ssl_cache.c b/library/ssl_cache.c
index 47867f1..62a0a29 100644
--- a/library/ssl_cache.c
+++ b/library/ssl_cache.c
@@ -40,6 +40,7 @@
#endif
#include "mbedtls/ssl_cache.h"
+#include "mbedtls/ssl_internal.h"
#include <string.h>
@@ -92,16 +93,24 @@
entry->session.id_len ) != 0 )
continue;
- memcpy( session->master, entry->session.master, 48 );
+ ret = mbedtls_ssl_session_copy( session, &entry->session );
+ if( ret != 0 )
+ {
+ ret = 1;
+ goto exit;
+ }
- session->verify_result = entry->session.verify_result;
-
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
/*
* Restore peer certificate (without rest of the original chain)
*/
if( entry->peer_cert.p != NULL )
{
+ /* `session->peer_cert` is NULL after the call to
+ * mbedtls_ssl_session_copy(), because cache entries
+ * have the `peer_cert` field set to NULL. */
+
if( ( session->peer_cert = mbedtls_calloc( 1,
sizeof(mbedtls_x509_crt) ) ) == NULL )
{
@@ -119,7 +128,7 @@
goto exit;
}
}
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
+#endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
ret = 0;
goto exit;
@@ -239,9 +248,8 @@
#endif
}
- memcpy( &cur->session, session, sizeof( mbedtls_ssl_session ) );
-
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
/*
* If we're reusing an entry, free its certificate first
*/
@@ -250,26 +258,43 @@
mbedtls_free( cur->peer_cert.p );
memset( &cur->peer_cert, 0, sizeof(mbedtls_x509_buf) );
}
+#endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
- /*
- * Store peer certificate
- */
- if( session->peer_cert != NULL )
+ /* Copy the entire session; this temporarily makes a copy of the
+ * X.509 CRT structure even though we only want to store the raw CRT.
+ * This inefficiency will go away as soon as we implement on-demand
+ * parsing of CRTs, in which case there's no need for the `peer_cert`
+ * field anymore in the first place, and we're done after this call. */
+ ret = mbedtls_ssl_session_copy( &cur->session, session );
+ if( ret != 0 )
{
- cur->peer_cert.p = mbedtls_calloc( 1, session->peer_cert->raw.len );
+ ret = 1;
+ goto exit;
+ }
+
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ /* If present, free the X.509 structure and only store the raw CRT data. */
+ if( cur->session.peer_cert != NULL )
+ {
+ cur->peer_cert.p =
+ mbedtls_calloc( 1, cur->session.peer_cert->raw.len );
if( cur->peer_cert.p == NULL )
{
ret = 1;
goto exit;
}
- memcpy( cur->peer_cert.p, session->peer_cert->raw.p,
- session->peer_cert->raw.len );
+ memcpy( cur->peer_cert.p,
+ cur->session.peer_cert->raw.p,
+ cur->session.peer_cert->raw.len );
cur->peer_cert.len = session->peer_cert->raw.len;
+ mbedtls_x509_crt_free( cur->session.peer_cert );
+ mbedtls_free( cur->session.peer_cert );
cur->session.peer_cert = NULL;
}
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
+#endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
ret = 0;
@@ -311,9 +336,10 @@
mbedtls_ssl_session_free( &prv->session );
-#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
mbedtls_free( prv->peer_cert.p );
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
+#endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
mbedtls_free( prv );
}
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index cd9793e..e5185d4 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -2288,6 +2288,7 @@
int ret;
size_t len_bytes = ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 ? 0 : 2;
unsigned char *p = ssl->handshake->premaster + pms_offset;
+ mbedtls_pk_context * peer_pk;
if( offset + len_bytes > MBEDTLS_SSL_OUT_CONTENT_LEN )
{
@@ -2313,23 +2314,28 @@
ssl->handshake->pmslen = 48;
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ peer_pk = &ssl->handshake->peer_pubkey;
+#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
if( ssl->session_negotiate->peer_cert == NULL )
{
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "certificate required" ) );
- return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
+ /* Should never happen */
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
+ peer_pk = &ssl->session_negotiate->peer_cert->pk;
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
/*
* Now write it out, encrypted
*/
- if( ! mbedtls_pk_can_do( &ssl->session_negotiate->peer_cert->pk,
- MBEDTLS_PK_RSA ) )
+ 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 );
}
- if( ( ret = mbedtls_pk_encrypt( &ssl->session_negotiate->peer_cert->pk,
+ if( ( ret = mbedtls_pk_encrypt( peer_pk,
p, ssl->handshake->pmslen,
ssl->out_msg + offset + len_bytes, olen,
MBEDTLS_SSL_OUT_CONTENT_LEN - offset - len_bytes,
@@ -2349,6 +2355,10 @@
}
#endif
+#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 );
}
#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED ||
@@ -2424,21 +2434,27 @@
{
int ret;
const mbedtls_ecp_keypair *peer_key;
+ mbedtls_pk_context * peer_pk;
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ peer_pk = &ssl->handshake->peer_pubkey;
+#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
if( ssl->session_negotiate->peer_cert == NULL )
{
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "certificate required" ) );
- return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
+ /* Should never happen */
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
+ peer_pk = &ssl->session_negotiate->peer_cert->pk;
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
- if( ! mbedtls_pk_can_do( &ssl->session_negotiate->peer_cert->pk,
- MBEDTLS_PK_ECKEY ) )
+ 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 );
}
- peer_key = mbedtls_pk_ec( ssl->session_negotiate->peer_cert->pk );
+ peer_key = mbedtls_pk_ec( *peer_pk );
if( ( ret = mbedtls_ecdh_get_params( &ssl->handshake->ecdh_ctx, peer_key,
MBEDTLS_ECDH_THEIRS ) ) != 0 )
@@ -2453,6 +2469,13 @@
return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE );
}
+#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 */
+
return( ret );
}
#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) ||
@@ -2645,6 +2668,8 @@
size_t params_len = p - params;
void *rs_ctx = NULL;
+ mbedtls_pk_context * peer_pk;
+
/*
* Handle the digitally-signed structure
*/
@@ -2747,18 +2772,22 @@
MBEDTLS_SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen );
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ peer_pk = &ssl->handshake->peer_pubkey;
+#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
if( ssl->session_negotiate->peer_cert == NULL )
{
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "certificate required" ) );
- mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
- MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE );
- return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
+ /* Should never happen */
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
+ peer_pk = &ssl->session_negotiate->peer_cert->pk;
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
/*
* Verify signature
*/
- if( ! mbedtls_pk_can_do( &ssl->session_negotiate->peer_cert->pk, pk_alg ) )
+ if( !mbedtls_pk_can_do( peer_pk, pk_alg ) )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) );
mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
@@ -2771,8 +2800,7 @@
rs_ctx = &ssl->handshake->ecrs_ctx.pk;
#endif
- if( ( ret = mbedtls_pk_verify_restartable(
- &ssl->session_negotiate->peer_cert->pk,
+ if( ( ret = mbedtls_pk_verify_restartable( peer_pk,
md_alg, hash, hashlen, p, sig_len, rs_ctx ) ) != 0 )
{
#if defined(MBEDTLS_SSL__ECP_RESTARTABLE)
@@ -2787,6 +2815,13 @@
#endif
return( ret );
}
+
+#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 */
}
#endif /* MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED */
@@ -3306,12 +3341,7 @@
return( 0 );
}
-#if !defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)&& \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
+#if !defined(MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED)
static int ssl_write_certificate_verify( mbedtls_ssl_context *ssl )
{
const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
@@ -3326,11 +3356,7 @@
return( ret );
}
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ if( !mbedtls_ssl_ciphersuite_cert_req_allowed( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) );
ssl->state++;
@@ -3340,7 +3366,7 @@
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
-#else
+#else /* !MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED */
static int ssl_write_certificate_verify( mbedtls_ssl_context *ssl )
{
int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
@@ -3369,11 +3395,7 @@
return( ret );
}
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ if( !mbedtls_ssl_ciphersuite_cert_req_allowed( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) );
ssl->state++;
@@ -3514,12 +3536,7 @@
return( ret );
}
-#endif /* !MBEDTLS_KEY_EXCHANGE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+#endif /* MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED */
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
static int ssl_parse_new_session_ticket( mbedtls_ssl_context *ssl )
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index d40a21f..06fe3ee 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -2801,12 +2801,7 @@
return( ret );
}
-#if !defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)&& \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
+#if !defined(MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED)
static int ssl_write_certificate_request( mbedtls_ssl_context *ssl )
{
const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
@@ -2814,11 +2809,7 @@
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write certificate request" ) );
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ if( !mbedtls_ssl_ciphersuite_cert_req_allowed( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) );
ssl->state++;
@@ -2828,7 +2819,7 @@
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
-#else
+#else /* !MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED */
static int ssl_write_certificate_request( mbedtls_ssl_context *ssl )
{
int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
@@ -2852,11 +2843,7 @@
#endif
authmode = ssl->conf->authmode;
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE ||
+ if( !mbedtls_ssl_ciphersuite_cert_req_allowed( ciphersuite_info ) ||
authmode == MBEDTLS_SSL_VERIFY_NONE )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) );
@@ -2995,12 +2982,7 @@
return( ret );
}
-#endif /* !MBEDTLS_KEY_EXCHANGE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+#endif /* MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED */
#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \
defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)
@@ -4147,12 +4129,7 @@
return( 0 );
}
-#if !defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)&& \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
+#if !defined(MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED)
static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
{
const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
@@ -4160,11 +4137,7 @@
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) );
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ if( !mbedtls_ssl_ciphersuite_cert_req_allowed( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
ssl->state++;
@@ -4174,7 +4147,7 @@
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
-#else
+#else /* !MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED */
static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
{
int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
@@ -4188,21 +4161,33 @@
mbedtls_md_type_t md_alg;
const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
ssl->handshake->ciphersuite_info;
+ mbedtls_pk_context * peer_pk;
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) );
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE ||
- ssl->session_negotiate->peer_cert == NULL )
+ if( !mbedtls_ssl_ciphersuite_cert_req_allowed( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
ssl->state++;
return( 0 );
}
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ if( ssl->session_negotiate->peer_cert == NULL )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
+ ssl->state++;
+ return( 0 );
+ }
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ if( ssl->session_negotiate->peer_cert_digest == NULL )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
+ ssl->state++;
+ return( 0 );
+ }
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
/* Read the message without adding it to the checksum */
ret = mbedtls_ssl_read_record( ssl, 0 /* no checksum update */ );
if( 0 != ret )
@@ -4223,6 +4208,17 @@
i = mbedtls_ssl_hs_hdr_len( ssl );
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ peer_pk = &ssl->handshake->peer_pubkey;
+#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ if( ssl->session_negotiate->peer_cert == NULL )
+ {
+ /* Should never happen */
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+ }
+ peer_pk = &ssl->session_negotiate->peer_cert->pk;
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
/*
* struct {
* SignatureAndHashAlgorithm algorithm; -- TLS 1.2 only
@@ -4237,8 +4233,7 @@
hashlen = 36;
/* For ECDSA, use SHA-1, not MD-5 + SHA-1 */
- if( mbedtls_pk_can_do( &ssl->session_negotiate->peer_cert->pk,
- MBEDTLS_PK_ECDSA ) )
+ if( mbedtls_pk_can_do( peer_pk, MBEDTLS_PK_ECDSA ) )
{
hash_start += 16;
hashlen -= 16;
@@ -4293,7 +4288,7 @@
/*
* Check the certificate's key type matches the signature alg
*/
- if( ! mbedtls_pk_can_do( &ssl->session_negotiate->peer_cert->pk, pk_alg ) )
+ 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 );
@@ -4329,7 +4324,7 @@
ssl->handshake->calc_verify( ssl, hash, &dummy_hlen );
}
- if( ( ret = mbedtls_pk_verify( &ssl->session_negotiate->peer_cert->pk,
+ if( ( ret = mbedtls_pk_verify( peer_pk,
md_alg, hash_start, hashlen,
ssl->in_msg + i, sig_len ) ) != 0 )
{
@@ -4343,12 +4338,7 @@
return( ret );
}
-#endif /* !MBEDTLS_KEY_EXCHANGE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED &&
- !MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+#endif /* MBEDTLS_KEY_EXCHANGE__CERT_REQ_ALLOWED__ENABLED */
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
static int ssl_write_new_session_ticket( mbedtls_ssl_context *ssl )
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 096b6c8..84a9b3b 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -368,13 +368,15 @@
}
#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
-#if defined(MBEDTLS_SSL_CLI_C)
-static int ssl_session_copy( mbedtls_ssl_session *dst, const mbedtls_ssl_session *src )
+int mbedtls_ssl_session_copy( mbedtls_ssl_session *dst,
+ const mbedtls_ssl_session *src )
{
mbedtls_ssl_session_free( dst );
memcpy( dst, src, sizeof( mbedtls_ssl_session ) );
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
if( src->peer_cert != NULL )
{
int ret;
@@ -393,6 +395,21 @@
return( ret );
}
}
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ if( src->peer_cert_digest != NULL )
+ {
+ dst->peer_cert_digest =
+ mbedtls_calloc( 1, src->peer_cert_digest_len );
+ if( dst->peer_cert_digest == NULL )
+ return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+
+ memcpy( dst->peer_cert_digest, src->peer_cert_digest,
+ src->peer_cert_digest_len );
+ dst->peer_cert_digest_type = src->peer_cert_digest_type;
+ dst->peer_cert_digest_len = src->peer_cert_digest_len;
+ }
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
#endif /* MBEDTLS_X509_CRT_PARSE_C */
#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
@@ -408,7 +425,6 @@
return( 0 );
}
-#endif /* MBEDTLS_SSL_CLI_C */
#if defined(MBEDTLS_SSL_HW_RECORD_ACCEL)
int (*mbedtls_ssl_hw_record_init)( mbedtls_ssl_context *ssl,
@@ -5973,16 +5989,33 @@
return( 0 );
}
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+static void ssl_clear_peer_cert( mbedtls_ssl_session *session )
+{
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ if( session->peer_cert != NULL )
+ {
+ mbedtls_x509_crt_free( session->peer_cert );
+ mbedtls_free( session->peer_cert );
+ session->peer_cert = NULL;
+ }
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ if( session->peer_cert_digest != NULL )
+ {
+ /* Zeroization is not necessary. */
+ mbedtls_free( session->peer_cert_digest );
+ session->peer_cert_digest = NULL;
+ session->peer_cert_digest_type = MBEDTLS_MD_NONE;
+ session->peer_cert_digest_len = 0;
+ }
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+}
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
/*
* Handshake functions
*/
-#if !defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \
- !defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)
+#if !defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED)
/* No certificate support -> dummy functions */
int mbedtls_ssl_write_certificate( mbedtls_ssl_context *ssl )
{
@@ -5990,10 +6023,7 @@
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write certificate" ) );
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ if( !mbedtls_ssl_ciphersuite_uses_srv_cert( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) );
ssl->state++;
@@ -6010,10 +6040,7 @@
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate" ) );
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ if( !mbedtls_ssl_ciphersuite_uses_srv_cert( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
ssl->state++;
@@ -6024,7 +6051,7 @@
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
-#else
+#else /* MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED */
/* Some certificate support -> implement write and parse */
int mbedtls_ssl_write_certificate( mbedtls_ssl_context *ssl )
@@ -6036,10 +6063,7 @@
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> write certificate" ) );
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ if( !mbedtls_ssl_ciphersuite_uses_srv_cert( ciphersuite_info ) )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) );
ssl->state++;
@@ -6144,6 +6168,8 @@
}
#if defined(MBEDTLS_SSL_RENEGOTIATION) && defined(MBEDTLS_SSL_CLI_C)
+
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
static int ssl_check_peer_crt_unchanged( mbedtls_ssl_context *ssl,
unsigned char *crt_buf,
size_t crt_buf_len )
@@ -6158,65 +6184,51 @@
return( memcmp( peer_crt->raw.p, crt_buf, crt_buf_len ) );
}
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+static int ssl_check_peer_crt_unchanged( mbedtls_ssl_context *ssl,
+ unsigned char *crt_buf,
+ size_t crt_buf_len )
+{
+ int ret;
+ unsigned char const * const peer_cert_digest =
+ ssl->session->peer_cert_digest;
+ mbedtls_md_type_t const peer_cert_digest_type =
+ ssl->session->peer_cert_digest_type;
+ mbedtls_md_info_t const * const digest_info =
+ mbedtls_md_info_from_type( peer_cert_digest_type );
+ unsigned char tmp_digest[MBEDTLS_SSL_PEER_CERT_DIGEST_MAX_LEN];
+ size_t digest_len;
+
+ if( peer_cert_digest == NULL || digest_info == NULL )
+ return( -1 );
+
+ digest_len = mbedtls_md_get_size( digest_info );
+ if( digest_len > MBEDTLS_SSL_PEER_CERT_DIGEST_MAX_LEN )
+ return( -1 );
+
+ ret = mbedtls_md( digest_info, crt_buf, crt_buf_len, tmp_digest );
+ if( ret != 0 )
+ return( -1 );
+
+ return( memcmp( tmp_digest, peer_cert_digest, digest_len ) );
+}
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
#endif /* MBEDTLS_SSL_RENEGOTIATION && MBEDTLS_SSL_CLI_C */
/*
* Once the certificate message is read, parse it into a cert chain and
* perform basic checks, but leave actual verification to the caller
*/
-static int ssl_parse_certificate_chain( mbedtls_ssl_context *ssl )
+static int ssl_parse_certificate_chain( mbedtls_ssl_context *ssl,
+ mbedtls_x509_crt *chain )
{
int ret;
+#if defined(MBEDTLS_SSL_RENEGOTIATION) && defined(MBEDTLS_SSL_CLI_C)
+ int crt_cnt=0;
+#endif
size_t i, n;
uint8_t alert;
-#if defined(MBEDTLS_SSL_SRV_C)
-#if defined(MBEDTLS_SSL_PROTO_SSL3)
- /*
- * Check if the client sent an empty certificate
- */
- if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER &&
- ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 )
- {
- if( ssl->in_msglen == 2 &&
- ssl->in_msgtype == MBEDTLS_SSL_MSG_ALERT &&
- ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING &&
- ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_NO_CERT )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "SSLv3 client has no certificate" ) );
-
- /* The client was asked for a certificate but didn't send
- one. The client should know what's going on, so we
- don't send an alert. */
- ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING;
- return( MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE );
- }
- }
-#endif /* MBEDTLS_SSL_PROTO_SSL3 */
-
-#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
- defined(MBEDTLS_SSL_PROTO_TLS1_2)
- if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER &&
- ssl->minor_ver != MBEDTLS_SSL_MINOR_VERSION_0 )
- {
- if( ssl->in_hslen == 3 + mbedtls_ssl_hs_hdr_len( ssl ) &&
- ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE &&
- ssl->in_msg[0] == MBEDTLS_SSL_HS_CERTIFICATE &&
- memcmp( ssl->in_msg + mbedtls_ssl_hs_hdr_len( ssl ), "\0\0\0", 3 ) == 0 )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "TLSv1 client has no certificate" ) );
-
- /* The client was asked for a certificate but didn't send
- one. The client should know what's going on, so we
- don't send an alert. */
- ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING;
- return( MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE );
- }
- }
-#endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 || \
- MBEDTLS_SSL_PROTO_TLS1_2 */
-#endif /* MBEDTLS_SSL_SRV_C */
-
if( ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate message" ) );
@@ -6253,14 +6265,6 @@
/* Make &ssl->in_msg[i] point to the beginning of the CRT chain. */
i += 3;
- /* In case we tried to reuse a session but it failed */
- if( ssl->session_negotiate->peer_cert != NULL )
- {
- mbedtls_x509_crt_free( ssl->session_negotiate->peer_cert );
- mbedtls_free( ssl->session_negotiate->peer_cert );
- ssl->session_negotiate->peer_cert = NULL;
- }
-
/* Iterate through and parse the CRTs in the provided chain. */
while( i < ssl->in_hslen )
{
@@ -6298,66 +6302,40 @@
}
/* Check if we're handling the first CRT in the chain. */
- if( ssl->session_negotiate->peer_cert == NULL )
+#if defined(MBEDTLS_SSL_RENEGOTIATION) && defined(MBEDTLS_SSL_CLI_C)
+ if( crt_cnt++ == 0 &&
+ ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT &&
+ ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS )
{
/* During client-side renegotiation, check that the server's
* end-CRTs hasn't changed compared to the initial handshake,
* mitigating the triple handshake attack. On success, reuse
* the original end-CRT instead of parsing it again. */
-#if defined(MBEDTLS_SSL_RENEGOTIATION) && defined(MBEDTLS_SSL_CLI_C)
- if( ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT &&
- ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS )
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "Check that peer CRT hasn't changed during renegotiation" ) );
+ if( ssl_check_peer_crt_unchanged( ssl,
+ &ssl->in_msg[i],
+ n ) != 0 )
{
- MBEDTLS_SSL_DEBUG_MSG( 3, ( "Check that peer CRT hasn't changed during renegotiation" ) );
- if( ssl_check_peer_crt_unchanged( ssl,
- &ssl->in_msg[i],
- n ) != 0 )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "new server cert during renegotiation" ) );
- mbedtls_ssl_send_alert_message( ssl,
- MBEDTLS_SSL_ALERT_LEVEL_FATAL,
- MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED );
- return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE );
- }
-
- /* Move CRT chain structure to new session instance. */
- ssl->session_negotiate->peer_cert = ssl->session->peer_cert;
- ssl->session->peer_cert = NULL;
-
- /* Delete all remaining CRTs from the original CRT chain. */
- mbedtls_x509_crt_free(
- ssl->session_negotiate->peer_cert->next );
- mbedtls_free( ssl->session_negotiate->peer_cert->next );
- ssl->session_negotiate->peer_cert->next = NULL;
-
- i += n;
- continue;
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "new server cert during renegotiation" ) );
+ mbedtls_ssl_send_alert_message( ssl,
+ MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED );
+ return( MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE );
}
+
+ /* Now we can safely free the original chain. */
+ ssl_clear_peer_cert( ssl->session );
+ }
#endif /* MBEDTLS_SSL_RENEGOTIATION && MBEDTLS_SSL_CLI_C */
- /* Outside of client-side renegotiation, create a fresh X.509 CRT
- * instance to parse the end-CRT into. */
-
- ssl->session_negotiate->peer_cert =
- mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) );
- if( ssl->session_negotiate->peer_cert == NULL )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "alloc(%d bytes) failed",
- sizeof( mbedtls_x509_crt ) ) );
- mbedtls_ssl_send_alert_message( ssl,
- MBEDTLS_SSL_ALERT_LEVEL_FATAL,
- MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR );
- return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
- }
-
- mbedtls_x509_crt_init( ssl->session_negotiate->peer_cert );
-
- /* Intentional fall through */
- }
-
/* Parse the next certificate in the chain. */
- ret = mbedtls_x509_crt_parse_der( ssl->session_negotiate->peer_cert,
- ssl->in_msg + i, n );
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ ret = mbedtls_x509_crt_parse_der( chain, ssl->in_msg + i, n );
+#else
+ /* If we don't need to store the CRT chain permanently, parse
+ * it in-place from the input buffer instead of making a copy. */
+ ret = mbedtls_x509_crt_parse_der_nocopy( chain, ssl->in_msg + i, n );
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
switch( ret )
{
case 0: /*ok*/
@@ -6385,15 +6363,292 @@
i += n;
}
- MBEDTLS_SSL_DEBUG_CRT( 3, "peer certificate", ssl->session_negotiate->peer_cert );
+ MBEDTLS_SSL_DEBUG_CRT( 3, "peer certificate", chain );
return( 0 );
}
-int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl )
+#if defined(MBEDTLS_SSL_SRV_C)
+static int ssl_srv_check_client_no_crt_notification( mbedtls_ssl_context *ssl )
+{
+ if( ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT )
+ return( -1 );
+
+#if defined(MBEDTLS_SSL_PROTO_SSL3)
+ /*
+ * Check if the client sent an empty certificate
+ */
+ if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 )
+ {
+ if( ssl->in_msglen == 2 &&
+ ssl->in_msgtype == MBEDTLS_SSL_MSG_ALERT &&
+ ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING &&
+ ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_NO_CERT )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "SSLv3 client has no certificate" ) );
+ return( 0 );
+ }
+
+ return( -1 );
+ }
+#endif /* MBEDTLS_SSL_PROTO_SSL3 */
+
+#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
+ defined(MBEDTLS_SSL_PROTO_TLS1_2)
+ if( ssl->in_hslen == 3 + mbedtls_ssl_hs_hdr_len( ssl ) &&
+ ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE &&
+ ssl->in_msg[0] == MBEDTLS_SSL_HS_CERTIFICATE &&
+ memcmp( ssl->in_msg + mbedtls_ssl_hs_hdr_len( ssl ), "\0\0\0", 3 ) == 0 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "TLSv1 client has no certificate" ) );
+ return( 0 );
+ }
+
+#endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 || \
+ MBEDTLS_SSL_PROTO_TLS1_2 */
+
+ return( -1 );
+}
+#endif /* MBEDTLS_SSL_SRV_C */
+
+/* Check if a certificate message is expected.
+ * Return either
+ * - SSL_CERTIFICATE_EXPECTED, or
+ * - SSL_CERTIFICATE_SKIP
+ * indicating whether a Certificate message is expected or not.
+ */
+#define SSL_CERTIFICATE_EXPECTED 0
+#define SSL_CERTIFICATE_SKIP 1
+static int ssl_parse_certificate_coordinate( mbedtls_ssl_context *ssl,
+ int authmode )
+{
+ const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
+ ssl->handshake->ciphersuite_info;
+
+ if( !mbedtls_ssl_ciphersuite_uses_srv_cert( ciphersuite_info ) )
+ return( SSL_CERTIFICATE_SKIP );
+
+#if defined(MBEDTLS_SSL_SRV_C)
+ if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
+ {
+ if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK )
+ return( SSL_CERTIFICATE_SKIP );
+
+ if( authmode == MBEDTLS_SSL_VERIFY_NONE )
+ {
+ ssl->session_negotiate->verify_result =
+ MBEDTLS_X509_BADCERT_SKIP_VERIFY;
+ return( SSL_CERTIFICATE_SKIP );
+ }
+ }
+#else
+ ((void) authmode);
+#endif /* MBEDTLS_SSL_SRV_C */
+
+ return( SSL_CERTIFICATE_EXPECTED );
+}
+
+static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl,
+ int authmode,
+ mbedtls_x509_crt *chain,
+ void *rs_ctx )
+{
+ int ret = 0;
+ const mbedtls_ssl_ciphersuite_t *ciphersuite_info =
+ ssl->handshake->ciphersuite_info;
+ mbedtls_x509_crt *ca_chain;
+ mbedtls_x509_crl *ca_crl;
+
+ if( authmode == MBEDTLS_SSL_VERIFY_NONE )
+ return( 0 );
+
+#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+ if( ssl->handshake->sni_ca_chain != NULL )
+ {
+ ca_chain = ssl->handshake->sni_ca_chain;
+ ca_crl = ssl->handshake->sni_ca_crl;
+ }
+ else
+#endif
+ {
+ ca_chain = ssl->conf->ca_chain;
+ ca_crl = ssl->conf->ca_crl;
+ }
+
+ /*
+ * Main check: verify certificate
+ */
+ ret = mbedtls_x509_crt_verify_restartable(
+ chain,
+ ca_chain, ca_crl,
+ ssl->conf->cert_profile,
+ ssl->hostname,
+ &ssl->session_negotiate->verify_result,
+ ssl->conf->f_vrfy, ssl->conf->p_vrfy, rs_ctx );
+
+ if( ret != 0 )
+ {
+ MBEDTLS_SSL_DEBUG_RET( 1, "x509_verify_cert", ret );
+ }
+
+#if defined(MBEDTLS_SSL__ECP_RESTARTABLE)
+ if( ret == MBEDTLS_ERR_ECP_IN_PROGRESS )
+ return( MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS );
+#endif
+
+ /*
+ * Secondary checks: always done, but change 'ret' only if it was 0
+ */
+
+#if defined(MBEDTLS_ECP_C)
+ {
+ const mbedtls_pk_context *pk = &chain->pk;
+
+ /* 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 )
+ {
+ 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;
+ }
+ }
+#endif /* MBEDTLS_ECP_C */
+
+ if( mbedtls_ssl_check_cert_usage( chain,
+ ciphersuite_info,
+ ! ssl->conf->endpoint,
+ &ssl->session_negotiate->verify_result ) != 0 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate (usage extensions)" ) );
+ if( ret == 0 )
+ ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE;
+ }
+
+ /* mbedtls_x509_crt_verify_with_profile is supposed to report a
+ * verification failure through MBEDTLS_ERR_X509_CERT_VERIFY_FAILED,
+ * with details encoded in the verification flags. All other kinds
+ * of error codes, including those from the user provided f_vrfy
+ * 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 ) )
+ {
+ 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;
+ }
+
+ if( ret != 0 )
+ {
+ uint8_t alert;
+
+ /* The certificate may have been rejected for several reasons.
+ Pick one and send the corresponding alert. Which alert to send
+ may be a subject of debate in some cases. */
+ if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_OTHER )
+ alert = MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_CN_MISMATCH )
+ alert = MBEDTLS_SSL_ALERT_MSG_BAD_CERT;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_KEY_USAGE )
+ alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_EXT_KEY_USAGE )
+ alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_NS_CERT_TYPE )
+ alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_BAD_PK )
+ alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_BAD_KEY )
+ alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_EXPIRED )
+ alert = MBEDTLS_SSL_ALERT_MSG_CERT_EXPIRED;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_REVOKED )
+ alert = MBEDTLS_SSL_ALERT_MSG_CERT_REVOKED;
+ else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_NOT_TRUSTED )
+ alert = MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA;
+ else
+ alert = MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN;
+ mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ alert );
+ }
+
+#if defined(MBEDTLS_DEBUG_C)
+ if( ssl->session_negotiate->verify_result != 0 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "! Certificate verification flags %x",
+ ssl->session_negotiate->verify_result ) );
+ }
+ else
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "Certificate verification flags clear" ) );
+ }
+#endif /* MBEDTLS_DEBUG_C */
+
+ return( ret );
+}
+
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+static int ssl_remember_peer_crt_digest( mbedtls_ssl_context *ssl,
+ unsigned char *start, size_t len )
{
int ret;
- const mbedtls_ssl_ciphersuite_t * const ciphersuite_info =
- ssl->handshake->ciphersuite_info;
+ /* Remember digest of the peer's end-CRT. */
+ ssl->session_negotiate->peer_cert_digest =
+ mbedtls_calloc( 1, MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN );
+ if( ssl->session_negotiate->peer_cert_digest == NULL )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "alloc(%d bytes) failed",
+ sizeof( MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN ) ) );
+ mbedtls_ssl_send_alert_message( ssl,
+ MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR );
+
+ return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+ }
+
+ ret = mbedtls_md( mbedtls_md_info_from_type(
+ MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE ),
+ start, len,
+ ssl->session_negotiate->peer_cert_digest );
+
+ ssl->session_negotiate->peer_cert_digest_type =
+ MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE;
+ ssl->session_negotiate->peer_cert_digest_len =
+ MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN;
+
+ return( ret );
+}
+
+static int ssl_remember_peer_pubkey( mbedtls_ssl_context *ssl,
+ unsigned char *start, size_t len )
+{
+ unsigned char *end = start + len;
+ int ret;
+
+ /* Make a copy of the peer's raw public key. */
+ mbedtls_pk_init( &ssl->handshake->peer_pubkey );
+ ret = mbedtls_pk_parse_subpubkey( &start, end,
+ &ssl->handshake->peer_pubkey );
+ if( ret != 0 )
+ {
+ /* We should have parsed the public key before. */
+ return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
+ }
+
+ return( 0 );
+}
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
+int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl )
+{
+ int ret = 0;
+ int crt_expected;
#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
const int authmode = ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET
? ssl->handshake->sni_authmode
@@ -6402,43 +6657,23 @@
const int authmode = ssl->conf->authmode;
#endif
void *rs_ctx = NULL;
+ mbedtls_x509_crt *chain = NULL;
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> parse certificate" ) );
- if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE )
+ crt_expected = ssl_parse_certificate_coordinate( ssl, authmode );
+ if( crt_expected == SSL_CERTIFICATE_SKIP )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
- ssl->state++;
- return( 0 );
+ goto exit;
}
-#if defined(MBEDTLS_SSL_SRV_C)
- if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER &&
- ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK )
- {
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
- ssl->state++;
- return( 0 );
- }
-
- if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER &&
- authmode == MBEDTLS_SSL_VERIFY_NONE )
- {
- ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY;
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
-
- ssl->state++;
- return( 0 );
- }
-#endif
-
#if defined(MBEDTLS_SSL__ECP_RESTARTABLE)
if( ssl->handshake->ecrs_enabled &&
ssl->handshake->ecrs_state == ssl_ecrs_crt_verify )
{
+ chain = ssl->handshake->ecrs_peer_cert;
+ ssl->handshake->ecrs_peer_cert = NULL;
goto crt_verify;
}
#endif
@@ -6448,22 +6683,44 @@
/* mbedtls_ssl_read_record may have sent an alert already. We
let it decide whether to alert. */
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_read_record", ret );
- return( ret );
+ goto exit;
}
- if( ( ret = ssl_parse_certificate_chain( ssl ) ) != 0 )
- {
#if defined(MBEDTLS_SSL_SRV_C)
- if( ret == MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE &&
- authmode == MBEDTLS_SSL_VERIFY_OPTIONAL )
- {
- ret = 0;
- }
-#endif
+ if( ssl_srv_check_client_no_crt_notification( ssl ) == 0 )
+ {
+ ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING;
- ssl->state++;
- return( ret );
+ if( authmode == MBEDTLS_SSL_VERIFY_OPTIONAL )
+ ret = 0;
+ else
+ ret = MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE;
+
+ goto exit;
}
+#endif /* MBEDTLS_SSL_SRV_C */
+
+ /* Clear existing peer CRT structure in case we tried to
+ * reuse a session but it failed, and allocate a new one. */
+ ssl_clear_peer_cert( ssl->session_negotiate );
+
+ chain = mbedtls_calloc( 1, sizeof( mbedtls_x509_crt ) );
+ if( chain == NULL )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "alloc(%d bytes) failed",
+ sizeof( mbedtls_x509_crt ) ) );
+ mbedtls_ssl_send_alert_message( ssl,
+ MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR );
+
+ ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
+ goto exit;
+ }
+ mbedtls_x509_crt_init( chain );
+
+ ret = ssl_parse_certificate_chain( ssl, chain );
+ if( ret != 0 )
+ goto exit;
#if defined(MBEDTLS_SSL__ECP_RESTARTABLE)
if( ssl->handshake->ecrs_enabled)
@@ -6474,154 +6731,71 @@
rs_ctx = &ssl->handshake->ecrs_ctx;
#endif
- if( authmode != MBEDTLS_SSL_VERIFY_NONE )
+ ret = ssl_parse_certificate_verify( ssl, authmode,
+ chain, rs_ctx );
+ if( ret != 0 )
+ goto exit;
+
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
{
- mbedtls_x509_crt *ca_chain;
- mbedtls_x509_crl *ca_crl;
+ unsigned char *crt_start, *pk_start;
+ size_t crt_len, pk_len;
-#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
- if( ssl->handshake->sni_ca_chain != NULL )
- {
- ca_chain = ssl->handshake->sni_ca_chain;
- ca_crl = ssl->handshake->sni_ca_crl;
- }
- else
-#endif
- {
- ca_chain = ssl->conf->ca_chain;
- ca_crl = ssl->conf->ca_crl;
- }
+ /* We parse the CRT chain without copying, so
+ * these pointers point into the input buffer,
+ * and are hence still valid after freeing the
+ * CRT chain. */
- /*
- * Main check: verify certificate
- */
- ret = mbedtls_x509_crt_verify_restartable(
- ssl->session_negotiate->peer_cert,
- ca_chain, ca_crl,
- ssl->conf->cert_profile,
- ssl->hostname,
- &ssl->session_negotiate->verify_result,
- ssl->conf->f_vrfy, ssl->conf->p_vrfy, rs_ctx );
+ crt_start = chain->raw.p;
+ crt_len = chain->raw.len;
+ pk_start = chain->pk_raw.p;
+ pk_len = chain->pk_raw.len;
+
+ /* Free the CRT structures before computing
+ * digest and copying the peer's public key. */
+ mbedtls_x509_crt_free( chain );
+ mbedtls_free( chain );
+ chain = NULL;
+
+ ret = ssl_remember_peer_crt_digest( ssl, crt_start, crt_len );
if( ret != 0 )
- {
- MBEDTLS_SSL_DEBUG_RET( 1, "x509_verify_cert", ret );
- }
+ goto exit;
-#if defined(MBEDTLS_SSL__ECP_RESTARTABLE)
- if( ret == MBEDTLS_ERR_ECP_IN_PROGRESS )
- return( MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS );
-#endif
-
- /*
- * Secondary checks: always done, but change 'ret' only if it was 0
- */
-
-#if defined(MBEDTLS_ECP_C)
- {
- const mbedtls_pk_context *pk = &ssl->session_negotiate->peer_cert->pk;
-
- /* 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 )
- {
- 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;
- }
- }
-#endif /* MBEDTLS_ECP_C */
-
- if( mbedtls_ssl_check_cert_usage( ssl->session_negotiate->peer_cert,
- ciphersuite_info,
- ! ssl->conf->endpoint,
- &ssl->session_negotiate->verify_result ) != 0 )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate (usage extensions)" ) );
- if( ret == 0 )
- ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE;
- }
-
- /* mbedtls_x509_crt_verify_with_profile is supposed to report a
- * verification failure through MBEDTLS_ERR_X509_CERT_VERIFY_FAILED,
- * with details encoded in the verification flags. All other kinds
- * of error codes, including those from the user provided f_vrfy
- * 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 ) )
- {
- 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;
- }
-
+ ret = ssl_remember_peer_pubkey( ssl, pk_start, pk_len );
if( ret != 0 )
- {
- uint8_t alert;
-
- /* The certificate may have been rejected for several reasons.
- Pick one and send the corresponding alert. Which alert to send
- may be a subject of debate in some cases. */
- if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_OTHER )
- alert = MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_CN_MISMATCH )
- alert = MBEDTLS_SSL_ALERT_MSG_BAD_CERT;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_KEY_USAGE )
- alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_EXT_KEY_USAGE )
- alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_NS_CERT_TYPE )
- alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_BAD_PK )
- alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_BAD_KEY )
- alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_EXPIRED )
- alert = MBEDTLS_SSL_ALERT_MSG_CERT_EXPIRED;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_REVOKED )
- alert = MBEDTLS_SSL_ALERT_MSG_CERT_REVOKED;
- else if( ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_NOT_TRUSTED )
- alert = MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA;
- else
- alert = MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN;
- mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
- alert );
- }
-
-#if defined(MBEDTLS_DEBUG_C)
- if( ssl->session_negotiate->verify_result != 0 )
- {
- MBEDTLS_SSL_DEBUG_MSG( 3, ( "! Certificate verification flags %x",
- ssl->session_negotiate->verify_result ) );
- }
- else
- {
- MBEDTLS_SSL_DEBUG_MSG( 3, ( "Certificate verification flags clear" ) );
- }
-#endif /* MBEDTLS_DEBUG_C */
+ goto exit;
}
-
- ssl->state++;
+#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ /* Pass ownership to session structure. */
+ ssl->session_negotiate->peer_cert = chain;
+ chain = NULL;
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= parse certificate" ) );
+exit:
+
+ if( ret == 0 )
+ ssl->state++;
+
+#if defined(MBEDTLS_SSL__ECP_RESTARTABLE)
+ if( ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS )
+ {
+ ssl->handshake->ecrs_peer_cert = chain;
+ chain = NULL;
+ }
+#endif
+
+ if( chain != NULL )
+ {
+ mbedtls_x509_crt_free( chain );
+ mbedtls_free( chain );
+ }
+
return( ret );
}
-#endif /* !MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
- !MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED
- !MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED
- !MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
- !MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
- !MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED
- !MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */
+#endif /* MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED */
int mbedtls_ssl_write_change_cipher_spec( mbedtls_ssl_context *ssl )
{
@@ -7403,6 +7577,11 @@
#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
handshake->sni_authmode = MBEDTLS_SSL_VERIFY_UNSET;
#endif
+
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ mbedtls_pk_init( &handshake->peer_pubkey );
+#endif
}
void mbedtls_ssl_transform_init( mbedtls_ssl_transform *transform )
@@ -7985,7 +8164,8 @@
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
}
- if( ( ret = ssl_session_copy( ssl->session_negotiate, session ) ) != 0 )
+ if( ( ret = mbedtls_ssl_session_copy( ssl->session_negotiate,
+ session ) ) != 0 )
return( ret );
ssl->handshake->resume = 1;
@@ -8839,12 +9019,17 @@
if( ssl == NULL || ssl->session == NULL )
return( NULL );
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
return( ssl->session->peer_cert );
+#else
+ return( NULL );
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
}
#endif /* MBEDTLS_X509_CRT_PARSE_C */
#if defined(MBEDTLS_SSL_CLI_C)
-int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl, mbedtls_ssl_session *dst )
+int mbedtls_ssl_get_session( const mbedtls_ssl_context *ssl,
+ mbedtls_ssl_session *dst )
{
if( ssl == NULL ||
dst == NULL ||
@@ -8854,7 +9039,7 @@
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
}
- return( ssl_session_copy( dst, ssl->session ) );
+ return( mbedtls_ssl_session_copy( dst, ssl->session ) );
}
#endif /* MBEDTLS_SSL_CLI_C */
@@ -8918,6 +9103,12 @@
#define SSL_SERIALIZED_SESSION_CONFIG_TICKET 0
#endif /* MBEDTLS_SSL_SESSION_TICKETS */
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_CRT 1
+#else
+#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_CRT 0
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
#define SSL_SERIALIZED_SESSION_CONFIG_TIME_BIT 0
#define SSL_SERIALIZED_SESSION_CONFIG_CRT_BIT 1
#define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT 2
@@ -8925,6 +9116,7 @@
#define SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC_BIT 4
#define SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT 5
#define SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT 6
+#define SSL_SERIALIZED_SESSION_CONFIG_KEEP_CRT_BIT 7
#define SSL_SERIALIZED_SESSION_CONFIG_BITFLAG \
( (uint16_t) ( \
@@ -8934,7 +9126,8 @@
( SSL_SERIALIZED_SESSION_CONFIG_MFL << SSL_SERIALIZED_SESSION_CONFIG_MFL_BIT ) | \
( SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC << SSL_SERIALIZED_SESSION_CONFIG_TRUNC_HMAC_BIT ) | \
( SSL_SERIALIZED_SESSION_CONFIG_ETM << SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT ) | \
- ( SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT ) ) )
+ ( SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT ) | \
+ ( SSL_SERIALIZED_SESSION_CONFIG_KEEP_CRT << SSL_SERIALIZED_SESSION_CONFIG_KEEP_CRT_BIT ) ) )
static unsigned char ssl_serialized_session_header[] = {
MBEDTLS_VERSION_MAJOR,
@@ -8967,7 +9160,11 @@
* opaque session_id[32];
* opaque master[48]; // fixed length in the standard
* uint32 verify_result;
- * opaque peer_cert<0..2^24-1>; // length 0 means no peer cert
+ * select (MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) {
+ * case enabled: opaque peer_cert<0..2^24-1>; // length 0 means no cert
+ * case disabled: uint8_t peer_cert_digest_type;
+ * opaque peer_cert_digest<0..2^8-1>;
+ * }
* opaque ticket<0..2^24-1>; // length 0 means no ticket
* uint32 ticket_lifetime;
* uint8 mfl_code; // up to 255 according to standard
@@ -8989,8 +9186,10 @@
uint64_t start;
#endif
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
size_t cert_len;
#endif
+#endif
/*
* Add version identifier
@@ -9060,6 +9259,7 @@
* Peer's end-entity certificate
*/
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
if( session->peer_cert == NULL )
cert_len = 0;
else
@@ -9079,6 +9279,31 @@
p += cert_len;
}
}
+
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ /* Digest of peer certificate */
+ if( session->peer_cert_digest != NULL )
+ {
+ used += 1 /* type */ + 1 /* length */ + session->peer_cert_digest_len;
+ if( used <= buf_len )
+ {
+ *p++ = (unsigned char) session->peer_cert_digest_type;
+ *p++ = (unsigned char) session->peer_cert_digest_len;
+ memcpy( p, session->peer_cert_digest,
+ session->peer_cert_digest_len );
+ p += session->peer_cert_digest_len;
+ }
+ }
+ else
+ {
+ used += 2;
+ if( used <= buf_len )
+ {
+ *p++ = (unsigned char) MBEDTLS_MD_NONE;
+ *p++ = 0;
+ }
+ }
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
#endif /* MBEDTLS_X509_CRT_PARSE_C */
/*
@@ -9155,8 +9380,10 @@
uint64_t start;
#endif
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
size_t cert_len;
-#endif /* MBEDTLS_X509_CRT_PARSE_C */
+#endif
+#endif
/*
* Check version identifier
@@ -9219,7 +9446,11 @@
/* Immediately clear invalid pointer values that have been read, in case
* we exit early before we replaced them with valid ones. */
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
session->peer_cert = NULL;
+#else
+ session->peer_cert_digest = NULL;
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
#endif
#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
session->ticket = NULL;
@@ -9229,6 +9460,7 @@
* Peer certificate
*/
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
if( 3 > (size_t)( end - p ) )
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
@@ -9264,6 +9496,36 @@
p += cert_len;
}
+#else /* defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ /* Deserialize CRT digest from the end of the ticket. */
+ if( 2 > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->peer_cert_digest_type = (mbedtls_md_type_t) *p++;
+ session->peer_cert_digest_len = (size_t) *p++;
+
+ if( session->peer_cert_digest_len != 0 )
+ {
+ const mbedtls_md_info_t *md_info =
+ mbedtls_md_info_from_type( session->peer_cert_digest_type );
+ if( md_info == NULL )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+ if( session->peer_cert_digest_len != mbedtls_md_get_size( md_info ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ if( session->peer_cert_digest_len > (size_t)( end - p ) )
+ return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
+
+ session->peer_cert_digest =
+ mbedtls_calloc( 1, session->peer_cert_digest_len );
+ if( session->peer_cert_digest == NULL )
+ return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+
+ memcpy( session->peer_cert_digest, p,
+ session->peer_cert_digest_len );
+ p += session->peer_cert_digest_len;
+ }
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
#endif /* MBEDTLS_X509_CRT_PARSE_C */
/*
@@ -10203,8 +10465,18 @@
#if defined(MBEDTLS_SSL__ECP_RESTARTABLE)
mbedtls_x509_crt_restart_free( &handshake->ecrs_ctx );
+ if( handshake->ecrs_peer_cert != NULL )
+ {
+ mbedtls_x509_crt_free( handshake->ecrs_peer_cert );
+ mbedtls_free( handshake->ecrs_peer_cert );
+ }
#endif
+#if defined(MBEDTLS_X509_CRT_PARSE_C) && \
+ !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ mbedtls_pk_free( &handshake->peer_pubkey );
+#endif /* MBEDTLS_X509_CRT_PARSE_C && !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
#if defined(MBEDTLS_SSL_PROTO_DTLS)
mbedtls_free( handshake->verify_cookie );
ssl_flight_free( handshake->flight );
@@ -10221,11 +10493,7 @@
return;
#if defined(MBEDTLS_X509_CRT_PARSE_C)
- if( session->peer_cert != NULL )
- {
- mbedtls_x509_crt_free( session->peer_cert );
- mbedtls_free( session->peer_cert );
- }
+ ssl_clear_peer_cert( session );
#endif
#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)
diff --git a/library/version_features.c b/library/version_features.c
index 245f957..5e9d923 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -456,6 +456,9 @@
#if defined(MBEDTLS_SSL_FALLBACK_SCSV)
"MBEDTLS_SSL_FALLBACK_SCSV",
#endif /* MBEDTLS_SSL_FALLBACK_SCSV */
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ "MBEDTLS_SSL_KEEP_PEER_CERTIFICATE",
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
#if defined(MBEDTLS_SSL_HW_RECORD_ACCEL)
"MBEDTLS_SSL_HW_RECORD_ACCEL",
#endif /* MBEDTLS_SSL_HW_RECORD_ACCEL */
diff --git a/library/x509_crt.c b/library/x509_crt.c
index 97d4b95..e4a35f6 100644
--- a/library/x509_crt.c
+++ b/library/x509_crt.c
@@ -990,11 +990,13 @@
/*
* 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,
diff --git a/programs/ssl/query_config.c b/programs/ssl/query_config.c
index dbb6cc1..ab3c772 100644
--- a/programs/ssl/query_config.c
+++ b/programs/ssl/query_config.c
@@ -1258,6 +1258,14 @@
}
#endif /* MBEDTLS_SSL_FALLBACK_SCSV */
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ if( strcmp( "MBEDTLS_SSL_KEEP_PEER_CERTIFICATE", config ) == 0 )
+ {
+ MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_KEEP_PEER_CERTIFICATE );
+ return( 0 );
+ }
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+
#if defined(MBEDTLS_SSL_HW_RECORD_ACCEL)
if( strcmp( "MBEDTLS_SSL_HW_RECORD_ACCEL", config ) == 0 )
{
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 9363316..d859101 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -494,6 +494,8 @@
}
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+static unsigned char peer_crt_info[1024];
+
/*
* Enabled if debug_level > 1 in code below
*/
@@ -506,8 +508,14 @@
((void) data);
#if !defined(MBEDTLS_X509_REMOVE_INFO)
- mbedtls_printf( "\nVerify requested for (Depth %d):\n", depth );
mbedtls_x509_crt_info( buf, sizeof( buf ) - 1, "", crt );
+ if( depth == 0 )
+ memcpy( peer_crt_info, buf, sizeof( buf ) );
+
+ if( opt.debug_level == 0 )
+ return( 0 );
+
+ mbedtls_printf( "\nVerify requested for (Depth %d):\n", depth );
mbedtls_printf( "%s", buf );
#else
((void) crt);
@@ -1641,8 +1649,8 @@
mbedtls_ssl_conf_sig_hashes( &conf, ssl_sig_hashes_for_test );
}
- if( opt.debug_level > 0 )
- mbedtls_ssl_conf_verify( &conf, my_verify, NULL );
+ mbedtls_ssl_conf_verify( &conf, my_verify, NULL );
+ memset( peer_crt_info, 0, sizeof( peer_crt_info ) );
#endif /* MBEDTLS_X509_CRT_PARSE_C */
#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
@@ -2021,13 +2029,8 @@
mbedtls_printf( " ok\n" );
#if !defined(MBEDTLS_X509_REMOVE_INFO)
- if( mbedtls_ssl_get_peer_cert( &ssl ) != NULL )
- {
- mbedtls_printf( " . Peer certificate information ...\n" );
- mbedtls_x509_crt_info( (char *) buf, sizeof( buf ) - 1, " ",
- mbedtls_ssl_get_peer_cert( &ssl ) );
- mbedtls_printf( "%s\n", buf );
- }
+ mbedtls_printf( " . Peer certificate information ...\n" );
+ mbedtls_printf( "%s\n", peer_crt_info );
#endif /* !MBEDTLS_X509_REMOVE_INFO */
#endif /* MBEDTLS_X509_CRT_PARSE_C */
@@ -2357,6 +2360,10 @@
mbedtls_printf( " . Restarting connection from same port..." );
fflush( stdout );
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ memset( peer_crt_info, 0, sizeof( peer_crt_info ) );
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 )
{
mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n",
@@ -2511,6 +2518,10 @@
mbedtls_printf( " . Reconnecting with saved session..." );
+#if defined(MBEDTLS_X509_CRT_PARSE_C)
+ memset( peer_crt_info, 0, sizeof( peer_crt_info ) );
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
+
if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 )
{
mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n",
diff --git a/programs/x509/cert_app.c b/programs/x509/cert_app.c
index 8eded34..fd52719 100644
--- a/programs/x509/cert_app.c
+++ b/programs/x509/cert_app.c
@@ -467,9 +467,12 @@
/*
* 5. Print the certificate
*/
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ mbedtls_printf( " . Peer certificate information ... skipped\n" );
+#else
mbedtls_printf( " . Peer certificate information ...\n" );
ret = mbedtls_x509_crt_info( (char *) buf, sizeof( buf ) - 1, " ",
- ssl.session->peer_cert );
+ mbedtls_ssl_get_peer_cert( &ssl ) );
if( ret == -1 )
{
mbedtls_printf( " failed\n ! mbedtls_x509_crt_info returned %d\n\n", ret );
@@ -477,6 +480,7 @@
}
mbedtls_printf( "%s\n", buf );
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
mbedtls_ssl_close_notify( &ssl );
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 8414d54..83e33fd 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -885,6 +885,22 @@
if_build_succeeded tests/ssl-opt.sh -f "Max fragment length"
}
+component_test_asan_remove_peer_certificate () {
+ msg "build: default config with MBEDTLS_SSL_KEEP_PEER_CERTIFICATE disabled (ASan build)"
+ scripts/config.pl unset MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
+ CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
+ make
+
+ msg "test: !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE"
+ make test
+
+ msg "test: ssl-opt.sh, !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE"
+ if_build_succeeded tests/ssl-opt.sh
+
+ msg "test: compat.sh, !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE"
+ if_build_succeeded tests/compat.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
diff --git a/tests/suites/test_suite_ssl.function b/tests/suites/test_suite_ssl.function
index c6dd564..fb77102 100644
--- a/tests/suites/test_suite_ssl.function
+++ b/tests/suites/test_suite_ssl.function
@@ -1,6 +1,7 @@
/* BEGIN_HEADER */
#include <mbedtls/ssl.h>
#include <mbedtls/ssl_internal.h>
+#include <mbedtls/md.h>
/*
* Helper function setting up inverse record transformations
@@ -287,15 +288,40 @@
#if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(MBEDTLS_FS_IO)
if( strlen( crt_file ) != 0 )
{
+ mbedtls_x509_crt tmp_crt;
int ret;
+ mbedtls_x509_crt_init( &tmp_crt );
+ ret = mbedtls_x509_crt_parse_file( &tmp_crt, crt_file );
+ if( ret != 0 )
+ return( ret );
+
+#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+ /* Calculate digest of temporary CRT. */
+ session->peer_cert_digest =
+ mbedtls_calloc( 1, MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN );
+ if( session->peer_cert_digest == NULL )
+ return( -1 );
+ ret = mbedtls_md( mbedtls_md_info_from_type(
+ MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE ),
+ tmp_crt.raw.p, tmp_crt.raw.len,
+ session->peer_cert_digest );
+ if( ret != 0 )
+ return( ret );
+ session->peer_cert_digest_type =
+ MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE;
+ session->peer_cert_digest_len =
+ MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN;
+#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ /* Move temporary CRT. */
session->peer_cert = mbedtls_calloc( 1, sizeof( *session->peer_cert ) );
if( session->peer_cert == NULL )
return( -1 );
+ *session->peer_cert = tmp_crt;
+ memset( &tmp_crt, 0, sizeof( tmp_crt ) );
+#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
- ret = mbedtls_x509_crt_parse_file( session->peer_cert, crt_file );
- if( ret != 0 )
- return( ret );
+ mbedtls_x509_crt_free( &tmp_crt );
}
#else
(void) crt_file;
@@ -681,6 +707,7 @@
restored.master, sizeof( original.master ) ) == 0 );
#if defined(MBEDTLS_X509_CRT_PARSE_C)
+#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
TEST_ASSERT( ( original.peer_cert == NULL ) ==
( restored.peer_cert == NULL ) );
if( original.peer_cert != NULL )
@@ -691,7 +718,21 @@
restored.peer_cert->raw.p,
original.peer_cert->raw.len ) == 0 );
}
-#endif
+#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+ TEST_ASSERT( original.peer_cert_digest_type ==
+ restored.peer_cert_digest_type );
+ TEST_ASSERT( original.peer_cert_digest_len ==
+ restored.peer_cert_digest_len );
+ TEST_ASSERT( ( original.peer_cert_digest == NULL ) ==
+ ( restored.peer_cert_digest == NULL ) );
+ if( original.peer_cert_digest != NULL )
+ {
+ TEST_ASSERT( memcmp( original.peer_cert_digest,
+ restored.peer_cert_digest,
+ original.peer_cert_digest_len ) == 0 );
+ }
+#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+#endif /* MBEDTLS_X509_CRT_PARSE_C */
TEST_ASSERT( original.verify_result == restored.verify_result );
#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C)