[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Fri Jan 6 15:54:05 UTC 2017


The branch master has been updated
       via  d805a57be266ba68ae40f7177aeea69cba2dfe47 (commit)
       via  8521ced61656eedff638cfb6ff683338bbdd406e (commit)
       via  3dd826b879f75b54f217bcc2fc18740f10b282fd (commit)
       via  0baed5e90b5c211b092a1279b0dac50166cd34d2 (commit)
       via  ac52c4be12399f58c66248a0a4d00434c4ab6b54 (commit)
       via  f63e42887271c61b1c803586a47ecbfa49243a0a (commit)
       via  e96e0f8e420c42f28b0e86c9cf757f152f696321 (commit)
       via  f97d4c370844081e5e735711bd8b91979313ce7b (commit)
       via  30aeba432c99d2642bec89505fb9922518979214 (commit)
      from  71f60ef3376144885384f2b1b3f00c3d54806f38 (commit)


- Log -----------------------------------------------------------------
commit d805a57be266ba68ae40f7177aeea69cba2dfe47
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jan 6 11:01:14 2017 +0000

    Fix various style issues following feedback
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit 8521ced61656eedff638cfb6ff683338bbdd406e
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jan 5 16:12:56 2017 +0000

    Rename the chain variable to chainidx
    
    This variable represents the index of the cert within the chain, so give it
    a name that better represents that.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit 3dd826b879f75b54f217bcc2fc18740f10b282fd
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jan 5 15:05:20 2017 +0000

    Fix a double blank line style issue
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit 0baed5e90b5c211b092a1279b0dac50166cd34d2
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Jan 2 11:16:37 2017 +0000

    Initialise the al variable
    
    al can be used uninitialised in an error path.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit ac52c4be12399f58c66248a0a4d00434c4ab6b54
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Dec 2 17:14:59 2016 +0000

    Update SSL_trace to understand TLSv1.3 Certificates
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit f63e42887271c61b1c803586a47ecbfa49243a0a
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Dec 2 14:46:54 2016 +0000

    Implement TLSv1.3 style CertificateStatus
    
    We remove the separate CertificateStatus message for TLSv1.3, and instead
    send back the response in the appropriate Certificate message extension.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit e96e0f8e420c42f28b0e86c9cf757f152f696321
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Dec 2 09:14:15 2016 +0000

    Create Certificate messages in TLS1.3 format
    
    Also updates TLSProxy to be able to understand the format and parse the
    contained extensions.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit f97d4c370844081e5e735711bd8b91979313ce7b
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Dec 1 15:21:08 2016 +0000

    Extends extension parsing to take the Certificate
    
    Continuing from the previous commit we also need to extend the extensions
    framework to supply the Certificate we just read during parsing.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

commit 30aeba432c99d2642bec89505fb9922518979214
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Dec 1 15:00:37 2016 +0000

    Extend tls_construct_extensions() to enable passing of a certificate
    
    The Certificate message in TLS1.3 has an extensions block for each
    Certificate. Therefore we need to extend tls_construct_extensions() to pass
    in the certificate we are working on. We also pass in the position in the
    chain (with 0 being the first certificate).
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2020)

-----------------------------------------------------------------------

Summary of changes:
 include/openssl/ssl.h                |   3 +
 ssl/ssl_cert.c                       | 110 ------------------
 ssl/ssl_err.c                        |   5 +
 ssl/ssl_locl.h                       |   3 +-
 ssl/statem/extensions.c              |  86 +++++++-------
 ssl/statem/extensions_clnt.c         | 108 +++++++++++------
 ssl/statem/extensions_srvr.c         |  94 ++++++++++-----
 ssl/statem/statem_clnt.c             | 123 ++++++++++++--------
 ssl/statem/statem_lib.c              | 131 ++++++++++++++++++++-
 ssl/statem/statem_locl.h             | 164 ++++++++++++++++----------
 ssl/statem/statem_srvr.c             |  77 ++++++++----
 ssl/t1_trce.c                        |  32 +++--
 test/recipes/70-test_tls13messages.t |  12 +-
 test/testlib/checkhandshake.pm       |  16 ++-
 util/TLSProxy/Certificate.pm         | 219 +++++++++++++++++++++++++++++++++++
 util/TLSProxy/Message.pm             |   9 ++
 util/TLSProxy/Proxy.pm               |   1 +
 17 files changed, 827 insertions(+), 366 deletions(-)
 create mode 100644 util/TLSProxy/Certificate.pm

diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 68c13d4..0974cfe 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -2136,6 +2136,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_SSL3_WRITE_PENDING                         159
 # define SSL_F_SSL_ADD_CERT_CHAIN                         316
 # define SSL_F_SSL_ADD_CERT_TO_BUF                        319
+# define SSL_F_SSL_ADD_CERT_TO_WPACKET                    493
 # define SSL_F_SSL_ADD_CLIENTHELLO_RENEGOTIATE_EXT        298
 # define SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT                 277
 # define SSL_F_SSL_ADD_CLIENTHELLO_USE_SRTP_EXT           307
@@ -2261,6 +2262,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_COLLECT_EXTENSIONS                     435
 # define SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST          372
 # define SSL_F_TLS_CONSTRUCT_CERT_STATUS                  429
+# define SSL_F_TLS_CONSTRUCT_CERT_STATUS_BODY             494
 # define SSL_F_TLS_CONSTRUCT_CHANGE_CIPHER_SPEC           427
 # define SSL_F_TLS_CONSTRUCT_CKE_DHE                      404
 # define SSL_F_TLS_CONSTRUCT_CKE_ECDHE                    405
@@ -2332,6 +2334,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE             360
 # define SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST            361
 # define SSL_F_TLS_PROCESS_CERT_STATUS                    362
+# define SSL_F_TLS_PROCESS_CERT_STATUS_BODY               495
 # define SSL_F_TLS_PROCESS_CERT_VERIFY                    379
 # define SSL_F_TLS_PROCESS_CHANGE_CIPHER_SPEC             363
 # define SSL_F_TLS_PROCESS_CKE_DHE                        411
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index f26e876..9668976 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -740,116 +740,6 @@ int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
     return ret;
 }
 
-/* Add a certificate to the WPACKET */
-static int ssl_add_cert_to_buf(WPACKET *pkt, X509 *x)
-{
-    int len;
-    unsigned char *outbytes;
-
-    len = i2d_X509(x, NULL);
-    if (len < 0) {
-        SSLerr(SSL_F_SSL_ADD_CERT_TO_BUF, ERR_R_BUF_LIB);
-        return 0;
-    }
-    if (!WPACKET_sub_allocate_bytes_u24(pkt, len, &outbytes)
-            || i2d_X509(x, &outbytes) != len) {
-        SSLerr(SSL_F_SSL_ADD_CERT_TO_BUF, ERR_R_INTERNAL_ERROR);
-        return 0;
-    }
-
-    return 1;
-}
-
-/* Add certificate chain to internal SSL BUF_MEM structure */
-int ssl_add_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk)
-{
-    int i, chain_count;
-    X509 *x;
-    STACK_OF(X509) *extra_certs;
-    STACK_OF(X509) *chain = NULL;
-    X509_STORE *chain_store;
-
-    if (cpk == NULL || cpk->x509 == NULL)
-        return 1;
-
-    x = cpk->x509;
-
-    /*
-     * If we have a certificate specific chain use it, else use parent ctx.
-     */
-    if (cpk->chain)
-        extra_certs = cpk->chain;
-    else
-        extra_certs = s->ctx->extra_certs;
-
-    if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs)
-        chain_store = NULL;
-    else if (s->cert->chain_store)
-        chain_store = s->cert->chain_store;
-    else
-        chain_store = s->ctx->cert_store;
-
-    if (chain_store) {
-        X509_STORE_CTX *xs_ctx = X509_STORE_CTX_new();
-
-        if (xs_ctx == NULL) {
-            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_MALLOC_FAILURE);
-            return (0);
-        }
-        if (!X509_STORE_CTX_init(xs_ctx, chain_store, x, NULL)) {
-            X509_STORE_CTX_free(xs_ctx);
-            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_X509_LIB);
-            return (0);
-        }
-        /*
-         * It is valid for the chain not to be complete (because normally we
-         * don't include the root cert in the chain). Therefore we deliberately
-         * ignore the error return from this call. We're not actually verifying
-         * the cert - we're just building as much of the chain as we can
-         */
-        (void)X509_verify_cert(xs_ctx);
-        /* Don't leave errors in the queue */
-        ERR_clear_error();
-        chain = X509_STORE_CTX_get0_chain(xs_ctx);
-        i = ssl_security_cert_chain(s, chain, NULL, 0);
-        if (i != 1) {
-#if 0
-            /* Dummy error calls so mkerr generates them */
-            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_EE_KEY_TOO_SMALL);
-            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_KEY_TOO_SMALL);
-            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_MD_TOO_WEAK);
-#endif
-            X509_STORE_CTX_free(xs_ctx);
-            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
-            return 0;
-        }
-        chain_count = sk_X509_num(chain);
-        for (i = 0; i < chain_count; i++) {
-            x = sk_X509_value(chain, i);
-
-            if (!ssl_add_cert_to_buf(pkt, x)) {
-                X509_STORE_CTX_free(xs_ctx);
-                return 0;
-            }
-        }
-        X509_STORE_CTX_free(xs_ctx);
-    } else {
-        i = ssl_security_cert_chain(s, extra_certs, x, 0);
-        if (i != 1) {
-            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
-            return 0;
-        }
-        if (!ssl_add_cert_to_buf(pkt, x))
-            return 0;
-        for (i = 0; i < sk_X509_num(extra_certs); i++) {
-            x = sk_X509_value(extra_certs, i);
-            if (!ssl_add_cert_to_buf(pkt, x))
-                return 0;
-        }
-    }
-    return 1;
-}
-
 /* Build a certificate chain for current certificate */
 int ssl_build_cert_chain(SSL *s, SSL_CTX *ctx, int flags)
 {
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 1b3e409..5685817 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -92,6 +92,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL3_WRITE_PENDING), "ssl3_write_pending"},
     {ERR_FUNC(SSL_F_SSL_ADD_CERT_CHAIN), "ssl_add_cert_chain"},
     {ERR_FUNC(SSL_F_SSL_ADD_CERT_TO_BUF), "ssl_add_cert_to_buf"},
+    {ERR_FUNC(SSL_F_SSL_ADD_CERT_TO_WPACKET), "ssl_add_cert_to_wpacket"},
     {ERR_FUNC(SSL_F_SSL_ADD_CLIENTHELLO_RENEGOTIATE_EXT),
      "ssl_add_clienthello_renegotiate_ext"},
     {ERR_FUNC(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT),
@@ -259,6 +260,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST),
      "tls_construct_certificate_request"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_CERT_STATUS), "tls_construct_cert_status"},
+    {ERR_FUNC(SSL_F_TLS_CONSTRUCT_CERT_STATUS_BODY),
+     "tls_construct_cert_status_body"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_CHANGE_CIPHER_SPEC),
      "tls_construct_change_cipher_spec"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_CKE_DHE), "tls_construct_cke_dhe"},
@@ -373,6 +376,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST),
      "tls_process_certificate_request"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_CERT_STATUS), "tls_process_cert_status"},
+    {ERR_FUNC(SSL_F_TLS_PROCESS_CERT_STATUS_BODY),
+     "tls_process_cert_status_body"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_CERT_VERIFY), "tls_process_cert_verify"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_CHANGE_CIPHER_SPEC),
      "tls_process_change_cipher_spec"},
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 5671a6f..c1b331a 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1896,7 +1896,6 @@ __owur X509 *ssl_cert_get0_next_certificate(CERT *c, int first);
 void ssl_cert_set_cert_cb(CERT *c, int (*cb) (SSL *ssl, void *arg), void *arg);
 
 __owur int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk);
-__owur int ssl_add_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk);
 __owur int ssl_build_cert_chain(SSL *s, SSL_CTX *ctx, int flags);
 __owur int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain,
                                    int ref);
@@ -1952,7 +1951,7 @@ __owur size_t ssl3_final_finish_mac(SSL *s, const char *sender, size_t slen,
 __owur int ssl3_finish_mac(SSL *s, const unsigned char *buf, size_t len);
 void ssl3_free_digest_list(SSL *s);
 __owur unsigned long ssl3_output_cert_chain(SSL *s, WPACKET *pkt,
-                                            CERT_PKEY *cpk);
+                                            CERT_PKEY *cpk, int *al);
 __owur const SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,
                                             STACK_OF(SSL_CIPHER) *clnt,
                                             STACK_OF(SSL_CIPHER) *srvr);
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 8ccb76f..0c65525 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -22,8 +22,6 @@ static int final_ec_pt_formats(SSL *s, unsigned int context, int sent,
 static int init_session_ticket(SSL *s, unsigned int context);
 #ifndef OPENSSL_NO_OCSP
 static int init_status_request(SSL *s, unsigned int context);
-static int final_status_request(SSL *s, unsigned int context, int sent,
-                                        int *al);
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
 static int init_npn(SSL *s, unsigned int context);
@@ -56,13 +54,15 @@ typedef struct extensions_definition_st {
      */
     int (*init)(SSL *s, unsigned int context);
     /* Parse extension sent from client to server */
-    int (*parse_ctos)(SSL *s, PACKET *pkt, int *al);
+    int (*parse_ctos)(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
     /* Parse extension send from server to client */
-    int (*parse_stoc)(SSL *s, PACKET *pkt, int *al);
+    int (*parse_stoc)(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
     /* Construct extension sent from server to client */
-    int (*construct_stoc)(SSL *s, WPACKET *pkt, int *al);
+    int (*construct_stoc)(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                          int *al);
     /* Construct extension sent from client to server */
-    int (*construct_ctos)(SSL *s, WPACKET *pkt, int *al);
+    int (*construct_ctos)(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                          int *al);
     /*
      * Finalise extension after parsing. Always called where an extensions was
      * initialised even if the extension was not present. |sent| is set to 1 if
@@ -161,7 +161,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
         | EXT_TLS1_3_CERTIFICATE,
         init_status_request, tls_parse_ctos_status_request,
         tls_parse_stoc_status_request, tls_construct_stoc_status_request,
-        tls_construct_ctos_status_request, final_status_request
+        tls_construct_ctos_status_request, NULL
     },
 #else
     INVALID_EXTENSION,
@@ -440,15 +440,18 @@ int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
  * Runs the parser for a given extension with index |idx|. |exts| contains the
  * list of all parsed extensions previously collected by
  * tls_collect_extensions(). The parser is only run if it is applicable for the
- * given |context| and the parser has not already been run. Returns 1 on success
- * or 0 on failure. In the event of a failure |*al| is populated with a suitable
- * alert code. If an extension is not present this counted as success.
+ * given |context| and the parser has not already been run. If this is for a
+ * Certificate message, then we also provide the parser with the relevant
+ * Certificate |x| and its position in the |chainidx| with 0 being the first
+ * Certificate. Returns 1 on success or 0 on failure. In the event of a failure
+ * |*al| is populated with a suitable alert code. If an extension is not present
+ * this counted as success.
  */
 int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
-                        RAW_EXTENSION *exts, int *al)
+                        RAW_EXTENSION *exts, X509 *x, size_t chainidx, int *al)
 {
     RAW_EXTENSION *currext = &exts[idx];
-    int (*parser)(SSL *s, PACKET *pkt, int *al) = NULL;
+    int (*parser)(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al) = NULL;
 
     /* Skip if the extension is not present */
     if (!currext->present)
@@ -477,7 +480,7 @@ int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
         parser = s->server ? extdef->parse_ctos : extdef->parse_stoc;
 
         if (parser != NULL)
-            return parser(s, &currext->data, al);
+            return parser(s, &currext->data, x, chainidx, al);
 
         /*
          * If the parser is NULL we fall through to the custom extension
@@ -488,7 +491,7 @@ int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
     /*
      * This is a custom extension. We only allow this if it is a non
      * resumed session on the server side.
-     *
+     *chain
      * TODO(TLS1.3): We only allow old style <=TLS1.2 custom extensions.
      * We're going to need a new mechanism for TLS1.3 to specify which
      * messages to add the custom extensions to.
@@ -508,10 +511,13 @@ int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
 /*
  * Parse all remaining extensions that have not yet been parsed. Also calls the
  * finalisation for all extensions at the end, whether we collected them or not.
- * Returns 1 for success or 0 for failure. On failure, |*al| is populated with a
- * suitable alert code.
+ * Returns 1 for success or 0 for failure. If we are working on a Certificate
+ * message then we also pass the Certificate |x| and its position in the
+ * |chainidx|, with 0 being the first certificate. On failure, |*al| is
+ * populated with a suitable alert code.
  */
-int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts, int *al)
+int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts, X509 *x,
+                             size_t chainidx, int *al)
 {
     size_t i, numexts = OSSL_NELEM(ext_defs);
     const EXTENSION_DEFINITION *thisexd;
@@ -525,7 +531,7 @@ int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts, int *al)
 
     /* Parse each extension in turn */
     for (i = 0; i < numexts; i++) {
-        if (!tls_parse_extension(s, i, context, exts, al))
+        if (!tls_parse_extension(s, i, context, exts, x, chainidx, al))
             return 0;
     }
 
@@ -545,12 +551,15 @@ int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts, int *al)
 
 /*
  * Construct all the extensions relevant to the current |context| and write
- * them to |pkt|. Returns 1 on success or 0 on failure. If a failure occurs then
- * |al| is populated with a suitable alert code. On a failure construction stops
- * at the first extension to fail to construct.
+ * them to |pkt|. If this is an extension for a Certificate in a Certificate
+ * message, then |x| will be set to the Certificate we are handling, and
+ * |chainidx| will indicate the position in the chainidx we are processing (with
+ * 0 being the first in the chain). Returns 1 on success or 0 on failure. If a
+ * failure occurs then |al| is populated with a suitable alert code. On a
+ * failure construction stops at the first extension to fail to construct.
  */
 int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
-                             int *al)
+                             X509 *x, size_t chainidx, int *al)
 {
     size_t i;
     int addcustom = 0, min_version, max_version = 0, reason, tmpal;
@@ -605,7 +614,8 @@ int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
     }
 
     for (i = 0, thisexd = ext_defs; i < OSSL_NELEM(ext_defs); i++, thisexd++) {
-        int (*construct)(SSL *s, WPACKET *pkt, int *al);
+        int (*construct)(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                         int *al);
 
         /* Skip if not relevant for our context */
         if ((thisexd->context & context) == 0)
@@ -632,7 +642,7 @@ int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
                 || construct == NULL)
             continue;
 
-        if (!construct(s, pkt, &tmpal))
+        if (!construct(s, pkt, x, chainidx, &tmpal))
             goto err;
     }
 
@@ -782,25 +792,17 @@ static int init_session_ticket(SSL *s, unsigned int context)
 #ifndef OPENSSL_NO_OCSP
 static int init_status_request(SSL *s, unsigned int context)
 {
-    if (s->server)
+    if (s->server) {
         s->tlsext_status_type = TLSEXT_STATUSTYPE_nothing;
-
-    return 1;
-}
-
-static int final_status_request(SSL *s, unsigned int context, int sent,
-                                        int *al)
-{
-    if (s->server)
-        return 1;
-
-    /*
-     * Ensure we get sensible values passed to tlsext_status_cb in the event
-     * that we don't receive a status message
-     */
-    OPENSSL_free(s->tlsext_ocsp_resp);
-    s->tlsext_ocsp_resp = NULL;
-    s->tlsext_ocsp_resplen = 0;
+    } else {
+        /*
+         * Ensure we get sensible values passed to tlsext_status_cb in the event
+         * that we don't receive a status message
+         */
+        OPENSSL_free(s->tlsext_ocsp_resp);
+        s->tlsext_ocsp_resp = NULL;
+        s->tlsext_ocsp_resplen = 0;
+    }
 
     return 1;
 }
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index f291e5f..277e062 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -12,7 +12,8 @@
 #include "../ssl_locl.h"
 #include "statem_locl.h"
 
-int tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, X509 *x,
+                                   size_t chainidx, int *al)
 {
     /* Add RI if renegotiating */
     if (!s->renegotiate)
@@ -30,7 +31,8 @@ int tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-int tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, X509 *x,
+                                   size_t chainidx, int *al)
 {
     if (s->tlsext_hostname == NULL)
         return 1;
@@ -54,7 +56,8 @@ int tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_SRP
-int tls_construct_ctos_srp(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_srp(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al)
 {
     /* Add SRP username if there is one */
     if (s->srp_ctx.login == NULL)
@@ -105,7 +108,8 @@ static int use_ecc(SSL *s)
     return i < end;
 }
 
-int tls_construct_ctos_ec_pt_formats(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_ec_pt_formats(SSL *s, WPACKET *pkt, X509 *x,
+                                     size_t chainidx, int *al)
 {
     const unsigned char *pformats;
     size_t num_formats;
@@ -128,8 +132,8 @@ int tls_construct_ctos_ec_pt_formats(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-
-int tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt, X509 *x,
+                                        size_t chainidx, int *al)
 {
     const unsigned char *pcurves = NULL, *pcurvestmp;
     size_t num_curves = 0, i;
@@ -178,7 +182,8 @@ int tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-int tls_construct_ctos_session_ticket(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_session_ticket(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al)
 {
     size_t ticklen;
 
@@ -217,7 +222,8 @@ int tls_construct_ctos_session_ticket(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-int tls_construct_ctos_sig_algs(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_sig_algs(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                int *al)
 {
     size_t salglen;
     const unsigned char *salg;
@@ -242,10 +248,15 @@ int tls_construct_ctos_sig_algs(SSL *s, WPACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_OCSP
-int tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al)
 {
     int i;
 
+    /* This extension isn't defined for client Certificates */
+    if (x != NULL)
+        return 1;
+
     if (s->tlsext_status_type != TLSEXT_STATUSTYPE_ocsp)
         return 1;
 
@@ -304,7 +315,8 @@ int tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, int *al)
 #endif
 
 #ifndef OPENSSL_NO_NEXTPROTONEG
-int tls_construct_ctos_npn(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_npn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al)
 {
     if (s->ctx->next_proto_select_cb == NULL || s->s3->tmp.finish_md_len != 0)
         return 1;
@@ -323,7 +335,8 @@ int tls_construct_ctos_npn(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-int tls_construct_ctos_alpn(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_alpn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                            int *al)
 {
     s->s3->alpn_sent = 0;
 
@@ -351,7 +364,8 @@ int tls_construct_ctos_alpn(SSL *s, WPACKET *pkt, int *al)
 
 
 #ifndef OPENSSL_NO_SRTP
-int tls_construct_ctos_use_srtp(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_use_srtp(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                int *al)
 {
     STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = SSL_get_srtp_profiles(s);
     int i, end;
@@ -390,7 +404,8 @@ int tls_construct_ctos_use_srtp(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-int tls_construct_ctos_etm(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_etm(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al)
 {
     if (s->options & SSL_OP_NO_ENCRYPT_THEN_MAC)
         return 1;
@@ -405,11 +420,16 @@ int tls_construct_ctos_etm(SSL *s, WPACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_CT
-int tls_construct_ctos_sct(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_sct(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al)
 {
     if (s->ct_validation_callback == NULL)
         return 1;
 
+    /* Not defined for client Certificates */
+    if (x != NULL)
+        return 1;
+
     if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_signed_certificate_timestamp)
             || !WPACKET_put_bytes_u16(pkt, 0)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_SCT, ERR_R_INTERNAL_ERROR);
@@ -420,7 +440,8 @@ int tls_construct_ctos_sct(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-int tls_construct_ctos_ems(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_ems(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al)
 {
     if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_extended_master_secret)
             || !WPACKET_put_bytes_u16(pkt, 0)) {
@@ -431,7 +452,8 @@ int tls_construct_ctos_ems(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-int tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt, X509 *x,
+                                          size_t chainidx, int *al)
 {
     int currv, min_version, max_version, reason;
 
@@ -477,8 +499,8 @@ int tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-
-int tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al)
 {
 #ifndef OPENSSL_NO_TLS1_3
     size_t i, sharessent = 0, num_curves = 0;
@@ -568,7 +590,8 @@ int tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, int *al)
 #define F5_WORKAROUND_MIN_MSG_LEN   0xff
 #define F5_WORKAROUND_MAX_MSG_LEN   0x200
 
-int tls_construct_ctos_padding(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_ctos_padding(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                               int *al)
 {
     unsigned char *padbytes;
     size_t hlen;
@@ -614,7 +637,8 @@ int tls_construct_ctos_padding(SSL *s, WPACKET *pkt, int *al)
 /*
  * Parse the server's renegotiation binding and abort if it's not right
  */
-int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al)
 {
     size_t expected_len = s->s3->previous_client_finished_len
         + s->s3->previous_server_finished_len;
@@ -671,7 +695,8 @@ int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, int *al)
     return 1;
 }
 
-int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al)
 {
     if (s->tlsext_hostname == NULL || PACKET_remaining(pkt) > 0) {
         *al = SSL_AD_UNRECOGNIZED_NAME;
@@ -694,7 +719,8 @@ int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_EC
-int tls_parse_stoc_ec_pt_formats(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_ec_pt_formats(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al)
 {
     unsigned int ecpointformatlist_length;
     PACKET ecptformatlist;
@@ -729,7 +755,8 @@ int tls_parse_stoc_ec_pt_formats(SSL *s, PACKET *pkt, int *al)
 }
 #endif
 
-int tls_parse_stoc_session_ticket(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_session_ticket(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al)
 {
     if (s->tls_session_ticket_ext_cb != NULL &&
         !s->tls_session_ticket_ext_cb(s, PACKET_data(pkt),
@@ -750,17 +777,28 @@ int tls_parse_stoc_session_ticket(SSL *s, PACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_OCSP
-int tls_parse_stoc_status_request(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_status_request(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al)
 {
     /*
-     * MUST be empty and only sent if we've requested a status
-     * request message.
+     * MUST only be sent if we've requested a status
+     * request message. In TLS <= 1.2 it must also be empty.
      */
     if (s->tlsext_status_type == TLSEXT_STATUSTYPE_nothing
-            || PACKET_remaining(pkt) > 0) {
+            || (!SSL_IS_TLS13(s) && PACKET_remaining(pkt) > 0)) {
         *al = SSL_AD_UNSUPPORTED_EXTENSION;
         return 0;
     }
+
+    if (SSL_IS_TLS13(s)) {
+        /* We only know how to handle this if it's for the first Certificate in
+         * the chain. We ignore any other repsonses.
+         */
+        if (chainidx != 0)
+            return 1;
+        return tls_process_cert_status_body(s, pkt, al);
+    }
+
     /* Set flag to expect CertificateStatus message */
     s->tlsext_status_expected = 1;
 
@@ -770,7 +808,7 @@ int tls_parse_stoc_status_request(SSL *s, PACKET *pkt, int *al)
 
 
 #ifndef OPENSSL_NO_CT
-int tls_parse_stoc_sct(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_sct(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     /*
      * Only take it if we asked for it - i.e if there is no CT validation
@@ -823,7 +861,7 @@ static int ssl_next_proto_validate(PACKET *pkt)
     return 1;
 }
 
-int tls_parse_stoc_npn(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_npn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     unsigned char *selected;
     unsigned char selected_len;
@@ -873,7 +911,7 @@ int tls_parse_stoc_npn(SSL *s, PACKET *pkt, int *al)
 }
 #endif
 
-int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     size_t len;
 
@@ -910,7 +948,8 @@ int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_SRTP
-int tls_parse_stoc_use_srtp(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_use_srtp(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                            int *al)
 {
     unsigned int id, ct, mki;
     int i;
@@ -963,7 +1002,7 @@ int tls_parse_stoc_use_srtp(SSL *s, PACKET *pkt, int *al)
 }
 #endif
 
-int tls_parse_stoc_etm(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_etm(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     /* Ignore if inappropriate ciphersuite */
     if (!(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC)
@@ -974,7 +1013,7 @@ int tls_parse_stoc_etm(SSL *s, PACKET *pkt, int *al)
     return 1;
 }
 
-int tls_parse_stoc_ems(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_ems(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     s->s3->flags |= TLS1_FLAGS_RECEIVED_EXTMS;
     if (!s->hit)
@@ -983,7 +1022,8 @@ int tls_parse_stoc_ems(SSL *s, PACKET *pkt, int *al)
     return 1;
 }
 
-int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, int *al)
+int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                             int *al)
 {
 #ifndef OPENSSL_NO_TLS1_3
     unsigned int group_id;
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index 74db91d..dfe5fc5 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -14,7 +14,8 @@
 /*
  * Parse the client's renegotiation binding and abort if it's not right
  */
-int tls_parse_ctos_renegotiate(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_renegotiate(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al)
 {
     unsigned int ilen;
     const unsigned char *data;
@@ -72,7 +73,8 @@ int tls_parse_ctos_renegotiate(SSL *s, PACKET *pkt, int *al)
  *   extension.
  * - On session reconnect, the servername extension may be absent.
  */
-int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al)
 {
     unsigned int servname_type;
     PACKET sni, hostname;
@@ -134,7 +136,7 @@ int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_SRP
-int tls_parse_ctos_srp(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_srp(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     PACKET srp_I;
 
@@ -158,7 +160,8 @@ int tls_parse_ctos_srp(SSL *s, PACKET *pkt, int *al)
 #endif
 
 #ifndef OPENSSL_NO_EC
-int tls_parse_ctos_ec_pt_formats(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_ec_pt_formats(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al)
 {
     PACKET ec_point_format_list;
 
@@ -181,7 +184,8 @@ int tls_parse_ctos_ec_pt_formats(SSL *s, PACKET *pkt, int *al)
 }
 #endif                          /* OPENSSL_NO_EC */
 
-int tls_parse_ctos_session_ticket(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_session_ticket(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al)
 {
     if (s->tls_session_ticket_ext_cb &&
             !s->tls_session_ticket_ext_cb(s, PACKET_data(pkt),
@@ -194,7 +198,8 @@ int tls_parse_ctos_session_ticket(SSL *s, PACKET *pkt, int *al)
     return 1;
 }
 
-int tls_parse_ctos_sig_algs(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_sig_algs(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                            int *al)
 {
     PACKET supported_sig_algs;
 
@@ -215,10 +220,15 @@ int tls_parse_ctos_sig_algs(SSL *s, PACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_OCSP
-int tls_parse_ctos_status_request(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_status_request(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al)
 {
     PACKET responder_id_list, exts;
 
+    /* Not defined if we get one of these in a client Certificate */
+    if (x != NULL)
+        return 1;
+
     if (!PACKET_get_1(pkt, (unsigned int *)&s->tlsext_status_type)) {
         *al = SSL_AD_DECODE_ERROR;
         return 0;
@@ -309,7 +319,7 @@ int tls_parse_ctos_status_request(SSL *s, PACKET *pkt, int *al)
 #endif
 
 #ifndef OPENSSL_NO_NEXTPROTONEG
-int tls_parse_ctos_npn(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_npn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     /*
      * We shouldn't accept this extension on a
@@ -340,7 +350,7 @@ int tls_parse_ctos_npn(SSL *s, PACKET *pkt, int *al)
  * extension, not including type and length. |al| is a pointer to the alert
  * value to send in the event of a failure. Returns: 1 on success, 0 on error.
  */
-int tls_parse_ctos_alpn(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_alpn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     PACKET protocol_list, save_protocol_list, protocol;
 
@@ -373,7 +383,8 @@ int tls_parse_ctos_alpn(SSL *s, PACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_SRTP
-int tls_parse_ctos_use_srtp(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_use_srtp(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                            int *al)
 {
     STACK_OF(SRTP_PROTECTION_PROFILE) *srvr;
     unsigned int ct, mki_len, id;
@@ -443,7 +454,7 @@ int tls_parse_ctos_use_srtp(SSL *s, PACKET *pkt, int *al)
 }
 #endif
 
-int tls_parse_ctos_etm(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_etm(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     if (!(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC))
         s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC;
@@ -487,7 +498,8 @@ static int check_in_list(SSL *s, unsigned int group_id,
  * the raw PACKET data for the extension. Returns 1 on success or 0 on failure.
  * If a failure occurs then |*al| is set to an appropriate alert value.
  */
-int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                             int *al)
 {
 #ifndef OPENSSL_NO_TLS1_3
     unsigned int group_id;
@@ -616,7 +628,8 @@ int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_EC
-int tls_parse_ctos_supported_groups(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_supported_groups(SSL *s, PACKET *pkt, X509 *x,
+                                    size_t chainidx, int *al)
 {
     PACKET supported_groups_list;
 
@@ -640,7 +653,7 @@ int tls_parse_ctos_supported_groups(SSL *s, PACKET *pkt, int *al)
 }
 #endif
 
-int tls_parse_ctos_ems(SSL *s, PACKET *pkt, int *al)
+int tls_parse_ctos_ems(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al)
 {
     /* The extension must always be empty */
     if (PACKET_remaining(pkt) != 0) {
@@ -656,7 +669,8 @@ int tls_parse_ctos_ems(SSL *s, PACKET *pkt, int *al)
 /*
  * Add the server's renegotiation binding
  */
-int tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, X509 *x, size_t
+                                   chainidx, int *al)
 {
     if (!s->s3->send_connection_binding)
         return 1;
@@ -677,7 +691,8 @@ int tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-int tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, X509 *x,
+                                   size_t chainidx, int *al)
 {
     if (s->hit || s->servername_done != 1
             || s->session->tlsext_hostname == NULL)
@@ -693,7 +708,8 @@ int tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_EC
-int tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, X509 *x,
+                                     size_t chainidx, int *al)
 {
     unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
     unsigned long alg_a = s->s3->tmp.new_cipher->algorithm_auth;
@@ -718,7 +734,8 @@ int tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-int tls_construct_stoc_session_ticket(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_session_ticket(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al)
 {
     if (!s->tlsext_ticket_expected || !tls_use_ticket(s)) {
         s->tlsext_ticket_expected = 0;
@@ -735,13 +752,28 @@ int tls_construct_stoc_session_ticket(SSL *s, WPACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_OCSP
-int tls_construct_stoc_status_request(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_status_request(SSL *s, WPACKET *pkt, X509 *x,
+                                     size_t chainidx, int *al)
 {
     if (!s->tlsext_status_expected)
         return 1;
 
+    if (SSL_IS_TLS13(s) && chainidx != 0)
+        return 1;
+
     if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_status_request)
-            || !WPACKET_put_bytes_u16(pkt, 0)) {
+            || !WPACKET_start_sub_packet_u16(pkt)) {
+        SSLerr(SSL_F_TLS_CONSTRUCT_STOC_STATUS_REQUEST, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    /*
+     * In TLSv1.3 we include the certificate status itself. In <= TLSv1.2 we
+     * send back an empty extension, with the certificate status appearing as a
+     * separate message
+     */
+    if ((SSL_IS_TLS13(s) && !tls_construct_cert_status_body(s, pkt))
+            || !WPACKET_close(pkt)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_STOC_STATUS_REQUEST, ERR_R_INTERNAL_ERROR);
         return 0;
     }
@@ -750,9 +782,9 @@ int tls_construct_stoc_status_request(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-
 #ifndef OPENSSL_NO_NEXTPROTONEG
-int tls_construct_stoc_next_proto_neg(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_next_proto_neg(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al)
 {
     const unsigned char *npa;
     unsigned int npalen;
@@ -779,7 +811,8 @@ int tls_construct_stoc_next_proto_neg(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-int tls_construct_stoc_alpn(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_alpn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                            int *al)
 {
     if (s->s3->alpn_selected == NULL)
         return 1;
@@ -800,7 +833,8 @@ int tls_construct_stoc_alpn(SSL *s, WPACKET *pkt, int *al)
 }
 
 #ifndef OPENSSL_NO_SRTP
-int tls_construct_stoc_use_srtp(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_use_srtp(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                int *al)
 {
     if (s->srtp_profile == NULL)
         return 1;
@@ -819,7 +853,8 @@ int tls_construct_stoc_use_srtp(SSL *s, WPACKET *pkt, int *al)
 }
 #endif
 
-int tls_construct_stoc_etm(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_etm(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al)
 {
     if ((s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC) == 0)
         return 1;
@@ -845,7 +880,8 @@ int tls_construct_stoc_etm(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-int tls_construct_stoc_ems(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_ems(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al)
 {
     if ((s->s3->flags & TLS1_FLAGS_RECEIVED_EXTMS) == 0)
         return 1;
@@ -859,7 +895,8 @@ int tls_construct_stoc_ems(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-int tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al)
 {
 #ifndef OPENSSL_NO_TLS1_3
     unsigned char *encodedPoint;
@@ -915,7 +952,8 @@ int tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, int *al)
     return 1;
 }
 
-int tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, int *al)
+int tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, X509 *x,
+                                     size_t chainidx, int *al)
 {
     const unsigned char cryptopro_ext[36] = {
         0xfd, 0xe8,         /* 65000 */
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index a80d670..a3133ad 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -169,17 +169,6 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt)
         break;
 
     case TLS_ST_CR_CERT:
-        /*
-         * The CertificateStatus message is optional even if
-         * |tlsext_status_expected| is set
-         */
-        if (s->tlsext_status_expected && mt == SSL3_MT_CERTIFICATE_STATUS) {
-            st->hand_state = TLS_ST_CR_CERT_STATUS;
-            return 1;
-        }
-        /* Fall through */
-
-    case TLS_ST_CR_CERT_STATUS:
         if (mt == SSL3_MT_FINISHED) {
             st->hand_state = TLS_ST_CR_FINISHED;
             return 1;
@@ -1011,7 +1000,7 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt)
     }
 
     /* TLS extensions */
-    if (!tls_construct_extensions(s, pkt, EXT_CLIENT_HELLO, &al)) {
+    if (!tls_construct_extensions(s, pkt, EXT_CLIENT_HELLO, NULL, 0, &al)) {
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
         SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
         return 0;
@@ -1312,7 +1301,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
     context = SSL_IS_TLS13(s) ? EXT_TLS1_3_SERVER_HELLO
                               : EXT_TLS1_2_SERVER_HELLO;
     if (!tls_collect_extensions(s, &extpkt, context, &extensions, &al)
-            || !tls_parse_all_extensions(s, context, extensions, &al))
+            || !tls_parse_all_extensions(s, context, extensions, NULL, 0, &al))
         goto f_err;
 
 #ifndef OPENSSL_NO_SCTP
@@ -1371,19 +1360,23 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
     const unsigned char *certstart, *certbytes;
     STACK_OF(X509) *sk = NULL;
     EVP_PKEY *pkey = NULL;
+    size_t chainidx;
+    unsigned int context = 0;
 
     if ((sk = sk_X509_new_null()) == NULL) {
         SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, ERR_R_MALLOC_FAILURE);
         goto err;
     }
 
-    if (!PACKET_get_net_3(pkt, &cert_list_len)
-        || PACKET_remaining(pkt) != cert_list_len) {
+    if ((SSL_IS_TLS13(s) && !PACKET_get_1(pkt, &context))
+            || context != 0
+            || !PACKET_get_net_3(pkt, &cert_list_len)
+            || PACKET_remaining(pkt) != cert_list_len) {
         al = SSL_AD_DECODE_ERROR;
         SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_LENGTH_MISMATCH);
         goto f_err;
     }
-    while (PACKET_remaining(pkt)) {
+    for (chainidx = 0; PACKET_remaining(pkt); chainidx++) {
         if (!PACKET_get_net_3(pkt, &cert_len)
             || !PACKET_get_bytes(pkt, &certbytes, cert_len)) {
             al = SSL_AD_DECODE_ERROR;
@@ -1405,6 +1398,23 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
                    SSL_R_CERT_LENGTH_MISMATCH);
             goto f_err;
         }
+
+        if (SSL_IS_TLS13(s)) {
+            RAW_EXTENSION *rawexts = NULL;
+            PACKET extensions;
+
+            if (!PACKET_get_length_prefixed_2(pkt, &extensions)) {
+                al = SSL_AD_DECODE_ERROR;
+                SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_BAD_LENGTH);
+                goto f_err;
+            }
+            if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
+                                        &rawexts, &al)
+                    || !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
+                                                 rawexts, x, chainidx, &al))
+                goto f_err;
+        }
+
         if (!sk_X509_push(sk, x)) {
             SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, ERR_R_MALLOC_FAILURE);
             goto err;
@@ -2170,41 +2180,57 @@ MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s, PACKET *pkt)
     return MSG_PROCESS_ERROR;
 }
 
-MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt)
+/*
+ * In TLSv1.3 this is called from the extensions code, otherwise it is used to
+ * parse a separate message. Returns 1 on success or 0 on failure. On failure
+ * |*al| is populated with a suitable alert code.
+ */
+int tls_process_cert_status_body(SSL *s, PACKET *pkt, int *al)
 {
-    int al;
     size_t resplen;
     unsigned int type;
 
     if (!PACKET_get_1(pkt, &type)
         || type != TLSEXT_STATUSTYPE_ocsp) {
-        al = SSL_AD_DECODE_ERROR;
-        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, SSL_R_UNSUPPORTED_STATUS_TYPE);
-        goto f_err;
+        *al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS_BODY,
+               SSL_R_UNSUPPORTED_STATUS_TYPE);
+        return 0;
     }
     if (!PACKET_get_net_3_len(pkt, &resplen)
         || PACKET_remaining(pkt) != resplen) {
-        al = SSL_AD_DECODE_ERROR;
-        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, SSL_R_LENGTH_MISMATCH);
-        goto f_err;
+        *al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS_BODY, SSL_R_LENGTH_MISMATCH);
+        return 0;
     }
     s->tlsext_ocsp_resp = OPENSSL_malloc(resplen);
     if (s->tlsext_ocsp_resp == NULL) {
-        al = SSL_AD_INTERNAL_ERROR;
-        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, ERR_R_MALLOC_FAILURE);
-        goto f_err;
+        *al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS_BODY, ERR_R_MALLOC_FAILURE);
+        return 0;
     }
     if (!PACKET_copy_bytes(pkt, s->tlsext_ocsp_resp, resplen)) {
-        al = SSL_AD_DECODE_ERROR;
-        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS, SSL_R_LENGTH_MISMATCH);
-        goto f_err;
+        *al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_CERT_STATUS_BODY, SSL_R_LENGTH_MISMATCH);
+        return 0;
     }
     s->tlsext_ocsp_resplen = resplen;
+
+    return 1;
+}
+    
+
+MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt)
+{
+    int al;
+
+    if (!tls_process_cert_status_body(s, pkt, &al)) {
+        ssl3_send_alert(s, SSL3_AL_FATAL, al);
+        ossl_statem_set_error(s);
+        return MSG_PROCESS_ERROR;
+    }
+
     return MSG_PROCESS_CONTINUE_READING;
- f_err:
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
-    ossl_statem_set_error(s);
-    return MSG_PROCESS_ERROR;
 }
 
 /*
@@ -2986,11 +3012,19 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst)
 
 int tls_construct_client_certificate(SSL *s, WPACKET *pkt)
 {
-    if (!ssl3_output_cert_chain(s, pkt,
+    int al = SSL_AD_INTERNAL_ERROR;
+
+    /*
+     * TODO(TLS1.3): For now we must put an empty context. Needs to be filled in
+     * later
+     */
+    if ((SSL_IS_TLS13(s) && !WPACKET_put_bytes_u8(pkt, 0))
+            || !ssl3_output_cert_chain(s, pkt,
                                (s->s3->tmp.cert_req == 2) ? NULL
-                                                          : s->cert->key)) {
+                                                          : s->cert->key,
+                                &al)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR);
-        ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        ssl3_send_alert(s, SSL3_AL_FATAL, al);
         return 0;
     }
 
@@ -3108,19 +3142,10 @@ static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL *s, PACKET *pkt)
         goto err;
     }
 
-    /*
-     * TODO(TLS1.3): For now we are processing Encrypted Extensions and
-     * Certificate extensions as part of this one message. Later we need to
-     * split out the Certificate extensions into the Certificate message
-     */
-    if (!tls_collect_extensions(s, &extensions,
-                                EXT_TLS1_3_ENCRYPTED_EXTENSIONS
-                                    | EXT_TLS1_3_CERTIFICATE,
+    if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
                                 &rawexts, &al)
-            || !tls_parse_all_extensions(s,
-                                         EXT_TLS1_3_ENCRYPTED_EXTENSIONS
-                                            | EXT_TLS1_3_CERTIFICATE,
-                                         rawexts, &al))
+            || !tls_parse_all_extensions(s, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+                                         rawexts, NULL, 0, &al))
         goto err;
 
     OPENSSL_free(rawexts);
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 742925f..4b64541 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -308,12 +308,139 @@ int tls_construct_change_cipher_spec(SSL *s, WPACKET *pkt)
     return 1;
 }
 
-unsigned long ssl3_output_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk)
+/* Add a certificate to the WPACKET */
+static int ssl_add_cert_to_wpacket(SSL *s, WPACKET *pkt, X509 *x, int chain,
+                                   int *al)
 {
+    int len;
+    unsigned char *outbytes;
+
+    len = i2d_X509(x, NULL);
+    if (len < 0) {
+        SSLerr(SSL_F_SSL_ADD_CERT_TO_WPACKET, ERR_R_BUF_LIB);
+        *al = SSL_AD_INTERNAL_ERROR;
+        return 0;
+    }
+    if (!WPACKET_sub_allocate_bytes_u24(pkt, len, &outbytes)
+            || i2d_X509(x, &outbytes) != len) {
+        SSLerr(SSL_F_SSL_ADD_CERT_TO_WPACKET, ERR_R_INTERNAL_ERROR);
+        *al = SSL_AD_INTERNAL_ERROR;
+        return 0;
+    }
+
+    if (SSL_IS_TLS13(s)
+            && !tls_construct_extensions(s, pkt, EXT_TLS1_3_CERTIFICATE, x,
+                                         chain, al))
+        return 0;
+
+    return 1;
+}
+
+/* Add certificate chain to provided WPACKET */
+static int ssl_add_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk, int *al)
+{
+    int i, chain_count;
+    X509 *x;
+    STACK_OF(X509) *extra_certs;
+    STACK_OF(X509) *chain = NULL;
+    X509_STORE *chain_store;
+    int tmpal = SSL_AD_INTERNAL_ERROR;
+
+    if (cpk == NULL || cpk->x509 == NULL)
+        return 1;
+
+    x = cpk->x509;
+
+    /*
+     * If we have a certificate specific chain use it, else use parent ctx.
+     */
+    if (cpk->chain != NULL)
+        extra_certs = cpk->chain;
+    else
+        extra_certs = s->ctx->extra_certs;
+
+    if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs)
+        chain_store = NULL;
+    else if (s->cert->chain_store)
+        chain_store = s->cert->chain_store;
+    else
+        chain_store = s->ctx->cert_store;
+
+    if (chain_store != NULL) {
+        X509_STORE_CTX *xs_ctx = X509_STORE_CTX_new();
+
+        if (xs_ctx == NULL) {
+            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
+        if (!X509_STORE_CTX_init(xs_ctx, chain_store, x, NULL)) {
+            X509_STORE_CTX_free(xs_ctx);
+            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_X509_LIB);
+            goto err;
+        }
+        /*
+         * It is valid for the chain not to be complete (because normally we
+         * don't include the root cert in the chain). Therefore we deliberately
+         * ignore the error return from this call. We're not actually verifying
+         * the cert - we're just building as much of the chain as we can
+         */
+        (void)X509_verify_cert(xs_ctx);
+        /* Don't leave errors in the queue */
+        ERR_clear_error();
+        chain = X509_STORE_CTX_get0_chain(xs_ctx);
+        i = ssl_security_cert_chain(s, chain, NULL, 0);
+        if (i != 1) {
+#if 0
+            /* Dummy error calls so mkerr generates them */
+            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_EE_KEY_TOO_SMALL);
+            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_KEY_TOO_SMALL);
+            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_MD_TOO_WEAK);
+#endif
+            X509_STORE_CTX_free(xs_ctx);
+            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
+            goto err;
+        }
+        chain_count = sk_X509_num(chain);
+        for (i = 0; i < chain_count; i++) {
+            x = sk_X509_value(chain, i);
+
+            if (!ssl_add_cert_to_wpacket(s, pkt, x, i, &tmpal)) {
+                X509_STORE_CTX_free(xs_ctx);
+                goto err;
+            }
+        }
+        X509_STORE_CTX_free(xs_ctx);
+    } else {
+        i = ssl_security_cert_chain(s, extra_certs, x, 0);
+        if (i != 1) {
+            SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
+            goto err;
+        }
+        if (!ssl_add_cert_to_wpacket(s, pkt, x, 0, &tmpal))
+            goto err;
+        for (i = 0; i < sk_X509_num(extra_certs); i++) {
+            x = sk_X509_value(extra_certs, i);
+            if (!ssl_add_cert_to_wpacket(s, pkt, x, i + 1, &tmpal))
+                goto err;
+        }
+    }
+    return 1;
+
+ err:
+    *al = tmpal;
+    return 0;
+}
+
+unsigned long ssl3_output_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk,
+                                     int *al)
+{
+    int tmpal = SSL_AD_INTERNAL_ERROR;
+
     if (!WPACKET_start_sub_packet_u24(pkt)
-            || !ssl_add_cert_chain(s, pkt, cpk)
+            || !ssl_add_cert_chain(s, pkt, cpk, &tmpal)
             || !WPACKET_close(pkt)) {
         SSLerr(SSL_F_SSL3_OUTPUT_CERT_CHAIN, ERR_R_INTERNAL_ERROR);
+        *al = tmpal;
         return 0;
     }
     return 1;
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 94e64b5..7da9754 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -115,6 +115,7 @@ __owur int tls_construct_client_hello(SSL *s, WPACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s, PACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s, PACKET *pkt);
+__owur int tls_process_cert_status_body(SSL *s, PACKET *pkt, int *al);
 __owur MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt);
 __owur int tls_construct_client_verify(SSL *s, WPACKET *pkt);
@@ -123,6 +124,7 @@ __owur int tls_construct_client_certificate(SSL *s, WPACKET *pkt);
 __owur int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey);
 __owur int tls_construct_client_key_exchange(SSL *s, WPACKET *pkt);
 __owur int tls_client_key_exchange_post_work(SSL *s);
+__owur int tls_construct_cert_status_body(SSL *s, WPACKET *pkt);
 __owur int tls_construct_cert_status(SSL *s, WPACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s, PACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt);
@@ -156,113 +158,159 @@ __owur int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt);
 __owur int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
                                   RAW_EXTENSION **res, int *al);
 __owur int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
-                               RAW_EXTENSION *exts, int *al);
+                               RAW_EXTENSION *exts,  X509 *x, size_t chainidx,
+                               int *al);
 __owur int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts,
-                                    int *al);
+                                    X509 *x, size_t chainidx, int *al);
 __owur int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
-                                    int *al);
+                                    X509 *x, size_t chainidx, int *al);
 
 /* Server Extension processing */
-int tls_parse_ctos_renegotiate(SSL *s, PACKET *pkt, int *al);
-int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_renegotiate(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al);
+int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al);
 #ifndef OPENSSL_NO_SRP
-int tls_parse_ctos_srp(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_srp(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
 #endif
 #ifndef OPENSSL_NO_EC
-int tls_parse_ctos_ec_pt_formats(SSL *s, PACKET *pkt, int *al);
-int tls_parse_ctos_supported_groups(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_ec_pt_formats(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al);
+int tls_parse_ctos_supported_groups(SSL *s, PACKET *pkt, X509 *x,
+                                    size_t chainidx, int *al);
 #endif
-int tls_parse_ctos_session_ticket(SSL *s, PACKET *pkt, int *al);
-int tls_parse_ctos_sig_algs(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_session_ticket(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al);
+int tls_parse_ctos_sig_algs(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                            int *al);
 #ifndef OPENSSL_NO_OCSP
-int tls_parse_ctos_status_request(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_status_request(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al);
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
-int tls_parse_ctos_npn(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_npn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
 #endif
-int tls_parse_ctos_alpn(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_alpn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
 #ifndef OPENSSL_NO_SRTP
-int tls_parse_ctos_use_srtp(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_use_srtp(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                            int *al);
 #endif
-int tls_parse_ctos_etm(SSL *s, PACKET *pkt, int *al);
-int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, int *al);
-int tls_parse_ctos_ems(SSL *s, PACKET *pkt, int *al);
+int tls_parse_ctos_etm(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
+int tls_parse_ctos_key_share(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                             int *al);
+int tls_parse_ctos_ems(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
 
-int tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, X509 *x,
+                                   size_t chainidx, int *al);
+int tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, X509 *x,
+                                   size_t chainidx, int *al);
 #ifndef OPENSSL_NO_EC
-int tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, X509 *x,
+                                     size_t chainidx, int *al);
 #endif
-int tls_construct_stoc_session_ticket(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_session_ticket(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al);
 #ifndef OPENSSL_NO_OCSP
-int tls_construct_stoc_status_request(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_status_request(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al);
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
-int tls_construct_stoc_next_proto_neg(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_next_proto_neg(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al);
 #endif
-int tls_construct_stoc_alpn(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_alpn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                            int *al);
 #ifndef OPENSSL_NO_SRTP
-int tls_construct_stoc_use_srtp(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_use_srtp(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                int *al);
 #endif
-int tls_construct_stoc_etm(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_stoc_ems(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_etm(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al);
+int tls_construct_stoc_ems(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al);
+int tls_construct_stoc_key_share(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al);
 /*
  * Not in public headers as this is not an official extension. Only used when
  * SSL_OP_CRYPTOPRO_TLSEXT_BUG is set.
  */
 #define TLSEXT_TYPE_cryptopro_bug      0xfde8
-int tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, X509 *x,
+                                     size_t chainidx, int *al);
 
 /* Client Extension processing */
-int tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, X509 *x,
+                                   size_t chainidx, int *al);
+int tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, X509 *x,
+                                   size_t chainidx, int *al);
 #ifndef OPENSSL_NO_SRP
-int tls_construct_ctos_srp(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_srp(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                   int *al);
 #endif
 #ifndef OPENSSL_NO_EC
-int tls_construct_ctos_ec_pt_formats(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_ec_pt_formats(SSL *s, WPACKET *pkt, X509 *x,
+                                     size_t chainidx, int *al);
+int tls_construct_ctos_supported_groups(SSL *s, WPACKET *pkt, X509 *x,
+                                        size_t chainidx, int *al);
 #endif
-int tls_construct_ctos_session_ticket(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_ctos_sig_algs(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_session_ticket(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al);
+int tls_construct_ctos_sig_algs(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                int *al);
 #ifndef OPENSSL_NO_OCSP
-int tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, X509 *x,
+                                      size_t chainidx, int *al);
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
-int tls_construct_ctos_npn(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_npn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al);
 #endif
-int tls_construct_ctos_alpn(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_alpn(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                            int *al);
 #ifndef OPENSSL_NO_SRTP
-int tls_construct_ctos_use_srtp(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_use_srtp(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                int *al);
 #endif
-int tls_construct_ctos_etm(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_etm(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al);
 #ifndef OPENSSL_NO_CT
-int tls_construct_ctos_sct(SSL *s, WPACKET *pkt, int *al);
+int tls_construct_ctos_sct(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al);
 #endif
-int tls_construct_ctos_ems(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, int *al);
-int tls_construct_ctos_padding(SSL *s, WPACKET *pkt, int *al);
-int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, int *al);
-int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, int *al);
+int tls_construct_ctos_ems(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                           int *al);
+int tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt, X509 *x,
+                                          size_t chainidx, int *al);
+int tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al);
+int tls_construct_ctos_padding(SSL *s, WPACKET *pkt, X509 *x, size_t chainidx,
+                               int *al);
+int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al);
+int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                               int *al);
 #ifndef OPENSSL_NO_EC
-int tls_parse_stoc_ec_pt_formats(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_ec_pt_formats(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                 int *al);
 #endif
-int tls_parse_stoc_session_ticket(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_session_ticket(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al);
 #ifndef OPENSSL_NO_OCSP
-int tls_parse_stoc_status_request(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_status_request(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                                  int *al);
 #endif
 #ifndef OPENSSL_NO_CT
-int tls_parse_stoc_sct(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_sct(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
 #endif
 #ifndef OPENSSL_NO_NEXTPROTONEG
-int tls_parse_stoc_npn(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_npn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
 #endif
-int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_alpn(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
 #ifndef OPENSSL_NO_SRTP
-int tls_parse_stoc_use_srtp(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_use_srtp(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                            int *al);
 #endif
-int tls_parse_stoc_etm(SSL *s, PACKET *pkt, int *al);
-int tls_parse_stoc_ems(SSL *s, PACKET *pkt, int *al);
-int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, int *al);
+int tls_parse_stoc_etm(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
+int tls_parse_stoc_ems(SSL *s, PACKET *pkt, X509 *x, size_t chainidx, int *al);
+int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, X509 *x, size_t chainidx,
+                             int *al);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 56f3998..b3d6419 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -427,11 +427,6 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_SW_CERT:
-            st->hand_state = s->tlsext_status_expected ? TLS_ST_SW_CERT_STATUS
-                                                       : TLS_ST_SW_FINISHED;
-        return WRITE_TRAN_CONTINUE;
-
-    case TLS_ST_SW_CERT_STATUS:
         st->hand_state = TLS_ST_SW_FINISHED;
         return WRITE_TRAN_CONTINUE;
 
@@ -1400,7 +1395,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
     /* We need to do this before getting the session */
     if (!tls_parse_extension(s, TLSEXT_IDX_extended_master_secret,
                              EXT_CLIENT_HELLO,
-                             clienthello.pre_proc_exts, &al)) {
+                             clienthello.pre_proc_exts, NULL, 0, &al)) {
         SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
         goto f_err;
     }
@@ -1504,7 +1499,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
 
     /* TLS extensions */
     if (!tls_parse_all_extensions(s, EXT_CLIENT_HELLO,
-                                  clienthello.pre_proc_exts, &al)) {
+                                  clienthello.pre_proc_exts, NULL, 0, &al)) {
         SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_PARSE_TLSEXT);
         goto f_err;
     }
@@ -1912,7 +1907,8 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt)
             || !tls_construct_extensions(s, pkt,
                                          SSL_IS_TLS13(s)
                                             ? EXT_TLS1_3_SERVER_HELLO
-                                            : EXT_TLS1_2_SERVER_HELLO, &al)) {
+                                            : EXT_TLS1_2_SERVER_HELLO,
+                                         NULL, 0, &al)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_SERVER_HELLO, ERR_R_INTERNAL_ERROR);
         goto err;
     }
@@ -3142,22 +3138,25 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
     unsigned long l, llen;
     const unsigned char *certstart, *certbytes;
     STACK_OF(X509) *sk = NULL;
-    PACKET spkt;
+    PACKET spkt, context;
+    size_t chainidx;
 
     if ((sk = sk_X509_new_null()) == NULL) {
         SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, ERR_R_MALLOC_FAILURE);
         goto f_err;
     }
 
-    if (!PACKET_get_net_3(pkt, &llen)
-        || !PACKET_get_sub_packet(pkt, &spkt, llen)
-        || PACKET_remaining(pkt) != 0) {
+    /* TODO(TLS1.3): For now we ignore the context. We need to verify this */
+    if ((SSL_IS_TLS13(s) && !PACKET_get_length_prefixed_1(pkt, &context))
+            || !PACKET_get_net_3(pkt, &llen)
+            || !PACKET_get_sub_packet(pkt, &spkt, llen)
+            || PACKET_remaining(pkt) != 0) {
         al = SSL_AD_DECODE_ERROR;
         SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, SSL_R_LENGTH_MISMATCH);
         goto f_err;
     }
 
-    while (PACKET_remaining(&spkt) > 0) {
+    for (chainidx = 0; PACKET_remaining(&spkt) > 0; chainidx++) {
         if (!PACKET_get_net_3(&spkt, &l)
             || !PACKET_get_bytes(&spkt, &certbytes, l)) {
             al = SSL_AD_DECODE_ERROR;
@@ -3178,6 +3177,23 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
                    SSL_R_CERT_LENGTH_MISMATCH);
             goto f_err;
         }
+
+        if (SSL_IS_TLS13(s)) {
+            RAW_EXTENSION *rawexts = NULL;
+            PACKET extensions;
+
+            if (!PACKET_get_length_prefixed_2(&spkt, &extensions)) {
+                al = SSL_AD_DECODE_ERROR;
+                SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, SSL_R_BAD_LENGTH);
+                goto f_err;
+            }
+            if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
+                                        &rawexts, &al)
+                    || !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
+                                                 rawexts, x, chainidx, &al))
+                goto f_err;
+        }
+
         if (!sk_X509_push(sk, x)) {
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, ERR_R_MALLOC_FAILURE);
             goto f_err;
@@ -3265,6 +3281,7 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
 int tls_construct_server_certificate(SSL *s, WPACKET *pkt)
 {
     CERT_PKEY *cpk;
+    int al = SSL_AD_INTERNAL_ERROR;
 
     cpk = ssl_get_server_send_pkey(s);
     if (cpk == NULL) {
@@ -3272,8 +3289,14 @@ int tls_construct_server_certificate(SSL *s, WPACKET *pkt)
         return 0;
     }
 
-    if (!ssl3_output_cert_chain(s, pkt, cpk)) {
+    /*
+     * In TLSv1.3 the certificate chain is always preceded by a 0 length context
+     * for the server Certificate message
+     */
+    if ((SSL_IS_TLS13(s) && !WPACKET_put_bytes_u8(pkt, 0))
+            || !ssl3_output_cert_chain(s, pkt, cpk, &al)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_SERVER_CERTIFICATE, ERR_R_INTERNAL_ERROR);
+        ssl3_send_alert(s, SSL3_AL_FATAL, al);
         return 0;
     }
 
@@ -3436,12 +3459,25 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
     return 0;
 }
 
-int tls_construct_cert_status(SSL *s, WPACKET *pkt)
+/*
+ * In TLSv1.3 this is called from the extensions code, otherwise it is used to
+ * create a separate message. Returns 1 on success or 0 on failure.
+ */
+int tls_construct_cert_status_body(SSL *s, WPACKET *pkt)
 {
     if (!WPACKET_put_bytes_u8(pkt, s->tlsext_status_type)
             || !WPACKET_sub_memcpy_u24(pkt, s->tlsext_ocsp_resp,
                                        s->tlsext_ocsp_resplen)) {
-        SSLerr(SSL_F_TLS_CONSTRUCT_CERT_STATUS, ERR_R_INTERNAL_ERROR);
+        SSLerr(SSL_F_TLS_CONSTRUCT_CERT_STATUS_BODY, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    return 1;
+}
+
+int tls_construct_cert_status(SSL *s, WPACKET *pkt)
+{
+    if (!tls_construct_cert_status_body(s, pkt)) {
         ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return 0;
     }
@@ -3491,13 +3527,8 @@ static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt)
 {
     int al;
 
-    /*
-     * TODO(TLS1.3): For now we send certificate extensions in with the
-     * encrypted extensions. Later we need to move these to the certificate
-     * message.
-     */
-    if (!tls_construct_extensions(s, pkt, EXT_TLS1_3_ENCRYPTED_EXTENSIONS
-                                          | EXT_TLS1_3_CERTIFICATE, &al)) {
+    if (!tls_construct_extensions(s, pkt, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+                                  NULL, 0, &al)) {
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
         SSLerr(SSL_F_TLS_CONSTRUCT_ENCRYPTED_EXTENSIONS, ERR_R_INTERNAL_ERROR);
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c
index 796759e..2b669e1 100644
--- a/ssl/t1_trce.c
+++ b/ssl/t1_trce.c
@@ -766,9 +766,11 @@ static int ssl_print_extension(BIO *bio, int indent, int server, int extype,
 }
 
 static int ssl_print_extensions(BIO *bio, int indent, int server,
-                                const unsigned char *msg, size_t msglen)
+                                const unsigned char **msgin, size_t *msginlen)
 {
-    size_t extslen;
+    size_t extslen, msglen = *msginlen;
+    const unsigned char *msg = *msgin;
+
     BIO_indent(bio, indent, 80);
     if (msglen == 0) {
         BIO_puts(bio, "No Extensions\n");
@@ -795,6 +797,9 @@ static int ssl_print_extensions(BIO *bio, int indent, int server,
         msg += extlen;
         msglen -= extlen + 4;
     }
+
+    *msgin = msg;
+    *msginlen = msglen;
     return 1;
 }
 
@@ -848,7 +853,7 @@ static int ssl_print_client_hello(BIO *bio, SSL *ssl, int indent,
         msglen--;
         len--;
     }
-    if (!ssl_print_extensions(bio, indent, 0, msg, msglen))
+    if (!ssl_print_extensions(bio, indent, 0, &msg, &msglen))
         return 0;
     return 1;
 }
@@ -893,7 +898,7 @@ static int ssl_print_server_hello(BIO *bio, int indent,
         msg++;
         msglen--;
     }
-    if (!ssl_print_extensions(bio, indent, 1, msg, msglen))
+    if (!ssl_print_extensions(bio, indent, 1, &msg, &msglen))
         return 0;
     return 1;
 }
@@ -1089,10 +1094,15 @@ static int ssl_print_certificate(BIO *bio, int indent,
     return 1;
 }
 
-static int ssl_print_certificates(BIO *bio, int indent,
+static int ssl_print_certificates(BIO *bio, SSL *s, int server, int indent,
                                   const unsigned char *msg, size_t msglen)
 {
     size_t clen;
+
+    if (SSL_IS_TLS13(s)
+            && !ssl_print_hexbuf(bio, indent, "context", 1, &msg, &msglen))
+        return 0;
+
     if (msglen < 3)
         return 0;
     clen = (msg[0] << 16) | (msg[1] << 8) | msg[2];
@@ -1104,6 +1114,9 @@ static int ssl_print_certificates(BIO *bio, int indent,
     while (clen > 0) {
         if (!ssl_print_certificate(bio, indent + 2, &msg, &clen))
             return 0;
+        if (!ssl_print_extensions(bio, indent + 2, server, &msg, &clen))
+            return 0;
+
     }
     return 1;
 }
@@ -1203,7 +1216,7 @@ static int ssl_print_ticket(BIO *bio, int indent,
     return 1;
 }
 
-static int ssl_print_handshake(BIO *bio, SSL *ssl,
+static int ssl_print_handshake(BIO *bio, SSL *ssl, int server,
                                const unsigned char *msg, size_t msglen,
                                int indent)
 {
@@ -1259,7 +1272,7 @@ static int ssl_print_handshake(BIO *bio, SSL *ssl,
         break;
 
     case SSL3_MT_CERTIFICATE:
-        if (!ssl_print_certificates(bio, indent + 2, msg, msglen))
+        if (!ssl_print_certificates(bio, ssl, server, indent + 2, msg, msglen))
             return 0;
         break;
 
@@ -1288,7 +1301,7 @@ static int ssl_print_handshake(BIO *bio, SSL *ssl,
         break;
 
     case SSL3_MT_ENCRYPTED_EXTENSIONS:
-        if (!ssl_print_extensions(bio, indent + 2, 1, msg, msglen))
+        if (!ssl_print_extensions(bio, indent + 2, 1, &msg, &msglen))
             return 0;
         break;
 
@@ -1338,7 +1351,8 @@ void SSL_trace(int write_p, int version, int content_type,
         }
         break;
     case SSL3_RT_HANDSHAKE:
-        if (!ssl_print_handshake(bio, ssl, msg, msglen, 4))
+        if (!ssl_print_handshake(bio, ssl, ssl->server ? write_p : !write_p,
+                                 msg, msglen, 4))
             BIO_printf(bio, "Message length parse error!\n");
         break;
 
diff --git a/test/recipes/70-test_tls13messages.t b/test/recipes/70-test_tls13messages.t
index 15dfa9f..8d42058 100755
--- a/test/recipes/70-test_tls13messages.t
+++ b/test/recipes/70-test_tls13messages.t
@@ -43,8 +43,6 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
         checkhandshake::CLIENT_AUTH_HANDSHAKE],
     [TLSProxy::Message::MT_CERTIFICATE,
         checkhandshake::ALL_HANDSHAKES & ~checkhandshake::RESUME_HANDSHAKE],
-    [TLSProxy::Message::MT_CERTIFICATE_STATUS,
-        checkhandshake::OCSP_HANDSHAKE],
     [TLSProxy::Message::MT_FINISHED,
         checkhandshake::ALL_HANDSHAKES],
     [TLSProxy::Message::MT_CERTIFICATE,
@@ -87,10 +85,12 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
 
     [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_SERVER_NAME,
         checkhandshake::SERVER_NAME_SRV_EXTENSION],
-    [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_STATUS_REQUEST,
-        checkhandshake::STATUS_REQUEST_SRV_EXTENSION],
     [TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_ALPN,
         checkhandshake::ALPN_SRV_EXTENSION],
+
+    [TLSProxy::Message::MT_CERTIFICATE, TLSProxy::Message::EXT_STATUS_REQUEST,
+        checkhandshake::STATUS_REQUEST_SRV_EXTENSION],
+
     [0,0,0]
 );
 
@@ -146,7 +146,7 @@ $proxy->clientflags("-status");
 $proxy->serverflags("-status_file "
                     .srctop_file("test", "recipes", "ocsp-response.der"));
 $proxy->start();
-checkhandshake($proxy, checkhandshake::OCSP_HANDSHAKE,
+checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
                | checkhandshake::STATUS_REQUEST_CLI_EXTENSION
                | checkhandshake::STATUS_REQUEST_SRV_EXTENSION,
@@ -230,7 +230,7 @@ $proxy->clientflags("-ct");
 $proxy->serverflags("-status_file "
                     .srctop_file("test", "recipes", "ocsp-response.der"));
 $proxy->start();
-checkhandshake($proxy, checkhandshake::OCSP_HANDSHAKE,
+checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
                | checkhandshake::SCT_CLI_EXTENSION
                | checkhandshake::STATUS_REQUEST_CLI_EXTENSION
diff --git a/test/testlib/checkhandshake.pm b/test/testlib/checkhandshake.pm
index 9529b94..0c3df6f 100644
--- a/test/testlib/checkhandshake.pm
+++ b/test/testlib/checkhandshake.pm
@@ -73,8 +73,14 @@ sub checkhandshake($$$$)
         if (($handtype & RENEG_HANDSHAKE) != 0) {
             $numtests += $#extensions + 2;
         }
-        #In TLS1.3 there are 3 messages with extensions (and no renegotiations)
-        $numtests += 1 if ($proxy->is_tls13());
+        #In TLS1.3 there are 4 messages with extensions (i.e. 2 extra) and no
+        #renegotiations: 1 ClientHello, 1 ServerHello, 1 EncryptedExtensions,
+        #1 Certificate
+        $numtests += 2 if ($proxy->is_tls13());
+        #Except in Client auth where we have an extra Certificate message, and
+        #one extension gets checked twice (once in each Certificate message)
+        $numtests += 2 if ($proxy->is_tls13()
+                          && ($handtype & CLIENT_AUTH_HANDSHAKE) != 0);
 
         plan tests => $numtests;
 
@@ -101,7 +107,11 @@ sub checkhandshake($$$$)
             next if ($message->mt() != TLSProxy::Message::MT_CLIENT_HELLO
                     && $message->mt() != TLSProxy::Message::MT_SERVER_HELLO
                     && $message->mt() !=
-                       TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS);
+                       TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS
+                    && $message->mt() != TLSProxy::Message::MT_CERTIFICATE);
+
+            next if $message->mt() == TLSProxy::Message::MT_CERTIFICATE
+                    && !TLSProxy::Proxy::is_tls13();
 
             if ($message->mt() == TLSProxy::Message::MT_CLIENT_HELLO) {
                 #Add renegotiate extension we will expect if renegotiating
diff --git a/util/TLSProxy/Certificate.pm b/util/TLSProxy/Certificate.pm
new file mode 100644
index 0000000..d3bf7f2
--- /dev/null
+++ b/util/TLSProxy/Certificate.pm
@@ -0,0 +1,219 @@
+# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+
+package TLSProxy::Certificate;
+
+use vars '@ISA';
+push @ISA, 'TLSProxy::Message';
+
+sub new
+{
+    my $class = shift;
+    my ($server,
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens) = @_;
+
+    my $self = $class->SUPER::new(
+        $server,
+        TLSProxy::Message::MT_CERTIFICATE,
+        $data,
+        $records,
+        $startoffset,
+        $message_frag_lens);
+
+    $self->{first_certificate} = "";
+    $self->{extension_data} = "";
+    $self->{remaining_certdata} = "";
+
+    return $self;
+}
+
+sub parse
+{
+    my $self = shift;
+
+    if (TLSProxy::Proxy->is_tls13()) {
+        my $context_len = unpack('C', $self->data);
+        my $context = substr($self->data, 1, $context_len);
+
+        my $remdata = substr($self->data, 1 + $context_len);
+
+        my ($hicertlistlen, $certlistlen) = unpack('Cn', $remdata);
+        $certlistlen += ($hicertlistlen << 16);
+
+        $remdata = substr($remdata, 3);
+
+        die "Invalid Certificate List length"
+            if length($remdata) != $certlistlen;
+
+        my ($hicertlen, $certlen) = unpack('Cn', $remdata);
+        $certlen += ($hicertlen << 16);
+
+        die "Certificate too long" if ($certlen + 3) > $certlistlen;
+
+        $remdata = substr($remdata, 3);
+
+        my $certdata = substr($remdata, 0, $certlen);
+
+        $remdata = substr($remdata, $certlen);
+
+        my $extensions_len = unpack('n', $remdata);
+        $remdata = substr($remdata, 2);
+
+        die "Extensions too long"
+            if ($certlen + 3 + $extensions_len + 2) > $certlistlen;
+
+        my $extension_data = "";
+        if ($extensions_len != 0) {
+            $extension_data = substr($remdata, 0, $extensions_len);
+
+            if (length($extension_data) != $extensions_len) {
+                die "Invalid extension length\n";
+            }
+        }
+        my %extensions = ();
+        while (length($extension_data) >= 4) {
+            my ($type, $size) = unpack("nn", $extension_data);
+            my $extdata = substr($extension_data, 4, $size);
+            $extension_data = substr($extension_data, 4 + $size);
+            $extensions{$type} = $extdata;
+        }
+        $remdata = substr($remdata, $extensions_len);
+
+        $self->context($context);
+        $self->first_certificate($certdata);
+        $self->extension_data(\%extensions);
+        $self->remaining_certdata($remdata);
+
+        print "    Context:".$context."\n";
+        print "    Certificate List Len:".$certlistlen."\n";
+        print "    Certificate Len:".$certlen."\n";
+        print "    Extensions Len:".$extensions_len."\n";
+    } else {
+        my ($hicertlistlen, $certlistlen) = unpack('Cn', $self->data);
+        $certlistlen += ($hicertlistlen << 16);
+
+        my $remdata = substr($self->data, 3);
+
+        die "Invalid Certificate List length"
+            if length($remdata) != $certlistlen;
+
+        my ($hicertlen, $certlen) = unpack('Cn', $remdata);
+        $certlen += ($hicertlen << 16);
+
+        die "Certificate too long" if ($certlen + 3) > $certlistlen;
+
+        $remdata = substr($remdata, 3);
+
+        my $certdata = substr($remdata, 0, $certlen);
+
+        $remdata = substr($remdata, $certlen);
+
+        $self->first_certificate($certdata);
+        $self->remaining_certdata($remdata);
+
+        print "    Certificate List Len:".$certlistlen."\n";
+        print "    Certificate Len:".$certlen."\n";
+    }
+}
+
+#Reconstruct the on-the-wire message data following changes
+sub set_message_contents
+{
+    my $self = shift;
+    my $data;
+    my $extensions = "";
+
+    if (TLSProxy::Proxy->is_tls13()) {
+        foreach my $key (keys %{$self->extension_data}) {
+            my $extdata = ${$self->extension_data}{$key};
+            $extensions .= pack("n", $key);
+            $extensions .= pack("n", length($extdata));
+            $extensions .= $extdata;
+            if ($key == TLSProxy::Message::EXT_DUPLICATE_EXTENSION) {
+              $extensions .= pack("n", $key);
+              $extensions .= pack("n", length($extdata));
+              $extensions .= $extdata;
+            }
+        }
+        $data = pack('C', length($self->context()));
+        $data .= $self->context;
+        my $certlen = length($self->first_certificate);
+        my $certlistlen = $certlen + length($extensions)
+                          + length($self->remaining_certdata);
+        my $hi = $certlistlen >> 16;
+        $certlistlen = $certlistlen & 0xffff;
+        $data .= pack('Cn', $hi, $certlistlen);
+        $hi = $certlen >> 16;
+        $certlen = $certlen & 0xffff;
+        $data .= pack('Cn', $hi, $certlen);
+        $data .= pack('n', length($extensions));
+        $data .= $extensions;
+        $data .= $self->remaining_certdata();
+        $self->data($data);
+    } else {
+        my $certlen = length($self->first_certificate);
+        my $certlistlen = $certlen + length($self->remaining_certdata);
+        my $hi = $certlistlen >> 16;
+        $certlistlen = $certlistlen & 0xffff;
+        $data .= pack('Cn', $hi, $certlistlen);
+        $hi = $certlen >> 16;
+        $certlen = $certlen & 0xffff;
+        $data .= pack('Cn', $hi, $certlen);
+        $data .= $self->remaining_certdata();
+        $self->data($data);
+    }
+}
+
+#Read/write accessors
+sub context
+{
+    my $self = shift;
+    if (@_) {
+      $self->{context} = shift;
+    }
+    return $self->{context};
+}
+sub first_certificate
+{
+    my $self = shift;
+    if (@_) {
+      $self->{first_certificate} = shift;
+    }
+    return $self->{first_certificate};
+}
+sub remaining_certdata
+{
+    my $self = shift;
+    if (@_) {
+      $self->{remaining_certdata} = shift;
+    }
+    return $self->{remaining_certdata};
+}
+sub extension_data
+{
+    my $self = shift;
+    if (@_) {
+      $self->{extension_data} = shift;
+    }
+    return $self->{extension_data};
+}
+sub set_extension
+{
+    my ($self, $ext_type, $ext_data) = @_;
+    $self->{extension_data}{$ext_type} = $ext_data;
+}
+sub delete_extension
+{
+    my ($self, $ext_type) = @_;
+    delete $self->{extension_data}{$ext_type};
+}
+1;
diff --git a/util/TLSProxy/Message.pm b/util/TLSProxy/Message.pm
index 7837787..704fe04 100644
--- a/util/TLSProxy/Message.pm
+++ b/util/TLSProxy/Message.pm
@@ -268,6 +268,15 @@ sub create_message
             [@message_frag_lens]
         );
         $message->parse();
+    } elsif ($mt == MT_CERTIFICATE) {
+        $message = TLSProxy::Certificate->new(
+            $server,
+            $data,
+            [@message_rec_list],
+            $startoffset,
+            [@message_frag_lens]
+        );
+        $message->parse();
     } elsif ($mt == MT_SERVER_KEY_EXCHANGE) {
         $message = TLSProxy::ServerKeyExchange->new(
             $server,
diff --git a/util/TLSProxy/Proxy.pm b/util/TLSProxy/Proxy.pm
index 84ca3a7..067e9be 100644
--- a/util/TLSProxy/Proxy.pm
+++ b/util/TLSProxy/Proxy.pm
@@ -18,6 +18,7 @@ use TLSProxy::Message;
 use TLSProxy::ClientHello;
 use TLSProxy::ServerHello;
 use TLSProxy::EncryptedExtensions;
+use TLSProxy::Certificate;
 use TLSProxy::ServerKeyExchange;
 use TLSProxy::NewSessionTicket;
 


More information about the openssl-commits mailing list