[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Thu Dec 14 15:16:24 UTC 2017


The branch master has been updated
       via  0ababfec93337a1fb8160a75df3ccc227faa72d4 (commit)
       via  091ecfad6e3f157a875f0dec2d913c0c992bf1d9 (commit)
       via  0ca3aea7d359144681d591eb53961ec915c8de8e (commit)
       via  758e05c52ec5acb133647e69495812269ad67525 (commit)
       via  318d3c0e62d0de511b8721e087278a6c70db8b92 (commit)
       via  75259b4346a1a786b4a23987123b18b674327b8d (commit)
       via  5cc807da2571c52cc7c1c87197a81963def7ee3a (commit)
       via  7b0a3ce0f9f54cf7b527fe57d98748a7aaa571bd (commit)
       via  426dfc9ff7c1afaf1ed5981a9c7846e310c7ae3e (commit)
       via  e7dd763e513f576b8e4e32bb5d08abc37bb08a40 (commit)
       via  be60b10a80663d7af6e87d53f908e58d63c54d95 (commit)
       via  fc7129dc37f38022382338cf37cee795d975450f (commit)
       via  6f40214f68d06820304e6f9a4c60099a1fbce10c (commit)
       via  597c51bc980ba6d7470dd8de747ac12a6c7a442b (commit)
       via  db37d32cb89160328b0ba48e3808f601a7b3ebe8 (commit)
       via  066904cceef26bbb5c63c237d20829fb0db82ddc (commit)
       via  4d02f8706381bf2bd002951daef9b26d9ed85968 (commit)
       via  fa9f9350f3d22168ebc53b72ad042b714e4cb691 (commit)
       via  fdd9236747d7b843f1f5644b3e95580b80d9a598 (commit)
       via  2d729db2f0c047e64c580342f6fba0d99b2ada50 (commit)
       via  a5816a5ab99610201dcec57a0e02b883d9d32891 (commit)
       via  88050dd1960bfaba7ede12a3ce1afe40f5deb124 (commit)
       via  86b165e39fa94d4eceb9bb1611350b949fea7cc9 (commit)
      from  f90852093f149ae942a77c2c27d2a61888cff8e9 (commit)


- Log -----------------------------------------------------------------
commit 0ababfec93337a1fb8160a75df3ccc227faa72d4
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 30 17:55:34 2017 +0000

    Fix some clang compilation errors
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 091ecfad6e3f157a875f0dec2d913c0c992bf1d9
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 30 17:55:06 2017 +0000

    Don't run the TLSv1.3 CCS tests if TLSv1.3 is not enabled
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 0ca3aea7d359144681d591eb53961ec915c8de8e
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 30 10:13:13 2017 +0000

    Add some TLSv1.3 CCS tests
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 758e05c52ec5acb133647e69495812269ad67525
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 30 15:49:08 2017 +0000

    Make sure we treat records written after HRR as TLSv1.3
    
    This fixes a bug where some CCS records were written with the wrong TLS
    record version.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 318d3c0e62d0de511b8721e087278a6c70db8b92
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 30 14:33:22 2017 +0000

    Issue a CCS from the client if we received an HRR
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 75259b4346a1a786b4a23987123b18b674327b8d
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 30 14:29:28 2017 +0000

    Fix server side HRR flushing
    
    Flush following the CCS after an HRR. Only flush the HRR if middlebox
    compat is turned off.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 5cc807da2571c52cc7c1c87197a81963def7ee3a
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 30 11:28:26 2017 +0000

    Delay flush until after CCS with early_data
    
    Normally we flush immediately after writing the ClientHello. However if
    we are going to write a CCS immediately because we've got early_data to
    come, then we should move the flush until after the CCS.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 7b0a3ce0f9f54cf7b527fe57d98748a7aaa571bd
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Nov 13 16:12:35 2017 +0000

    Ensure CCS sent before early_data has the correct record version
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 426dfc9ff7c1afaf1ed5981a9c7846e310c7ae3e
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Dec 5 10:16:25 2017 +0000

    Send supported_versions in an HRR
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit e7dd763e513f576b8e4e32bb5d08abc37bb08a40
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Nov 13 15:01:07 2017 +0000

    Make sure supported_versions appears in an HRR too
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit be60b10a80663d7af6e87d53f908e58d63c54d95
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Nov 13 14:40:46 2017 +0000

    Update TLSProxy to know about new HRR style
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit fc7129dc37f38022382338cf37cee795d975450f
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Nov 13 11:24:51 2017 +0000

    Update state machine to send CCS based on whether we did an HRR
    
    The CCS may be sent at different times based on whether or not we
    sent an HRR earlier. In order to make that decision this commit
    also updates things to make sure we remember whether an HRR was
    used or not.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 6f40214f68d06820304e6f9a4c60099a1fbce10c
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Nov 9 16:03:40 2017 +0000

    Fix an HRR bug
    
    Ensure that after an HRR we can only negotiate TLSv1.3
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 597c51bc980ba6d7470dd8de747ac12a6c7a442b
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Dec 5 10:14:35 2017 +0000

    Merge HRR into ServerHello
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit db37d32cb89160328b0ba48e3808f601a7b3ebe8
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Nov 8 15:00:48 2017 +0000

    Send a CCS after ServerHello in TLSv1.3 if using middlebox compat mode
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 066904cceef26bbb5c63c237d20829fb0db82ddc
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Nov 8 14:26:48 2017 +0000

    Send a CCS from a client in an early_data handshake
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 4d02f8706381bf2bd002951daef9b26d9ed85968
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Nov 8 11:37:12 2017 +0000

    Send a CCS from the client in a non-early_data handshake
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit fa9f9350f3d22168ebc53b72ad042b714e4cb691
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Nov 8 11:18:00 2017 +0000

    Remove TLSv1.3 specific write transition for ClientHello
    
    Since we no longer do version negotiation during the processing of an HRR
    we do not need the TLSv1.3 specific write transition for ClientHello
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit fdd9236747d7b843f1f5644b3e95580b80d9a598
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 7 16:36:51 2017 +0000

    Drop CCS messages received in the TLSv1.3 handshake
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 2d729db2f0c047e64c580342f6fba0d99b2ada50
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 7 16:04:35 2017 +0000

    Send TLSv1.2 as the record version when using TLSv1.3
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit a5816a5ab99610201dcec57a0e02b883d9d32891
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Nov 7 10:45:43 2017 +0000

    Implement session id TLSv1.3 middlebox compatibility mode
    
    Clients will send a "fake" session id and servers must echo it back.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 88050dd1960bfaba7ede12a3ce1afe40f5deb124
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Nov 3 16:38:48 2017 +0000

    Update ServerHello to new draft-22 format
    
    The new ServerHello format is essentially now the same as the old TLSv1.2
    one, but it must additionally include supported_versions. The version
    field is fixed at TLSv1.2, and the version negotiation happens solely via
    supported_versions.
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

commit 86b165e39fa94d4eceb9bb1611350b949fea7cc9
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Nov 3 11:26:29 2017 +0000

    Update the TLSv1.3 draft version indicators to draft 22
    
    Reviewed-by: Ben Kaduk <kaduk at mit.edu>
    (Merged from https://github.com/openssl/openssl/pull/4701)

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

Summary of changes:
 apps/apps.h                                        |   8 +-
 apps/s_cb.c                                        |   1 -
 crypto/err/openssl.txt                             |   8 +
 include/openssl/ssl.h                              |  10 +-
 include/openssl/ssl3.h                             |   1 -
 include/openssl/sslerr.h                           |   7 +
 include/openssl/tls1.h                             |   6 +-
 ssl/record/rec_layer_s3.c                          |   3 +-
 ssl/record/ssl3_record.c                           |  39 +-
 ssl/ssl_conf.c                                     |   6 +-
 ssl/ssl_err.c                                      |  12 +
 ssl/ssl_lib.c                                      |   6 +-
 ssl/ssl_locl.h                                     |  22 +-
 ssl/ssl_stat.c                                     |   8 -
 ssl/statem/extensions.c                            |  60 ++-
 ssl/statem/extensions_clnt.c                       |  69 ++-
 ssl/statem/extensions_srvr.c                       |  25 +-
 ssl/statem/statem_clnt.c                           | 347 +++++++++------
 ssl/statem/statem_lib.c                            | 116 +++--
 ssl/statem/statem_locl.h                           |   9 +
 ssl/statem/statem_srvr.c                           | 164 +++----
 ssl/t1_trce.c                                      |  45 +-
 test/asynciotest.c                                 |  42 +-
 test/build.info                                    |   6 +-
 test/clienthellotest.c                             |   2 +
 test/recipes/70-test_key_share.t                   |  14 +-
 test/recipes/70-test_sslrecords.t                  |   3 +-
 test/recipes/70-test_sslversions.t                 |   4 +-
 test/recipes/70-test_tls13cookie.t                 |   2 +-
 test/recipes/70-test_tls13kexmodes.t               |   8 +-
 test/recipes/70-test_tls13messages.t               |  10 +-
 ...0-test_tls13encryption.t => 90-test_tls13ccs.t} |  10 +-
 test/tls13ccstest.c                                | 493 +++++++++++++++++++++
 util/perl/TLSProxy/HelloRetryRequest.pm            | 150 -------
 util/perl/TLSProxy/Message.pm                      |  21 +-
 util/perl/TLSProxy/Proxy.pm                        |   1 -
 util/perl/TLSProxy/Record.pm                       |  28 +-
 util/perl/TLSProxy/ServerHello.pm                  |  58 +--
 util/perl/checkhandshake.pm                        |  68 ++-
 39 files changed, 1299 insertions(+), 593 deletions(-)
 copy test/recipes/{90-test_tls13encryption.t => 90-test_tls13ccs.t} (60%)
 create mode 100644 test/tls13ccstest.c
 delete mode 100644 util/perl/TLSProxy/HelloRetryRequest.pm

diff --git a/apps/apps.h b/apps/apps.h
index bb89eae..321f644 100644
--- a/apps/apps.h
+++ b/apps/apps.h
@@ -208,7 +208,7 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate,
         OPT_S_STRICT, OPT_S_SIGALGS, OPT_S_CLIENTSIGALGS, OPT_S_GROUPS, \
         OPT_S_CURVES, OPT_S_NAMEDCURVE, OPT_S_CIPHER, \
         OPT_S_RECORD_PADDING, OPT_S_DEBUGBROKE, OPT_S_COMP, \
-        OPT_S_NO_RENEGOTIATION, OPT_S__LAST
+        OPT_S_NO_RENEGOTIATION, OPT_S_NO_MIDDLEBOX, OPT_S__LAST
 
 # define OPT_S_OPTIONS \
         {"no_ssl3", OPT_S_NOSSL3, '-',"Just disable SSLv3" }, \
@@ -253,7 +253,8 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate,
         {"record_padding", OPT_S_RECORD_PADDING, 's', \
             "Block size to pad TLS 1.3 records to."}, \
         {"debug_broken_protocol", OPT_S_DEBUGBROKE, '-', \
-            "Perform all sorts of protocol violations for testing purposes"}
+            "Perform all sorts of protocol violations for testing purposes"}, \
+        {"no_middlebox", OPT_S_NO_MIDDLEBOX, '-', "Disable TLSv1.3 middlebox compat mode" }
 
 
 # define OPT_S_CASES \
@@ -283,7 +284,8 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate,
         case OPT_S_CIPHER: \
         case OPT_S_RECORD_PADDING: \
         case OPT_S_NO_RENEGOTIATION: \
-        case OPT_S_DEBUGBROKE
+        case OPT_S_DEBUGBROKE: \
+        case OPT_S_NO_MIDDLEBOX
 
 #define IS_NO_PROT_FLAG(o) \
  (o == OPT_S_NOSSL3 || o == OPT_S_NOTLS1 || o == OPT_S_NOTLS1_1 \
diff --git a/apps/s_cb.c b/apps/s_cb.c
index 79d2919..c7c9ecb 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -525,7 +525,6 @@ static STRINT_PAIR handshakes[] = {
     {", HelloVerifyRequest", DTLS1_MT_HELLO_VERIFY_REQUEST},
     {", NewSessionTicket", SSL3_MT_NEWSESSION_TICKET},
     {", EndOfEarlyData", SSL3_MT_END_OF_EARLY_DATA},
-    {", HelloRetryRequest", SSL3_MT_HELLO_RETRY_REQUEST},
     {", EncryptedExtensions", SSL3_MT_ENCRYPTED_EXTENSIONS},
     {", Certificate", SSL3_MT_CERTIFICATE},
     {", ServerKeyExchange", SSL3_MT_SERVER_KEY_EXCHANGE},
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 308abae..872016f 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1292,6 +1292,8 @@ SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET:460:tls_construct_stoc_session_ticket
 SSL_F_TLS_CONSTRUCT_STOC_STATUS_REQUEST:461:tls_construct_stoc_status_request
 SSL_F_TLS_CONSTRUCT_STOC_SUPPORTED_GROUPS:544:\
 	tls_construct_stoc_supported_groups
+SSL_F_TLS_CONSTRUCT_STOC_SUPPORTED_VERSIONS:608:\
+	tls_construct_stoc_supported_versions
 SSL_F_TLS_CONSTRUCT_STOC_USE_SRTP:462:tls_construct_stoc_use_srtp
 SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO:521:\
 	tls_early_post_process_client_hello
@@ -1332,11 +1334,13 @@ SSL_F_TLS_PARSE_STOC_SCT:564:tls_parse_stoc_sct
 SSL_F_TLS_PARSE_STOC_SERVER_NAME:583:tls_parse_stoc_server_name
 SSL_F_TLS_PARSE_STOC_SESSION_TICKET:584:tls_parse_stoc_session_ticket
 SSL_F_TLS_PARSE_STOC_STATUS_REQUEST:585:tls_parse_stoc_status_request
+SSL_F_TLS_PARSE_STOC_SUPPORTED_VERSIONS:609:tls_parse_stoc_supported_versions
 SSL_F_TLS_PARSE_STOC_USE_SRTP:446:tls_parse_stoc_use_srtp
 SSL_F_TLS_POST_PROCESS_CLIENT_HELLO:378:tls_post_process_client_hello
 SSL_F_TLS_POST_PROCESS_CLIENT_KEY_EXCHANGE:384:\
 	tls_post_process_client_key_exchange
 SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE:360:tls_prepare_client_certificate
+SSL_F_TLS_PROCESS_AS_HELLO_RETRY_REQUEST:610:tls_process_as_hello_retry_request
 SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST:361:tls_process_certificate_request
 SSL_F_TLS_PROCESS_CERT_STATUS:362:*
 SSL_F_TLS_PROCESS_CERT_STATUS_BODY:495:tls_process_cert_status_body
@@ -2357,6 +2361,7 @@ SSL_R_BAD_EXTENSION:110:bad extension
 SSL_R_BAD_HANDSHAKE_LENGTH:332:bad handshake length
 SSL_R_BAD_HANDSHAKE_STATE:236:bad handshake state
 SSL_R_BAD_HELLO_REQUEST:105:bad hello request
+SSL_R_BAD_HRR_VERSION:263:bad hrr version
 SSL_R_BAD_KEY_SHARE:108:bad key share
 SSL_R_BAD_KEY_UPDATE:122:bad key update
 SSL_R_BAD_LENGTH:271:bad length
@@ -2450,6 +2455,7 @@ SSL_R_INCONSISTENT_EARLY_DATA_SNI:231:inconsistent early data sni
 SSL_R_INCONSISTENT_EXTMS:104:inconsistent extms
 SSL_R_INSUFFICIENT_SECURITY:241:insufficient security
 SSL_R_INVALID_ALERT:205:invalid alert
+SSL_R_INVALID_CCS_MESSAGE:260:invalid ccs message
 SSL_R_INVALID_CERTIFICATE_OR_ALG:238:invalid certificate or alg
 SSL_R_INVALID_COMMAND:280:invalid command
 SSL_R_INVALID_COMPRESSION_ALGORITHM:341:invalid compression algorithm
@@ -2460,6 +2466,7 @@ SSL_R_INVALID_MAX_EARLY_DATA:174:invalid max early data
 SSL_R_INVALID_NULL_CMD_NAME:385:invalid null cmd name
 SSL_R_INVALID_SEQUENCE_NUMBER:402:invalid sequence number
 SSL_R_INVALID_SERVERINFO_DATA:388:invalid serverinfo data
+SSL_R_INVALID_SESSION_ID:999:invalid session id
 SSL_R_INVALID_SRP_USERNAME:357:invalid srp username
 SSL_R_INVALID_STATUS_RESPONSE:328:invalid status response
 SSL_R_INVALID_TICKET_KEYS_LENGTH:325:invalid ticket keys length
@@ -2579,6 +2586,7 @@ SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS:239:\
 	unable to find public key parameters
 SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES:242:unable to load ssl3 md5 routines
 SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES:243:unable to load ssl3 sha1 routines
+SSL_R_UNEXPECTED_CCS_MESSAGE:262:unexpected ccs message
 SSL_R_UNEXPECTED_END_OF_EARLY_DATA:178:unexpected end of early data
 SSL_R_UNEXPECTED_MESSAGE:244:unexpected message
 SSL_R_UNEXPECTED_RECORD:245:unexpected record
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index a5251b5..98a106b 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -338,9 +338,17 @@ typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);
 # define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION        0x00040000U
 /* Disable encrypt-then-mac */
 # define SSL_OP_NO_ENCRYPT_THEN_MAC                      0x00080000U
+
+/*
+ * Enable TLSv1.3 Compatibility mode. This is on by default. A future version
+ * of OpenSSL may have this disabled by default.
+ */
+# define SSL_OP_ENABLE_MIDDLEBOX_COMPAT                  0x00100000U
+
 /* Prioritize Chacha20Poly1305 when client does.
  * Modifies SSL_OP_CIPHER_SERVER_PREFERENCE */
 # define SSL_OP_PRIORITIZE_CHACHA                        0x00200000U
+
 /*
  * Set on servers to choose the cipher according to the server's preferences
  */
@@ -979,8 +987,6 @@ typedef enum {
     TLS_ST_CR_CERT_VRFY,
     TLS_ST_SW_CERT_VRFY,
     TLS_ST_CR_HELLO_REQ,
-    TLS_ST_SW_HELLO_RETRY_REQUEST,
-    TLS_ST_CR_HELLO_RETRY_REQUEST,
     TLS_ST_SW_KEY_UPDATE,
     TLS_ST_CW_KEY_UPDATE,
     TLS_ST_SR_KEY_UPDATE,
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index e9d56a8..b781f61 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -289,7 +289,6 @@ extern "C" {
 # define SSL3_MT_SERVER_HELLO                    2
 # define SSL3_MT_NEWSESSION_TICKET               4
 # define SSL3_MT_END_OF_EARLY_DATA               5
-# define SSL3_MT_HELLO_RETRY_REQUEST             6
 # define SSL3_MT_ENCRYPTED_EXTENSIONS            8
 # define SSL3_MT_CERTIFICATE                     11
 # define SSL3_MT_SERVER_KEY_EXCHANGE             12
diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h
index b54459b..2986be8 100644
--- a/include/openssl/sslerr.h
+++ b/include/openssl/sslerr.h
@@ -340,6 +340,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET          460
 # define SSL_F_TLS_CONSTRUCT_STOC_STATUS_REQUEST          461
 # define SSL_F_TLS_CONSTRUCT_STOC_SUPPORTED_GROUPS        544
+# define SSL_F_TLS_CONSTRUCT_STOC_SUPPORTED_VERSIONS      608
 # define SSL_F_TLS_CONSTRUCT_STOC_USE_SRTP                462
 # define SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO        521
 # define SSL_F_TLS_FINISH_HANDSHAKE                       597
@@ -379,10 +380,12 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_PARSE_STOC_SERVER_NAME                 583
 # define SSL_F_TLS_PARSE_STOC_SESSION_TICKET              584
 # define SSL_F_TLS_PARSE_STOC_STATUS_REQUEST              585
+# define SSL_F_TLS_PARSE_STOC_SUPPORTED_VERSIONS          609
 # define SSL_F_TLS_PARSE_STOC_USE_SRTP                    446
 # define SSL_F_TLS_POST_PROCESS_CLIENT_HELLO              378
 # define SSL_F_TLS_POST_PROCESS_CLIENT_KEY_EXCHANGE       384
 # define SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE             360
+# define SSL_F_TLS_PROCESS_AS_HELLO_RETRY_REQUEST         610
 # 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
@@ -441,6 +444,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_BAD_HANDSHAKE_LENGTH                       332
 # define SSL_R_BAD_HANDSHAKE_STATE                        236
 # define SSL_R_BAD_HELLO_REQUEST                          105
+# define SSL_R_BAD_HRR_VERSION                            263
 # define SSL_R_BAD_KEY_SHARE                              108
 # define SSL_R_BAD_KEY_UPDATE                             122
 # define SSL_R_BAD_LENGTH                                 271
@@ -531,6 +535,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_INCONSISTENT_EXTMS                         104
 # define SSL_R_INSUFFICIENT_SECURITY                      241
 # define SSL_R_INVALID_ALERT                              205
+# define SSL_R_INVALID_CCS_MESSAGE                        260
 # define SSL_R_INVALID_CERTIFICATE_OR_ALG                 238
 # define SSL_R_INVALID_COMMAND                            280
 # define SSL_R_INVALID_COMPRESSION_ALGORITHM              341
@@ -541,6 +546,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_INVALID_NULL_CMD_NAME                      385
 # define SSL_R_INVALID_SEQUENCE_NUMBER                    402
 # define SSL_R_INVALID_SERVERINFO_DATA                    388
+# define SSL_R_INVALID_SESSION_ID                         999
 # define SSL_R_INVALID_SRP_USERNAME                       357
 # define SSL_R_INVALID_STATUS_RESPONSE                    328
 # define SSL_R_INVALID_TICKET_KEYS_LENGTH                 325
@@ -682,6 +688,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS       239
 # define SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES           242
 # define SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES          243
+# define SSL_R_UNEXPECTED_CCS_MESSAGE                     262
 # define SSL_R_UNEXPECTED_END_OF_EARLY_DATA               178
 # define SSL_R_UNEXPECTED_MESSAGE                         244
 # define SSL_R_UNEXPECTED_RECORD                          245
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index d114fb5..8fc1b49 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -30,9 +30,9 @@ extern "C" {
 # define TLS1_3_VERSION                  0x0304
 # define TLS_MAX_VERSION                 TLS1_3_VERSION
 
-/* TODO(TLS1.3) REMOVE ME: Version indicator for draft -21 */
-# define TLS1_3_VERSION_DRAFT            0x7f15
-# define TLS1_3_VERSION_DRAFT_TXT        "TLS 1.3 (draft 21)"
+/* TODO(TLS1.3) REMOVE ME: Version indicator for draft -22 */
+# define TLS1_3_VERSION_DRAFT            0x7f16
+# define TLS1_3_VERSION_DRAFT_TXT        "TLS 1.3 (draft 22)"
 
 /* Special value for method supporting multiple versions */
 # define TLS_ANY_VERSION                 0x10000
diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c
index 5f01b04..5b0d2d6 100644
--- a/ssl/record/rec_layer_s3.c
+++ b/ssl/record/rec_layer_s3.c
@@ -816,7 +816,8 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
     /* Clear our SSL3_RECORD structures */
     memset(wr, 0, sizeof(wr));
     for (j = 0; j < numpipes; j++) {
-        unsigned int version = SSL_TREAT_AS_TLS13(s) ? TLS1_VERSION : s->version;
+        unsigned int version = SSL_TREAT_AS_TLS13(s) ? TLS1_2_VERSION
+                                                     : s->version;
         unsigned char *compressdata = NULL;
         size_t maxcomplen;
         unsigned int rectype;
diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c
index 213f001..28ee2cc 100644
--- a/ssl/record/ssl3_record.c
+++ b/ssl/record/ssl3_record.c
@@ -276,7 +276,7 @@ int ssl3_get_record(SSL *s)
                  * that explicitly
                  */
                 if (!s->first_packet && !SSL_IS_TLS13(s)
-                        && !s->hello_retry_request
+                        && s->hello_retry_request != SSL_HRR_PENDING
                         && version != (unsigned int)s->version) {
                     if ((s->version & 0xFF00) == (version & 0xFF00)
                         && !s->enc_write_ctx && !s->write_hash) {
@@ -333,8 +333,11 @@ int ssl3_get_record(SSL *s)
                     }
                 }
 
-                if (SSL_IS_TLS13(s) && s->enc_read_ctx != NULL
-                        && thisrr->type != SSL3_RT_APPLICATION_DATA) {
+                if (SSL_IS_TLS13(s)
+                        && s->enc_read_ctx != NULL
+                        && thisrr->type != SSL3_RT_APPLICATION_DATA
+                        && (thisrr->type != SSL3_RT_CHANGE_CIPHER_SPEC
+                            || !SSL_IS_FIRST_HANDSHAKE(s))) {
                     SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE,
                              SSL_F_SSL3_GET_RECORD, SSL_R_BAD_RECORD_TYPE);
                     return -1;
@@ -444,6 +447,36 @@ int ssl3_get_record(SSL *s)
                  & EVP_CIPH_FLAG_PIPELINE)
              && ssl3_record_app_data_waiting(s));
 
+    if (num_recs == 1
+            && thisrr->type == SSL3_RT_CHANGE_CIPHER_SPEC
+            && (SSL_IS_TLS13(s) || s->hello_retry_request != SSL_HRR_NONE)
+            && SSL_IS_FIRST_HANDSHAKE(s)) {
+        /*
+         * CCS messages must be exactly 1 byte long, containing the value 0x01
+         */
+        if (thisrr->length != 1 || thisrr->data[0] != 0x01) {
+            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_SSL3_GET_RECORD,
+                     SSL_R_INVALID_CCS_MESSAGE);
+            return -1;
+        }
+        /*
+         * CCS messages are ignored in TLSv1.3. We treat it like an empty
+         * handshake record
+         */
+        thisrr->type = SSL3_RT_HANDSHAKE;
+        RECORD_LAYER_inc_empty_record_count(&s->rlayer);
+        if (RECORD_LAYER_get_empty_record_count(&s->rlayer)
+            > MAX_EMPTY_RECORDS) {
+            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_SSL3_GET_RECORD,
+                     SSL_R_UNEXPECTED_CCS_MESSAGE);
+            return -1;
+        }
+        thisrr->read = 1;
+        RECORD_LAYER_set_numrpipes(&s->rlayer, 1);
+
+        return 1;
+    }
+
     /*
      * If in encrypt-then-mac mode calculate mac from encrypted record. All
      * the details below are public so no timing details can leak.
diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c
index fe090ae..0cd8ace 100644
--- a/ssl/ssl_conf.c
+++ b/ssl/ssl_conf.c
@@ -369,7 +369,8 @@ static int cmd_Options(SSL_CONF_CTX *cctx, const char *value)
         SSL_FLAG_TBL_INV("EncryptThenMac", SSL_OP_NO_ENCRYPT_THEN_MAC),
         SSL_FLAG_TBL("NoRenegotiation", SSL_OP_NO_RENEGOTIATION),
         SSL_FLAG_TBL("AllowNoDHEKEX", SSL_OP_ALLOW_NO_DHE_KEX),
-        SSL_FLAG_TBL("PrioritizeChaCha", SSL_OP_PRIORITIZE_CHACHA)
+        SSL_FLAG_TBL("PrioritizeChaCha", SSL_OP_PRIORITIZE_CHACHA),
+        SSL_FLAG_TBL("MiddleboxCompat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT)
     };
     if (value == NULL)
         return -3;
@@ -591,6 +592,7 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
     SSL_CONF_CMD_SWITCH("allow_no_dhe_kex", 0),
     SSL_CONF_CMD_SWITCH("prioritize_chacha", SSL_CONF_FLAG_SERVER),
     SSL_CONF_CMD_SWITCH("strict", 0),
+    SSL_CONF_CMD_SWITCH("no_middlebox", 0),
     SSL_CONF_CMD_STRING(SignatureAlgorithms, "sigalgs", 0),
     SSL_CONF_CMD_STRING(ClientSignatureAlgorithms, "client_sigalgs", 0),
     SSL_CONF_CMD_STRING(Curves, "curves", 0),
@@ -665,6 +667,8 @@ static const ssl_switch_tbl ssl_cmd_switches[] = {
     /* chacha reprioritization */
     {SSL_OP_PRIORITIZE_CHACHA, 0},
     {SSL_CERT_FLAG_TLS_STRICT, SSL_TFLAG_CERT}, /* strict */
+    /* no_middlebox */
+    {SSL_OP_ENABLE_MIDDLEBOX_COMPAT, SSL_TFLAG_INV},
 };
 
 static int ssl_conf_cmd_skip_prefix(SSL_CONF_CTX *cctx, const char **pcmd)
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 7b201e0..1e3eb2c 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -524,6 +524,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
      "tls_construct_stoc_status_request"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_SUPPORTED_GROUPS, 0),
      "tls_construct_stoc_supported_groups"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_SUPPORTED_VERSIONS, 0),
+     "tls_construct_stoc_supported_versions"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_USE_SRTP, 0),
      "tls_construct_stoc_use_srtp"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, 0),
@@ -593,6 +595,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
      "tls_parse_stoc_session_ticket"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_STATUS_REQUEST, 0),
      "tls_parse_stoc_status_request"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_SUPPORTED_VERSIONS, 0),
+     "tls_parse_stoc_supported_versions"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_USE_SRTP, 0),
      "tls_parse_stoc_use_srtp"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_POST_PROCESS_CLIENT_HELLO, 0),
@@ -601,6 +605,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
      "tls_post_process_client_key_exchange"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PREPARE_CLIENT_CERTIFICATE, 0),
      "tls_prepare_client_certificate"},
+    {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PROCESS_AS_HELLO_RETRY_REQUEST, 0),
+     "tls_process_as_hello_retry_request"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PROCESS_CERTIFICATE_REQUEST, 0),
      "tls_process_certificate_request"},
     {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PROCESS_CERT_STATUS, 0), ""},
@@ -700,6 +706,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HANDSHAKE_STATE),
     "bad handshake state"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HELLO_REQUEST), "bad hello request"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HRR_VERSION), "bad hrr version"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_KEY_SHARE), "bad key share"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_KEY_UPDATE), "bad key update"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_LENGTH), "bad length"},
@@ -848,6 +855,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INSUFFICIENT_SECURITY),
     "insufficient security"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_ALERT), "invalid alert"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CCS_MESSAGE),
+    "invalid ccs message"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CERTIFICATE_OR_ALG),
     "invalid certificate or alg"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMMAND), "invalid command"},
@@ -867,6 +876,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     "invalid sequence number"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SERVERINFO_DATA),
     "invalid serverinfo data"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SESSION_ID), "invalid session id"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SRP_USERNAME),
     "invalid srp username"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_STATUS_RESPONSE),
@@ -1120,6 +1130,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = {
     "unable to load ssl3 md5 routines"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES),
     "unable to load ssl3 sha1 routines"},
+    {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_CCS_MESSAGE),
+    "unexpected ccs message"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_END_OF_EARLY_DATA),
     "unexpected end of early data"},
     {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_MESSAGE), "unexpected message"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 2007318..bba0291 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2894,9 +2894,11 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
      * Disable compression by default to prevent CRIME. Applications can
      * re-enable compression by configuring
      * SSL_CTX_clear_options(ctx, SSL_OP_NO_COMPRESSION);
-     * or by using the SSL_CONF library.
+     * or by using the SSL_CONF library. Similarly we also enable TLSv1.3
+     * middlebox compatibility by default. This may be disabled by default in
+     * a later OpenSSL version.
      */
-    ret->options |= SSL_OP_NO_COMPRESSION;
+    ret->options |= SSL_OP_NO_COMPRESSION | SSL_OP_ENABLE_MIDDLEBOX_COMPAT;
 
     ret->ext.status_type = TLSEXT_STATUSTYPE_nothing;
 
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 952a8f9..eec5be3 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -323,10 +323,14 @@
                           && (s)->method->version != TLS_ANY_VERSION)
 
 # define SSL_TREAT_AS_TLS13(s) \
-    (SSL_IS_TLS13(s) || (s)->early_data_state == SSL_EARLY_DATA_WRITING \
-     || (s)->early_data_state == SSL_EARLY_DATA_WRITE_RETRY)
+    (SSL_IS_TLS13(s) || (s)->early_data_state == SSL_EARLY_DATA_CONNECTING \
+     || (s)->early_data_state == SSL_EARLY_DATA_CONNECT_RETRY \
+     || (s)->early_data_state == SSL_EARLY_DATA_WRITING \
+     || (s)->early_data_state == SSL_EARLY_DATA_WRITE_RETRY \
+     || (s)->hello_retry_request == SSL_HRR_PENDING)
 
-# define SSL_IS_FIRST_HANDSHAKE(S) ((s)->s3->tmp.finish_md_len == 0)
+# define SSL_IS_FIRST_HANDSHAKE(S) ((s)->s3->tmp.finish_md_len == 0 \
+                                    || (s)->s3->tmp.peer_finish_md_len == 0)
 
 /* See if we need explicit IV */
 # define SSL_USE_EXPLICIT_IV(s)  \
@@ -1116,7 +1120,8 @@ struct ssl_st {
     size_t cert_verify_hash_len;
 
     /* Flag to indicate whether we should send a HelloRetryRequest or not */
-    int hello_retry_request;
+    enum {SSL_HRR_NONE = 0, SSL_HRR_PENDING, SSL_HRR_COMPLETE}
+        hello_retry_request;
 
     /*
      * the session_id_context is used to ensure sessions are only reused in
@@ -1132,6 +1137,12 @@ struct ssl_st {
     size_t psksession_id_len;
     /* Default generate session ID callback. */
     GEN_SESSION_CB generate_session_id;
+    /*
+     * The temporary TLSv1.3 session id. This isn't really a session id at all
+     * but is a random value sent in the legacy session id field.
+     */
+    unsigned char tmp_session_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
+    size_t tmp_session_id_len;
     /* Used in SSL3 */
     /*
      * 0 don't care about verify failure.
@@ -2266,7 +2277,8 @@ __owur int ssl_check_version_downgrade(SSL *s);
 __owur int ssl_set_version_bound(int method_version, int version, int *bound);
 __owur int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello,
                                      DOWNGRADE *dgrd);
-__owur int ssl_choose_client_version(SSL *s, int version, int checkdgrd);
+__owur int ssl_choose_client_version(SSL *s, int version,
+                                     RAW_EXTENSION *extensions);
 int ssl_get_min_max_version(const SSL *s, int *min_version, int *max_version);
 
 __owur long tls1_default_timeout(void);
diff --git a/ssl/ssl_stat.c b/ssl/ssl_stat.c
index 5dd71d2..179513b 100644
--- a/ssl/ssl_stat.c
+++ b/ssl/ssl_stat.c
@@ -97,10 +97,6 @@ const char *SSL_state_string_long(const SSL *s)
         return "TLSv1.3 write server certificate verify";
     case TLS_ST_CR_HELLO_REQ:
         return "SSLv3/TLS read hello request";
-    case TLS_ST_SW_HELLO_RETRY_REQUEST:
-        return "TLSv1.3 write hello retry request";
-    case TLS_ST_CR_HELLO_RETRY_REQUEST:
-        return "TLSv1.3 read hello retry request";
     case TLS_ST_SW_KEY_UPDATE:
         return "TLSv1.3 write server key update";
     case TLS_ST_CW_KEY_UPDATE:
@@ -208,10 +204,6 @@ const char *SSL_state_string(const SSL *s)
         return "TRSCV";
     case TLS_ST_CR_HELLO_REQ:
         return "TRHR";
-    case TLS_ST_SW_HELLO_RETRY_REQUEST:
-        return "TWHRR";
-    case TLS_ST_CR_HELLO_RETRY_REQUEST:
-        return "TRHRR";
     case TLS_ST_SW_KEY_UPDATE:
         return "TWSKU";
     case TLS_ST_CW_KEY_UPDATE:
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 464a5ef..28f7ada 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -9,6 +9,7 @@
 
 #include <string.h>
 #include "internal/nelem.h"
+#include "internal/cryptlib.h"
 #include "../ssl_locl.h"
 #include "statem_locl.h"
 
@@ -261,11 +262,14 @@ static const EXTENSION_DEFINITION ext_defs[] = {
     },
     {
         TLSEXT_TYPE_supported_versions,
-        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS_IMPLEMENTATION_ONLY
-        | SSL_EXT_TLS1_3_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_3_SERVER_HELLO | SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
+        | SSL_EXT_TLS_IMPLEMENTATION_ONLY,
         NULL,
         /* Processed inline as part of version selection */
-        NULL, NULL, NULL, tls_construct_ctos_supported_versions, NULL
+        NULL, tls_parse_stoc_supported_versions,
+        tls_construct_stoc_supported_versions,
+        tls_construct_ctos_supported_versions, NULL
     },
     {
         TLSEXT_TYPE_psk_kex_modes,
@@ -357,6 +361,44 @@ static int validate_context(SSL *s, unsigned int extctx, unsigned int thisctx)
     return 1;
 }
 
+int tls_validate_all_contexts(SSL *s, unsigned int thisctx, RAW_EXTENSION *exts)
+{
+    size_t i, num_exts, builtin_num = OSSL_NELEM(ext_defs), offset;
+    RAW_EXTENSION *thisext;
+    unsigned int context;
+    ENDPOINT role = ENDPOINT_BOTH;
+
+    if ((thisctx & SSL_EXT_CLIENT_HELLO) != 0)
+        role = ENDPOINT_SERVER;
+    else if ((thisctx & SSL_EXT_TLS1_2_SERVER_HELLO) != 0)
+        role = ENDPOINT_CLIENT;
+
+    /* Calculate the number of extensions in the extensions list */
+    num_exts = builtin_num + s->cert->custext.meths_count;
+
+    for (thisext = exts, i = 0; i < num_exts; i++, thisext++) {
+        if (!thisext->present)
+            continue;
+
+        if (i < builtin_num) {
+            context = ext_defs[i].context;
+        } else {
+            custom_ext_method *meth = NULL;
+
+            meth = custom_ext_find(&s->cert->custext, role, thisext->type,
+                                   &offset);
+            if (!ossl_assert(meth != NULL))
+                return 0;
+            context = meth->context;
+        }
+
+        if (!validate_context(s, context, thisctx))
+            return 0;
+    }
+
+    return 1;
+}
+
 /*
  * Verify whether we are allowed to use the extension |type| in the current
  * |context|. Returns 1 to indicate the extension is allowed or unknown or 0 to
@@ -1194,7 +1236,7 @@ static int final_key_share(SSL *s, unsigned int context, int sent)
      */
     if (s->server && s->s3->peer_tmp == NULL) {
         /* No suitable share */
-        if (s->hello_retry_request == 0 && sent
+        if (s->hello_retry_request == SSL_HRR_NONE && sent
                 && (!s->hit
                     || (s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE)
                        != 0)) {
@@ -1219,7 +1261,7 @@ static int final_key_share(SSL *s, unsigned int context, int sent)
             if (i < num_groups) {
                 /* A shared group exists so send a HelloRetryRequest */
                 s->s3->group_id = group_id;
-                s->hello_retry_request = 1;
+                s->hello_retry_request = SSL_HRR_PENDING;
                 return 1;
             }
         }
@@ -1234,8 +1276,8 @@ static int final_key_share(SSL *s, unsigned int context, int sent)
     }
 
     /* We have a key_share so don't send any more HelloRetryRequest messages */
-    if (s->server)
-        s->hello_retry_request = 0;
+    if (s->server && s->hello_retry_request == SSL_HRR_PENDING)
+        s->hello_retry_request = SSL_HRR_COMPLETE;
 
     /*
      * For a client side resumption with no key_share we need to generate
@@ -1364,7 +1406,7 @@ int tls_psk_do_binder(SSL *s, const EVP_MD *md, const unsigned char *msgstart,
      * following a HelloRetryRequest then this includes the hash of the first
      * ClientHello and the HelloRetryRequest itself.
      */
-    if (s->hello_retry_request) {
+    if (s->hello_retry_request == SSL_HRR_PENDING) {
         size_t hdatalen;
         void *hdata;
 
@@ -1475,7 +1517,7 @@ static int final_early_data(SSL *s, unsigned int context, int sent)
             || s->session->ext.tick_identity != 0
             || s->early_data_state != SSL_EARLY_DATA_ACCEPTING
             || !s->ext.early_data_ok
-            || s->hello_retry_request) {
+            || s->hello_retry_request != SSL_HRR_NONE) {
         s->ext.early_data = SSL_EARLY_DATA_REJECTED;
     } else {
         s->ext.early_data = SSL_EARLY_DATA_ACCEPTED;
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index b7ef54e..2b39459 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -507,6 +507,20 @@ EXT_RETURN tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt,
 {
     int currv, min_version, max_version, reason;
 
+    reason = ssl_get_min_max_version(s, &min_version, &max_version);
+    if (reason != 0) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                 SSL_F_TLS_CONSTRUCT_CTOS_SUPPORTED_VERSIONS, reason);
+        return EXT_RETURN_FAIL;
+    }
+
+    /*
+     * Don't include this if we can't negotiate TLSv1.3. We can do a straight
+     * comparison here because we will never be called in DTLS.
+     */
+    if (max_version < TLS1_3_VERSION)
+        return EXT_RETURN_NOT_SENT;
+
     if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_versions)
             || !WPACKET_start_sub_packet_u16(pkt)
             || !WPACKET_start_sub_packet_u8(pkt)) {
@@ -516,13 +530,6 @@ EXT_RETURN tls_construct_ctos_supported_versions(SSL *s, WPACKET *pkt,
         return EXT_RETURN_FAIL;
     }
 
-    reason = ssl_get_min_max_version(s, &min_version, &max_version);
-    if (reason != 0) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_CTOS_SUPPORTED_VERSIONS, reason);
-        return EXT_RETURN_FAIL;
-    }
-
     /*
      * TODO(TLS1.3): There is some discussion on the TLS list as to whether
      * we should include versions <TLS1.2. For the moment we do. To be
@@ -592,7 +599,7 @@ static int add_key_share(SSL *s, WPACKET *pkt, unsigned int curve_id)
     size_t encodedlen;
 
     if (s->s3->tmp.pkey != NULL) {
-        if (!ossl_assert(s->hello_retry_request)) {
+        if (!ossl_assert(s->hello_retry_request == SSL_HRR_PENDING)) {
             SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_ADD_KEY_SHARE,
                      ERR_R_INTERNAL_ERROR);
             return 0;
@@ -742,7 +749,7 @@ EXT_RETURN tls_construct_ctos_early_data(SSL *s, WPACKET *pkt,
     SSL_SESSION *edsess = NULL;
     const EVP_MD *handmd = NULL;
 
-    if (s->hello_retry_request)
+    if (s->hello_retry_request == SSL_HRR_PENDING)
         handmd = ssl_handshake_md(s);
 
     if (s->psk_use_session_cb != NULL
@@ -954,7 +961,7 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
             || (s->session->ext.ticklen == 0 && s->psksession == NULL))
         return EXT_RETURN_NOT_SENT;
 
-    if (s->hello_retry_request)
+    if (s->hello_retry_request == SSL_HRR_PENDING)
         handmd = ssl_handshake_md(s);
 
     if (s->session->ext.ticklen != 0) {
@@ -973,7 +980,7 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
             goto dopsksess;
         }
 
-        if (s->hello_retry_request && mdres != handmd) {
+        if (s->hello_retry_request == SSL_HRR_PENDING && mdres != handmd) {
             /*
              * Selected ciphersuite hash does not match the hash for the session
              * so we can't use it.
@@ -1037,7 +1044,7 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context,
             return EXT_RETURN_FAIL;
         }
 
-        if (s->hello_retry_request && mdpsk != handmd) {
+        if (s->hello_retry_request == SSL_HRR_PENDING && mdpsk != handmd) {
             /*
              * Selected ciphersuite hash does not match the hash for the PSK
              * session. This is an application bug.
@@ -1633,6 +1640,44 @@ int tls_parse_stoc_ems(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
     return 1;
 }
 
+int tls_parse_stoc_supported_versions(SSL *s, PACKET *pkt, unsigned int context,
+                                      X509 *x, size_t chainidx)
+{
+    unsigned int version;
+
+    if (!PACKET_get_net_2(pkt, &version)
+            || PACKET_remaining(pkt) != 0) {
+        SSLfatal(s, SSL_AD_DECODE_ERROR,
+                 SSL_F_TLS_PARSE_STOC_SUPPORTED_VERSIONS,
+                 SSL_R_LENGTH_MISMATCH);
+        return 0;
+    }
+
+    /* TODO(TLS1.3): Remove this before release */
+    if (version == TLS1_3_VERSION_DRAFT)
+        version = TLS1_3_VERSION;
+
+    /* We ignore this extension for HRRs except to sanity check it */
+    if (context == SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) {
+        /*
+         * The only protocol version we support which has an HRR message is
+         * TLSv1.3, therefore we shouldn't be getting an HRR for anything else.
+         */
+        if (version != TLS1_3_VERSION) {
+            SSLfatal(s, SSL_AD_PROTOCOL_VERSION,
+                     SSL_F_TLS_PARSE_STOC_SUPPORTED_VERSIONS,
+                     SSL_R_BAD_HRR_VERSION);
+            return 0;
+        }
+        return 1;
+    }
+
+    /* We just set it here. We validate it in ssl_choose_client_version */
+    s->version = version;
+
+    return 1;
+}
+
 int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                              size_t chainidx)
 {
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index b07376f..d34a7c5 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -704,7 +704,7 @@ int tls_parse_ctos_early_data(SSL *s, PACKET *pkt, unsigned int context,
         return 0;
     }
 
-    if (s->hello_retry_request) {
+    if (s->hello_retry_request != SSL_HRR_NONE) {
         SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
                  SSL_F_TLS_PARSE_CTOS_EARLY_DATA, SSL_R_BAD_EXTENSION);
         return 0;
@@ -1213,6 +1213,27 @@ EXT_RETURN tls_construct_stoc_ems(SSL *s, WPACKET *pkt, unsigned int context,
     return EXT_RETURN_SENT;
 }
 
+EXT_RETURN tls_construct_stoc_supported_versions(SSL *s, WPACKET *pkt,
+                                                 unsigned int context, X509 *x,
+                                                 size_t chainidx)
+{
+    if (!SSL_IS_TLS13(s))
+        return EXT_RETURN_NOT_SENT;
+
+    if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_versions)
+            || !WPACKET_start_sub_packet_u16(pkt)
+                /* TODO(TLS1.3): Update to remove the TLSv1.3 draft indicator */
+            || !WPACKET_put_bytes_u16(pkt, TLS1_3_VERSION_DRAFT)
+            || !WPACKET_close(pkt)) {
+        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                 SSL_F_TLS_CONSTRUCT_STOC_SUPPORTED_VERSIONS,
+                 ERR_R_INTERNAL_ERROR);
+        return EXT_RETURN_FAIL;
+    }
+
+    return EXT_RETURN_SENT;
+}
+
 EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt,
                                         unsigned int context, X509 *x,
                                         size_t chainidx)
@@ -1224,7 +1245,7 @@ EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt,
 
     if (ckey == NULL) {
         /* No key_share received from client */
-        if (s->hello_retry_request) {
+        if (s->hello_retry_request == SSL_HRR_PENDING) {
             if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share)
                     || !WPACKET_start_sub_packet_u16(pkt)
                     || !WPACKET_put_bytes_u16(pkt, s->s3->group_id)
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index a7a5a17..51cdd58 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -22,7 +22,7 @@
 #include <openssl/bn.h>
 #include <openssl/engine.h>
 
-static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt);
+static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL *s, PACKET *pkt);
 static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL *s, PACKET *pkt);
 
 static ossl_inline int cert_req_allowed(SSL *s);
@@ -206,11 +206,6 @@ int ossl_statem_client_read_transition(SSL *s, int mt)
                 st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST;
                 return 1;
             }
-        } else {
-            if (mt == SSL3_MT_HELLO_RETRY_REQUEST) {
-                st->hand_state = TLS_ST_CR_HELLO_RETRY_REQUEST;
-                return 1;
-            }
         }
         break;
 
@@ -224,10 +219,6 @@ int ossl_statem_client_read_transition(SSL *s, int mt)
             st->hand_state = TLS_ST_CR_SRVR_HELLO;
             return 1;
         }
-        if (mt == SSL3_MT_HELLO_RETRY_REQUEST) {
-            st->hand_state = TLS_ST_CR_HELLO_RETRY_REQUEST;
-            return 1;
-        }
         break;
 
     case TLS_ST_CR_SRVR_HELLO:
@@ -391,14 +382,13 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
                  ERR_R_INTERNAL_ERROR);
         return WRITE_TRAN_ERROR;
 
-    case TLS_ST_CW_CLNT_HELLO:
-        /* We only hit this in the case of HelloRetryRequest */
-        return WRITE_TRAN_FINISHED;
-
     case TLS_ST_CR_FINISHED:
         if (s->early_data_state == SSL_EARLY_DATA_WRITE_RETRY
                 || s->early_data_state == SSL_EARLY_DATA_FINISHED_WRITING)
             st->hand_state = TLS_ST_PENDING_EARLY_DATA_END;
+        else if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0
+                 && s->hello_retry_request == SSL_HRR_NONE)
+            st->hand_state = TLS_ST_CW_CHANGE;
         else
             st->hand_state = (s->s3->tmp.cert_req != 0) ? TLS_ST_CW_CERT
                                                         : TLS_ST_CW_FINISHED;
@@ -412,6 +402,7 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
         /* Fall through */
 
     case TLS_ST_CW_END_OF_EARLY_DATA:
+    case TLS_ST_CW_CHANGE:
         st->hand_state = (s->s3->tmp.cert_req != 0) ? TLS_ST_CW_CERT
                                                     : TLS_ST_CW_FINISHED;
         return WRITE_TRAN_CONTINUE;
@@ -494,7 +485,10 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s)
              * We are assuming this is a TLSv1.3 connection, although we haven't
              * actually selected a version yet.
              */
-            st->hand_state = TLS_ST_EARLY_DATA;
+            if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0)
+                st->hand_state = TLS_ST_CW_CHANGE;
+            else
+                st->hand_state = TLS_ST_EARLY_DATA;
             return WRITE_TRAN_CONTINUE;
         }
         /*
@@ -503,8 +497,17 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s)
          */
         return WRITE_TRAN_FINISHED;
 
-    case TLS_ST_CR_HELLO_RETRY_REQUEST:
-        st->hand_state = TLS_ST_CW_CLNT_HELLO;
+    case TLS_ST_CR_SRVR_HELLO:
+        /*
+         * We only get here in TLSv1.3. We just received an HRR, so issue a
+         * CCS unless middlebox compat mode is off, or we already issued one
+         * because we did early data.
+         */
+        if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0
+                && s->early_data_state != SSL_EARLY_DATA_FINISHED_WRITING)
+            st->hand_state = TLS_ST_CW_CHANGE;
+        else
+            st->hand_state = TLS_ST_CW_CLNT_HELLO;
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_EARLY_DATA:
@@ -551,15 +554,20 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s)
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_CW_CHANGE:
+        if (s->hello_retry_request == SSL_HRR_PENDING) {
+            st->hand_state = TLS_ST_CW_CLNT_HELLO;
+        } else if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) {
+            st->hand_state = TLS_ST_EARLY_DATA;
+        } else {
 #if defined(OPENSSL_NO_NEXTPROTONEG)
-        st->
-        hand_state = TLS_ST_CW_FINISHED;
-#else
-        if (!SSL_IS_DTLS(s) && s->s3->npn_seen)
-            st->hand_state = TLS_ST_CW_NEXT_PROTO;
-        else
             st->hand_state = TLS_ST_CW_FINISHED;
+#else
+            if (!SSL_IS_DTLS(s) && s->s3->npn_seen)
+                st->hand_state = TLS_ST_CW_NEXT_PROTO;
+            else
+                st->hand_state = TLS_ST_CW_FINISHED;
 #endif
+        }
         return WRITE_TRAN_CONTINUE;
 
 #if !defined(OPENSSL_NO_NEXTPROTONEG)
@@ -681,14 +689,6 @@ WORK_STATE ossl_statem_client_post_work(SSL *s, WORK_STATE wst)
         break;
 
     case TLS_ST_CW_CLNT_HELLO:
-        if (wst == WORK_MORE_A && statem_flush(s) != 1)
-            return WORK_MORE_A;
-
-        if (SSL_IS_DTLS(s)) {
-            /* Treat the next message as the first packet */
-            s->first_packet = 1;
-        }
-
         if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
                 && s->max_early_data > 0) {
             /*
@@ -696,11 +696,23 @@ WORK_STATE ossl_statem_client_post_work(SSL *s, WORK_STATE wst)
              * cipher state function associated with the SSL_METHOD. Instead
              * we call tls13_change_cipher_state() directly.
              */
-            if (!tls13_change_cipher_state(s,
-                        SSL3_CC_EARLY | SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
-                /* SSLfatal() already called */
-                return WORK_ERROR;
+            if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) == 0) {
+                if (!statem_flush(s))
+                    return WORK_MORE_A;
+                if (!tls13_change_cipher_state(s,
+                            SSL3_CC_EARLY | SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+                    /* SSLfatal() already called */
+                    return WORK_ERROR;
+                }
             }
+            /* else we're in compat mode so we delay flushing until after CCS */
+        } else if (!statem_flush(s)) {
+            return WORK_MORE_A;
+        }
+
+        if (SSL_IS_DTLS(s)) {
+            /* Treat the next message as the first packet */
+            s->first_packet = 1;
         }
         break;
 
@@ -721,6 +733,22 @@ WORK_STATE ossl_statem_client_post_work(SSL *s, WORK_STATE wst)
         break;
 
     case TLS_ST_CW_CHANGE:
+        if (SSL_IS_TLS13(s) || s->hello_retry_request == SSL_HRR_PENDING)
+            break;
+        if (s->early_data_state == SSL_EARLY_DATA_CONNECTING
+                    && s->max_early_data > 0) {
+            if (statem_flush(s) != 1)
+                return WORK_MORE_A;
+            /*
+             * We haven't selected TLSv1.3 yet so we don't call the change
+             * cipher state function associated with the SSL_METHOD. Instead
+             * we call tls13_change_cipher_state() directly.
+             */
+            if (!tls13_change_cipher_state(s,
+                        SSL3_CC_EARLY | SSL3_CHANGE_CIPHER_CLIENT_WRITE))
+                return WORK_ERROR;
+            break;
+        }
         s->session->cipher = s->s3->tmp.new_cipher;
 #ifdef OPENSSL_NO_COMP
         s->session->compress_meth = 0;
@@ -891,9 +919,6 @@ size_t ossl_statem_client_max_message_size(SSL *s)
     case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
         return HELLO_VERIFY_REQUEST_MAX_LENGTH;
 
-    case TLS_ST_CR_HELLO_RETRY_REQUEST:
-        return HELLO_RETRY_REQUEST_MAX_LENGTH;
-
     case TLS_ST_CR_CERT:
         return s->max_cert_list;
 
@@ -957,9 +982,6 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL *s, PACKET *pkt)
     case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
         return dtls_process_hello_verify(s, pkt);
 
-    case TLS_ST_CR_HELLO_RETRY_REQUEST:
-        return tls_process_hello_retry_request(s, pkt);
-
     case TLS_ST_CR_CERT:
         return tls_process_server_certificate(s, pkt);
 
@@ -1028,6 +1050,7 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt)
     SSL_COMP *comp;
 #endif
     SSL_SESSION *sess = s->session;
+    unsigned char *session_id;
 
     if (!WPACKET_set_max_size(pkt, SSL3_RT_MAX_PLAIN_LENGTH)) {
         /* Should not happen */
@@ -1047,7 +1070,8 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt)
     if (sess == NULL
             || !ssl_version_supported(s, sess->ssl_version)
             || !SSL_SESSION_is_resumable(sess)) {
-        if (!ssl_get_new_session(s, 0)) {
+        if (s->hello_retry_request == SSL_HRR_NONE
+                && !ssl_get_new_session(s, 0)) {
             /* SSLfatal() already called */
             return 0;
         }
@@ -1070,7 +1094,7 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt)
             }
         }
     } else {
-        i = s->hello_retry_request == 0;
+        i = (s->hello_retry_request == SSL_HRR_NONE);
     }
 
     if (i && ssl_fill_hello_random(s, 0, p, sizeof(s->s3->client_random),
@@ -1121,13 +1145,34 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt)
     }
 
     /* Session ID */
-    if (s->new_session || s->session->ssl_version == TLS1_3_VERSION)
-        sess_id_len = 0;
-    else
+    session_id = s->session->session_id;
+    if (s->new_session || s->session->ssl_version == TLS1_3_VERSION) {
+        if (s->version == TLS1_3_VERSION
+                && (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0) {
+            sess_id_len = sizeof(s->tmp_session_id);
+            s->tmp_session_id_len = sess_id_len;
+            session_id = s->tmp_session_id;
+            if (s->hello_retry_request == SSL_HRR_NONE
+                    && ssl_randbytes(s, s->tmp_session_id,
+                                     sess_id_len) <= 0) {
+                SSLfatal(s, SSL_AD_INTERNAL_ERROR,
+                         SSL_F_TLS_CONSTRUCT_CLIENT_HELLO,
+                         ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
+        } else {
+            sess_id_len = 0;
+        }
+    } else {
         sess_id_len = s->session->session_id_length;
+        if (s->version == TLS1_3_VERSION) {
+            s->tmp_session_id_len = sess_id_len;
+            memcpy(s->tmp_session_id, s->session->session_id, sess_id_len);
+        }
+    }
     if (sess_id_len > sizeof(s->session->session_id)
             || !WPACKET_start_sub_packet_u8(pkt)
-            || (sess_id_len != 0 && !WPACKET_memcpy(pkt, s->session->session_id,
+            || (sess_id_len != 0 && !WPACKET_memcpy(pkt, session_id,
                                                     sess_id_len))
             || !WPACKET_close(pkt)) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CLIENT_HELLO,
@@ -1310,6 +1355,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
     PACKET session_id, extpkt;
     size_t session_id_len;
     const unsigned char *cipherchars;
+    int hrr = 0;
     unsigned int compression;
     unsigned int sversion;
     unsigned int context;
@@ -1326,50 +1372,37 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
     }
 
     /* load the server random */
-    if (!PACKET_copy_bytes(pkt, s->s3->server_random, SSL3_RANDOM_SIZE)) {
-        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_SERVER_HELLO,
-                 SSL_R_LENGTH_MISMATCH);
-        goto err;
-    }
-
-    /*
-     * We do this immediately so we know what format the ServerHello is in.
-     * Must be done after reading the random data so we can check for the
-     * TLSv1.3 downgrade sentinels
-     */
-    if (!ssl_choose_client_version(s, sversion, 1)) {
-        /* SSLfatal() already called */
-        goto err;
-    }
-
-    /*
-     * In TLSv1.3 a ServerHello message signals a key change so the end of the
-     * message must be on a record boundary.
-     */
-    if (SSL_IS_TLS13(s) && RECORD_LAYER_processed_read_pending(&s->rlayer)) {
-        SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_TLS_PROCESS_SERVER_HELLO,
-                 SSL_R_NOT_ON_RECORD_BOUNDARY);
-        goto err;
-    }
-
-    /* Get the session-id. */
-    if (!SSL_IS_TLS13(s)) {
-        if (!PACKET_get_length_prefixed_1(pkt, &session_id)) {
+    if (s->version == TLS1_3_VERSION
+            && sversion == TLS1_2_VERSION
+            && PACKET_remaining(pkt) >= SSL3_RANDOM_SIZE
+            && memcmp(hrrrandom, PACKET_data(pkt), SSL3_RANDOM_SIZE) == 0) {
+        s->hello_retry_request = SSL_HRR_PENDING;
+        hrr = 1;
+        if (!PACKET_forward(pkt, SSL3_RANDOM_SIZE)) {
             SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_SERVER_HELLO,
                      SSL_R_LENGTH_MISMATCH);
             goto err;
         }
-        session_id_len = PACKET_remaining(&session_id);
-        if (session_id_len > sizeof(s->session->session_id)
-            || session_id_len > SSL3_SESSION_ID_SIZE) {
-            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
-                     SSL_F_TLS_PROCESS_SERVER_HELLO,
-                     SSL_R_SSL3_SESSION_ID_TOO_LONG);
+    } else {
+        if (!PACKET_copy_bytes(pkt, s->s3->server_random, SSL3_RANDOM_SIZE)) {
+            SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_SERVER_HELLO,
+                     SSL_R_LENGTH_MISMATCH);
             goto err;
         }
-    } else {
-        PACKET_null_init(&session_id);
-        session_id_len = 0;
+    }
+
+    /* Get the session-id. */
+    if (!PACKET_get_length_prefixed_1(pkt, &session_id)) {
+        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_SERVER_HELLO,
+                 SSL_R_LENGTH_MISMATCH);
+        goto err;
+    }
+    session_id_len = PACKET_remaining(&session_id);
+    if (session_id_len > sizeof(s->session->session_id)
+        || session_id_len > SSL3_SESSION_ID_SIZE) {
+        SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PROCESS_SERVER_HELLO,
+                 SSL_R_SSL3_SESSION_ID_TOO_LONG);
+        goto err;
     }
 
     if (!PACKET_get_bytes(pkt, &cipherchars, TLS_CIPHER_LEN)) {
@@ -1378,18 +1411,14 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
         goto err;
     }
 
-    if (!SSL_IS_TLS13(s)) {
-        if (!PACKET_get_1(pkt, &compression)) {
-            SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_SERVER_HELLO,
-                     SSL_R_LENGTH_MISMATCH);
-            goto err;
-        }
-    } else {
-        compression = 0;
+    if (!PACKET_get_1(pkt, &compression)) {
+        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_SERVER_HELLO,
+                 SSL_R_LENGTH_MISMATCH);
+        goto err;
     }
 
     /* TLS extensions */
-    if (PACKET_remaining(pkt) == 0) {
+    if (PACKET_remaining(pkt) == 0 && !hrr) {
         PACKET_null_init(&extpkt);
     } else if (!PACKET_as_length_prefixed_2(pkt, &extpkt)
                || PACKET_remaining(pkt) != 0) {
@@ -1398,20 +1427,77 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
         goto err;
     }
 
+    if (!hrr) {
+        if (!tls_collect_extensions(s, &extpkt,
+                                    SSL_EXT_TLS1_2_SERVER_HELLO
+                                    | SSL_EXT_TLS1_3_SERVER_HELLO,
+                                    &extensions, NULL, 1)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+
+        if (!ssl_choose_client_version(s, sversion, extensions)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+    }
+
+    if (SSL_IS_TLS13(s) || hrr) {
+        if (compression != 0) {
+            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
+                     SSL_F_TLS_PROCESS_SERVER_HELLO,
+                     SSL_R_INVALID_COMPRESSION_ALGORITHM);
+            goto err;
+        }
+
+        if (session_id_len != s->tmp_session_id_len
+                || memcmp(PACKET_data(&session_id), s->tmp_session_id,
+                          session_id_len) != 0) {
+            SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
+                     SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_INVALID_SESSION_ID);
+            goto err;
+        }
+    }
+
+    if (hrr) {
+        if (!set_client_ciphersuite(s, cipherchars)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+
+        return tls_process_as_hello_retry_request(s, &extpkt);
+    }
+
+    /*
+     * Now we have chosen the version we need to check again that the extensions
+     * are appropriate for this version.
+     */
     context = SSL_IS_TLS13(s) ? SSL_EXT_TLS1_3_SERVER_HELLO
                               : SSL_EXT_TLS1_2_SERVER_HELLO;
-    if (!tls_collect_extensions(s, &extpkt, context, &extensions, NULL, 1)) {
-        /* SSLfatal() already called */
+    if (!tls_validate_all_contexts(s, context, extensions)) {
+        SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PROCESS_SERVER_HELLO,
+                 SSL_R_BAD_EXTENSION);
         goto err;
     }
 
     s->hit = 0;
 
     if (SSL_IS_TLS13(s)) {
+        /*
+         * In TLSv1.3 a ServerHello message signals a key change so the end of
+         * the message must be on a record boundary.
+         */
+        if (RECORD_LAYER_processed_read_pending(&s->rlayer)) {
+            SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE,
+                     SSL_F_TLS_PROCESS_SERVER_HELLO,
+                     SSL_R_NOT_ON_RECORD_BOUNDARY);
+            goto err;
+        }
+
         /* This will set s->hit if we are resuming */
         if (!tls_parse_extension(s, TLSEXT_IDX_psk,
                                  SSL_EXT_TLS1_3_SERVER_HELLO,
-                                 extensions, NULL, 0l)) {
+                                 extensions, NULL, 0)) {
             /* SSLfatal() already called */
             goto err;
         }
@@ -1489,11 +1575,19 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
         }
 
         s->session->ssl_version = s->version;
-        s->session->session_id_length = session_id_len;
-        /* session_id_len could be 0 */
-        if (session_id_len > 0)
-            memcpy(s->session->session_id, PACKET_data(&session_id),
-                   session_id_len);
+        /*
+         * In TLSv1.2 and below we save the session id we were sent so we can
+         * resume it later. In TLSv1.3 the session id we were sent is just an
+         * echo of what we originally sent in the ClientHello and should not be
+         * used for resumption.
+         */
+        if (!SSL_IS_TLS13(s)) {
+            s->session->session_id_length = session_id_len;
+            /* session_id_len could be 0 */
+            if (session_id_len > 0)
+                memcpy(s->session->session_id, PACKET_data(&session_id),
+                       session_id_len);
+        }
     }
 
     /* Session version and negotiated protocol version should match */
@@ -1605,28 +1699,10 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
     return MSG_PROCESS_ERROR;
 }
 
-static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt)
+static MSG_PROCESS_RETURN tls_process_as_hello_retry_request(SSL *s,
+                                                             PACKET *extpkt)
 {
-    unsigned int sversion;
-    const unsigned char *cipherchars;
     RAW_EXTENSION *extensions = NULL;
-    PACKET extpkt;
-
-    if (!PACKET_get_net_2(pkt, &sversion)) {
-        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST,
-                 SSL_R_LENGTH_MISMATCH);
-        goto err;
-    }
-
-    /* TODO(TLS1.3): Remove the TLS1_3_VERSION_DRAFT clause before release */
-    if (sversion != TLS1_3_VERSION && sversion != TLS1_3_VERSION_DRAFT) {
-        SSLfatal(s, SSL_AD_PROTOCOL_VERSION,
-                 SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST,
-                 SSL_R_WRONG_SSL_VERSION);
-        goto err;
-    }
-
-    s->hello_retry_request = 1;
 
     /*
      * If we were sending early_data then the enc_write_ctx is now invalid and
@@ -1635,28 +1711,7 @@ static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt)
     EVP_CIPHER_CTX_free(s->enc_write_ctx);
     s->enc_write_ctx = NULL;
 
-    if (!PACKET_get_bytes(pkt, &cipherchars, TLS_CIPHER_LEN)) {
-        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST,
-                 SSL_R_LENGTH_MISMATCH);
-        goto err;
-    }
-
-    if (!set_client_ciphersuite(s, cipherchars)) {
-        /* SSLfatal() already called */
-        goto err;
-    }
-
-    if (!PACKET_as_length_prefixed_2(pkt, &extpkt)
-               /* Must have a non-empty extensions block */
-            || PACKET_remaining(&extpkt) == 0
-               /* Must be no trailing data after extensions */
-            || PACKET_remaining(pkt) != 0) {
-        SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST,
-                 SSL_R_BAD_LENGTH);
-        goto err;
-    }
-
-    if (!tls_collect_extensions(s, &extpkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
+    if (!tls_collect_extensions(s, extpkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
                                 &extensions, NULL, 1)
             || !tls_parse_all_extensions(s, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
                                          extensions, NULL, 0, 1)) {
@@ -1677,8 +1732,8 @@ static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt)
          * ClientHello will not change
          */
         SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
-                 SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST,
-                  SSL_R_NO_CHANGE_FOLLOWING_HRR);
+                 SSL_F_TLS_PROCESS_AS_HELLO_RETRY_REQUEST,
+                 SSL_R_NO_CHANGE_FOLLOWING_HRR);
         goto err;
     }
 
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index b8e094b..b65dfa1 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -19,6 +19,13 @@
 #include <openssl/evp.h>
 #include <openssl/x509.h>
 
+/* Fixed value used in the ServerHello random field to identify an HRR */
+const unsigned char hrrrandom[] = {
+    0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, 0x02,
+    0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e,
+    0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c
+};
+
 /*
  * send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or
  * SSL3_RT_CHANGE_CIPHER_SPEC)
@@ -1238,12 +1245,18 @@ int tls_get_message_body(SSL *s, size_t *len)
          * We defer feeding in the HRR until later. We'll do it as part of
          * processing the message
          */
-        if (s->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST
-                && !ssl3_finish_mac(s, (unsigned char *)s->init_buf->data,
-                                    s->init_num + SSL3_HM_HEADER_LENGTH)) {
-            /* SSLfatal() already called */
-            *len = 0;
-            return 0;
+#define SERVER_HELLO_RANDOM_OFFSET  (SSL3_HM_HEADER_LENGTH + 2)
+        if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO
+                || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE
+                || memcmp(hrrrandom,
+                          s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET,
+                          SSL3_RANDOM_SIZE) != 0) {
+            if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data,
+                                 s->init_num + SSL3_HM_HEADER_LENGTH)) {
+                /* SSLfatal() already called */
+                *len = 0;
+                return 0;
+            }
         }
         if (s->msg_callback)
             s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data,
@@ -1642,6 +1655,10 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello, DOWNGRADE *dgrd)
 
     suppversions = &hello->pre_proc_exts[TLSEXT_IDX_supported_versions];
 
+    /* If we did an HRR then supported versions is mandatory */
+    if (!suppversions->present && s->hello_retry_request != SSL_HRR_NONE)
+        return SSL_R_UNSUPPORTED_PROTOCOL;
+
     if (suppversions->present && !SSL_IS_DTLS(s)) {
         unsigned int candidate_vers = 0;
         unsigned int best_vers = 0;
@@ -1686,10 +1703,10 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello, DOWNGRADE *dgrd)
         }
 
         if (best_vers > 0) {
-            if (SSL_IS_TLS13(s)) {
+            if (s->hello_retry_request != SSL_HRR_NONE) {
                 /*
-                 * We get here if this is after a HelloRetryRequest. In this
-                 * case we just check that we still negotiated TLSv1.3
+                 * This is after a HelloRetryRequest so we better check that we
+                 * negotiated TLSv1.3
                  */
                 if (best_vers != TLS1_3_VERSION)
                     return SSL_R_UNSUPPORTED_PROTOCOL;
@@ -1739,21 +1756,32 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello, DOWNGRADE *dgrd)
  *
  * @s: client SSL handle.
  * @version: The proposed version from the server's HELLO.
- * @checkdgrd: Whether to check the downgrade sentinels in the server_random
+ * @extensions: The extensions received
  *
  * Returns 1 on success or 0 on error.
  */
-int ssl_choose_client_version(SSL *s, int version, int checkdgrd)
+int ssl_choose_client_version(SSL *s, int version, RAW_EXTENSION *extensions)
 {
     const version_info *vent;
     const version_info *table;
     int highver = 0;
+    int origv;
 
-    /* TODO(TLS1.3): Remove this before release */
-    if (version == TLS1_3_VERSION_DRAFT)
-        version = TLS1_3_VERSION;
+    origv = s->version;
+    s->version = version;
 
-    if (s->hello_retry_request && version != TLS1_3_VERSION) {
+    /* This will overwrite s->version if the extension is present */
+    if (!tls_parse_extension(s, TLSEXT_IDX_supported_versions,
+                             SSL_EXT_TLS1_2_SERVER_HELLO
+                             | SSL_EXT_TLS1_3_SERVER_HELLO, extensions,
+                             NULL, 0)) {
+        s->version = origv;
+        return 0;
+    }
+
+    if (s->hello_retry_request != SSL_HRR_NONE
+            && s->version != TLS1_3_VERSION) {
+        s->version = origv;
         SSLfatal(s, SSL_AD_PROTOCOL_VERSION, SSL_F_SSL_CHOOSE_CLIENT_VERSION,
                  SSL_R_WRONG_SSL_VERSION);
         return 0;
@@ -1761,7 +1789,8 @@ int ssl_choose_client_version(SSL *s, int version, int checkdgrd)
 
     switch (s->method->version) {
     default:
-        if (version != s->version) {
+        if (s->version != s->method->version) {
+            s->version = origv;
             SSLfatal(s, SSL_AD_PROTOCOL_VERSION,
                      SSL_F_SSL_CHOOSE_CLIENT_VERSION,
                      SSL_R_WRONG_SSL_VERSION);
@@ -1790,13 +1819,14 @@ int ssl_choose_client_version(SSL *s, int version, int checkdgrd)
         if (vent->cmeth == NULL)
             continue;
 
-        if (highver != 0 && version != vent->version)
+        if (highver != 0 && s->version != vent->version)
             continue;
 
         method = vent->cmeth();
         err = ssl_method_error(s, method);
         if (err != 0) {
-            if (version == vent->version) {
+            if (s->version == vent->version) {
+                s->version = origv;
                 SSLfatal(s, SSL_AD_PROTOCOL_VERSION,
                          SSL_F_SSL_CHOOSE_CLIENT_VERSION, err);
                 return 0;
@@ -1807,43 +1837,43 @@ int ssl_choose_client_version(SSL *s, int version, int checkdgrd)
         if (highver == 0)
             highver = vent->version;
 
-        if (version != vent->version)
+        if (s->version != vent->version)
             continue;
 
 #ifndef OPENSSL_NO_TLS13DOWNGRADE
         /* Check for downgrades */
-        if (checkdgrd) {
-            if (version == TLS1_2_VERSION && highver > version) {
-                if (memcmp(tls12downgrade,
-                           s->s3->server_random + SSL3_RANDOM_SIZE
-                                                - sizeof(tls12downgrade),
-                           sizeof(tls12downgrade)) == 0) {
-                    SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
-                             SSL_F_SSL_CHOOSE_CLIENT_VERSION,
-                             SSL_R_INAPPROPRIATE_FALLBACK);
-                    return 0;
-                }
-            } else if (!SSL_IS_DTLS(s)
-                       && version < TLS1_2_VERSION
-                       && highver > version) {
-                if (memcmp(tls11downgrade,
-                           s->s3->server_random + SSL3_RANDOM_SIZE
-                                                - sizeof(tls11downgrade),
-                           sizeof(tls11downgrade)) == 0) {
-                    SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
-                             SSL_F_SSL_CHOOSE_CLIENT_VERSION,
-                             SSL_R_INAPPROPRIATE_FALLBACK);
-                    return 0;
-                }
+        if (s->version == TLS1_2_VERSION && highver > s->version) {
+            if (memcmp(tls12downgrade,
+                       s->s3->server_random + SSL3_RANDOM_SIZE
+                                            - sizeof(tls12downgrade),
+                       sizeof(tls12downgrade)) == 0) {
+                s->version = origv;
+                SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
+                         SSL_F_SSL_CHOOSE_CLIENT_VERSION,
+                         SSL_R_INAPPROPRIATE_FALLBACK);
+                return 0;
+            }
+        } else if (!SSL_IS_DTLS(s)
+                   && s->version < TLS1_2_VERSION
+                   && highver > s->version) {
+            if (memcmp(tls11downgrade,
+                       s->s3->server_random + SSL3_RANDOM_SIZE
+                                            - sizeof(tls11downgrade),
+                       sizeof(tls11downgrade)) == 0) {
+                s->version = origv;
+                SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER,
+                         SSL_F_SSL_CHOOSE_CLIENT_VERSION,
+                         SSL_R_INAPPROPRIATE_FALLBACK);
+                return 0;
             }
         }
 #endif
 
         s->method = method;
-        s->version = version;
         return 1;
     }
 
+    s->version = origv;
     SSLfatal(s, SSL_AD_PROTOCOL_VERSION, SSL_F_SSL_CHOOSE_CLIENT_VERSION,
              SSL_R_UNSUPPORTED_PROTOCOL);
     return 0;
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 888c0b5..5e0ce7e 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -35,6 +35,8 @@
 /* Dummy message type */
 #define SSL3_MT_DUMMY   -1
 
+extern const unsigned char hrrrandom[];
+
 /* Message processing return codes */
 typedef enum {
     /* Something bad happened */
@@ -161,6 +163,8 @@ typedef enum ext_return_en {
     EXT_RETURN_NOT_SENT
 } EXT_RETURN;
 
+__owur int tls_validate_all_contexts(SSL *s, unsigned int thisctx,
+                                     RAW_EXTENSION *exts);
 __owur int extension_is_relevant(SSL *s, unsigned int extctx,
                                  unsigned int thisctx);
 __owur int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
@@ -271,6 +275,9 @@ EXT_RETURN tls_construct_stoc_etm(SSL *s, WPACKET *pkt, unsigned int context,
                                   X509 *x, size_t chainidx);
 EXT_RETURN tls_construct_stoc_ems(SSL *s, WPACKET *pkt, unsigned int context,
                                   X509 *x, size_t chainidx);
+EXT_RETURN tls_construct_stoc_supported_versions(SSL *s, WPACKET *pkt,
+                                                 unsigned int context, X509 *x,
+                                                 size_t chainidx);
 EXT_RETURN tls_construct_stoc_key_share(SSL *s, WPACKET *pkt,
                                         unsigned int context, X509 *x,
                                         size_t chainidx);
@@ -388,6 +395,8 @@ int tls_parse_stoc_etm(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                        size_t chainidx);
 int tls_parse_stoc_ems(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                        size_t chainidx);
+int tls_parse_stoc_supported_versions(SSL *s, PACKET *pkt, unsigned int context,
+                                      X509 *x, size_t chainidx);
 int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
                              size_t chainidx);
 int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index f95c19b..249ee40 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -24,7 +24,6 @@
 #include <openssl/md5.h>
 
 static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt);
-static int tls_construct_hello_retry_request(SSL *s, WPACKET *pkt);
 
 /*
  * ossl_statem_server13_read_transition() encapsulates the logic for the allowed
@@ -49,7 +48,7 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt)
         break;
 
     case TLS_ST_EARLY_DATA:
-        if (s->hello_retry_request) {
+        if (s->hello_retry_request == SSL_HRR_PENDING) {
             if (mt == SSL3_MT_CLIENT_HELLO) {
                 st->hand_state = TLS_ST_SR_CLNT_HELLO;
                 return 1;
@@ -392,18 +391,24 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
         return WRITE_TRAN_FINISHED;
 
     case TLS_ST_SR_CLNT_HELLO:
-        if (s->hello_retry_request)
-            st->hand_state = TLS_ST_SW_HELLO_RETRY_REQUEST;
-        else
-            st->hand_state = TLS_ST_SW_SRVR_HELLO;
+        st->hand_state = TLS_ST_SW_SRVR_HELLO;
         return WRITE_TRAN_CONTINUE;
 
-    case TLS_ST_SW_HELLO_RETRY_REQUEST:
-        st->hand_state = TLS_ST_EARLY_DATA;
+    case TLS_ST_SW_SRVR_HELLO:
+        if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0
+                && s->hello_retry_request != SSL_HRR_COMPLETE)
+            st->hand_state = TLS_ST_SW_CHANGE;
+        else if (s->hello_retry_request == SSL_HRR_PENDING)
+            st->hand_state = TLS_ST_EARLY_DATA;
+        else
+            st->hand_state = TLS_ST_SW_ENCRYPTED_EXTENSIONS;
         return WRITE_TRAN_CONTINUE;
 
-    case TLS_ST_SW_SRVR_HELLO:
-        st->hand_state = TLS_ST_SW_ENCRYPTED_EXTENSIONS;
+    case TLS_ST_SW_CHANGE:
+        if (s->hello_retry_request == SSL_HRR_PENDING)
+            st->hand_state = TLS_ST_EARLY_DATA;
+        else
+            st->hand_state = TLS_ST_SW_ENCRYPTED_EXTENSIONS;
         return WRITE_TRAN_CONTINUE;
 
     case TLS_ST_SW_ENCRYPTED_EXTENSIONS:
@@ -663,6 +668,8 @@ WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst)
         break;
 
     case TLS_ST_SW_CHANGE:
+        if (SSL_IS_TLS13(s))
+            break;
         s->session->cipher = s->s3->tmp.new_cipher;
         if (!s->method->ssl3_enc->setup_key_block(s)) {
             /* SSLfatal() already called */
@@ -707,11 +714,6 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
         /* No post work to be done */
         break;
 
-    case TLS_ST_SW_HELLO_RETRY_REQUEST:
-        if (statem_flush(s) != 1)
-            return WORK_MORE_A;
-        break;
-
     case TLS_ST_SW_HELLO_REQ:
         if (statem_flush(s) != 1)
             return WORK_MORE_A;
@@ -737,6 +739,12 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
         break;
 
     case TLS_ST_SW_SRVR_HELLO:
+        if (SSL_IS_TLS13(s) && s->hello_retry_request == SSL_HRR_PENDING) {
+            if ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) == 0
+                    && statem_flush(s) != 1)
+                return WORK_MORE_A;
+            break;
+        }
 #ifndef OPENSSL_NO_SCTP
         if (SSL_IS_DTLS(s) && s->hit) {
             unsigned char sctpauthkey[64];
@@ -763,6 +771,18 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
                      sizeof(sctpauthkey), sctpauthkey);
         }
 #endif
+        if (!SSL_IS_TLS13(s)
+                || ((s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0
+                    && s->hello_retry_request != SSL_HRR_COMPLETE))
+            break;
+        /* Fall through */
+
+    case TLS_ST_SW_CHANGE:
+        if (s->hello_retry_request == SSL_HRR_PENDING) {
+            if (!statem_flush(s))
+                return WORK_MORE_A;
+            break;
+        }
         /*
          * 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
@@ -783,10 +803,9 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst)
                 /* SSLfatal() already called */
                 return WORK_ERROR;
             }
+            break;
         }
-        break;
 
-    case TLS_ST_SW_CHANGE:
 #ifndef OPENSSL_NO_SCTP
         if (SSL_IS_DTLS(s) && !s->hit) {
             /*
@@ -951,11 +970,6 @@ int ossl_statem_server_construct_message(SSL *s, WPACKET *pkt,
         *mt = SSL3_MT_ENCRYPTED_EXTENSIONS;
         break;
 
-    case TLS_ST_SW_HELLO_RETRY_REQUEST:
-        *confunc = tls_construct_hello_retry_request;
-        *mt = SSL3_MT_HELLO_RETRY_REQUEST;
-        break;
-
     case TLS_ST_SW_KEY_UPDATE:
         *confunc = tls_construct_key_update;
         *mt = SSL3_MT_KEY_UPDATE;
@@ -1266,7 +1280,8 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
     if (clienthello->isv2) {
         unsigned int mt;
 
-        if (!SSL_IS_FIRST_HANDSHAKE(s) || s->hello_retry_request) {
+        if (!SSL_IS_FIRST_HANDSHAKE(s)
+                || s->hello_retry_request != SSL_HRR_NONE) {
             SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE,
                      SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNEXPECTED_MESSAGE);
             goto err;
@@ -1623,7 +1638,7 @@ static int tls_early_post_process_client_hello(SSL *s)
                      SSL_R_NO_SHARED_CIPHER);
             goto err;
         }
-        if (s->hello_retry_request
+        if (s->hello_retry_request == SSL_HRR_PENDING
                 && (s->s3->tmp.new_cipher == NULL
                     || s->s3->tmp.new_cipher->id != cipher->id)) {
             /*
@@ -1686,6 +1701,12 @@ static int tls_early_post_process_client_hello(SSL *s)
         }
     }
 
+    if (SSL_IS_TLS13(s)) {
+        memcpy(s->tmp_session_id, s->clienthello->session_id,
+               s->clienthello->session_id_len);
+        s->tmp_session_id_len = s->clienthello->session_id_len;
+    }
+
     /*
      * If it is a hit, check that the cipher is in the list. In TLSv1.3 we check
      * ciphersuite compatibility with the session as part of resumption.
@@ -2192,15 +2213,19 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt)
     int compm;
     size_t sl, len;
     int version;
+    unsigned char *session_id;
+    int usetls13 = SSL_IS_TLS13(s) || s->hello_retry_request == SSL_HRR_PENDING;
 
-    /* TODO(TLS1.3): Remove the DRAFT conditional before release */
-    version = SSL_IS_TLS13(s) ? TLS1_3_VERSION_DRAFT : s->version;
+    version = usetls13 ? TLS1_2_VERSION : s->version;
     if (!WPACKET_put_bytes_u16(pkt, version)
                /*
                 * Random stuff. Filling of the server_random takes place in
                 * tls_process_client_hello()
                 */
-            || !WPACKET_memcpy(pkt, s->s3->server_random, SSL3_RANDOM_SIZE)) {
+            || !WPACKET_memcpy(pkt,
+                               s->hello_retry_request == SSL_HRR_PENDING
+                                   ? hrrrandom : s->s3->server_random,
+                               SSL3_RANDOM_SIZE)) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_SERVER_HELLO,
                  ERR_R_INTERNAL_ERROR);
         return 0;
@@ -2218,6 +2243,8 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt)
      *   session ID.
      * - However, if we want the new session to be single-use,
      *   we send back a 0-length session ID.
+     * - In TLSv1.3 we echo back the session id sent to us by the client
+     *   regardless
      * s->hit is non-zero in either case of session reuse,
      * so the following won't overwrite an ID that we're supposed
      * to send back.
@@ -2227,7 +2254,14 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt)
          && !s->hit))
         s->session->session_id_length = 0;
 
-    sl = s->session->session_id_length;
+    if (usetls13) {
+        sl = s->tmp_session_id_len;
+        session_id = s->tmp_session_id;
+    } else {
+        sl = s->session->session_id_length;
+        session_id = s->session->session_id;
+    }
+
     if (sl > sizeof(s->session->session_id)) {
         SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_SERVER_HELLO,
                  ERR_R_INTERNAL_ERROR);
@@ -2238,28 +2272,43 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt)
 #ifdef OPENSSL_NO_COMP
     compm = 0;
 #else
-    if (s->s3->tmp.new_compression == NULL)
+    if (usetls13 || s->s3->tmp.new_compression == NULL)
         compm = 0;
     else
         compm = s->s3->tmp.new_compression->id;
 #endif
 
-    if ((!SSL_IS_TLS13(s)
-                && !WPACKET_sub_memcpy_u8(pkt, s->session->session_id, sl))
+    if (!WPACKET_sub_memcpy_u8(pkt, session_id, sl)
             || !s->method->put_cipher_by_char(s->s3->tmp.new_cipher, pkt, &len)
-            || (!SSL_IS_TLS13(s)
-                && !WPACKET_put_bytes_u8(pkt, compm))
+            || !WPACKET_put_bytes_u8(pkt, compm)
             || !tls_construct_extensions(s, pkt,
-                                         SSL_IS_TLS13(s)
-                                            ? SSL_EXT_TLS1_3_SERVER_HELLO
-                                            : SSL_EXT_TLS1_2_SERVER_HELLO,
+                                         s->hello_retry_request
+                                            == SSL_HRR_PENDING
+                                            ? SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
+                                            : (SSL_IS_TLS13(s)
+                                                ? SSL_EXT_TLS1_3_SERVER_HELLO
+                                                : SSL_EXT_TLS1_2_SERVER_HELLO),
                                          NULL, 0)) {
         /* SSLfatal() already called */
         return 0;
     }
 
-    if (!(s->verify_mode & SSL_VERIFY_PEER)
-            && !ssl3_digest_cached_records(s, 0)) {
+    if (s->hello_retry_request == SSL_HRR_PENDING) {
+        /* Ditch the session. We'll create a new one next time around */
+        SSL_SESSION_free(s->session);
+        s->session = NULL;
+        s->hit = 0;
+
+        /*
+         * Re-initialise the Transcript Hash. We're going to prepopulate it with
+         * a synthetic message_hash in place of ClientHello1.
+         */
+        if (!create_synthetic_message_hash(s)) {
+            /* SSLfatal() already called */
+            return 0;
+        }
+    } else if (!(s->verify_mode & SSL_VERIFY_PEER)
+                && !ssl3_digest_cached_records(s, 0)) {
         /* SSLfatal() already called */;
         return 0;
     }
@@ -3831,45 +3880,6 @@ static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt)
     return 1;
 }
 
-static int tls_construct_hello_retry_request(SSL *s, WPACKET *pkt)
-{
-    size_t len = 0;
-
-    /*
-     * TODO(TLS1.3): Remove the DRAFT version before release
-     * (should be s->version)
-     */
-    if (!WPACKET_put_bytes_u16(pkt, TLS1_3_VERSION_DRAFT)
-            || !s->method->put_cipher_by_char(s->s3->tmp.new_cipher, pkt,
-                                              &len)) {
-        SSLfatal(s, SSL_AD_INTERNAL_ERROR,
-                 SSL_F_TLS_CONSTRUCT_HELLO_RETRY_REQUEST, ERR_R_INTERNAL_ERROR);
-       return 0;
-    }
-
-    if (!tls_construct_extensions(s, pkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
-                                  NULL, 0)) {
-        /* SSLfatal() already called */
-        return 0;
-    }
-
-    /* Ditch the session. We'll create a new one next time around */
-    SSL_SESSION_free(s->session);
-    s->session = NULL;
-    s->hit = 0;
-
-    /*
-     * Re-initialise the Transcript Hash. We're going to prepopulate it with
-     * a synthetic message_hash in place of ClientHello1.
-     */
-    if (!create_synthetic_message_hash(s)) {
-        /* SSLfatal() already called */
-        return 0;
-    }
-
-    return 1;
-}
-
 MSG_PROCESS_RETURN tls_process_end_of_early_data(SSL *s, PACKET *pkt)
 {
     if (PACKET_remaining(pkt) != 0) {
diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c
index 13e3062..59d0efc 100644
--- a/ssl/t1_trce.c
+++ b/ssl/t1_trce.c
@@ -87,7 +87,6 @@ static const ssl_trace_tbl ssl_handshake_tbl[] = {
     {DTLS1_MT_HELLO_VERIFY_REQUEST, "HelloVerifyRequest"},
     {SSL3_MT_NEWSESSION_TICKET, "NewSessionTicket"},
     {SSL3_MT_END_OF_EARLY_DATA, "EndOfEarlyData"},
-    {SSL3_MT_HELLO_RETRY_REQUEST, "HelloRetryRequest"},
     {SSL3_MT_ENCRYPTED_EXTENSIONS, "EncryptedExtensions"},
     {SSL3_MT_CERTIFICATE, "Certificate"},
     {SSL3_MT_SERVER_KEY_EXCHANGE, "ServerKeyExchange"},
@@ -783,11 +782,10 @@ static int ssl_print_extension(BIO *bio, int indent, int server,
         break;
 
     case TLSEXT_TYPE_key_share:
-        if (mt == SSL3_MT_HELLO_RETRY_REQUEST) {
+        if (server && extlen == 2) {
             int group_id;
 
-            if (extlen != 2)
-                return 0;
+            /* We assume this is an HRR, otherwise this is an invalid key_share */
             group_id = (ext[0] << 8) | ext[1];
             BIO_indent(bio, indent + 4, 80);
             BIO_printf(bio, "NamedGroup: %s (%d)\n",
@@ -823,6 +821,17 @@ static int ssl_print_extension(BIO *bio, int indent, int server,
         break;
 
     case TLSEXT_TYPE_supported_versions:
+        if (server) {
+            int version;
+
+            if (extlen != 2)
+                return 0;
+            version = (ext[0] << 8) | ext[1];
+            BIO_indent(bio, indent + 4, 80);
+            BIO_printf(bio, "%s (%d)\n",
+                       ssl_trace_str(version, ssl_version_tbl), version);
+            break;
+        }
         if (extlen < 1)
             return 0;
         xlen = ext[0];
@@ -1004,29 +1013,6 @@ static int ssl_print_server_hello(BIO *bio, int indent,
     return 1;
 }
 
-static int ssl_print_hello_retry_request(BIO *bio, int indent,
-                                         const unsigned char *msg,
-                                         size_t msglen)
-{
-    unsigned int cs;
-
-    if (!ssl_print_version(bio, indent, "server_version", &msg, &msglen, NULL))
-        return 0;
-
-    cs = (msg[0] << 8) | msg[1];
-    BIO_indent(bio, indent, 80);
-    BIO_printf(bio, "cipher_suite {0x%02X, 0x%02X} %s\n",
-               msg[0], msg[1], ssl_trace_str(cs, ssl_ciphers_tbl));
-    msg += 2;
-    msglen -= 2;
-
-    if (!ssl_print_extensions(bio, indent, 1, SSL3_MT_HELLO_RETRY_REQUEST, &msg,
-                              &msglen))
-        return 0;
-
-    return 1;
-}
-
 static int ssl_get_keyex(const char **pname, const SSL *ssl)
 {
     unsigned long alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
@@ -1460,11 +1446,6 @@ static int ssl_print_handshake(BIO *bio, const SSL *ssl, int server,
             return 0;
         break;
 
-    case SSL3_MT_HELLO_RETRY_REQUEST:
-        if (!ssl_print_hello_retry_request(bio, indent + 2, msg, msglen))
-            return 0;
-        break;
-
     case SSL3_MT_ENCRYPTED_EXTENSIONS:
         if (!ssl_print_extensions(bio, indent + 2, 1,
                                   SSL3_MT_ENCRYPTED_EXTENSIONS, &msg, &msglen))
diff --git a/test/asynciotest.c b/test/asynciotest.c
index fdb9770..179fe26 100644
--- a/test/asynciotest.c
+++ b/test/asynciotest.c
@@ -146,7 +146,7 @@ static int async_write(BIO *bio, const char *in, int inl)
                 return -1;
 
             while (PACKET_remaining(&pkt) > 0) {
-                PACKET payload, wholebody;
+                PACKET payload, wholebody, sessionid, extensions;
                 unsigned int contenttype, versionhi, versionlo, data;
                 unsigned int msgtype = 0, negversion = 0;
 
@@ -164,11 +164,43 @@ static int async_write(BIO *bio, const char *in, int inl)
                         && !PACKET_get_1(&wholebody, &msgtype))
                     return -1;
 
-                if (msgtype == SSL3_MT_SERVER_HELLO
-                        && (!PACKET_forward(&wholebody,
+                if (msgtype == SSL3_MT_SERVER_HELLO) {
+                    if (!PACKET_forward(&wholebody,
                                             SSL3_HM_HEADER_LENGTH - 1)
-                            || !PACKET_get_net_2(&wholebody, &negversion)))
-                    return -1;
+                            || !PACKET_get_net_2(&wholebody, &negversion)
+                               /* Skip random (32 bytes) */
+                            || !PACKET_forward(&wholebody, 32)
+                               /* Skip session id */
+                            || !PACKET_get_length_prefixed_1(&wholebody,
+                                                             &sessionid)
+                               /*
+                                * Skip ciphersuite (2 bytes) and compression
+                                * method (1 byte)
+                                */
+                            || !PACKET_forward(&wholebody, 2 + 1)
+                            || !PACKET_get_length_prefixed_2(&wholebody,
+                                                             &extensions))
+                        return -1;
+
+                    /*
+                     * Find the negotiated version in supported_versions
+                     * extension, if present.
+                     */
+                    while (PACKET_remaining(&extensions)) {
+                        unsigned int type;
+                        PACKET extbody;
+
+                        if (!PACKET_get_net_2(&extensions, &type)
+                                || !PACKET_get_length_prefixed_2(&extensions,
+                                &extbody))
+                            return -1;
+
+                        if (type == TLSEXT_TYPE_supported_versions
+                                && (!PACKET_get_net_2(&extbody, &negversion)
+                                    || PACKET_remaining(&extbody) != 0))
+                            return -1;
+                    }
+                }
 
                 while (PACKET_get_1(&payload, &data)) {
                     /* Create a new one byte long record for each byte in the
diff --git a/test/build.info b/test/build.info
index 3c92c80..c4c5441 100644
--- a/test/build.info
+++ b/test/build.info
@@ -46,7 +46,7 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
           x509_time_test x509_dup_cert_test x509_check_cert_pkey_test \
           recordlentest drbgtest sslbuffertest \
           time_offset_test pemtest ssl_cert_table_internal_test ciphername_test \
-          servername_test ocspapitest rsa_mp_test fatalerrtest
+          servername_test ocspapitest rsa_mp_test fatalerrtest tls13ccstest
 
   SOURCE[aborttest]=aborttest.c
   INCLUDE[aborttest]=../include
@@ -160,6 +160,10 @@ INCLUDE_MAIN___test_libtestutil_OLB = /INCLUDE=MAIN
   INCLUDE[fatalerrtest]=../include ..
   DEPEND[fatalerrtest]=../libcrypto ../libssl libtestutil.a
 
+  SOURCE[tls13ccstest]=tls13ccstest.c ssltestlib.c
+  INCLUDE[tls13ccstest]=../include
+  DEPEND[tls13ccstest]=../libcrypto ../libssl libtestutil.a
+
   SOURCE[evp_test]=evp_test.c
   INCLUDE[evp_test]=../include
   DEPEND[evp_test]=../libcrypto libtestutil.a
diff --git a/test/clienthellotest.c b/test/clienthellotest.c
index 8ba65ce..88e0a1c 100644
--- a/test/clienthellotest.c
+++ b/test/clienthellotest.c
@@ -90,6 +90,8 @@ static int test_client_hello(int currtest)
     case TEST_ADD_PADDING:
     case TEST_PADDING_NOT_NEEDED:
         SSL_CTX_set_options(ctx, SSL_OP_TLSEXT_PADDING);
+        /* Make sure we get a consistent size across TLS versions */
+        SSL_CTX_clear_options(ctx, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
         /*
          * Add some dummy ALPN protocols so that the ClientHello is at least
          * F5_WORKAROUND_MIN_MSG_LEN bytes long - meaning padding will be
diff --git a/test/recipes/70-test_key_share.t b/test/recipes/70-test_key_share.t
index ae0a2b0..e2cdf09 100644
--- a/test/recipes/70-test_key_share.t
+++ b/test/recipes/70-test_key_share.t
@@ -223,6 +223,7 @@ ok(TLSProxy::Message->success(), "Ignore key_share for TLS<=1.2 server");
 #Test 22: The server sending an HRR but not requesting a new key_share should
 #         fail
 $proxy->clear();
+$direction = SERVER_TO_CLIENT;
 $testtype = NO_KEY_SHARES_IN_HRR;
 $proxy->serverflags("-curves X25519");
 $proxy->start();
@@ -341,6 +342,12 @@ sub modify_key_shares_filter
             if ($testtype == LOOK_ONLY) {
                 return;
             }
+            if ($testtype == NO_KEY_SHARES_IN_HRR) {
+                $message->delete_extension(TLSProxy::Message::EXT_KEY_SHARE);
+                $message->set_extension(TLSProxy::Message::EXT_UNKNOWN, "");
+                $message->repack();
+                return;
+            }
             if ($testtype == SELECT_X25519) {
                 $ext = pack "C4H64",
                     0x00, 0x1d, #x25519
@@ -370,12 +377,7 @@ sub modify_key_shares_filter
             $message->set_extension(TLSProxy::Message::EXT_KEY_SHARE, $ext);
 
             $message->repack();
-        } elsif ($message->mt == TLSProxy::Message::MT_HELLO_RETRY_REQUEST
-                 && $testtype == NO_KEY_SHARES_IN_HRR) {
-            $message->delete_extension(TLSProxy::Message::EXT_KEY_SHARE);
-            $message->set_extension(TLSProxy::Message::EXT_UNKNOWN, "");
-            $message->repack();
-         }
+        }
     }
 }
 
diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t
index ef46792..94dd11e 100644
--- a/test/recipes/70-test_sslrecords.t
+++ b/test/recipes/70-test_sslrecords.t
@@ -485,7 +485,8 @@ sub change_outer_record_type
     for ($i = 0; ${$proxy->record_list}[$i]->flight() < 1; $i++) {
         next;
     }
-    $i++;
+    #Skip CCS and ServerHello
+    $i += 2;
     ${$proxy->record_list}[$i]->outer_content_type(TLSProxy::Record::RT_HANDSHAKE);
 }
 
diff --git a/test/recipes/70-test_sslversions.t b/test/recipes/70-test_sslversions.t
index 1f3db22..6044a05 100644
--- a/test/recipes/70-test_sslversions.t
+++ b/test/recipes/70-test_sslversions.t
@@ -87,7 +87,7 @@ $testtype = REVERSE_ORDER_VERSIONS;
 $proxy->start();
 $record = pop @{$proxy->record_list};
 ok(TLSProxy::Message->success()
-   && $record->version() == TLSProxy::Record::VERS_TLS_1_0
+   && $record->version() == TLSProxy::Record::VERS_TLS_1_2
    && TLSProxy::Proxy->is_tls13(),
    "Reverse order versions");
 
@@ -107,7 +107,7 @@ $testtype = WITH_TLS1_4;
 $proxy->start();
 $record = pop @{$proxy->record_list};
 ok(TLSProxy::Message->success()
-   && $record->version() == TLSProxy::Record::VERS_TLS_1_0
+   && $record->version() == TLSProxy::Record::VERS_TLS_1_2
    && TLSProxy::Proxy->is_tls13(),
    "TLS1.4 in supported versions extension");
 
diff --git a/test/recipes/70-test_tls13cookie.t b/test/recipes/70-test_tls13cookie.t
index 3d3a10f..289e589 100644
--- a/test/recipes/70-test_tls13cookie.t
+++ b/test/recipes/70-test_tls13cookie.t
@@ -74,7 +74,7 @@ sub cookie_filter
         0x04, 0x05;
 
     foreach my $message (@{$proxy->message_list}) {
-        if ($message->mt == TLSProxy::Message::MT_HELLO_RETRY_REQUEST
+        if ($message->mt == TLSProxy::Message::MT_SERVER_HELLO
                 && ${$message->records}[0]->flight == 1) {
             $message->delete_extension(TLSProxy::Message::EXT_KEY_SHARE)
                 if ($testtype == COOKIE_ONLY);
diff --git a/test/recipes/70-test_tls13kexmodes.t b/test/recipes/70-test_tls13kexmodes.t
index 97cbf76..7afb560 100644
--- a/test/recipes/70-test_tls13kexmodes.t
+++ b/test/recipes/70-test_tls13kexmodes.t
@@ -35,7 +35,7 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
 @handmessages = (
     [TLSProxy::Message::MT_CLIENT_HELLO,
         checkhandshake::ALL_HANDSHAKES],
-    [TLSProxy::Message::MT_HELLO_RETRY_REQUEST,
+    [TLSProxy::Message::MT_SERVER_HELLO,
         checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
     [TLSProxy::Message::MT_CLIENT_HELLO,
         checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
@@ -90,7 +90,9 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
         checkhandshake::PSK_CLI_EXTENSION],
 
-    [TLSProxy::Message::MT_HELLO_RETRY_REQUEST, TLSProxy::Message::EXT_KEY_SHARE,
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
         checkhandshake::KEY_SHARE_HRR_EXTENSION],
 
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
@@ -122,6 +124,8 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
         checkhandshake::PSK_CLI_EXTENSION],
 
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        checkhandshake::DEFAULT_EXTENSIONS],
     [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
         checkhandshake::KEY_SHARE_SRV_EXTENSION],
     [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_PSK,
diff --git a/test/recipes/70-test_tls13messages.t b/test/recipes/70-test_tls13messages.t
index 5bd2a96..2cf822a 100644
--- a/test/recipes/70-test_tls13messages.t
+++ b/test/recipes/70-test_tls13messages.t
@@ -35,7 +35,7 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
 @handmessages = (
     [TLSProxy::Message::MT_CLIENT_HELLO,
         checkhandshake::ALL_HANDSHAKES],
-    [TLSProxy::Message::MT_HELLO_RETRY_REQUEST,
+    [TLSProxy::Message::MT_SERVER_HELLO,
         checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
     [TLSProxy::Message::MT_CLIENT_HELLO,
         checkhandshake::HRR_HANDSHAKE | checkhandshake::HRR_RESUME_HANDSHAKE],
@@ -90,7 +90,9 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
         checkhandshake::PSK_CLI_EXTENSION],
 
-    [TLSProxy::Message::MT_HELLO_RETRY_REQUEST, TLSProxy::Message::EXT_KEY_SHARE,
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        checkhandshake::DEFAULT_EXTENSIONS],
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
         checkhandshake::KEY_SHARE_HRR_EXTENSION],
 
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_SERVER_NAME,
@@ -122,6 +124,8 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
     [TLSProxy::Message::MT_CLIENT_HELLO, TLSProxy::Message::EXT_PSK,
         checkhandshake::PSK_CLI_EXTENSION],
 
+    [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_SUPPORTED_VERSIONS,
+        checkhandshake::DEFAULT_EXTENSIONS],
     [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_KEY_SHARE,
         checkhandshake::DEFAULT_EXTENSIONS],
     [TLSProxy::Message::MT_SERVER_HELLO, TLSProxy::Message::EXT_PSK,
@@ -322,6 +326,6 @@ $proxy->start();
 checkhandshake($proxy, checkhandshake::DEFAULT_HANDSHAKE,
                checkhandshake::DEFAULT_EXTENSIONS
                | checkhandshake::SUPPORTED_GROUPS_SRV_EXTENSION,
-               "Default handshake test");
+               "Acceptable but non preferred key_share");
 
 unlink $session;
diff --git a/test/recipes/90-test_tls13encryption.t b/test/recipes/90-test_tls13ccs.t
similarity index 60%
copy from test/recipes/90-test_tls13encryption.t
copy to test/recipes/90-test_tls13ccs.t
index 63e62db..2ec28ce 100644
--- a/test/recipes/90-test_tls13encryption.t
+++ b/test/recipes/90-test_tls13ccs.t
@@ -1,15 +1,16 @@
 #! /usr/bin/env perl
-# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2017 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 OpenSSL::Test;
+
 use OpenSSL::Test::Utils;
+use OpenSSL::Test qw/:DEFAULT srctop_file/;
 
-my $test_name = "tls13encryption";
+my $test_name = "test_tls13ccs";
 setup($test_name);
 
 plan skip_all => "$test_name is not supported in this build"
@@ -17,4 +18,5 @@ plan skip_all => "$test_name is not supported in this build"
 
 plan tests => 1;
 
-ok(run(test(["tls13encryptiontest"])), "running tls13encryptiontest");
+ok(run(test(["tls13ccstest", srctop_file("apps", "server.pem"),
+             srctop_file("apps", "server.pem")])), "tls13ccstest");
diff --git a/test/tls13ccstest.c b/test/tls13ccstest.c
new file mode 100644
index 0000000..c51c2ce
--- /dev/null
+++ b/test/tls13ccstest.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright 2017 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
+ */
+
+#include <openssl/ssl.h>
+#include <string.h>
+#include "ssltestlib.h"
+#include "testutil.h"
+#include "../ssl/packet_locl.h"
+
+static char *cert = NULL;
+static char *privkey = NULL;
+
+static BIO *s_to_c_fbio = NULL, *c_to_s_fbio = NULL;
+static int chseen = 0, shseen = 0, sccsseen = 0, ccsaftersh = 0;
+static int ccsbeforesh = 0, sappdataseen = 0, cappdataseen = 0, badccs = 0;
+static int badvers = 0, badsessid = 0;
+
+static unsigned char chsessid[SSL_MAX_SSL_SESSION_ID_LENGTH];
+static size_t chsessidlen = 0;
+
+static int watchccs_new(BIO *bi);
+static int watchccs_free(BIO *a);
+static int watchccs_read(BIO *b, char *out, int outl);
+static int watchccs_write(BIO *b, const char *in, int inl);
+static long watchccs_ctrl(BIO *b, int cmd, long num, void *ptr);
+static int watchccs_gets(BIO *bp, char *buf, int size);
+static int watchccs_puts(BIO *bp, const char *str);
+
+/* Choose a sufficiently large type likely to be unused for this custom BIO */
+# define BIO_TYPE_WATCHCCS_FILTER  (0x80 | BIO_TYPE_FILTER)
+
+static BIO_METHOD *method_watchccs = NULL;
+
+static const BIO_METHOD *bio_f_watchccs_filter()
+{
+    if (method_watchccs == NULL) {
+        method_watchccs = BIO_meth_new(BIO_TYPE_WATCHCCS_FILTER,
+                                       "Watch CCS filter");
+        if (   method_watchccs == NULL
+            || !BIO_meth_set_write(method_watchccs, watchccs_write)
+            || !BIO_meth_set_read(method_watchccs, watchccs_read)
+            || !BIO_meth_set_puts(method_watchccs, watchccs_puts)
+            || !BIO_meth_set_gets(method_watchccs, watchccs_gets)
+            || !BIO_meth_set_ctrl(method_watchccs, watchccs_ctrl)
+            || !BIO_meth_set_create(method_watchccs, watchccs_new)
+            || !BIO_meth_set_destroy(method_watchccs, watchccs_free))
+            return NULL;
+    }
+    return method_watchccs;
+}
+
+static int watchccs_new(BIO *bio)
+{
+    BIO_set_init(bio, 1);
+    return 1;
+}
+
+static int watchccs_free(BIO *bio)
+{
+    BIO_set_init(bio, 0);
+    return 1;
+}
+
+static int watchccs_read(BIO *bio, char *out, int outl)
+{
+    int ret = 0;
+    BIO *next = BIO_next(bio);
+
+    if (outl <= 0)
+        return 0;
+    if (next == NULL)
+        return 0;
+
+    BIO_clear_retry_flags(bio);
+
+    ret = BIO_read(next, out, outl);
+    if (ret <= 0 && BIO_should_read(next))
+        BIO_set_retry_read(bio);
+
+    return ret;
+}
+
+static int watchccs_write(BIO *bio, const char *in, int inl)
+{
+    int ret = 0;
+    BIO *next = BIO_next(bio);
+    PACKET pkt, msg, msgbody, sessionid;
+    unsigned int rectype, recvers, msgtype, expectedrecvers;
+
+    if (inl <= 0)
+        return 0;
+    if (next == NULL)
+        return 0;
+
+    BIO_clear_retry_flags(bio);
+
+    if (!PACKET_buf_init(&pkt, (const unsigned char *)in, inl))
+        return 0;
+
+    /* We assume that we always write complete records each time */
+    while (PACKET_remaining(&pkt)) {
+        if (!PACKET_get_1(&pkt, &rectype)
+                || !PACKET_get_net_2(&pkt, &recvers)
+                || !PACKET_get_length_prefixed_2(&pkt, &msg))
+            return 0;
+
+        expectedrecvers = TLS1_2_VERSION;
+
+        if (rectype == SSL3_RT_HANDSHAKE) {
+            if (!PACKET_get_1(&msg, &msgtype)
+                    || !PACKET_get_length_prefixed_3(&msg, &msgbody))
+                return 0;
+            if (msgtype == SSL3_MT_CLIENT_HELLO) {
+                chseen++;
+                expectedrecvers = TLS1_VERSION;
+                /*
+                 * Skip legacy_version (2 bytes) and Random (32 bytes) to read
+                 * session_id.
+                 */
+                if (!PACKET_forward(&msgbody, 34)
+                        || !PACKET_get_length_prefixed_1(&msgbody, &sessionid))
+                    return 0;
+
+                if (chseen == 1) {
+                    /* Save the session id for later */
+                    chsessidlen = PACKET_remaining(&sessionid);
+                    if (!PACKET_copy_bytes(&sessionid, chsessid, chsessidlen))
+                        return 0;
+                } else {
+                    /*
+                     * Check the session id for the second ClientHello is the
+                     * same as the first one.
+                     */
+                    if (PACKET_remaining(&sessionid) != chsessidlen
+                            || (chsessidlen > 0
+                                && memcmp(chsessid, PACKET_data(&sessionid),
+                                          chsessidlen) != 0))
+                        badsessid = 1;
+                }
+            } else if (msgtype == SSL3_MT_SERVER_HELLO) {
+                shseen++;
+                /*
+                 * Skip legacy_version (2 bytes) and Random (32 bytes) to read
+                 * session_id.
+                 */
+                if (!PACKET_forward(&msgbody, 34)
+                        || !PACKET_get_length_prefixed_1(&msgbody, &sessionid))
+                    return 0;
+
+                /*
+                 * Check the session id is the same as the one in the
+                 * ClientHello
+                 */
+                if (PACKET_remaining(&sessionid) != chsessidlen
+                        || (chsessidlen > 0
+                            && memcmp(chsessid, PACKET_data(&sessionid),
+                                      chsessidlen) != 0))
+                    badsessid = 1;
+            }
+        } else if (rectype == SSL3_RT_CHANGE_CIPHER_SPEC) {
+            if (bio == s_to_c_fbio) {
+                /*
+                 * Server writing. We shouldn't have written any app data
+                 * yet, and we should have seen both the ClientHello and the
+                 * ServerHello
+                 */
+                if (!sappdataseen
+                        && chseen == 1
+                        && shseen == 1
+                        && !sccsseen)
+                    sccsseen = 1;
+                else
+                    badccs = 1;
+            } else if (!cappdataseen) {
+                /*
+                 * Client writing. We shouldn't have written any app data
+                 * yet, and we should have seen the ClientHello
+                 */
+                if (shseen == 1 && !ccsaftersh)
+                    ccsaftersh = 1;
+                else if (shseen == 0 && !ccsbeforesh)
+                    ccsbeforesh = 1;
+                else
+                    badccs = 1;
+            } else {
+                badccs = 1;
+            }
+        } else if(rectype == SSL3_RT_APPLICATION_DATA) {
+            if (bio == s_to_c_fbio)
+                sappdataseen = 1;
+            else
+                cappdataseen = 1;
+        }
+        if (recvers != expectedrecvers)
+            badvers = 1;
+    }
+
+    ret = BIO_write(next, in, inl);
+    if (ret <= 0 && BIO_should_write(next))
+        BIO_set_retry_write(bio);
+
+    return ret;
+}
+
+static long watchccs_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+    long ret;
+    BIO *next = BIO_next(bio);
+
+    if (next == NULL)
+        return 0;
+
+    switch (cmd) {
+    case BIO_CTRL_DUP:
+        ret = 0;
+        break;
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    }
+    return ret;
+}
+
+static int watchccs_gets(BIO *bio, char *buf, int size)
+{
+    /* We don't support this - not needed anyway */
+    return -1;
+}
+
+static int watchccs_puts(BIO *bio, const char *str)
+{
+    return watchccs_write(bio, str, strlen(str));
+}
+
+static int test_tls13ccs(int tst)
+{
+    SSL_CTX *sctx = NULL, *cctx = NULL;
+    SSL *sssl = NULL, *cssl = NULL;
+    int ret = 0;
+    const char msg[] = "Dummy data";
+    char buf[80];
+    size_t written, readbytes;
+    SSL_SESSION *sess = NULL;
+
+    chseen = shseen = sccsseen = ccsaftersh = ccsbeforesh = 0;
+    sappdataseen = cappdataseen = badccs = badvers = badsessid = 0;
+    chsessidlen = 0;
+
+    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(),
+                                       &sctx, &cctx, cert, privkey)))
+        goto err;
+
+    /*
+     * Test 0: Simple Handshake
+     * Test 1: Simple Handshake, client middlebox compat mode disabled
+     * Test 2: Simple Handshake, server middlebox compat mode disabled
+     * Test 3: HRR Handshake
+     * Test 4: HRR Handshake, client middlebox compat mode disabled
+     * Test 5: HRR Handshake, server middlebox compat mode disabled
+     * Test 6: Early data handshake
+     * Test 7: Early data handshake, client middlebox compat mode disabled
+     * Test 8: Early data handshake, server middlebox compat mode disabled
+     * Test 9: Early data then HRR
+     * Test 10: Early data then HRR, client middlebox compat mode disabled
+     * Test 11: Early data then HRR, server middlebox compat mode disabled
+     */
+    switch (tst) {
+    case 0:
+    case 3:
+    case 6:
+    case 9:
+        break;
+    case 1:
+    case 4:
+    case 7:
+    case 10:
+        SSL_CTX_clear_options(cctx, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
+        break;
+    case 2:
+    case 5:
+    case 8:
+    case 11:
+        SSL_CTX_clear_options(sctx, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
+        break;
+    default:
+        TEST_error("Invalid test value");
+        goto err;
+    }
+
+    if (tst >= 6) {
+        /* Get a session suitable for early_data */
+        if (!TEST_true(create_ssl_objects(sctx, cctx, &sssl, &cssl, NULL, NULL))
+                || !TEST_true(create_ssl_connection(sssl, cssl, SSL_ERROR_NONE)))
+            goto err;
+        sess = SSL_get1_session(cssl);
+        if (!TEST_ptr(sess))
+            goto err;
+        SSL_shutdown(cssl);
+        SSL_shutdown(sssl);
+        SSL_free(sssl);
+        SSL_free(cssl);
+        sssl = cssl = NULL;
+    }
+
+    if ((tst >= 3 && tst <= 5) || tst >= 9) {
+        /* HRR handshake */
+        if (!TEST_true(SSL_CTX_set1_groups_list(sctx, "P-256")))
+            goto err;
+    }
+
+    s_to_c_fbio = BIO_new(bio_f_watchccs_filter());
+    c_to_s_fbio = BIO_new(bio_f_watchccs_filter());
+    if (!TEST_ptr(s_to_c_fbio)
+            || !TEST_ptr(c_to_s_fbio)) {
+        BIO_free(s_to_c_fbio);
+        BIO_free(c_to_s_fbio);
+        goto err;
+    }
+
+    /* BIOs get freed on error */
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &sssl, &cssl, s_to_c_fbio,
+                                      c_to_s_fbio)))
+        goto err;
+
+    if (tst >= 6) {
+        /* Early data */
+        if (!TEST_true(SSL_set_session(cssl, sess))
+                || !TEST_true(SSL_write_early_data(cssl, msg, strlen(msg),
+                                                   &written))
+                || (tst <= 8
+                    && !TEST_int_eq(SSL_read_early_data(sssl, buf,  sizeof(buf),
+                                                &readbytes),
+                                                SSL_READ_EARLY_DATA_SUCCESS)))
+            goto err;
+        if (tst <= 8) {
+            if (!TEST_int_gt(SSL_connect(cssl), 0))
+                goto err;
+        } else {
+            if (!TEST_int_le(SSL_connect(cssl), 0))
+                goto err;
+        }
+        if (!TEST_int_eq(SSL_read_early_data(sssl, buf,  sizeof(buf),
+                                             &readbytes),
+                         SSL_READ_EARLY_DATA_FINISH))
+            goto err;
+    }
+
+    /* Perform handshake (or complete it if doing early data ) */
+    if (!TEST_true(create_ssl_connection(sssl, cssl, SSL_ERROR_NONE)))
+        goto err;
+
+    /*
+     * Check there were no unexpected CCS messages, all record versions
+     * were as expected, and that the session ids were reflected by the server
+     * correctly.
+     */
+    if (!TEST_false(badccs) || !TEST_false(badvers) || !TEST_false(badsessid))
+        goto err;
+
+    switch (tst) {
+    case 0:
+        if (!TEST_true(sccsseen)
+                || !TEST_true(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 1:
+        if (!TEST_true(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_eq(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 2:
+        if (!TEST_false(sccsseen)
+                || !TEST_true(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 3:
+        if (!TEST_true(sccsseen)
+                || !TEST_true(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 4:
+        if (!TEST_true(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_eq(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 5:
+        if (!TEST_false(sccsseen)
+                || !TEST_true(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 6:
+        if (!TEST_true(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_true(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 7:
+        if (!TEST_true(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_eq(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 8:
+        if (!TEST_false(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_true(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 9:
+        if (!TEST_true(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_true(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 10:
+        if (!TEST_true(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_false(ccsbeforesh)
+                || !TEST_size_t_eq(chsessidlen, 0))
+            goto err;
+        break;
+
+    case 11:
+        if (!TEST_false(sccsseen)
+                || !TEST_false(ccsaftersh)
+                || !TEST_true(ccsbeforesh)
+                || !TEST_size_t_gt(chsessidlen, 0))
+            goto err;
+        break;
+
+    default:
+        TEST_error("Invalid test value");
+        goto err;
+    }
+
+    ret = 1;
+ err:
+    SSL_SESSION_free(sess);
+    SSL_free(sssl);
+    SSL_free(cssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return ret;
+}
+
+int setup_tests(void)
+{
+    if (!TEST_ptr(cert = test_get_argument(0))
+            || !TEST_ptr(privkey = test_get_argument(1)))
+        return 0;
+
+    ADD_ALL_TESTS(test_tls13ccs, 12);
+
+    return 1;
+}
+
+void cleanup_tests(void)
+{
+    BIO_meth_free(method_watchccs);
+}
diff --git a/util/perl/TLSProxy/HelloRetryRequest.pm b/util/perl/TLSProxy/HelloRetryRequest.pm
deleted file mode 100644
index c4125b7..0000000
--- a/util/perl/TLSProxy/HelloRetryRequest.pm
+++ /dev/null
@@ -1,150 +0,0 @@
-# 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::HelloRetryRequest;
-
-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_HELLO_RETRY_REQUEST,
-        $data,
-        $records,
-        $startoffset,
-        $message_frag_lens);
-
-    $self->{extension_data} = "";
-
-    return $self;
-}
-
-sub parse
-{
-    my $self = shift;
-    my $ptr = 2;
-
-    TLSProxy::Proxy->is_tls13(1);
-
-    my ($server_version) = unpack('n', $self->data);
-    # TODO(TLS1.3): Replace this reference to draft version before release
-    if ($server_version == TLSProxy::Record::VERS_TLS_1_3_DRAFT) {
-        $server_version = TLSProxy::Record::VERS_TLS_1_3;
-    }
-
-    my $ciphersuite = unpack('n', substr($self->data, $ptr));
-    $ptr += 2;
-
-    my $extensions_len = unpack('n', substr($self->data, $ptr));
-    if (!defined $extensions_len) {
-        $extensions_len = 0;
-    }
-
-    $ptr += 2;
-    my $extension_data;
-    if ($extensions_len != 0) {
-        $extension_data = substr($self->data, $ptr);
-
-        if (length($extension_data) != $extensions_len) {
-            die "Invalid extension length\n";
-        }
-    } else {
-        if (length($self->data) != 2) {
-            die "Invalid extension length\n";
-        }
-        $extension_data = "";
-    }
-    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;
-    }
-
-    $self->server_version($server_version);
-    $self->ciphersuite($ciphersuite);
-    $self->extension_data(\%extensions);
-
-    print "    Server Version:".$server_version."\n";
-    print "    Ciphersuite:".$ciphersuite."\n";
-    print "    Extensions Len:".$extensions_len."\n";
-}
-
-#Reconstruct the on-the-wire message data following changes
-sub set_message_contents
-{
-    my $self = shift;
-    my $data;
-    my $extensions = "";
-
-    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('n', $self->server_version);
-    $data .= pack('n', $self->ciphersuite);
-    $data .= pack('n', length($extensions));
-    $data .= $extensions;
-    $self->data($data);
-}
-
-#Read/write accessors
-sub server_version
-{
-    my $self = shift;
-    if (@_) {
-      $self->{server_version} = shift;
-    }
-    return $self->{server_version};
-}
-sub ciphersuite
-{
-    my $self = shift;
-    if (@_) {
-      $self->{ciphersuite} = shift;
-    }
-    return $self->{ciphersuite};
-}
-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/perl/TLSProxy/Message.pm b/util/perl/TLSProxy/Message.pm
index 1c2bd20..1777e24 100644
--- a/util/perl/TLSProxy/Message.pm
+++ b/util/perl/TLSProxy/Message.pm
@@ -17,7 +17,6 @@ use constant {
     MT_CLIENT_HELLO => 1,
     MT_SERVER_HELLO => 2,
     MT_NEW_SESSION_TICKET => 4,
-    MT_HELLO_RETRY_REQUEST => 6,
     MT_ENCRYPTED_EXTENSIONS => 8,
     MT_CERTIFICATE => 11,
     MT_SERVER_KEY_EXCHANGE => 12,
@@ -48,7 +47,6 @@ my %message_type = (
     MT_CLIENT_HELLO, "ClientHello",
     MT_SERVER_HELLO, "ServerHello",
     MT_NEW_SESSION_TICKET, "NewSessionTicket",
-    MT_HELLO_RETRY_REQUEST, "HelloRetryRequest",
     MT_ENCRYPTED_EXTENSIONS, "EncryptedExtensions",
     MT_CERTIFICATE, "Certificate",
     MT_SERVER_KEY_EXCHANGE, "ServerKeyExchange",
@@ -172,10 +170,12 @@ sub get_messages
             #We can't handle this yet
             die "CCS received before message data complete\n";
         }
-        if ($server) {
-            TLSProxy::Record->server_encrypting(1);
-        } else {
-            TLSProxy::Record->client_encrypting(1);
+        if (!TLSProxy::Proxy->is_tls13()) {
+            if ($server) {
+                TLSProxy::Record->server_encrypting(1);
+            } else {
+                TLSProxy::Record->client_encrypting(1);
+            }
         }
     } elsif ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) {
         if ($record->len == 0 || $record->len_real == 0) {
@@ -296,15 +296,6 @@ sub create_message
             [@message_frag_lens]
         );
         $message->parse();
-    } elsif ($mt == MT_HELLO_RETRY_REQUEST) {
-        $message = TLSProxy::HelloRetryRequest->new(
-            $server,
-            $data,
-            [@message_rec_list],
-            $startoffset,
-            [@message_frag_lens]
-        );
-        $message->parse();
     } elsif ($mt == MT_SERVER_HELLO) {
         $message = TLSProxy::ServerHello->new(
             $server,
diff --git a/util/perl/TLSProxy/Proxy.pm b/util/perl/TLSProxy/Proxy.pm
index 83a6494..99b0ded 100644
--- a/util/perl/TLSProxy/Proxy.pm
+++ b/util/perl/TLSProxy/Proxy.pm
@@ -16,7 +16,6 @@ use IO::Select;
 use TLSProxy::Record;
 use TLSProxy::Message;
 use TLSProxy::ClientHello;
-use TLSProxy::HelloRetryRequest;
 use TLSProxy::ServerHello;
 use TLSProxy::EncryptedExtensions;
 use TLSProxy::Certificate;
diff --git a/util/perl/TLSProxy/Record.pm b/util/perl/TLSProxy/Record.pm
index 5017c90..61ac8e2 100644
--- a/util/perl/TLSProxy/Record.pm
+++ b/util/perl/TLSProxy/Record.pm
@@ -36,7 +36,7 @@ my %record_type = (
 
 use constant {
     VERS_TLS_1_4 => 0x0305,
-    VERS_TLS_1_3_DRAFT => 0x7f15,
+    VERS_TLS_1_3_DRAFT => 0x7f16,
     VERS_TLS_1_3 => 0x0304,
     VERS_TLS_1_2 => 0x0303,
     VERS_TLS_1_1 => 0x0302,
@@ -109,19 +109,21 @@ sub get_records
                 substr($packet, TLS_RECORD_HEADER_LENGTH, $len_real)
             );
 
-            if (($server && $server_encrypting)
-                     || (!$server && $client_encrypting)) {
-                if (!TLSProxy::Proxy->is_tls13() && $etm) {
-                    $record->decryptETM();
-                } else {
-                    $record->decrypt();
+            if ($content_type != RT_CCS) {
+                if (($server && $server_encrypting)
+                         || (!$server && $client_encrypting)) {
+                    if (!TLSProxy::Proxy->is_tls13() && $etm) {
+                        $record->decryptETM();
+                    } else {
+                        $record->decrypt();
+                    }
+                    $record->encrypted(1);
+
+                    if (TLSProxy::Proxy->is_tls13()) {
+                        print "  Inner content type: "
+                              .$record_type{$record->content_type()}."\n";
+                    }
                 }
-                $record->encrypted(1);
-            }
-
-            if (TLSProxy::Proxy->is_tls13()) {
-                print "  Inner content type: "
-                      .$record_type{$record->content_type()}."\n";
             }
 
             push @record_list, $record;
diff --git a/util/perl/TLSProxy/ServerHello.pm b/util/perl/TLSProxy/ServerHello.pm
index 1abdd05..934eaf4 100644
--- a/util/perl/TLSProxy/ServerHello.pm
+++ b/util/perl/TLSProxy/ServerHello.pm
@@ -12,6 +12,11 @@ package TLSProxy::ServerHello;
 use vars '@ISA';
 push @ISA, 'TLSProxy::Message';
 
+my $hrrrandom = pack("C*", 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE,
+                           0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2,
+                           0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09,
+                           0xE2, 0xC8, 0xA8, 0x33, 0x9C);
+
 sub new
 {
     my $class = shift;
@@ -45,30 +50,23 @@ sub parse
     my $self = shift;
     my $ptr = 2;
     my ($server_version) = unpack('n', $self->data);
-
-    # TODO(TLS1.3): Replace this reference to draft version before release
-    if ($server_version == TLSProxy::Record::VERS_TLS_1_3_DRAFT) {
-        $server_version = TLSProxy::Record::VERS_TLS_1_3;
-        TLSProxy::Proxy->is_tls13(1);
-    }
+    my $neg_version = $server_version;
 
     my $random = substr($self->data, $ptr, 32);
     $ptr += 32;
     my $session_id_len = 0;
     my $session = "";
-    if (!TLSProxy::Proxy->is_tls13()) {
-        $session_id_len = unpack('C', substr($self->data, $ptr));
-        $ptr++;
-        $session = substr($self->data, $ptr, $session_id_len);
-        $ptr += $session_id_len;
-    }
+    $session_id_len = unpack('C', substr($self->data, $ptr));
+    $ptr++;
+    $session = substr($self->data, $ptr, $session_id_len);
+    $ptr += $session_id_len;
+
     my $ciphersuite = unpack('n', substr($self->data, $ptr));
     $ptr += 2;
     my $comp_meth = 0;
-    if (!TLSProxy::Proxy->is_tls13()) {
-        $comp_meth = unpack('C', substr($self->data, $ptr));
-        $ptr++;
-    }
+    $comp_meth = unpack('C', substr($self->data, $ptr));
+    $ptr++;
+
     my $extensions_len = unpack('n', substr($self->data, $ptr));
     if (!defined $extensions_len) {
         $extensions_len = 0;
@@ -96,6 +94,20 @@ sub parse
         my $extdata = substr($extension_data, 4, $size);
         $extension_data = substr($extension_data, 4 + $size);
         $extensions{$type} = $extdata;
+        if ($type == TLSProxy::Message::EXT_SUPPORTED_VERSIONS) {
+            $neg_version = unpack('n', $extdata);
+        }
+    }
+
+    if ($random eq $hrrrandom) {
+        TLSProxy::Proxy->is_tls13(1);
+        # TODO(TLS1.3): Replace this reference to draft version before release
+    } elsif ($neg_version == TLSProxy::Record::VERS_TLS_1_3_DRAFT) {
+        $neg_version = TLSProxy::Record::VERS_TLS_1_3;
+        TLSProxy::Proxy->is_tls13(1);
+
+        TLSProxy::Record->server_encrypting(1);
+        TLSProxy::Record->client_encrypting(1);
     }
 
     $self->server_version($server_version);
@@ -109,10 +121,6 @@ sub parse
 
     $self->process_data();
 
-    if (TLSProxy::Proxy->is_tls13()) {
-        TLSProxy::Record->server_encrypting(1);
-        TLSProxy::Record->client_encrypting(1);
-    }
 
     print "    Server Version:".$server_version."\n";
     print "    Session ID Len:".$session_id_len."\n";
@@ -138,14 +146,10 @@ sub set_message_contents
 
     $data = pack('n', $self->server_version);
     $data .= $self->random;
-    if (!TLSProxy::Proxy->is_tls13()) {
-        $data .= pack('C', $self->session_id_len);
-        $data .= $self->session;
-    }
+    $data .= pack('C', $self->session_id_len);
+    $data .= $self->session;
     $data .= pack('n', $self->ciphersuite);
-    if (!TLSProxy::Proxy->is_tls13()) {
-        $data .= pack('C', $self->comp_meth);
-    }
+    $data .= pack('C', $self->comp_meth);
 
     foreach my $key (keys %{$self->extension_data}) {
         my $extdata = ${$self->extension_data}{$key};
diff --git a/util/perl/checkhandshake.pm b/util/perl/checkhandshake.pm
index 65c5135..e1667d5 100644
--- a/util/perl/checkhandshake.pm
+++ b/util/perl/checkhandshake.pm
@@ -69,10 +69,33 @@ sub checkhandshake($$$$)
         my $extcount;
         my $clienthelloseen = 0;
 
+        my $lastmt = 0;
+        my $numsh = 0;
+        if (TLSProxy::Proxy::is_tls13()) {
+            #How many ServerHellos are we expecting?
+            for ($numtests = 0; $handmessages[$loop][1] != 0; $loop++) {
+                next if (($handmessages[$loop][1] & $handtype) == 0);
+                $numsh++ if ($lastmt != TLSProxy::Message::MT_SERVER_HELLO
+                             && $handmessages[$loop][0] == TLSProxy::Message::MT_SERVER_HELLO);
+                $lastmt = $handmessages[$loop][0];
+            }
+        }
+
         #First count the number of tests
         my $nextmess = 0;
         my $message = undef;
         my $chnum = 0;
+        my $shnum = 0;
+        if (!TLSProxy::Proxy::is_tls13()) {
+            # In non-TLSv1.3 we always treat reneg CH and SH like the first CH
+            # and SH
+            $chnum = 1;
+            $shnum = 1;
+        }
+        #If we're only expecting one ServerHello out of two then we skip the
+        #first ServerHello in the list completely
+        $shnum++ if ($numsh == 1 && TLSProxy::Proxy::is_tls13());
+        $loop = 0;
         for ($numtests = 0; $handmessages[$loop][1] != 0; $loop++) {
             next if (($handmessages[$loop][1] & $handtype) == 0);
             if (scalar @{$proxy->message_list} > $nextmess) {
@@ -84,10 +107,11 @@ sub checkhandshake($$$$)
             $numtests++;
 
             next if (!defined $message);
-            $chnum = 1 if $message->mt() != TLSProxy::Message::MT_CLIENT_HELLO
-                          && TLSProxy::Proxy::is_tls13();
+            if (TLSProxy::Proxy::is_tls13()) {
+                $chnum++ if $message->mt() == TLSProxy::Message::MT_CLIENT_HELLO;
+                $shnum++ if $message->mt() == TLSProxy::Message::MT_SERVER_HELLO;
+            }
             next if ($message->mt() != TLSProxy::Message::MT_CLIENT_HELLO
-                    && $message->mt() != TLSProxy::Message::MT_HELLO_RETRY_REQUEST
                     && $message->mt() != TLSProxy::Message::MT_SERVER_HELLO
                     && $message->mt() !=
                        TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS
@@ -96,14 +120,19 @@ sub checkhandshake($$$$)
             next if $message->mt() == TLSProxy::Message::MT_CERTIFICATE
                     && !TLSProxy::Proxy::is_tls13();
 
-            my $extchnum = 0;
+            my $extchnum = 1;
+            my $extshnum = 1;
             for (my $extloop = 0;
                     $extensions[$extloop][2] != 0;
                     $extloop++) {
-                $extchnum = 1 if $extensions[$extloop][0] != TLSProxy::Message::MT_CLIENT_HELLO
+                $extchnum = 2 if $extensions[$extloop][0] != TLSProxy::Message::MT_CLIENT_HELLO
                                  && TLSProxy::Proxy::is_tls13();
+                $extshnum = 2 if $extensions[$extloop][0] != TLSProxy::Message::MT_SERVER_HELLO
+                                 && $extchnum == 2;
                 next if $extensions[$extloop][0] == TLSProxy::Message::MT_CLIENT_HELLO
                                  && $extchnum != $chnum;
+                next if $extensions[$extloop][0] == TLSProxy::Message::MT_SERVER_HELLO
+                                 && $extshnum != $shnum;
                 next if ($message->mt() != $extensions[$extloop][0]);
                 $numtests++;
             }
@@ -114,7 +143,18 @@ sub checkhandshake($$$$)
 
         $nextmess = 0;
         $message = undef;
-        $chnum = 0;
+        if (TLSProxy::Proxy::is_tls13()) {
+            $chnum = 0;
+            $shnum = 0;
+        } else {
+            # In non-TLSv1.3 we always treat reneg CH and SH like the first CH
+            # and SH
+            $chnum = 1;
+            $shnum = 1;
+        }
+        #If we're only expecting one ServerHello out of two then we skip the
+        #first ServerHello in the list completely
+        $shnum++ if ($numsh == 1 && TLSProxy::Proxy::is_tls13());
         for ($loop = 0; $handmessages[$loop][1] != 0; $loop++) {
             next if (($handmessages[$loop][1] & $handtype) == 0);
             if (scalar @{$proxy->message_list} > $nextmess) {
@@ -132,11 +172,12 @@ sub checkhandshake($$$$)
                    "Message type check. Got ".$message->mt
                    .", expected ".$handmessages[$loop][0]);
             }
-            $chnum = 1 if $message->mt() != TLSProxy::Message::MT_CLIENT_HELLO
-                          && TLSProxy::Proxy::is_tls13();
+            if (TLSProxy::Proxy::is_tls13()) {
+                $chnum++ if $message->mt() == TLSProxy::Message::MT_CLIENT_HELLO;
+                $shnum++ if $message->mt() == TLSProxy::Message::MT_SERVER_HELLO;
+            }
 
             next if ($message->mt() != TLSProxy::Message::MT_CLIENT_HELLO
-                    && $message->mt() != TLSProxy::Message::MT_HELLO_RETRY_REQUEST
                     && $message->mt() != TLSProxy::Message::MT_SERVER_HELLO
                     && $message->mt() !=
                        TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS
@@ -153,16 +194,21 @@ sub checkhandshake($$$$)
             }
             #Now check that we saw the extensions we expected
             my $msgexts = $message->extension_data();
-            my $extchnum = 0;
+            my $extchnum = 1;
+            my $extshnum = 1;
             for (my $extloop = 0, $extcount = 0; $extensions[$extloop][2] != 0;
                                 $extloop++) {
                 #In TLSv1.3 we can have two ClientHellos if there has been a
                 #HelloRetryRequest, and they may have different extensions. Skip
                 #if these are extensions for a different ClientHello
-                $extchnum = 1 if $extensions[$extloop][0] != TLSProxy::Message::MT_CLIENT_HELLO
+                $extchnum = 2 if $extensions[$extloop][0] != TLSProxy::Message::MT_CLIENT_HELLO
                                  && TLSProxy::Proxy::is_tls13();
+                $extshnum = 2 if $extensions[$extloop][0] != TLSProxy::Message::MT_SERVER_HELLO
+                                 && $extchnum == 2;
                 next if $extensions[$extloop][0] == TLSProxy::Message::MT_CLIENT_HELLO
                                  && $extchnum != $chnum;
+                next if $extensions[$extloop][0] == TLSProxy::Message::MT_SERVER_HELLO
+                                 && $extshnum != $shnum;
                 next if ($message->mt() != $extensions[$extloop][0]);
                 ok (($extensions[$extloop][2] & $exttype) == 0
                       || defined ($msgexts->{$extensions[$extloop][1]}),


More information about the openssl-commits mailing list