Merge pull request #3946 from AndrzejKurek/optimized-key-exchange

Key exchange optimizations
diff --git a/configs/baremetal.h b/configs/baremetal.h
index 80ed74c..a0fb744 100644
--- a/configs/baremetal.h
+++ b/configs/baremetal.h
@@ -161,6 +161,13 @@
 #define MBEDTLS_FI_COUNTERMEASURES
 #define MBEDTLS_CCM_SHUFFLING_MASKING
 
+/* Further optimizations */
+#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
+#define MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION
+#define MBEDTLS_SSL_FREE_SERVER_CERTIFICATE
+#define MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
+#define MBEDTLS_SSL_EARLY_KEY_COMPUTATION
+
 #if defined(MBEDTLS_USER_CONFIG_FILE)
 #include MBEDTLS_USER_CONFIG_FILE
 #endif
diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index 6e7c270..4c92954 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -910,6 +910,15 @@
 #undef MBEDTLS_HASHES_ENABLED
 #endif /* MBEDTLS_MD_SINGLE_HASH */
 
+#if defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION) && !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+#error "MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION can only be used with MBEDTLS_SSL_KEEP_PEER_CERTIFICATE"
+#endif
+
+
+#if defined(MBEDTLS_SSL_EARLY_KEY_COMPUTATION) && !defined(MBEDTLS_USE_TINYCRYPT)
+#error "MBEDTLS_SSL_EARLY_KEY_COMPUTATION can only be used with MBEDTLS_USE_TINYCRYPT"
+#endif
+
 /*
  * Note: the dependency on TinyCrypt is reflected in several ways in the code:
  *
diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index ee25107..22eba11 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -41,6 +41,15 @@
  */
 
 /**
+ * \def MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION
+ *
+ * Enable the delayed verification of server
+ * certificates on the client side.
+ *
+ */
+//#define MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION
+
+/**
  * \def MBEDTLS_HAVE_ASM
  *
  * The compiler has support for asm().
@@ -1593,6 +1602,39 @@
 #define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
 
 /**
+ * \def MBEDTLS_SSL_FREE_SERVER_CERTIFICATE
+ *
+ * This option controls determines whether the server certificate is discarded
+ * after a handshake when the MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is enabled.
+ *
+ * Use of this option is useful in combined with the delayed certificate verification
+ * when the server certificate has to be kept for the duration of the handshake
+ * but not afterwards.
+ *
+ */
+//#define MBEDTLS_SSL_FREE_SERVER_CERTIFICATE
+
+
+/**
+ * \def MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
+ *
+ * Force stack to immediately transmit messages.
+ *
+ * Requires: MBEDTLS_SSL_PROTO_DTLS
+ */
+//#define MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
+
+/**
+ * \def MBEDTLS_SSL_EARLY_KEY_COMPUTATION
+ *
+ * Create ephemeral Diffie-Hellman key pair after
+ * the ClientHello has been successfully transmitted.
+ *
+ * Requires:
+ */
+//#define MBEDTLS_SSL_EARLY_KEY_COMPUTATION
+
+/**
  * \def MBEDTLS_SSL_HW_RECORD_ACCEL
  *
  * Enable hooking functions in SSL module for hardware acceleration of
diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index ea1b847..441109d 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -573,6 +573,10 @@
 
 #if defined(MBEDTLS_USE_TINYCRYPT)
     uint8_t ecdh_privkey[NUM_ECC_BYTES];
+#if defined(MBEDTLS_SSL_EARLY_KEY_COMPUTATION)
+    uint8_t ecdhe_computed;
+    uint8_t ecdh_publickey[2*NUM_ECC_BYTES];
+#endif /* MBEDTLS_SSL_EARLY_KEY_COMPUTATION */
     uint8_t ecdh_peerkey[2*NUM_ECC_BYTES];
 #endif /* MBEDTLS_USE_TINYCRYPT */
 
@@ -1085,6 +1089,14 @@
                                 mbedtls_md_type_t md );
 #endif
 
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) && defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION)
+int mbedtls_ssl_parse_delayed_certificate_verify( mbedtls_ssl_context *ssl,
+                                                  int authmode,
+                                                  mbedtls_x509_crt *chain,
+                                                  void *rs_ctx );
+#endif /* MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED && MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION */
+
+
 static inline int mbedtls_ssl_get_minor_ver( mbedtls_ssl_context const *ssl )
 {
 #if !defined(MBEDTLS_SSL_CONF_FIXED_MINOR_VER)
@@ -1191,6 +1203,9 @@
 void mbedtls_ssl_recv_flight_completed( mbedtls_ssl_context *ssl );
 int mbedtls_ssl_resend( mbedtls_ssl_context *ssl );
 int mbedtls_ssl_flight_transmit( mbedtls_ssl_context *ssl );
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+void mbedtls_ssl_immediate_flight_done( mbedtls_ssl_context *ssl );
+#endif
 #endif
 
 /* Visible for testing purposes only */
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index cec2f09..85473e6 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -1141,11 +1141,17 @@
     }
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
-    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) &&
-        ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )
     {
-        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
-        return( ret );
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+        mbedtls_ssl_immediate_flight_done( ssl );
+#else
+        if( ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
+            return( ret );
+        }
+#endif
     }
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
 
@@ -3593,7 +3599,7 @@
                                           size_t buflen,
                                           size_t *olen )
 {
-    int ret;
+    int ret = MBEDTLS_ERR_PLATFORM_FAULT_DETECTED;
     unsigned char *p, *end;
     size_t n;
     mbedtls_ssl_ciphersuite_handle_t ciphersuite_info =
@@ -3654,18 +3660,23 @@
 
     {
         ((void) n);
-
+        ((void) ret);
         if( (size_t)( end - p ) < 2 * NUM_ECC_BYTES + 2 )
             return( MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL );
 
         *p++ = 2 * NUM_ECC_BYTES + 1;
         *p++ = 0x04; /* uncompressed point presentation */
 
+#if defined(MBEDTLS_SSL_EARLY_KEY_COMPUTATION)
+        mbedtls_platform_memcpy( p, ssl->handshake->ecdh_publickey,
+                                 2 * NUM_ECC_BYTES );
+#else
         ret = uECC_make_key( p, ssl->handshake->ecdh_privkey );
         if( ret == UECC_FAULT_DETECTED )
             return( MBEDTLS_ERR_PLATFORM_FAULT_DETECTED );
         if( ret != UECC_SUCCESS )
             return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+#endif /* MBEDTLS_SSL_EARLY_KEY_COMPUTATION && MBEDTLS_USE_TINYCRYPT */
         p += 2 * NUM_ECC_BYTES;
     }
     else
@@ -4217,7 +4228,11 @@
  */
 int mbedtls_ssl_handshake_client_step( mbedtls_ssl_context *ssl )
 {
-    int ret = 0;
+    int ret = MBEDTLS_ERR_PLATFORM_FAULT_DETECTED;
+#if defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION)
+    void *rs_ctx = NULL;
+    int authmode;
+#endif /* MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION */
 
     if( ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER || ssl->handshake == NULL )
         return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
@@ -4246,10 +4261,12 @@
     }
 #endif
 
+    ret = MBEDTLS_ERR_PLATFORM_FAULT_DETECTED;
     switch( ssl->state )
     {
         case MBEDTLS_SSL_HELLO_REQUEST:
             ssl->state = MBEDTLS_SSL_CLIENT_HELLO;
+            ret = 0;
             break;
 
        /*
@@ -4267,6 +4284,25 @@
         *        ServerHelloDone
         */
        case MBEDTLS_SSL_SERVER_HELLO:
+#if defined(MBEDTLS_SSL_EARLY_KEY_COMPUTATION) && defined(MBEDTLS_USE_TINYCRYPT)
+       {
+           volatile uint8_t ecdhe_computed = ssl->handshake->ecdhe_computed;
+           /* Make sure that the ECDHE pre-computation is only done once */
+           if( ecdhe_computed == 0 )
+           {
+               ret = uECC_make_key( ssl->handshake->ecdh_publickey, ssl->handshake->ecdh_privkey );
+               if( ret == UECC_FAULT_DETECTED )
+                   return( MBEDTLS_ERR_PLATFORM_FAULT_DETECTED );
+               if( ret != UECC_SUCCESS )
+                   return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
+               ssl->handshake->ecdhe_computed = 1;
+               ecdhe_computed = 1;
+           }
+           if( ecdhe_computed  == 0 || ssl->handshake->ecdhe_computed == 0 )
+               return( MBEDTLS_ERR_PLATFORM_FAULT_DETECTED );
+       }
+#endif /* MBEDTLS_SSL_EARLY_KEY_COMPUTATION && MBEDTLS_USE_TINYCRYPT */
+
            ret = ssl_parse_server_hello( ssl );
            break;
 
@@ -4310,6 +4346,24 @@
            break;
 
        case MBEDTLS_SSL_CLIENT_FINISHED:
+
+#if defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION)
+#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_SERVER_NAME_INDICATION)
+           authmode = ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET
+                       ? ssl->handshake->sni_authmode
+                       : mbedtls_ssl_conf_get_authmode( ssl->conf );
+#else
+           authmode = mbedtls_ssl_conf_get_authmode( ssl->conf );
+#endif
+
+           MBEDTLS_SSL_DEBUG_MSG( 3, ( "execute delayed server certificate verification" ) );
+
+           ret = mbedtls_ssl_parse_delayed_certificate_verify( ssl, authmode,
+                                   ssl->session_negotiate->peer_cert, rs_ctx );
+           if( ret != 0 )
+               break;
+#endif /* MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION */
+
            ret = mbedtls_ssl_write_finished( ssl );
            break;
 
@@ -4335,6 +4389,7 @@
        case MBEDTLS_SSL_FLUSH_BUFFERS:
            MBEDTLS_SSL_DEBUG_MSG( 2, ( "handshake: done" ) );
            ssl->state = MBEDTLS_SSL_HANDSHAKE_WRAPUP;
+           ret = 0;
            break;
 
        case MBEDTLS_SSL_HANDSHAKE_WRAPUP:
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index 389a24e..7ef263c 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -2743,11 +2743,17 @@
     }
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
-    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) &&
-        ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )
     {
-        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
-        return( ret );
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+        mbedtls_ssl_immediate_flight_done( ssl );
+#else
+        if( ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
+            return( ret );
+        }
+#endif
     }
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
 
@@ -3802,11 +3808,17 @@
     }
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
-    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) &&
-        ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )
     {
-        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
-        return( ret );
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+        mbedtls_ssl_immediate_flight_done( ssl );
+#else
+        if( ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
+            return( ret );
+        }
+#endif
     }
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
 
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index a33760f..6415281 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -4360,6 +4360,131 @@
  * Functions to handle the DTLS retransmission state machine
  */
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
+static int ssl_swap_epochs( mbedtls_ssl_context *ssl );
+
+static int mbedtls_ssl_flight_transmit_msg( mbedtls_ssl_context *ssl, mbedtls_ssl_flight_item *msg )
+{
+    size_t max_frag_len;
+    int ret = MBEDTLS_ERR_PLATFORM_FAULT_DETECTED;
+    int const is_retransmitting =
+            ( ssl->handshake->retransmit_state == MBEDTLS_SSL_RETRANS_SENDING );
+    int const is_finished =
+        ( msg->type == MBEDTLS_SSL_MSG_HANDSHAKE &&
+          msg->p[0] == MBEDTLS_SSL_HS_FINISHED );
+
+    uint8_t const force_flush = ssl->disable_datagram_packing == 1 ?
+        SSL_FORCE_FLUSH : SSL_DONT_FORCE_FLUSH;
+
+    /* Swap epochs before sending Finished: we can't do it after
+     * sending ChangeCipherSpec, in case write returns WANT_READ.
+     * Must be done before copying, may change out_msg pointer */
+    if( is_retransmitting && is_finished && ssl->handshake->cur_msg_p == ( msg->p + 12 ) )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 2, ( "swap epochs to send finished message" ) );
+        if( ( ret = ssl_swap_epochs( ssl ) ) != 0 )
+            return( ret );
+    }
+
+    ret = ssl_get_remaining_payload_in_datagram( ssl );
+    if( ret < 0 )
+        return( ret );
+    max_frag_len = (size_t) ret;
+
+    /* CCS is copied as is, while HS messages may need fragmentation */
+    if( msg->type == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC )
+    {
+        if( max_frag_len == 0 )
+        {
+            if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 )
+                return( ret );
+
+            return( 0 );
+        }
+
+        mbedtls_platform_memcpy( ssl->out_msg, msg->p, msg->len );
+        ssl->out_msglen  = msg->len;
+        ssl->out_msgtype = msg->type;
+
+        /* Update position inside current message */
+        ssl->handshake->cur_msg_p += msg->len;
+    }
+    else
+    {
+        const unsigned char * const p = ssl->handshake->cur_msg_p;
+        const size_t hs_len = msg->len - 12;
+        const size_t frag_off = p - ( msg->p + 12 );
+        const size_t rem_len = hs_len - frag_off;
+        size_t cur_hs_frag_len, max_hs_frag_len;
+
+        if( ( max_frag_len < 12 ) || ( max_frag_len == 12 && hs_len != 0 ) )
+        {
+            if( is_finished && is_retransmitting )
+            {
+                if( ( ret = ssl_swap_epochs( ssl ) ) != 0 )
+                    return( ret );
+            }
+
+            if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 )
+                return( ret );
+
+            return( 0 );
+        }
+        max_hs_frag_len = max_frag_len - 12;
+
+        cur_hs_frag_len = rem_len > max_hs_frag_len ?
+            max_hs_frag_len : rem_len;
+
+        if( frag_off == 0 && cur_hs_frag_len != hs_len )
+        {
+            MBEDTLS_SSL_DEBUG_MSG( 2, ( "fragmenting handshake message (%u > %u)",
+                                        (unsigned) cur_hs_frag_len,
+                                        (unsigned) max_hs_frag_len ) );
+        }
+
+        /* Messages are stored with handshake headers as if not fragmented,
+         * copy beginning of headers then fill fragmentation fields.
+         * Handshake headers: type(1) len(3) seq(2) f_off(3) f_len(3) */
+        mbedtls_platform_memcpy( ssl->out_msg, msg->p, 6 );
+
+        (void)mbedtls_platform_put_uint24_be( &ssl->out_msg[6], frag_off );
+        (void)mbedtls_platform_put_uint24_be( &ssl->out_msg[9],
+                                              cur_hs_frag_len );
+
+        MBEDTLS_SSL_DEBUG_BUF( 3, "handshake header", ssl->out_msg, 12 );
+
+        /* Copy the handshake message content and set records fields */
+        mbedtls_platform_memcpy( ssl->out_msg + 12, p, cur_hs_frag_len );
+        ssl->out_msglen = cur_hs_frag_len + 12;
+        ssl->out_msgtype = msg->type;
+
+        /* Update position inside current message */
+        ssl->handshake->cur_msg_p += cur_hs_frag_len;
+    }
+
+    /* If done with the current message move to the next one if any */
+    if( ssl->handshake->cur_msg_p >= msg->p + msg->len )
+    {
+        if( msg->next != NULL )
+        {
+            ssl->handshake->cur_msg = msg->next;
+            ssl->handshake->cur_msg_p = msg->next->p + 12;
+        }
+        else
+        {
+            ssl->handshake->cur_msg = NULL;
+            ssl->handshake->cur_msg_p = NULL;
+        }
+    }
+
+    /* Actually send the message out */
+    if( ( ret = mbedtls_ssl_write_record( ssl, force_flush ) ) != 0 )
+    {
+        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_write_record", ret );
+        return( ret );
+    }
+    return( ret );
+}
+
 /*
  * Append current handshake message to current outgoing flight
  */
@@ -4402,6 +4527,24 @@
         cur->next = msg;
     }
 
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+    ssl->handshake->cur_msg = msg;
+    ssl->handshake->cur_msg_p = msg->p + 12;
+    {
+        int ret = MBEDTLS_ERR_PLATFORM_FAULT_DETECTED;
+        while( ssl->handshake->cur_msg != NULL )
+        {
+            if( ( ret = mbedtls_ssl_flight_transmit_msg( ssl, ssl->handshake->cur_msg ) ) != 0 )
+            {
+                MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit_msg", ret );
+                return( ret );
+            }
+
+            if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 )
+                return( ret );
+        }
+    }
+#endif
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= ssl_flight_append" ) );
     return( 0 );
 }
@@ -4491,6 +4634,24 @@
     return( ret );
 }
 
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+void mbedtls_ssl_immediate_flight_done( mbedtls_ssl_context *ssl )
+{
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> mbedtls_ssl_immediate_flight_done" ) );
+
+    /* Update state and set timer */
+    if( ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER )
+        ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_FINISHED;
+    else
+    {
+        ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_WAITING;
+        ssl_set_timer( ssl, ssl->handshake->retransmit_timeout );
+    }
+
+    MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= mbedtls_ssl_immediate_flight_done" ) );
+}
+#endif
+
 /*
  * Transmit or retransmit the current flight of messages.
  *
@@ -4517,121 +4678,9 @@
 
     while( ssl->handshake->cur_msg != NULL )
     {
-        size_t max_frag_len;
-        const mbedtls_ssl_flight_item * const cur = ssl->handshake->cur_msg;
-
-        int const is_finished =
-            ( cur->type == MBEDTLS_SSL_MSG_HANDSHAKE &&
-              cur->p[0] == MBEDTLS_SSL_HS_FINISHED );
-
-        uint8_t const force_flush = ssl->disable_datagram_packing == 1 ?
-            SSL_FORCE_FLUSH : SSL_DONT_FORCE_FLUSH;
-
-        /* Swap epochs before sending Finished: we can't do it after
-         * sending ChangeCipherSpec, in case write returns WANT_READ.
-         * Must be done before copying, may change out_msg pointer */
-        if( is_finished && ssl->handshake->cur_msg_p == ( cur->p + 12 ) )
+        if( ( ret = mbedtls_ssl_flight_transmit_msg( ssl, ssl->handshake->cur_msg ) ) != 0 )
         {
-            MBEDTLS_SSL_DEBUG_MSG( 2, ( "swap epochs to send finished message" ) );
-            if( ( ret = ssl_swap_epochs( ssl ) ) != 0 )
-                return( ret );
-        }
-
-        ret = ssl_get_remaining_payload_in_datagram( ssl );
-        if( ret < 0 )
-            return( ret );
-        max_frag_len = (size_t) ret;
-
-        /* CCS is copied as is, while HS messages may need fragmentation */
-        if( cur->type == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC )
-        {
-            if( max_frag_len == 0 )
-            {
-                if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 )
-                    return( ret );
-
-                continue;
-            }
-
-            mbedtls_platform_memcpy( ssl->out_msg, cur->p, cur->len );
-            ssl->out_msglen  = cur->len;
-            ssl->out_msgtype = cur->type;
-
-            /* Update position inside current message */
-            ssl->handshake->cur_msg_p += cur->len;
-        }
-        else
-        {
-            const unsigned char * const p = ssl->handshake->cur_msg_p;
-            const size_t hs_len = cur->len - 12;
-            const size_t frag_off = p - ( cur->p + 12 );
-            const size_t rem_len = hs_len - frag_off;
-            size_t cur_hs_frag_len, max_hs_frag_len;
-
-            if( ( max_frag_len < 12 ) || ( max_frag_len == 12 && hs_len != 0 ) )
-            {
-                if( is_finished )
-                {
-                    if( ( ret = ssl_swap_epochs( ssl ) ) != 0 )
-                        return( ret );
-                }
-
-                if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 )
-                    return( ret );
-
-                continue;
-            }
-            max_hs_frag_len = max_frag_len - 12;
-
-            cur_hs_frag_len = rem_len > max_hs_frag_len ?
-                max_hs_frag_len : rem_len;
-
-            if( frag_off == 0 && cur_hs_frag_len != hs_len )
-            {
-                MBEDTLS_SSL_DEBUG_MSG( 2, ( "fragmenting handshake message (%u > %u)",
-                                            (unsigned) cur_hs_frag_len,
-                                            (unsigned) max_hs_frag_len ) );
-            }
-
-            /* Messages are stored with handshake headers as if not fragmented,
-             * copy beginning of headers then fill fragmentation fields.
-             * Handshake headers: type(1) len(3) seq(2) f_off(3) f_len(3) */
-            mbedtls_platform_memcpy( ssl->out_msg, cur->p, 6 );
-
-            (void)mbedtls_platform_put_uint24_be( &ssl->out_msg[6], frag_off );
-            (void)mbedtls_platform_put_uint24_be( &ssl->out_msg[9],
-                                                  cur_hs_frag_len );
-
-            MBEDTLS_SSL_DEBUG_BUF( 3, "handshake header", ssl->out_msg, 12 );
-
-            /* Copy the handshake message content and set records fields */
-            mbedtls_platform_memcpy( ssl->out_msg + 12, p, cur_hs_frag_len );
-            ssl->out_msglen = cur_hs_frag_len + 12;
-            ssl->out_msgtype = cur->type;
-
-            /* Update position inside current message */
-            ssl->handshake->cur_msg_p += cur_hs_frag_len;
-        }
-
-        /* If done with the current message move to the next one if any */
-        if( ssl->handshake->cur_msg_p >= cur->p + cur->len )
-        {
-            if( cur->next != NULL )
-            {
-                ssl->handshake->cur_msg = cur->next;
-                ssl->handshake->cur_msg_p = cur->next->p + 12;
-            }
-            else
-            {
-                ssl->handshake->cur_msg = NULL;
-                ssl->handshake->cur_msg_p = NULL;
-            }
-        }
-
-        /* Actually send the message out */
-        if( ( ret = mbedtls_ssl_write_record( ssl, force_flush ) ) != 0 )
-        {
-            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_write_record", ret );
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit_msg", ret );
             return( ret );
         }
     }
@@ -4650,7 +4699,7 @@
 
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= mbedtls_ssl_flight_transmit" ) );
 
-    return( 0 );
+    return( ret );
 }
 
 /*
@@ -7972,6 +8021,26 @@
     return( verify_ret );
 }
 
+
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) && defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION)
+/* mbedtls_ssl_parse_delayed_certificate_verify() defines a wrapper around ssl_parse_certificate_verify
+ * to call it in ssl_cli.c rather than purely internal to ssl_tls.c.
+ */
+int mbedtls_ssl_parse_delayed_certificate_verify( mbedtls_ssl_context *ssl,
+                                                  int authmode,
+                                                  mbedtls_x509_crt *chain,
+                                                  void *rs_ctx )
+{
+
+    return( ssl_parse_certificate_verify( ssl,
+                                          authmode,
+                                          chain,
+                                          rs_ctx ) );
+
+}
+#endif /* MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED && MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION */
+
+
 #if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
 
 #if defined(MBEDTLS_SSL_RENEGOTIATION)
@@ -8112,10 +8181,19 @@
         rs_ctx = &ssl->handshake->ecrs_ctx;
 #endif
 
-    ret = ssl_parse_certificate_verify( ssl, authmode,
-                                        chain, rs_ctx );
-    if( ret != 0 )
-        goto exit;
+#if defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION)
+    if (mbedtls_ssl_conf_get_endpoint( ssl->conf ) == MBEDTLS_SSL_IS_CLIENT )
+    {
+        MBEDTLS_SSL_DEBUG_MSG( 3, ( "delay server certificate verification" ) );
+    }
+    else
+#endif /* MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION */
+    {
+        ret = ssl_parse_certificate_verify( ssl, authmode,
+                                            chain, rs_ctx );
+        if( ret != 0 )
+            goto exit;
+    }
 
 #if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
     {
@@ -8663,13 +8741,19 @@
     }
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
-    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) &&
-        ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+    if( MBEDTLS_SSL_TRANSPORT_IS_DTLS( ssl->conf->transport ) )
     {
-        MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
-        return( ret );
-    }
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+        mbedtls_ssl_immediate_flight_done( ssl );
+#else
+        if( ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
+        {
+            MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_flight_transmit", ret );
+            return( ret );
+        }
 #endif
+    }
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
 
     MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= write finished" ) );
 
@@ -12013,6 +12097,19 @@
 #endif
 #endif /* MBEDTLS_SSL_PROTO_TLS1_2 */
 
+#if defined(MBEDTLS_SSL_FREE_SERVER_CERTIFICATE) && \
+    defined(MBEDTLS_X509_CRT_PARSE_C) && \
+    defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
+    if( ssl->session_negotiate )
+    {
+        ssl_clear_peer_cert( ssl->session_negotiate );
+    }
+    if( ssl->session )
+    {
+        ssl_clear_peer_cert( ssl->session );
+    }
+#endif /* MBEDTLS_SSL_FREE_SERVER_CERTIFICATE */
+
 #if defined(MBEDTLS_DHM_C)
     mbedtls_dhm_free( &handshake->dhm_ctx );
 #endif
diff --git a/library/version_features.c b/library/version_features.c
index ea072ac..c270c3a 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -33,6 +33,9 @@
 
 static const char *features[] = {
 #if defined(MBEDTLS_VERSION_FEATURES)
+#if defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION)
+    "MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION",
+#endif /* MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION */
 #if defined(MBEDTLS_HAVE_ASM)
     "MBEDTLS_HAVE_ASM",
 #endif /* MBEDTLS_HAVE_ASM */
@@ -489,6 +492,15 @@
 #if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
     "MBEDTLS_SSL_KEEP_PEER_CERTIFICATE",
 #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
+#if defined(MBEDTLS_SSL_FREE_SERVER_CERTIFICATE)
+    "MBEDTLS_SSL_FREE_SERVER_CERTIFICATE",
+#endif /* MBEDTLS_SSL_FREE_SERVER_CERTIFICATE */
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+    "MBEDTLS_SSL_IMMEDIATE_TRANSMISSION",
+#endif /* MBEDTLS_SSL_IMMEDIATE_TRANSMISSION */
+#if defined(MBEDTLS_SSL_EARLY_KEY_COMPUTATION)
+    "MBEDTLS_SSL_EARLY_KEY_COMPUTATION",
+#endif /* MBEDTLS_SSL_EARLY_KEY_COMPUTATION */
 #if defined(MBEDTLS_SSL_HW_RECORD_ACCEL)
     "MBEDTLS_SSL_HW_RECORD_ACCEL",
 #endif /* MBEDTLS_SSL_HW_RECORD_ACCEL */
diff --git a/programs/ssl/query_config.c b/programs/ssl/query_config.c
index 0711703..4798f7c 100644
--- a/programs/ssl/query_config.c
+++ b/programs/ssl/query_config.c
@@ -130,6 +130,14 @@
 
 int query_config( const char *config )
 {
+#if defined(MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION)
+    if( strcmp( "MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION );
+        return( 0 );
+    }
+#endif /* MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION */
+
 #if defined(MBEDTLS_HAVE_ASM)
     if( strcmp( "MBEDTLS_HAVE_ASM", config ) == 0 )
     {
@@ -1346,6 +1354,30 @@
     }
 #endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
 
+#if defined(MBEDTLS_SSL_FREE_SERVER_CERTIFICATE)
+    if( strcmp( "MBEDTLS_SSL_FREE_SERVER_CERTIFICATE", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_FREE_SERVER_CERTIFICATE );
+        return( 0 );
+    }
+#endif /* MBEDTLS_SSL_FREE_SERVER_CERTIFICATE */
+
+#if defined(MBEDTLS_SSL_IMMEDIATE_TRANSMISSION)
+    if( strcmp( "MBEDTLS_SSL_IMMEDIATE_TRANSMISSION", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_IMMEDIATE_TRANSMISSION );
+        return( 0 );
+    }
+#endif /* MBEDTLS_SSL_IMMEDIATE_TRANSMISSION */
+
+#if defined(MBEDTLS_SSL_EARLY_KEY_COMPUTATION)
+    if( strcmp( "MBEDTLS_SSL_EARLY_KEY_COMPUTATION", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_EARLY_KEY_COMPUTATION );
+        return( 0 );
+    }
+#endif /* MBEDTLS_SSL_EARLY_KEY_COMPUTATION */
+
 #if defined(MBEDTLS_SSL_HW_RECORD_ACCEL)
     if( strcmp( "MBEDTLS_SSL_HW_RECORD_ACCEL", config ) == 0 )
     {
diff --git a/scripts/config.pl b/scripts/config.pl
index af85824..6d6a470 100755
--- a/scripts/config.pl
+++ b/scripts/config.pl
@@ -61,6 +61,11 @@
 #   MBEDTLS_VALIDATE_SSL_KEYS_INTEGRITY
 #   MBEDTLS_OPTIMIZE_TINYCRYPT_ASM
 #   MBEDTLS_AES_128_BIT_MASKED
+#   MBEDTLS_PLATFORM_FAULT_CALLBACKS
+#   MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION
+#   MBEDTLS_SSL_FREE_SERVER_CERTIFICATE
+#   MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
+#   MBEDTLS_SSL_EARLY_KEY_COMPUTATION
 #   and any symbol beginning _ALT
 #
 # The baremetal configuration excludes options that require a library or
@@ -149,6 +154,10 @@
 MBEDTLS_OPTIMIZE_TINYCRYPT_ASM
 MBEDTLS_AES_128_BIT_MASKED
 MBEDTLS_PLATFORM_FAULT_CALLBACKS
+MBEDTLS_SSL_DELAYED_SERVER_CERT_VERIFICATION
+MBEDTLS_SSL_FREE_SERVER_CERTIFICATE
+MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
+MBEDTLS_SSL_EARLY_KEY_COMPUTATION
 _ALT\s*$
 );
 
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index a770f6d..fd67349 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -1827,6 +1827,13 @@
     msg "test: baremetal.h + baremetal_test.h"
     if_build_succeeded make test
     if_build_succeeded tests/ssl-opt.sh
+
+    # Optional parts (slow; currently broken on OS X because programs don't
+    # seem to receive signals under valgrind on OS X).
+    if [ "$MEMORY" -gt 0 ]; then
+        msg "test: ssl-opt.sh --memcheck"
+        if_build_succeeded tests/ssl-opt.sh --memcheck
+    fi
 }
 
 component_test_hardware_entropy () {
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 81ea9e1..bcefb8e 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -1368,6 +1368,7 @@
             0
 
 # Tests for datagram packing
+requires_config_disabled MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
 run_test    "DTLS: multiple records in same datagram, client and server" \
             "$P_SRV dtls=1 dgram_packing=1 debug_level=2" \
             "$P_CLI dtls=1 dgram_packing=1 debug_level=2" \
@@ -1375,6 +1376,7 @@
             -c "next record in same datagram" \
             -s "next record in same datagram"
 
+requires_config_disabled MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
 run_test    "DTLS: multiple records in same datagram, client only" \
             "$P_SRV dtls=1 dgram_packing=0 debug_level=2" \
             "$P_CLI dtls=1 dgram_packing=1 debug_level=2" \
@@ -1382,6 +1384,7 @@
             -s "next record in same datagram" \
             -C "next record in same datagram"
 
+requires_config_disabled MBEDTLS_SSL_IMMEDIATE_TRANSMISSION
 run_test    "DTLS: multiple records in same datagram, server only" \
             "$P_SRV dtls=1 dgram_packing=1 debug_level=2" \
             "$P_CLI dtls=1 dgram_packing=0 debug_level=2" \