Merge remote-tracking branch 'restricted/pr/594' into baremetal-proposed

* restricted/pr/594:
  Adapt baremetal.h and baremetal.sh
  Don't incl. CAs in CertReq message in baremetal build
  Allow config'n of incl of CertificateReq CA list Y/N at compile-time
  Allow configuration of endpoint (cli/srv) at compile-time
  Allow configuration of read timeouts at compile-time
  Allow configuration of ConnectionID at compile-time
  Allow compile-time configuration of legacy renegotiation
  Allow compile-time configuration of authentication mode
  Allow compile-time configuration of DTLS badmac limit
  Allow compile-time configuration of DTLS anti replay
diff --git a/ChangeLog b/ChangeLog
index 49c3acf..515b19e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -38,17 +38,12 @@
      ServerHello.
    * Add new configuration option MBEDTLS_SSL_PROTO_NO_TLS that enables code
      size savings in configurations where only DTLS is used.
-
-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.
+   * Add new configuration option MBEDTLS_SSL_NO_SESSION_CACHE that enables
+     code size savings in configurations where cache-based session resumption is
+     not used.
+   * Add new configuration option MBEDTLS_SSL_NO_SESSION_RESUMPTION that
+     enables code size savings in configurations where no form of session
+     resumption is used.
 
 Bugfix
    * Server's RSA certificate in certs.c was SHA-1 signed. In the default
@@ -98,6 +93,24 @@
      improve clarity. Fixes #2258.
    * Replace multiple uses of MD2 by SHA-256 in X.509 test suite. Fixes #821.
 
+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.
+   * 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.
+
 = mbed TLS 2.16.1 branch released 2019-03-19
 
 Features
diff --git a/configs/baremetal.h b/configs/baremetal.h
index 7c09051..7ff7b07 100644
--- a/configs/baremetal.h
+++ b/configs/baremetal.h
@@ -71,6 +71,8 @@
 #define MBEDTLS_SSL_TLS_C
 #define MBEDTLS_SSL_PROTO_TLS1_2
 #define MBEDTLS_SSL_EXTENDED_MASTER_SECRET
+#define MBEDTLS_SSL_NO_SESSION_CACHE
+#define MBEDTLS_SSL_NO_SESSION_RESUMPTION
 #define MBEDTLS_SSL_COOKIE_C
 #define MBEDTLS_SSL_PROTO_DTLS
 #define MBEDTLS_SSL_PROTO_NO_TLS
@@ -102,6 +104,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/check_config.h b/include/mbedtls/check_config.h
index 4487bd7..61ca0b1 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -685,6 +685,16 @@
 #error "MBEDTLS_SSL_SERVER_NAME_INDICATION defined, but not all prerequisites"
 #endif
 
+#if defined(MBEDTLS_SSL_SESSION_TICKETS) &&  \
+    defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
+#error "MBEDTLS_SSL_SESSION_TICKETS cannot be defined with MBEDTLS_SSL_NO_SESSION_RESUMPTION"
+#endif
+
+#if !defined(MBEDTLS_SSL_NO_SESSION_CACHE) &&  \
+    defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
+#error "MBEDTLS_SSL_NO_SESSION_CACHE needs to be defined with MBEDTLS_SSL_NO_SESSION_RESUMPTION"
+#endif
+
 #if defined(MBEDTLS_THREADING_PTHREAD)
 #if !defined(MBEDTLS_THREADING_C) || defined(MBEDTLS_THREADING_IMPL)
 #error "MBEDTLS_THREADING_PTHREAD defined, but not all prerequisites"
diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index 3e70260..96a877b 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -1278,8 +1278,8 @@
  * which allows to identify DTLS connections across changes
  * in the underlying transport.
  *
- * Setting this option enables the SSL APIs `mbedtls_ssl_set_cid()`,
- * `mbedtls_ssl_get_peer_cid()` and `mbedtls_ssl_conf_cid()`.
+ * Setting this option enables the SSL APIs mbedtls_ssl_set_cid(),
+ * mbedtls_ssl_get_peer_cid() and mbedtls_ssl_conf_cid().
  * See the corresponding documentation for more information.
  *
  * \warning The Connection ID extension is still in draft state.
@@ -1664,11 +1664,64 @@
  * tickets, including authenticated encryption and key management. Example
  * callbacks are provided by MBEDTLS_SSL_TICKET_C.
  *
- * Comment this macro to disable support for SSL session tickets
+ * Requires: !MBEDTLS_SSL_NO_SESSION_RESUMPTION
+ *
+ * Comment this macro to disable support for SSL session tickets.
  */
 #define MBEDTLS_SSL_SESSION_TICKETS
 
 /**
+ * \def MBEDTLS_SSL_NO_SESSION_CACHE
+ *
+ * Disable support for cache based session resumption. This is useful to
+ * reduce code size in configurations where cache-based session resumption is
+ * not used.
+ *
+ * This option is only about the server-side support of the session caches.
+ * Client will only need !MBEDTLS_SSL_NO_SESSION_RESUMPTION to support
+ * cache based session resumption.
+ *
+ * Server-side, you also need to provide callbacks for storing and reading
+ * sessions from cache. Example callbacks are provided by MBEDTLS_SSL_CACHE_C.
+ *
+ * If MBEDTLS_SSL_NO_SESSION_RESUMPTION is defined, this needs to be defined
+ * as well.
+ *
+ * Uncomment this macro to disable support for SSL session cache.
+ */
+//#define MBEDTLS_SSL_NO_SESSION_CACHE
+
+/**
+ * \def MBEDTLS_SSL_NO_SESSION_RESUMPTION
+ *
+ * Disable support for session resumption. This is useful to reduce code size
+ * in configurations where no form of session resumption is used.
+ *
+ * \note Session resumption is part of the TLS standard, disabling this
+ * option means that the full implementation of the standard is no longer
+ * used. This shouldn't cause any interoperability issues as the standard
+ * mandates that peers who want to resume a session need to be prepared to
+ * fall back to a full handshake.
+ *
+ * When this flag is enabled, following needs to be true:
+ *     MBEDTLS_SSL_NO_SESSION_CACHE enabled
+ *     MBEDTLS_SSL_SESSION_TICKETS disabled
+ *
+ * Client-side, this is enough to enable support for cache-based session
+ * resumption (as defined by the TLS standard); for ticket-based resumption
+ * you'll also need to enable MBEDTLS_SSL_SESSION_TICKETS.
+ *
+ * Server-side, this option is only useful in conjunction with at least
+ * one of !MBEDTLS_SSL_NO_SESSION_CACHE or MBEDTLS_SSL_SESSION_TICKETS.
+ * Each one of these additionally requires an implementation of the cache
+ * or tickets, examples of which are provided by MBEDTLS_SSL_CACHE_C
+ * and MBEDTLS_SSL_TICKET_C respectively.
+ *
+ * Uncomment this macro to disable support for SSL session resumption.
+ */
+//#define MBEDTLS_SSL_NO_SESSION_RESUMPTION
+
+/**
  * \def MBEDTLS_SSL_EXPORT_KEYS
  *
  * Enable support for exporting key block and master secret.
@@ -1758,6 +1811,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/ssl.h b/include/mbedtls/ssl.h
index 7c5cadc..e7db3ff 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -906,11 +906,13 @@
     int  (*f_rng)(void *, unsigned char *, size_t);
     void *p_rng;                    /*!< context for the RNG function       */
 
+#if defined(MBEDTLS_SSL_SRV_C) && !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
     /** Callback to retrieve a session from the cache                       */
     int (*f_get_cache)(void *, mbedtls_ssl_session *);
     /** Callback to store a session into the cache                          */
     int (*f_set_cache)(void *, const mbedtls_ssl_session *);
     void *p_cache;                  /*!< context for cache callbacks        */
+#endif /* MBEDTLS_SSL_SRV_C && !MBEDTLS_SSL_NO_SESSION_CACHE */
 
 #if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
     /** Callback for setting cert according to SNI extension                */
@@ -2172,7 +2174,7 @@
 void mbedtls_ssl_conf_handshake_timeout( mbedtls_ssl_config *conf, uint32_t min, uint32_t max );
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
 
-#if defined(MBEDTLS_SSL_SRV_C)
+#if defined(MBEDTLS_SSL_SRV_C) && !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
 /**
  * \brief          Set the session cache callbacks (server-side only)
  *                 If not set, no session resuming is done (except if session
@@ -2214,9 +2216,9 @@
         void *p_cache,
         int (*f_get_cache)(void *, mbedtls_ssl_session *),
         int (*f_set_cache)(void *, const mbedtls_ssl_session *) );
-#endif /* MBEDTLS_SSL_SRV_C */
+#endif /* MBEDTLS_SSL_SRV_C && !MBEDTLS_SSL_NO_SESSION_CACHE */
 
-#if defined(MBEDTLS_SSL_CLI_C)
+#if defined(MBEDTLS_SSL_CLI_C) && !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
 /**
  * \brief          Request resumption of session (client-side only)
  *                 Session data is copied from presented session structure.
@@ -2232,7 +2234,7 @@
  * \sa             mbedtls_ssl_get_session()
  */
 int mbedtls_ssl_set_session( mbedtls_ssl_context *ssl, const mbedtls_ssl_session *session );
-#endif /* MBEDTLS_SSL_CLI_C */
+#endif /* MBEDTLS_SSL_CLI_C && !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
 
 /**
  * \brief          Load serialized session data into a session structure.
diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index 138b6fd..74c9f1a 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -509,7 +509,9 @@
     unsigned char premaster[MBEDTLS_PREMASTER_SIZE];
                                         /*!<  premaster secret        */
 
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
     int resume;                         /*!<  session resume indicator*/
+#endif /* !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
     int max_major_ver;                  /*!< max. major version client*/
     int max_minor_ver;                  /*!< max. minor version client*/
     int cli_exts;                       /*!< client extension presence*/
@@ -1081,6 +1083,33 @@
 
 
 /*
+ * Accessor functions for optional fields of various structures
+ */
+
+static inline int mbedtls_ssl_handshake_get_resume(
+        const mbedtls_ssl_handshake_params *handshake )
+{
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
+    return( handshake->resume );
+#else
+    (void) handshake;
+    return( 0 );
+#endif
+}
+
+static inline int mbedtls_ssl_get_renego_status(
+        const mbedtls_ssl_context *ssl )
+{
+#if defined(MBEDTLS_SSL_RENEGOTIATION)
+    return( ssl->renego_status );
+#else
+    (void) ssl;
+    return( MBEDTLS_SSL_INITIAL_HANDSHAKE );
+#endif
+}
+
+
+/*
  * Getter functions for fields in mbedtls_ssl_config which may
  * be fixed at compile time via one of MBEDTLS_SSL_SSL_CONF_XXX.
  */
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 92c7c73..2749389 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -828,9 +828,7 @@
         return( MBEDTLS_ERR_SSL_NO_RNG );
     }
 
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-    if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE )
-#endif
+    if( mbedtls_ssl_get_renego_status( ssl ) == MBEDTLS_SSL_INITIAL_HANDSHAKE )
     {
         ssl->major_ver = ssl->conf->min_major_ver;
         ssl->minor_ver = ssl->conf->min_minor_ver;
@@ -882,36 +880,40 @@
      *   ..   . ..    extensions length (2 bytes)
      *   ..   . ..    extensions
      */
-    n = ssl->session_negotiate->id_len;
 
-    if( n < 16 || n > 32 ||
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-        ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE ||
-#endif
-        ssl->handshake->resume == 0 )
+    /*
+     * We'll write a session of non-zero length if resumption was requested
+     * by the user, we're not renegotiating, and the session ID is of
+     * appropriate length. Otherwise make the length 0 (for now, see next code
+     * block for behaviour with tickets).
+     */
+    if( mbedtls_ssl_handshake_get_resume( ssl->handshake ) == 0 ||
+        mbedtls_ssl_get_renego_status( ssl ) != MBEDTLS_SSL_INITIAL_HANDSHAKE ||
+        ssl->session_negotiate->id_len < 16 ||
+        ssl->session_negotiate->id_len > 32 )
     {
         n = 0;
     }
+    else
+    {
+        n = ssl->session_negotiate->id_len;
+    }
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
     /*
      * RFC 5077 section 3.4: "When presenting a ticket, the client MAY
      * generate and include a Session ID in the TLS ClientHello."
      */
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-    if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE )
-#endif
+    if( mbedtls_ssl_get_renego_status( ssl ) == MBEDTLS_SSL_INITIAL_HANDSHAKE &&
+        ssl->session_negotiate->ticket != NULL &&
+        ssl->session_negotiate->ticket_len != 0 )
     {
-        if( ssl->session_negotiate->ticket != NULL &&
-                ssl->session_negotiate->ticket_len != 0 )
-        {
-            ret = ssl->conf->f_rng( ssl->conf->p_rng, ssl->session_negotiate->id, 32 );
+        ret = ssl->conf->f_rng( ssl->conf->p_rng, ssl->session_negotiate->id, 32 );
 
-            if( ret != 0 )
-                return( ret );
+        if( ret != 0 )
+            return( ret );
 
-            ssl->session_negotiate->id_len = n = 32;
-        }
+        ssl->session_negotiate->id_len = n = 32;
     }
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
@@ -985,9 +987,7 @@
     /*
      * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV
      */
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-    if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE )
-#endif
+    if( mbedtls_ssl_get_renego_status( ssl ) == MBEDTLS_SSL_INITIAL_HANDSHAKE )
     {
         MBEDTLS_SSL_DEBUG_MSG( 3, ( "adding EMPTY_RENEGOTIATION_INFO_SCSV" ) );
         *p++ = (unsigned char)( MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO >> 8 );
@@ -1797,28 +1797,30 @@
 
     /*
      * Check if the session can be resumed
+     *
+     * We're only resuming a session if it was requested (handshake->resume
+     * already set to 1 by mbedtls_ssl_set_session()), and further conditions
+     * are satisfied (not renegotiating, ID and ciphersuite match, etc).
+     *
+     * Update handshake->resume to the value it will keep for the rest of the
+     * handshake, and that will be used to determine the relative order
+     * client/server last flights, as well as in handshake_wrapup().
      */
-    if( ssl->handshake->resume == 0 || n == 0 ||
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-        ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE ||
-#endif
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
+    if( n == 0 ||
+        mbedtls_ssl_get_renego_status( ssl ) != MBEDTLS_SSL_INITIAL_HANDSHAKE ||
         ssl->session_negotiate->ciphersuite != i ||
         ssl->session_negotiate->compression != comp ||
         ssl->session_negotiate->id_len != n ||
         memcmp( ssl->session_negotiate->id, buf + 35, n ) != 0 )
     {
-        ssl->state++;
         ssl->handshake->resume = 0;
-#if defined(MBEDTLS_HAVE_TIME)
-        ssl->session_negotiate->start = mbedtls_time( NULL );
-#endif
-        ssl->session_negotiate->ciphersuite = i;
-        ssl->session_negotiate->compression = comp;
-        ssl->session_negotiate->id_len = n;
-        memcpy( ssl->session_negotiate->id, buf + 35, n );
     }
-    else
+#endif /* !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
+
+    if( mbedtls_ssl_handshake_get_resume( ssl->handshake ) == 1 )
     {
+        /* Resume a session */
         ssl->state = MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC;
 
         if( ( ret = mbedtls_ssl_derive_keys( ssl ) ) != 0 )
@@ -1829,9 +1831,21 @@
             return( ret );
         }
     }
+    else
+    {
+        /* Start a new session */
+        ssl->state++;
+#if defined(MBEDTLS_HAVE_TIME)
+        ssl->session_negotiate->start = mbedtls_time( NULL );
+#endif
+        ssl->session_negotiate->ciphersuite = i;
+        ssl->session_negotiate->compression = comp;
+        ssl->session_negotiate->id_len = n;
+        memcpy( ssl->session_negotiate->id, buf + 35, n );
+    }
 
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "%s session has been resumed",
-                   ssl->handshake->resume ? "a" : "no" ) );
+               mbedtls_ssl_handshake_get_resume( ssl->handshake ) ? "a" : "no" ) );
 
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %04x", i ) );
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d", buf[37 + n] ) );
@@ -2336,7 +2350,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 )
@@ -2352,7 +2374,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,
@@ -2362,7 +2385,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) || \
@@ -2375,11 +2398,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 */
@@ -2465,13 +2493,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 );
@@ -2480,21 +2516,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 );
 }
@@ -2801,7 +2842,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 */
 
         /*
@@ -2812,6 +2860,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 );
         }
 
@@ -2833,6 +2884,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 );
         }
 
@@ -2841,7 +2895,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 f8d2ec4..038b77e 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;
@@ -1288,16 +1342,12 @@
      * otherwise read it ourselves manually in order to support SSLv2
      * ClientHello, which doesn't use the same record layer format.
      */
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-    if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE )
-#endif
+    if( mbedtls_ssl_get_renego_status( ssl ) == MBEDTLS_SSL_INITIAL_HANDSHAKE &&
+        ( ret = mbedtls_ssl_fetch_input( ssl, 5 ) ) != 0 )
     {
-        if( ( ret = mbedtls_ssl_fetch_input( ssl, 5 ) ) != 0 )
-        {
-            /* No alert on a read error. */
-            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_fetch_input", ret );
-            return( ret );
-        }
+        /* No alert on a read error. */
+        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_fetch_input", ret );
+        return( ret );
     }
 
     buf = ssl->in_hdr;
@@ -1352,11 +1402,8 @@
     /* For DTLS if this is the initial handshake, remember the client sequence
      * number to use it in our next message (RFC 6347 4.2.1) */
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
-    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport )
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-        && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE
-#endif
-        )
+    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) &&
+        mbedtls_ssl_get_renego_status( ssl ) == MBEDTLS_SSL_INITIAL_HANDSHAKE )
     {
         /* Epoch should be 0 for initial handshakes */
         if( ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0 )
@@ -1617,11 +1664,8 @@
                        buf + cookie_offset + 1, cookie_len );
 
 #if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY)
-        if( ssl->conf->f_cookie_check != NULL
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-            && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE
-#endif
-            )
+        if( ssl->conf->f_cookie_check != NULL &&
+            mbedtls_ssl_get_renego_status( ssl ) == MBEDTLS_SSL_INITIAL_HANDSHAKE )
         {
             if( ssl->conf->f_cookie_check( ssl->conf->p_cookie,
                                      buf + cookie_offset + 1, cookie_len,
@@ -2640,15 +2684,14 @@
 
     MBEDTLS_SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 );
 
+#if !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
     /*
      * Resume is 0  by default, see ssl_handshake_init().
      * It may be already set to 1 by ssl_parse_session_ticket_ext().
      * If not, try looking up session ID in our cache.
      */
-    if( ssl->handshake->resume == 0 &&
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
-        ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE &&
-#endif
+    if( mbedtls_ssl_handshake_get_resume( ssl->handshake ) == 0 &&
+        mbedtls_ssl_get_renego_status( ssl ) == MBEDTLS_SSL_INITIAL_HANDSHAKE &&
         ssl->session_negotiate->id_len != 0 &&
         ssl->conf->f_get_cache != NULL &&
         ssl->conf->f_get_cache( ssl->conf->p_cache, ssl->session_negotiate ) == 0 )
@@ -2656,8 +2699,25 @@
         MBEDTLS_SSL_DEBUG_MSG( 3, ( "session successfully restored from cache" ) );
         ssl->handshake->resume = 1;
     }
+#endif /* !MBEDTLS_SSL_NO_SESSION_CACHE */
 
-    if( ssl->handshake->resume == 0 )
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
+    if( mbedtls_ssl_handshake_get_resume( ssl->handshake ) == 1 )
+    {
+        /*
+         * Resuming a session
+         */
+        n = ssl->session_negotiate->id_len;
+        ssl->state = MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC;
+
+        if( ( ret = mbedtls_ssl_derive_keys( ssl ) ) != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_derive_keys", ret );
+            return( ret );
+        }
+    }
+    else
+#endif /* !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
     {
         /*
          * New session, create a new session id,
@@ -2684,20 +2744,6 @@
                 return( ret );
         }
     }
-    else
-    {
-        /*
-         * Resuming a session
-         */
-        n = ssl->session_negotiate->id_len;
-        ssl->state = MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC;
-
-        if( ( ret = mbedtls_ssl_derive_keys( ssl ) ) != 0 )
-        {
-            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_derive_keys", ret );
-            return( ret );
-        }
-    }
 
     /*
      *    38  .  38     session id length
@@ -2714,7 +2760,7 @@
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "server hello, session id len.: %d", n ) );
     MBEDTLS_SSL_DEBUG_BUF( 3,   "server hello, session id", buf + 39, n );
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "%s session has been resumed",
-                   ssl->handshake->resume ? "a" : "no" ) );
+            mbedtls_ssl_handshake_get_resume( ssl->handshake ) ? "a" : "no" ) );
 
     *p++ = (unsigned char)( ssl->session_negotiate->ciphersuite >> 8 );
     *p++ = (unsigned char)( ssl->session_negotiate->ciphersuite      );
@@ -2957,26 +3003,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;
         }
     }
@@ -3618,9 +3676,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
@@ -3638,12 +3695,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 ) )
         {
@@ -3653,12 +3715,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
      */
@@ -4198,7 +4254,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 )
@@ -4213,7 +4277,7 @@
     if( 0 != ret )
     {
         MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record" ), ret );
-        return( ret );
+        goto exit;
     }
 
     ssl->state++;
@@ -4223,7 +4287,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 );
@@ -4258,7 +4323,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;
         }
 
         /*
@@ -4270,7 +4336,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)
@@ -4291,7 +4358,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;
         }
 
         /*
@@ -4300,7 +4368,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++;
@@ -4315,7 +4384,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];
@@ -4324,7 +4394,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 */
@@ -4338,13 +4409,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 dc0eaf9..2a96043 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -1285,11 +1285,13 @@
     (void) ssl;
 #endif
 
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
     if( handshake->resume != 0 )
     {
         MBEDTLS_SSL_DEBUG_MSG( 3, ( "no premaster (session resumed)" ) );
         return( 0 );
     }
+#endif /* !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
 
     MBEDTLS_SSL_DEBUG_BUF( 3, "premaster secret", handshake->premaster,
                                                   handshake->pmslen );
@@ -6492,6 +6494,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;
@@ -6516,7 +6519,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,
@@ -6524,13 +6527,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
 
@@ -6540,17 +6543,27 @@
 
 #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 */
@@ -6562,8 +6575,8 @@
                                       &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;
+        if( verify_ret == 0 )
+            verify_ret = MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE;
     }
 
     /* mbedtls_x509_crt_verify_with_profile is supposed to report a
@@ -6573,19 +6586,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;
 
@@ -6630,7 +6643,7 @@
     }
 #endif /* MBEDTLS_DEBUG_C */
 
-    return( ret );
+    return( verify_ret );
 }
 
 #if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
@@ -6797,8 +6810,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. */
@@ -7310,8 +7323,6 @@
 
 void mbedtls_ssl_handshake_wrapup( mbedtls_ssl_context *ssl )
 {
-    int resume = ssl->handshake->resume;
-
     MBEDTLS_SSL_DEBUG_MSG( 3, ( "=> handshake wrapup" ) );
 
 #if defined(MBEDTLS_SSL_RENEGOTIATION)
@@ -7339,16 +7350,18 @@
     ssl->session = ssl->session_negotiate;
     ssl->session_negotiate = NULL;
 
+#if defined(MBEDTLS_SSL_SRV_C) && !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
     /*
      * Add cache entry
      */
     if( ssl->conf->f_set_cache != NULL &&
         ssl->session->id_len != 0 &&
-        resume == 0 )
+        ssl->handshake->resume == 0 )
     {
         if( ssl->conf->f_set_cache( ssl->conf->p_cache, ssl->session ) != 0 )
             MBEDTLS_SSL_DEBUG_MSG( 1, ( "cache did not store session" ) );
     }
+#endif /* MBEDTLS_SSL_SRV_C && !MBEDTLS_SSL_NO_SESSION_CACHE */
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
     if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) &&
@@ -7398,6 +7411,7 @@
     ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE;
     ssl->out_msg[0]  = MBEDTLS_SSL_HS_FINISHED;
 
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
     /*
      * In case of session resuming, invert the client and server
      * ChangeCipherSpec messages order.
@@ -7420,6 +7434,7 @@
 #endif
     }
     else
+#endif /* !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
         ssl->state++;
 
     /*
@@ -7561,6 +7576,7 @@
     memcpy( ssl->peer_verify_data, buf, hash_len );
 #endif
 
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
     if( ssl->handshake->resume != 0 )
     {
 #if defined(MBEDTLS_SSL_CLI_C)
@@ -7573,6 +7589,7 @@
 #endif
     }
     else
+#endif /* !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
         ssl->state++;
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
@@ -8223,7 +8240,7 @@
     ssl_set_timer( ssl, 0 );
 }
 
-#if defined(MBEDTLS_SSL_SRV_C)
+#if defined(MBEDTLS_SSL_SRV_C) && !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
 void mbedtls_ssl_conf_session_cache( mbedtls_ssl_config *conf,
         void *p_cache,
         int (*f_get_cache)(void *, mbedtls_ssl_session *),
@@ -8233,9 +8250,9 @@
     conf->f_get_cache = f_get_cache;
     conf->f_set_cache = f_set_cache;
 }
-#endif /* MBEDTLS_SSL_SRV_C */
+#endif /* MBEDTLS_SSL_SRV_C && !MBEDTLS_SSL_NO_SESSION_CACHE */
 
-#if defined(MBEDTLS_SSL_CLI_C)
+#if defined(MBEDTLS_SSL_CLI_C) && !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
 int mbedtls_ssl_set_session( mbedtls_ssl_context *ssl, const mbedtls_ssl_session *session )
 {
     int ret;
@@ -8256,7 +8273,7 @@
 
     return( 0 );
 }
-#endif /* MBEDTLS_SSL_CLI_C */
+#endif /* MBEDTLS_SSL_CLI_C && !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
 
 void mbedtls_ssl_conf_ciphersuites( mbedtls_ssl_config *conf,
                                    const int *ciphersuites )
diff --git a/library/version_features.c b/library/version_features.c
index 5e9d923..c0b102d 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -513,6 +513,12 @@
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
     "MBEDTLS_SSL_SESSION_TICKETS",
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
+#if defined(MBEDTLS_SSL_NO_SESSION_CACHE)
+    "MBEDTLS_SSL_NO_SESSION_CACHE",
+#endif /* MBEDTLS_SSL_NO_SESSION_CACHE */
+#if defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
+    "MBEDTLS_SSL_NO_SESSION_RESUMPTION",
+#endif /* MBEDTLS_SSL_NO_SESSION_RESUMPTION */
 #if defined(MBEDTLS_SSL_EXPORT_KEYS)
     "MBEDTLS_SSL_EXPORT_KEYS",
 #endif /* MBEDTLS_SSL_EXPORT_KEYS */
@@ -534,6 +540,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, &params );
+    if( ret != 0 )
+        return( MBEDTLS_ERR_X509_INVALID_ALG + ret );
+
+    return( mbedtls_x509_get_sig_alg( &alg, &params, 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/dtls_server.c b/programs/ssl/dtls_server.c
index dd21fbf..6566bae 100644
--- a/programs/ssl/dtls_server.c
+++ b/programs/ssl/dtls_server.c
@@ -236,11 +236,11 @@
     mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg );
     mbedtls_ssl_conf_dbg( &conf, my_debug, stdout );
 
-#if defined(MBEDTLS_SSL_CACHE_C)
+#if defined(MBEDTLS_SSL_CACHE_C) && !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
     mbedtls_ssl_conf_session_cache( &conf, &cache,
                                    mbedtls_ssl_cache_get,
                                    mbedtls_ssl_cache_set );
-#endif
+#endif /* MBEDTLS_SSL_CACHE_C && !MBEDTLS_SSL_NO_SESSION_CACHE */
 
     mbedtls_ssl_conf_ca_chain( &conf, srvcert.next, NULL );
    if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
diff --git a/programs/ssl/query_config.c b/programs/ssl/query_config.c
index bcdafb6..7154364 100644
--- a/programs/ssl/query_config.c
+++ b/programs/ssl/query_config.c
@@ -1410,6 +1410,22 @@
     }
 #endif /* MBEDTLS_SSL_SESSION_TICKETS */
 
+#if defined(MBEDTLS_SSL_NO_SESSION_CACHE)
+    if( strcmp( "MBEDTLS_SSL_NO_SESSION_CACHE", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_NO_SESSION_CACHE );
+        return( 0 );
+    }
+#endif /* MBEDTLS_SSL_NO_SESSION_CACHE */
+
+#if defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
+    if( strcmp( "MBEDTLS_SSL_NO_SESSION_RESUMPTION", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_NO_SESSION_RESUMPTION );
+        return( 0 );
+    }
+#endif /* MBEDTLS_SSL_NO_SESSION_RESUMPTION */
+
 #if defined(MBEDTLS_SSL_EXPORT_KEYS)
     if( strcmp( "MBEDTLS_SSL_EXPORT_KEYS", config ) == 0 )
     {
@@ -1466,6 +1482,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_client2.c b/programs/ssl/ssl_client2.c
index 391513b..a5d3eb1 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -2579,12 +2579,14 @@
             }
         }
 
+#if !defined(MBEDTLS_SSL_NO_SESSION_RESUMPTION)
         if( ( ret = mbedtls_ssl_set_session( &ssl, &saved_session ) ) != 0 )
         {
             mbedtls_printf( " failed\n  ! mbedtls_ssl_set_session returned -0x%x\n\n",
                             -ret );
             goto exit;
         }
+#endif /* !MBEDTLS_SSL_NO_SESSION_RESUMPTION */
 
         if( ( ret = mbedtls_net_connect( &server_fd,
                         opt.server_addr, opt.server_port,
diff --git a/programs/ssl/ssl_server.c b/programs/ssl/ssl_server.c
index 9791856..5052435 100644
--- a/programs/ssl/ssl_server.c
+++ b/programs/ssl/ssl_server.c
@@ -224,11 +224,11 @@
     mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg );
     mbedtls_ssl_conf_dbg( &conf, my_debug, stdout );
 
-#if defined(MBEDTLS_SSL_CACHE_C)
+#if defined(MBEDTLS_SSL_CACHE_C) && !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
     mbedtls_ssl_conf_session_cache( &conf, &cache,
                                    mbedtls_ssl_cache_get,
                                    mbedtls_ssl_cache_set );
-#endif
+#endif /* MBEDTLS_SSL_CACHE_C && !MBEDTLS_SSL_NO_SESSION_CACHE */
 
     mbedtls_ssl_conf_ca_chain( &conf, srvcert.next, NULL );
     if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &srvcert, &pkey ) ) != 0 )
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 5fbbddd..72b5189 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -1099,6 +1099,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;
@@ -1107,9 +1108,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.
@@ -1118,8 +1127,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 )
@@ -2577,9 +2592,11 @@
     if( opt.cache_timeout != -1 )
         mbedtls_ssl_cache_set_timeout( &cache, opt.cache_timeout );
 
+#if !defined(MBEDTLS_SSL_NO_SESSION_CACHE)
     mbedtls_ssl_conf_session_cache( &conf, &cache,
                                    mbedtls_ssl_cache_get,
                                    mbedtls_ssl_cache_set );
+#endif /* !MBEDTLS_SSL_NO_SESSION_CACHE */
 #endif
 
 #if defined(MBEDTLS_SSL_SESSION_TICKETS)
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/scripts/config.pl b/scripts/config.pl
index c10a3b3..1c7c736 100755
--- a/scripts/config.pl
+++ b/scripts/config.pl
@@ -32,6 +32,8 @@
 #   MBEDTLS_REMOVE_3DES_CIPHERSUITES
 #   MBEDTLS_SSL_HW_RECORD_ACCEL
 #   MBEDTLS_SSL_PROTO_NO_DTLS
+#   MBEDTLS_SSL_NO_SESSION_CACHE
+#   MBEDTLS_SSL_NO_SESSION_RESUMPTION
 #   MBEDTLS_RSA_NO_CRT
 #   MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
 #   MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
@@ -95,6 +97,8 @@
 MBEDTLS_REMOVE_3DES_CIPHERSUITES
 MBEDTLS_SSL_HW_RECORD_ACCEL
 MBEDTLS_SSL_PROTO_NO_TLS
+MBEDTLS_SSL_NO_SESSION_CACHE
+MBEDTLS_SSL_NO_SESSION_RESUMPTION
 MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3
 MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
 MBEDTLS_X509_REMOVE_INFO
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..996616a 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -682,6 +682,21 @@
     if_build_succeeded tests/compat.sh -t RSA
 }
 
+component_test_no_resumption () {
+    msg "build: Default + MBEDTLS_SSL_NO_SESSION_RESUMPTION (ASan build)" # ~ 6 min
+    scripts/config.pl unset MBEDTLS_SSL_SESSION_TICKETS
+    scripts/config.pl set MBEDTLS_SSL_NO_SESSION_CACHE
+    scripts/config.pl set MBEDTLS_SSL_NO_SESSION_RESUMPTION
+    CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan .
+    make
+
+    msg "test: MBEDTLS_SSL_NO_SESSION_RESUMPTION - main suites (inc. selftests) (ASan build)" # ~ 50s
+    make test
+
+    msg "test: MBEDTLS_SSL_NO_SESSION_RESUMPTION - ssl-opt.sh (ASan build)" # ~ 6 min
+    if_build_succeeded tests/ssl-opt.sh
+}
+
 component_test_small_ssl_out_content_len () {
     msg "build: small SSL_OUT_CONTENT_LEN (ASan build)"
     scripts/config.pl set MBEDTLS_SSL_IN_CONTENT_LEN 16384
@@ -728,7 +743,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 +765,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 +774,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 +822,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 +830,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 +852,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 +862,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 +886,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 +933,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 +1039,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 +1061,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 +1076,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/ssl-opt.sh b/tests/ssl-opt.sh
index c5ff97e..e76b3ed 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -2172,6 +2172,9 @@
             -s "inapropriate fallback"
 
 ## Here the expected response is a valid ServerHello prefix, up to the random.
+## Due to the way the clienthello was generated, this currently needs the
+## server to have support for session tickets.
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 requires_openssl_with_fallback_scsv
 run_test    "Fallback SCSV: not in list" \
             "$P_SRV debug_level=2" \
@@ -2248,6 +2251,8 @@
 
 # Tests for Session Tickets
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets: basic" \
             "$P_SRV debug_level=3 tickets=1" \
             "$P_CLI debug_level=3 tickets=1 reconnect=1" \
@@ -2262,6 +2267,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets: cache disabled" \
             "$P_SRV debug_level=3 tickets=1 cache_max=0" \
             "$P_CLI debug_level=3 tickets=1 reconnect=1" \
@@ -2276,6 +2283,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets: timeout" \
             "$P_SRV debug_level=3 tickets=1 cache_max=0 ticket_timeout=1" \
             "$P_CLI debug_level=3 tickets=1 reconnect=1 reco_delay=2" \
@@ -2290,6 +2299,8 @@
             -S "a session has been resumed" \
             -C "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets: session copy" \
             "$P_SRV debug_level=3 tickets=1 cache_max=0" \
             "$P_CLI debug_level=3 tickets=1 reconnect=1 reco_mode=0" \
@@ -2304,6 +2315,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets: openssl server" \
             "$O_SRV" \
             "$P_CLI debug_level=3 tickets=1 reconnect=1" \
@@ -2313,6 +2326,8 @@
             -c "parse new session ticket" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets: openssl client" \
             "$P_SRV debug_level=3 tickets=1" \
             "( $O_CLI -sess_out $SESSION; \
@@ -2327,6 +2342,8 @@
 
 # Tests for Session Tickets with DTLS
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets, DTLS: basic" \
             "$P_SRV debug_level=3 dtls=1 tickets=1" \
             "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1" \
@@ -2341,6 +2358,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets, DTLS: cache disabled" \
             "$P_SRV debug_level=3 dtls=1 tickets=1 cache_max=0" \
             "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1" \
@@ -2355,6 +2374,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets, DTLS: timeout" \
             "$P_SRV debug_level=3 dtls=1 tickets=1 cache_max=0 ticket_timeout=1" \
             "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1 reco_delay=2" \
@@ -2369,6 +2390,8 @@
             -S "a session has been resumed" \
             -C "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets, DTLS: session copy" \
             "$P_SRV debug_level=3 dtls=1 tickets=1 cache_max=0" \
             "$P_CLI debug_level=3 dtls=1 tickets=1 reconnect=1 reco_mode=0" \
@@ -2383,6 +2406,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets, DTLS: openssl server" \
             "$O_SRV -dtls1" \
             "$P_CLI dtls=1 debug_level=3 tickets=1 reconnect=1" \
@@ -2392,6 +2417,8 @@
             -c "parse new session ticket" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "Session resume using tickets, DTLS: openssl client" \
             "$P_SRV dtls=1 debug_level=3 tickets=1" \
             "( $O_CLI -dtls1 -sess_out $SESSION; \
@@ -2406,6 +2433,9 @@
 
 # Tests for Session Resume based on session-ID and cache
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: tickets enabled on client" \
             "$P_SRV debug_level=3 tickets=0" \
             "$P_CLI debug_level=3 tickets=1 reconnect=1" \
@@ -2420,6 +2450,9 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: tickets enabled on server" \
             "$P_SRV debug_level=3 tickets=1" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1" \
@@ -2434,6 +2467,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: cache_max=0" \
             "$P_SRV debug_level=3 tickets=0 cache_max=0" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1" \
@@ -2443,6 +2478,8 @@
             -S "a session has been resumed" \
             -C "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: cache_max=1" \
             "$P_SRV debug_level=3 tickets=0 cache_max=1" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1" \
@@ -2452,6 +2489,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: timeout > delay" \
             "$P_SRV debug_level=3 tickets=0" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_delay=0" \
@@ -2461,6 +2500,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: timeout < delay" \
             "$P_SRV debug_level=3 tickets=0 cache_timeout=1" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_delay=2" \
@@ -2470,6 +2511,8 @@
             -S "a session has been resumed" \
             -C "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: no timeout" \
             "$P_SRV debug_level=3 tickets=0 cache_timeout=0" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_delay=2" \
@@ -2479,6 +2522,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: session copy" \
             "$P_SRV debug_level=3 tickets=0" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1 reco_mode=0" \
@@ -2488,6 +2533,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: openssl client" \
             "$P_SRV debug_level=3 tickets=0" \
             "( $O_CLI -sess_out $SESSION; \
@@ -2500,6 +2547,8 @@
             -S "session successfully restored from ticket" \
             -s "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache: openssl server" \
             "$O_SRV" \
             "$P_CLI debug_level=3 tickets=0 reconnect=1" \
@@ -2510,6 +2559,9 @@
 
 # Tests for Session Resume based on session-ID and cache, DTLS
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: tickets enabled on client" \
             "$P_SRV dtls=1 debug_level=3 tickets=0" \
             "$P_CLI dtls=1 debug_level=3 tickets=1 reconnect=1" \
@@ -2524,6 +2576,9 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: tickets enabled on server" \
             "$P_SRV dtls=1 debug_level=3 tickets=1" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1" \
@@ -2538,6 +2593,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: cache_max=0" \
             "$P_SRV dtls=1 debug_level=3 tickets=0 cache_max=0" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1" \
@@ -2547,6 +2604,8 @@
             -S "a session has been resumed" \
             -C "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: cache_max=1" \
             "$P_SRV dtls=1 debug_level=3 tickets=0 cache_max=1" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1" \
@@ -2556,6 +2615,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: timeout > delay" \
             "$P_SRV dtls=1 debug_level=3 tickets=0" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 reco_delay=0" \
@@ -2565,6 +2626,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: timeout < delay" \
             "$P_SRV dtls=1 debug_level=3 tickets=0 cache_timeout=1" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 reco_delay=2" \
@@ -2574,6 +2637,8 @@
             -S "a session has been resumed" \
             -C "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: no timeout" \
             "$P_SRV dtls=1 debug_level=3 tickets=0 cache_timeout=0" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 reco_delay=2" \
@@ -2583,6 +2648,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: session copy" \
             "$P_SRV dtls=1 debug_level=3 tickets=0" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1 reco_mode=0" \
@@ -2592,6 +2659,8 @@
             -s "a session has been resumed" \
             -c "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: openssl client" \
             "$P_SRV dtls=1 debug_level=3 tickets=0" \
             "( $O_CLI -dtls1 -sess_out $SESSION; \
@@ -2604,6 +2673,8 @@
             -S "session successfully restored from ticket" \
             -s "a session has been resumed"
 
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "Session resume using cache, DTLS: openssl server" \
             "$O_SRV -dtls1" \
             "$P_CLI dtls=1 debug_level=3 tickets=0 reconnect=1" \
@@ -7906,6 +7977,8 @@
             -S "Injecting buffered CCS message" \
             -S "Remember CCS message"
 
+# This needs session tickets; otherwise CCS is the first message in its flight
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
 run_test    "DTLS reordering: Buffer out-of-order CCS message on client"\
             -p "$P_PXY delay_srv=NewSessionTicket" \
             "$P_SRV dgram_packing=0 cookies=0 dtls=1 debug_level=2 \
@@ -8048,6 +8121,9 @@
             -c "HTTP/1.0 200 OK"
 
 client_needs_more_time 4
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "DTLS proxy: 3d, min handshake, resumption" \
             -p "$P_PXY drop=5 delay=5 duplicate=5" \
             "$P_SRV dtls=1 dgram_packing=0 hs_timeout=500-10000 tickets=0 auth_mode=none \
@@ -8062,6 +8138,9 @@
             -c "HTTP/1.0 200 OK"
 
 client_needs_more_time 4
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_RESUMPTION
+requires_config_enabled MBEDTLS_SSL_SESSION_TICKETS
+requires_config_disabled MBEDTLS_SSL_NO_SESSION_CACHE
 run_test    "DTLS proxy: 3d, min handshake, resumption, nbio" \
             -p "$P_PXY drop=5 delay=5 duplicate=5" \
             "$P_SRV dtls=1 dgram_packing=0 hs_timeout=500-10000 tickets=0 auth_mode=none \
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>