[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Wed Nov 23 15:43:01 UTC 2016


The branch master has been updated
       via  6530c4909ffbf4fd655416cbd765b1e7174b9b83 (commit)
       via  f5ca0b04bbc98b5b8a41f5cd7b4ee35e345c1e6c (commit)
       via  c805f6189e7384d8f27e82c09ee8cae202ade876 (commit)
       via  cc24a22b83d8cc210b9c279f185b79f0875817c1 (commit)
       via  acf65ae5c852c8d05b5d3af263f29dd5115f556b (commit)
       via  c11237c23e9f60cecdb899580b7b9ffb88614a7e (commit)
       via  20b65c7bdd9ca34c497624d1d07edd433be88a83 (commit)
       via  5abeaf3596210d8cc0be1edf7a0a772b7e2c7e6f (commit)
       via  7776a36cfa5853175a858fa32983f22f36513171 (commit)
       via  9970290e1d984bf8cc1dce7093bca915062cfdd7 (commit)
       via  6484776f177b38dd668618a75bee58674ca42578 (commit)
       via  92760c21e62c6e5ef172fa110cf47a509cd50f2f (commit)
       via  0d9824c1712b6cacd9b0ecfba26fb66ae4badfb4 (commit)
       via  9362c93ebc5b14bf18e82cdebf380ccc52f3d92f (commit)
      from  82c9c030173898b9536a1c8da4e49b4b19251dbd (commit)


- Log -----------------------------------------------------------------
commit 6530c4909ffbf4fd655416cbd765b1e7174b9b83
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Nov 23 15:38:32 2016 +0000

    Fix some style issues with TLSv1.3 state machine PR
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit f5ca0b04bbc98b5b8a41f5cd7b4ee35e345c1e6c
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Nov 21 12:10:35 2016 +0000

    Fix some style issues identified during review
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit c805f6189e7384d8f27e82c09ee8cae202ade876
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Nov 21 13:24:50 2016 +0000

    Fix SSL_IS_TLS13(s)
    
    The SSL_IS_TLS13() macro wasn't quite right. It would come back with true
    in the case where we haven't yet negotiated TLSv1.3, but it could be
    negotiated.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit cc24a22b83d8cc210b9c279f185b79f0875817c1
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 15 14:53:37 2016 +0000

    Extend test_tls13messages
    
    Add various different handshake types that are possible.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit acf65ae5c852c8d05b5d3af263f29dd5115f556b
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 15 14:22:29 2016 +0000

    Add an s_server capability to read an OCSP Response from a file
    
    Current s_server can only get an OCSP Response from an OCSP responder. This
    provides the capability to instead get the OCSP Response from a DER encoded
    file.
    
    This should make testing of OCSP easier.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit c11237c23e9f60cecdb899580b7b9ffb88614a7e
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 15 13:26:12 2016 +0000

    Add a test for the TLSv1.3 state machine
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 20b65c7bdd9ca34c497624d1d07edd433be88a83
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 15 11:09:25 2016 +0000

    Fix some TLSProxy warnings
    
    After the client processes the server's initial flight in TLS1.3 it may
    respond with either an encrypted, or an unencrypted alert. We needed to
    teach TLSProxy about this so that it didn't issue spurious warnings.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 5abeaf3596210d8cc0be1edf7a0a772b7e2c7e6f
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 15 10:30:34 2016 +0000

    Ensure unexpected messages are handled consistently
    
    In one case we weren't always sending an unexpected message alert if we
    don't get what we expect.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 7776a36cfa5853175a858fa32983f22f36513171
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 15 10:13:09 2016 +0000

    Ensure the end of first server flight processing is done
    
    There is a set of miscellaneous processing for OCSP, CT etc at the end of
    the ServerDone processing. In TLS1.3 we don't have a ServerDone, so this
    needs to move elsewhere.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 9970290e1d984bf8cc1dce7093bca915062cfdd7
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Nov 11 16:22:19 2016 +0000

    Fix the tests following the state machine changes for TLSv1.3
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 6484776f177b38dd668618a75bee58674ca42578
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Nov 11 00:20:19 2016 +0000

    Create the Finished message payload
    
    The previous commit had a dummy payload for the Finished data. This commit
    fills it in with a real value.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 92760c21e62c6e5ef172fa110cf47a509cd50f2f
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Nov 9 14:06:12 2016 +0000

    Update state machine to be closer to TLS1.3
    
    This is a major overhaul of the TLSv1.3 state machine. Currently it still
    looks like TLSv1.2. This commit changes things around so that it starts
    to look a bit less like TLSv1.2 and bit more like TLSv1.3.
    
    After this commit we have:
    
    ClientHello
    + key_share          ---->
                               ServerHello
                               +key_share
                               {CertificateRequest*}
                               {Certificate*}
                               {CertificateStatus*}
                         <---- {Finished}
    {Certificate*}
    {CertificateVerify*}
    {Finished}           ---->
    [ApplicationData]    <---> [Application Data]
    
    Key differences between this intermediate position and the final TLSv1.3
    position are:
    - No EncryptedExtensions message yet
    - No server side CertificateVerify message yet
    - CertificateStatus still exists as a separate message
    - A number of the messages are still in the TLSv1.2 format
    - Still running on the TLSv1.2 record layer
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 0d9824c1712b6cacd9b0ecfba26fb66ae4badfb4
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 8 23:20:31 2016 +0000

    Implement tls13_change_cipher_state()
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

commit 9362c93ebc5b14bf18e82cdebf380ccc52f3d92f
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 8 16:10:21 2016 +0000

    Remove old style NewSessionTicket from TLSv1.3
    
    TLSv1.3 has a NewSessionTicket message, but it is *completely* different to
    the TLSv1.2 one and may as well have been called something else. This commit
    removes the old style NewSessionTicket from TLSv1.3. We will have to add the
    new style one back in later.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>

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

Summary of changes:
 apps/s_server.c                       | 119 +++++++++++----
 doc/man1/s_server.pod                 |   6 +
 include/openssl/ssl.h                 |   4 +
 include/openssl/ssl3.h                |   2 +
 ssl/s3_lib.c                          |  21 ++-
 ssl/ssl_err.c                         |   5 +
 ssl/ssl_lib.c                         |   3 +-
 ssl/ssl_locl.h                        |  10 +-
 ssl/statem/statem_clnt.c              | 195 ++++++++++++------------
 ssl/statem/statem_lib.c               |  32 +++-
 ssl/statem/statem_locl.h              |   1 +
 ssl/statem/statem_srvr.c              | 104 ++++++-------
 ssl/t1_lib.c                          |  44 +++---
 ssl/tls13_enc.c                       | 275 ++++++++++++++++++++++++++++++----
 test/asynciotest.c                    |  24 ++-
 test/clienthellotest.c                |  13 ++
 test/recipes/70-test_sslrecords.t     |   1 +
 test/recipes/70-test_sslsessiontick.t |  25 ++--
 test/recipes/70-test_tls13messages.t  | 123 +++++++++++++++
 test/recipes/ocsp-response.der        | Bin 0 -> 1517 bytes
 test/ssl-tests/06-sni-ticket.conf     |  17 +++
 test/ssl-tests/06-sni-ticket.conf.in  |  33 ++--
 test/ssltestlib.c                     |   2 +-
 test/tls13secretstest.c               |  16 ++
 util/TLSProxy/Message.pm              |   4 +-
 util/TLSProxy/Proxy.pm                |  12 +-
 util/TLSProxy/Record.pm               |  41 +++--
 util/TLSProxy/ServerHello.pm          |   7 +
 28 files changed, 842 insertions(+), 297 deletions(-)
 create mode 100755 test/recipes/70-test_tls13messages.t
 create mode 100644 test/recipes/ocsp-response.der

diff --git a/apps/s_server.c b/apps/s_server.c
index ee66318..f349412 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -451,45 +451,41 @@ static int ssl_servername_cb(SSL *s, int *ad, void *arg)
 /* Structure passed to cert status callback */
 
 typedef struct tlsextstatusctx_st {
+    int timeout;
+    /* File to load OCSP Response from (or NULL if no file) */
+    char *respin;
     /* Default responder to use */
     char *host, *path, *port;
     int use_ssl;
-    int timeout;
     int verbose;
 } tlsextstatusctx;
 
-static tlsextstatusctx tlscstatp = { NULL, NULL, NULL, 0, -1, 0 };
+static tlsextstatusctx tlscstatp = { -1 };
 
 #ifndef OPENSSL_NO_OCSP
+
 /*
- * Certificate Status callback. This is called when a client includes a
- * certificate status request extension. This is a simplified version. It
- * examines certificates each time and makes one OCSP responder query for
- * each request. A full version would store details such as the OCSP
- * certificate IDs and minimise the number of OCSP responses by caching them
- * until they were considered "expired".
+ * Helper function to get an OCSP_RESPONSE from a responder. This is a
+ * simplified version. It examines certificates each time and makes one OCSP
+ * responder query for each request. A full version would store details such as
+ * the OCSP certificate IDs and minimise the number of OCSP responses by caching
+ * them until they were considered "expired".
  */
-
-static int cert_status_cb(SSL *s, void *arg)
+static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx,
+                                        OCSP_RESPONSE **resp)
 {
-    tlsextstatusctx *srctx = arg;
     char *host = NULL, *port = NULL, *path = NULL;
     int use_ssl;
-    unsigned char *rspder = NULL;
-    int rspderlen;
     STACK_OF(OPENSSL_STRING) *aia = NULL;
     X509 *x = NULL;
     X509_STORE_CTX *inctx = NULL;
     X509_OBJECT *obj;
     OCSP_REQUEST *req = NULL;
-    OCSP_RESPONSE *resp = NULL;
     OCSP_CERTID *id = NULL;
     STACK_OF(X509_EXTENSION) *exts;
     int ret = SSL_TLSEXT_ERR_NOACK;
     int i;
 
-    if (srctx->verbose)
-        BIO_puts(bio_err, "cert_status: callback called\n");
     /* Build up OCSP query from server certificate */
     x = SSL_get_certificate(s);
     aia = X509_get1_ocsp(x);
@@ -544,29 +540,24 @@ static int cert_status_cb(SSL *s, void *arg)
         if (!OCSP_REQUEST_add_ext(req, ext, -1))
             goto err;
     }
-    resp = process_responder(req, host, path, port, use_ssl, NULL,
+    *resp = process_responder(req, host, path, port, use_ssl, NULL,
                              srctx->timeout);
-    if (!resp) {
+    if (*resp == NULL) {
         BIO_puts(bio_err, "cert_status: error querying responder\n");
         goto done;
     }
-    rspderlen = i2d_OCSP_RESPONSE(resp, &rspder);
-    if (rspderlen <= 0)
-        goto err;
-    SSL_set_tlsext_status_ocsp_resp(s, rspder, rspderlen);
-    if (srctx->verbose) {
-        BIO_puts(bio_err, "cert_status: ocsp response sent:\n");
-        OCSP_RESPONSE_print(bio_err, resp, 2);
-    }
+
     ret = SSL_TLSEXT_ERR_OK;
     goto done;
 
  err:
     ret = SSL_TLSEXT_ERR_ALERT_FATAL;
  done:
-    if (ret != SSL_TLSEXT_ERR_OK)
-        ERR_print_errors(bio_err);
-    if (aia) {
+    /*
+     * If we parsed aia we need to free; otherwise they were copied and we
+     * don't
+     */
+    if (aia != NULL) {
         OPENSSL_free(host);
         OPENSSL_free(path);
         OPENSSL_free(port);
@@ -574,10 +565,64 @@ static int cert_status_cb(SSL *s, void *arg)
     }
     OCSP_CERTID_free(id);
     OCSP_REQUEST_free(req);
-    OCSP_RESPONSE_free(resp);
     X509_STORE_CTX_free(inctx);
     return ret;
 }
+
+/*
+ * Certificate Status callback. This is called when a client includes a
+ * certificate status request extension. The response is either obtained from a
+ * file, or from an OCSP responder.
+ */
+static int cert_status_cb(SSL *s, void *arg)
+{
+    tlsextstatusctx *srctx = arg;
+    OCSP_RESPONSE *resp = NULL;
+    unsigned char *rspder = NULL;
+    int rspderlen;
+    int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+
+    if (srctx->verbose)
+        BIO_puts(bio_err, "cert_status: callback called\n");
+
+    if (srctx->respin != NULL) {
+        BIO *derbio = bio_open_default(srctx->respin, 'r', FORMAT_ASN1);
+        if (derbio == NULL) {
+            BIO_puts(bio_err, "cert_status: Cannot open OCSP response file\n");
+            goto err;
+        }
+        resp = d2i_OCSP_RESPONSE_bio(derbio, NULL);
+        BIO_free(derbio);
+        if (resp == NULL) {
+            BIO_puts(bio_err, "cert_status: Error reading OCSP response\n");
+            goto err;
+        }
+    } else {
+        ret = get_ocsp_resp_from_responder(s, srctx, &resp);
+        if (ret != SSL_TLSEXT_ERR_OK)
+            goto err;
+    }
+
+    rspderlen = i2d_OCSP_RESPONSE(resp, &rspder);
+    if (rspderlen <= 0)
+        goto err;
+
+    SSL_set_tlsext_status_ocsp_resp(s, rspder, rspderlen);
+    if (srctx->verbose) {
+        BIO_puts(bio_err, "cert_status: ocsp response sent:\n");
+        OCSP_RESPONSE_print(bio_err, resp, 2);
+    }
+
+    ret = SSL_TLSEXT_ERR_OK;
+
+ err:
+    if (ret != SSL_TLSEXT_ERR_OK)
+        ERR_print_errors(bio_err);
+
+    OCSP_RESPONSE_free(resp);
+
+    return ret;
+}
 #endif
 
 #ifndef OPENSSL_NO_NEXTPROTONEG
@@ -663,9 +708,9 @@ typedef enum OPTION_choice {
     OPT_BUILD_CHAIN, OPT_CAFILE, OPT_NOCAFILE, OPT_CHAINCAFILE,
     OPT_VERIFYCAFILE, OPT_NBIO, OPT_NBIO_TEST, OPT_IGN_EOF, OPT_NO_IGN_EOF,
     OPT_DEBUG, OPT_TLSEXTDEBUG, OPT_STATUS, OPT_STATUS_VERBOSE,
-    OPT_STATUS_TIMEOUT, OPT_STATUS_URL, OPT_MSG, OPT_MSGFILE, OPT_TRACE,
-    OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_STATE, OPT_CRLF,
-    OPT_QUIET, OPT_BRIEF, OPT_NO_DHE,
+    OPT_STATUS_TIMEOUT, OPT_STATUS_URL, OPT_STATUS_FILE, OPT_MSG, OPT_MSGFILE,
+    OPT_TRACE, OPT_SECURITY_DEBUG, OPT_SECURITY_DEBUG_VERBOSE, OPT_STATE,
+    OPT_CRLF, OPT_QUIET, OPT_BRIEF, OPT_NO_DHE,
     OPT_NO_RESUME_EPHEMERAL, OPT_PSK_HINT, OPT_PSK, OPT_SRPVFILE,
     OPT_SRPUSERSEED, OPT_REV, OPT_WWW, OPT_UPPER_WWW, OPT_HTTP, OPT_ASYNC,
     OPT_SSL_CONFIG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF,
@@ -788,6 +833,8 @@ const OPTIONS s_server_options[] = {
     {"status_timeout", OPT_STATUS_TIMEOUT, 'n',
      "Status request responder timeout"},
     {"status_url", OPT_STATUS_URL, 's', "Status request fallback URL"},
+    {"status_file", OPT_STATUS_FILE, '<',
+     "File containing DER encoded OCSP Response"},
 #endif
 #ifndef OPENSSL_NO_SSL_TRACE
     {"trace", OPT_TRACE, '-', "trace protocol messages"},
@@ -1239,6 +1286,12 @@ int s_server_main(int argc, char *argv[])
             }
 #endif
             break;
+        case OPT_STATUS_FILE:
+#ifndef OPENSSL_NO_OCSP
+            s_tlsextstatus = 1;
+            tlscstatp.respin = opt_arg();
+#endif
+            break;
         case OPT_MSG:
             s_msg = 1;
             break;
diff --git a/doc/man1/s_server.pod b/doc/man1/s_server.pod
index 94065ba..f1a13dc 100644
--- a/doc/man1/s_server.pod
+++ b/doc/man1/s_server.pod
@@ -109,6 +109,7 @@ B<openssl> B<s_server>
 [B<-status_verbose>]
 [B<-status_timeout nsec>]
 [B<-status_url url>]
+[B<-status_file file>]
 [B<-alpn protocols>]
 [B<-nextprotoneg protocols>]
 
@@ -501,6 +502,11 @@ Sets a fallback responder URL to use if no responder URL is present in the
 server certificate. Without this option an error is returned if the server
 certificate does not contain a responder address.
 
+=item B<-status_file file>
+
+Overrides any OCSP responder URLs from the certificate and always provides the
+OCSP Response stored in the file. The file must be in DER format.
+
 =item B<-alpn protocols>, B<-nextprotoneg protocols>
 
 these flags enable the 
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 1f9aaf8..8769f46 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -2242,6 +2242,8 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_SSL_WRITE_EX                               433
 # define SSL_F_STATE_MACHINE                              353
 # define SSL_F_TLS12_CHECK_PEER_SIGALG                    333
+# define SSL_F_TLS13_CHANGE_CIPHER_STATE                  440
+# define SSL_F_TLS13_SETUP_KEY_BLOCK                      441
 # define SSL_F_TLS1_CHANGE_CIPHER_STATE                   209
 # define SSL_F_TLS1_CHECK_DUPLICATE_EXTENSIONS            341
 # define SSL_F_TLS1_ENC                                   401
@@ -2292,6 +2294,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_PROCESS_CLIENT_HELLO                   381
 # define SSL_F_TLS_PROCESS_CLIENT_KEY_EXCHANGE            382
 # define SSL_F_TLS_PROCESS_FINISHED                       364
+# define SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT          442
 # define SSL_F_TLS_PROCESS_KEY_EXCHANGE                   365
 # define SSL_F_TLS_PROCESS_NEW_SESSION_TICKET             366
 # define SSL_F_TLS_PROCESS_NEXT_PROTO                     383
@@ -2335,6 +2338,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_BIO_NOT_SET                                128
 # define SSL_R_BLOCK_CIPHER_PAD_IS_WRONG                  129
 # define SSL_R_BN_LIB                                     130
+# define SSL_R_CANNOT_CHANGE_CIPHER                       109
 # define SSL_R_CA_DN_LENGTH_MISMATCH                      131
 # define SSL_R_CA_KEY_TOO_SMALL                           397
 # define SSL_R_CA_MD_TOO_WEAK                             398
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index aca1922..321a8dd 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -296,6 +296,8 @@ extern "C" {
 # define SSL3_CC_WRITE           0x02
 # define SSL3_CC_CLIENT          0x10
 # define SSL3_CC_SERVER          0x20
+# define SSL3_CC_HANDSHAKE       0x40
+# define SSL3_CC_APPLICATION     0x80
 # define SSL3_CHANGE_CIPHER_CLIENT_WRITE (SSL3_CC_CLIENT|SSL3_CC_WRITE)
 # define SSL3_CHANGE_CIPHER_SERVER_READ  (SSL3_CC_SERVER|SSL3_CC_READ)
 # define SSL3_CHANGE_CIPHER_CLIENT_READ  (SSL3_CC_CLIENT|SSL3_CC_READ)
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index bcc0f9e..524f530 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -4067,8 +4067,8 @@ EVP_PKEY *ssl_generate_pkey_curve(int id)
 }
 #endif
 
-/* Derive premaster or master secret for ECDH/DH */
-int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int genmaster)
+/* Derive secrets for ECDH/DH */
+int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int gensecret)
 {
     int rv = 0;
     unsigned char *pms = NULL;
@@ -4093,9 +4093,20 @@ int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int genmaster)
     if (EVP_PKEY_derive(pctx, pms, &pmslen) <= 0)
         goto err;
 
-    if (genmaster) {
-        /* Generate master secret and discard premaster */
-        rv = ssl_generate_master_secret(s, pms, pmslen, 1);
+    if (gensecret) {
+        if (SSL_IS_TLS13(s)) {
+            /*
+             * TODO(TLS1.3): For now we just use the default early_secret, this
+             * will need to change later when other early_secrets will be
+             * possible.
+             */
+            rv = tls13_generate_early_secret(s, NULL, 0)
+                 && tls13_generate_handshake_secret(s, pms, pmslen);
+            OPENSSL_free(pms);
+        } else {
+            /* Generate master secret and discard premaster */
+            rv = ssl_generate_master_secret(s, pms, pmslen, 1);
+        }
         pms = NULL;
     } else {
         /* Save premaster secret */
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 235a53c..49a9d44 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -238,6 +238,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_SSL_WRITE_EX), "SSL_write_ex"},
     {ERR_FUNC(SSL_F_STATE_MACHINE), "state_machine"},
     {ERR_FUNC(SSL_F_TLS12_CHECK_PEER_SIGALG), "tls12_check_peer_sigalg"},
+    {ERR_FUNC(SSL_F_TLS13_CHANGE_CIPHER_STATE), "tls13_change_cipher_state"},
+    {ERR_FUNC(SSL_F_TLS13_SETUP_KEY_BLOCK), "tls13_setup_key_block"},
     {ERR_FUNC(SSL_F_TLS1_CHANGE_CIPHER_STATE), "tls1_change_cipher_state"},
     {ERR_FUNC(SSL_F_TLS1_CHECK_DUPLICATE_EXTENSIONS),
      "tls1_check_duplicate_extensions"},
@@ -311,6 +313,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_TLS_PROCESS_CLIENT_KEY_EXCHANGE),
      "tls_process_client_key_exchange"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_FINISHED), "tls_process_finished"},
+    {ERR_FUNC(SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT),
+     "tls_process_initial_server_flight"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_KEY_EXCHANGE), "tls_process_key_exchange"},
     {ERR_FUNC(SSL_F_TLS_PROCESS_NEW_SESSION_TICKET),
      "tls_process_new_session_ticket"},
@@ -367,6 +371,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_REASON(SSL_R_BLOCK_CIPHER_PAD_IS_WRONG),
      "block cipher pad is wrong"},
     {ERR_REASON(SSL_R_BN_LIB), "bn lib"},
+    {ERR_REASON(SSL_R_CANNOT_CHANGE_CIPHER), "cannot change cipher"},
     {ERR_REASON(SSL_R_CA_DN_LENGTH_MISMATCH), "ca dn length mismatch"},
     {ERR_REASON(SSL_R_CA_KEY_TOO_SMALL), "ca key too small"},
     {ERR_REASON(SSL_R_CA_MD_TOO_WEAK), "ca md too weak"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 5f2c941..4d41b17 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3828,8 +3828,7 @@ EVP_MD_CTX *ssl_replace_hash(EVP_MD_CTX **hash, const EVP_MD *md)
 void ssl_clear_hash_ctx(EVP_MD_CTX **hash)
 {
 
-    if (*hash)
-        EVP_MD_CTX_free(*hash);
+    EVP_MD_CTX_free(*hash);
     *hash = NULL;
 }
 
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 41382ba..d269595 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -352,7 +352,9 @@
 # define SSL_IS_DTLS(s)  (s->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_DTLS)
 
 /* Check if we are using TLSv1.3 */
-# define SSL_IS_TLS13(s) (!SSL_IS_DTLS(s) && (s)->version >= TLS1_3_VERSION)
+# define SSL_IS_TLS13(s) (!SSL_IS_DTLS(s) \
+                          && (s)->method->version >= TLS1_3_VERSION \
+                          && (s)->method->version != TLS_ANY_VERSION)
 
 /* See if we need explicit IV */
 # define SSL_USE_EXPLICIT_IV(s)  \
@@ -955,6 +957,8 @@ struct ssl_st {
      */
     unsigned char early_secret[EVP_MAX_MD_SIZE];
     unsigned char handshake_secret[EVP_MAX_MD_SIZE];
+    unsigned char client_finished_secret[EVP_MAX_MD_SIZE];
+    unsigned char server_finished_secret[EVP_MAX_MD_SIZE];
     EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */
     EVP_MD_CTX *read_hash;      /* used for mac generation */
     COMP_CTX *compress;         /* compression */
@@ -2003,6 +2007,10 @@ __owur size_t tls1_final_finish_mac(SSL *s, const char *str, size_t slen,
 __owur int tls1_generate_master_secret(SSL *s, unsigned char *out,
                                        unsigned char *p, size_t len,
                                        size_t *secret_size);
+__owur int tls13_setup_key_block(SSL *s);
+__owur size_t tls13_final_finish_mac(SSL *s, const char *str, size_t slen,
+                                     unsigned char *p);
+__owur int tls13_change_cipher_state(SSL *s, int which);
 __owur int tls13_derive_secret(SSL *s, const unsigned char *insecret,
                                const unsigned char *label, size_t labellen,
                                unsigned char *secret);
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index f89d317..1f4e630 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -136,16 +136,15 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt)
 
     case TLS_ST_CR_SRVR_HELLO:
         if (s->hit) {
-            if (s->tlsext_ticket_expected) {
-                if (mt == SSL3_MT_NEWSESSION_TICKET) {
-                    st->hand_state = TLS_ST_CR_SESSION_TICKET;
-                    return 1;
-                }
-            } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-                st->hand_state = TLS_ST_CR_CHANGE;
+            if (mt == SSL3_MT_FINISHED) {
+                st->hand_state = TLS_ST_CR_FINISHED;
                 return 1;
             }
         } else {
+            if (mt == SSL3_MT_CERTIFICATE_REQUEST) {
+                st->hand_state = TLS_ST_CR_CERT_REQ;
+                return 1;
+            }
             if (mt == SSL3_MT_CERTIFICATE) {
                 st->hand_state = TLS_ST_CR_CERT;
                 return 1;
@@ -153,6 +152,13 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt)
         }
         break;
 
+    case TLS_ST_CR_CERT_REQ:
+        if (mt == SSL3_MT_CERTIFICATE) {
+            st->hand_state = TLS_ST_CR_CERT;
+            return 1;
+        }
+        break;
+
     case TLS_ST_CR_CERT:
         /*
          * The CertificateStatus message is optional even if
@@ -165,54 +171,15 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt)
         /* Fall through */
 
     case TLS_ST_CR_CERT_STATUS:
-        if (mt == SSL3_MT_CERTIFICATE_REQUEST) {
-            if (cert_req_allowed(s)) {
-                st->hand_state = TLS_ST_CR_CERT_REQ;
-                return 1;
-            }
-            goto err;
-        }
-        /* Fall through */
-
-    case TLS_ST_CR_CERT_REQ:
-        if (mt == SSL3_MT_SERVER_DONE) {
-            st->hand_state = TLS_ST_CR_SRVR_DONE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_CW_FINISHED:
-        if (s->tlsext_ticket_expected) {
-            if (mt == SSL3_MT_NEWSESSION_TICKET) {
-                st->hand_state = TLS_ST_CR_SESSION_TICKET;
-                return 1;
-            }
-        } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_CR_CHANGE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_CR_SESSION_TICKET:
-        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_CR_CHANGE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_CR_CHANGE:
         if (mt == SSL3_MT_FINISHED) {
             st->hand_state = TLS_ST_CR_FINISHED;
             return 1;
         }
         break;
+
     }
 
- err:
     /* No valid transition found */
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_UNEXPECTED_MESSAGE);
-    SSLerr(SSL_F_OSSL_STATEM_CLIENT13_READ_TRANSITION,
-           SSL_R_UNEXPECTED_MESSAGE);
     return 0;
 }
 
@@ -234,8 +201,11 @@ int ossl_statem_client_read_transition(SSL *s, int mt)
      * Note that after a ClientHello we don't know what version we are going
      * to negotiate yet, so we don't take this branch until later
      */
-    if (s->method->version == TLS1_3_VERSION)
-        return ossl_statem_client13_read_transition(s, mt);
+    if (SSL_IS_TLS13(s)) {
+        if (!ossl_statem_client13_read_transition(s, mt))
+            goto err;
+        return 1;
+    }
 
     switch (st->hand_state) {
     default:
@@ -410,38 +380,22 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
         /* Shouldn't happen */
         return WRITE_TRAN_ERROR;
 
-    case TLS_ST_CR_SRVR_DONE:
+    case TLS_ST_CR_FINISHED:
         st->hand_state = (s->s3->tmp.cert_req != 0) ? TLS_ST_CW_CERT
-                                                    : TLS_ST_CW_CHANGE;
+                                                    : TLS_ST_CW_FINISHED;
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_CW_CERT:
         /* If a non-empty Certificate we also send CertificateVerify */
         st->hand_state = (s->s3->tmp.cert_req == 1) ? TLS_ST_CW_CERT_VRFY
-                                                    : TLS_ST_CW_CHANGE;
+                                                    : TLS_ST_CW_FINISHED;
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_CW_CERT_VRFY:
-        st->hand_state = TLS_ST_CW_CHANGE;
-        return WRITE_TRAN_CONTINUE;
-
-    case TLS_ST_CW_CHANGE:
         st->hand_state = TLS_ST_CW_FINISHED;
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_CW_FINISHED:
-        if (s->hit) {
-            st->hand_state = TLS_ST_OK;
-            ossl_statem_set_in_init(s, 0);
-            return WRITE_TRAN_CONTINUE;
-        }
-        return WRITE_TRAN_FINISHED;
-
-    case TLS_ST_CR_FINISHED:
-        if (s->hit) {
-            st->hand_state = TLS_ST_CW_CHANGE;
-            return WRITE_TRAN_CONTINUE;
-        }
         st->hand_state = TLS_ST_OK;
         ossl_statem_set_in_init(s, 0);
         return WRITE_TRAN_CONTINUE;
@@ -461,7 +415,7 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s)
      * version we are going to negotiate yet, so we don't take this branch until
      * later
      */
-    if (s->method->version == TLS1_3_VERSION)
+    if (SSL_IS_TLS13(s))
         return ossl_statem_client13_write_transition(s);
 
     switch (st->hand_state) {
@@ -683,6 +637,12 @@ WORK_STATE ossl_statem_client_post_work(SSL *s, WORK_STATE wst)
 #endif
         if (statem_flush(s) != 1)
             return WORK_MORE_B;
+
+        if (SSL_IS_TLS13(s)) {
+            if (!s->method->ssl3_enc->change_cipher_state(s,
+                        SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_WRITE))
+            return WORK_ERROR;
+        }
         break;
     }
 
@@ -1360,6 +1320,21 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
     }
 #endif
 
+    /*
+     * In TLSv1.3 we have some post-processing to change cipher state, otherwise
+     * we're done with this message
+     */
+    if (SSL_IS_TLS13(s)
+            && (!s->method->ssl3_enc->setup_key_block(s)
+                || !s->method->ssl3_enc->change_cipher_state(s,
+                    SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_WRITE)
+                || !s->method->ssl3_enc->change_cipher_state(s,
+                    SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_CLIENT_READ))) {
+        al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_CANNOT_CHANGE_CIPHER);
+        goto f_err;
+    }
+
     return MSG_PROCESS_CONTINUE_READING;
  f_err:
     ssl3_send_alert(s, SSL3_AL_FATAL, al);
@@ -2212,34 +2187,21 @@ MSG_PROCESS_RETURN tls_process_cert_status(SSL *s, PACKET *pkt)
     return MSG_PROCESS_ERROR;
 }
 
-MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt)
+/*
+ * Perform miscellaneous checks and processing after we have received the
+ * server's initial flight. In TLS1.3 this is after the Server Finished message.
+ * In <=TLS1.2 this is after the ServerDone message. Returns 1 on success or 0
+ * on failure.
+ */
+int tls_process_initial_server_flight(SSL *s, int *al)
 {
-    if (PACKET_remaining(pkt) > 0) {
-        /* should contain no data */
-        ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-        SSLerr(SSL_F_TLS_PROCESS_SERVER_DONE, SSL_R_LENGTH_MISMATCH);
-        ossl_statem_set_error(s);
-        return MSG_PROCESS_ERROR;
-    }
-#ifndef OPENSSL_NO_SRP
-    if (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kSRP) {
-        if (SRP_Calc_A_param(s) <= 0) {
-            SSLerr(SSL_F_TLS_PROCESS_SERVER_DONE, SSL_R_SRP_A_CALC);
-            ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-            ossl_statem_set_error(s);
-            return MSG_PROCESS_ERROR;
-        }
-    }
-#endif
-
     /*
      * at this point we check that we have the required stuff from
      * the server
      */
     if (!ssl3_check_cert_and_algorithm(s)) {
-        ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
-        ossl_statem_set_error(s);
-        return MSG_PROCESS_ERROR;
+        *al = SSL_AD_HANDSHAKE_FAILURE;
+        return 0;
     }
 
     /*
@@ -2251,28 +2213,56 @@ MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt)
         int ret;
         ret = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg);
         if (ret == 0) {
-            ssl3_send_alert(s, SSL3_AL_FATAL,
-                            SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE);
-            SSLerr(SSL_F_TLS_PROCESS_SERVER_DONE,
+            *al = SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE;
+            SSLerr(SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT,
                    SSL_R_INVALID_STATUS_RESPONSE);
-            return MSG_PROCESS_ERROR;
+            return 0;
         }
         if (ret < 0) {
-            ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-            SSLerr(SSL_F_TLS_PROCESS_SERVER_DONE, ERR_R_MALLOC_FAILURE);
-            return MSG_PROCESS_ERROR;
+            *al = SSL_AD_INTERNAL_ERROR;
+            SSLerr(SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT,
+                   ERR_R_MALLOC_FAILURE);
+            return 0;
         }
     }
 #ifndef OPENSSL_NO_CT
     if (s->ct_validation_callback != NULL) {
         /* Note we validate the SCTs whether or not we abort on error */
         if (!ssl_validate_ct(s) && (s->verify_mode & SSL_VERIFY_PEER)) {
-            ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
-            return MSG_PROCESS_ERROR;
+            *al = SSL_AD_HANDSHAKE_FAILURE;
+            return 0;
         }
     }
 #endif
 
+    return 1;
+}
+
+MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt)
+{
+    int al = SSL_AD_INTERNAL_ERROR;
+
+    if (PACKET_remaining(pkt) > 0) {
+        /* should contain no data */
+        al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_PROCESS_SERVER_DONE, SSL_R_LENGTH_MISMATCH);
+        goto err;
+    }
+#ifndef OPENSSL_NO_SRP
+    if (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kSRP) {
+        if (SRP_Calc_A_param(s) <= 0) {
+            SSLerr(SSL_F_TLS_PROCESS_SERVER_DONE, SSL_R_SRP_A_CALC);
+            goto err;
+        }
+    }
+#endif
+
+    /*
+     * Error queue messages are generated directly by this function
+     */
+    if (!tls_process_initial_server_flight(s, &al))
+        goto err;
+
 #ifndef OPENSSL_NO_SCTP
     /* Only applies to renegotiation */
     if (SSL_IS_DTLS(s) && BIO_dgram_is_sctp(SSL_get_wbio(s))
@@ -2281,6 +2271,11 @@ MSG_PROCESS_RETURN tls_process_server_done(SSL *s, PACKET *pkt)
     else
 #endif
         return MSG_PROCESS_FINISHED_READING;
+
+ err:
+    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    ossl_statem_set_error(s);
+    return MSG_PROCESS_ERROR;
 }
 
 static int tls_construct_cke_psk_preamble(SSL *s, WPACKET *pkt, int *al)
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index 46ffb47..a971c51 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -326,11 +326,11 @@ MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, PACKET *pkt)
 
 MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt)
 {
-    int al;
+    int al = SSL_AD_INTERNAL_ERROR;
     size_t md_len;
 
     /* If this occurs, we have missed a message */
-    if (!s->s3->change_cipher_spec) {
+    if (!SSL_IS_TLS13(s) && !s->s3->change_cipher_spec) {
         al = SSL_AD_UNEXPECTED_MESSAGE;
         SSLerr(SSL_F_TLS_PROCESS_FINISHED, SSL_R_GOT_A_FIN_BEFORE_A_CCS);
         goto f_err;
@@ -367,6 +367,34 @@ MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt)
         s->s3->previous_server_finished_len = md_len;
     }
 
+    /*
+     * In TLS1.3 we also have to change cipher state and do any final processing
+     * of the initial server flight (if we are a client)
+     */
+    if (SSL_IS_TLS13(s)) {
+        if (s->server) {
+            if (!s->method->ssl3_enc->change_cipher_state(s,
+                    SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_READ)) {
+                SSLerr(SSL_F_TLS_PROCESS_FINISHED, SSL_R_CANNOT_CHANGE_CIPHER);
+                goto f_err;
+            }
+        } else {
+            if (!s->method->ssl3_enc->generate_master_secret(s,
+                    s->session->master_key, s->handshake_secret, 0,
+                    &s->session->master_key_length)) {
+                SSLerr(SSL_F_TLS_PROCESS_FINISHED, SSL_R_CANNOT_CHANGE_CIPHER);
+                goto f_err;
+            }
+            if (!s->method->ssl3_enc->change_cipher_state(s,
+                    SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_READ)) {
+                SSLerr(SSL_F_TLS_PROCESS_FINISHED, SSL_R_CANNOT_CHANGE_CIPHER);
+                goto f_err;
+            }
+            if (!tls_process_initial_server_flight(s, &al))
+                goto f_err;
+        }
+    }
+
     return MSG_PROCESS_FINISHED_READING;
  f_err:
     ssl3_send_alert(s, SSL3_AL_FATAL, al);
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 740595b..f6c76ab 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -77,6 +77,7 @@ __owur int tls_get_message_body(SSL *s, size_t *len);
 __owur int dtls_get_message(SSL *s, int *mt, size_t *len);
 
 /* Message construction and processing functions */
+__owur int tls_process_initial_server_flight(SSL *s, int *al);
 __owur MSG_PROCESS_RETURN tls_process_change_cipher_spec(SSL *s, PACKET *pkt);
 __owur MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt);
 __owur int tls_construct_change_cipher_spec(SSL *s, WPACKET *pkt);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 97ecbcd..33808ed 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -94,15 +94,15 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt)
     default:
         break;
 
-    case TLS_ST_SW_SRVR_DONE:
+    case TLS_ST_SW_FINISHED:
         if (s->s3->tmp.cert_request) {
             if (mt == SSL3_MT_CERTIFICATE) {
                 st->hand_state = TLS_ST_SR_CERT;
                 return 1;
             }
         } else {
-            if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-                st->hand_state = TLS_ST_SR_CHANGE;
+            if (mt == SSL3_MT_FINISHED) {
+                st->hand_state = TLS_ST_SR_FINISHED;
                 return 1;
             }
         }
@@ -110,8 +110,8 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt)
 
     case TLS_ST_SR_CERT:
         if (s->session->peer == NULL) {
-            if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-                st->hand_state = TLS_ST_SR_CHANGE;
+            if (mt == SSL3_MT_FINISHED) {
+                st->hand_state = TLS_ST_SR_FINISHED;
                 return 1;
             }
         } else {
@@ -123,25 +123,11 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt)
         break;
 
     case TLS_ST_SR_CERT_VRFY:
-        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_SR_CHANGE;
-            return 1;
-        }
-        break;
-
-    case TLS_ST_SR_CHANGE:
         if (mt == SSL3_MT_FINISHED) {
             st->hand_state = TLS_ST_SR_FINISHED;
             return 1;
         }
         break;
-
-    case TLS_ST_SW_FINISHED:
-        if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) {
-            st->hand_state = TLS_ST_SR_CHANGE;
-            return 1;
-        }
-        break;
     }
 
     /* No valid transition found */
@@ -164,8 +150,11 @@ int ossl_statem_server_read_transition(SSL *s, int mt)
 {
     OSSL_STATEM *st = &s->statem;
 
-    if (s->method->version == TLS1_3_VERSION)
-        return ossl_statem_server13_read_transition(s, mt);
+    if (SSL_IS_TLS13(s)) {
+        if (!ossl_statem_server13_read_transition(s, mt))
+            goto err;
+        return 1;
+    }
 
     switch (st->hand_state) {
     default:
@@ -298,6 +287,7 @@ int ossl_statem_server_read_transition(SSL *s, int mt)
         break;
     }
 
+ err:
     /* No valid transition found */
     ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_UNEXPECTED_MESSAGE);
     SSLerr(SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION, SSL_R_UNEXPECTED_MESSAGE);
@@ -419,57 +409,31 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
 
     case TLS_ST_SW_SRVR_HELLO:
         if (s->hit)
-            st->hand_state = s->tlsext_ticket_expected
-                                ? TLS_ST_SW_SESSION_TICKET : TLS_ST_SW_CHANGE;
+            st->hand_state = TLS_ST_SW_FINISHED;
+        else if (send_certificate_request(s))
+            st->hand_state = TLS_ST_SW_CERT_REQ;
         else
             st->hand_state = TLS_ST_SW_CERT;
 
         return WRITE_TRAN_CONTINUE;
 
-    case TLS_ST_SW_CERT:
-        if (s->tlsext_status_expected) {
-            st->hand_state = TLS_ST_SW_CERT_STATUS;
-            return WRITE_TRAN_CONTINUE;
-        }
-        /* Fall through */
-
-    case TLS_ST_SW_CERT_STATUS:
-        if (send_certificate_request(s)) {
-            st->hand_state = TLS_ST_SW_CERT_REQ;
-            return WRITE_TRAN_CONTINUE;
-        }
-        /* Fall through */
-
     case TLS_ST_SW_CERT_REQ:
-        st->hand_state = TLS_ST_SW_SRVR_DONE;
-        return WRITE_TRAN_CONTINUE;
-
-    case TLS_ST_SW_SRVR_DONE:
-        return WRITE_TRAN_FINISHED;
-
-    case TLS_ST_SR_FINISHED:
-        if (s->hit) {
-            st->hand_state = TLS_ST_OK;
-            ossl_statem_set_in_init(s, 0);
-            return WRITE_TRAN_CONTINUE;
-        }
-
-        st->hand_state = s->tlsext_ticket_expected ? TLS_ST_SW_SESSION_TICKET
-                                                   : TLS_ST_SW_CHANGE;
+        st->hand_state = TLS_ST_SW_CERT;
         return WRITE_TRAN_CONTINUE;
 
-    case TLS_ST_SW_SESSION_TICKET:
-        st->hand_state = TLS_ST_SW_CHANGE;
+    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_CHANGE:
+    case TLS_ST_SW_CERT_STATUS:
         st->hand_state = TLS_ST_SW_FINISHED;
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_SW_FINISHED:
-        if (s->hit)
-            return WRITE_TRAN_FINISHED;
+        return WRITE_TRAN_FINISHED;
 
+    case TLS_ST_SR_FINISHED:
         st->hand_state = TLS_ST_OK;
         ossl_statem_set_in_init(s, 0);
         return WRITE_TRAN_CONTINUE;
@@ -489,7 +453,7 @@ WRITE_TRAN ossl_statem_server_write_transition(SSL *s)
      * to negotiate yet, so we don't take this branch until later
      */
 
-    if (s->method->version == TLS1_3_VERSION)
+    if (SSL_IS_TLS13(s))
         return ossl_statem_server13_write_transition(s);
 
     switch (st->hand_state) {
@@ -745,6 +709,20 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
                      sizeof(sctpauthkey), sctpauthkey);
         }
 #endif
+        /*
+         * TODO(TLS1.3): This actually causes a problem. We don't yet know
+         * whether the next record we are going to receive is an unencrypted
+         * alert, or an encrypted handshake message. We're going to need
+         * something clever in the record layer for this.
+         */
+        if (SSL_IS_TLS13(s)) {
+            if (!s->method->ssl3_enc->setup_key_block(s)
+                || !s->method->ssl3_enc->change_cipher_state(s,
+                        SSL3_CC_HANDSHAKE | SSL3_CHANGE_CIPHER_SERVER_WRITE)
+                || !s->method->ssl3_enc->change_cipher_state(s,
+                        SSL3_CC_HANDSHAKE |SSL3_CHANGE_CIPHER_SERVER_READ))
+            return WORK_ERROR;
+        }
         break;
 
     case TLS_ST_SW_CHANGE:
@@ -787,6 +765,14 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
                      0, NULL);
         }
 #endif
+        if (SSL_IS_TLS13(s)) {
+            if (!s->method->ssl3_enc->generate_master_secret(s,
+                        s->session->master_key, s->handshake_secret, 0,
+                        &s->session->master_key_length)
+                || !s->method->ssl3_enc->change_cipher_state(s,
+                        SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_SERVER_WRITE))
+            return WORK_ERROR;
+        }
         break;
     }
 
@@ -1006,7 +992,7 @@ WORK_STATE ossl_statem_server_post_process_message(SSL *s, WORK_STATE wst)
 #endif
         return WORK_FINISHED_CONTINUE;
     }
-
+    return WORK_FINISHED_CONTINUE;
 }
 
 #ifndef OPENSSL_NO_SRP
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 74022ee..3e592be 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -81,10 +81,10 @@ SSL3_ENC_METHOD const TLSv1_2_enc_data = {
 SSL3_ENC_METHOD const TLSv1_3_enc_data = {
     tls1_enc,
     tls1_mac,
-    tls1_setup_key_block,
-    tls1_generate_master_secret,
-    tls1_change_cipher_state,
-    tls1_final_finish_mac,
+    tls13_setup_key_block,
+    tls13_generate_master_secret,
+    tls13_change_cipher_state,
+    tls13_final_finish_mac,
     TLS_MD_CLIENT_FINISH_CONST, TLS_MD_CLIENT_FINISH_CONST_SIZE,
     TLS_MD_SERVER_FINISH_CONST, TLS_MD_SERVER_FINISH_CONST_SIZE,
     tls1_alert_code,
@@ -943,7 +943,7 @@ int ssl_cipher_disabled(SSL *s, const SSL_CIPHER *c, int op)
 
 static int tls_use_ticket(SSL *s)
 {
-    if (s->options & SSL_OP_NO_TICKET)
+    if ((s->options & SSL_OP_NO_TICKET) || SSL_IS_TLS13(s))
         return 0;
     return ssl_security(s, SSL_SECOP_TICKET, 0, 0, NULL);
 }
@@ -1029,9 +1029,10 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al)
     const unsigned char *pcurves = NULL;
     size_t num_curves = 0;
     int using_ecc = 0;
+    int min_version, max_version, reason;
 
     /* See if we support any ECC ciphersuites */
-    if ((s->version >= TLS1_VERSION && s->version <= TLS1_2_VERSION)
+    if ((s->version >= TLS1_VERSION && s->version <= TLS1_3_VERSION)
             || SSL_IS_DTLS(s)) {
         int i;
         unsigned long alg_k, alg_a;
@@ -1043,17 +1044,12 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al)
             alg_k = c->algorithm_mkey;
             alg_a = c->algorithm_auth;
             if ((alg_k & (SSL_kECDHE | SSL_kECDHEPSK))
-                || (alg_a & SSL_aECDSA)) {
+                || (alg_a & SSL_aECDSA)
+                || c->min_tls >= TLS1_3_VERSION) {
                 using_ecc = 1;
                 break;
             }
         }
-    } else if (SSL_IS_TLS13(s)) {
-        /*
-         * TODO(TLS1.3): We always use ECC for TLSv1.3 at the moment. This will
-         * change if we implement DH key shares
-         */
-        using_ecc = 1;
     }
 #else
     if (SSL_IS_TLS13(s)) {
@@ -1366,9 +1362,15 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al)
         return 0;
     }
 
+    reason = ssl_get_client_min_max_version(s, &min_version, &max_version);
+    if (reason != 0) {
+        SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, reason);
+        return 0;
+    }
+
     /* TLS1.3 specific extensions */
-    if (SSL_IS_TLS13(s)) {
-        int min_version, max_version, reason, currv;
+    if (!SSL_IS_DTLS(s) && max_version >= TLS1_3_VERSION) {
+        int currv;
         size_t i, sharessent = 0;
 
         /* TODO(TLS1.3): Should we add this extension for versions < TLS1.3? */
@@ -1379,11 +1381,7 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al)
             SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR);
             return 0;
         }
-        reason = ssl_get_client_min_max_version(s, &min_version, &max_version);
-        if (reason != 0) {
-            SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, reason);
-            return 0;
-        }
+
         /*
          * TODO(TLS1.3): There is some discussion on the TLS list as to wheter
          * we should include versions <TLS1.2. For the moment we do. To be
@@ -2287,7 +2285,8 @@ static int ssl_scan_clienthello_tlsext(SSL *s, CLIENTHELLO_MSG *hello, int *al)
             }
         }
 #endif                          /* OPENSSL_NO_EC */
-        else if (currext->type == TLSEXT_TYPE_session_ticket) {
+        else if (currext->type == TLSEXT_TYPE_session_ticket
+                && !SSL_IS_TLS13(s)) {
             if (s->tls_session_ticket_ext_cb &&
                 !s->tls_session_ticket_ext_cb(s,
                     PACKET_data(&currext->data),
@@ -3176,7 +3175,8 @@ int tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello,
     s->tlsext_ticket_expected = 0;
 
     /*
-     * If tickets disabled behave as if no ticket present to permit stateful
+     * If tickets disabled or not supported by the protocol version
+     * (e.g. TLSv1.3) behave as if no ticket present to permit stateful
      * resumption.
      */
     if (s->version <= SSL3_VERSION || !tls_use_ticket(s))
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index 39a61f4..f8ccdec 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -17,15 +17,11 @@
 /* Always filled with zeros */
 static const unsigned char default_zeros[EVP_MAX_MD_SIZE];
 
-static const unsigned char keylabel[] = "key";
-static const unsigned char ivlabel[] = "iv";
-
 /*
  * Given a |secret|; a |label| of length |labellen|; and a |hash| of the
  * handshake messages, derive a new secret |outlen| bytes long and store it in
- * the location pointed to be |out|. The |hash| value may be NULL.
- *
- * Returns 1 on success  0 on failure.
+ * the location pointed to be |out|. The |hash| value may be NULL. Returns 1 on
+ * success  0 on failure.
  */
 static int tls13_hkdf_expand(SSL *s, const unsigned char *secret,
                              const unsigned char *label, size_t labellen,
@@ -81,9 +77,8 @@ static int tls13_hkdf_expand(SSL *s, const unsigned char *secret,
 /*
  * Given a input secret |insecret| and a |label| of length |labellen|, derive a
  * new |secret|. This will be the length of the current hash output size and
- * will be based on the current state of the handshake hashes.
- *
- * Returns 1 on success  0 on failure.
+ * will be based on the current state of the handshake hashes. Returns 1 on
+ * success  0 on failure.
  */
 int tls13_derive_secret(SSL *s, const unsigned char *insecret,
                         const unsigned char *label, size_t labellen,
@@ -103,35 +98,44 @@ int tls13_derive_secret(SSL *s, const unsigned char *insecret,
 }
 
 /*
- * Given a |secret| generate a |key| of length |keylen| bytes.
- *
- * Returns 1 on success  0 on failure.
+ * Given a |secret| generate a |key| of length |keylen| bytes. Returns 1 on
+ * success  0 on failure.
  */
 int tls13_derive_key(SSL *s, const unsigned char *secret, unsigned char *key,
                      size_t keylen)
 {
+    static const unsigned char keylabel[] = "key";
+
     return tls13_hkdf_expand(s, secret, keylabel, sizeof(keylabel) - 1, NULL,
                              key, keylen);
 }
 
 /*
- * Given a |secret| generate an |iv| of length |ivlen| bytes.
- *
- * Returns 1 on success  0 on failure.
+ * Given a |secret| generate an |iv| of length |ivlen| bytes. Returns 1 on
+ * success  0 on failure.
  */
 int tls13_derive_iv(SSL *s, const unsigned char *secret, unsigned char *iv,
                     size_t ivlen)
 {
+    static const unsigned char ivlabel[] = "iv";
+
     return tls13_hkdf_expand(s, secret, ivlabel, sizeof(ivlabel) - 1, NULL,
                              iv, ivlen);
 }
 
+static int tls13_derive_finishedkey(SSL *s, const unsigned char *secret,
+                                 unsigned char *fin, size_t finlen)
+{
+    static const unsigned char finishedlabel[] = "finished";
+
+    return tls13_hkdf_expand(s, secret, finishedlabel,
+                             sizeof(finishedlabel) - 1, NULL, fin, finlen);
+}
+
 /*
  * Given the previous secret |prevsecret| and a new input secret |insecret| of
  * length |insecretlen|, generate a new secret and store it in the location
- * pointed to by |outsecret|.
- *
- * Returns 1 on success  0 on failure.
+ * pointed to by |outsecret|. Returns 1 on success  0 on failure.
  */
 static int tls13_generate_secret(SSL *s, const unsigned char *prevsecret,
                                  const unsigned char *insecret,
@@ -175,9 +179,7 @@ static int tls13_generate_secret(SSL *s, const unsigned char *prevsecret,
 
 /*
  * Given an input secret |insecret| of length |insecretlen| generate the early
- * secret.
- *
- * Returns 1 on success  0 on failure.
+ * secret. Returns 1 on success  0 on failure.
  */
 int tls13_generate_early_secret(SSL *s, const unsigned char *insecret,
                                 size_t insecretlen)
@@ -189,9 +191,7 @@ int tls13_generate_early_secret(SSL *s, const unsigned char *insecret,
 /*
  * Given an input secret |insecret| of length |insecretlen| generate the
  * handshake secret. This requires the early secret to already have been
- * generated.
- *
- * Returns 1 on success  0 on failure.
+ * generated. Returns 1 on success  0 on failure.
  */
 int tls13_generate_handshake_secret(SSL *s, const unsigned char *insecret,
                                 size_t insecretlen)
@@ -202,9 +202,8 @@ int tls13_generate_handshake_secret(SSL *s, const unsigned char *insecret,
 
 /*
  * Given the handshake secret |prev| of length |prevlen| generate the master
- * secret and store its length in |*secret_size|
- *
- * Returns 1 on success  0 on failure.
+ * secret and store its length in |*secret_size|. Returns 1 on success  0 on
+ * failure.
  */
 int tls13_generate_master_secret(SSL *s, unsigned char *out,
                                  unsigned char *prev, size_t prevlen,
@@ -214,4 +213,226 @@ int tls13_generate_master_secret(SSL *s, unsigned char *out,
     return tls13_generate_secret(s, prev, NULL, 0, out);
 }
 
+/*
+ * Generates the mac for the Finished message. Returns the length of the MAC or
+ * 0 on error.
+ */
+size_t tls13_final_finish_mac(SSL *s, const char *str, size_t slen,
+                             unsigned char *out)
+{
+    const EVP_MD *md = ssl_handshake_md(s);
+    unsigned char hash[EVP_MAX_MD_SIZE];
+    size_t hashlen, ret = 0;
+    EVP_PKEY *key = NULL;
+    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
+
+    if (!ssl_handshake_hash(s, hash, sizeof(hash), &hashlen))
+        goto err;
+
+    if (str == s->method->ssl3_enc->server_finished_label)
+        key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL,
+                                   s->server_finished_secret, hashlen);
+    else
+        key = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL,
+                                   s->client_finished_secret, hashlen);
+
+    if (key == NULL
+            || ctx == NULL
+            || EVP_DigestSignInit(ctx, NULL, md, NULL, key) <= 0
+            || EVP_DigestSignUpdate(ctx, hash, hashlen) <= 0
+            || EVP_DigestSignFinal(ctx, out, &hashlen) <= 0)
+        goto err;
 
+    ret = hashlen;
+ err:
+    EVP_PKEY_free(key);
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+/*
+ * There isn't really a key block in TLSv1.3, but we still need this function
+ * for initialising the cipher and hash. Returns 1 on success or 0 on failure.
+ */
+int tls13_setup_key_block(SSL *s)
+{
+    const EVP_CIPHER *c;
+    const EVP_MD *hash;
+    int mac_type = NID_undef;
+
+    s->session->cipher = s->s3->tmp.new_cipher;
+    if (!ssl_cipher_get_evp
+        (s->session, &c, &hash, &mac_type, NULL, NULL, 0)) {
+        SSLerr(SSL_F_TLS13_SETUP_KEY_BLOCK, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
+        return 0;
+    }
+
+    s->s3->tmp.new_sym_enc = c;
+    s->s3->tmp.new_hash = hash;
+
+    return 1;
+}
+
+int tls13_change_cipher_state(SSL *s, int which)
+{
+    static const unsigned char client_handshake_traffic[] =
+        "client handshake traffic secret";
+    static const unsigned char client_application_traffic[] =
+        "client application traffic secret";
+    static const unsigned char server_handshake_traffic[] =
+        "server handshake traffic secret";
+    static const unsigned char server_application_traffic[] =
+        "server application traffic secret";
+    unsigned char key[EVP_MAX_KEY_LENGTH];
+    unsigned char iv[EVP_MAX_IV_LENGTH];
+    unsigned char secret[EVP_MAX_MD_SIZE];
+    unsigned char *insecret;
+    unsigned char *finsecret = NULL;
+    EVP_CIPHER_CTX *ciph_ctx;
+    const EVP_CIPHER *ciph = s->s3->tmp.new_sym_enc;;
+    size_t ivlen, keylen, finsecretlen;
+    const unsigned char *label;
+    size_t labellen;
+    int ret = 0;
+
+    if (which & SSL3_CC_READ) {
+        if (s->enc_read_ctx != NULL) {
+            EVP_CIPHER_CTX_reset(s->enc_read_ctx);
+        } else {
+            s->enc_read_ctx = EVP_CIPHER_CTX_new();
+            if (s->enc_read_ctx == NULL) {
+                SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        }
+        ciph_ctx = s->enc_read_ctx;
+
+        RECORD_LAYER_reset_read_sequence(&s->rlayer);
+    } else {
+        if (s->enc_write_ctx != NULL) {
+            EVP_CIPHER_CTX_reset(s->enc_write_ctx);
+        } else {
+            s->enc_write_ctx = EVP_CIPHER_CTX_new();
+            if (s->enc_write_ctx == NULL) {
+                SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_MALLOC_FAILURE);
+                goto err;
+            }
+        }
+        ciph_ctx = s->enc_write_ctx;
+
+        RECORD_LAYER_reset_write_sequence(&s->rlayer);
+    }
+
+    if (((which & SSL3_CC_CLIENT) && (which & SSL3_CC_WRITE))
+            || ((which & SSL3_CC_SERVER) && (which & SSL3_CC_READ))) {
+        if (which & SSL3_CC_HANDSHAKE) {
+            insecret = s->handshake_secret;
+            finsecret = s->client_finished_secret;
+            finsecretlen = sizeof(s->client_finished_secret);
+            label = client_handshake_traffic;
+            labellen = sizeof(client_handshake_traffic) - 1;
+        } else {
+            insecret = s->session->master_key;
+            label = client_application_traffic;
+            labellen = sizeof(client_application_traffic) - 1;
+        }
+    } else {
+        if (which & SSL3_CC_HANDSHAKE) {
+            insecret = s->handshake_secret;
+            finsecret = s->server_finished_secret;
+            finsecretlen = sizeof(s->server_finished_secret);
+            label = server_handshake_traffic;
+            labellen = sizeof(server_handshake_traffic) - 1;
+        } else {
+            insecret = s->session->master_key;
+            label = server_application_traffic;
+            labellen = sizeof(server_application_traffic) - 1;
+        }
+    }
+
+    if (!tls13_derive_secret(s, insecret, label, labellen, secret)) {
+        SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    /* TODO(size_t): convert me */
+    keylen = EVP_CIPHER_key_length(ciph);
+
+    if (EVP_CIPHER_mode(ciph) == EVP_CIPH_GCM_MODE)
+        ivlen = EVP_GCM_TLS_FIXED_IV_LEN;
+    else if (EVP_CIPHER_mode(ciph) == EVP_CIPH_CCM_MODE)
+        ivlen = EVP_CCM_TLS_FIXED_IV_LEN;
+    else
+        ivlen = EVP_CIPHER_iv_length(ciph);
+
+    if (!tls13_derive_key(s, secret, key, keylen)
+            || !tls13_derive_iv(s, secret, iv, ivlen)
+            || (finsecret != NULL && !tls13_derive_finishedkey(s, secret,
+                                                               finsecret,
+                                                               finsecretlen))) {
+        SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (EVP_CIPHER_mode(ciph) == EVP_CIPH_GCM_MODE) {
+        if (!EVP_CipherInit_ex(ciph_ctx, ciph, NULL, key, NULL,
+                               (which & SSL3_CC_WRITE))
+                || !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_GCM_SET_IV_FIXED,
+                                        (int)ivlen, iv)) {
+            SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_EVP_LIB);
+            goto err;
+        }
+    } else if (EVP_CIPHER_mode(ciph) == EVP_CIPH_CCM_MODE) {
+        int taglen;
+
+        if (s->s3->tmp.new_cipher->algorithm_enc
+                & (SSL_AES128CCM8 | SSL_AES256CCM8))
+            taglen = 8;
+        else
+            taglen = 16;
+        if (!EVP_CipherInit_ex(ciph_ctx, ciph, NULL, NULL, NULL,
+                               (which & SSL3_CC_WRITE))
+                || !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_IVLEN, 12,
+                                        NULL)
+                || !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_TAG, taglen,
+                                        NULL)
+                || !EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_CCM_SET_IV_FIXED,
+                                        (int)ivlen, iv)
+                || !EVP_CipherInit_ex(ciph_ctx, NULL, NULL, key, NULL, -1)) {
+            SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_EVP_LIB);
+            goto err;
+        }
+    } else {
+        if (!EVP_CipherInit_ex(ciph_ctx, ciph, NULL, key, iv,
+                               (which & SSL3_CC_WRITE))) {
+            SSLerr(SSL_F_TLS13_CHANGE_CIPHER_STATE, ERR_R_EVP_LIB);
+            goto err;
+        }
+    }
+
+#ifdef OPENSSL_SSL_TRACE_CRYPTO
+    if (s->msg_callback) {
+        int wh = which & SSL3_CC_WRITE ? TLS1_RT_CRYPTO_WRITE : 0;
+
+        if (ciph->key_len)
+            s->msg_callback(2, s->version, wh | TLS1_RT_CRYPTO_KEY,
+                            key, ciph->key_len, s, s->msg_callback_arg);
+        if (ivlen) {
+            if (EVP_CIPHER_mode(ciph) == EVP_CIPH_GCM_MODE)
+                wh |= TLS1_RT_CRYPTO_FIXED_IV;
+            else
+                wh |= TLS1_RT_CRYPTO_IV;
+            s->msg_callback(2, s->version, wh, iv, ivlen, s,
+                            s->msg_callback_arg);
+        }
+    }
+#endif
+
+    ret = 1;
+
+ err:
+    OPENSSL_cleanse(secret, sizeof(secret));
+    OPENSSL_cleanse(key, sizeof(key));
+    OPENSSL_cleanse(iv, sizeof(iv));
+    return ret;
+}
diff --git a/test/asynciotest.c b/test/asynciotest.c
index a4f43f8..d7b1dd3 100644
--- a/test/asynciotest.c
+++ b/test/asynciotest.c
@@ -142,8 +142,9 @@ static int async_write(BIO *bio, const char *in, int inl)
                 abort();
 
             while (PACKET_remaining(&pkt) > 0) {
-                PACKET payload;
+                PACKET payload, wholebody;
                 unsigned int contenttype, versionhi, versionlo, data;
+                unsigned int msgtype = 0, negversion;
 
                 if (   !PACKET_get_1(&pkt, &contenttype)
                     || !PACKET_get_1(&pkt, &versionhi)
@@ -154,6 +155,17 @@ static int async_write(BIO *bio, const char *in, int inl)
                 /* Pretend we wrote out the record header */
                 written += SSL3_RT_HEADER_LENGTH;
 
+                wholebody = payload;
+                if (contenttype == SSL3_RT_HANDSHAKE
+                        && !PACKET_get_1(&wholebody, &msgtype))
+                    abort();
+
+                if (msgtype == SSL3_MT_SERVER_HELLO
+                        && (!PACKET_forward(&wholebody,
+                                            SSL3_HM_HEADER_LENGTH - 1)
+                            || !PACKET_get_net_2(&wholebody, &negversion)))
+                    abort();
+
                 while (PACKET_get_1(&payload, &data)) {
                     /* Create a new one byte long record for each byte in the
                      * record in the input buffer
@@ -177,10 +189,14 @@ static int async_write(BIO *bio, const char *in, int inl)
                     written++;
                 }
                 /*
-                 * We can't fragment anything after the CCS, otherwise we
-                 * get a bad record MAC
+                 * We can't fragment anything after the ServerHello (or CCS <=
+                 * TLS1.2), otherwise we get a bad record MAC
+                 * TODO(TLS1.3): Change TLS1_3_VERSION_DRAFT to TLS1_3_VERSION
+                 * before release
                  */
-                if (contenttype == SSL3_RT_CHANGE_CIPHER_SPEC) {
+                if (contenttype == SSL3_RT_CHANGE_CIPHER_SPEC
+                        || (negversion == TLS1_3_VERSION_DRAFT
+                            && msgtype == SSL3_MT_SERVER_HELLO)) {
                     fragment = 0;
                     break;
                 }
diff --git a/test/clienthellotest.c b/test/clienthellotest.c
index b8157f2..61e81c3 100644
--- a/test/clienthellotest.c
+++ b/test/clienthellotest.c
@@ -56,10 +56,23 @@ int main(int argc, char *argv[])
     for (; currtest < TOTAL_NUM_TESTS; currtest++) {
         testresult = 0;
         ctx = SSL_CTX_new(TLS_method());
+
+        /* Testing for session tickets <= TLS1.2; not relevant for 1.3 */
+        if (ctx == NULL || !SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION))
+            goto end;
+
         con = SSL_new(ctx);
+        if (con == NULL)
+            goto end;
 
         rbio = BIO_new(BIO_s_mem());
         wbio = BIO_new(BIO_s_mem());
+        if (rbio == NULL || wbio == NULL) {
+            BIO_free(rbio);
+            BIO_free(wbio);
+            goto end;
+        }
+
         SSL_set_bio(con, rbio, wbio);
         SSL_set_connect_state(con);
 
diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t
index cafa30c..e6f7a36 100644
--- a/test/recipes/70-test_sslrecords.t
+++ b/test/recipes/70-test_sslrecords.t
@@ -128,6 +128,7 @@ ok(TLSProxy::Message->fail(), "Alert before SSLv2 ClientHello test");
 
 #Test 10: Sending an unrecognised record type in TLS1.2 should fail
 $proxy->clear();
+$proxy->serverflags("-tls1_2");
 $proxy->filter(\&add_unknown_record_type);
 $proxy->start();
 ok(TLSProxy::Message->fail(), "Unrecognised record type in TLS1.2");
diff --git a/test/recipes/70-test_sslsessiontick.t b/test/recipes/70-test_sslsessiontick.t
index 0c29ec7..8b7b20c 100755
--- a/test/recipes/70-test_sslsessiontick.t
+++ b/test/recipes/70-test_sslsessiontick.t
@@ -24,8 +24,8 @@ plan skip_all => "$test_name needs the dynamic engine feature enabled"
 plan skip_all => "$test_name needs the sock feature enabled"
     if disabled("sock");
 
-plan skip_all => "$test_name needs TLS enabled"
-    if alldisabled(available_protocols("tls"));
+plan skip_all => "$test_name needs SSLv3, TLSv1, TLSv1.1 or TLSv1.2 enabled"
+    if alldisabled(("ssl3", "tls1", "tls1_1", "tls1_2"));
 
 $ENV{OPENSSL_ia32cap} = '~0x200000200000000';
 
@@ -48,6 +48,7 @@ my $proxy = TLSProxy::Proxy->new(
 #Test 1: By default with no existing session we should get a session ticket
 #Expected result: ClientHello extension seen; ServerHello extension seen
 #                 NewSessionTicket message seen; Full handshake
+$proxy->clientflags("-no_tls1_3");
 $proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
 plan tests => 10;
 checkmessages(1, "Default session ticket test", 1, 1, 1, 1);
@@ -57,6 +58,7 @@ checkmessages(1, "Default session ticket test", 1, 1, 1, 1);
 #Expected result: ClientHello extension seen; ServerHello extension not seen
 #                 NewSessionTicket message not seen; Full handshake
 clearall();
+$proxy->clientflags("-no_tls1_3");
 $proxy->serverflags("-no_ticket");
 $proxy->start();
 checkmessages(2, "No server support session ticket test", 1, 0, 0, 1);
@@ -66,7 +68,7 @@ checkmessages(2, "No server support session ticket test", 1, 0, 0, 1);
 #Expected result: ClientHello extension not seen; ServerHello extension not seen
 #                 NewSessionTicket message not seen; Full handshake
 clearall();
-$proxy->clientflags("-no_ticket");
+$proxy->clientflags("-no_tls1_3 -no_ticket");
 $proxy->start();
 checkmessages(3, "No client support session ticket test", 0, 0, 0, 1);
 
@@ -76,10 +78,10 @@ checkmessages(3, "No client support session ticket test", 0, 0, 0, 1);
 clearall();
 (undef, my $session) = tempfile();
 $proxy->serverconnects(2);
-$proxy->clientflags("-sess_out ".$session);
+$proxy->clientflags("-no_tls1_3 -sess_out ".$session);
 $proxy->start();
 $proxy->clearClient();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_tls1_3 -sess_in ".$session);
 $proxy->clientstart();
 checkmessages(4, "Session resumption session ticket test", 1, 0, 0, 0);
 unlink $session;
@@ -90,10 +92,10 @@ unlink $session;
 clearall();
 (undef, $session) = tempfile();
 $proxy->serverconnects(2);
-$proxy->clientflags("-sess_out ".$session." -no_ticket");
+$proxy->clientflags("-no_tls1_3 -sess_out ".$session." -no_ticket");
 $proxy->start();
 $proxy->clearClient();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_tls1_3 -sess_in ".$session);
 $proxy->clientstart();
 checkmessages(5, "Session resumption with ticket capable client without a "
                  ."ticket", 1, 1, 1, 0);
@@ -104,6 +106,7 @@ unlink $session;
 #                 NewSessionTicket message seen; Full handshake.
 clearall();
 $proxy->filter(\&ticket_filter);
+$proxy->clientflags("-no_tls1_3");
 $proxy->start();
 checkmessages(6, "Empty ticket test",  1, 1, 1, 1);
 
@@ -112,17 +115,17 @@ clearall();
 (undef, $session) = tempfile();
 $proxy->serverconnects(3);
 $proxy->filter(undef);
-$proxy->clientflags("-sess_out ".$session);
+$proxy->clientflags("-no_tls1_3 -sess_out ".$session);
 $proxy->start();
 $proxy->clearClient();
-$proxy->clientflags("-sess_in ".$session." -sess_out ".$session);
+$proxy->clientflags("-no_tls1_3 -sess_in ".$session." -sess_out ".$session);
 $proxy->filter(\&inject_empty_ticket_filter);
 $proxy->clientstart();
 #Expected result: ClientHello extension seen; ServerHello extension seen;
 #                 NewSessionTicket message seen; Abbreviated handshake.
 checkmessages(7, "Empty ticket resumption test",  1, 1, 1, 0);
 clearclient();
-$proxy->clientflags("-sess_in ".$session);
+$proxy->clientflags("-no_tls1_3 -sess_in ".$session);
 $proxy->filter(undef);
 $proxy->clientstart();
 #Expected result: ClientHello extension seen; ServerHello extension not seen;
@@ -134,6 +137,7 @@ unlink $session;
 #NewSessionTicket
 #Expected result: Connection failure
 clearall();
+$proxy->clientflags("-no_tls1_3");
 $proxy->serverflags("-no_ticket");
 $proxy->filter(\&inject_ticket_extension_filter);
 $proxy->start();
@@ -143,6 +147,7 @@ ok(TLSProxy::Message->fail, "Server sends ticket extension but no ticket test");
 #NewSessionTicket
 #Expected result: Connection failure
 clearall();
+$proxy->clientflags("-no_tls1_3");
 $proxy->serverflags("-no_ticket");
 $proxy->filter(\&inject_empty_ticket_filter);
 $proxy->start();
diff --git a/test/recipes/70-test_tls13messages.t b/test/recipes/70-test_tls13messages.t
new file mode 100755
index 0000000..62c12c4
--- /dev/null
+++ b/test/recipes/70-test_tls13messages.t
@@ -0,0 +1,123 @@
+#! /usr/bin/env perl
+# Copyright 2015-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;
+use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/;
+use OpenSSL::Test::Utils;
+use File::Temp qw(tempfile);
+use TLSProxy::Proxy;
+my $test_name = "test_tls13messages";
+setup($test_name);
+
+plan skip_all => "TLSProxy isn't usable on $^O"
+    if $^O =~ /^(VMS|MSWin32)$/;
+
+plan skip_all => "$test_name needs the dynamic engine feature enabled"
+    if disabled("engine") || disabled("dynamic-engine");
+
+plan skip_all => "$test_name needs the sock feature enabled"
+    if disabled("sock");
+
+plan skip_all => "$test_name needs TLSv1.3 enabled"
+    if disabled("tls1_3");
+
+$ENV{OPENSSL_ia32cap} = '~0x200000200000000';
+
+use constant {
+    DEFAULT_HANDSHAKE => 1,
+    OCSP_HANDSHAKE => 2,
+    RESUME_HANDSHAKE => 4,
+    CLIENT_AUTH_HANDSHAKE => 8,
+    ALL_HANDSHAKES => 15
+};
+
+my @handmessages = (
+    [TLSProxy::Message::MT_CLIENT_HELLO, ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_SERVER_HELLO, ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_CERTIFICATE_REQUEST, CLIENT_AUTH_HANDSHAKE],
+    [TLSProxy::Message::MT_CERTIFICATE, ALL_HANDSHAKES & ~RESUME_HANDSHAKE],
+    [TLSProxy::Message::MT_CERTIFICATE_STATUS, OCSP_HANDSHAKE],
+    [TLSProxy::Message::MT_FINISHED, ALL_HANDSHAKES],
+    [TLSProxy::Message::MT_CERTIFICATE, CLIENT_AUTH_HANDSHAKE],
+    [TLSProxy::Message::MT_CERTIFICATE_VERIFY, CLIENT_AUTH_HANDSHAKE],
+    [TLSProxy::Message::MT_FINISHED, ALL_HANDSHAKES],
+    [0, 0]
+);
+
+my $proxy = TLSProxy::Proxy->new(
+    undef,
+    cmdstr(app(["openssl"]), display => 1),
+    srctop_file("apps", "server.pem"),
+    (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE})
+);
+
+sub checkmessages($$);
+
+#Test 1: Check we get all the right messages for a default handshake
+(undef, my $session) = tempfile();
+$proxy->serverconnects(2);
+$proxy->clientflags("-sess_out ".$session);
+$proxy->start() or plan skip_all => "Unable to start up Proxy for tests";
+plan tests => 4;
+checkmessages(DEFAULT_HANDSHAKE, "Default handshake test");
+
+#Test 2: Resumption handshake
+$proxy->clearClient();
+$proxy->clientflags("-sess_in ".$session);
+$proxy->clientstart();
+checkmessages(RESUME_HANDSHAKE, "Resumption handshake test");
+unlink $session;
+
+#Test 3: A default handshake, but with a CertificateStatus message
+#TODO(TLS1.3): TLS1.3 doesn't actually have CertificateStatus messages. This is
+#a temporary test until such time as we do proper TLS1.3 style certificate
+#status
+$proxy->clear();
+$proxy->clientflags("-status");
+$proxy->serverflags("-status_file "
+                    .srctop_file("test", "recipes", "ocsp-response.der"));
+$proxy->start();
+checkmessages(OCSP_HANDSHAKE, "OCSP handshake test");
+
+#Test 4: A client auth handshake
+$proxy->clear();
+$proxy->clientflags("-cert ".srctop_file("apps", "server.pem"));
+$proxy->serverflags("-Verify 5");
+$proxy->start();
+checkmessages(CLIENT_AUTH_HANDSHAKE, "Client auth handshake test");
+
+sub checkmessages($$)
+{
+    my ($handtype, $testname) = @_;
+
+    subtest $testname => sub {
+        my $loop = 0;
+        my $numtests;
+
+        #First count the number of tests
+        for ($numtests = 1; $handmessages[$loop][1] != 0; $loop++) {
+            $numtests++ if (($handmessages[$loop][1] & $handtype) != 0);
+        }
+
+        plan tests => $numtests;
+
+        $loop = 0;
+        foreach my $message (@{$proxy->message_list}) {
+            for (; $handmessages[$loop][1] != 0
+                   && ($handmessages[$loop][1] & $handtype) == 0; $loop++) {
+                next;
+            }
+            ok($handmessages[$loop][1] != 0
+               && $message->mt == $handmessages[$loop][0],
+               "Message type check. Got ".$message->mt
+               .", expected ".$handmessages[$loop][0]);
+            $loop++;
+        }
+        ok($handmessages[$loop][1] == 0, "All expected messages processed");
+    }
+}
diff --git a/test/recipes/ocsp-response.der b/test/recipes/ocsp-response.der
new file mode 100644
index 0000000..31351a0
Binary files /dev/null and b/test/recipes/ocsp-response.der differ
diff --git a/test/ssl-tests/06-sni-ticket.conf b/test/ssl-tests/06-sni-ticket.conf
index 9620e01..ce0f63b 100644
--- a/test/ssl-tests/06-sni-ticket.conf
+++ b/test/ssl-tests/06-sni-ticket.conf
@@ -43,6 +43,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [0-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -84,6 +85,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [1-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -126,6 +128,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [2-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -168,6 +171,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [3-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -210,6 +214,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [4-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -252,6 +257,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [5-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -294,6 +300,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [6-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -336,6 +343,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [7-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -378,6 +386,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [8-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -420,6 +429,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [9-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -462,6 +472,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [10-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -504,6 +515,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [11-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -546,6 +558,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [12-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -588,6 +601,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [13-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -630,6 +644,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [14-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -672,6 +687,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [15-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
@@ -714,6 +730,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
 
 [16-sni-session-ticket-client]
 CipherString = DEFAULT
+MaxProtocol = TLSv1.2
 Options = -SessionTicket
 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
 VerifyMode = Peer
diff --git a/test/ssl-tests/06-sni-ticket.conf.in b/test/ssl-tests/06-sni-ticket.conf.in
index ccb9cbd..9c5266f 100644
--- a/test/ssl-tests/06-sni-ticket.conf.in
+++ b/test/ssl-tests/06-sni-ticket.conf.in
@@ -7,7 +7,7 @@
 # https://www.openssl.org/source/license.html
 
 
-## Test version negotiation
+## Test SNI/Session tickets
 
 use strict;
 use warnings;
@@ -17,12 +17,15 @@ package ssltests;
 
 our @tests = ();
 
+#Note: MaxProtocol is set to TLSv1.2 as session tickets work differently in
+#TLSv1.3.
+#TODO(TLS1.3): Implement TLSv1.3 style session tickets
 sub generate_tests() {
     foreach my $c ("SessionTicket", "-SessionTicket") {
-	foreach my $s1 ("SessionTicket", "-SessionTicket") {
-	    foreach my $s2 ("SessionTicket", "-SessionTicket") {
-		foreach my $n ("server1", "server2") {
-		    my $result = expected_result($c, $s1, $s2, $n);
+        foreach my $s1 ("SessionTicket", "-SessionTicket") {
+            foreach my $s2 ("SessionTicket", "-SessionTicket") {
+                foreach my $n ("server1", "server2") {
+                    my $result = expected_result($c, $s1, $s2, $n);
                     push @tests, {
                         "name" => "sni-session-ticket",
                         "client" => {
@@ -30,6 +33,7 @@ sub generate_tests() {
                             "extra" => {
                                 "ServerName" => $n,
                             },
+                            "MaxProtocol" => "TLSv1.2"
                         },
                         "server" => {
                             "Options" => $s1,
@@ -38,13 +42,13 @@ sub generate_tests() {
                                 "ServerNameCallback" => "IgnoreMismatch",
                             },
                         },
-			"server2" => {
-			    "Options" => $s2,
-			},
+                        "server2" => {
+                            "Options" => $s2,
+                        },
                         "test" => {
                             "ExpectedServerName" => $n,
                             "ExpectedResult" => "Success",
-			    "SessionTicketExpected" => $result,
+                            "SessionTicketExpected" => $result,
                         }
                     };
                 }
@@ -72,23 +76,24 @@ sub expected_result {
 push @tests, {
     "name" => "sni-session-ticket",
     "client" => {
-	"Options" => "SessionTicket",
+        "MaxProtocol" => "TLSv1.2",
+        "Options" => "SessionTicket",
         "extra" => {
             "ServerName" => "server1",
         }
     },
     "server" => {
-	"Options" => "SessionTicket",
+        "Options" => "SessionTicket",
         "extra" => {
               "BrokenSessionTicket" => "Yes",
         },
     },
     "server2" => {
-	"Options" => "SessionTicket",
+        "Options" => "SessionTicket",
     },
     "test" => {
-	"ExpectedResult" => "Success",
-	"SessionTicketExpected" => "No",
+        "ExpectedResult" => "Success",
+        "SessionTicketExpected" => "No",
     }
 };
 
diff --git a/test/ssltestlib.c b/test/ssltestlib.c
index 4e20763..42ba98c 100644
--- a/test/ssltestlib.c
+++ b/test/ssltestlib.c
@@ -564,7 +564,7 @@ int create_ssl_ctx_pair(const SSL_METHOD *sm, const SSL_METHOD *cm,
     return 0;
 }
 
-#define MAXLOOPS    100000
+#define MAXLOOPS    1000000
 
 /*
  * NOTE: Transfers control of the BIOs - this function will free them on error
diff --git a/test/tls13secretstest.c b/test/tls13secretstest.c
index 6b6c9bc..ccb8a12 100644
--- a/test/tls13secretstest.c
+++ b/test/tls13secretstest.c
@@ -162,6 +162,22 @@ const EVP_MD *ssl_handshake_md(SSL *s)
     return EVP_sha256();
 }
 
+void RECORD_LAYER_reset_read_sequence(RECORD_LAYER *rl)
+{
+}
+
+void RECORD_LAYER_reset_write_sequence(RECORD_LAYER *rl)
+{
+}
+
+int ssl_cipher_get_evp(const SSL_SESSION *s, const EVP_CIPHER **enc,
+                       const EVP_MD **md, int *mac_pkey_type,
+                       size_t *mac_secret_size, SSL_COMP **comp, int use_etm)
+
+{
+    return 0;
+}
+
 /* End of mocked out code */
 
 static int test_secret(SSL *s, unsigned char *prk,
diff --git a/util/TLSProxy/Message.pm b/util/TLSProxy/Message.pm
index 6bf5a72..3259edc 100644
--- a/util/TLSProxy/Message.pm
+++ b/util/TLSProxy/Message.pm
@@ -115,9 +115,9 @@ sub get_messages
             die "CCS received before message data complete\n";
         }
         if ($server) {
-            TLSProxy::Record->server_ccs_seen(1);
+            TLSProxy::Record->server_encrypting(1);
         } else {
-            TLSProxy::Record->client_ccs_seen(1);
+            TLSProxy::Record->client_encrypting(1);
         }
     } elsif ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
         if ($record->len == 0 || $record->len_real == 0) {
diff --git a/util/TLSProxy/Proxy.pm b/util/TLSProxy/Proxy.pm
index 16fd094..be9f8f8 100644
--- a/util/TLSProxy/Proxy.pm
+++ b/util/TLSProxy/Proxy.pm
@@ -23,6 +23,8 @@ use TLSProxy::NewSessionTicket;
 my $have_IPv6 = 0;
 my $IP_factory;
 
+my $is_tls13 = 0;
+
 sub new
 {
     my $class = shift;
@@ -103,6 +105,7 @@ sub clearClient
     $self->{record_list} = [];
     $self->{message_list} = [];
     $self->{clientflags} = "";
+    $is_tls13 = 0;
 
     TLSProxy::Message->clear();
     TLSProxy::Record->clear();
@@ -503,5 +506,12 @@ sub fill_known_data
     }
     return $ret;
 }
-
+sub is_tls13
+{
+    my $class = shift;
+    if (@_) {
+      $is_tls13 = shift;
+    }
+    return $is_tls13;
+}
 1;
diff --git a/util/TLSProxy/Record.pm b/util/TLSProxy/Record.pm
index bf6de43..7189035 100644
--- a/util/TLSProxy/Record.pm
+++ b/util/TLSProxy/Record.pm
@@ -11,8 +11,8 @@ use TLSProxy::Proxy;
 
 package TLSProxy::Record;
 
-my $server_ccs_seen = 0;
-my $client_ccs_seen = 0;
+my $server_encrypting = 0;
+my $client_encrypting = 0;
 my $etm = 0;
 
 use constant TLS_RECORD_HEADER_LENGTH => 5;
@@ -36,6 +36,7 @@ my %record_type = (
 
 use constant {
     VERS_TLS_1_4 => 773,
+    VERS_TLS_1_3_DRAFT => 32530,
     VERS_TLS_1_3 => 772,
     VERS_TLS_1_2 => 771,
     VERS_TLS_1_1 => 770,
@@ -108,9 +109,9 @@ sub get_records
                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real)
             );
 
-            if (($server && $server_ccs_seen)
-                     || (!$server && $client_ccs_seen)) {
-                if ($version != VERS_TLS_1_3() && $etm) {
+            if (($server && $server_encrypting)
+                     || (!$server && $client_encrypting)) {
+                if (!TLSProxy::Proxy->is_tls13() && $etm) {
                     $record->decryptETM();
                 } else {
                     $record->decrypt();
@@ -133,26 +134,26 @@ sub get_records
 
 sub clear
 {
-    $server_ccs_seen = 0;
-    $client_ccs_seen = 0;
+    $server_encrypting = 0;
+    $client_encrypting = 0;
 }
 
 #Class level accessors
-sub server_ccs_seen
+sub server_encrypting
 {
     my $class = shift;
     if (@_) {
-      $server_ccs_seen = shift;
+      $server_encrypting = shift;
     }
-    return $server_ccs_seen;
+    return $server_encrypting;
 }
-sub client_ccs_seen
+sub client_encrypting
 {
     my $class = shift;
     if (@_) {
-      $client_ccs_seen = shift;
+      $client_encrypting= shift;
     }
-    return $client_ccs_seen;
+    return $client_encrypting;
 }
 #Enable/Disable Encrypt-then-MAC
 sub etm
@@ -228,7 +229,19 @@ sub decrypt()
     my $data = $self->data;
 
     #Throw away any IVs
-    if ($self->version >= VERS_TLS_1_3()) {
+    if (TLSProxy::Proxy->is_tls13()) {
+        #A TLS1.3 client, when processing the server's initial flight, could
+        #respond with either an encrypted or an unencrypted alert.
+        if ($self->content_type() == RT_ALERT) {
+            #TODO(TLS1.3): Eventually it is sufficient just to check the record
+            #content type. If an alert is encrypted it will have a record
+            #content type of application data. However we haven't done the
+            #record layer changes yet, so it's a bit more complicated. For now
+            #we will additionally check if the data length is 2 (1 byte for
+            #alert level, 1 byte for alert description). If it is, then this is
+            #an unecrypted alert, so don't try to decrypt
+            return $data if (length($data) == 2);
+        }
         #8 bytes for a GCM IV
         $data = substr($data, 8);
         $mactaglen = 16;
diff --git a/util/TLSProxy/ServerHello.pm b/util/TLSProxy/ServerHello.pm
index 79a8be9..a1bc7b3 100644
--- a/util/TLSProxy/ServerHello.pm
+++ b/util/TLSProxy/ServerHello.pm
@@ -94,6 +94,13 @@ sub parse
 
     $self->process_data();
 
+    # TODO(TLS1.3): Replace this reference to draft version before release
+    if ($server_version == TLSProxy::Record::VERS_TLS_1_3_DRAFT) {
+        TLSProxy::Record->server_encrypting(1);
+        TLSProxy::Record->client_encrypting(1);
+        TLSProxy::Proxy->is_tls13(1);
+    }
+
     print "    Server Version:".$server_version."\n";
     print "    Session ID Len:".$session_id_len."\n";
     print "    Ciphersuite:".$ciphersuite."\n";


More information about the openssl-commits mailing list