Add TLS 1.3 1-RTT.

This adds the machinery for doing TLS 1.3 1RTT.

Change-Id: I736921ffe9dc6f6e64a08a836df6bb166d20f504
Reviewed-on: https://boringssl-review.googlesource.com/8720
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index f9d557e..5800d41 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -44,6 +44,7 @@
 SSL,142,DIGEST_CHECK_FAILED
 SSL,254,DOWNGRADE_DETECTED
 SSL,143,DTLS_MESSAGE_TOO_BIG
+SSL,257,DUPLICATE_EXTENSION
 SSL,144,ECC_CERT_NOT_FOR_SIGNING
 SSL,145,EMS_STATE_INCONSISTENT
 SSL,146,ENCRYPTED_LENGTH_TOO_LONG
@@ -59,6 +60,7 @@
 SSL,156,HTTP_REQUEST
 SSL,157,INAPPROPRIATE_FALLBACK
 SSL,158,INVALID_COMMAND
+SSL,256,INVALID_COMPRESSION_LIST
 SSL,159,INVALID_MESSAGE
 SSL,251,INVALID_OUTER_RECORD_TYPE
 SSL,160,INVALID_SSL_SESSION
@@ -66,6 +68,7 @@
 SSL,162,LENGTH_MISMATCH
 SSL,163,LIBRARY_HAS_NO_CIPHERS
 SSL,164,MISSING_EXTENSION
+SSL,258,MISSING_KEY_SHARE
 SSL,165,MISSING_RSA_CERTIFICATE
 SSL,166,MISSING_TMP_DH_KEY
 SSL,167,MISSING_TMP_ECDH_KEY
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 44026aa..d84ca30 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -548,6 +548,8 @@
 #define DTLS1_VERSION 0xfeff
 #define DTLS1_2_VERSION 0xfefd
 
+#define TLS1_3_DRAFT_VERSION 13
+
 /* SSL_CTX_set_min_version sets the minimum protocol version for |ctx| to
  * |version|. */
 OPENSSL_EXPORT void SSL_CTX_set_min_version(SSL_CTX *ctx, uint16_t version);
@@ -2655,6 +2657,7 @@
 #define SSL_AD_INTERNAL_ERROR TLS1_AD_INTERNAL_ERROR
 #define SSL_AD_USER_CANCELLED TLS1_AD_USER_CANCELLED
 #define SSL_AD_NO_RENEGOTIATION TLS1_AD_NO_RENEGOTIATION
+#define SSL_AD_MISSING_EXTENSION TLS1_AD_MISSING_EXTENSION
 #define SSL_AD_UNSUPPORTED_EXTENSION TLS1_AD_UNSUPPORTED_EXTENSION
 #define SSL_AD_CERTIFICATE_UNOBTAINABLE TLS1_AD_CERTIFICATE_UNOBTAINABLE
 #define SSL_AD_UNRECOGNIZED_NAME TLS1_AD_UNRECOGNIZED_NAME
@@ -2919,6 +2922,7 @@
 #define SSL_ST_INIT (SSL_ST_CONNECT | SSL_ST_ACCEPT)
 #define SSL_ST_OK 0x03
 #define SSL_ST_RENEGOTIATE (0x04 | SSL_ST_INIT)
+#define SSL_ST_TLS13 (0x05 | SSL_ST_INIT)
 
 /* SSL_CB_* are possible values for the |type| parameter in the info
  * callback and the bitmasks that make them up. */
@@ -3538,6 +3542,7 @@
 typedef struct ssl_protocol_method_st SSL_PROTOCOL_METHOD;
 typedef struct ssl3_enc_method SSL3_ENC_METHOD;
 typedef struct ssl_aead_ctx_st SSL_AEAD_CTX;
+typedef struct ssl_handshake_st SSL_HANDSHAKE;
 
 struct ssl_cipher_st {
   /* name is the OpenSSL name for the cipher. */
@@ -3578,6 +3583,8 @@
    * A zero indicates that the value is unknown. */
   uint32_t key_exchange_info;
 
+  /* master_key, in TLS 1.2 and below, is the master secret associated with the
+   * session. In TLS 1.3 and up, it is the resumption secret. */
   int master_key_length;
   uint8_t master_key[SSL_MAX_MASTER_KEY_LENGTH];
 
@@ -4234,11 +4241,21 @@
   uint8_t *pending_message;
   uint32_t pending_message_len;
 
+  /* hs is the handshake state for the current handshake or NULL if there isn't
+   * one. */
+  SSL_HANDSHAKE *hs;
+
+  uint8_t write_traffic_secret[EVP_MAX_MD_SIZE];
+  uint8_t write_traffic_secret_len;
+  uint8_t read_traffic_secret[EVP_MAX_MD_SIZE];
+  uint8_t read_traffic_secret_len;
+  uint8_t exporter_secret[EVP_MAX_MD_SIZE];
+  uint8_t exporter_secret_len;
+
   /* State pertaining to the pending handshake.
    *
-   * TODO(davidben): State is current spread all over the place. Move
-   * pending handshake state here so it can be managed separately from
-   * established connection state in case of renegotiations. */
+   * TODO(davidben): Move everything not needed after the handshake completes to
+   * |hs| and remove this. */
   struct {
     uint8_t finish_md[EVP_MAX_MD_SIZE];
     uint8_t finish_md_len;
@@ -4715,6 +4732,9 @@
 #define SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS 253
 #define SSL_R_DOWNGRADE_DETECTED 254
 #define SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE 255
+#define SSL_R_INVALID_COMPRESSION_LIST 256
+#define SSL_R_DUPLICATE_EXTENSION 257
+#define SSL_R_MISSING_KEY_SHARE 258
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index b71c1de..beab572 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -392,6 +392,8 @@
 #define SSL3_MT_CLIENT_HELLO 1
 #define SSL3_MT_SERVER_HELLO 2
 #define SSL3_MT_NEW_SESSION_TICKET 4
+#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
 #define SSL3_MT_CERTIFICATE_REQUEST 13
@@ -401,6 +403,7 @@
 #define SSL3_MT_FINISHED 20
 #define SSL3_MT_CERTIFICATE_STATUS 22
 #define SSL3_MT_SUPPLEMENTAL_DATA 23
+#define SSL3_MT_KEY_UPDATE 24
 #define SSL3_MT_NEXT_PROTO 67
 #define SSL3_MT_CHANNEL_ID 203
 #define DTLS1_MT_HELLO_VERIFY_REQUEST 3
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index ae1ab7c..3c97d26 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -211,6 +211,14 @@
 #define TLSEXT_TYPE_early_data 42
 #define TLSEXT_TYPE_cookie 44
 
+/* TLSEXT_TYPE_draft_version is the extension used to advertise the TLS 1.3
+ * draft implemented.
+ *
+ * See
+ * https://github.com/tlswg/tls13-spec/wiki/Implementations#version-negotiation
+ */
+#define TLSEXT_TYPE_draft_version 0xff02
+
 /* ExtensionType value from RFC5746 */
 #define TLSEXT_TYPE_renegotiate 0xff01
 
diff --git a/ssl/CMakeLists.txt b/ssl/CMakeLists.txt
index d5cb870..9287a63 100644
--- a/ssl/CMakeLists.txt
+++ b/ssl/CMakeLists.txt
@@ -31,6 +31,10 @@
   t1_lib.c
   tls_method.c
   tls_record.c
+  tls13_both.c
+  tls13_client.c
+  tls13_enc.c
+  tls13_server.c
 )
 
 target_link_libraries(ssl crypto)
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index ee2de2b..4a82c49 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -200,7 +200,9 @@
       case SSL_ST_CONNECT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
-        if (!ssl->method->begin_handshake(ssl)) {
+        ssl->s3->hs = ssl_handshake_new(tls13_client_handshake);
+        if (ssl->s3->hs == NULL ||
+            !ssl->method->begin_handshake(ssl)) {
           ret = -1;
           goto end;
         }
@@ -244,6 +246,9 @@
 
       case SSL3_ST_CR_SRVR_HELLO_A:
         ret = ssl3_get_server_hello(ssl);
+        if (ssl->state == SSL_ST_TLS13) {
+          break;
+        }
         if (ret <= 0) {
           goto end;
         }
@@ -490,6 +495,14 @@
         }
         break;
 
+      case SSL_ST_TLS13:
+        ret = tls13_handshake(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
+        ssl->state = SSL_ST_OK;
+        break;
+
       case SSL_ST_OK:
         /* clean a few things up */
         ssl3_cleanup_key_block(ssl);
@@ -499,6 +512,9 @@
         /* Remove write buffering now. */
         ssl_free_wbio_buffer(ssl);
 
+        ssl_handshake_free(ssl->s3->hs);
+        ssl->s3->hs = NULL;
+
         const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
 
         ssl->s3->tmp.in_false_start = 0;
@@ -730,8 +746,7 @@
   uint16_t server_wire_version, server_version, cipher_suite;
   uint8_t compression_method;
 
-  int ret =
-      ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_HELLO, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
   if (ret <= 0) {
     uint32_t err = ERR_peek_error();
     if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
@@ -747,14 +762,16 @@
     return ret;
   }
 
+  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO &&
+      ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    return -1;
+  }
+
   CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
 
-  if (!CBS_get_u16(&server_hello, &server_wire_version) ||
-      !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
-      CBS_len(&session_id) > SSL3_SESSION_ID_SIZE ||
-      !CBS_get_u16(&server_hello, &cipher_suite) ||
-      !CBS_get_u8(&server_hello, &compression_method)) {
+  if (!CBS_get_u16(&server_hello, &server_wire_version)) {
     al = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     goto f_err;
@@ -784,6 +801,27 @@
     goto f_err;
   }
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    ssl->state = SSL_ST_TLS13;
+    return 1;
+  }
+
+  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    return -1;
+  }
+
+  if (!CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
+      CBS_len(&session_id) > SSL3_SESSION_ID_SIZE ||
+      !CBS_get_u16(&server_hello, &cipher_suite) ||
+      !CBS_get_u8(&server_hello, &compression_method)) {
+    al = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    goto f_err;
+  }
+
   /* Copy over the server random. */
   memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE);
 
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 52b681c..8427a29 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -201,7 +201,9 @@
       case SSL_ST_ACCEPT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
-        if (!ssl->method->begin_handshake(ssl)) {
+        ssl->s3->hs = ssl_handshake_new(tls13_server_handshake);
+        if (ssl->s3->hs == NULL ||
+            !ssl->method->begin_handshake(ssl)) {
           ret = -1;
           goto end;
         }
@@ -226,6 +228,9 @@
       case SSL3_ST_SR_CLNT_HELLO_B:
       case SSL3_ST_SR_CLNT_HELLO_C:
         ret = ssl3_get_client_hello(ssl);
+        if (ssl->state == SSL_ST_TLS13) {
+          break;
+        }
         if (ret <= 0) {
           goto end;
         }
@@ -458,6 +463,14 @@
         }
         break;
 
+      case SSL_ST_TLS13:
+        ret = tls13_handshake(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
+        ssl->state = SSL_ST_OK;
+        break;
+
       case SSL_ST_OK:
         /* clean a few things up */
         ssl3_cleanup_key_block(ssl);
@@ -467,6 +480,9 @@
         /* remove buffering on output */
         ssl_free_wbio_buffer(ssl);
 
+        ssl_handshake_free(ssl->s3->hs);
+        ssl->s3->hs = NULL;
+
         /* If we aren't retaining peer certificates then we can discard it
          * now. */
         if (ssl->ctx->retain_only_sha256_of_client_certs) {
@@ -569,10 +585,7 @@
   }
 
   CBS_init(&client_hello, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u16(&client_hello, &client_wire_version) ||
-      !CBS_get_bytes(&client_hello, &client_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
-      CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+  if (!CBS_get_u16(&client_hello, &client_wire_version)) {
     al = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     goto f_err;
@@ -584,20 +597,6 @@
    * see RFC 2246, Appendix E, second paragraph) */
   ssl->client_version = client_wire_version;
 
-  /* Load the client random. */
-  memcpy(ssl->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE);
-
-  if (SSL_IS_DTLS(ssl)) {
-    CBS cookie;
-
-    if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) ||
-        CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto f_err;
-    }
-  }
-
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     al = SSL_AD_PROTOCOL_VERSION;
@@ -632,6 +631,33 @@
     goto f_err;
   }
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    ssl->state = SSL_ST_TLS13;
+    return 1;
+  }
+
+  if (!CBS_get_bytes(&client_hello, &client_random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
+      CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+    al = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    goto f_err;
+  }
+
+  /* Load the client random. */
+  memcpy(ssl->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE);
+
+  if (SSL_IS_DTLS(ssl)) {
+    CBS cookie;
+
+    if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) ||
+        CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      goto f_err;
+    }
+  }
+
   ssl->hit = 0;
   int send_new_ticket = 0;
   switch (ssl_get_prev_session(ssl, &session, &send_new_ticket, &early_ctx)) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 5620c53..a70400b 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -615,6 +615,9 @@
  * call it in the zero state. */
 void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx);
 
+/* SSL_ECDH_CTX_get_id returns the group ID for |ctx|. */
+uint16_t SSL_ECDH_CTX_get_id(const SSL_ECDH_CTX *ctx);
+
 /* SSL_ECDH_CTX_get_key calls the |get_key| method of |SSL_ECDH_METHOD|. */
 int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out);
 
@@ -772,6 +775,141 @@
 int ssl_check_leaf_certificate(SSL *ssl, X509 *leaf);
 
 
+/* TLS 1.3 key derivation. */
+
+/* tls13_init_key_schedule initializes the handshake hash and key derivation
+ * state with the given resumption context. The cipher suite and PRF hash must
+ * have been selected at this point. It returns one on success and zero on
+ * error. */
+int tls13_init_key_schedule(SSL *ssl, const uint8_t *resumption_ctx,
+                            size_t resumption_ctx_len);
+
+/* tls13_advance_key_schedule incorporates |in| into the key schedule with
+ * HKDF-Extract. It returns one on success and zero on error. */
+int tls13_advance_key_schedule(SSL *ssl, const uint8_t *in, size_t len);
+
+/* tls13_get_context_hashes writes Hash(Handshake Context) +
+ * Hash(resumption_context) to |out| which much have room for at least 2 *
+ * |EVP_MAX_MD_SIZE| bytes. On success, it returns one and sets |*out_len| to
+ * the number of bytes written. Otherwise, it returns zero. */
+int tls13_get_context_hashes(SSL *ssl, uint8_t *out, size_t *out_len);
+
+enum tls_record_type_t {
+  type_early_handshake,
+  type_early_data,
+  type_handshake,
+  type_data,
+};
+
+/* tls13_set_traffic_key sets the read or write traffic keys to |traffic_secret|
+ * for the given traffic phase |type|. It returns one on success and zero on
+ * error. */
+int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
+                          enum evp_aead_direction_t direction,
+                          const uint8_t *traffic_secret,
+                          size_t traffic_secret_len);
+
+/* tls13_set_handshake_traffic derives the handshake traffic secret and
+ * switches both read and write traffic to it. It returns one on success and
+ * zero on error. */
+int tls13_set_handshake_traffic(SSL *ssl);
+
+/* tls13_derive_traffic_secret_0 derives the initial application data traffic
+ * secret based on the handshake transcripts and |master_secret|. It returns one
+ * on success and zero on error. */
+int tls13_derive_traffic_secret_0(SSL *ssl);
+
+/* tls13_finalize_keys derives the |exporter_secret| and |resumption_secret|. */
+int tls13_finalize_keys(SSL *ssl);
+
+/* tls13_export_keying_material provides and exporter interface to use the
+ * |exporter_secret|. */
+int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
+                                 const char *label, size_t label_len,
+                                 const uint8_t *context, size_t context_len,
+                                 int use_context);
+
+/* tls13_finished_mac calculates the MAC of the handshake transcript to verify
+ * the integrity of the Finished message, and stores the result in |out| and
+ * length in |out_len|. |is_server| is 1 if this is for the Server Finished and
+ * 0 for the Client Finished. */
+int tls13_finished_mac(SSL *ssl, uint8_t *out, size_t *out_len, int is_server);
+
+
+/* Handshake functions. */
+
+enum ssl_hs_wait_t {
+  ssl_hs_error,
+  ssl_hs_ok,
+  ssl_hs_read_message,
+  ssl_hs_write_message,
+  ssl_hs_flush,
+  ssl_hs_x509_lookup,
+  ssl_hs_private_key_operation,
+};
+
+struct ssl_handshake_st {
+  /* wait contains the operation |do_handshake| is currently blocking on or
+   * |ssl_hs_ok| if none. */
+  enum ssl_hs_wait_t wait;
+
+  /* do_handshake runs the handshake. On completion, it returns |ssl_hs_ok|.
+   * Otherwise, it returns a value corresponding to what operation is needed to
+   * progress. */
+  enum ssl_hs_wait_t (*do_handshake)(SSL *ssl);
+
+  int state;
+
+  size_t hash_len;
+  uint8_t resumption_hash[EVP_MAX_MD_SIZE];
+  uint8_t secret[EVP_MAX_MD_SIZE];
+  uint8_t traffic_secret_0[EVP_MAX_MD_SIZE];
+
+  SSL_ECDH_CTX *groups;
+  size_t groups_len;
+  uint8_t *public_key;
+  size_t public_key_len;
+
+  uint8_t *cert_context;
+  size_t cert_context_len;
+} /* SSL_HANDSHAKE */;
+
+SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl));
+
+/* ssl_handshake_free releases all memory associated with |hs|. */
+void ssl_handshake_free(SSL_HANDSHAKE *hs);
+
+/* tls13_handshake runs the TLS 1.3 handshake. It returns one on success and <=
+ * 0 on error. */
+int tls13_handshake(SSL *ssl);
+
+/* The following are implementations of |do_handshake| for the client and
+ * server. */
+enum ssl_hs_wait_t tls13_client_handshake(SSL *ssl);
+enum ssl_hs_wait_t tls13_server_handshake(SSL *ssl);
+
+/* tls13_check_message_type checks if the current message has type |type|. If so
+ * it returns one. Otherwise, it sends an alert and returns zero. */
+int tls13_check_message_type(SSL *ssl, int type);
+
+int tls13_process_certificate(SSL *ssl);
+int tls13_process_certificate_verify(SSL *ssl);
+int tls13_process_finished(SSL *ssl);
+
+int tls13_prepare_certificate(SSL *ssl);
+enum ssl_private_key_result_t tls13_prepare_certificate_verify(
+    SSL *ssl, int is_first_run);
+int tls13_prepare_finished(SSL *ssl);
+
+int ext_key_share_parse_serverhello(SSL *ssl, uint8_t **out_secret,
+                                    size_t *out_secret_len, uint8_t *out_alert,
+                                    CBS *contents);
+int ext_key_share_parse_clienthello(SSL *ssl, uint8_t **out_secret,
+                                    size_t *out_secret_len, uint8_t *out_alert,
+                                    CBS *contents);
+int ext_key_share_add_serverhello(SSL *ssl, CBB *out);
+
+
 /* Underdocumented functions.
  *
  * Functions below here haven't been touched up and may be underdocumented. */
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 3827f15..c93722a 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -213,6 +213,7 @@
   OPENSSL_free(ssl->s3->tmp.peer_psk_identity_hint);
   ssl3_free_handshake_buffer(ssl);
   ssl3_free_handshake_hash(ssl);
+  ssl_handshake_free(ssl->s3->hs);
   OPENSSL_free(ssl->s3->next_proto_negotiated);
   OPENSSL_free(ssl->s3->alpn_selected);
   SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx);
diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c
index dec8288..060ef69 100644
--- a/ssl/s3_pkt.c
+++ b/ssl/s3_pkt.c
@@ -443,6 +443,13 @@
   /* Process unexpected records. */
 
   if (type == SSL3_RT_APPLICATION_DATA && rr->type == SSL3_RT_HANDSHAKE) {
+    if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+      /* TODO(svaldez): Handle TLS 1.3 post-handshake messages. For now,
+       * silently drop all handshake records. */
+      rr->length = 0;
+      goto start;
+    }
+
     /* If peer renegotiations are disabled, all out-of-order handshake records
      * are fatal. Renegotiations as a server are never supported. */
     if (ssl->server || !ssl3_can_renegotiate(ssl)) {
diff --git a/ssl/ssl_ecdh.c b/ssl/ssl_ecdh.c
index bc363e9..dd70bdb 100644
--- a/ssl/ssl_ecdh.c
+++ b/ssl/ssl_ecdh.c
@@ -567,6 +567,19 @@
   ctx->method = &kCECPQ1Method;
 }
 
+void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) {
+  if (ctx->method == NULL) {
+    return;
+  }
+  ctx->method->cleanup(ctx);
+  ctx->method = NULL;
+  ctx->data = NULL;
+}
+
+uint16_t SSL_ECDH_CTX_get_id(const SSL_ECDH_CTX *ctx) {
+  return ctx->method->group_id;
+}
+
 int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out) {
   if (ctx->method == NULL) {
     return 0;
@@ -581,15 +594,6 @@
   return ctx->method->add_key(cbb, out_contents);
 }
 
-void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) {
-  if (ctx->method == NULL) {
-    return;
-  }
-  ctx->method->cleanup(ctx);
-  ctx->method = NULL;
-  ctx->data = NULL;
-}
-
 int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key) {
   return ctx->method->offer(ctx, out_public_key);
 }
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 9842d96..66ce84d 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1122,7 +1122,8 @@
 }
 
 int SSL_get_extms_support(const SSL *ssl) {
-  return ssl->s3->tmp.extended_master_secret == 1;
+  return ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+         ssl->s3->tmp.extended_master_secret == 1;
 }
 
 int (*SSL_get_verify_callback(const SSL *ssl))(int, X509_STORE_CTX *) {
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index 053196d..74c5d41 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -501,6 +501,11 @@
     return 0;
   }
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return tls13_export_keying_material(ssl, out, out_len, label, label_len,
+                                        context, context_len, use_context);
+  }
+
   size_t seed_len = 2 * SSL3_RANDOM_SIZE;
   if (use_context) {
     if (context_len >= 1u << 16) {
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 9761dc9..9143ba8 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -808,6 +808,10 @@
 
 static int ext_ri_parse_serverhello(SSL *ssl, uint8_t *out_alert,
                                     CBS *contents) {
+  if (contents != NULL && ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 0;
+  }
+
   /* Servers may not switch between omitting the extension and supporting it.
    * See RFC 5746, sections 3.5 and 4.2. */
   if (ssl->s3->initial_handshake_complete &&
@@ -877,6 +881,10 @@
    * called after the initial handshake. */
   assert(!ssl->s3->initial_handshake_complete);
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 1;
+  }
+
   CBS fake_contents;
   static const uint8_t kFakeExtension[] = {0};
 
@@ -917,6 +925,10 @@
 }
 
 static int ext_ri_add_serverhello(SSL *ssl, CBB *out) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 1;
+  }
+
   CBB contents, prev_finished;
   if (!CBB_add_u16(out, TLSEXT_TYPE_renegotiate) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
@@ -960,7 +972,12 @@
     return 1;
   }
 
-  if (ssl->version == SSL3_VERSION || CBS_len(contents) != 0) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+      ssl->version == SSL3_VERSION) {
+    return 0;
+  }
+
+  if (CBS_len(contents) != 0) {
     return 0;
   }
 
@@ -970,7 +987,12 @@
 
 static int ext_ems_parse_clienthello(SSL *ssl, uint8_t *out_alert,
                                      CBS *contents) {
-  if (ssl->version == SSL3_VERSION || contents == NULL) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+      ssl->version == SSL3_VERSION) {
+    return 1;
+  }
+
+  if (contents == NULL) {
     return 1;
   }
 
@@ -1038,6 +1060,10 @@
     return 1;
   }
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 0;
+  }
+
   /* If |SSL_OP_NO_TICKET| is set then no extension will have been sent and
    * this function should never be called, even if the server tries to send the
    * extension. */
@@ -1185,6 +1211,10 @@
 }
 
 static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 1;
+  }
+
   /* The extension shouldn't be sent when resuming sessions. */
   if (ssl->hit ||
       !ssl->s3->tmp.ocsp_stapling_requested ||
@@ -1229,6 +1259,10 @@
     return 1;
   }
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 0;
+  }
+
   /* If any of these are false then we should never have sent the NPN
    * extension in the ClientHello and thus this function should never have been
    * called. */
@@ -1279,6 +1313,10 @@
 
 static int ext_npn_parse_clienthello(SSL *ssl, uint8_t *out_alert,
                                      CBS *contents) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 1;
+  }
+
   if (contents != NULL && CBS_len(contents) != 0) {
     return 0;
   }
@@ -1555,6 +1593,10 @@
     return 1;
   }
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 0;
+  }
+
   assert(!SSL_IS_DTLS(ssl));
   assert(ssl->tlsext_channel_id_enabled);
 
@@ -1583,6 +1625,10 @@
 }
 
 static int ext_channel_id_add_serverhello(SSL *ssl, CBB *out) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 1;
+  }
+
   if (!ssl->s3->tlsext_channel_id_valid) {
     return 1;
   }
@@ -1801,6 +1847,10 @@
     return 1;
   }
 
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 0;
+  }
+
   CBS ec_point_format_list;
   if (!CBS_get_u8_length_prefixed(contents, &ec_point_format_list) ||
       CBS_len(contents) != 0) {
@@ -1820,10 +1870,18 @@
 
 static int ext_ec_point_parse_clienthello(SSL *ssl, uint8_t *out_alert,
                                           CBS *contents) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 1;
+  }
+
   return ext_ec_point_parse_serverhello(ssl, out_alert, contents);
 }
 
 static int ext_ec_point_add_serverhello(SSL *ssl, CBB *out) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return 1;
+  }
+
   const uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
   const uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
   const int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA);
@@ -1836,6 +1894,182 @@
 }
 
 
+/* Draft Version Extension */
+
+static int ext_draft_version_add_clienthello(SSL *ssl, CBB *out) {
+  uint16_t min_version, max_version;
+  if (!ssl_get_version_range(ssl, &min_version, &max_version) ||
+      max_version >= TLS1_3_VERSION) {
+    return 1;
+  }
+
+  CBB contents;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_draft_version) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u16(&contents, TLS1_3_DRAFT_VERSION)) {
+    return 0;
+  }
+
+  return CBB_flush(out);
+}
+
+
+/* Key Share
+ *
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-12 */
+
+static int ext_key_share_add_clienthello(SSL *ssl, CBB *out) {
+  uint16_t min_version, max_version;
+  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+    return 0;
+  }
+
+  if (max_version < TLS1_3_VERSION || !ssl_any_ec_cipher_suites_enabled(ssl)) {
+    return 1;
+  }
+
+  CBB contents, kse_bytes;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u16_length_prefixed(&contents, &kse_bytes)) {
+    return 0;
+  }
+
+  const uint16_t *groups;
+  size_t groups_len;
+  tls1_get_grouplist(ssl, 0 /* local groups */, &groups, &groups_len);
+
+  ssl->s3->hs->groups = OPENSSL_malloc(groups_len * sizeof(SSL_ECDH_CTX));
+  if (ssl->s3->hs->groups == NULL) {
+    return 0;
+  }
+  memset(ssl->s3->hs->groups, 0, groups_len * sizeof(SSL_ECDH_CTX));
+  ssl->s3->hs->groups_len = groups_len;
+
+  for (size_t i = 0; i < groups_len; i++) {
+    if (!CBB_add_u16(&kse_bytes, groups[i])) {
+      return 0;
+    }
+
+    CBB key_exchange;
+    if (!CBB_add_u16_length_prefixed(&kse_bytes, &key_exchange) ||
+        !SSL_ECDH_CTX_init(&ssl->s3->hs->groups[i], groups[i]) ||
+        !SSL_ECDH_CTX_offer(&ssl->s3->hs->groups[i], &key_exchange) ||
+        !CBB_flush(&kse_bytes)) {
+      return 0;
+    }
+  }
+
+  return CBB_flush(out);
+}
+
+int ext_key_share_parse_serverhello(SSL *ssl, uint8_t **out_secret,
+                                    size_t *out_secret_len, uint8_t *out_alert,
+                                    CBS *contents) {
+  CBS peer_key;
+  uint16_t group;
+  if (!CBS_get_u16(contents, &group) ||
+      !CBS_get_u16_length_prefixed(contents, &peer_key)) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
+  }
+
+  SSL_ECDH_CTX *group_ctx = NULL;
+  for (size_t i = 0; i < ssl->s3->hs->groups_len; i++) {
+    if (SSL_ECDH_CTX_get_id(&ssl->s3->hs->groups[i]) == group) {
+      group_ctx = &ssl->s3->hs->groups[i];
+      break;
+    }
+  }
+
+  if (group_ctx == NULL) {
+    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+    return 0;
+  }
+
+  if (!SSL_ECDH_CTX_finish(group_ctx, out_secret, out_secret_len, out_alert,
+                           CBS_data(&peer_key), CBS_len(&peer_key))) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return 0;
+  }
+
+  for (size_t i = 0; i < ssl->s3->hs->groups_len; i++) {
+    SSL_ECDH_CTX_cleanup(&ssl->s3->hs->groups[i]);
+  }
+  OPENSSL_free(ssl->s3->hs->groups);
+  ssl->s3->hs->groups = NULL;
+
+  return 1;
+}
+
+int ext_key_share_parse_clienthello(SSL *ssl, uint8_t **out_secret,
+                                    size_t *out_secret_len, uint8_t *out_alert,
+                                    CBS *contents) {
+  uint16_t group_id;
+  CBS key_shares;
+  if (!tls1_get_shared_group(ssl, &group_id) ||
+      !CBS_get_u16_length_prefixed(contents, &key_shares)) {
+    return 0;
+  }
+
+  int found = 0;
+  while (CBS_len(&key_shares) > 0) {
+    uint16_t id;
+    CBS peer_key;
+    if (!CBS_get_u16(&key_shares, &id) ||
+        !CBS_get_u16_length_prefixed(&key_shares, &peer_key)) {
+      return 0;
+    }
+
+    if (id != group_id || found) {
+      continue;
+    }
+
+    SSL_ECDH_CTX group;
+    memset(&group, 0, sizeof(SSL_ECDH_CTX));
+    CBB public_key;
+    if (!CBB_init(&public_key, 0) ||
+        !SSL_ECDH_CTX_init(&group, group_id) ||
+        !SSL_ECDH_CTX_accept(&group, &public_key, out_secret, out_secret_len,
+                             out_alert, CBS_data(&peer_key),
+                             CBS_len(&peer_key)) ||
+        !CBB_finish(&public_key, &ssl->s3->hs->public_key,
+                    &ssl->s3->hs->public_key_len)) {
+      SSL_ECDH_CTX_cleanup(&group);
+      CBB_cleanup(&public_key);
+      return 0;
+    }
+    SSL_ECDH_CTX_cleanup(&group);
+
+    found = 1;
+  }
+
+  return found;
+}
+
+int ext_key_share_add_serverhello(SSL *ssl, CBB *out) {
+  if (ssl->s3->tmp.new_cipher->algorithm_mkey != SSL_kECDHE) {
+    return 1;
+  }
+
+  uint16_t group_id;
+  CBB kse_bytes, public_key;
+  if (!tls1_get_shared_group(ssl, &group_id) ||
+      !CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
+      !CBB_add_u16_length_prefixed(out, &kse_bytes) ||
+      !CBB_add_u16(&kse_bytes, group_id) ||
+      !CBB_add_u16_length_prefixed(&kse_bytes, &public_key) ||
+      !CBB_add_bytes(&public_key, ssl->s3->hs->public_key,
+                     ssl->s3->hs->public_key_len) ||
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+
 /* Negotiated Groups
  *
  * https://tools.ietf.org/html/rfc4492#section-5.1.2
@@ -2029,6 +2263,22 @@
     ext_ec_point_parse_clienthello,
     ext_ec_point_add_serverhello,
   },
+  {
+    TLSEXT_TYPE_draft_version,
+    NULL,
+    ext_draft_version_add_clienthello,
+    forbid_parse_serverhello,
+    ignore_parse_clienthello,
+    dont_add_serverhello,
+  },
+  {
+    TLSEXT_TYPE_key_share,
+    NULL,
+    ext_key_share_add_clienthello,
+    forbid_parse_serverhello,
+    ignore_parse_clienthello,
+    dont_add_serverhello,
+  },
   /* The final extension must be non-empty. WebSphere Application Server 7.0 is
    * intolerant to the last extension being zero-length. See
    * https://crbug.com/363583. */
@@ -2175,8 +2425,9 @@
     goto err;
   }
 
-  /* Discard empty extensions blocks. */
-  if (CBB_len(&extensions) == 0) {
+  /* Discard empty extensions blocks before TLS 1.3. */
+  if (ssl3_protocol_version(ssl) < TLS1_3_VERSION &&
+      CBB_len(&extensions) == 0) {
     CBB_discard_child(out);
   }
 
@@ -2287,57 +2538,58 @@
 OPENSSL_COMPILE_ASSERT(kNumExtensions <= sizeof(uint32_t) * 8, too_many_bits);
 
 static int ssl_scan_serverhello_tlsext(SSL *ssl, CBS *cbs, int *out_alert) {
-  uint32_t received = 0;
+  /* Before TLS 1.3, ServerHello extensions blocks may be omitted if empty. */
+  if (CBS_len(cbs) == 0 && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
+    return 1;
+  }
 
-  if (CBS_len(cbs) != 0) {
-    /* Decode the extensions block and check it is valid. */
-    CBS extensions;
-    if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
-        !tls1_check_duplicate_extensions(&extensions)) {
+  /* Decode the extensions block and check it is valid. */
+  CBS extensions;
+  if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
+      !tls1_check_duplicate_extensions(&extensions)) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
+  }
+
+  uint32_t received = 0;
+  while (CBS_len(&extensions) != 0) {
+    uint16_t type;
+    CBS extension;
+
+    /* Decode the next extension. */
+    if (!CBS_get_u16(&extensions, &type) ||
+        !CBS_get_u16_length_prefixed(&extensions, &extension)) {
       *out_alert = SSL_AD_DECODE_ERROR;
       return 0;
     }
 
+    unsigned ext_index;
+    const struct tls_extension *const ext =
+        tls_extension_find(&ext_index, type);
 
-    while (CBS_len(&extensions) != 0) {
-      uint16_t type;
-      CBS extension;
-
-      /* Decode the next extension. */
-      if (!CBS_get_u16(&extensions, &type) ||
-          !CBS_get_u16_length_prefixed(&extensions, &extension)) {
-        *out_alert = SSL_AD_DECODE_ERROR;
+    if (ext == NULL) {
+      if (!custom_ext_parse_serverhello(ssl, out_alert, type, &extension)) {
         return 0;
       }
+      continue;
+    }
 
-      unsigned ext_index;
-      const struct tls_extension *const ext =
-          tls_extension_find(&ext_index, type);
+    if (!(ssl->s3->tmp.extensions.sent & (1u << ext_index))) {
+      /* If the extension was never sent then it is illegal. */
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+      ERR_add_error_dataf("extension :%u", (unsigned)type);
+      *out_alert = SSL_AD_DECODE_ERROR;
+      return 0;
+    }
 
-      if (ext == NULL) {
-        if (!custom_ext_parse_serverhello(ssl, out_alert, type, &extension)) {
-          return 0;
-        }
-        continue;
-      }
+    received |= (1u << ext_index);
 
-      if (!(ssl->s3->tmp.extensions.sent & (1u << ext_index))) {
-        /* If the extension was never sent then it is illegal. */
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-        ERR_add_error_dataf("extension :%u", (unsigned)type);
-        *out_alert = SSL_AD_DECODE_ERROR;
-        return 0;
-      }
-
-      received |= (1u << ext_index);
-
-      uint8_t alert = SSL_AD_DECODE_ERROR;
-      if (!ext->parse_serverhello(ssl, &alert, &extension)) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
-        ERR_add_error_dataf("extension: %u", (unsigned)type);
-        *out_alert = alert;
-        return 0;
-      }
+    uint8_t alert = SSL_AD_DECODE_ERROR;
+    if (!ext->parse_serverhello(ssl, &alert, &extension)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
+      ERR_add_error_dataf("extension: %u", (unsigned)type);
+      *out_alert = alert;
+      return 0;
     }
   }
 
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 0bdfc94..d1aadb8 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1045,7 +1045,9 @@
   if (expect_handshake_done && !config->is_server) {
     bool expect_new_session =
         !config->expect_no_session &&
-        (!SSL_session_reused(ssl) || config->expect_ticket_renewal);
+        (!SSL_session_reused(ssl) || config->expect_ticket_renewal) &&
+        /* TODO(svaldez): Implement Session Resumption. */
+        SSL_version(ssl) != TLS1_3_VERSION;
     if (expect_new_session != GetTestState(ssl)->got_new_session) {
       fprintf(stderr,
               "new session was%s cached, but we expected the opposite\n",
@@ -1264,7 +1266,8 @@
   if (config->no_ssl3) {
     SSL_set_options(ssl.get(), SSL_OP_NO_SSLv3);
   }
-  if (!config->expected_channel_id.empty()) {
+  if (!config->expected_channel_id.empty() ||
+      config->enable_channel_id) {
     SSL_enable_tls_channel_id(ssl.get());
   }
   if (!config->send_channel_id.empty()) {
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 5fd3436..0ae360a 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -18,8 +18,7 @@
 	"time"
 )
 
-// TODO(davidben): Flip this to true when the C code lands.
-const enableTLS13Handshake = false
+const enableTLS13Handshake = true
 
 const (
 	VersionSSL30 = 0x0300
@@ -919,6 +918,43 @@
 	// IgnoreSignatureVersionChecks, if true, causes all signature
 	// algorithms to be enabled at all TLS versions.
 	IgnoreSignatureVersionChecks bool
+
+	// NegotiateRenegotiationInfoAtAllVersions, if true, causes
+	// Renegotiation Info to be negotiated at all versions.
+	NegotiateRenegotiationInfoAtAllVersions bool
+
+	// NegotiateChannelIDAtAllVersions, if true, causes Channel ID to be
+	// negotiated at all versions.
+	NegotiateChannelIDAtAllVersions bool
+
+	// NegotiateNPNAtAllVersions, if true, causes NPN to be negotiated at
+	// all versions.
+	NegotiateNPNAtAllVersions bool
+
+	// NegotiateEMSAtAllVersions, if true, causes EMS to be negotiated at
+	// all versions.
+	NegotiateEMSAtAllVersions bool
+
+	// AdvertiseTicketExtension, if true, causes the ticket extension to be
+	// advertised in server extensions
+	AdvertiseTicketExtension bool
+
+	// MissingKeyShare, if true, causes the TLS 1.3 implementation to skip
+	// sending a key_share extension and use the zero ECDHE secret
+	// instead.
+	MissingKeyShare bool
+
+	// DuplicateKeyShares, if true, causes the TLS 1.3 client to send two
+	// copies of each KeyShareEntry.
+	DuplicateKeyShares bool
+
+	// EmptyEncryptedExtensions, if true, causes the TLS 1.3 server to
+	// emit an empty EncryptedExtensions block.
+	EmptyEncryptedExtensions bool
+
+	// EncryptedExtensionsWithKeyShare, if true, causes the TLS 1.3 server to
+	// include the KeyShare extension in the EncryptedExtensions block.
+	EncryptedExtensionsWithKeyShare bool
 }
 
 func (c *Config) serverInit() {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index a2ea50e..3d9fa5a 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -133,6 +133,14 @@
 				keyExchange: publicKey,
 			})
 			keyShares[curveID] = curve
+
+			if c.config.Bugs.DuplicateKeyShares {
+				hello.keyShares = append(hello.keyShares, hello.keyShares[len(hello.keyShares)-1])
+			}
+		}
+
+		if c.config.Bugs.MissingKeyShare {
+			hello.keyShares = nil
 		}
 	}
 
@@ -477,7 +485,7 @@
 
 	// Resolve ECDHE and compute the handshake secret.
 	var ecdheSecret []byte
-	if hs.suite.flags&suiteECDHE != 0 {
+	if hs.suite.flags&suiteECDHE != 0 && !c.config.Bugs.MissingKeyShare {
 		if !hs.serverHello.hasKeyShare {
 			c.sendAlert(alertMissingExtension)
 			return errors.New("tls: server omitted the key share extension")
@@ -1014,6 +1022,10 @@
 		return errors.New("tls: server advertised extended master secret over TLS 1.3")
 	}
 
+	if serverExtensions.ticketSupported && c.vers >= VersionTLS13 && enableTLS13Handshake {
+		return errors.New("tls: server advertised ticket extension over TLS 1.3")
+	}
+
 	if serverExtensions.srtpProtectionProfile != 0 {
 		if serverExtensions.srtpMasterKeyIdentifier != "" {
 			return errors.New("tls: server selected SRTP MKI value")
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 6d0f920..376d88c 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -847,6 +847,7 @@
 type encryptedExtensionsMsg struct {
 	raw        []byte
 	extensions serverExtensions
+	empty      bool
 }
 
 func (m *encryptedExtensionsMsg) marshal() []byte {
@@ -857,8 +858,10 @@
 	encryptedExtensionsMsg := newByteBuilder()
 	encryptedExtensionsMsg.addU8(typeEncryptedExtensions)
 	encryptedExtensions := encryptedExtensionsMsg.addU24LengthPrefixed()
-	extensions := encryptedExtensions.addU16LengthPrefixed()
-	m.extensions.marshal(extensions, VersionTLS13)
+	if !m.empty {
+		extensions := encryptedExtensions.addU16LengthPrefixed()
+		m.extensions.marshal(extensions, VersionTLS13)
+	}
 
 	m.raw = encryptedExtensionsMsg.finish()
 	return m.raw
@@ -902,6 +905,8 @@
 	sctList                 []byte
 	customExtension         string
 	npnLast                 bool
+	hasKeyShare             bool
+	keyShare                keyShareEntry
 }
 
 func (m *serverExtensions) marshal(extensions *byteBuilder, version uint16) {
@@ -999,6 +1004,13 @@
 			npn.addBytes([]byte(v))
 		}
 	}
+	if m.hasKeyShare {
+		extensions.addU16(extensionKeyShare)
+		keyShare := extensions.addU16LengthPrefixed()
+		keyShare.addU16(uint16(m.keyShare.group))
+		keyExchange := keyShare.addU16LengthPrefixed()
+		keyExchange.addBytes(m.keyShare.keyExchange)
+	}
 }
 
 func (m *serverExtensions) unmarshal(data []byte, version uint16) bool {
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index d4dba75..24d6425 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -279,6 +279,7 @@
 
 	// Prepare an EncryptedExtensions message, but do not send it yet.
 	encryptedExtensions := new(encryptedExtensionsMsg)
+	encryptedExtensions.empty = config.Bugs.EmptyEncryptedExtensions
 	if err := hs.processClientExtensions(&encryptedExtensions.extensions); err != nil {
 		return err
 	}
@@ -341,7 +342,7 @@
 
 	// Resolve ECDHE and compute the handshake secret.
 	var ecdheSecret []byte
-	if hs.suite.flags&suiteECDHE != 0 {
+	if hs.suite.flags&suiteECDHE != 0 && !config.Bugs.MissingKeyShare {
 		// Look for the key share corresponding to our selected curve.
 		var selectedKeyShare *keyShareEntry
 		for i := range hs.clientHello.keyShares {
@@ -384,6 +385,14 @@
 			group:       curveID,
 			keyExchange: publicKey,
 		}
+
+		if config.Bugs.EncryptedExtensionsWithKeyShare {
+			encryptedExtensions.extensions.hasKeyShare = true
+			encryptedExtensions.extensions.keyShare = keyShareEntry{
+				group:       curveID,
+				keyExchange: publicKey,
+			}
+		}
 	} else {
 		ecdheSecret = hs.finishedHash.zeroSecret()
 	}
@@ -700,7 +709,7 @@
 	config := hs.c.config
 	c := hs.c
 
-	if c.vers < VersionTLS13 || !enableTLS13Handshake {
+	if c.vers < VersionTLS13 || config.Bugs.NegotiateRenegotiationInfoAtAllVersions || !enableTLS13Handshake {
 		if !bytes.Equal(c.clientVerify, hs.clientHello.secureRenegotiation) {
 			c.sendAlert(alertHandshakeFailure)
 			return errors.New("tls: renegotiation mismatch")
@@ -751,7 +760,7 @@
 		}
 	}
 
-	if c.vers < VersionTLS13 || !enableTLS13Handshake {
+	if c.vers < VersionTLS13 || config.Bugs.NegotiateNPNAtAllVersions || !enableTLS13Handshake {
 		if len(hs.clientHello.alpnProtocols) == 0 || c.config.Bugs.NegotiateALPNAndNPN {
 			// Although sending an empty NPN extension is reasonable, Firefox has
 			// had a bug around this. Best to send nothing at all if
@@ -763,9 +772,13 @@
 				serverExtensions.npnLast = config.Bugs.SwapNPNAndALPN
 			}
 		}
+	}
 
+	if c.vers < VersionTLS13 || config.Bugs.NegotiateEMSAtAllVersions || !enableTLS13Handshake {
 		serverExtensions.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !c.config.Bugs.NoExtendedMasterSecret
+	}
 
+	if c.vers < VersionTLS13 || config.Bugs.NegotiateChannelIDAtAllVersions || !enableTLS13Handshake {
 		if hs.clientHello.channelIDSupported && config.RequestChannelID {
 			serverExtensions.channelIDRequested = true
 		}
@@ -795,6 +808,10 @@
 	}
 	serverExtensions.customExtension = config.Bugs.CustomExtension
 
+	if c.config.Bugs.AdvertiseTicketExtension {
+		serverExtensions.ticketSupported = true
+	}
+
 	return nil
 }
 
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index a0edf77..e5faae5 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -941,8 +941,7 @@
 	{"TLS1", VersionTLS10, "-no-tls1", true},
 	{"TLS11", VersionTLS11, "-no-tls11", false},
 	{"TLS12", VersionTLS12, "-no-tls12", true},
-	// TODO(nharper): Once we have a real implementation of TLS 1.3, update the name here.
-	{"FakeTLS13", VersionTLS13, "-no-tls13", false},
+	{"TLS13", VersionTLS13, "-no-tls13", false},
 }
 
 var testCipherSuites = []struct {
@@ -1340,7 +1339,6 @@
 		{
 			name: "CertMismatchRSA",
 			config: Config{
-				// TODO(davidben): Add a TLS 1.3 version of this test.
 				MaxVersion:   VersionTLS12,
 				CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
 				Certificates: []Certificate{ecdsaP256Certificate},
@@ -1352,9 +1350,21 @@
 			expectedError: ":WRONG_CERTIFICATE_TYPE:",
 		},
 		{
+			name: "CertMismatchRSA-TLS13",
+			config: Config{
+				MaxVersion:   VersionTLS13,
+				CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+				Certificates: []Certificate{ecdsaP256Certificate},
+				Bugs: ProtocolBugs{
+					SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":WRONG_CERTIFICATE_TYPE:",
+		},
+		{
 			name: "CertMismatchECDSA",
 			config: Config{
-				// TODO(davidben): Add a TLS 1.3 version of this test.
 				MaxVersion:   VersionTLS12,
 				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 				Certificates: []Certificate{rsaCertificate},
@@ -1366,9 +1376,21 @@
 			expectedError: ":WRONG_CERTIFICATE_TYPE:",
 		},
 		{
+			name: "CertMismatchECDSA-TLS13",
+			config: Config{
+				MaxVersion:   VersionTLS13,
+				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				Certificates: []Certificate{rsaCertificate},
+				Bugs: ProtocolBugs{
+					SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":WRONG_CERTIFICATE_TYPE:",
+		},
+		{
 			name: "EmptyCertificateList",
 			config: Config{
-				// TODO(davidben): Add a TLS 1.3 version of this test.
 				MaxVersion:   VersionTLS12,
 				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 				Bugs: ProtocolBugs{
@@ -1694,7 +1716,6 @@
 		{
 			name: "BadFinished-Client",
 			config: Config{
-				// TODO(davidben): Add a TLS 1.3 version of this.
 				MaxVersion: VersionTLS12,
 				Bugs: ProtocolBugs{
 					BadFinished: true,
@@ -1704,10 +1725,20 @@
 			expectedError: ":DIGEST_CHECK_FAILED:",
 		},
 		{
+			name: "BadFinished-Client-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					BadFinished: true,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":DIGEST_CHECK_FAILED:",
+		},
+		{
 			testType: serverTest,
 			name:     "BadFinished-Server",
 			config: Config{
-				// TODO(davidben): Add a TLS 1.3 version of this.
 				MaxVersion: VersionTLS12,
 				Bugs: ProtocolBugs{
 					BadFinished: true,
@@ -1717,6 +1748,18 @@
 			expectedError: ":DIGEST_CHECK_FAILED:",
 		},
 		{
+			testType: serverTest,
+			name:     "BadFinished-Server-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					BadFinished: true,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":DIGEST_CHECK_FAILED:",
+		},
+		{
 			name: "FalseStart-BadFinished",
 			config: Config{
 				MaxVersion:   VersionTLS12,
@@ -2182,7 +2225,6 @@
 	testCases = append(testCases, testCase{
 		name: "NoSharedCipher",
 		config: Config{
-			// TODO(davidben): Add a TLS 1.3 version of this test.
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{},
 		},
@@ -2191,6 +2233,16 @@
 	})
 
 	testCases = append(testCases, testCase{
+		name: "NoSharedCipher-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{},
+		},
+		shouldFail:    true,
+		expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:",
+	})
+
+	testCases = append(testCases, testCase{
 		name: "UnsupportedCipherSuite",
 		config: Config{
 			MaxVersion:   VersionTLS12,
@@ -2509,9 +2561,6 @@
 		}
 	}
 
-	// TODO(davidben): These tests will need TLS 1.3 versions when the
-	// handshake is separate.
-
 	testCases = append(testCases, testCase{
 		name: "NoClientCertificate",
 		config: Config{
@@ -2523,6 +2572,16 @@
 	})
 
 	testCases = append(testCases, testCase{
+		name: "NoClientCertificate-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequireAnyClientCert,
+		},
+		shouldFail:         true,
+		expectedLocalError: "client didn't provide a certificate",
+	})
+
+	testCases = append(testCases, testCase{
 		testType: serverTest,
 		name:     "RequireAnyClientCertificate",
 		config: Config{
@@ -2535,6 +2594,17 @@
 
 	testCases = append(testCases, testCase{
 		testType: serverTest,
+		name:     "RequireAnyClientCertificate-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+		},
+		flags:         []string{"-require-any-client-certificate"},
+		shouldFail:    true,
+		expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
 		name:     "RequireAnyClientCertificate-SSL3",
 		config: Config{
 			MaxVersion: VersionSSL30,
@@ -2559,6 +2629,21 @@
 		expectedError: ":UNEXPECTED_MESSAGE:",
 	})
 
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "SkipClientCertificate-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SkipClientCertificate: true,
+			},
+		},
+		// Setting SSL_VERIFY_PEER allows anonymous clients.
+		flags:         []string{"-verify-peer"},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_MESSAGE:",
+	})
+
 	// Client auth is only legal in certificate-based ciphers.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
@@ -2616,10 +2701,8 @@
 
 	for _, with := range []bool{false, true} {
 		prefix := "No"
-		var flags []string
 		if with {
 			prefix = ""
-			flags = []string{expectEMSFlag}
 		}
 
 		for _, isClient := range []bool{false, true} {
@@ -2630,10 +2713,14 @@
 				testType = clientTest
 			}
 
-			// TODO(davidben): Once the new TLS 1.3 handshake is in,
-			// test that the extension is irrelevant, but the API
-			// acts as if it is enabled.
 			for _, ver := range tlsVersions {
+				// In TLS 1.3, the extension is irrelevant and
+				// always reports as enabled.
+				var flags []string
+				if with || ver.version >= VersionTLS13 {
+					flags = []string{expectEMSFlag}
+				}
+
 				test := testCase{
 					testType: testType,
 					name:     prefix + "ExtendedMasterSecret-" + ver.name + suffix,
@@ -2784,9 +2871,6 @@
 
 	// Basic handshake, with resumption. Client and server,
 	// session ID and session ticket.
-	//
-	// TODO(davidben): Add TLS 1.3 tests for all of its different handshake
-	// shapes.
 	tests = append(tests, testCase{
 		name: "Basic-Client",
 		config: Config{
@@ -2862,9 +2946,22 @@
 		resumeSession: true,
 	})
 
+	// TLS 1.3 basic handshake shapes.
+	tests = append(tests, testCase{
+		name: "TLS13-1RTT-Client",
+		config: Config{
+			MaxVersion: VersionTLS13,
+		},
+	})
+	tests = append(tests, testCase{
+		testType: serverTest,
+		name:     "TLS13-1RTT-Server",
+		config: Config{
+			MaxVersion: VersionTLS13,
+		},
+	})
+
 	// TLS client auth.
-	//
-	// TODO(davidben): Add TLS 1.3 client auth tests.
 	tests = append(tests, testCase{
 		testType: clientTest,
 		name:     "ClientAuth-NoCertificate-Client",
@@ -2900,6 +2997,23 @@
 			// Setting SSL_VERIFY_PEER allows anonymous clients.
 			flags: []string{"-verify-peer"},
 		})
+		tests = append(tests, testCase{
+			testType: clientTest,
+			name:     "ClientAuth-NoCertificate-Client-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				ClientAuth: RequestClientCert,
+			},
+		})
+		tests = append(tests, testCase{
+			testType: serverTest,
+			name:     "ClientAuth-NoCertificate-Server-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			// Setting SSL_VERIFY_PEER allows anonymous clients.
+			flags: []string{"-verify-peer"},
+		})
 	}
 	tests = append(tests, testCase{
 		testType: clientTest,
@@ -2915,6 +3029,18 @@
 	})
 	tests = append(tests, testCase{
 		testType: clientTest,
+		name:     "ClientAuth-RSA-Client-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+		},
+	})
+	tests = append(tests, testCase{
+		testType: clientTest,
 		name:     "ClientAuth-ECDSA-Client",
 		config: Config{
 			MaxVersion: VersionTLS12,
@@ -2927,6 +3053,18 @@
 	})
 	tests = append(tests, testCase{
 		testType: clientTest,
+		name:     "ClientAuth-ECDSA-Client-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+			"-key-file", path.Join(*resourceDir, ecdsaP256KeyFile),
+		},
+	})
+	tests = append(tests, testCase{
+		testType: clientTest,
 		name:     "ClientAuth-NoCertificate-OldCallback",
 		config: Config{
 			MaxVersion: VersionTLS12,
@@ -2936,6 +3074,15 @@
 	})
 	tests = append(tests, testCase{
 		testType: clientTest,
+		name:     "ClientAuth-NoCertificate-OldCallback-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequestClientCert,
+		},
+		flags: []string{"-use-old-client-cert-callback"},
+	})
+	tests = append(tests, testCase{
+		testType: clientTest,
 		name:     "ClientAuth-OldCallback",
 		config: Config{
 			MaxVersion: VersionTLS12,
@@ -2948,6 +3095,19 @@
 		},
 	})
 	tests = append(tests, testCase{
+		testType: clientTest,
+		name:     "ClientAuth-OldCallback-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-use-old-client-cert-callback",
+		},
+	})
+	tests = append(tests, testCase{
 		testType: serverTest,
 		name:     "ClientAuth-Server",
 		config: Config{
@@ -2956,10 +3116,17 @@
 		},
 		flags: []string{"-require-any-client-certificate"},
 	})
+	tests = append(tests, testCase{
+		testType: serverTest,
+		name:     "ClientAuth-Server-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			Certificates: []Certificate{rsaCertificate},
+		},
+		flags: []string{"-require-any-client-certificate"},
+	})
 
 	// Test each key exchange on the server side for async keys.
-	//
-	// TODO(davidben): Add TLS 1.3 versions of these.
 	tests = append(tests, testCase{
 		testType: serverTest,
 		name:     "Basic-Server-RSA",
@@ -3068,42 +3235,45 @@
 	})
 
 	// Certificate verification tests.
-	//
-	// TODO(davidben): Test the TLS 1.3 version.
-	tests = append(tests, testCase{
-		testType: clientTest,
-		name:     "CertificateVerificationSucceed",
-		config: Config{
-			MaxVersion: VersionTLS12,
-		},
-		flags: []string{
-			"-verify-peer",
-		},
-	})
-	tests = append(tests, testCase{
-		testType: clientTest,
-		name:     "CertificateVerificationFail",
-		config: Config{
-			MaxVersion: VersionTLS12,
-		},
-		flags: []string{
-			"-verify-fail",
-			"-verify-peer",
-		},
-		shouldFail:    true,
-		expectedError: ":CERTIFICATE_VERIFY_FAILED:",
-	})
-	tests = append(tests, testCase{
-		testType: clientTest,
-		name:     "CertificateVerificationSoftFail",
-		config: Config{
-			MaxVersion: VersionTLS12,
-		},
-		flags: []string{
-			"-verify-fail",
-			"-expect-verify-result",
-		},
-	})
+	for _, vers := range tlsVersions {
+		if config.protocol == dtls && !vers.hasDTLS {
+			continue
+		}
+		tests = append(tests, testCase{
+			testType: clientTest,
+			name:     "CertificateVerificationSucceed-" + vers.name,
+			config: Config{
+				MaxVersion: vers.version,
+			},
+			flags: []string{
+				"-verify-peer",
+			},
+		})
+		tests = append(tests, testCase{
+			testType: clientTest,
+			name:     "CertificateVerificationFail-" + vers.name,
+			config: Config{
+				MaxVersion: vers.version,
+			},
+			flags: []string{
+				"-verify-fail",
+				"-verify-peer",
+			},
+			shouldFail:    true,
+			expectedError: ":CERTIFICATE_VERIFY_FAILED:",
+		})
+		tests = append(tests, testCase{
+			testType: clientTest,
+			name:     "CertificateVerificationSoftFail-" + vers.name,
+			config: Config{
+				MaxVersion: vers.version,
+			},
+			flags: []string{
+				"-verify-fail",
+				"-expect-verify-result",
+			},
+		})
+	}
 
 	if config.protocol == tls {
 		tests = append(tests, testCase{
@@ -3391,15 +3561,13 @@
 
 func addDDoSCallbackTests() {
 	// DDoS callback.
-
+	// TODO(davidben): Implement DDoS resumption tests for TLS 1.3.
 	for _, resume := range []bool{false, true} {
 		suffix := "Resume"
 		if resume {
 			suffix = "No" + suffix
 		}
 
-		// TODO(davidben): Test TLS 1.3's version of the DDoS callback.
-
 		testCases = append(testCases, testCase{
 			testType: serverTest,
 			name:     "Server-DDoS-OK-" + suffix,
@@ -3409,6 +3577,17 @@
 			flags:         []string{"-install-ddos-callback"},
 			resumeSession: resume,
 		})
+		if !resume {
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "Server-DDoS-OK-" + suffix + "-TLS13",
+				config: Config{
+					MaxVersion: VersionTLS13,
+				},
+				flags:         []string{"-install-ddos-callback"},
+				resumeSession: resume,
+			})
+		}
 
 		failFlag := "-fail-ddos-callback"
 		if resume {
@@ -3425,6 +3604,19 @@
 			shouldFail:    true,
 			expectedError: ":CONNECTION_REJECTED:",
 		})
+		if !resume {
+			testCases = append(testCases, testCase{
+				testType: serverTest,
+				name:     "Server-DDoS-Reject-" + suffix + "-TLS13",
+				config: Config{
+					MaxVersion: VersionTLS13,
+				},
+				flags:         []string{"-install-ddos-callback", failFlag},
+				resumeSession: resume,
+				shouldFail:    true,
+				expectedError: ":CONNECTION_REJECTED:",
+			})
+		}
 	}
 }
 
@@ -4228,6 +4420,95 @@
 			},
 		},
 	})
+
+	// Test that illegal extensions in TLS 1.3 are rejected by the client if
+	// in ServerHello.
+	testCases = append(testCases, testCase{
+		name: "NPN-Forbidden-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			NextProtos: []string{"foo"},
+			Bugs: ProtocolBugs{
+				NegotiateNPNAtAllVersions: true,
+			},
+		},
+		flags:         []string{"-select-next-proto", "foo"},
+		shouldFail:    true,
+		expectedError: ":ERROR_PARSING_EXTENSION:",
+	})
+	testCases = append(testCases, testCase{
+		name: "EMS-Forbidden-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				NegotiateEMSAtAllVersions: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":ERROR_PARSING_EXTENSION:",
+	})
+	testCases = append(testCases, testCase{
+		name: "RenegotiationInfo-Forbidden-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				NegotiateRenegotiationInfoAtAllVersions: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":ERROR_PARSING_EXTENSION:",
+	})
+	testCases = append(testCases, testCase{
+		name: "ChannelID-Forbidden-TLS13",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			RequestChannelID: true,
+			Bugs: ProtocolBugs{
+				NegotiateChannelIDAtAllVersions: true,
+			},
+		},
+		flags:         []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)},
+		shouldFail:    true,
+		expectedError: ":ERROR_PARSING_EXTENSION:",
+	})
+	testCases = append(testCases, testCase{
+		name: "Ticket-Forbidden-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS12,
+		},
+		resumeConfig: &Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				AdvertiseTicketExtension: true,
+			},
+		},
+		resumeSession: true,
+		shouldFail:    true,
+		expectedError: ":ERROR_PARSING_EXTENSION:",
+	})
+
+	// Test that illegal extensions in TLS 1.3 are declined by the server if
+	// offered in ClientHello. The runner's server will fail if this occurs,
+	// so we exercise the offering path. (EMS and Renegotiation Info are
+	// implicit in every test.)
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "ChannelID-Declined-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ChannelID:  channelIDKey,
+		},
+		flags: []string{"-enable-channel-id"},
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "NPN-Server",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			NextProtos: []string{"bar"},
+		},
+		flags: []string{"-advertise-npn", "\x03foo\x03bar\x03baz"},
+	})
 }
 
 func addResumptionVersionTests() {
@@ -4636,17 +4917,21 @@
 	})
 
 	// Renegotiation is forbidden in TLS 1.3.
+	//
+	// TODO(davidben): This test current asserts that we ignore
+	// HelloRequests, but we actually should hard reject them. Fix this
+	// test once we actually parse post-handshake messages.
 	testCases = append(testCases, testCase{
 		name: "Renegotiate-Client-TLS13",
 		config: Config{
 			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendHelloRequestBeforeEveryAppDataRecord: true,
+			},
 		},
-		renegotiate: 1,
 		flags: []string{
 			"-renegotiate-freely",
 		},
-		shouldFail:    true,
-		expectedError: ":NO_RENEGOTIATION:",
 	})
 
 	// Stray HelloRequests during the handshake are forbidden in TLS 1.3.
@@ -4948,8 +5233,6 @@
 	}
 
 	// Test that algorithm selection takes the key type into account.
-	//
-	// TODO(davidben): Test this in TLS 1.3.
 	testCases = append(testCases, testCase{
 		name: "ClientAuth-SignatureType",
 		config: Config{
@@ -4969,6 +5252,25 @@
 	})
 
 	testCases = append(testCases, testCase{
+		name: "ClientAuth-SignatureType-TLS13",
+		config: Config{
+			ClientAuth: RequireAnyClientCert,
+			MaxVersion: VersionTLS13,
+			VerifySignatureAlgorithms: []signatureAlgorithm{
+				signatureECDSAWithP521AndSHA512,
+				signatureRSAPKCS1WithSHA384,
+				signatureRSAPSSWithSHA384,
+				signatureECDSAWithSHA1,
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+		},
+		expectedPeerSignatureAlgorithm: signatureRSAPSSWithSHA384,
+	})
+
+	testCases = append(testCases, testCase{
 		testType: serverTest,
 		name:     "ServerAuth-SignatureType",
 		config: Config{
@@ -4983,9 +5285,23 @@
 		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA384,
 	})
 
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "ServerAuth-SignatureType-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			VerifySignatureAlgorithms: []signatureAlgorithm{
+				signatureECDSAWithP521AndSHA512,
+				signatureRSAPKCS1WithSHA384,
+				signatureRSAPSSWithSHA384,
+				signatureECDSAWithSHA1,
+			},
+		},
+		expectedPeerSignatureAlgorithm: signatureRSAPSSWithSHA384,
+	})
+
 	// Test that signature verification takes the key type into account.
-	//
-	// TODO(davidben): Test this in TLS 1.3.
 	testCases = append(testCases, testCase{
 		testType: serverTest,
 		name:     "Verify-ClientAuth-SignatureType",
@@ -5007,6 +5323,26 @@
 	})
 
 	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "Verify-ClientAuth-SignatureType-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			Certificates: []Certificate{rsaCertificate},
+			SignSignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPSSWithSHA256,
+			},
+			Bugs: ProtocolBugs{
+				SendSignatureAlgorithm: signatureECDSAWithP256AndSHA256,
+			},
+		},
+		flags: []string{
+			"-require-any-client-certificate",
+		},
+		shouldFail:    true,
+		expectedError: ":WRONG_SIGNATURE_TYPE:",
+	})
+
+	testCases = append(testCases, testCase{
 		name: "Verify-ServerAuth-SignatureType",
 		config: Config{
 			MaxVersion:   VersionTLS12,
@@ -5022,6 +5358,22 @@
 		expectedError: ":WRONG_SIGNATURE_TYPE:",
 	})
 
+	testCases = append(testCases, testCase{
+		name: "Verify-ServerAuth-SignatureType-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			SignSignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPSSWithSHA256,
+			},
+			Bugs: ProtocolBugs{
+				SendSignatureAlgorithm: signatureECDSAWithP256AndSHA256,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":WRONG_SIGNATURE_TYPE:",
+	})
+
 	// Test that, if the list is missing, the peer falls back to SHA-1 in
 	// TLS 1.2, but not TLS 1.3.
 	testCases = append(testCases, testCase{
@@ -5137,8 +5489,6 @@
 
 	// Test that the agreed upon digest respects the client preferences and
 	// the server digests.
-	//
-	// TODO(davidben): Add TLS 1.3 versions of these.
 	testCases = append(testCases, testCase{
 		name: "NoCommonAlgorithms-Digests",
 		config: Config{
@@ -5421,7 +5771,7 @@
 	// shim must send flight N again, testing that the shim implements DTLS
 	// retransmit on a timeout.
 
-	// TODO(davidben): Add TLS 1.3 versions of these tests. There will
+	// TODO(davidben): Add DTLS 1.3 versions of these tests. There will
 	// likely be more epochs to cross and the final message's retransmit may
 	// be more complex.
 
@@ -5658,7 +6008,6 @@
 	expectedContents := "custom extension"
 	emptyString := ""
 
-	// TODO(davidben): Add TLS 1.3 versions of these tests.
 	for _, isClient := range []bool{false, true} {
 		suffix := "Server"
 		flag := "-enable-server-custom-extension"
@@ -5681,6 +6030,18 @@
 			},
 			flags: []string{flag},
 		})
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name:     "CustomExtensions-" + suffix + "-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					CustomExtension:         expectedContents,
+					ExpectedCustomExtension: &expectedContents,
+				},
+			},
+			flags: []string{flag},
+		})
 
 		// If the parse callback fails, the handshake should also fail.
 		testCases = append(testCases, testCase{
@@ -5697,6 +6058,20 @@
 			shouldFail:    true,
 			expectedError: ":CUSTOM_EXTENSION_ERROR:",
 		})
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name:     "CustomExtensions-ParseError-" + suffix + "-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					CustomExtension:         expectedContents + "foo",
+					ExpectedCustomExtension: &expectedContents,
+				},
+			},
+			flags:         []string{flag},
+			shouldFail:    true,
+			expectedError: ":CUSTOM_EXTENSION_ERROR:",
+		})
 
 		// If the add callback fails, the handshake should also fail.
 		testCases = append(testCases, testCase{
@@ -5713,6 +6088,20 @@
 			shouldFail:    true,
 			expectedError: ":CUSTOM_EXTENSION_ERROR:",
 		})
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name:     "CustomExtensions-FailAdd-" + suffix + "-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					CustomExtension:         expectedContents,
+					ExpectedCustomExtension: &expectedContents,
+				},
+			},
+			flags:         []string{flag, "-custom-extension-fail-add"},
+			shouldFail:    true,
+			expectedError: ":CUSTOM_EXTENSION_ERROR:",
+		})
 
 		// If the add callback returns zero, no extension should be
 		// added.
@@ -5734,6 +6123,18 @@
 			},
 			flags: []string{flag, "-custom-extension-skip"},
 		})
+		testCases = append(testCases, testCase{
+			testType: testType,
+			name:     "CustomExtensions-Skip-" + suffix + "-TLS13",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					CustomExtension:         skipCustomExtension,
+					ExpectedCustomExtension: &emptyString,
+				},
+			},
+			flags: []string{flag, "-custom-extension-skip"},
+		})
 	}
 
 	// The custom extension add callback should not be called if the client
@@ -5750,6 +6151,18 @@
 		flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"},
 	})
 
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "CustomExtensions-NotCalled-Server-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				ExpectedCustomExtension: &emptyString,
+			},
+		},
+		flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"},
+	})
+
 	// Test an unknown extension from the server.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
@@ -5763,6 +6176,18 @@
 		shouldFail:    true,
 		expectedError: ":UNEXPECTED_EXTENSION:",
 	})
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "UnknownExtension-Client-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				CustomExtension: expectedContents,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_EXTENSION:",
+	})
 }
 
 func addRSAClientKeyExchangeTests() {
@@ -5797,7 +6222,6 @@
 }
 
 func addCurveTests() {
-	// TODO(davidben): Add a TLS 1.3 versions of these tests.
 	for _, curve := range testCurves {
 		testCases = append(testCases, testCase{
 			name: "CurveTest-Client-" + curve.name,
@@ -5809,6 +6233,15 @@
 			flags: []string{"-enable-all-curves"},
 		})
 		testCases = append(testCases, testCase{
+			name: "CurveTest-Client-" + curve.name + "-TLS13",
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				CurvePreferences: []CurveID{curve.id},
+			},
+			flags: []string{"-enable-all-curves"},
+		})
+		testCases = append(testCases, testCase{
 			testType: serverTest,
 			name:     "CurveTest-Server-" + curve.name,
 			config: Config{
@@ -5818,6 +6251,16 @@
 			},
 			flags: []string{"-enable-all-curves"},
 		})
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "CurveTest-Server-" + curve.name + "-TLS13",
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				CurvePreferences: []CurveID{curve.id},
+			},
+			flags: []string{"-enable-all-curves"},
+		})
 	}
 
 	// The server must be tolerant to bogus curves.
@@ -5837,7 +6280,6 @@
 		testType: serverTest,
 		name:     "NoSupportedCurves",
 		config: Config{
-			// TODO(davidben): Add a TLS 1.3 version of this.
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 			Bugs: ProtocolBugs{
@@ -5847,6 +6289,19 @@
 		shouldFail:    true,
 		expectedError: ":NO_SHARED_CIPHER:",
 	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "NoSupportedCurves-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				NoSupportedCurves: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":NO_SHARED_CIPHER:",
+	})
 
 	// The server must fall back to another cipher when there are no
 	// supported curves.
@@ -5868,7 +6323,6 @@
 	testCases = append(testCases, testCase{
 		name: "BadECDHECurve",
 		config: Config{
-			// TODO(davidben): Add a TLS 1.3 version of this.
 			MaxVersion:   VersionTLS12,
 			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 			Bugs: ProtocolBugs{
@@ -5878,11 +6332,23 @@
 		shouldFail:    true,
 		expectedError: ":WRONG_CURVE:",
 	})
+	testCases = append(testCases, testCase{
+		name: "BadECDHECurve-TLS13",
+		config: Config{
+			MaxVersion:   VersionTLS13,
+			CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				SendCurve: bogusCurve,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":WRONG_CURVE:",
+	})
 
 	testCases = append(testCases, testCase{
 		name: "UnsupportedCurve",
 		config: Config{
-			// TODO(davidben): Add a TLS 1.3 version of this.
+			// TODO(davidben): Add a TLS 1.3 version of this test.
 			MaxVersion:       VersionTLS12,
 			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 			CurvePreferences: []CurveID{CurveP256},
@@ -5899,7 +6365,6 @@
 	testCases = append(testCases, testCase{
 		name: "InvalidECDHPoint-Client",
 		config: Config{
-			// TODO(davidben): Add a TLS 1.3 version of this test.
 			MaxVersion:       VersionTLS12,
 			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 			CurvePreferences: []CurveID{CurveP256},
@@ -5911,10 +6376,22 @@
 		expectedError: ":INVALID_ENCODING:",
 	})
 	testCases = append(testCases, testCase{
+		name: "InvalidECDHPoint-Client-TLS13",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			CurvePreferences: []CurveID{CurveP256},
+			Bugs: ProtocolBugs{
+				InvalidECDHPoint: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":INVALID_ENCODING:",
+	})
+	testCases = append(testCases, testCase{
 		testType: serverTest,
 		name:     "InvalidECDHPoint-Server",
 		config: Config{
-			// TODO(davidben): Add a TLS 1.3 version of this test.
 			MaxVersion:       VersionTLS12,
 			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 			CurvePreferences: []CurveID{CurveP256},
@@ -5925,6 +6402,20 @@
 		shouldFail:    true,
 		expectedError: ":INVALID_ENCODING:",
 	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "InvalidECDHPoint-Server-TLS13",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			CurvePreferences: []CurveID{CurveP256},
+			Bugs: ProtocolBugs{
+				InvalidECDHPoint: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":INVALID_ENCODING:",
+	})
 }
 
 func addCECPQ1Tests() {
@@ -6016,9 +6507,6 @@
 		flags: []string{"-expect-dhe-group-size", "2048"},
 	})
 
-	// TODO(davidben): Add TLS 1.3 versions of these tests once the
-	// handshake is separate.
-
 	testCases = append(testCases, testCase{
 		name: "KeyExchangeInfo-ECDHE-Client",
 		config: Config{
@@ -6242,6 +6730,32 @@
 		expectedError: ":BUFFERED_MESSAGES_ON_CIPHER_CHANGE:",
 	})
 
+	// Test synchronization between encryption changes and the handshake in
+	// TLS 1.3, where ChangeCipherSpec is implicit.
+	testCases = append(testCases, testCase{
+		name: "PartialEncryptedExtensionsWithServerHello",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				PartialEncryptedExtensionsWithServerHello: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":BUFFERED_MESSAGES_ON_CIPHER_CHANGE:",
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "PartialClientFinishedWithClientHello",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				PartialClientFinishedWithClientHello: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":BUFFERED_MESSAGES_ON_CIPHER_CHANGE:",
+	})
+
 	// Test that early ChangeCipherSpecs are handled correctly.
 	testCases = append(testCases, testCase{
 		testType: serverTest,
@@ -6589,6 +7103,238 @@
 	}
 }
 
+func addTLS13WrongMessageTypeTests() {
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "WrongMessageType-TLS13-ClientHello",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeClientHello,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "WrongMessageType-TLS13-ServerHello",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeServerHello,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_MESSAGE:",
+		// The alert comes in with the wrong encryption.
+		expectedLocalError: "local error: bad record MAC",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "WrongMessageType-TLS13-EncryptedExtensions",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeEncryptedExtensions,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "WrongMessageType-TLS13-CertificateRequest",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequireAnyClientCert,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeCertificateRequest,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "WrongMessageType-TLS13-ServerCertificate",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeCertificate,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "WrongMessageType-TLS13-ServerCertificateVerify",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeCertificateVerify,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "WrongMessageType-TLS13-ServerFinished",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeFinished,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "WrongMessageType-TLS13-ClientCertificate",
+		config: Config{
+			Certificates: []Certificate{rsaCertificate},
+			MaxVersion:   VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeCertificate,
+			},
+		},
+		flags:              []string{"-require-any-client-certificate"},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "WrongMessageType-TLS13-ClientCertificateVerify",
+		config: Config{
+			Certificates: []Certificate{rsaCertificate},
+			MaxVersion:   VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeCertificateVerify,
+			},
+		},
+		flags:              []string{"-require-any-client-certificate"},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "WrongMessageType-TLS13-ClientFinished",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendWrongMessageType: typeFinished,
+			},
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+}
+
+func addTLS13HandshakeTests() {
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "MissingKeyShare-Client",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				MissingKeyShare: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":MISSING_KEY_SHARE:",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "MissingKeyShare-Server",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				MissingKeyShare: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":MISSING_KEY_SHARE:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "ClientHelloMissingKeyShare",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				MissingKeyShare: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":MISSING_KEY_SHARE:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "MissingKeyShare",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				MissingKeyShare: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":MISSING_KEY_SHARE:",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "DuplicateKeyShares",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				DuplicateKeyShares: true,
+			},
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "EmptyEncryptedExtensions",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				EmptyEncryptedExtensions: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: error decoding message",
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "EncryptedExtensionsWithKeyShare",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				EncryptedExtensionsWithKeyShare: true,
+			},
+		},
+		shouldFail:         true,
+		expectedLocalError: "remote error: unsupported extension",
+	})
+}
+
 func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
 	defer wg.Done()
 
@@ -6694,6 +7440,8 @@
 	addAllStateMachineCoverageTests()
 	addChangeCipherSpecTests()
 	addWrongMessageTypeTests()
+	addTLS13WrongMessageTypeTests()
+	addTLS13HandshakeTests()
 
 	var wg sync.WaitGroup
 
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 24a4646..b1a0792 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -61,6 +61,7 @@
   { "-no-tls11", &TestConfig::no_tls11 },
   { "-no-tls1", &TestConfig::no_tls1 },
   { "-no-ssl3", &TestConfig::no_ssl3 },
+  { "-enable-channel-id", &TestConfig::enable_channel_id },
   { "-shim-writes-first", &TestConfig::shim_writes_first },
   { "-expect-session-miss", &TestConfig::expect_session_miss },
   { "-decline-alpn", &TestConfig::decline_alpn },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 4ee717e..73bdc6e 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -46,6 +46,7 @@
   bool no_tls1 = false;
   bool no_ssl3 = false;
   std::string expected_channel_id;
+  bool enable_channel_id = false;
   std::string send_channel_id;
   bool shim_writes_first = false;
   std::string host_name;
diff --git a/ssl/tls13_both.c b/ssl/tls13_both.c
new file mode 100644
index 0000000..4e9feaa
--- /dev/null
+++ b/ssl/tls13_both.c
@@ -0,0 +1,437 @@
+/* Copyright (c) 2016, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+#include <openssl/hkdf.h>
+#include <openssl/mem.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "internal.h"
+
+
+SSL_HANDSHAKE *ssl_handshake_new(enum ssl_hs_wait_t (*do_handshake)(SSL *ssl)) {
+  SSL_HANDSHAKE *hs = OPENSSL_malloc(sizeof(SSL_HANDSHAKE));
+  if (hs == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  memset(hs, 0, sizeof(SSL_HANDSHAKE));
+  hs->do_handshake = do_handshake;
+  hs->wait = ssl_hs_ok;
+  return hs;
+}
+
+void ssl_handshake_free(SSL_HANDSHAKE *hs) {
+  if (hs == NULL) {
+    return;
+  }
+
+  OPENSSL_cleanse(hs->secret, sizeof(hs->secret));
+  OPENSSL_cleanse(hs->traffic_secret_0, sizeof(hs->traffic_secret_0));
+  if (hs->groups != NULL) {
+    for (size_t i = 0; i < hs->groups_len; i++) {
+      SSL_ECDH_CTX_cleanup(&hs->groups[i]);
+    }
+    OPENSSL_free(hs->groups);
+  }
+  OPENSSL_free(hs->public_key);
+  OPENSSL_free(hs->cert_context);
+  OPENSSL_free(hs);
+}
+
+int tls13_handshake(SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+
+  for (;;) {
+    /* Resolve the operation the handshake was waiting on. */
+    switch (hs->wait) {
+      case ssl_hs_error:
+        OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
+        return -1;
+
+      case ssl_hs_read_message: {
+        int ret = ssl->method->ssl_get_message(ssl, -1, ssl_dont_hash_message);
+        if (ret <= 0) {
+          return ret;
+        }
+        break;
+      }
+
+      case ssl_hs_write_message: {
+        int ret = ssl->method->write_message(ssl);
+        if (ret <= 0) {
+          return ret;
+        }
+        break;
+      }
+
+      case ssl_hs_flush: {
+        int ret = BIO_flush(ssl->wbio);
+        if (ret <= 0) {
+          ssl->rwstate = SSL_WRITING;
+          return ret;
+        }
+        break;
+      }
+
+      case ssl_hs_x509_lookup:
+        ssl->rwstate = SSL_X509_LOOKUP;
+        hs->wait = ssl_hs_ok;
+        return -1;
+
+      case ssl_hs_private_key_operation:
+        ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        hs->wait = ssl_hs_ok;
+        return -1;
+
+      case ssl_hs_ok:
+        break;
+    }
+
+    /* Run the state machine again. */
+    hs->wait = hs->do_handshake(ssl);
+    if (hs->wait == ssl_hs_error) {
+      /* Don't loop around to avoid a stray |SSL_R_SSL_HANDSHAKE_FAILURE| the
+       * first time around. */
+      return -1;
+    }
+    if (hs->wait == ssl_hs_ok) {
+      /* The handshake has completed. */
+      return 1;
+    }
+
+    /* Otherwise, loop to the beginning and resolve what was blocking the
+     * handshake. */
+  }
+}
+
+static int tls13_get_cert_verify_signature_input(SSL *ssl, uint8_t **out,
+                                                 size_t *out_len, int server) {
+  CBB cbb;
+  if (!CBB_init(&cbb, 64 + 33 + 1 + 2 * EVP_MAX_MD_SIZE)) {
+    goto err;
+  }
+
+  for (size_t i = 0; i < 64; i++) {
+    if (!CBB_add_u8(&cbb, 0x20)) {
+      goto err;
+    }
+  }
+
+  if (server) {
+    /* Include the NUL byte. */
+    static const char kContext[] = "TLS 1.3, server CertificateVerify";
+    if (!CBB_add_bytes(&cbb, (const uint8_t *)kContext, sizeof(kContext))) {
+      goto err;
+    }
+  } else {
+    static const char kContext[] = "TLS 1.3, client CertificateVerify";
+    if (!CBB_add_bytes(&cbb, (const uint8_t *)kContext, sizeof(kContext))) {
+      goto err;
+    }
+  }
+
+  uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
+  size_t context_hashes_len;
+  if (!tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len) ||
+      !CBB_add_bytes(&cbb, context_hashes, context_hashes_len) ||
+      !CBB_finish(&cbb, out, out_len)) {
+    goto err;
+  }
+
+  return 1;
+
+err:
+  OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+  CBB_cleanup(&cbb);
+  return 0;
+}
+
+int tls13_process_certificate(SSL *ssl) {
+  CBS cbs, context;
+  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u8_length_prefixed(&cbs, &context) ||
+      CBS_len(&context) != 0) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return 0;
+  }
+
+  int ret = 0;
+  uint8_t alert;
+  STACK_OF(X509) *chain = ssl_parse_cert_chain(
+      ssl, &alert,
+      ssl->ctx->retain_only_sha256_of_client_certs ? ssl->session->peer_sha256
+                                                   : NULL,
+      &cbs);
+  if (chain == NULL) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    goto err;
+  }
+
+  if (CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    goto err;
+  }
+
+  if (sk_X509_num(chain) == 0) {
+    /* Clients must receive a certificate from the server. */
+    if (!ssl->server) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      goto err;
+    }
+
+    /* Servers may be configured to accept anonymous clients. */
+    if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
+        (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      goto err;
+    }
+
+    /* No certificate, so nothing more to do. */
+    ret = 1;
+    goto err;
+  }
+
+  if (ssl->server && ssl->ctx->retain_only_sha256_of_client_certs) {
+    /* The hash was filled in by |ssl_parse_cert_chain|. */
+    ssl->session->peer_sha256_valid = 1;
+  }
+
+  X509 *leaf = sk_X509_value(chain, 0);
+  if (!ssl->server && !ssl_check_leaf_certificate(ssl, leaf)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    goto err;
+  }
+
+  int verify_ret = ssl_verify_cert_chain(ssl, chain);
+  /* If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result. */
+  if (ssl->verify_mode != SSL_VERIFY_NONE && verify_ret <= 0) {
+    int al = ssl_verify_alarm_type(ssl->verify_result);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
+    goto err;
+  }
+  ERR_clear_error();
+
+  ssl->session->verify_result = ssl->verify_result;
+
+  X509_free(ssl->session->peer);
+  /* For historical reasons, the client and server differ on whether the chain
+   * includes the leaf. */
+  if (ssl->server) {
+    ssl->session->peer = sk_X509_shift(chain);
+  } else {
+    ssl->session->peer = X509_up_ref(leaf);
+  }
+
+  sk_X509_pop_free(ssl->session->cert_chain, X509_free);
+  ssl->session->cert_chain = chain;
+  chain = NULL;
+
+  ret = 1;
+
+err:
+  sk_X509_pop_free(chain, X509_free);
+  return ret;
+}
+
+int tls13_process_certificate_verify(SSL *ssl) {
+  int ret = 0;
+  X509 *peer = ssl->session->peer;
+  EVP_PKEY *pkey = NULL;
+  uint8_t *msg = NULL;
+  size_t msg_len;
+
+  /* Filter out unsupported certificate types. */
+  pkey = X509_get_pubkey(peer);
+  if (pkey == NULL) {
+    goto err;
+  }
+
+  CBS cbs, signature;
+  uint16_t signature_algorithm;
+  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u16(&cbs, &signature_algorithm) ||
+      !CBS_get_u16_length_prefixed(&cbs, &signature) ||
+      CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    goto err;
+  }
+
+  int al;
+  if (!tls12_check_peer_sigalg(ssl, &al, signature_algorithm)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+    goto err;
+  }
+  ssl->s3->tmp.peer_signature_algorithm = signature_algorithm;
+
+  if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
+                                             !ssl->server)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    goto err;
+  }
+
+  int sig_ok =
+      ssl_public_key_verify(ssl, CBS_data(&signature), CBS_len(&signature),
+                            signature_algorithm, pkey, msg, msg_len);
+  if (!sig_ok) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  EVP_PKEY_free(pkey);
+  OPENSSL_free(msg);
+  return ret;
+}
+
+int tls13_check_message_type(SSL *ssl, int type) {
+  if (ssl->s3->tmp.message_type != type) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    return 0;
+  }
+
+  return 1;
+}
+
+int tls13_process_finished(SSL *ssl) {
+  uint8_t verify_data[EVP_MAX_MD_SIZE];
+  size_t verify_data_len;
+  if (!tls13_finished_mac(ssl, verify_data, &verify_data_len, !ssl->server)) {
+    return 0;
+  }
+
+  if (ssl->init_num != verify_data_len ||
+      CRYPTO_memcmp(verify_data, ssl->init_msg, verify_data_len) != 0) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
+    return 0;
+  }
+
+  return 1;
+}
+
+int tls13_prepare_certificate(SSL *ssl) {
+  CBB cbb, body, context;
+  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
+      !CBB_add_u8_length_prefixed(&body, &context) ||
+      !CBB_add_bytes(&context, ssl->s3->hs->cert_context,
+                     ssl->s3->hs->cert_context_len) ||
+      !ssl_add_cert_chain(ssl, &body) ||
+      !ssl->method->finish_message(ssl, &cbb)) {
+    CBB_cleanup(&cbb);
+    return 0;
+  }
+
+  return 1;
+}
+
+enum ssl_private_key_result_t tls13_prepare_certificate_verify(
+    SSL *ssl, int is_first_run) {
+  enum ssl_private_key_result_t ret = ssl_private_key_failure;
+  uint8_t *msg = NULL;
+  size_t msg_len;
+  CBB cbb, body;
+  CBB_zero(&cbb);
+
+  uint16_t signature_algorithm;
+  if (!tls1_choose_signature_algorithm(ssl, &signature_algorithm)) {
+    goto err;
+  }
+  if (!ssl->method->init_message(ssl, &cbb, &body,
+                                 SSL3_MT_CERTIFICATE_VERIFY) ||
+      !CBB_add_u16(&body, signature_algorithm)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  /* Sign the digest. */
+  CBB child;
+  const size_t max_sig_len = ssl_private_key_max_signature_len(ssl);
+  uint8_t *sig;
+  size_t sig_len;
+  if (!CBB_add_u16_length_prefixed(&body, &child) ||
+      !CBB_reserve(&child, &sig, max_sig_len)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    goto err;
+  }
+
+  enum ssl_private_key_result_t sign_result;
+  if (is_first_run) {
+    if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
+                                               ssl->server)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      goto err;
+    }
+    sign_result = ssl_private_key_sign(ssl, sig, &sig_len, max_sig_len,
+                                       signature_algorithm, msg, msg_len);
+  } else {
+    sign_result = ssl_private_key_complete(ssl, sig, &sig_len, max_sig_len);
+  }
+
+  if (sign_result != ssl_private_key_success) {
+    ret = sign_result;
+    goto err;
+  }
+
+  if (!CBB_did_write(&child, sig_len) ||
+      !ssl->method->finish_message(ssl, &cbb)) {
+    goto err;
+  }
+
+  ret = ssl_private_key_success;
+
+err:
+  CBB_cleanup(&cbb);
+  OPENSSL_free(msg);
+  return ret;
+}
+
+int tls13_prepare_finished(SSL *ssl) {
+  size_t verify_data_len;
+  uint8_t verify_data[EVP_MAX_MD_SIZE];
+
+  if (!tls13_finished_mac(ssl, verify_data, &verify_data_len, ssl->server)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
+    return 0;
+  }
+
+  CBB cbb, body;
+  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
+      !CBB_add_bytes(&body, verify_data, verify_data_len) ||
+      !ssl->method->finish_message(ssl, &cbb)) {
+    CBB_cleanup(&cbb);
+    return 0;
+  }
+
+  return 1;
+}
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
new file mode 100644
index 0000000..cf8b390
--- /dev/null
+++ b/ssl/tls13_client.c
@@ -0,0 +1,462 @@
+/* Copyright (c) 2016, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+
+#include "internal.h"
+
+
+enum client_hs_state_t {
+  state_process_server_hello = 0,
+  state_process_encrypted_extensions,
+  state_process_certificate_request,
+  state_process_server_certificate,
+  state_process_server_certificate_verify,
+  state_process_server_finished,
+  state_certificate_callback,
+  state_send_client_certificate,
+  state_send_client_certificate_verify,
+  state_complete_client_certificate_verify,
+  state_send_client_finished,
+  state_flush,
+  state_done,
+};
+
+static enum ssl_hs_wait_t do_process_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!tls13_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
+    return ssl_hs_error;
+  }
+
+  CBS cbs, server_random, extensions;
+  uint16_t server_wire_version;
+  uint16_t cipher_suite;
+  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u16(&cbs, &server_wire_version) ||
+      !CBS_get_bytes(&cbs, &server_random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u16(&cbs, &cipher_suite) ||
+      !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
+      CBS_len(&cbs) != 0) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  /* Parse out the extensions. */
+  int have_key_share = 0;
+  CBS key_share;
+  while (CBS_len(&extensions) != 0) {
+    uint16_t type;
+    CBS extension;
+    if (!CBS_get_u16(&extensions, &type) ||
+        !CBS_get_u16_length_prefixed(&extensions, &extension)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      return ssl_hs_error;
+    }
+
+    switch (type) {
+      case TLSEXT_TYPE_key_share:
+        if (have_key_share) {
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+          return ssl_hs_error;
+        }
+        key_share = extension;
+        have_key_share = 1;
+        break;
+      default:
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        return ssl_hs_error;
+    }
+  }
+
+  assert(ssl->s3->have_version);
+  memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE);
+
+  ssl->hit = 0;
+  if (!ssl_get_new_session(ssl, 0)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    return ssl_hs_error;
+  }
+
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
+  if (cipher == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
+  }
+
+  /* Check if the cipher is disabled. */
+  if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
+      (cipher->algorithm_auth & ssl->cert->mask_a) ||
+      SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
+      SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) ||
+      !sk_SSL_CIPHER_find(ssl_get_ciphers_by_id(ssl), NULL, cipher)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
+  }
+
+  ssl->session->cipher = cipher;
+  ssl->s3->tmp.new_cipher = cipher;
+
+  /* The PRF hash is now known. Set up the key schedule. */
+  static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
+  size_t hash_len =
+      EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
+  if (!tls13_init_key_schedule(ssl, kZeroes, hash_len)) {
+    return ssl_hs_error;
+  }
+
+  /* Resolve PSK and incorporate it into the secret. */
+  if (cipher->algorithm_auth == SSL_aPSK) {
+    /* TODO(davidben): Support PSK. */
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return ssl_hs_error;
+  } else if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) {
+    return ssl_hs_error;
+  }
+
+  /* Resolve ECDHE and incorporate it into the secret. */
+  if (cipher->algorithm_mkey == SSL_kECDHE) {
+    if (!have_key_share) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
+      return ssl_hs_error;
+    }
+
+    uint8_t *dhe_secret;
+    size_t dhe_secret_len;
+    uint8_t alert = SSL_AD_DECODE_ERROR;
+    if (!ext_key_share_parse_serverhello(ssl, &dhe_secret, &dhe_secret_len,
+                                         &alert, &key_share)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      return ssl_hs_error;
+    }
+
+    int ok = tls13_advance_key_schedule(ssl, dhe_secret, dhe_secret_len);
+    OPENSSL_free(dhe_secret);
+    if (!ok) {
+      return ssl_hs_error;
+    }
+  } else {
+    if (have_key_share) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+      return ssl_hs_error;
+    }
+    if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) {
+      return ssl_hs_error;
+    }
+    return ssl_hs_error;
+  }
+
+  if (!tls13_set_handshake_traffic(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_process_encrypted_extensions;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_encrypted_extensions(SSL *ssl,
+                                                          SSL_HANDSHAKE *hs) {
+  if (!tls13_check_message_type(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS)) {
+    return ssl_hs_error;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
+  if (!ssl_parse_serverhello_tlsext(ssl, &cbs)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+    return ssl_hs_error;
+  }
+  if (CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  if (!ssl->method->hash_current_message(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_process_certificate_request;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_certificate_request(SSL *ssl,
+                                                         SSL_HANDSHAKE *hs) {
+  ssl->s3->tmp.cert_request = 0;
+
+  /* CertificateRequest may only be sent in certificate-based ciphers. */
+  if (!ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+    hs->state = state_process_server_finished;
+    return ssl_hs_ok;
+  }
+
+  /* CertificateRequest is optional. */
+  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) {
+    hs->state = state_process_server_certificate;
+    return ssl_hs_ok;
+  }
+
+  CBS cbs, context, supported_signature_algorithms;
+  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u8_length_prefixed(&cbs, &context) ||
+      !CBS_stow(&context, &ssl->s3->hs->cert_context,
+                &ssl->s3->hs->cert_context_len) ||
+      !CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
+      !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  uint8_t alert;
+  STACK_OF(X509_NAME) *ca_sk = ssl_parse_client_CA_list(ssl, &alert, &cbs);
+  if (ca_sk == NULL) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return ssl_hs_error;
+  }
+
+  /* Ignore extensions. */
+  CBS extensions;
+  if (!CBS_get_u16_length_prefixed(&cbs, &extensions) ||
+      CBS_len(&cbs) != 0) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  ssl->s3->tmp.cert_request = 1;
+  sk_X509_NAME_pop_free(ssl->s3->tmp.ca_names, X509_NAME_free);
+  ssl->s3->tmp.ca_names = ca_sk;
+
+  if (!ssl->method->hash_current_message(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_process_server_certificate;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_server_certificate(SSL *ssl,
+                                                        SSL_HANDSHAKE *hs) {
+  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
+      !tls13_process_certificate(ssl) ||
+      !ssl->method->hash_current_message(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_process_server_certificate_verify;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_server_certificate_verify(
+    SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
+      !tls13_process_certificate_verify(ssl) ||
+      !ssl->method->hash_current_message(ssl)) {
+    return 0;
+  }
+
+  hs->state = state_process_server_finished;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_server_finished(SSL *ssl,
+                                                     SSL_HANDSHAKE *hs) {
+  static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
+
+  if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
+      !tls13_process_finished(ssl) ||
+      !ssl->method->hash_current_message(ssl) ||
+      /* Update the secret to the master secret and derive traffic keys. */
+      !tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
+      !tls13_derive_traffic_secret_0(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_certificate_callback;
+  return ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_certificate_callback(SSL *ssl, SSL_HANDSHAKE *hs) {
+  /* The peer didn't request a certificate. */
+  if (!ssl->s3->tmp.cert_request) {
+    hs->state = state_send_client_finished;
+    return ssl_hs_ok;
+  }
+
+  /* Call cert_cb to update the certificate. */
+  if (ssl->cert->cert_cb != NULL) {
+    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+    if (rv == 0) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
+      return ssl_hs_error;
+    }
+    if (rv < 0) {
+      hs->state = state_certificate_callback;
+      return ssl_hs_x509_lookup;
+    }
+  }
+
+  hs->state = state_send_client_certificate;
+  return ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_send_client_certificate(SSL *ssl,
+                                                     SSL_HANDSHAKE *hs) {
+  if (!ssl_has_certificate(ssl) && ssl->ctx->client_cert_cb != NULL) {
+    X509 *x509 = NULL;
+    EVP_PKEY *pkey = NULL;
+    int rv = ssl->ctx->client_cert_cb(ssl, &x509, &pkey);
+    if (rv < 0) {
+      hs->state = state_send_client_certificate;
+      return ssl_hs_x509_lookup;
+    }
+
+    int setup_error = rv == 1 && (!SSL_use_certificate(ssl, x509) ||
+                                  !SSL_use_PrivateKey(ssl, pkey));
+    X509_free(x509);
+    EVP_PKEY_free(pkey);
+    if (setup_error) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_hs_error;
+    }
+  }
+
+  if (!tls13_prepare_certificate(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_send_client_certificate_verify;
+  return ssl_hs_write_message;
+}
+
+static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL *ssl,
+                                                            SSL_HANDSHAKE *hs,
+                                                            int is_first_run) {
+  /* Don't send CertificateVerify if there is no certificate. */
+  if (!ssl_has_certificate(ssl)) {
+    hs->state = state_send_client_finished;
+    return ssl_hs_ok;
+  }
+
+  switch (tls13_prepare_certificate_verify(ssl, is_first_run)) {
+    case ssl_private_key_success:
+      hs->state = state_send_client_finished;
+      return ssl_hs_write_message;
+
+    case ssl_private_key_retry:
+      hs->state = state_complete_client_certificate_verify;
+      return ssl_hs_private_key_operation;
+
+    case ssl_private_key_failure:
+      return ssl_hs_error;
+  }
+
+  assert(0);
+  return ssl_hs_error;
+}
+
+static enum ssl_hs_wait_t do_send_client_finished(SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!tls13_prepare_finished(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_flush;
+  return ssl_hs_write_message;
+}
+
+static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!tls13_set_traffic_key(ssl, type_data, evp_aead_open,
+                             hs->traffic_secret_0, hs->hash_len) ||
+      !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
+                             hs->traffic_secret_0, hs->hash_len) ||
+      !tls13_finalize_keys(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_done;
+  return ssl_hs_flush;
+}
+
+enum ssl_hs_wait_t tls13_client_handshake(SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+
+  while (hs->state != state_done) {
+    enum ssl_hs_wait_t ret = ssl_hs_error;
+    enum client_hs_state_t state = hs->state;
+    switch (state) {
+      case state_process_server_hello:
+        ret = do_process_server_hello(ssl, hs);
+        break;
+      case state_process_encrypted_extensions:
+        ret = do_process_encrypted_extensions(ssl, hs);
+        break;
+      case state_process_certificate_request:
+        ret = do_process_certificate_request(ssl, hs);
+        break;
+      case state_process_server_certificate:
+        ret = do_process_server_certificate(ssl, hs);
+        break;
+      case state_process_server_certificate_verify:
+        ret = do_process_server_certificate_verify(ssl, hs);
+        break;
+      case state_process_server_finished:
+        ret = do_process_server_finished(ssl, hs);
+        break;
+      case state_certificate_callback:
+        ret = do_certificate_callback(ssl, hs);
+        break;
+      case state_send_client_certificate:
+        ret = do_send_client_certificate(ssl, hs);
+        break;
+      case state_send_client_certificate_verify:
+        ret = do_send_client_certificate_verify(ssl, hs, 1 /* first run */);
+      break;
+      case state_complete_client_certificate_verify:
+        ret = do_send_client_certificate_verify(ssl, hs, 0 /* complete */);
+      break;
+      case state_send_client_finished:
+        ret = do_send_client_finished(ssl, hs);
+        break;
+      case state_flush:
+        ret = do_flush(ssl, hs);
+        break;
+      case state_done:
+        ret = ssl_hs_ok;
+        break;
+    }
+
+    if (ret != ssl_hs_ok) {
+      return ret;
+    }
+  }
+
+  return ssl_hs_ok;
+}
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
new file mode 100644
index 0000000..ea88675
--- /dev/null
+++ b/ssl/tls13_enc.c
@@ -0,0 +1,344 @@
+/* Copyright (c) 2016, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/aead.h>
+#include <openssl/bytestring.h>
+#include <openssl/digest.h>
+#include <openssl/hmac.h>
+#include <openssl/hkdf.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+int tls13_init_key_schedule(SSL *ssl, const uint8_t *resumption_ctx,
+                            size_t resumption_ctx_len) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+
+  hs->hash_len = EVP_MD_size(digest);
+
+  /* Save the hash of the resumption context. */
+  unsigned resumption_hash_len;
+  if (!EVP_Digest(resumption_ctx, resumption_ctx_len, hs->resumption_hash,
+                  &resumption_hash_len, digest, NULL)) {
+    return 0;
+  }
+
+  /* Initialize the secret to the zero key. */
+  memset(hs->secret, 0, hs->hash_len);
+
+  /* Initialize the rolling hashes and release the handshake buffer. */
+  if (!ssl3_init_handshake_hash(ssl)) {
+    return 0;
+  }
+  ssl3_free_handshake_buffer(ssl);
+  return 1;
+}
+
+int tls13_advance_key_schedule(SSL *ssl, const uint8_t *in, size_t len) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+
+  return HKDF_extract(hs->secret, &hs->hash_len, digest, in, len, hs->secret,
+                      hs->hash_len);
+}
+
+static int hkdf_expand_label(uint8_t *out, const EVP_MD *digest,
+                             const uint8_t *secret, size_t secret_len,
+                             const uint8_t *label, size_t label_len,
+                             const uint8_t *hash, size_t hash_len, size_t len) {
+  static const char kTLS13LabelVersion[] = "TLS 1.3, ";
+
+  CBB cbb, child;
+  uint8_t *hkdf_label;
+  size_t hkdf_label_len;
+  if (!CBB_init(&cbb, 2 + 1 + strlen(kTLS13LabelVersion) + label_len + 1 +
+                          hash_len) ||
+      !CBB_add_u16(&cbb, len) ||
+      !CBB_add_u8_length_prefixed(&cbb, &child) ||
+      !CBB_add_bytes(&child, (const uint8_t *)kTLS13LabelVersion,
+                     strlen(kTLS13LabelVersion)) ||
+      !CBB_add_bytes(&child, label, label_len) ||
+      !CBB_add_u8_length_prefixed(&cbb, &child) ||
+      !CBB_add_bytes(&child, hash, hash_len) ||
+      !CBB_finish(&cbb, &hkdf_label, &hkdf_label_len)) {
+    CBB_cleanup(&cbb);
+    return 0;
+  }
+
+  int ret = HKDF_expand(out, len, digest, secret, secret_len, hkdf_label,
+                        hkdf_label_len);
+  OPENSSL_free(hkdf_label);
+  return ret;
+}
+
+int tls13_get_context_hashes(SSL *ssl, uint8_t *out, size_t *out_len) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+
+  EVP_MD_CTX ctx;
+  EVP_MD_CTX_init(&ctx);
+  unsigned handshake_len = 0;
+  int ok = EVP_MD_CTX_copy_ex(&ctx, &ssl->s3->handshake_hash) &&
+           EVP_DigestFinal_ex(&ctx, out, &handshake_len);
+  EVP_MD_CTX_cleanup(&ctx);
+  if (!ok) {
+    return 0;
+  }
+
+  memcpy(out + handshake_len, hs->resumption_hash, hs->hash_len);
+  *out_len = handshake_len + hs->hash_len;
+  return 1;
+}
+
+/* derive_secret derives a secret of length |len| and writes the result in |out|
+ * with the given label and the current base secret and most recently-saved
+ * handshake context. It returns one on success and zero on error. */
+static int derive_secret(SSL *ssl, uint8_t *out, size_t len,
+                         const uint8_t *label, size_t label_len) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+
+  uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
+  size_t context_hashes_len;
+  if (!tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len)) {
+    return 0;
+  }
+
+  return hkdf_expand_label(out, digest, hs->secret, hs->hash_len, label,
+                           label_len, context_hashes, context_hashes_len, len);
+}
+
+int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
+                          enum evp_aead_direction_t direction,
+                          const uint8_t *traffic_secret,
+                          size_t traffic_secret_len) {
+  if (traffic_secret_len > 0xff) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+    return 0;
+  }
+
+  const char *phase;
+  switch (type) {
+    case type_early_handshake:
+      phase = "early handshake key expansion, ";
+      break;
+    case type_early_data:
+      phase = "early application data key expansion, ";
+      break;
+    case type_handshake:
+      phase = "handshake key expansion, ";
+      break;
+    case type_data:
+      phase = "application data key expansion, ";
+      break;
+    default:
+      return 0;
+  }
+  size_t phase_len = strlen(phase);
+
+  const char *purpose = "client write key";
+  if ((ssl->server && direction == evp_aead_seal) ||
+      (!ssl->server && direction == evp_aead_open)) {
+    purpose = "server write key";
+  }
+  size_t purpose_len = strlen(purpose);
+
+  /* The longest label has length 38 (type_early_data) + 16 (either purpose
+   * value). */
+  uint8_t label[38 + 16];
+  size_t label_len = phase_len + purpose_len;
+  if (label_len > sizeof(label)) {
+    assert(0);
+    return 0;
+  }
+  memcpy(label, phase, phase_len);
+  memcpy(label + phase_len, purpose, purpose_len);
+
+  /* Look up cipher suite properties. */
+  const EVP_AEAD *aead;
+  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+  size_t mac_secret_len, fixed_iv_len;
+  if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
+                               ssl->session->cipher,
+                               ssl3_protocol_version(ssl))) {
+    return 0;
+  }
+
+  /* Derive the key. */
+  size_t key_len = EVP_AEAD_key_length(aead);
+  uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
+  if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len, label,
+                         label_len, NULL, 0, key_len)) {
+    return 0;
+  }
+
+  /* The IV's label ends in "iv" instead of "key". */
+  if (label_len < 3) {
+    assert(0);
+    return 0;
+  }
+  label_len--;
+  label[label_len - 2] = 'i';
+  label[label_len - 1] = 'v';
+
+  /* Derive the IV. */
+  size_t iv_len = EVP_AEAD_nonce_length(aead);
+  uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
+  if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, label,
+                         label_len, NULL, 0, iv_len)) {
+    return 0;
+  }
+
+  SSL_AEAD_CTX *traffic_aead = SSL_AEAD_CTX_new(direction,
+                                                ssl3_protocol_version(ssl),
+                                                ssl->session->cipher,
+                                                key, key_len, NULL, 0,
+                                                iv, iv_len);
+  if (traffic_aead == NULL) {
+    return 0;
+  }
+
+  if (direction == evp_aead_open) {
+    if (!ssl->method->set_read_state(ssl, traffic_aead)) {
+      return 0;
+    }
+  } else {
+    if (!ssl->method->set_write_state(ssl, traffic_aead)) {
+      return 0;
+    }
+  }
+
+  /* Save the traffic secret. */
+  if (direction == evp_aead_open) {
+    memcpy(ssl->s3->read_traffic_secret, traffic_secret, traffic_secret_len);
+    ssl->s3->read_traffic_secret_len = traffic_secret_len;
+  } else {
+    memcpy(ssl->s3->write_traffic_secret, traffic_secret, traffic_secret_len);
+    ssl->s3->write_traffic_secret_len = traffic_secret_len;
+  }
+
+  return 1;
+}
+
+static const char kTLS13LabelHandshakeTraffic[] = "handshake traffic secret";
+static const char kTLS13LabelApplicationTraffic[] =
+    "application traffic secret";
+
+int tls13_set_handshake_traffic(SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+
+  uint8_t traffic_secret[EVP_MAX_MD_SIZE];
+  if (!derive_secret(ssl, traffic_secret, hs->hash_len,
+                     (const uint8_t *)kTLS13LabelHandshakeTraffic,
+                     strlen(kTLS13LabelHandshakeTraffic)) ||
+      !tls13_set_traffic_key(ssl, type_handshake, evp_aead_open, traffic_secret,
+                             hs->hash_len) ||
+      !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal, traffic_secret,
+                             hs->hash_len)) {
+    return 0;
+  }
+  return 1;
+}
+
+int tls13_derive_traffic_secret_0(SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+
+  return derive_secret(ssl, hs->traffic_secret_0, hs->hash_len,
+                       (const uint8_t *)kTLS13LabelApplicationTraffic,
+                       strlen(kTLS13LabelApplicationTraffic));
+}
+
+static const char kTLS13LabelExporter[] = "exporter master secret";
+static const char kTLS13LabelResumption[] = "resumption master secret";
+
+int tls13_finalize_keys(SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+
+  ssl->s3->exporter_secret_len = hs->hash_len;
+  ssl->session->master_key_length = hs->hash_len;
+  if (!derive_secret(
+          ssl, ssl->s3->exporter_secret, ssl->s3->exporter_secret_len,
+          (const uint8_t *)kTLS13LabelExporter, strlen(kTLS13LabelExporter)) ||
+      !derive_secret(ssl, ssl->session->master_key,
+                     ssl->session->master_key_length,
+                     (const uint8_t *)kTLS13LabelResumption,
+                     strlen(kTLS13LabelResumption))) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int tls13_finished_mac(SSL *ssl, uint8_t *out, size_t *out_len, int is_server) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+
+  uint8_t key[EVP_MAX_MD_SIZE];
+  size_t key_len = EVP_MD_size(digest);
+
+  uint8_t *traffic_secret;
+  const char *label;
+  if (is_server) {
+    label = "server finished";
+    if (ssl->server) {
+      traffic_secret = ssl->s3->write_traffic_secret;
+    } else {
+      traffic_secret = ssl->s3->read_traffic_secret;
+    }
+  } else {
+    label = "client finished";
+    if (!ssl->server) {
+      traffic_secret = ssl->s3->write_traffic_secret;
+    } else {
+      traffic_secret = ssl->s3->read_traffic_secret;
+    }
+  }
+
+  uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
+  size_t context_hashes_len;
+  unsigned len;
+  if (!hkdf_expand_label(key, digest, traffic_secret, hs->hash_len,
+                         (const uint8_t *)label, strlen(label), NULL, 0,
+                         hs->hash_len) ||
+      !tls13_get_context_hashes(ssl, context_hashes, &context_hashes_len) ||
+      HMAC(digest, key, key_len, context_hashes, context_hashes_len, out,
+           &len) == NULL) {
+    return 0;
+  }
+  *out_len = len;
+  return 1;
+}
+
+int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
+                                 const char *label, size_t label_len,
+                                 const uint8_t *context, size_t context_len,
+                                 int use_context) {
+  const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
+
+  const uint8_t *hash = NULL;
+  size_t hash_len = 0;
+  if (use_context) {
+    hash = context;
+    hash_len = context_len;
+  }
+  return hkdf_expand_label(out, digest, ssl->s3->exporter_secret,
+                           ssl->s3->exporter_secret_len, (const uint8_t *)label,
+                           label_len, hash, hash_len, out_len);
+}
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
new file mode 100644
index 0000000..ce65abf
--- /dev/null
+++ b/ssl/tls13_server.c
@@ -0,0 +1,483 @@
+/* Copyright (c) 2016, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/digest.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/rand.h>
+#include <openssl/stack.h>
+
+#include "internal.h"
+
+
+enum server_hs_state_t {
+  state_process_client_hello = 0,
+  state_send_server_hello,
+  state_send_encrypted_extensions,
+  state_send_certificate_request,
+  state_send_server_certificate,
+  state_send_server_certificate_verify,
+  state_complete_server_certificate_verify,
+  state_send_server_finished,
+  state_flush,
+  state_read_client_second_flight,
+  state_process_client_certificate,
+  state_process_client_certificate_verify,
+  state_process_client_finished,
+  state_done,
+};
+
+static enum ssl_hs_wait_t do_process_client_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+    return ssl_hs_error;
+  }
+
+  struct ssl_early_callback_ctx early_ctx;
+  uint16_t client_wire_version;
+  CBS client_random, session_id, cipher_suites, compression_methods;
+
+  memset(&early_ctx, 0, sizeof(early_ctx));
+  early_ctx.ssl = ssl;
+  early_ctx.client_hello = ssl->init_msg;
+  early_ctx.client_hello_len = ssl->init_num;
+  if (!ssl_early_callback_init(&early_ctx)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u16(&cbs, &client_wire_version) ||
+      !CBS_get_bytes(&cbs, &client_random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&cbs, &session_id) ||
+      CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  uint16_t min_version, max_version;
+  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    return ssl_hs_error;
+  }
+
+  assert(ssl->s3->have_version);
+
+  /* Load the client random. */
+  memcpy(ssl->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE);
+
+  ssl->hit = 0;
+  if (!ssl_get_new_session(ssl, 1 /* server */)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    return ssl_hs_error;
+  }
+
+  if (ssl->ctx->dos_protection_cb != NULL &&
+      ssl->ctx->dos_protection_cb(&early_ctx) == 0) {
+    /* Connection rejected for DOS reasons. */
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ACCESS_DENIED);
+    return ssl_hs_error;
+  }
+
+  if (!CBS_get_u16_length_prefixed(&cbs, &cipher_suites) ||
+      CBS_len(&cipher_suites) == 0 ||
+      CBS_len(&cipher_suites) % 2 != 0 ||
+      !CBS_get_u8_length_prefixed(&cbs, &compression_methods) ||
+      CBS_len(&compression_methods) == 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  /* TLS 1.3 requires the peer only advertise the null compression. */
+  if (CBS_len(&compression_methods) != 1 ||
+      CBS_data(&compression_methods)[0] != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
+  }
+
+  /* TLS extensions. */
+  if (!ssl_parse_clienthello_tlsext(ssl, &cbs)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+    return ssl_hs_error;
+  }
+
+  /* There should be nothing left over in the message. */
+  if (CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  /* Let cert callback update server certificates if required.
+   *
+   * TODO(davidben): Can this get run earlier? */
+  if (ssl->cert->cert_cb != NULL) {
+    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+    if (rv == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_hs_error;
+    }
+    if (rv < 0) {
+      hs->state = state_process_client_hello;
+      return ssl_hs_x509_lookup;
+    }
+  }
+
+  STACK_OF(SSL_CIPHER) *ciphers =
+      ssl_bytes_to_cipher_list(ssl, &cipher_suites, max_version);
+  if (ciphers == NULL) {
+    return ssl_hs_error;
+  }
+
+  const SSL_CIPHER *cipher =
+      ssl3_choose_cipher(ssl, ciphers, ssl_get_cipher_preferences(ssl));
+  sk_SSL_CIPHER_free(ciphers);
+  if (cipher == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    return ssl_hs_error;
+  }
+
+  ssl->session->cipher = cipher;
+  ssl->s3->tmp.new_cipher = cipher;
+
+  /* The PRF hash is now known. Set up the key schedule and hash the
+   * ClientHello. */
+  static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
+  size_t hash_len =
+      EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
+  if (!tls13_init_key_schedule(ssl, kZeroes, hash_len)) {
+    return ssl_hs_error;
+  }
+
+  /* Resolve PSK and incorporate it into the secret. */
+  if (cipher->algorithm_auth == SSL_aPSK) {
+    /* TODO(davidben): Support PSK. */
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return ssl_hs_error;
+  } else if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) {
+    return ssl_hs_error;
+  }
+
+  /* Resolve ECDHE and incorporate it into the secret. */
+  if (cipher->algorithm_mkey == SSL_kECDHE) {
+    const uint8_t *key_share_buf = NULL;
+    size_t key_share_len = 0;
+    CBS key_share;
+    if (!SSL_early_callback_ctx_extension_get(&early_ctx, TLSEXT_TYPE_key_share,
+                                              &key_share_buf, &key_share_len)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
+      return ssl_hs_error;
+    }
+
+    CBS_init(&key_share, key_share_buf, key_share_len);
+    uint8_t *dhe_secret;
+    size_t dhe_secret_len;
+    uint8_t alert;
+    if (!ext_key_share_parse_clienthello(ssl, &dhe_secret, &dhe_secret_len,
+                                         &alert, &key_share)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      return ssl_hs_error;
+    }
+
+    int ok = tls13_advance_key_schedule(ssl, dhe_secret, dhe_secret_len);
+    OPENSSL_free(dhe_secret);
+    if (!ok) {
+      return ssl_hs_error;
+    }
+  } else if (!tls13_advance_key_schedule(ssl, kZeroes, hash_len)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_send_server_hello;
+  return ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_send_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
+  CBB cbb, body, extensions;
+  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
+      !CBB_add_u16(&body, ssl->version) ||
+      !RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) ||
+      !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
+      !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
+      !CBB_add_u16_length_prefixed(&body, &extensions) ||
+      !ext_key_share_add_serverhello(ssl, &extensions) ||
+      !ssl->method->finish_message(ssl, &cbb)) {
+    CBB_cleanup(&cbb);
+    return ssl_hs_error;
+  }
+
+  hs->state = state_send_encrypted_extensions;
+  return ssl_hs_write_message;
+}
+
+static enum ssl_hs_wait_t do_send_encrypted_extensions(SSL *ssl,
+                                                       SSL_HANDSHAKE *hs) {
+  if (!tls13_set_handshake_traffic(ssl)) {
+    return ssl_hs_error;
+  }
+
+  CBB cbb, body;
+  if (!ssl->method->init_message(ssl, &cbb, &body,
+                                 SSL3_MT_ENCRYPTED_EXTENSIONS) ||
+      !ssl_add_serverhello_tlsext(ssl, &body) ||
+      !ssl->method->finish_message(ssl, &cbb)) {
+    CBB_cleanup(&cbb);
+    return ssl_hs_error;
+  }
+
+  hs->state = state_send_certificate_request;
+  return ssl_hs_write_message;
+}
+
+static enum ssl_hs_wait_t do_send_certificate_request(SSL *ssl,
+                                                      SSL_HANDSHAKE *hs) {
+  /* Determine whether to request a client certificate. */
+  ssl->s3->tmp.cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+  /* CertificateRequest may only be sent in certificate-based ciphers. */
+  if (!ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+    ssl->s3->tmp.cert_request = 0;
+  }
+
+  if (!ssl->s3->tmp.cert_request) {
+    /* Skip this state. */
+    hs->state = state_send_server_certificate;
+    return ssl_hs_ok;
+  }
+
+  CBB cbb, body, sigalgs_cbb;
+  if (!ssl->method->init_message(ssl, &cbb, &body,
+                                 SSL3_MT_CERTIFICATE_REQUEST) ||
+      !CBB_add_u8(&body, 0 /* no certificate_request_context. */)) {
+    goto err;
+  }
+
+  const uint16_t *sigalgs;
+  size_t sigalgs_len = tls12_get_psigalgs(ssl, &sigalgs);
+  if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
+    goto err;
+  }
+
+  for (size_t i = 0; i < sigalgs_len; i++) {
+    if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
+      goto err;
+    }
+  }
+
+  if (!ssl_add_client_CA_list(ssl, &body) ||
+      !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
+      !ssl->method->finish_message(ssl, &cbb)) {
+    goto err;
+  }
+
+  hs->state = state_send_server_certificate;
+  return ssl_hs_write_message;
+
+err:
+  CBB_cleanup(&cbb);
+  return ssl_hs_error;
+}
+
+static enum ssl_hs_wait_t do_send_server_certificate(SSL *ssl,
+                                                     SSL_HANDSHAKE *hs) {
+  if (!ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+    hs->state = state_send_server_finished;
+    return ssl_hs_ok;
+  }
+
+  if (!ssl_has_certificate(ssl)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
+    return ssl_hs_error;
+  }
+
+  if (!tls13_prepare_certificate(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_send_server_certificate_verify;
+  return ssl_hs_write_message;
+}
+
+static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL *ssl,
+                                                            SSL_HANDSHAKE *hs,
+                                                            int is_first_run) {
+  switch (tls13_prepare_certificate_verify(ssl, is_first_run)) {
+    case ssl_private_key_success:
+      hs->state = state_send_server_finished;
+      return ssl_hs_write_message;
+
+    case ssl_private_key_retry:
+      hs->state = state_complete_server_certificate_verify;
+      return ssl_hs_private_key_operation;
+
+    case ssl_private_key_failure:
+      return ssl_hs_error;
+  }
+
+  assert(0);
+  return ssl_hs_error;
+}
+
+static enum ssl_hs_wait_t do_send_server_finished(SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (!tls13_prepare_finished(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_flush;
+  return ssl_hs_write_message;
+}
+
+static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
+  hs->state = state_read_client_second_flight;
+  return ssl_hs_flush;
+}
+
+static enum ssl_hs_wait_t do_read_client_second_flight(SSL *ssl,
+                                                       SSL_HANDSHAKE *hs) {
+  /* Update the secret to the master secret and derive traffic keys. */
+  static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
+  if (!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
+      !tls13_derive_traffic_secret_0(ssl) ||
+      !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
+                             hs->traffic_secret_0, hs->hash_len)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_process_client_certificate;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_client_certificate(SSL *ssl,
+                                                        SSL_HANDSHAKE *hs) {
+  if (!ssl->s3->tmp.cert_request) {
+    /* Skip this state. */
+    hs->state = state_process_client_certificate_verify;
+    return ssl_hs_ok;
+  }
+
+  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
+      !tls13_process_certificate(ssl) ||
+      !ssl->method->hash_current_message(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_process_client_certificate_verify;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_client_certificate_verify(
+    SSL *ssl, SSL_HANDSHAKE *hs) {
+  if (ssl->session->peer == NULL) {
+    /* Skip this state. */
+    hs->state = state_process_client_finished;
+    return ssl_hs_ok;
+  }
+
+  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
+      !tls13_process_certificate_verify(ssl) ||
+      !ssl->method->hash_current_message(ssl)) {
+    return 0;
+  }
+
+  hs->state = state_process_client_finished;
+  return ssl_hs_read_message;
+}
+
+static enum ssl_hs_wait_t do_process_client_finished(SSL *ssl,
+                                                     SSL_HANDSHAKE *hs) {
+  if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
+      !tls13_process_finished(ssl) ||
+      !ssl->method->hash_current_message(ssl) ||
+      /* evp_aead_seal keys have already been switched. */
+      !tls13_set_traffic_key(ssl, type_data, evp_aead_open,
+                             hs->traffic_secret_0, hs->hash_len) ||
+      !tls13_finalize_keys(ssl)) {
+    return ssl_hs_error;
+  }
+
+  hs->state = state_done;
+  return ssl_hs_ok;
+}
+
+enum ssl_hs_wait_t tls13_server_handshake(SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+
+  while (hs->state != state_done) {
+    enum ssl_hs_wait_t ret = ssl_hs_error;
+    enum server_hs_state_t state = hs->state;
+    switch (state) {
+      case state_process_client_hello:
+        ret = do_process_client_hello(ssl, hs);
+        break;
+      case state_send_server_hello:
+        ret = do_send_server_hello(ssl, hs);
+        break;
+      case state_send_encrypted_extensions:
+        ret = do_send_encrypted_extensions(ssl, hs);
+        break;
+      case state_send_certificate_request:
+        ret = do_send_certificate_request(ssl, hs);
+        break;
+      case state_send_server_certificate:
+        ret = do_send_server_certificate(ssl, hs);
+        break;
+      case state_send_server_certificate_verify:
+        ret = do_send_server_certificate_verify(ssl, hs, 1 /* first run */);
+      break;
+      case state_complete_server_certificate_verify:
+        ret = do_send_server_certificate_verify(ssl, hs, 0 /* complete */);
+      break;
+      case state_send_server_finished:
+        ret = do_send_server_finished(ssl, hs);
+        break;
+      case state_flush:
+        ret = do_flush(ssl, hs);
+        break;
+      case state_read_client_second_flight:
+        ret = do_read_client_second_flight(ssl, hs);
+        break;
+      case state_process_client_certificate:
+        ret = do_process_client_certificate(ssl, hs);
+        break;
+      case state_process_client_certificate_verify:
+        ret = do_process_client_certificate_verify(ssl, hs);
+        break;
+      case state_process_client_finished:
+        ret = do_process_client_finished(ssl, hs);
+        break;
+      case state_done:
+        ret = ssl_hs_ok;
+        break;
+    }
+
+    if (ret != ssl_hs_ok) {
+      return ret;
+    }
+  }
+
+  return ssl_hs_ok;
+}
diff --git a/ssl/tls_method.c b/ssl/tls_method.c
index ccf4f98..39daa5a 100644
--- a/ssl/tls_method.c
+++ b/ssl/tls_method.c
@@ -56,7 +56,6 @@
 
 #include <openssl/ssl.h>
 
-#include <assert.h>
 #include <string.h>
 
 #include <openssl/buf.h>
@@ -93,9 +92,13 @@
 }
 
 static int ssl3_set_read_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) {
-  /* TODO(davidben): In TLS 1.3, cipher changes are not always preceeded by a
-   * ChangeCipherSpec, so this must become a runtime check. */
-  assert(ssl->s3->rrec.length == 0);
+  if (ssl->s3->rrec.length != 0) {
+    /* There may not be unprocessed record data at a cipher change. */
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    SSL_AEAD_CTX_free(aead_ctx);
+    return 0;
+  }
 
   memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));