external/boringssl: Sync to 3120950b1e27635ee9b9d167052ce11ce9c96fd4.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/5e578c9dba73460c3eb17f771c77fc8e36f7812e..3120950b1e27635ee9b9d167052ce11ce9c96fd4

Test: BoringSSL CTS Presubmits.
Change-Id: I54d7540777ffdf1e72c4ff67f3138097cbdbeafb
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index 3444825..e2c7315 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -335,8 +335,10 @@
   }
 }
 
-int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len) {
+int dtls1_write_app_data(SSL *ssl, int *out_needs_handshake, const uint8_t *buf,
+                         int len) {
   assert(!SSL_in_init(ssl));
+  *out_needs_handshake = 0;
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DTLS_MESSAGE_TOO_BIG);
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index f204286..dddf602 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -169,7 +169,7 @@
 
 
 static int ssl3_send_client_hello(SSL_HANDSHAKE *hs);
-static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs);
+static int dtls1_get_hello_verify_request(SSL_HANDSHAKE *hs);
 static int ssl3_get_server_hello(SSL_HANDSHAKE *hs);
 static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs);
 static int ssl3_get_cert_status(SSL_HANDSHAKE *hs);
@@ -218,8 +218,10 @@
               ret = -1;
               goto end;
             }
+            hs->next_state = SSL3_ST_WRITE_EARLY_DATA;
+          } else {
+            hs->next_state = SSL3_ST_CR_SRVR_HELLO_A;
           }
-          hs->next_state = SSL3_ST_CR_SRVR_HELLO_A;
         } else {
           hs->next_state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
         }
@@ -228,7 +230,7 @@
 
       case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
         assert(SSL_is_dtls(ssl));
-        ret = dtls1_get_hello_verify(hs);
+        ret = dtls1_get_hello_verify_request(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -240,6 +242,18 @@
         }
         break;
 
+      case SSL3_ST_WRITE_EARLY_DATA:
+        /* Stash the early data session, so connection properties may be queried
+         * out of it. */
+        hs->in_early_data = 1;
+        hs->early_session = ssl->session;
+        SSL_SESSION_up_ref(ssl->session);
+
+        hs->state = SSL3_ST_CR_SRVR_HELLO_A;
+        hs->can_early_write = 1;
+        ret = 1;
+        goto end;
+
       case SSL3_ST_CR_SRVR_HELLO_A:
         ret = ssl3_get_server_hello(hs);
         if (hs->state == SSL_ST_TLS13) {
@@ -332,7 +346,6 @@
         break;
 
       case SSL3_ST_CW_CERT_VRFY_A:
-      case SSL3_ST_CW_CERT_VRFY_B:
         if (hs->cert_request && ssl_has_certificate(ssl)) {
           ret = ssl3_send_cert_verify(hs);
           if (ret <= 0) {
@@ -554,9 +567,8 @@
   }
 }
 
-static int ssl_write_client_cipher_list(SSL *ssl, CBB *out,
-                                        uint16_t min_version,
-                                        uint16_t max_version) {
+static int ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
   uint32_t mask_a, mask_k;
   ssl_get_client_disabled(ssl, &mask_a, &mask_k);
 
@@ -573,7 +585,7 @@
 
   /* Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on
    * hardware support. */
-  if (max_version >= TLS1_3_VERSION) {
+  if (hs->max_version >= TLS1_3_VERSION) {
     if (!EVP_has_aes_hardware() &&
         !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
       return 0;
@@ -588,7 +600,7 @@
     }
   }
 
-  if (min_version < TLS1_3_VERSION) {
+  if (hs->min_version < TLS1_3_VERSION) {
     STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl);
     int any_enabled = 0;
     for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
@@ -598,8 +610,8 @@
           (cipher->algorithm_auth & mask_a)) {
         continue;
       }
-      if (SSL_CIPHER_get_min_version(cipher) > max_version ||
-          SSL_CIPHER_get_max_version(cipher) < min_version) {
+      if (SSL_CIPHER_get_min_version(cipher) > hs->max_version ||
+          SSL_CIPHER_get_max_version(cipher) < hs->min_version) {
         continue;
       }
       any_enabled = 1;
@@ -609,7 +621,7 @@
     }
 
     /* If all ciphers were disabled, return the error to the caller. */
-    if (!any_enabled && max_version < TLS1_3_VERSION) {
+    if (!any_enabled && hs->max_version < TLS1_3_VERSION) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
       return 0;
     }
@@ -617,7 +629,7 @@
 
   /* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is
    * added. */
-  if (max_version == SSL3_VERSION &&
+  if (hs->max_version == SSL3_VERSION &&
       !ssl->s3->initial_handshake_complete) {
     if (!CBB_add_u16(&child, SSL3_CK_SCSV & 0xffff)) {
       return 0;
@@ -635,11 +647,6 @@
 
 int ssl_write_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO)) {
     goto err;
@@ -668,7 +675,7 @@
 
   size_t header_len =
       SSL_is_dtls(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH;
-  if (!ssl_write_client_cipher_list(ssl, &body, min_version, max_version) ||
+  if (!ssl_write_client_cipher_list(hs, &body) ||
       !CBB_add_u8(&body, 1 /* one compression method */) ||
       !CBB_add_u8(&body, 0 /* null compression */) ||
       !ssl_add_clienthello_tlsext(hs, &body, header_len + CBB_len(&body))) {
@@ -705,12 +712,12 @@
     return -1;
   }
 
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
+  /* Freeze the version range. */
+  if (!ssl_get_version_range(ssl, &hs->min_version, &hs->max_version)) {
     return -1;
   }
 
-  uint16_t max_wire_version = ssl->method->version_to_wire(max_version);
+  uint16_t max_wire_version = ssl->method->version_to_wire(hs->max_version);
   assert(hs->state == SSL3_ST_CW_CLNT_HELLO_A);
   if (!ssl->s3->have_version) {
     ssl->version = max_wire_version;
@@ -720,7 +727,7 @@
    * even on renegotiation. The static RSA key exchange uses this field, and
    * some servers fail when it changes across handshakes. */
   hs->client_version = max_wire_version;
-  if (max_version >= TLS1_3_VERSION) {
+  if (hs->max_version >= TLS1_3_VERSION) {
     hs->client_version = ssl->method->version_to_wire(TLS1_2_VERSION);
   }
 
@@ -735,7 +742,8 @@
          ssl->session->session_id_length == 0) ||
         ssl->session->not_resumable ||
         !ssl_session_is_time_valid(ssl, ssl->session) ||
-        session_version < min_version || session_version > max_version) {
+        session_version < hs->min_version ||
+        session_version > hs->max_version) {
       ssl_set_session(ssl, NULL);
     }
   }
@@ -754,9 +762,8 @@
   return 1;
 }
 
-static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs) {
+static int dtls1_get_hello_verify_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int al;
   CBS hello_verify_request, cookie;
   uint16_t server_version;
 
@@ -774,15 +781,11 @@
   CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u16(&hello_verify_request, &server_version) ||
       !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
+      CBS_len(&cookie) > sizeof(ssl->d1->cookie) ||
       CBS_len(&hello_verify_request) != 0) {
-    al = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
-  }
-
-  if (CBS_len(&cookie) > sizeof(ssl->d1->cookie)) {
-    al = SSL_AD_ILLEGAL_PARAMETER;
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return -1;
   }
 
   OPENSSL_memcpy(ssl->d1->cookie, CBS_data(&cookie), CBS_len(&cookie));
@@ -790,10 +793,6 @@
 
   ssl->d1->send_cookie = 1;
   return 1;
-
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  return -1;
 }
 
 static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
@@ -833,10 +832,9 @@
     return -1;
   }
 
-  uint16_t min_version, max_version, server_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version) ||
-      !ssl->method->version_from_wire(&server_version, server_wire_version) ||
-      server_version < min_version || server_version > max_version) {
+  uint16_t server_version;
+  if (!ssl->method->version_from_wire(&server_version, server_wire_version) ||
+      server_version < hs->min_version || server_version > hs->max_version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return -1;
@@ -1076,10 +1074,6 @@
 
 static int ssl3_get_cert_status(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int al;
-  CBS certificate_status, ocsp_response;
-  uint8_t status_type;
-
   int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
@@ -1096,28 +1090,27 @@
     return -1;
   }
 
+  CBS certificate_status, ocsp_response;
+  uint8_t status_type;
   CBS_init(&certificate_status, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u8(&certificate_status, &status_type) ||
       status_type != TLSEXT_STATUSTYPE_ocsp ||
       !CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) ||
       CBS_len(&ocsp_response) == 0 ||
       CBS_len(&certificate_status) != 0) {
-    al = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return -1;
   }
 
   if (!CBS_stow(&ocsp_response, &hs->new_session->ocsp_response,
                 &hs->new_session->ocsp_response_length)) {
-    al = SSL_AD_INTERNAL_ERROR;
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    return -1;
   }
-  return 1;
 
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  return -1;
+  return 1;
 }
 
 static int ssl3_verify_server_cert(SSL_HANDSHAKE *hs) {
@@ -1131,7 +1124,6 @@
 
 static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int al;
   EC_KEY *ecdh = NULL;
   EC_POINT *srvr_ecpoint = NULL;
 
@@ -1170,9 +1162,9 @@
     /* Each of the PSK key exchanges begins with a psk_identity_hint. */
     if (!CBS_get_u16_length_prefixed(&server_key_exchange,
                                      &psk_identity_hint)) {
-      al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      goto err;
     }
 
     /* Store PSK identity hint for later use, hint is used in
@@ -1184,9 +1176,9 @@
      * a specific identity. */
     if (CBS_len(&psk_identity_hint) > PSK_MAX_IDENTITY_LEN ||
         CBS_contains_zero_byte(&psk_identity_hint)) {
-      al = SSL_AD_HANDSHAKE_FAILURE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      goto err;
     }
 
     /* Save non-empty identity hints as a C string. Empty identity hints we
@@ -1196,9 +1188,9 @@
      * and missing as identical. */
     if (CBS_len(&psk_identity_hint) != 0 &&
         !CBS_strdup(&psk_identity_hint, &hs->peer_psk_identity_hint)) {
-      al = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      goto err;
     }
   }
 
@@ -1211,17 +1203,17 @@
         group_type != NAMED_CURVE_TYPE ||
         !CBS_get_u16(&server_key_exchange, &group_id) ||
         !CBS_get_u8_length_prefixed(&server_key_exchange, &point)) {
-      al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      goto err;
     }
     hs->new_session->group_id = group_id;
 
     /* Ensure the group is consistent with preferences. */
     if (!tls1_check_group_id(ssl, group_id)) {
-      al = SSL_AD_ILLEGAL_PARAMETER;
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      goto err;
     }
 
     /* Initialize ECDH and save the peer public key for later. */
@@ -1230,9 +1222,9 @@
       goto err;
     }
   } else if (!(alg_k & SSL_kPSK)) {
-    al = SSL_AD_UNEXPECTED_MESSAGE;
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    goto err;
   }
 
   /* At this point, |server_key_exchange| contains the signature, if any, while
@@ -1247,28 +1239,30 @@
     uint16_t signature_algorithm = 0;
     if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
       if (!CBS_get_u16(&server_key_exchange, &signature_algorithm)) {
-        al = SSL_AD_DECODE_ERROR;
         OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-        goto f_err;
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        goto err;
       }
-      if (!tls12_check_peer_sigalg(ssl, &al, signature_algorithm)) {
-        goto f_err;
+      uint8_t alert = SSL_AD_DECODE_ERROR;
+      if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+        goto err;
       }
       hs->new_session->peer_signature_algorithm = signature_algorithm;
     } else if (!tls1_get_legacy_signature_algorithm(&signature_algorithm,
                                                     hs->peer_pubkey)) {
-      al = SSL_AD_UNSUPPORTED_CERTIFICATE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_CERTIFICATE);
+      goto err;
     }
 
     /* The last field in |server_key_exchange| is the signature. */
     CBS signature;
     if (!CBS_get_u16_length_prefixed(&server_key_exchange, &signature) ||
         CBS_len(&server_key_exchange) != 0) {
-      al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      goto err;
     }
 
     CBB transcript;
@@ -1280,9 +1274,9 @@
         !CBB_add_bytes(&transcript, CBS_data(&parameter), CBS_len(&parameter)) ||
         !CBB_finish(&transcript, &transcript_data, &transcript_len)) {
       CBB_cleanup(&transcript);
-      al = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      goto err;
     }
 
     int sig_ok = ssl_public_key_verify(
@@ -1296,24 +1290,22 @@
 #endif
     if (!sig_ok) {
       /* bad signature */
-      al = SSL_AD_DECRYPT_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      goto err;
     }
   } else {
     /* PSK ciphers are the only supported certificate-less ciphers. */
     assert(alg_a == SSL_aPSK);
 
     if (CBS_len(&server_key_exchange) > 0) {
-      al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_EXTRA_DATA_IN_MESSAGE);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      goto err;
     }
   }
   return 1;
 
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   EC_POINT_free(srvr_ecpoint);
   EC_KEY_free(ecdh);
@@ -1661,58 +1653,43 @@
   }
 
   size_t sig_len = max_sig_len;
-  enum ssl_private_key_result_t sign_result;
-  if (hs->state == SSL3_ST_CW_CERT_VRFY_A) {
-    /* The SSL3 construction for CertificateVerify does not decompose into a
-     * single final digest and signature, and must be special-cased. */
-    if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
-      if (ssl->cert->key_method != NULL) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
-        goto err;
-      }
-
-      uint8_t digest[EVP_MAX_MD_SIZE];
-      size_t digest_len;
-      if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(&hs->transcript, digest,
-                                                &digest_len, hs->new_session,
-                                                signature_algorithm)) {
-        goto err;
-      }
-
-      sign_result = ssl_private_key_success;
-
-      EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL);
-      if (pctx == NULL ||
-          !EVP_PKEY_sign_init(pctx) ||
-          !EVP_PKEY_sign(pctx, ptr, &sig_len, digest, digest_len)) {
-        EVP_PKEY_CTX_free(pctx);
-        sign_result = ssl_private_key_failure;
-        goto err;
-      }
-      EVP_PKEY_CTX_free(pctx);
-    } else {
-      sign_result = ssl_private_key_sign(
-          ssl, ptr, &sig_len, max_sig_len, signature_algorithm,
-          (const uint8_t *)hs->transcript.buffer->data,
-          hs->transcript.buffer->length);
+  /* The SSL3 construction for CertificateVerify does not decompose into a
+   * single final digest and signature, and must be special-cased. */
+  if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
+    if (ssl->cert->key_method != NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
+      goto err;
     }
 
-    /* The handshake buffer is no longer necessary. */
-    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
-  } else {
-    assert(hs->state == SSL3_ST_CW_CERT_VRFY_B);
-    sign_result = ssl_private_key_complete(ssl, ptr, &sig_len, max_sig_len);
-  }
+    uint8_t digest[EVP_MAX_MD_SIZE];
+    size_t digest_len;
+    if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(&hs->transcript, digest,
+                                              &digest_len, hs->new_session,
+                                              signature_algorithm)) {
+      goto err;
+    }
 
-  switch (sign_result) {
-    case ssl_private_key_success:
-      break;
-    case ssl_private_key_failure:
+    EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL);
+    int ok = pctx != NULL &&
+             EVP_PKEY_sign_init(pctx) &&
+             EVP_PKEY_sign(pctx, ptr, &sig_len, digest, digest_len);
+    EVP_PKEY_CTX_free(pctx);
+    if (!ok) {
       goto err;
-    case ssl_private_key_retry:
-      ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
-      hs->state = SSL3_ST_CW_CERT_VRFY_B;
-      goto err;
+    }
+  } else {
+    switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
+                                 signature_algorithm,
+                                 (const uint8_t *)hs->transcript.buffer->data,
+                                 hs->transcript.buffer->length)) {
+      case ssl_private_key_success:
+        break;
+      case ssl_private_key_failure:
+        goto err;
+      case ssl_private_key_retry:
+        ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        goto err;
+    }
   }
 
   if (!CBB_did_write(&child, sig_len) ||
@@ -1720,6 +1697,8 @@
     goto err;
   }
 
+  /* The handshake buffer is no longer necessary. */
+  SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   return 1;
 
 err:
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index 4eaf3cb..d591c80 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -175,31 +175,17 @@
 static int ssl3_select_parameters(SSL_HANDSHAKE *hs);
 static int ssl3_send_server_hello(SSL_HANDSHAKE *hs);
 static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs);
-static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs);
 static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs);
-static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs);
 static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs);
 static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs);
 static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs);
 static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs);
 static int ssl3_get_next_proto(SSL_HANDSHAKE *hs);
 static int ssl3_get_channel_id(SSL_HANDSHAKE *hs);
-static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs);
-
-static struct CRYPTO_STATIC_MUTEX g_v2clienthello_lock =
-    CRYPTO_STATIC_MUTEX_INIT;
-static uint64_t g_v2clienthello_count = 0;
-
-uint64_t SSL_get_v2clienthello_count(void) {
-  CRYPTO_STATIC_MUTEX_lock_read(&g_v2clienthello_lock);
-  uint64_t ret = g_v2clienthello_count;
-  CRYPTO_STATIC_MUTEX_unlock_read(&g_v2clienthello_lock);
-  return ret;
-}
+static int ssl3_send_server_finished(SSL_HANDSHAKE *hs);
 
 int ssl3_accept(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint32_t alg_a;
   int ret = -1;
 
   assert(ssl->handshake_func == ssl3_accept);
@@ -255,55 +241,28 @@
           goto end;
         }
         if (ssl->session != NULL) {
-          hs->state = SSL3_ST_SW_SESSION_TICKET_A;
+          hs->state = SSL3_ST_SW_FINISHED_A;
         } else {
           hs->state = SSL3_ST_SW_CERT_A;
         }
         break;
 
       case SSL3_ST_SW_CERT_A:
-        if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-          ret = ssl3_send_server_certificate(hs);
-          if (ret <= 0) {
-            goto end;
-          }
-        }
-        hs->state = SSL3_ST_SW_CERT_STATUS_A;
-        break;
-
-      case SSL3_ST_SW_CERT_STATUS_A:
-        if (hs->certificate_status_expected) {
-          ret = ssl3_send_certificate_status(hs);
-          if (ret <= 0) {
-            goto end;
-          }
+        ret = ssl3_send_server_certificate(hs);
+        if (ret <= 0) {
+          goto end;
         }
         hs->state = SSL3_ST_SW_KEY_EXCH_A;
         break;
 
       case SSL3_ST_SW_KEY_EXCH_A:
-      case SSL3_ST_SW_KEY_EXCH_B:
-        alg_a = hs->new_cipher->algorithm_auth;
-
-        /* PSK ciphers send ServerKeyExchange if there is an identity hint. */
-        if (ssl_cipher_requires_server_key_exchange(hs->new_cipher) ||
-            ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
+        if (hs->server_params_len > 0) {
           ret = ssl3_send_server_key_exchange(hs);
           if (ret <= 0) {
             goto end;
           }
         }
 
-        hs->state = SSL3_ST_SW_CERT_REQ_A;
-        break;
-
-      case SSL3_ST_SW_CERT_REQ_A:
-        if (hs->cert_request) {
-          ret = ssl3_send_certificate_request(hs);
-          if (ret <= 0) {
-            goto end;
-          }
-        }
         hs->state = SSL3_ST_SW_SRVR_DONE_A;
         break;
 
@@ -388,7 +347,7 @@
         if (ssl->session != NULL) {
           hs->state = SSL_ST_OK;
         } else {
-          hs->state = SSL3_ST_SW_SESSION_TICKET_A;
+          hs->state = SSL3_ST_SW_FINISHED_A;
         }
 
         /* If this is a full handshake with ChannelID then record the handshake
@@ -402,28 +361,8 @@
         }
         break;
 
-      case SSL3_ST_SW_SESSION_TICKET_A:
-        if (hs->ticket_expected) {
-          ret = ssl3_send_new_session_ticket(hs);
-          if (ret <= 0) {
-            goto end;
-          }
-        }
-        hs->state = SSL3_ST_SW_CHANGE;
-        break;
-
-      case SSL3_ST_SW_CHANGE:
-        if (!ssl->method->add_change_cipher_spec(ssl) ||
-            !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
-          ret = -1;
-          goto end;
-        }
-
-        hs->state = SSL3_ST_SW_FINISHED_A;
-        break;
-
       case SSL3_ST_SW_FINISHED_A:
-        ret = ssl3_send_finished(hs);
+        ret = ssl3_send_server_finished(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -485,12 +424,6 @@
           hs->new_session = NULL;
         }
 
-        if (hs->v2_clienthello) {
-          CRYPTO_STATIC_MUTEX_lock_write(&g_v2clienthello_lock);
-          g_v2clienthello_count++;
-          CRYPTO_STATIC_MUTEX_unlock_write(&g_v2clienthello_lock);
-        }
-
         ssl->s3->initial_handshake_complete = 1;
         ssl_update_cache(hs, SSL_SESS_CACHE_SERVER);
 
@@ -538,12 +471,6 @@
                              const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
   assert(!ssl->s3->have_version);
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    *out_alert = SSL_AD_PROTOCOL_VERSION;
-    return 0;
-  }
-
   uint16_t version = 0;
   /* Check supported_versions extension if it is present. */
   CBS supported_versions;
@@ -572,8 +499,8 @@
       if (!ssl->method->version_from_wire(&ext_version, ext_version)) {
         continue;
       }
-      if (min_version <= ext_version &&
-          ext_version <= max_version &&
+      if (hs->min_version <= ext_version &&
+          ext_version <= hs->max_version &&
           (!found_version || version < ext_version)) {
         version = ext_version;
         found_version = 1;
@@ -609,11 +536,11 @@
     }
 
     /* Apply our minimum and maximum version. */
-    if (version > max_version) {
-      version = max_version;
+    if (version > hs->max_version) {
+      version = hs->max_version;
     }
 
-    if (version < min_version) {
+    if (version < hs->min_version) {
       goto unsupported_protocol;
     }
   }
@@ -621,7 +548,7 @@
   /* Handle FALLBACK_SCSV. */
   if (ssl_client_cipher_list_contains_cipher(client_hello,
                                              SSL3_CK_FALLBACK_SCSV & 0xffff) &&
-      version < max_version) {
+      version < hs->max_version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
     *out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
     return 0;
@@ -821,6 +748,11 @@
     }
   }
 
+  /* Freeze the version range after the early callback. */
+  if (!ssl_get_version_range(ssl, &hs->min_version, &hs->max_version)) {
+    return -1;
+  }
+
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!negotiate_version(hs, &alert, &client_hello)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -903,7 +835,6 @@
 
 static int ssl3_select_parameters(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint8_t al = SSL_AD_INTERNAL_ERROR;
   int ret = -1;
   SSL_SESSION *session = NULL;
 
@@ -933,9 +864,9 @@
     if (session->extended_master_secret && !hs->extended_master_secret) {
       /* A ClientHello without EMS that attempts to resume a session with EMS
        * is fatal to the connection. */
-      al = SSL_AD_HANDSHAKE_FAILURE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      goto err;
     }
 
     if (!ssl_session_is_resumable(hs, session) ||
@@ -969,9 +900,9 @@
   if (ssl->ctx->dos_protection_cb != NULL &&
       ssl->ctx->dos_protection_cb(&client_hello) == 0) {
     /* Connection rejected for DOS reasons. */
-    al = SSL_AD_INTERNAL_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    goto err;
   }
 
   if (ssl->session == NULL) {
@@ -982,8 +913,8 @@
       OPENSSL_free(hs->new_session->tlsext_hostname);
       hs->new_session->tlsext_hostname = BUF_strdup(hs->hostname);
       if (hs->new_session->tlsext_hostname == NULL) {
-        al = SSL_AD_INTERNAL_ERROR;
-        goto f_err;
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        goto err;
       }
     }
 
@@ -1008,8 +939,10 @@
 
   /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
    * deferred. Complete it now. */
-  if (!ssl_negotiate_alpn(hs, &al, &client_hello)) {
-    goto f_err;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+    goto err;
   }
 
   /* Now that all parameters are known, initialize the handshake hash and hash
@@ -1017,7 +950,8 @@
   if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(ssl),
                                 hs->new_cipher->algorithm_prf) ||
       !ssl_hash_current_message(hs)) {
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    goto err;
   }
 
   /* Release the handshake buffer if client authentication isn't required. */
@@ -1027,11 +961,6 @@
 
   ret = 1;
 
-  if (0) {
-  f_err:
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  }
-
 err:
   SSL_SESSION_free(session);
   return ret;
@@ -1094,48 +1023,48 @@
 
 static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_has_certificate(ssl)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
-    return -1;
-  }
-
-  if (!ssl3_output_cert_chain(ssl)) {
-    return -1;
-  }
-  return 1;
-}
-
-static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  CBB cbb, body, ocsp_response;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
-                                 SSL3_MT_CERTIFICATE_STATUS) ||
-      !CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) ||
-      !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
-      !CBB_add_bytes(&ocsp_response,
-                     CRYPTO_BUFFER_data(ssl->cert->ocsp_response),
-                     CRYPTO_BUFFER_len(ssl->cert->ocsp_response)) ||
-      !ssl_add_message_cbb(ssl, &cbb)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    CBB_cleanup(&cbb);
-    return -1;
-  }
-
-  return 1;
-}
-
-static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  CBB cbb, child;
+  int ret = -1;
+  CBB cbb;
   CBB_zero(&cbb);
 
-  /* Put together the parameters. */
-  if (hs->state == SSL3_ST_SW_KEY_EXCH_A) {
-    uint32_t alg_k = hs->new_cipher->algorithm_mkey;
-    uint32_t alg_a = hs->new_cipher->algorithm_auth;
+  if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
+    if (!ssl_has_certificate(ssl)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
+      goto err;
+    }
 
-    /* Pre-allocate enough room to comfortably fit an ECDHE public key. */
-    if (!CBB_init(&cbb, 128)) {
+    if (!ssl3_output_cert_chain(ssl)) {
+      goto err;
+    }
+
+    if (hs->certificate_status_expected) {
+      CBB body, ocsp_response;
+      if (!ssl->method->init_message(ssl, &cbb, &body,
+                                     SSL3_MT_CERTIFICATE_STATUS) ||
+          !CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) ||
+          !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
+          !CBB_add_bytes(&ocsp_response,
+                         CRYPTO_BUFFER_data(ssl->cert->ocsp_response),
+                         CRYPTO_BUFFER_len(ssl->cert->ocsp_response)) ||
+          !ssl_add_message_cbb(ssl, &cbb)) {
+        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+    }
+  }
+
+  /* Assemble ServerKeyExchange parameters if needed. */
+  uint32_t alg_k = hs->new_cipher->algorithm_mkey;
+  uint32_t alg_a = hs->new_cipher->algorithm_auth;
+  if (ssl_cipher_requires_server_key_exchange(hs->new_cipher) ||
+      ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
+
+    /* Pre-allocate enough room to comfortably fit an ECDHE public key. Prepend
+     * the client and server randoms for the signing transcript. */
+    CBB child;
+    if (!CBB_init(&cbb, SSL3_RANDOM_SIZE * 2 + 128) ||
+        !CBB_add_bytes(&cbb, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
+        !CBB_add_bytes(&cbb, ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
       goto err;
     }
 
@@ -1177,11 +1106,22 @@
     }
   }
 
-  /* Assemble the message. */
-  CBB body;
+  ret = 1;
+
+err:
+  CBB_cleanup(&cbb);
+  return ret;
+}
+
+static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  CBB cbb, body, child;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_SERVER_KEY_EXCHANGE) ||
-      !CBB_add_bytes(&body, hs->server_params, hs->server_params_len)) {
+      /* |hs->server_params| contains a prefix for signing. */
+      hs->server_params_len < 2 * SSL3_RANDOM_SIZE ||
+      !CBB_add_bytes(&body, hs->server_params + 2 * SSL3_RANDOM_SIZE,
+                     hs->server_params_len - 2 * SSL3_RANDOM_SIZE)) {
     goto err;
   }
 
@@ -1214,36 +1154,9 @@
     }
 
     size_t sig_len;
-    enum ssl_private_key_result_t sign_result;
-    if (hs->state == SSL3_ST_SW_KEY_EXCH_A) {
-      CBB transcript;
-      uint8_t *transcript_data;
-      size_t transcript_len;
-      if (!CBB_init(&transcript,
-                    2 * SSL3_RANDOM_SIZE + hs->server_params_len) ||
-          !CBB_add_bytes(&transcript, ssl->s3->client_random,
-                         SSL3_RANDOM_SIZE) ||
-          !CBB_add_bytes(&transcript, ssl->s3->server_random,
-                         SSL3_RANDOM_SIZE) ||
-          !CBB_add_bytes(&transcript, hs->server_params,
-                         hs->server_params_len) ||
-          !CBB_finish(&transcript, &transcript_data, &transcript_len)) {
-        CBB_cleanup(&transcript);
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-        goto err;
-      }
-
-      sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len,
-                                         signature_algorithm, transcript_data,
-                                         transcript_len);
-      OPENSSL_free(transcript_data);
-    } else {
-      assert(hs->state == SSL3_ST_SW_KEY_EXCH_B);
-      sign_result = ssl_private_key_complete(ssl, ptr, &sig_len, max_sig_len);
-    }
-
-    switch (sign_result) {
+    switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
+                                 signature_algorithm, hs->server_params,
+                                 hs->server_params_len)) {
       case ssl_private_key_success:
         if (!CBB_did_write(&child, sig_len)) {
           goto err;
@@ -1253,7 +1166,6 @@
         goto err;
       case ssl_private_key_retry:
         ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
-        hs->state = SSL3_ST_SW_KEY_EXCH_B;
         goto err;
     }
   }
@@ -1273,26 +1185,28 @@
   return -1;
 }
 
-static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs) {
+static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CBB cbb, body, cert_types, sigalgs_cbb;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
-                                 SSL3_MT_CERTIFICATE_REQUEST) ||
-      !CBB_add_u8_length_prefixed(&body, &cert_types) ||
-      !CBB_add_u8(&cert_types, SSL3_CT_RSA_SIGN) ||
-      (ssl->version >= TLS1_VERSION &&
-       !CBB_add_u8(&cert_types, TLS_CT_ECDSA_SIGN))) {
-    goto err;
-  }
+  CBB cbb, body;
 
-  if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
-    if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
-        !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb)) {
+  if (hs->cert_request) {
+    CBB cert_types, sigalgs_cbb;
+    if (!ssl->method->init_message(ssl, &cbb, &body,
+                                   SSL3_MT_CERTIFICATE_REQUEST) ||
+        !CBB_add_u8_length_prefixed(&body, &cert_types) ||
+        !CBB_add_u8(&cert_types, SSL3_CT_RSA_SIGN) ||
+        (ssl3_protocol_version(ssl) >= TLS1_VERSION &&
+         !CBB_add_u8(&cert_types, TLS_CT_ECDSA_SIGN)) ||
+        (ssl3_protocol_version(ssl) >= TLS1_2_VERSION &&
+         (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
+          !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb))) ||
+        !ssl_add_client_CA_list(ssl, &body) ||
+        !ssl_add_message_cbb(ssl, &cbb)) {
       goto err;
     }
   }
 
-  if (!ssl_add_client_CA_list(ssl, &body) ||
+  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) ||
       !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
@@ -1305,19 +1219,6 @@
   return -1;
 }
 
-static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  CBB cbb, body;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) ||
-      !ssl_add_message_cbb(ssl, &cbb)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    CBB_cleanup(&cbb);
-    return -1;
-  }
-
-  return 1;
-}
-
 static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   assert(hs->cert_request);
@@ -1417,34 +1318,27 @@
 
 static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int al;
   CBS client_key_exchange;
-  uint32_t alg_k;
-  uint32_t alg_a;
   uint8_t *premaster_secret = NULL;
   size_t premaster_secret_len = 0;
   uint8_t *decrypt_buf = NULL;
 
-  unsigned psk_len = 0;
-  uint8_t psk[PSK_MAX_PSK_LEN];
-
   if (hs->state == SSL3_ST_SR_KEY_EXCH_A) {
     int ret = ssl->method->ssl_get_message(ssl);
     if (ret <= 0) {
       return ret;
     }
+  }
 
-    if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE) ||
-        !ssl_hash_current_message(hs)) {
-      return -1;
-    }
+  if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE)) {
+    return -1;
   }
 
   CBS_init(&client_key_exchange, ssl->init_msg, ssl->init_num);
-  alg_k = hs->new_cipher->algorithm_mkey;
-  alg_a = hs->new_cipher->algorithm_auth;
+  uint32_t alg_k = hs->new_cipher->algorithm_mkey;
+  uint32_t alg_a = hs->new_cipher->algorithm_auth;
 
-  /* If using a PSK key exchange, prepare the pre-shared key. */
+  /* If using a PSK key exchange, parse the PSK identity. */
   if (alg_a & SSL_aPSK) {
     CBS psk_identity;
 
@@ -1453,47 +1347,40 @@
     if (!CBS_get_u16_length_prefixed(&client_key_exchange, &psk_identity) ||
         ((alg_k & SSL_kPSK) && CBS_len(&client_key_exchange) != 0)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      al = SSL_AD_DECODE_ERROR;
-      goto f_err;
-    }
-
-    if (ssl->psk_server_callback == NULL) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_SERVER_CB);
-      al = SSL_AD_INTERNAL_ERROR;
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      goto err;
     }
 
     if (CBS_len(&psk_identity) > PSK_MAX_IDENTITY_LEN ||
         CBS_contains_zero_byte(&psk_identity)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      goto err;
     }
 
     if (!CBS_strdup(&psk_identity, &hs->new_session->psk_identity)) {
-      al = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto f_err;
-    }
-
-    /* Look up the key for the identity. */
-    psk_len = ssl->psk_server_callback(ssl, hs->new_session->psk_identity, psk,
-                                       sizeof(psk));
-    if (psk_len > PSK_MAX_PSK_LEN) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      al = SSL_AD_INTERNAL_ERROR;
-      goto f_err;
-    } else if (psk_len == 0) {
-      /* PSK related to the given identity not found */
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
-      al = SSL_AD_UNKNOWN_PSK_IDENTITY;
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      goto err;
     }
   }
 
   /* Depending on the key exchange method, compute |premaster_secret| and
    * |premaster_secret_len|. */
   if (alg_k & SSL_kRSA) {
+    CBS encrypted_premaster_secret;
+    if (ssl->version > SSL3_VERSION) {
+      if (!CBS_get_u16_length_prefixed(&client_key_exchange,
+                                       &encrypted_premaster_secret) ||
+          CBS_len(&client_key_exchange) != 0) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        goto err;
+      }
+    } else {
+      encrypted_premaster_secret = client_key_exchange;
+    }
+
     /* Allocate a buffer large enough for an RSA decryption. */
     const size_t rsa_size = EVP_PKEY_size(hs->local_pubkey);
     decrypt_buf = OPENSSL_malloc(rsa_size);
@@ -1502,43 +1389,12 @@
       goto err;
     }
 
-    enum ssl_private_key_result_t decrypt_result;
+    /* Decrypt with no padding. PKCS#1 padding will be removed as part of the
+     * timing-sensitive code below. */
     size_t decrypt_len;
-    if (hs->state == SSL3_ST_SR_KEY_EXCH_A) {
-      if (!ssl_has_private_key(ssl) ||
-          EVP_PKEY_id(hs->local_pubkey) != EVP_PKEY_RSA) {
-        al = SSL_AD_HANDSHAKE_FAILURE;
-        OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_RSA_CERTIFICATE);
-        goto f_err;
-      }
-      CBS encrypted_premaster_secret;
-      if (ssl->version > SSL3_VERSION) {
-        if (!CBS_get_u16_length_prefixed(&client_key_exchange,
-                                         &encrypted_premaster_secret) ||
-            CBS_len(&client_key_exchange) != 0) {
-          al = SSL_AD_DECODE_ERROR;
-          OPENSSL_PUT_ERROR(SSL,
-                            SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG);
-          goto f_err;
-        }
-      } else {
-        encrypted_premaster_secret = client_key_exchange;
-      }
-
-      /* Decrypt with no padding. PKCS#1 padding will be removed as part of the
-       * timing-sensitive code below. */
-      decrypt_result = ssl_private_key_decrypt(
-          ssl, decrypt_buf, &decrypt_len, rsa_size,
-          CBS_data(&encrypted_premaster_secret),
-          CBS_len(&encrypted_premaster_secret));
-    } else {
-      assert(hs->state == SSL3_ST_SR_KEY_EXCH_B);
-      /* Complete async decrypt. */
-      decrypt_result =
-          ssl_private_key_complete(ssl, decrypt_buf, &decrypt_len, rsa_size);
-    }
-
-    switch (decrypt_result) {
+    switch (ssl_private_key_decrypt(hs, decrypt_buf, &decrypt_len, rsa_size,
+                                    CBS_data(&encrypted_premaster_secret),
+                                    CBS_len(&encrypted_premaster_secret))) {
       case ssl_private_key_success:
         break;
       case ssl_private_key_failure:
@@ -1550,9 +1406,9 @@
     }
 
     if (decrypt_len != rsa_size) {
-      al = SSL_AD_DECRYPT_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      goto err;
     }
 
     /* Prepare a random premaster, to be used on invalid padding. See RFC 5246,
@@ -1570,9 +1426,9 @@
     /* The smallest padded premaster is 11 bytes of overhead. Small keys are
      * publicly invalid. */
     if (decrypt_len < 11 + premaster_secret_len) {
-      al = SSL_AD_DECRYPT_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      goto err;
     }
 
     /* Check the padding. See RFC 3447, section 7.2.2. */
@@ -1605,9 +1461,9 @@
     CBS peer_key;
     if (!CBS_get_u8_length_prefixed(&client_key_exchange, &peer_key) ||
         CBS_len(&client_key_exchange) != 0) {
-      al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      goto err;
     }
 
     /* Compute the premaster. */
@@ -1615,35 +1471,57 @@
     if (!SSL_ECDH_CTX_finish(&hs->ecdh_ctx, &premaster_secret,
                              &premaster_secret_len, &alert, CBS_data(&peer_key),
                              CBS_len(&peer_key))) {
-      al = alert;
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      goto err;
     }
 
     /* The key exchange state may now be discarded. */
     SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
-  } else if (alg_k & SSL_kPSK) {
-    /* For plain PSK, other_secret is a block of 0s with the same length as the
-     * pre-shared key. */
-    premaster_secret_len = psk_len;
-    premaster_secret = OPENSSL_malloc(premaster_secret_len);
-    if (premaster_secret == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-    OPENSSL_memset(premaster_secret, 0, premaster_secret_len);
-  } else {
-    al = SSL_AD_HANDSHAKE_FAILURE;
+  } else if (!(alg_k & SSL_kPSK)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_TYPE);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    goto err;
   }
 
   /* For a PSK cipher suite, the actual pre-master secret is combined with the
    * pre-shared key. */
   if (alg_a & SSL_aPSK) {
+    if (ssl->psk_server_callback == NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_SERVER_CB);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      goto err;
+    }
+
+    /* Look up the key for the identity. */
+    uint8_t psk[PSK_MAX_PSK_LEN];
+    unsigned psk_len = ssl->psk_server_callback(
+        ssl, hs->new_session->psk_identity, psk, sizeof(psk));
+    if (psk_len > PSK_MAX_PSK_LEN) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      goto err;
+    } else if (psk_len == 0) {
+      /* PSK related to the given identity not found */
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNKNOWN_PSK_IDENTITY);
+      goto err;
+    }
+
+    if (alg_k & SSL_kPSK) {
+      /* In plain PSK, other_secret is a block of 0s with the same length as the
+       * pre-shared key. */
+      premaster_secret_len = psk_len;
+      premaster_secret = OPENSSL_malloc(premaster_secret_len);
+      if (premaster_secret == NULL) {
+        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+        goto err;
+      }
+      OPENSSL_memset(premaster_secret, 0, premaster_secret_len);
+    }
+
     CBB new_premaster, child;
     uint8_t *new_data;
     size_t new_len;
-
     CBB_zero(&new_premaster);
     if (!CBB_init(&new_premaster, 2 + psk_len + 2 + premaster_secret_len) ||
         !CBB_add_u16_length_prefixed(&new_premaster, &child) ||
@@ -1662,6 +1540,10 @@
     premaster_secret_len = new_len;
   }
 
+  if (!ssl_hash_current_message(hs)) {
+    goto err;
+  }
+
   /* Compute the master secret */
   hs->new_session->master_key_length = tls1_generate_master_secret(
       hs, hs->new_session->master_key, premaster_secret, premaster_secret_len);
@@ -1674,8 +1556,6 @@
   OPENSSL_free(premaster_secret);
   return 1;
 
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   if (premaster_secret != NULL) {
     OPENSSL_cleanse(premaster_secret, premaster_secret_len);
@@ -1688,7 +1568,6 @@
 
 static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int al;
   CBS certificate_verify, signature;
 
   /* Only RSA and ECDSA client certificates are supported, so a
@@ -1714,27 +1593,29 @@
   uint16_t signature_algorithm = 0;
   if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     if (!CBS_get_u16(&certificate_verify, &signature_algorithm)) {
-      al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto f_err;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      return -1;
     }
-    if (!tls12_check_peer_sigalg(ssl, &al, signature_algorithm)) {
-      goto f_err;
+    uint8_t alert = SSL_AD_DECODE_ERROR;
+    if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      return -1;
     }
     hs->new_session->peer_signature_algorithm = signature_algorithm;
   } else if (!tls1_get_legacy_signature_algorithm(&signature_algorithm,
                                                   hs->peer_pubkey)) {
-    al = SSL_AD_UNSUPPORTED_CERTIFICATE;
     OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_CERTIFICATE);
+    return -1;
   }
 
   /* Parse and verify the signature. */
   if (!CBS_get_u16_length_prefixed(&certificate_verify, &signature) ||
       CBS_len(&certificate_verify) != 0) {
-    al = SSL_AD_DECODE_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return -1;
   }
 
   int sig_ok;
@@ -1746,7 +1627,7 @@
     if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(&hs->transcript, digest,
                                               &digest_len, hs->new_session,
                                               signature_algorithm)) {
-      goto err;
+      return -1;
     }
 
     EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(hs->peer_pubkey, NULL);
@@ -1767,24 +1648,19 @@
   ERR_clear_error();
 #endif
   if (!sig_ok) {
-    al = SSL_AD_DECRYPT_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
-    goto f_err;
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+    return -1;
   }
 
   /* The handshake buffer is no longer necessary, and we may hash the current
    * message.*/
   SSL_TRANSCRIPT_free_buffer(&hs->transcript);
   if (!ssl_hash_current_message(hs)) {
-    goto err;
+    return -1;
   }
 
   return 1;
-
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-err:
-  return 0;
 }
 
 /* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
@@ -1835,40 +1711,46 @@
   return 1;
 }
 
-static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs) {
+static int ssl3_send_server_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  const SSL_SESSION *session;
-  SSL_SESSION *session_copy = NULL;
-  if (ssl->session == NULL) {
-    /* Fix the timeout to measure from the ticket issuance time. */
-    ssl_session_rebase_time(ssl, hs->new_session);
-    session = hs->new_session;
-  } else {
-    /* We are renewing an existing session. Duplicate the session to adjust the
-     * timeout. */
-    session_copy = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH);
-    if (session_copy == NULL) {
-      return -1;
+
+  if (hs->ticket_expected) {
+    const SSL_SESSION *session;
+    SSL_SESSION *session_copy = NULL;
+    if (ssl->session == NULL) {
+      /* Fix the timeout to measure from the ticket issuance time. */
+      ssl_session_rebase_time(ssl, hs->new_session);
+      session = hs->new_session;
+    } else {
+      /* We are renewing an existing session. Duplicate the session to adjust
+       * the timeout. */
+      session_copy = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH);
+      if (session_copy == NULL) {
+        return -1;
+      }
+
+      ssl_session_rebase_time(ssl, session_copy);
+      session = session_copy;
     }
 
-    ssl_session_rebase_time(ssl, session_copy);
-    session = session_copy;
+    CBB cbb, body, ticket;
+    int ok = ssl->method->init_message(ssl, &cbb, &body,
+                                       SSL3_MT_NEW_SESSION_TICKET) &&
+             CBB_add_u32(&body, session->timeout) &&
+             CBB_add_u16_length_prefixed(&body, &ticket) &&
+             ssl_encrypt_ticket(ssl, &ticket, session) &&
+             ssl_add_message_cbb(ssl, &cbb);
+    SSL_SESSION_free(session_copy);
+    CBB_cleanup(&cbb);
+    if (!ok) {
+      return -1;
+    }
   }
 
-  CBB cbb, body, ticket;
-  int ok =
-      ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEW_SESSION_TICKET) &&
-      CBB_add_u32(&body, session->timeout) &&
-      CBB_add_u16_length_prefixed(&body, &ticket) &&
-      ssl_encrypt_ticket(ssl, &ticket, session) &&
-      ssl_add_message_cbb(ssl, &cbb);
-
-  SSL_SESSION_free(session_copy);
-  CBB_cleanup(&cbb);
-
-  if (!ok) {
+  if (!ssl->method->add_change_cipher_spec(ssl) ||
+      !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
     return -1;
   }
 
-  return 1;
+  return ssl3_send_finished(hs);
 }
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index bf0ef02..d56c73b 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -548,22 +548,20 @@
  * configured and zero otherwise. */
 int ssl_has_private_key(const SSL *ssl);
 
-/* ssl_private_key_* call the corresponding function on the
- * |SSL_PRIVATE_KEY_METHOD| for |ssl|, if configured. Otherwise, they implement
- * the operation with |EVP_PKEY|. */
+/* ssl_private_key_* perform the corresponding operation on
+ * |SSL_PRIVATE_KEY_METHOD|. If there is a custom private key configured, they
+ * call the corresponding function or |complete| depending on whether there is a
+ * pending operation. Otherwise, they implement the operation with
+ * |EVP_PKEY|. */
 
 enum ssl_private_key_result_t ssl_private_key_sign(
-    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+    SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
     uint16_t signature_algorithm, const uint8_t *in, size_t in_len);
 
 enum ssl_private_key_result_t ssl_private_key_decrypt(
-    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+    SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
     const uint8_t *in, size_t in_len);
 
-enum ssl_private_key_result_t ssl_private_key_complete(SSL *ssl, uint8_t *out,
-                                                       size_t *out_len,
-                                                       size_t max_out);
-
 /* ssl_private_key_supports_signature_algorithm returns one if |hs|'s private
  * key supports |sigalg| and zero otherwise. */
 int ssl_private_key_supports_signature_algorithm(SSL_HANDSHAKE *hs,
@@ -925,6 +923,7 @@
   ssl_hs_channel_id_lookup,
   ssl_hs_private_key_operation,
   ssl_hs_pending_ticket,
+  ssl_hs_early_data_rejected,
   ssl_hs_read_end_of_early_data,
 };
 
@@ -951,6 +950,14 @@
    * depend on |do_tls13_handshake| but the starting state is always zero. */
   int tls13_state;
 
+  /* min_version is the minimum accepted protocol version, taking account both
+   * |SSL_OP_NO_*| and |SSL_CTX_set_min_proto_version| APIs. */
+  uint16_t min_version;
+
+  /* max_version is the maximum accepted protocol version, taking account both
+   * |SSL_OP_NO_*| and |SSL_CTX_set_max_proto_version| APIs. */
+  uint16_t max_version;
+
   size_t hash_len;
   uint8_t secret[EVP_MAX_MD_SIZE];
   uint8_t early_traffic_secret[EVP_MAX_MD_SIZE];
@@ -1022,8 +1029,9 @@
   uint8_t *peer_key;
   size_t peer_key_len;
 
-  /* server_params, in TLS 1.2, stores the ServerKeyExchange parameters to be
-   * signed while the signature is being computed. */
+  /* server_params, in a TLS 1.2 server, stores the ServerKeyExchange
+   * parameters. It has client and server randoms prepended for signing
+   * convenience. */
   uint8_t *server_params;
   size_t server_params_len;
 
@@ -1057,6 +1065,10 @@
    * handshake. It should not be cached. */
   SSL_SESSION *new_session;
 
+  /* early_session is the session corresponding to the current 0-RTT state on
+   * the client if |in_early_data| is true. */
+  SSL_SESSION *early_session;
+
   /* new_cipher is the cipher being negotiated in this handshake. */
   const SSL_CIPHER *new_cipher;
 
@@ -1097,6 +1109,10 @@
    * Start. The client may write data at this point. */
   unsigned in_false_start:1;
 
+  /* in_early_data is one if there is a pending handshake that has progressed
+   * enough to send and receive early data. */
+  unsigned in_early_data:1;
+
   /* early_data_offered is one if the client sent the early_data extension. */
   unsigned early_data_offered:1;
 
@@ -1115,19 +1131,24 @@
    * or received. */
   unsigned ticket_expected:1;
 
-  /* v2_clienthello is one if we received a V2ClientHello. */
-  unsigned v2_clienthello:1;
-
   /* extended_master_secret is one if the extended master secret extension is
    * negotiated in this handshake. */
   unsigned extended_master_secret:1;
 
+  /* pending_private_key_op is one if there is a pending private key operation
+   * in progress. */
+  unsigned pending_private_key_op:1;
+
   /* client_version is the value sent or received in the ClientHello version. */
   uint16_t client_version;
 
   /* early_data_read is the amount of early data that has been read by the
    * record layer. */
   uint16_t early_data_read;
+
+  /* early_data_written is the amount of early data that has been written by the
+   * record layer. */
+  uint16_t early_data_written;
 } /* SSL_HANDSHAKE */;
 
 SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl);
@@ -1162,8 +1183,12 @@
 int tls13_process_finished(SSL_HANDSHAKE *hs, int use_saved_value);
 
 int tls13_add_certificate(SSL_HANDSHAKE *hs);
-enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs,
-                                                           int is_first_run);
+
+/* tls13_add_certificate_verify adds a TLS 1.3 CertificateVerify message to the
+ * handshake. If it returns |ssl_private_key_retry|, it should be called again
+ * to retry when the signing operation is completed. */
+enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs);
+
 int tls13_add_finished(SSL_HANDSHAKE *hs);
 int tls13_process_new_session_ticket(SSL *ssl);
 
@@ -1292,7 +1317,7 @@
 /* tls12_check_peer_sigalg checks if |sigalg| is acceptable for the peer
  * signature. It returns one on success and zero on error, setting |*out_alert|
  * to an alert to send. */
-int tls12_check_peer_sigalg(SSL *ssl, int *out_alert, uint16_t sigalg);
+int tls12_check_peer_sigalg(SSL *ssl, uint8_t *out_alert, uint16_t sigalg);
 
 
 /* Underdocumented functions.
@@ -1421,7 +1446,8 @@
                        int peek);
   int (*read_change_cipher_spec)(SSL *ssl);
   void (*read_close_notify)(SSL *ssl);
-  int (*write_app_data)(SSL *ssl, const uint8_t *buf, int len);
+  int (*write_app_data)(SSL *ssl, int *out_needs_handshake, const uint8_t *buf,
+                        int len);
   int (*dispatch_alert)(SSL *ssl);
   /* supports_cipher returns one if |cipher| is supported by this protocol and
    * zero otherwise. */
@@ -1632,6 +1658,9 @@
    * outstanding. */
   unsigned key_update_pending:1;
 
+  /* wpend_pending is one if we have a pending write outstanding. */
+  unsigned wpend_pending:1;
+
   uint8_t send_alert[2];
 
   /* pending_flight is the pending outgoing flight. This is used to flush each
@@ -1819,13 +1848,15 @@
   /* version is the protocol version. */
   int version;
 
-  /* max_version is the maximum acceptable protocol version. Note this version
-   * is normalized in DTLS. */
-  uint16_t max_version;
+  /* conf_max_version is the maximum acceptable protocol version configured by
+   * |SSL_set_max_proto_version|. Note this version is normalized in DTLS and is
+   * further constrainted by |SSL_OP_NO_*|. */
+  uint16_t conf_max_version;
 
-  /* min_version is the minimum acceptable protocol version. Note this version
-   * is normalized in DTLS. */
-  uint16_t min_version;
+  /* conf_min_version is the minimum acceptable protocol version configured by
+   * |SSL_set_min_proto_version|. Note this version is normalized in DTLS and is
+   * further constrainted by |SSL_OP_NO_*|. */
+  uint16_t conf_min_version;
 
   uint16_t max_send_fragment;
 
@@ -2088,7 +2119,8 @@
 int ssl3_read_change_cipher_spec(SSL *ssl);
 void ssl3_read_close_notify(SSL *ssl);
 int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len);
-int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len);
+int ssl3_write_app_data(SSL *ssl, int *out_needs_handshake, const uint8_t *buf,
+                        int len);
 int ssl3_output_cert_chain(SSL *ssl);
 
 int ssl3_new(SSL *ssl);
@@ -2129,7 +2161,8 @@
 int dtls1_read_change_cipher_spec(SSL *ssl);
 void dtls1_read_close_notify(SSL *ssl);
 
-int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len);
+int dtls1_write_app_data(SSL *ssl, int *out_needs_handshake, const uint8_t *buf,
+                         int len);
 
 /* dtls1_write_record sends a record. It returns one on success and <= 0 on
  * error. */
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index e05a16e..65d438a 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -168,6 +168,7 @@
   OPENSSL_free(hs->key_share_bytes);
   OPENSSL_free(hs->ecdh_public_key);
   SSL_SESSION_free(hs->new_session);
+  SSL_SESSION_free(hs->early_session);
   OPENSSL_free(hs->peer_sigalgs);
   OPENSSL_free(hs->peer_supported_group_list);
   OPENSSL_free(hs->peer_key);
@@ -331,12 +332,14 @@
     return -1;
   }
 
-  /* The handshake flight buffer is mutually exclusive with application data.
-   *
-   * TODO(davidben): This will not be true when closure alerts use this. */
+  /* If there is pending data in the write buffer, it must be flushed out before
+   * any new data in pending_flight. */
   if (ssl_write_buffer_is_pending(ssl)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
+    int ret = ssl_write_buffer_flush(ssl);
+    if (ret <= 0) {
+      ssl->rwstate = SSL_WRITING;
+      return ret;
+    }
   }
 
   /* Write the pending flight. */
@@ -675,8 +678,6 @@
   ssl_read_buffer_discard(ssl);
 
   ssl->s3->is_v2_hello = 1;
-  /* This is the first message, so hs must be non-NULL. */
-  ssl->s3->hs->v2_clienthello = 1;
   return 1;
 }
 
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 23b39f2..445f882 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -188,10 +188,13 @@
   return -1;
 }
 
-int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len) {
+int ssl3_write_app_data(SSL *ssl, int *out_needs_handshake, const uint8_t *buf,
+                        int len) {
   assert(ssl_can_write(ssl));
   assert(ssl->s3->aead_write_ctx != NULL);
 
+  *out_needs_handshake = 0;
+
   unsigned tot, n, nw;
 
   assert(ssl->s3->wnum <= INT_MAX);
@@ -210,11 +213,25 @@
     return -1;
   }
 
+  const int is_early_data_write =
+      !ssl->server && SSL_in_early_data(ssl) && ssl->s3->hs->can_early_write;
+
   n = len - tot;
   for (;;) {
     /* max contains the maximum number of bytes that we can put into a
      * record. */
     unsigned max = ssl->max_send_fragment;
+    if (is_early_data_write && max > ssl->session->ticket_max_early_data -
+                                         ssl->s3->hs->early_data_written) {
+      max = ssl->session->ticket_max_early_data - ssl->s3->hs->early_data_written;
+      if (max == 0) {
+        ssl->s3->wnum = tot;
+        ssl->s3->hs->can_early_write = 0;
+        *out_needs_handshake = 1;
+        return -1;
+      }
+    }
+
     if (n > max) {
       nw = max;
     } else {
@@ -227,6 +244,10 @@
       return ret;
     }
 
+    if (is_early_data_write) {
+      ssl->s3->hs->early_data_written += ret;
+    }
+
     if (ret == (int)n || (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE)) {
       return tot + ret;
     }
@@ -250,13 +271,14 @@
   if (ret <= 0) {
     return ret;
   }
+  ssl->s3->wpend_pending = 0;
   return ssl->s3->wpend_ret;
 }
 
 /* do_ssl3_write writes an SSL record of the given type. */
 static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) {
   /* If there is still data from the previous record, flush it. */
-  if (ssl_write_buffer_is_pending(ssl)) {
+  if (ssl->s3->wpend_pending) {
     return ssl3_write_pending(ssl, type, buf, len);
   }
 
@@ -317,6 +339,7 @@
   ssl->s3->wpend_buf = buf;
   ssl->s3->wpend_type = type;
   ssl->s3->wpend_ret = len;
+  ssl->s3->wpend_pending = 1;
 
   /* we now just need to write the buffer */
   return ssl3_write_pending(ssl, type, buf, len);
@@ -488,6 +511,17 @@
       }
     }
 
+    /* WatchGuard's TLS 1.3 interference bug is very distinctive: they drop the
+     * ServerHello and send the remaining encrypted application data records
+     * as-is. This manifests as an application data record when we expect
+     * handshake. Report a dedicated error code for this case. */
+    if (!ssl->server && rr->type == SSL3_RT_APPLICATION_DATA &&
+        ssl->s3->aead_read_ctx == NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      return -1;
+    }
+
     if (rr->type != SSL3_RT_HANDSHAKE) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
diff --git a/src/ssl/ssl_buffer.c b/src/ssl/ssl_buffer.c
index aefc044..9ea5c68 100644
--- a/src/ssl/ssl_buffer.c
+++ b/src/ssl/ssl_buffer.c
@@ -33,26 +33,37 @@
 OPENSSL_COMPILE_ASSERT((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0,
                        align_to_a_power_of_two);
 
-/* setup_buffer initializes |buf| with capacity |cap|, aligned such that data
- * written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte
+/* ensure_buffer ensures |buf| has capacity at least |cap|, aligned such that
+ * data written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte
  * boundary. It returns one on success and zero on error. */
-static int setup_buffer(SSL3_BUFFER *buf, size_t header_len, size_t cap) {
-  if (buf->buf != NULL || cap > 0xffff) {
+static int ensure_buffer(SSL3_BUFFER *buf, size_t header_len, size_t cap) {
+  if (cap > 0xffff) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
+  if (buf->cap >= cap) {
+    return 1;
+  }
+
   /* Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment. */
-  buf->buf = OPENSSL_malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
-  if (buf->buf == NULL) {
+  uint8_t *new_buf = OPENSSL_malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
+  if (new_buf == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
   }
 
-  /* Arrange the buffer such that the record body is aligned. */
-  buf->offset = (0 - header_len - (uintptr_t)buf->buf) &
-                (SSL3_ALIGN_PAYLOAD - 1);
-  buf->len = 0;
+  /* Offset the buffer such that the record body is aligned. */
+  size_t new_offset =
+      (0 - header_len - (uintptr_t)new_buf) & (SSL3_ALIGN_PAYLOAD - 1);
+
+  if (buf->buf != NULL) {
+    OPENSSL_memcpy(new_buf + new_offset, buf->buf + buf->offset, buf->len);
+    OPENSSL_free(buf->buf);
+  }
+
+  buf->buf = new_buf;
+  buf->offset = new_offset;
   buf->cap = cap;
   return 1;
 }
@@ -71,30 +82,6 @@
   OPENSSL_memset(buf, 0, sizeof(SSL3_BUFFER));
 }
 
-OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH <=
-                           0xffff,
-                       maximum_read_buffer_too_large);
-
-/* setup_read_buffer initializes the read buffer if not already initialized. It
- * returns one on success and zero on failure. */
-static int setup_read_buffer(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
-
-  if (buf->buf != NULL) {
-    return 1;
-  }
-
-  size_t header_len = ssl_record_prefix_len(ssl);
-  size_t cap = SSL3_RT_MAX_ENCRYPTED_LENGTH;
-  if (SSL_is_dtls(ssl)) {
-    cap += DTLS1_RT_HEADER_LENGTH;
-  } else {
-    cap += SSL3_RT_HEADER_LENGTH;
-  }
-
-  return setup_buffer(buf, header_len, cap);
-}
-
 uint8_t *ssl_read_buffer(SSL *ssl) {
   return ssl->s3->read_buffer.buf + ssl->s3->read_buffer.offset;
 }
@@ -154,7 +141,16 @@
   /* |ssl_read_buffer_extend_to| implicitly discards any consumed data. */
   ssl_read_buffer_discard(ssl);
 
-  if (!setup_read_buffer(ssl)) {
+  if (SSL_is_dtls(ssl)) {
+    OPENSSL_COMPILE_ASSERT(
+        DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH <= 0xffff,
+        dtls_read_buffer_too_large);
+
+    /* The |len| parameter is ignored in DTLS. */
+    len = DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
+  }
+
+  if (!ensure_buffer(&ssl->s3->read_buffer, ssl_record_prefix_len(ssl), len)) {
     return -1;
   }
 
@@ -225,7 +221,7 @@
     return 0;
   }
 
-  if (!setup_buffer(buf, ssl_seal_align_prefix_len(ssl), max_len)) {
+  if (!ensure_buffer(buf, ssl_seal_align_prefix_len(ssl), max_len)) {
     return 0;
   }
   *out_ptr = buf->buf + buf->offset;
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 01bb872..562c1f3 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -160,6 +160,7 @@
     /* Cipher 02 */
     {
      SSL3_TXT_RSA_NULL_SHA,
+     "TLS_RSA_WITH_NULL_SHA",
      SSL3_CK_RSA_NULL_SHA,
      SSL_kRSA,
      SSL_aRSA,
@@ -171,6 +172,7 @@
     /* Cipher 0A */
     {
      SSL3_TXT_RSA_DES_192_CBC3_SHA,
+     "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
      SSL3_CK_RSA_DES_192_CBC3_SHA,
      SSL_kRSA,
      SSL_aRSA,
@@ -185,6 +187,7 @@
     /* Cipher 2F */
     {
      TLS1_TXT_RSA_WITH_AES_128_SHA,
+     "TLS_RSA_WITH_AES_128_CBC_SHA",
      TLS1_CK_RSA_WITH_AES_128_SHA,
      SSL_kRSA,
      SSL_aRSA,
@@ -196,6 +199,7 @@
     /* Cipher 35 */
     {
      TLS1_TXT_RSA_WITH_AES_256_SHA,
+     "TLS_RSA_WITH_AES_256_CBC_SHA",
      TLS1_CK_RSA_WITH_AES_256_SHA,
      SSL_kRSA,
      SSL_aRSA,
@@ -210,6 +214,7 @@
     /* Cipher 3C */
     {
      TLS1_TXT_RSA_WITH_AES_128_SHA256,
+     "TLS_RSA_WITH_AES_128_CBC_SHA256",
      TLS1_CK_RSA_WITH_AES_128_SHA256,
      SSL_kRSA,
      SSL_aRSA,
@@ -221,6 +226,7 @@
     /* Cipher 3D */
     {
      TLS1_TXT_RSA_WITH_AES_256_SHA256,
+     "TLS_RSA_WITH_AES_256_CBC_SHA256",
      TLS1_CK_RSA_WITH_AES_256_SHA256,
      SSL_kRSA,
      SSL_aRSA,
@@ -234,6 +240,7 @@
     /* Cipher 8C */
     {
      TLS1_TXT_PSK_WITH_AES_128_CBC_SHA,
+     "TLS_PSK_WITH_AES_128_CBC_SHA",
      TLS1_CK_PSK_WITH_AES_128_CBC_SHA,
      SSL_kPSK,
      SSL_aPSK,
@@ -245,6 +252,7 @@
     /* Cipher 8D */
     {
      TLS1_TXT_PSK_WITH_AES_256_CBC_SHA,
+     "TLS_PSK_WITH_AES_256_CBC_SHA",
      TLS1_CK_PSK_WITH_AES_256_CBC_SHA,
      SSL_kPSK,
      SSL_aPSK,
@@ -258,6 +266,7 @@
     /* Cipher 9C */
     {
      TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256,
+     "TLS_RSA_WITH_AES_128_GCM_SHA256",
      TLS1_CK_RSA_WITH_AES_128_GCM_SHA256,
      SSL_kRSA,
      SSL_aRSA,
@@ -269,6 +278,7 @@
     /* Cipher 9D */
     {
      TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384,
+     "TLS_RSA_WITH_AES_256_GCM_SHA384",
      TLS1_CK_RSA_WITH_AES_256_GCM_SHA384,
      SSL_kRSA,
      SSL_aRSA,
@@ -282,6 +292,7 @@
     /* Cipher 1301 */
     {
       TLS1_TXT_AES_128_GCM_SHA256,
+      "TLS_AES_128_GCM_SHA256",
       TLS1_CK_AES_128_GCM_SHA256,
       SSL_kGENERIC,
       SSL_aGENERIC,
@@ -293,6 +304,7 @@
     /* Cipher 1302 */
     {
       TLS1_TXT_AES_256_GCM_SHA384,
+      "TLS_AES_256_GCM_SHA384",
       TLS1_CK_AES_256_GCM_SHA384,
       SSL_kGENERIC,
       SSL_aGENERIC,
@@ -304,6 +316,7 @@
     /* Cipher 1303 */
     {
       TLS1_TXT_CHACHA20_POLY1305_SHA256,
+      "TLS_CHACHA20_POLY1305_SHA256",
       TLS1_CK_CHACHA20_POLY1305_SHA256,
       SSL_kGENERIC,
       SSL_aGENERIC,
@@ -315,6 +328,7 @@
     /* Cipher C009 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+     "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
      TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
      SSL_kECDHE,
      SSL_aECDSA,
@@ -326,6 +340,7 @@
     /* Cipher C00A */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+     "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
      TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
      SSL_kECDHE,
      SSL_aECDSA,
@@ -337,6 +352,7 @@
     /* Cipher C013 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+     "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
      TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA,
      SSL_kECDHE,
      SSL_aRSA,
@@ -348,6 +364,7 @@
     /* Cipher C014 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+     "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
      TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA,
      SSL_kECDHE,
      SSL_aRSA,
@@ -362,6 +379,7 @@
     /* Cipher C023 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256,
+     "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
      TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256,
      SSL_kECDHE,
      SSL_aECDSA,
@@ -373,6 +391,7 @@
     /* Cipher C024 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384,
+     "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
      TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384,
      SSL_kECDHE,
      SSL_aECDSA,
@@ -384,6 +403,7 @@
     /* Cipher C027 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256,
+     "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
      TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
      SSL_kECDHE,
      SSL_aRSA,
@@ -395,6 +415,7 @@
     /* Cipher C028 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384,
+     "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
      TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
      SSL_kECDHE,
      SSL_aRSA,
@@ -409,6 +430,7 @@
     /* Cipher C02B */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+     "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
      TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
      SSL_kECDHE,
      SSL_aECDSA,
@@ -420,6 +442,7 @@
     /* Cipher C02C */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+     "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
      TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
      SSL_kECDHE,
      SSL_aECDSA,
@@ -431,6 +454,7 @@
     /* Cipher C02F */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+     "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
      TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
      SSL_kECDHE,
      SSL_aRSA,
@@ -442,6 +466,7 @@
     /* Cipher C030 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+     "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
      TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
      SSL_kECDHE,
      SSL_aRSA,
@@ -455,6 +480,7 @@
     /* Cipher C035 */
     {
      TLS1_TXT_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+     "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
      TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA,
      SSL_kECDHE,
      SSL_aPSK,
@@ -466,6 +492,7 @@
     /* Cipher C036 */
     {
      TLS1_TXT_ECDHE_PSK_WITH_AES_256_CBC_SHA,
+     "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA",
      TLS1_CK_ECDHE_PSK_WITH_AES_256_CBC_SHA,
      SSL_kECDHE,
      SSL_aPSK,
@@ -479,6 +506,7 @@
     /* Cipher CCA8 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+     "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
      TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
      SSL_kECDHE,
      SSL_aRSA,
@@ -490,6 +518,7 @@
     /* Cipher CCA9 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+     "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
      TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
      SSL_kECDHE,
      SSL_aECDSA,
@@ -501,6 +530,7 @@
     /* Cipher CCAB */
     {
      TLS1_TXT_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+     "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
      TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
      SSL_kECDHE,
      SSL_aPSK,
@@ -1089,8 +1119,8 @@
       ch = *l;
       buf = l;
       buf_len = 0;
-      while (((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) ||
-             ((ch >= 'a') && (ch <= 'z')) || (ch == '-') || (ch == '.')) {
+      while ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') ||
+             (ch >= 'a' && ch <= 'z') || ch == '-' || ch == '.' || ch == '_') {
         ch = *(++l);
         buf_len++;
       }
@@ -1111,7 +1141,8 @@
       if (!multi && ch != '+') {
         for (j = 0; j < kCiphersLen; j++) {
           const SSL_CIPHER *cipher = &kCiphers[j];
-          if (rule_equals(cipher->name, buf, buf_len)) {
+          if (rule_equals(cipher->name, buf, buf_len) ||
+              rule_equals(cipher->standard_name, buf, buf_len)) {
             cipher_id = cipher->id;
             break;
           }
@@ -1447,6 +1478,10 @@
   return "(NONE)";
 }
 
+const char *SSL_CIPHER_standard_name(const SSL_CIPHER *cipher) {
+  return cipher->standard_name;
+}
+
 const char *SSL_CIPHER_get_kx_name(const SSL_CIPHER *cipher) {
   if (cipher == NULL) {
     return "";
@@ -1483,79 +1518,12 @@
   }
 }
 
-static const char *ssl_cipher_get_enc_name(const SSL_CIPHER *cipher) {
-  switch (cipher->algorithm_enc) {
-    case SSL_3DES:
-      return "3DES_EDE_CBC";
-    case SSL_AES128:
-      return "AES_128_CBC";
-    case SSL_AES256:
-      return "AES_256_CBC";
-    case SSL_AES128GCM:
-      return "AES_128_GCM";
-    case SSL_AES256GCM:
-      return "AES_256_GCM";
-    case SSL_CHACHA20POLY1305:
-      return "CHACHA20_POLY1305";
-      break;
-    default:
-      assert(0);
-      return "UNKNOWN";
-  }
-}
-
-static const char *ssl_cipher_get_prf_name(const SSL_CIPHER *cipher) {
-  switch (cipher->algorithm_prf) {
-    case SSL_HANDSHAKE_MAC_DEFAULT:
-      /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which
-       * is SHA-1 for all supported ciphers. */
-      assert(cipher->algorithm_mac == SSL_SHA1);
-      return "SHA";
-    case SSL_HANDSHAKE_MAC_SHA256:
-      return "SHA256";
-    case SSL_HANDSHAKE_MAC_SHA384:
-      return "SHA384";
-  }
-  assert(0);
-  return "UNKNOWN";
-}
-
 char *SSL_CIPHER_get_rfc_name(const SSL_CIPHER *cipher) {
   if (cipher == NULL) {
     return NULL;
   }
 
-  const char *kx_name = SSL_CIPHER_get_kx_name(cipher);
-  const char *enc_name = ssl_cipher_get_enc_name(cipher);
-  const char *prf_name = ssl_cipher_get_prf_name(cipher);
-
-  /* The final name is TLS_{kx_name}_WITH_{enc_name}_{prf_name} or
-   * TLS_{enc_name}_{prf_name} depending on whether the cipher is AEAD-only. */
-  size_t len = 4 + strlen(enc_name) + 1 + strlen(prf_name) + 1;
-
-  if (cipher->algorithm_mkey != SSL_kGENERIC) {
-    len += strlen(kx_name) + 6;
-  }
-
-  char *ret = OPENSSL_malloc(len);
-  if (ret == NULL) {
-    return NULL;
-  }
-
-  if (BUF_strlcpy(ret, "TLS_", len) >= len ||
-      (cipher->algorithm_mkey != SSL_kGENERIC &&
-       (BUF_strlcat(ret, kx_name, len) >= len ||
-        BUF_strlcat(ret, "_WITH_", len) >= len)) ||
-      BUF_strlcat(ret, enc_name, len) >= len ||
-      BUF_strlcat(ret, "_", len) >= len ||
-      BUF_strlcat(ret, prf_name, len) >= len) {
-    assert(0);
-    OPENSSL_free(ret);
-    return NULL;
-  }
-
-  assert(strlen(ret) + 1 == len);
-  return ret;
+  return OPENSSL_strdup(SSL_CIPHER_standard_name(cipher));
 }
 
 int SSL_CIPHER_get_bits(const SSL_CIPHER *cipher, int *out_alg_bits) {
diff --git a/src/ssl/ssl_ecdh.c b/src/ssl/ssl_ecdh.c
index 30fe7e4..49652f2 100644
--- a/src/ssl/ssl_ecdh.c
+++ b/src/ssl/ssl_ecdh.c
@@ -189,6 +189,7 @@
 
   uint8_t *secret = OPENSSL_malloc(32);
   if (secret == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return 0;
   }
 
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index b3e397d..1b002a5 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -371,8 +371,8 @@
   }
   OPENSSL_memset(ssl, 0, sizeof(SSL));
 
-  ssl->min_version = ctx->min_version;
-  ssl->max_version = ctx->max_version;
+  ssl->conf_min_version = ctx->conf_min_version;
+  ssl->conf_max_version = ctx->conf_max_version;
 
   /* RFC 6347 states that implementations SHOULD use an initial timer value of
    * 1 second. */
@@ -467,7 +467,10 @@
     return;
   }
 
-  ssl->ctx->x509_method->ssl_free(ssl);
+  if (ssl->ctx != NULL) {
+    ssl->ctx->x509_method->ssl_free(ssl);
+  }
+
   CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data);
 
   BIO_free_all(ssl->rbio);
@@ -757,19 +760,23 @@
     return -1;
   }
 
-  /* If necessary, complete the handshake implicitly. */
-  if (!ssl_can_write(ssl)) {
-    int ret = SSL_do_handshake(ssl);
-    if (ret < 0) {
-      return ret;
+  int ret = 0, needs_handshake = 0;
+  do {
+    /* If necessary, complete the handshake implicitly. */
+    if (!ssl_can_write(ssl)) {
+      ret = SSL_do_handshake(ssl);
+      if (ret < 0) {
+        return ret;
+      }
+      if (ret == 0) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
+        return -1;
+      }
     }
-    if (ret == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
-      return -1;
-    }
-  }
 
-  return ssl->method->write_app_data(ssl, buf, num);
+    ret = ssl->method->write_app_data(ssl, &needs_handshake, buf, num);
+  } while (needs_handshake);
+  return ret;
 }
 
 int SSL_shutdown(SSL *ssl) {
@@ -842,10 +849,35 @@
   ssl->cert->enable_early_data = !!enabled;
 }
 
+int SSL_in_early_data(const SSL *ssl) {
+  if (ssl->s3->hs == NULL) {
+    return 0;
+  }
+  return ssl->s3->hs->in_early_data;
+}
+
 int SSL_early_data_accepted(const SSL *ssl) {
   return ssl->early_data_accepted;
 }
 
+void SSL_reset_early_data_reject(SSL *ssl) {
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  if (hs == NULL ||
+      hs->wait != ssl_hs_early_data_rejected) {
+    abort();
+  }
+
+  hs->wait = ssl_hs_ok;
+  hs->in_early_data = 0;
+  SSL_SESSION_free(hs->early_session);
+  hs->early_session = NULL;
+
+  /* Discard any unfinished writes from the perspective of |SSL_write|'s
+   * retry. The handshake will transparently flush out the pending record
+   * (discarded by the server) to keep the framing correct. */
+  ssl->s3->wpend_pending = 0;
+}
+
 static int bio_retry_reason_to_error(int reason) {
   switch (reason) {
     case BIO_RR_CONNECT:
@@ -938,6 +970,9 @@
 
     case SSL_PENDING_TICKET:
       return SSL_ERROR_PENDING_TICKET;
+
+    case SSL_EARLY_DATA_REJECTED:
+      return SSL_ERROR_EARLY_DATA_REJECTED;
   }
 
   return SSL_ERROR_SYSCALL;
@@ -982,19 +1017,19 @@
 }
 
 int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
-  return set_min_version(ctx->method, &ctx->min_version, version);
+  return set_min_version(ctx->method, &ctx->conf_min_version, version);
 }
 
 int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
-  return set_max_version(ctx->method, &ctx->max_version, version);
+  return set_max_version(ctx->method, &ctx->conf_max_version, version);
 }
 
 int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
-  return set_min_version(ssl->method, &ssl->min_version, version);
+  return set_min_version(ssl->method, &ssl->conf_min_version, version);
 }
 
 int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
-  return set_max_version(ssl->method, &ssl->max_version, version);
+  return set_max_version(ssl->method, &ssl->conf_max_version, version);
 }
 
 uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
@@ -1643,32 +1678,31 @@
   return 1;
 }
 
-int SSL_select_next_proto(uint8_t **out, uint8_t *out_len,
-                          const uint8_t *server, unsigned server_len,
-                          const uint8_t *client, unsigned client_len) {
-  unsigned int i, j;
+int SSL_select_next_proto(uint8_t **out, uint8_t *out_len, const uint8_t *peer,
+                          unsigned peer_len, const uint8_t *supported,
+                          unsigned supported_len) {
   const uint8_t *result;
-  int status = OPENSSL_NPN_UNSUPPORTED;
+  int status;
 
-  /* For each protocol in server preference order, see if we support it. */
-  for (i = 0; i < server_len;) {
-    for (j = 0; j < client_len;) {
-      if (server[i] == client[j] &&
-          OPENSSL_memcmp(&server[i + 1], &client[j + 1], server[i]) == 0) {
+  /* For each protocol in peer preference order, see if we support it. */
+  for (unsigned i = 0; i < peer_len;) {
+    for (unsigned j = 0; j < supported_len;) {
+      if (peer[i] == supported[j] &&
+          OPENSSL_memcmp(&peer[i + 1], &supported[j + 1], peer[i]) == 0) {
         /* We found a match */
-        result = &server[i];
+        result = &peer[i];
         status = OPENSSL_NPN_NEGOTIATED;
         goto found;
       }
-      j += client[j];
+      j += supported[j];
       j++;
     }
-    i += server[i];
+    i += peer[i];
     i++;
   }
 
-  /* There's no overlap between our protocols and the server's list. */
-  result = client;
+  /* There's no overlap between our protocols and the peer's list. */
+  result = supported;
   status = OPENSSL_NPN_NO_OVERLAP;
 
 found:
@@ -1680,11 +1714,7 @@
 void SSL_get0_next_proto_negotiated(const SSL *ssl, const uint8_t **out_data,
                                     unsigned *out_len) {
   *out_data = ssl->s3->next_proto_negotiated;
-  if (*out_data == NULL) {
-    *out_len = 0;
-  } else {
-    *out_len = ssl->s3->next_proto_negotiated_len;
-  }
+  *out_len = ssl->s3->next_proto_negotiated_len;
 }
 
 void SSL_CTX_set_next_protos_advertised_cb(
@@ -1737,13 +1767,11 @@
 
 void SSL_get0_alpn_selected(const SSL *ssl, const uint8_t **out_data,
                             unsigned *out_len) {
-  *out_data = NULL;
-  if (ssl->s3) {
-    *out_data = ssl->s3->alpn_selected;
-  }
-  if (*out_data == NULL) {
-    *out_len = 0;
+  if (SSL_in_early_data(ssl) && !ssl->server) {
+    *out_data = ssl->s3->hs->early_session->early_alpn;
+    *out_len = ssl->s3->hs->early_session->early_alpn_len;
   } else {
+    *out_data = ssl->s3->alpn_selected;
     *out_len = ssl->s3->alpn_selected_len;
   }
 }
@@ -1934,7 +1962,7 @@
 }
 
 int SSL_session_reused(const SSL *ssl) {
-  return ssl->s3->session_reused;
+  return ssl->s3->session_reused || SSL_in_early_data(ssl);
 }
 
 const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; }
@@ -2104,47 +2132,23 @@
                                                        int keylength)) {
 }
 
-int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) {
-  if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
-    return 0;
-  }
-
-  OPENSSL_free(ctx->psk_identity_hint);
-
-  if (identity_hint != NULL) {
-    ctx->psk_identity_hint = BUF_strdup(identity_hint);
-    if (ctx->psk_identity_hint == NULL) {
-      return 0;
-    }
-  } else {
-    ctx->psk_identity_hint = NULL;
-  }
-
-  return 1;
-}
-
-int SSL_use_psk_identity_hint(SSL *ssl, const char *identity_hint) {
-  if (ssl == NULL) {
-    return 0;
-  }
-
+static int use_psk_identity_hint(char **out, const char *identity_hint) {
   if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
     return 0;
   }
 
   /* Clear currently configured hint, if any. */
-  OPENSSL_free(ssl->psk_identity_hint);
-  ssl->psk_identity_hint = NULL;
+  OPENSSL_free(*out);
+  *out = NULL;
 
   /* Treat the empty hint as not supplying one. Plain PSK makes it possible to
    * send either no hint (omit ServerKeyExchange) or an empty hint, while
    * ECDHE_PSK can only spell empty hint. Having different capabilities is odd,
    * so we interpret empty and missing as identical. */
   if (identity_hint != NULL && identity_hint[0] != '\0') {
-    ssl->psk_identity_hint = BUF_strdup(identity_hint);
-    if (ssl->psk_identity_hint == NULL) {
+    *out = BUF_strdup(identity_hint);
+    if (*out == NULL) {
       return 0;
     }
   }
@@ -2152,6 +2156,14 @@
   return 1;
 }
 
+int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) {
+  return use_psk_identity_hint(&ctx->psk_identity_hint, identity_hint);
+}
+
+int SSL_use_psk_identity_hint(SSL *ssl, const char *identity_hint) {
+  return use_psk_identity_hint(&ssl->psk_identity_hint, identity_hint);
+}
+
 const char *SSL_get_psk_identity_hint(const SSL *ssl) {
   if (ssl == NULL) {
     return NULL;
@@ -2342,8 +2354,8 @@
     }
   }
 
-  uint16_t min_version = ssl->min_version;
-  uint16_t max_version = ssl->max_version;
+  uint16_t min_version = ssl->conf_min_version;
+  uint16_t max_version = ssl->conf_max_version;
 
   /* Bound the range to only those implemented in this protocol. */
   if (min_version < ssl->method->min_version) {
diff --git a/src/ssl/ssl_privkey.c b/src/ssl/ssl_privkey.c
index 8492748..257d03e 100644
--- a/src/ssl/ssl_privkey.c
+++ b/src/ssl/ssl_privkey.c
@@ -56,6 +56,7 @@
 
 #include <openssl/ssl.h>
 
+#include <assert.h>
 #include <limits.h>
 
 #include <openssl/ec.h>
@@ -406,33 +407,43 @@
          !alg->is_rsa_pss;
 }
 
+static enum ssl_private_key_result_t legacy_sign(
+    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, uint16_t sigalg,
+    const uint8_t *in, size_t in_len) {
+  /* TODO(davidben): Remove support for |sign_digest|-only
+   * |SSL_PRIVATE_KEY_METHOD|s. */
+  const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
+  if (alg == NULL || !legacy_sign_digest_supported(alg)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
+    return ssl_private_key_failure;
+  }
+
+  const EVP_MD *md = alg->digest_func();
+  uint8_t hash[EVP_MAX_MD_SIZE];
+  unsigned hash_len;
+  if (!EVP_Digest(in, in_len, hash, &hash_len, md, NULL)) {
+    return ssl_private_key_failure;
+  }
+
+  return ssl->cert->key_method->sign_digest(ssl, out, out_len, max_out, md,
+                                            hash, hash_len);
+}
+
 enum ssl_private_key_result_t ssl_private_key_sign(
-    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+    SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
     uint16_t sigalg, const uint8_t *in, size_t in_len) {
+  SSL *const ssl = hs->ssl;
   if (ssl->cert->key_method != NULL) {
-    if (ssl->cert->key_method->sign != NULL) {
-      return ssl->cert->key_method->sign(ssl, out, out_len, max_out, sigalg, in,
-                                         in_len);
+    enum ssl_private_key_result_t ret;
+    if (hs->pending_private_key_op) {
+      ret = ssl->cert->key_method->complete(ssl, out, out_len, max_out);
+    } else {
+      ret = (ssl->cert->key_method->sign != NULL
+                 ? ssl->cert->key_method->sign
+                 : legacy_sign)(ssl, out, out_len, max_out, sigalg, in, in_len);
     }
-
-    /* TODO(davidben): Remove support for |sign_digest|-only
-     * |SSL_PRIVATE_KEY_METHOD|s. */
-    const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
-    if (alg == NULL ||
-        !legacy_sign_digest_supported(alg)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
-      return ssl_private_key_failure;
-    }
-
-    const EVP_MD *md = alg->digest_func();
-    uint8_t hash[EVP_MAX_MD_SIZE];
-    unsigned hash_len;
-    if (!EVP_Digest(in, in_len, hash, &hash_len, md, NULL)) {
-      return ssl_private_key_failure;
-    }
-
-    return ssl->cert->key_method->sign_digest(ssl, out, out_len, max_out, md,
-                                              hash, hash_len);
+    hs->pending_private_key_op = ret == ssl_private_key_retry;
+    return ret;
   }
 
   *out_len = max_out;
@@ -456,11 +467,19 @@
 }
 
 enum ssl_private_key_result_t ssl_private_key_decrypt(
-    SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+    SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
     const uint8_t *in, size_t in_len) {
+  SSL *const ssl = hs->ssl;
   if (ssl->cert->key_method != NULL) {
-    return ssl->cert->key_method->decrypt(ssl, out, out_len, max_out, in,
-                                          in_len);
+    enum ssl_private_key_result_t ret;
+    if (hs->pending_private_key_op) {
+      ret = ssl->cert->key_method->complete(ssl, out, out_len, max_out);
+    } else {
+      ret = ssl->cert->key_method->decrypt(ssl, out, out_len, max_out, in,
+                                           in_len);
+    }
+    hs->pending_private_key_op = ret == ssl_private_key_retry;
+    return ret;
   }
 
   RSA *rsa = EVP_PKEY_get0_RSA(ssl->cert->privatekey);
@@ -478,13 +497,6 @@
   return ssl_private_key_success;
 }
 
-enum ssl_private_key_result_t ssl_private_key_complete(SSL *ssl, uint8_t *out,
-                                                       size_t *out_len,
-                                                       size_t max_out) {
-  /* Only custom keys may be asynchronous. */
-  return ssl->cert->key_method->complete(ssl, out, out_len, max_out);
-}
-
 int ssl_private_key_supports_signature_algorithm(SSL_HANDSHAKE *hs,
                                                  uint16_t sigalg) {
   SSL *const ssl = hs->ssl;
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index b105cd0..f025364 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -467,8 +467,12 @@
   if (!SSL_in_init(ssl)) {
     return ssl->s3->established_session;
   }
-  if (ssl->s3->hs->new_session != NULL) {
-    return ssl->s3->hs->new_session;
+  SSL_HANDSHAKE *hs = ssl->s3->hs;
+  if (hs->early_session != NULL) {
+    return hs->early_session;
+  }
+  if (hs->new_session != NULL) {
+    return hs->new_session;
   }
   return ssl->session;
 }
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c
index 571b4a9..22149e2 100644
--- a/src/ssl/ssl_stat.c
+++ b/src/ssl/ssl_stat.c
@@ -142,11 +142,7 @@
     case SSL3_ST_CW_CERT_VRFY_A:
       return "SSLv3 write certificate verify A";
 
-    case SSL3_ST_CW_CERT_VRFY_B:
-      return "SSLv3 write certificate verify B";
-
     case SSL3_ST_CW_CHANGE:
-    case SSL3_ST_SW_CHANGE:
       return "SSLv3 write change cipher spec";
 
     case SSL3_ST_CW_FINISHED_A:
@@ -183,12 +179,6 @@
     case SSL3_ST_SW_KEY_EXCH_A:
       return "SSLv3 write key exchange A";
 
-    case SSL3_ST_SW_CERT_REQ_A:
-      return "SSLv3 write certificate request A";
-
-    case SSL3_ST_SW_SESSION_TICKET_A:
-      return "SSLv3 write session ticket A";
-
     case SSL3_ST_SW_SRVR_DONE_A:
       return "SSLv3 write server done A";
 
@@ -256,10 +246,6 @@
     case SSL3_ST_CW_CERT_VRFY_A:
       return "3WCV_A";
 
-    case SSL3_ST_CW_CERT_VRFY_B:
-      return "3WCV_B";
-
-    case SSL3_ST_SW_CHANGE:
     case SSL3_ST_CW_CHANGE:
       return "3WCCS_";
 
@@ -293,12 +279,6 @@
     case SSL3_ST_SW_KEY_EXCH_A:
       return "3WSKEA";
 
-    case SSL3_ST_SW_KEY_EXCH_B:
-      return "3WSKEB";
-
-    case SSL3_ST_SW_CERT_REQ_A:
-      return "3WCR_A";
-
     case SSL3_ST_SW_SRVR_DONE_A:
       return "3WSD_A";
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 80465ce..84b7496 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -177,6 +177,20 @@
         },
         false,
     },
+    // Standard names may be used instead of OpenSSL names.
+    {
+        "[TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256|"
+         "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]:"
+        "[TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256]:"
+        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+        {
+            {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 1},
+            {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+            {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+        },
+        false,
+    },
     // @STRENGTH performs a stable strength-sort of the selected ciphers and
     // only the selected ciphers.
     {
@@ -715,8 +729,8 @@
                                  const SSL_METHOD *(*method)(void)) {
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method()));
   ASSERT_TRUE(ctx);
-  EXPECT_EQ(min_version, ctx->min_version);
-  EXPECT_EQ(max_version, ctx->max_version);
+  EXPECT_EQ(min_version, ctx->conf_min_version);
+  EXPECT_EQ(max_version, ctx->conf_max_version);
 }
 
 TEST(SSLTest, DefaultVersion) {
@@ -730,43 +744,42 @@
   ExpectDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &DTLSv1_2_method);
 }
 
-typedef struct {
-  int id;
-  const char *rfc_name;
-} CIPHER_RFC_NAME_TEST;
+TEST(SSLTest, CipherGetStandardName) {
+  static const struct {
+    int id;
+    const char *standard_name;
+  } kTests[] = {
+      {SSL3_CK_RSA_DES_192_CBC3_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
+      {TLS1_CK_RSA_WITH_AES_128_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"},
+      {TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
+       "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"},
+      {TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
+       "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
+      {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+       "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"},
+      {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+       "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"},
+      {TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+       "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"},
+      {TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+       "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"},
+      {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+       "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
+      {TLS1_CK_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384"},
+      {TLS1_CK_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256"},
+      {TLS1_CK_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256"},
+  };
 
-static const CIPHER_RFC_NAME_TEST kCipherRFCNameTests[] = {
-    {SSL3_CK_RSA_DES_192_CBC3_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
-    {TLS1_CK_RSA_WITH_AES_128_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"},
-    {TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
-     "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"},
-    {TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
-     "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
-    {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-     "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"},
-    {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-     "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"},
-    {TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-     "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"},
-    {TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-     "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"},
-    {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-     "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
-    {TLS1_CK_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384"},
-    {TLS1_CK_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256"},
-    {TLS1_CK_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256"},
-};
-
-TEST(SSLTest, CipherGetRFCName) {
-  for (const CIPHER_RFC_NAME_TEST &t : kCipherRFCNameTests) {
-    SCOPED_TRACE(t.rfc_name);
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.standard_name);
 
     const SSL_CIPHER *cipher = SSL_get_cipher_by_value(t.id & 0xffff);
     ASSERT_TRUE(cipher);
+    EXPECT_STREQ(t.standard_name, SSL_CIPHER_standard_name(cipher));
+
     bssl::UniquePtr<char> rfc_name(SSL_CIPHER_get_rfc_name(cipher));
     ASSERT_TRUE(rfc_name);
-
-    EXPECT_STREQ(t.rfc_name, rfc_name.get());
+    EXPECT_STREQ(t.standard_name, rfc_name.get());
   }
 }
 
@@ -1497,6 +1510,7 @@
 }
 
 static void ExpectFDs(const SSL *ssl, int rfd, int wfd) {
+  EXPECT_EQ(rfd, SSL_get_fd(ssl));
   EXPECT_EQ(rfd, SSL_get_rfd(ssl));
   EXPECT_EQ(wfd, SSL_get_wfd(ssl));
 
@@ -1945,7 +1959,7 @@
 }
 
 static bssl::UniquePtr<SSL_SESSION> CreateClientSession(SSL_CTX *client_ctx,
-                                             SSL_CTX *server_ctx) {
+                                                        SSL_CTX *server_ctx) {
   g_last_session = nullptr;
   SSL_CTX_sess_set_new_cb(client_ctx, SaveLastSession);
 
@@ -2543,15 +2557,15 @@
 
   // Zero is the default version.
   EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), 0));
-  EXPECT_EQ(TLS1_2_VERSION, ctx->max_version);
+  EXPECT_EQ(TLS1_2_VERSION, ctx->conf_max_version);
   EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), 0));
-  EXPECT_EQ(TLS1_VERSION, ctx->min_version);
+  EXPECT_EQ(TLS1_VERSION, ctx->conf_min_version);
 
   // SSL 3.0 and TLS 1.3 are available, but not by default.
   EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), SSL3_VERSION));
-  EXPECT_EQ(SSL3_VERSION, ctx->min_version);
+  EXPECT_EQ(SSL3_VERSION, ctx->conf_min_version);
   EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_VERSION));
-  EXPECT_EQ(TLS1_3_VERSION, ctx->max_version);
+  EXPECT_EQ(TLS1_3_VERSION, ctx->conf_max_version);
 
   ctx.reset(SSL_CTX_new(DTLS_method()));
   ASSERT_TRUE(ctx);
@@ -2571,9 +2585,9 @@
   EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0x1234));
 
   EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), 0));
-  EXPECT_EQ(TLS1_2_VERSION, ctx->max_version);
+  EXPECT_EQ(TLS1_2_VERSION, ctx->conf_max_version);
   EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), 0));
-  EXPECT_EQ(TLS1_1_VERSION, ctx->min_version);
+  EXPECT_EQ(TLS1_1_VERSION, ctx->conf_min_version);
 }
 
 static const char *GetVersionName(uint16_t version) {
@@ -3032,6 +3046,81 @@
   return true;
 }
 
+static bool TestRecordCallback(bool is_dtls, const SSL_METHOD *method,
+                               uint16_t version) {
+  bssl::UniquePtr<X509> cert = GetChainTestCertificate();
+  bssl::UniquePtr<X509> intermediate = GetChainTestIntermediate();
+  bssl::UniquePtr<EVP_PKEY> key = GetChainTestKey();
+  if (!cert || !intermediate || !key) {
+    return false;
+  }
+
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method));
+  if (!ctx ||
+      !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) ||
+      !SSL_CTX_set_min_proto_version(ctx.get(), version) ||
+      !SSL_CTX_set_max_proto_version(ctx.get(), version)) {
+    return false;
+  }
+
+  bool read_seen = false;
+  bool write_seen = false;
+  auto cb = [&](int is_write, int cb_version, int cb_type, const void *buf,
+                size_t len, SSL *ssl) {
+    if (cb_type != SSL3_RT_HEADER) {
+      return;
+    }
+
+    // The callback does not report a version for records.
+    EXPECT_EQ(0, cb_version);
+
+    if (is_write) {
+      write_seen = true;
+    } else {
+      read_seen = true;
+    }
+
+    // Sanity-check that the record header is plausible.
+    CBS cbs;
+    CBS_init(&cbs, reinterpret_cast<const uint8_t *>(buf), len);
+    uint8_t type;
+    uint16_t record_version, length;
+    ASSERT_TRUE(CBS_get_u8(&cbs, &type));
+    ASSERT_TRUE(CBS_get_u16(&cbs, &record_version));
+    EXPECT_TRUE(record_version == version ||
+                record_version == (is_dtls ? DTLS1_VERSION : TLS1_VERSION))
+        << "Invalid record version: " << record_version;
+    if (is_dtls) {
+      uint16_t epoch;
+      ASSERT_TRUE(CBS_get_u16(&cbs, &epoch));
+      EXPECT_TRUE(epoch == 0 || epoch == 1) << "Invalid epoch: " << epoch;
+      ASSERT_TRUE(CBS_skip(&cbs, 6));
+    }
+    ASSERT_TRUE(CBS_get_u16(&cbs, &length));
+    EXPECT_EQ(0u, CBS_len(&cbs));
+  };
+  using CallbackType = decltype(cb);
+  SSL_CTX_set_msg_callback(
+      ctx.get(), [](int is_write, int cb_version, int cb_type, const void *buf,
+                    size_t len, SSL *ssl, void *arg) {
+        CallbackType *cb_ptr = reinterpret_cast<CallbackType *>(arg);
+        (*cb_ptr)(is_write, cb_version, cb_type, buf, len, ssl);
+      });
+  SSL_CTX_set_msg_callback_arg(ctx.get(), &cb);
+
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
+                              nullptr /* no session */)) {
+    return false;
+  }
+
+  EXPECT_TRUE(read_seen);
+  EXPECT_TRUE(write_seen);
+  return true;
+}
+
+
 static bool ForEachVersion(bool (*test_func)(bool is_dtls,
                                              const SSL_METHOD *method,
                                              uint16_t version)) {
@@ -3454,6 +3543,49 @@
   EXPECT_EQ(SSL_R_NO_SUPPORTED_VERSIONS_ENABLED, ERR_GET_REASON(err));
 }
 
+TEST(SSLTest, SelectNextProto) {
+  uint8_t *result;
+  uint8_t result_len;
+
+  // If there is an overlap, it should be returned.
+  EXPECT_EQ(OPENSSL_NPN_NEGOTIATED,
+            SSL_select_next_proto(&result, &result_len,
+                                  (const uint8_t *)"\1a\2bb\3ccc", 9,
+                                  (const uint8_t *)"\1x\1y\1a\1z", 8));
+  EXPECT_EQ(Bytes("a"), Bytes(result, result_len));
+
+  EXPECT_EQ(OPENSSL_NPN_NEGOTIATED,
+            SSL_select_next_proto(&result, &result_len,
+                                  (const uint8_t *)"\1a\2bb\3ccc", 9,
+                                  (const uint8_t *)"\1x\1y\2bb\1z", 9));
+  EXPECT_EQ(Bytes("bb"), Bytes(result, result_len));
+
+  EXPECT_EQ(OPENSSL_NPN_NEGOTIATED,
+            SSL_select_next_proto(&result, &result_len,
+                                  (const uint8_t *)"\1a\2bb\3ccc", 9,
+                                  (const uint8_t *)"\1x\1y\3ccc\1z", 10));
+  EXPECT_EQ(Bytes("ccc"), Bytes(result, result_len));
+
+  // Peer preference order takes precedence over local.
+  EXPECT_EQ(OPENSSL_NPN_NEGOTIATED,
+            SSL_select_next_proto(&result, &result_len,
+                                  (const uint8_t *)"\1a\2bb\3ccc", 9,
+                                  (const uint8_t *)"\3ccc\2bb\1a", 9));
+  EXPECT_EQ(Bytes("a"), Bytes(result, result_len));
+
+  // If there is no overlap, return the first local protocol.
+  EXPECT_EQ(OPENSSL_NPN_NO_OVERLAP,
+            SSL_select_next_proto(&result, &result_len,
+                                  (const uint8_t *)"\1a\2bb\3ccc", 9,
+                                  (const uint8_t *)"\1x\2yy\3zzz", 9));
+  EXPECT_EQ(Bytes("x"), Bytes(result, result_len));
+
+  EXPECT_EQ(OPENSSL_NPN_NO_OVERLAP,
+            SSL_select_next_proto(&result, &result_len, nullptr, 0,
+                                  (const uint8_t *)"\1x\2yy\3zzz", 9));
+  EXPECT_EQ(Bytes("x"), Bytes(result, result_len));
+}
+
 // TODO(davidben): Convert this file to GTest properly.
 TEST(SSLTest, AllTests) {
   if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
@@ -3482,7 +3614,8 @@
       !ForEachVersion(TestALPNCipherAvailable) ||
       !ForEachVersion(TestSSLClearSessionResumption) ||
       !ForEachVersion(TestAutoChain) ||
-      !ForEachVersion(TestSSLWriteRetry)) {
+      !ForEachVersion(TestSSLWriteRetry) ||
+      !ForEachVersion(TestRecordCallback)) {
     ADD_FAILURE() << "Tests failed";
   }
 }
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 000a8cd..1b14371 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -536,7 +536,7 @@
   return 1;
 }
 
-int tls12_check_peer_sigalg(SSL *ssl, int *out_alert, uint16_t sigalg) {
+int tls12_check_peer_sigalg(SSL *ssl, uint8_t *out_alert, uint16_t sigalg) {
   const uint16_t *sigalgs = kVerifySignatureAlgorithms;
   size_t num_sigalgs = OPENSSL_ARRAY_SIZE(kVerifySignatureAlgorithms);
   if (ssl->ctx->num_verify_sigalgs != 0) {
@@ -722,13 +722,8 @@
 
 static int ext_ri_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
   /* Renegotiation indication is not necessary in TLS 1.3. */
-  if (min_version >= TLS1_3_VERSION) {
+  if (hs->min_version >= TLS1_3_VERSION) {
     return 1;
   }
 
@@ -883,13 +878,8 @@
  * https://tools.ietf.org/html/rfc7627 */
 
 static int ext_ems_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(hs->ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
   /* Extended master secret is not necessary in TLS 1.3. */
-  if (min_version >= TLS1_3_VERSION || max_version <= SSL3_VERSION) {
+  if (hs->min_version >= TLS1_3_VERSION || hs->max_version <= SSL3_VERSION) {
     return 1;
   }
 
@@ -967,13 +957,8 @@
 
 static int ext_ticket_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
   /* TLS 1.3 uses a different ticket extension. */
-  if (min_version >= TLS1_3_VERSION ||
+  if (hs->min_version >= TLS1_3_VERSION ||
       SSL_get_options(ssl) & SSL_OP_NO_TICKET) {
     return 1;
   }
@@ -1055,12 +1040,7 @@
 
 static int ext_sigalgs_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
-  if (max_version < TLS1_2_VERSION) {
+  if (hs->max_version < TLS1_2_VERSION) {
     return 1;
   }
 
@@ -1814,13 +1794,8 @@
 }
 
 static int ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(hs->ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
   /* The point format extension is unneccessary in TLS 1.3. */
-  if (min_version >= TLS1_3_VERSION) {
+  if (hs->min_version >= TLS1_3_VERSION) {
     return 1;
   }
 
@@ -1888,13 +1863,8 @@
 
 static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
   uint16_t session_version;
-  if (max_version < TLS1_3_VERSION || ssl->session == NULL ||
+  if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
       !ssl->method->version_from_wire(&session_version,
                                       ssl->session->ssl_version) ||
       session_version < TLS1_3_VERSION) {
@@ -1913,13 +1883,8 @@
 
 static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
   uint16_t session_version;
-  if (max_version < TLS1_3_VERSION || ssl->session == NULL ||
+  if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
       !ssl->method->version_from_wire(&session_version,
                                       ssl->session->ssl_version) ||
       session_version < TLS1_3_VERSION) {
@@ -2062,13 +2027,7 @@
 
 static int ext_psk_key_exchange_modes_add_clienthello(SSL_HANDSHAKE *hs,
                                                       CBB *out) {
-  SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
-  if (max_version < TLS1_3_VERSION) {
+  if (hs->max_version < TLS1_3_VERSION) {
     return 1;
   }
 
@@ -2194,12 +2153,7 @@
 
 static int ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
-  if (max_version < TLS1_3_VERSION) {
+  if (hs->max_version < TLS1_3_VERSION) {
     return 1;
   }
 
@@ -2404,12 +2358,7 @@
 
 static int ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  uint16_t min_version, max_version;
-  if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
-    return 0;
-  }
-
-  if (max_version <= TLS1_2_VERSION) {
+  if (hs->max_version <= TLS1_2_VERSION) {
     return 1;
   }
 
@@ -2426,7 +2375,8 @@
     return 0;
   }
 
-  for (uint16_t version = max_version; version >= min_version; version--) {
+  for (uint16_t version = hs->max_version; version >= hs->min_version;
+       version--) {
     if (!CBB_add_u16(&versions, ssl->method->version_to_wire(version))) {
       return 0;
     }
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index f9b6553..3cb2856 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -265,6 +265,9 @@
     const std::string &hex_names) {
   const std::vector<std::string> der_names = DecodeHexStrings(hex_names);
   bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
+  if (!ret) {
+    return nullptr;
+  }
 
   for (const auto &der_name : der_names) {
     const uint8_t *const data =
@@ -1361,24 +1364,23 @@
 // CheckHandshakeProperties checks, immediately after |ssl| completes its
 // initial handshake (or False Starts), whether all the properties are
 // consistent with the test configuration and invariants.
-static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
-  const TestConfig *config = GetTestConfig(ssl);
-
+static bool CheckHandshakeProperties(SSL *ssl, bool is_resume,
+                                     const TestConfig *config) {
   if (SSL_get_current_cipher(ssl) == nullptr) {
     fprintf(stderr, "null cipher after handshake\n");
     return false;
   }
 
-  if (is_resume &&
-      (!!SSL_session_reused(ssl) == config->expect_session_miss)) {
-    fprintf(stderr, "session was%s reused\n",
+  bool expect_resume =
+      is_resume && (!config->expect_session_miss || SSL_in_early_data(ssl));
+  if (!!SSL_session_reused(ssl) != expect_resume) {
+    fprintf(stderr, "session unexpectedly was%s reused\n",
             SSL_session_reused(ssl) ? "" : " not");
     return false;
   }
 
   bool expect_handshake_done =
-      (is_resume || !config->false_start) &&
-      !(config->is_server && SSL_early_data_accepted(ssl));
+      (is_resume || !config->false_start) && !SSL_in_early_data(ssl);
   if (expect_handshake_done != GetTestState(ssl)->handshake_done) {
     fprintf(stderr, "handshake was%s completed\n",
             GetTestState(ssl)->handshake_done ? "" : " not");
@@ -1552,7 +1554,7 @@
     return false;
   }
 
-  if (is_resume) {
+  if (is_resume && !SSL_in_early_data(ssl)) {
     if ((config->expect_accept_early_data && !SSL_early_data_accepted(ssl)) ||
         (config->expect_reject_early_data && SSL_early_data_accepted(ssl))) {
       fprintf(stderr,
@@ -1643,13 +1645,17 @@
   return true;
 }
 
-// DoExchange runs a test SSL exchange against the peer. On success, it returns
+static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, SSL *ssl,
+                       const TestConfig *config, bool is_resume, bool is_retry);
+
+// DoConnection tests an SSL connection against the peer. On success, it returns
 // true and sets |*out_session| to the negotiated SSL session. If the test is a
 // resumption attempt, |is_resume| is true and |session| is the session from the
 // previous exchange.
-static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
-                       SSL_CTX *ssl_ctx, const TestConfig *config,
-                       bool is_resume, SSL_SESSION *session) {
+static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session,
+                         SSL_CTX *ssl_ctx, const TestConfig *config,
+                         const TestConfig *retry_config, bool is_resume,
+                         SSL_SESSION *session) {
   bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx));
   if (!ssl) {
     return false;
@@ -1865,20 +1871,62 @@
     SSL_set_connect_state(ssl.get());
   }
 
+  bool ret = DoExchange(out_session, ssl.get(), config, is_resume, false);
+  if (!config->is_server && is_resume && config->expect_reject_early_data) {
+    // We must have failed due to an early data rejection.
+    if (ret) {
+      fprintf(stderr, "0-RTT exchange unexpected succeeded.\n");
+      return false;
+    }
+    if (SSL_get_error(ssl.get(), -1) != SSL_ERROR_EARLY_DATA_REJECTED) {
+      fprintf(stderr,
+              "SSL_get_error did not signal SSL_ERROR_EARLY_DATA_REJECTED.\n");
+      return false;
+    }
+
+    // Before reseting, early state should still be available.
+    if (!SSL_in_early_data(ssl.get()) ||
+        !CheckHandshakeProperties(ssl.get(), is_resume, config)) {
+      fprintf(stderr, "SSL_in_early_data returned false before reset.\n");
+      return false;
+    }
+
+    // Reset the connection and try again at 1-RTT.
+    SSL_reset_early_data_reject(ssl.get());
+
+    // After reseting, the socket should report it is no longer in an early data
+    // state.
+    if (SSL_in_early_data(ssl.get())) {
+      fprintf(stderr, "SSL_in_early_data returned true after reset.\n");
+      return false;
+    }
+
+    if (!SetTestConfig(ssl.get(), retry_config)) {
+      return false;
+    }
+
+    ret = DoExchange(out_session, ssl.get(), retry_config, is_resume, true);
+  }
+  return ret;
+}
+
+static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, SSL *ssl,
+                       const TestConfig *config, bool is_resume,
+                       bool is_retry) {
   int ret;
   if (!config->implicit_handshake) {
     do {
-      ret = SSL_do_handshake(ssl.get());
-    } while (config->async && RetryAsync(ssl.get(), ret));
+      ret = SSL_do_handshake(ssl);
+    } while (config->async && RetryAsync(ssl, ret));
     if (ret != 1 ||
-        !CheckHandshakeProperties(ssl.get(), is_resume)) {
+        !CheckHandshakeProperties(ssl, is_resume, config)) {
       return false;
     }
 
     if (config->handshake_twice) {
       do {
-        ret = SSL_do_handshake(ssl.get());
-      } while (config->async && RetryAsync(ssl.get(), ret));
+        ret = SSL_do_handshake(ssl);
+      } while (config->async && RetryAsync(ssl, ret));
       if (ret != 1) {
         return false;
       }
@@ -1886,28 +1934,28 @@
 
     // Skip the |config->async| logic as this should be a no-op.
     if (config->no_op_extra_handshake &&
-        SSL_do_handshake(ssl.get()) != 1) {
+        SSL_do_handshake(ssl) != 1) {
       fprintf(stderr, "Extra SSL_do_handshake was not a no-op.\n");
       return false;
     }
 
     // Reset the state to assert later that the callback isn't called in
     // renegotations.
-    GetTestState(ssl.get())->got_new_session = false;
+    GetTestState(ssl)->got_new_session = false;
   }
 
   if (config->export_keying_material > 0) {
     std::vector<uint8_t> result(
         static_cast<size_t>(config->export_keying_material));
     if (!SSL_export_keying_material(
-            ssl.get(), result.data(), result.size(),
-            config->export_label.data(), config->export_label.size(),
-            reinterpret_cast<const uint8_t*>(config->export_context.data()),
+            ssl, result.data(), result.size(), config->export_label.data(),
+            config->export_label.size(),
+            reinterpret_cast<const uint8_t *>(config->export_context.data()),
             config->export_context.size(), config->use_export_context)) {
       fprintf(stderr, "failed to export keying material\n");
       return false;
     }
-    if (WriteAll(ssl.get(), result.data(), result.size()) < 0) {
+    if (WriteAll(ssl, result.data(), result.size()) < 0) {
       return false;
     }
   }
@@ -1915,7 +1963,7 @@
   if (config->tls_unique) {
     uint8_t tls_unique[16];
     size_t tls_unique_len;
-    if (!SSL_get_tls_unique(ssl.get(), tls_unique, &tls_unique_len,
+    if (!SSL_get_tls_unique(ssl, tls_unique, &tls_unique_len,
                             sizeof(tls_unique))) {
       fprintf(stderr, "failed to get tls-unique\n");
       return false;
@@ -1927,13 +1975,13 @@
       return false;
     }
 
-    if (WriteAll(ssl.get(), tls_unique, tls_unique_len) < 0) {
+    if (WriteAll(ssl, tls_unique, tls_unique_len) < 0) {
       return false;
     }
   }
 
   if (config->send_alert) {
-    if (DoSendFatalAlert(ssl.get(), SSL_AD_DECOMPRESSION_FAILURE) < 0) {
+    if (DoSendFatalAlert(ssl, SSL_AD_DECOMPRESSION_FAILURE) < 0) {
       return false;
     }
     return true;
@@ -1957,7 +2005,7 @@
         fprintf(stderr, "Bad kRecordSizes value.\n");
         return false;
       }
-      if (WriteAll(ssl.get(), buf.get(), len) < 0) {
+      if (WriteAll(ssl, buf.get(), len) < 0) {
         return false;
       }
     }
@@ -1970,15 +2018,17 @@
         return false;
       }
 
+      // Let only one byte of the record through.
+      AsyncBioAllowWrite(GetTestState(ssl)->async_bio, 1);
       int write_ret =
-          SSL_write(ssl.get(), kInitialWrite, strlen(kInitialWrite));
-      if (SSL_get_error(ssl.get(), write_ret) != SSL_ERROR_WANT_WRITE) {
+          SSL_write(ssl, kInitialWrite, strlen(kInitialWrite));
+      if (SSL_get_error(ssl, write_ret) != SSL_ERROR_WANT_WRITE) {
         fprintf(stderr, "Failed to leave unfinished write.\n");
         return false;
       }
       pending_initial_write = true;
     } else if (config->shim_writes_first) {
-      if (WriteAll(ssl.get(), kInitialWrite, strlen(kInitialWrite)) < 0) {
+      if (WriteAll(ssl, kInitialWrite, strlen(kInitialWrite)) < 0) {
         return false;
       }
     }
@@ -1992,8 +2042,8 @@
         }
         std::unique_ptr<uint8_t[]> buf(new uint8_t[read_size]);
 
-        int n = DoRead(ssl.get(), buf.get(), read_size);
-        int err = SSL_get_error(ssl.get(), n);
+        int n = DoRead(ssl, buf.get(), read_size);
+        int err = SSL_get_error(ssl, n);
         if (err == SSL_ERROR_ZERO_RETURN ||
             (n == 0 && err == SSL_ERROR_SYSCALL)) {
           if (n != 0) {
@@ -2015,17 +2065,24 @@
           return false;
         }
 
+        if (!config->is_server && is_resume && !is_retry &&
+            config->expect_reject_early_data) {
+          fprintf(stderr,
+                  "Unexpectedly received data instead of 0-RTT reject.\n");
+          return false;
+        }
+
         // After a successful read, with or without False Start, the handshake
         // must be complete unless we are doing early data.
-        if (!GetTestState(ssl.get())->handshake_done &&
-            !SSL_early_data_accepted(ssl.get())) {
+        if (!GetTestState(ssl)->handshake_done &&
+            !SSL_early_data_accepted(ssl)) {
           fprintf(stderr, "handshake was not completed after SSL_read\n");
           return false;
         }
 
         // Clear the initial write, if unfinished.
         if (pending_initial_write) {
-          if (WriteAll(ssl.get(), kInitialWrite, strlen(kInitialWrite)) < 0) {
+          if (WriteAll(ssl, kInitialWrite, strlen(kInitialWrite)) < 0) {
             return false;
           }
           pending_initial_write = false;
@@ -2034,7 +2091,7 @@
         for (int i = 0; i < n; i++) {
           buf[i] ^= 0xff;
         }
-        if (WriteAll(ssl.get(), buf.get(), n) < 0) {
+        if (WriteAll(ssl, buf.get(), n) < 0) {
           return false;
         }
       }
@@ -2044,25 +2101,25 @@
   if (!config->is_server && !config->false_start &&
       !config->implicit_handshake &&
       // Session tickets are sent post-handshake in TLS 1.3.
-      GetProtocolVersion(ssl.get()) < TLS1_3_VERSION &&
-      GetTestState(ssl.get())->got_new_session) {
+      GetProtocolVersion(ssl) < TLS1_3_VERSION &&
+      GetTestState(ssl)->got_new_session) {
     fprintf(stderr, "new session was established after the handshake\n");
     return false;
   }
 
-  if (GetProtocolVersion(ssl.get()) >= TLS1_3_VERSION && !config->is_server) {
+  if (GetProtocolVersion(ssl) >= TLS1_3_VERSION && !config->is_server) {
     bool expect_new_session =
         !config->expect_no_session && !config->shim_shuts_down;
-    if (expect_new_session != GetTestState(ssl.get())->got_new_session) {
+    if (expect_new_session != GetTestState(ssl)->got_new_session) {
       fprintf(stderr,
               "new session was%s cached, but we expected the opposite\n",
-              GetTestState(ssl.get())->got_new_session ? "" : " not");
+              GetTestState(ssl)->got_new_session ? "" : " not");
       return false;
     }
 
     if (expect_new_session) {
       bool got_early_data_info =
-          GetTestState(ssl.get())->new_session->ticket_max_early_data != 0;
+          GetTestState(ssl)->new_session->ticket_max_early_data != 0;
       if (config->expect_early_data_info != got_early_data_info) {
         fprintf(
             stderr,
@@ -2075,10 +2132,10 @@
   }
 
   if (out_session) {
-    *out_session = std::move(GetTestState(ssl.get())->new_session);
+    *out_session = std::move(GetTestState(ssl)->new_session);
   }
 
-  ret = DoShutdown(ssl.get());
+  ret = DoShutdown(ssl);
 
   if (config->shim_shuts_down && config->check_close_notify) {
     // We initiate shutdown, so |SSL_shutdown| will return in two stages. First
@@ -2088,7 +2145,7 @@
       fprintf(stderr, "Unexpected SSL_shutdown result: %d != 0\n", ret);
       return false;
     }
-    ret = DoShutdown(ssl.get());
+    ret = DoShutdown(ssl);
   }
 
   if (ret != 1) {
@@ -2096,11 +2153,9 @@
     return false;
   }
 
-  if (SSL_total_renegotiations(ssl.get()) !=
-      config->expect_total_renegotiations) {
+  if (SSL_total_renegotiations(ssl) != config->expect_total_renegotiations) {
     fprintf(stderr, "Expected %d renegotiations, got %d\n",
-            config->expect_total_renegotiations,
-            SSL_total_renegotiations(ssl.get()));
+            config->expect_total_renegotiations, SSL_total_renegotiations(ssl));
     return false;
   }
 
@@ -2141,9 +2196,9 @@
     return 1;
   }
 
-  TestConfig initial_config, resume_config;
-  if (!ParseConfig(argc - 1, argv + 1, false, &initial_config) ||
-      !ParseConfig(argc - 1, argv + 1, true, &resume_config)) {
+  TestConfig initial_config, resume_config, retry_config;
+  if (!ParseConfig(argc - 1, argv + 1, &initial_config, &resume_config,
+                   &retry_config)) {
     return Usage(argv[0]);
   }
 
@@ -2172,8 +2227,8 @@
     }
 
     bssl::UniquePtr<SSL_SESSION> offer_session = std::move(session);
-    if (!DoExchange(&session, ssl_ctx.get(), config, is_resume,
-                    offer_session.get())) {
+    if (!DoConnection(&session, ssl_ctx.get(), config, &retry_config, is_resume,
+                      offer_session.get())) {
       fprintf(stderr, "Connection %d failed.\n", i + 1);
       ERR_print_errors_fp(stderr);
       return 1;
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 052f879..0a6648f 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -1175,6 +1175,14 @@
 	// the number of records or their content do not match.
 	ExpectEarlyData [][]byte
 
+	// ExpectLateEarlyData causes a TLS 1.3 server to read application
+	// data after the ServerFinished (assuming the server is able to
+	// derive the key under which the data is encrypted) before it
+	// sends the ClientFinished. It checks that the application data it
+	// reads matches what is provided in ExpectLateEarlyData and errors if
+	// the number of records or their content do not match.
+	ExpectLateEarlyData [][]byte
+
 	// SendHalfRTTData causes a TLS 1.3 server to send the provided
 	// data in application data records before reading the client's
 	// Finished message.
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 0eb64e7..fce0049 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -857,17 +857,8 @@
 			c.sendAlert(alertInternalError)
 			return c.in.setErrorLocked(errors.New("tls: ChangeCipherSpec requested after handshake complete"))
 		}
-	case recordTypeApplicationData:
-		if !c.handshakeComplete && !c.config.Bugs.ExpectFalseStart && len(c.config.Bugs.ExpectHalfRTTData) == 0 && len(c.config.Bugs.ExpectEarlyData) == 0 {
-			c.sendAlert(alertInternalError)
-			return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete"))
-		}
-	case recordTypeAlert, recordTypeHandshake:
-		// Looking for a close_notify or handshake message. Note: unlike
-		// a real implementation, this is not tolerant of additional
-		// records. See the documentation for ExpectCloseNotify.
-		// Post-handshake requests for handshake messages are allowed if
-		// the caller used ReadKeyUpdateACK.
+	case recordTypeApplicationData, recordTypeAlert, recordTypeHandshake:
+		break
 	}
 
 Again:
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index 603ca24..fd819f9 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -40,7 +40,12 @@
     "TLS13-EarlyData-NonZeroRTTSession-Server": "Trial decryption does not work with the NULL cipher.",
     "TLS13-EarlyData-SkipEndOfEarlyData": "Trial decryption does not work with the NULL cipher.",
     "TLS13-EarlyData-ALPNMismatch-Server": "Trial decryption does not work with the NULL cipher.",
+    "TLS13-EarlyData-ALPNOmitted1-Client": "Trial decryption does not work with the NULL cipher.",
+    "TLS13-EarlyData-ALPNOmitted2-Client": "Trial decryption does not work with the NULL cipher.",
     "TLS13-EarlyData-ALPNOmitted1-Server": "Trial decryption does not work with the NULL cipher.",
-    "TLS13-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher."
+    "TLS13-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher.",
+    "TLS13-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
+    "TLS13-EarlyData-Reject-Client": "Trial decryption does not work with the NULL cipher.",
+    "TLS13-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher."
   }
 }
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 8dc0446..3a182ec 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -680,7 +680,6 @@
 				}
 				c.in.freeBlock(c.input)
 				c.input = nil
-
 			}
 		} else {
 			c.skipEarlyData = true
@@ -880,6 +879,19 @@
 	}
 	c.flushHandshake()
 
+	if encryptedExtensions.extensions.hasEarlyData && !c.skipEarlyData {
+		for _, expectedMsg := range config.Bugs.ExpectLateEarlyData {
+			if err := c.readRecord(recordTypeApplicationData); err != nil {
+				return err
+			}
+			if !bytes.Equal(c.input.data[c.input.off:], expectedMsg) {
+				return errors.New("ExpectLateEarlyData: did not get expected message")
+			}
+			c.in.freeBlock(c.input)
+			c.input = nil
+		}
+	}
+
 	// The various secrets do not incorporate the client's final leg, so
 	// derive them now before updating the handshake context.
 	hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret())
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 10e728e..d9218f2 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -412,11 +412,14 @@
 	// which will be compared against the expected value.
 	testTLSUnique bool
 	// sendEmptyRecords is the number of consecutive empty records to send
-	// before and after the test message.
+	// before each test message.
 	sendEmptyRecords int
 	// sendWarningAlerts is the number of consecutive warning alerts to send
-	// before and after the test message.
+	// before each test message.
 	sendWarningAlerts int
+	// sendBogusAlertType, if true, causes a bogus alert of invalid type to
+	// be sent before each test message.
+	sendBogusAlertType bool
 	// sendKeyUpdates is the number of consecutive key updates to send
 	// before and after the test message.
 	sendKeyUpdates int
@@ -428,6 +431,11 @@
 	// expectPeerCertificate, if not nil, is the certificate chain the peer
 	// is expected to send.
 	expectPeerCertificate *Certificate
+	// shimPrefix is the prefix that the shim will send to the server.
+	shimPrefix string
+	// resumeShimPrefix is the prefix that the shim will send to the server on a
+	// resumption.
+	resumeShimPrefix string
 }
 
 var testCases []testCase
@@ -685,20 +693,26 @@
 		tlsConn.SendHalfHelloRequest()
 	}
 
-	shimPrefixPending := test.shimWritesFirst || test.readWithUnfinishedWrite
+	shimPrefix := test.shimPrefix
+	if isResume {
+		shimPrefix = test.resumeShimPrefix
+	}
+	if test.shimWritesFirst || test.readWithUnfinishedWrite {
+		shimPrefix = "hello"
+	}
 	if test.renegotiate > 0 {
 		// If readWithUnfinishedWrite is set, the shim prefix will be
 		// available later.
-		if shimPrefixPending && !test.readWithUnfinishedWrite {
-			var buf [5]byte
-			_, err := io.ReadFull(tlsConn, buf[:])
+		if shimPrefix != "" && !test.readWithUnfinishedWrite {
+			var buf = make([]byte, len(shimPrefix))
+			_, err := io.ReadFull(tlsConn, buf)
 			if err != nil {
 				return err
 			}
-			if string(buf[:]) != "hello" {
-				return fmt.Errorf("bad initial message")
+			if string(buf) != shimPrefix {
+				return fmt.Errorf("bad initial message %v vs %v", string(buf), shimPrefix)
 			}
-			shimPrefixPending = false
+			shimPrefix = ""
 		}
 
 		if test.renegotiateCiphers != nil {
@@ -750,6 +764,10 @@
 			tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage)
 		}
 
+		if test.sendBogusAlertType {
+			tlsConn.SendAlert(0x42, alertUnexpectedMessage)
+		}
+
 		testMessage := make([]byte, messageLen)
 		for i := range testMessage {
 			testMessage[i] = 0x42 ^ byte(j)
@@ -757,16 +775,16 @@
 		tlsConn.Write(testMessage)
 
 		// Consume the shim prefix if needed.
-		if shimPrefixPending {
-			var buf [5]byte
-			_, err := io.ReadFull(tlsConn, buf[:])
+		if shimPrefix != "" {
+			var buf = make([]byte, len(shimPrefix))
+			_, err := io.ReadFull(tlsConn, buf)
 			if err != nil {
 				return err
 			}
-			if string(buf[:]) != "hello" {
-				return fmt.Errorf("bad initial message")
+			if string(buf) != shimPrefix {
+				return fmt.Errorf("bad initial message %v vs %v", string(buf), shimPrefix)
 			}
-			shimPrefixPending = false
+			shimPrefix = ""
 		}
 
 		if test.shimShutsDown || test.expectMessageDropped {
@@ -1559,7 +1577,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":UNEXPECTED_RECORD:",
+			expectedError: ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			name: "AppDataBeforeHandshake-Empty",
@@ -1569,7 +1587,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":UNEXPECTED_RECORD:",
+			expectedError: ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			protocol: dtls,
@@ -1739,7 +1757,7 @@
 			},
 			shimWritesFirst: true,
 			shouldFail:      true,
-			expectedError:   ":UNEXPECTED_RECORD:",
+			expectedError:   ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			name: "FalseStart-SkipServerSecondLeg-Implicit",
@@ -1760,7 +1778,7 @@
 				"-advertise-alpn", "\x03foo",
 			},
 			shouldFail:    true,
-			expectedError: ":UNEXPECTED_RECORD:",
+			expectedError: ":APPLICATION_DATA_INSTEAD_OF_HANDSHAKE:",
 		},
 		{
 			testType:           serverTest,
@@ -2097,6 +2115,21 @@
 			expectedError:     ":TOO_MANY_WARNING_ALERTS:",
 		},
 		{
+			name:               "SendBogusAlertType",
+			sendBogusAlertType: true,
+			shouldFail:         true,
+			expectedError:      ":UNKNOWN_ALERT_TYPE:",
+			expectedLocalError: "remote error: illegal parameter",
+		},
+		{
+			protocol:           dtls,
+			name:               "SendBogusAlertType-DTLS",
+			sendBogusAlertType: true,
+			shouldFail:         true,
+			expectedError:      ":UNKNOWN_ALERT_TYPE:",
+			expectedLocalError: "remote error: illegal parameter",
+		},
+		{
 			name: "TooManyKeyUpdates",
 			config: Config{
 				MaxVersion: VersionTLS13,
@@ -3617,7 +3650,6 @@
 			resumeSession: true,
 		})
 
-		// TODO(svaldez): Send data on early data once implemented.
 		tests = append(tests, testCase{
 			testType: clientTest,
 			name:     "TLS13-EarlyData-Client",
@@ -3626,15 +3658,110 @@
 				MinVersion:       VersionTLS13,
 				MaxEarlyDataSize: 16384,
 			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				MaxEarlyDataSize: 16384,
+				Bugs: ProtocolBugs{
+					ExpectEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
+				},
+			},
 			resumeSession: true,
 			flags: []string{
 				"-enable-early-data",
 				"-expect-early-data-info",
 				"-expect-accept-early-data",
+				"-on-resume-shim-writes-first",
 			},
 		})
 
 		tests = append(tests, testCase{
+			testType: clientTest,
+			name:     "TLS13-EarlyData-TooMuchData-Client",
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				MaxEarlyDataSize: 2,
+			},
+			resumeConfig: &Config{
+				MaxVersion:       VersionTLS13,
+				MinVersion:       VersionTLS13,
+				MaxEarlyDataSize: 2,
+				Bugs: ProtocolBugs{
+					ExpectEarlyData: [][]byte{{'h', 'e'}},
+				},
+			},
+			resumeShimPrefix: "llo",
+			resumeSession:    true,
+			flags: []string{
+				"-enable-early-data",
+				"-expect-early-data-info",
+				"-expect-accept-early-data",
+				"-on-resume-shim-writes-first",
+			},
+		})
+
+		// Unfinished writes can only be tested when operations are async. EarlyData
+		// can't be tested as part of an ImplicitHandshake in this case since
+		// otherwise the early data will be sent as normal data.
+		if config.async && !config.implicitHandshake {
+			tests = append(tests, testCase{
+				testType: clientTest,
+				name:     "TLS13-EarlyData-UnfinishedWrite-Client",
+				config: Config{
+					MaxVersion:       VersionTLS13,
+					MinVersion:       VersionTLS13,
+					MaxEarlyDataSize: 16384,
+				},
+				resumeConfig: &Config{
+					MaxVersion:       VersionTLS13,
+					MinVersion:       VersionTLS13,
+					MaxEarlyDataSize: 16384,
+					Bugs: ProtocolBugs{
+						ExpectLateEarlyData: [][]byte{{'h', 'e', 'l', 'l', 'o'}},
+					},
+				},
+				resumeSession: true,
+				flags: []string{
+					"-enable-early-data",
+					"-expect-early-data-info",
+					"-expect-accept-early-data",
+					"-on-resume-read-with-unfinished-write",
+					"-on-resume-shim-writes-first",
+				},
+			})
+
+			// Rejected unfinished writes are discarded (from the
+			// perspective of the calling application) on 0-RTT
+			// reject.
+			tests = append(tests, testCase{
+				testType: clientTest,
+				name:     "TLS13-EarlyData-RejectUnfinishedWrite-Client",
+				config: Config{
+					MaxVersion:       VersionTLS13,
+					MinVersion:       VersionTLS13,
+					MaxEarlyDataSize: 16384,
+				},
+				resumeConfig: &Config{
+					MaxVersion:       VersionTLS13,
+					MinVersion:       VersionTLS13,
+					MaxEarlyDataSize: 16384,
+					Bugs: ProtocolBugs{
+						AlwaysRejectEarlyData: true,
+					},
+				},
+				resumeSession: true,
+				flags: []string{
+					"-enable-early-data",
+					"-expect-early-data-info",
+					"-expect-reject-early-data",
+					"-on-resume-read-with-unfinished-write",
+					"-on-resume-shim-writes-first",
+				},
+			})
+		}
+
+		tests = append(tests, testCase{
 			testType: serverTest,
 			name:     "TLS13-EarlyData-Server",
 			config: Config{
@@ -10264,7 +10391,7 @@
 
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-DataLessEarlyData-Reject-Client",
+		name:     "TLS13-EarlyData-Reject-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
@@ -10281,12 +10408,42 @@
 			"-enable-early-data",
 			"-expect-early-data-info",
 			"-expect-reject-early-data",
+			"-on-resume-shim-writes-first",
 		},
 	})
 
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-DataLessEarlyData-HRR-Client",
+		name:     "TLS13-EarlyData-RejectTicket-Client",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			Certificates:     []Certificate{rsaCertificate},
+		},
+		resumeConfig: &Config{
+			MaxVersion:             VersionTLS13,
+			MaxEarlyDataSize:       16384,
+			Certificates:           []Certificate{ecdsaP256Certificate},
+			SessionTicketsDisabled: true,
+		},
+		resumeSession:        true,
+		expectResumeRejected: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-early-data-info",
+			"-expect-reject-early-data",
+			"-on-resume-shim-writes-first",
+			"-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+			// Session tickets are disabled, so the runner will not send a ticket.
+			"-on-retry-expect-no-session",
+		},
+	})
+
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-EarlyData-HRR-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
@@ -10373,6 +10530,7 @@
 		flags: []string{
 			"-enable-early-data",
 			"-expect-early-data-info",
+			"-expect-reject-early-data",
 		},
 		shouldFail:    true,
 		expectedError: ":UNEXPECTED_EXTENSION:",
@@ -10385,7 +10543,7 @@
 	// that changed it.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-DataLessEarlyData-ALPNMismatch-Client",
+		name:     "TLS13-EarlyData-ALPNMismatch-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
@@ -10407,7 +10565,8 @@
 			"-expect-early-data-info",
 			"-expect-reject-early-data",
 			"-on-initial-expect-alpn", "foo",
-			"-on-resume-expect-alpn", "bar",
+			"-on-resume-expect-alpn", "foo",
+			"-on-retry-expect-alpn", "bar",
 		},
 	})
 
@@ -10415,7 +10574,7 @@
 	// ALPN was omitted from the first connection.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-DataLessEarlyData-ALPNOmitted1-Client",
+		name:     "TLS13-EarlyData-ALPNOmitted1-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
@@ -10432,7 +10591,9 @@
 			"-expect-early-data-info",
 			"-expect-reject-early-data",
 			"-on-initial-expect-alpn", "",
-			"-on-resume-expect-alpn", "foo",
+			"-on-resume-expect-alpn", "",
+			"-on-retry-expect-alpn", "foo",
+			"-on-resume-shim-writes-first",
 		},
 	})
 
@@ -10440,7 +10601,7 @@
 	// ALPN was omitted from the second connection.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-DataLessEarlyData-ALPNOmitted2-Client",
+		name:     "TLS13-EarlyData-ALPNOmitted2-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
@@ -10457,14 +10618,16 @@
 			"-expect-early-data-info",
 			"-expect-reject-early-data",
 			"-on-initial-expect-alpn", "foo",
-			"-on-resume-expect-alpn", "",
+			"-on-resume-expect-alpn", "foo",
+			"-on-retry-expect-alpn", "",
+			"-on-resume-shim-writes-first",
 		},
 	})
 
 	// Test that the client enforces ALPN match on 0-RTT accept.
 	testCases = append(testCases, testCase{
 		testType: clientTest,
-		name:     "TLS13-DataLessEarlyData-BadALPNMismatch-Client",
+		name:     "TLS13-EarlyData-BadALPNMismatch-Client",
 		config: Config{
 			MaxVersion:       VersionTLS13,
 			MaxEarlyDataSize: 16384,
@@ -10486,7 +10649,8 @@
 			"-enable-early-data",
 			"-expect-early-data-info",
 			"-on-initial-expect-alpn", "foo",
-			"-on-resume-expect-alpn", "bar",
+			"-on-resume-expect-alpn", "foo",
+			"-on-retry-expect-alpn", "bar",
 		},
 		shouldFail:    true,
 		expectedError: ":ALPN_MISMATCH_ON_EARLY_DATA:",
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index d7c3239..960240e 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -142,6 +142,7 @@
   { "-host-name", &TestConfig::host_name },
   { "-advertise-alpn", &TestConfig::advertise_alpn },
   { "-expect-alpn", &TestConfig::expected_alpn },
+  { "-expect-late-alpn", &TestConfig::expected_late_alpn },
   { "-expect-advertised-alpn", &TestConfig::expected_advertised_alpn },
   { "-select-alpn", &TestConfig::select_alpn },
   { "-psk", &TestConfig::psk },
@@ -192,110 +193,123 @@
   { "-verify-prefs", &TestConfig::verify_prefs },
 };
 
+bool ParseFlag(char *flag, int argc, char **argv, int *i,
+               bool skip, TestConfig *out_config) {
+  bool *bool_field = FindField(out_config, kBoolFlags, flag);
+  if (bool_field != NULL) {
+    if (!skip) {
+      *bool_field = true;
+    }
+    return true;
+  }
+
+  std::string *string_field = FindField(out_config, kStringFlags, flag);
+  if (string_field != NULL) {
+    *i = *i + 1;
+    if (*i >= argc) {
+      fprintf(stderr, "Missing parameter\n");
+      return false;
+    }
+    if (!skip) {
+      string_field->assign(argv[*i]);
+    }
+    return true;
+  }
+
+  std::string *base64_field = FindField(out_config, kBase64Flags, flag);
+  if (base64_field != NULL) {
+    *i = *i + 1;
+    if (*i >= argc) {
+      fprintf(stderr, "Missing parameter\n");
+      return false;
+    }
+    size_t len;
+    if (!EVP_DecodedLength(&len, strlen(argv[*i]))) {
+      fprintf(stderr, "Invalid base64: %s\n", argv[*i]);
+      return false;
+    }
+    std::unique_ptr<uint8_t[]> decoded(new uint8_t[len]);
+    if (!EVP_DecodeBase64(decoded.get(), &len, len,
+                          reinterpret_cast<const uint8_t *>(argv[*i]),
+                          strlen(argv[*i]))) {
+      fprintf(stderr, "Invalid base64: %s\n", argv[*i]);
+      return false;
+    }
+    if (!skip) {
+      base64_field->assign(reinterpret_cast<const char *>(decoded.get()),
+                           len);
+    }
+    return true;
+  }
+
+  int *int_field = FindField(out_config, kIntFlags, flag);
+  if (int_field) {
+    *i = *i + 1;
+    if (*i >= argc) {
+      fprintf(stderr, "Missing parameter\n");
+      return false;
+    }
+    if (!skip) {
+      *int_field = atoi(argv[*i]);
+    }
+    return true;
+  }
+
+  std::vector<int> *int_vector_field =
+      FindField(out_config, kIntVectorFlags, flag);
+  if (int_vector_field) {
+    *i = *i + 1;
+    if (*i >= argc) {
+      fprintf(stderr, "Missing parameter\n");
+      return false;
+    }
+
+    // Each instance of the flag adds to the list.
+    if (!skip) {
+      int_vector_field->push_back(atoi(argv[*i]));
+    }
+    return true;
+  }
+
+  fprintf(stderr, "Unknown argument: %s\n", flag);
+  return false;
+}
+
 const char kInit[] = "-on-initial";
 const char kResume[] = "-on-resume";
+const char kRetry[] = "-on-retry";
 
 }  // namespace
 
-bool ParseConfig(int argc, char **argv, bool is_resume,
-                 TestConfig *out_config) {
+bool ParseConfig(int argc, char **argv,
+                 TestConfig *out_initial,
+                 TestConfig *out_resume,
+                 TestConfig *out_retry) {
   for (int i = 0; i < argc; i++) {
     bool skip = false;
     char *flag = argv[i];
-    const char *prefix = is_resume ? kResume : kInit;
-    const char *opposite = is_resume ? kInit : kResume;
-    if (strncmp(flag, prefix, strlen(prefix)) == 0) {
-      flag = flag + strlen(prefix);
-      for (int j = 0; j < argc; j++) {
-        if (strcmp(argv[j], flag) == 0) {
-          fprintf(stderr, "Can't use default and prefixed arguments: %s\n",
-                  flag);
-          return false;
-        }
-      }
-    } else if (strncmp(flag, opposite, strlen(opposite)) == 0) {
-      flag = flag + strlen(opposite);
-      skip = true;
-    }
-
-    bool *bool_field = FindField(out_config, kBoolFlags, flag);
-    if (bool_field != NULL) {
-      if (!skip) {
-        *bool_field = true;
-      }
-      continue;
-    }
-
-    std::string *string_field = FindField(out_config, kStringFlags, flag);
-    if (string_field != NULL) {
-      i++;
-      if (i >= argc) {
-        fprintf(stderr, "Missing parameter\n");
+    if (strncmp(flag, kInit, strlen(kInit)) == 0) {
+      if (!ParseFlag(flag + strlen(kInit), argc, argv, &i, skip, out_initial)) {
         return false;
       }
-      if (!skip) {
-        string_field->assign(argv[i]);
+    } else if (strncmp(flag, kResume, strlen(kResume)) == 0) {
+      if (!ParseFlag(flag + strlen(kResume), argc, argv, &i, skip,
+                     out_resume)) {
+        return false;
       }
-      continue;
+    } else if (strncmp(flag, kRetry, strlen(kRetry)) == 0) {
+      if (!ParseFlag(flag + strlen(kRetry), argc, argv, &i, skip, out_retry)) {
+        return false;
+      }
+    } else {
+      int i_init = i;
+      int i_resume = i;
+      if (!ParseFlag(flag, argc, argv, &i_init, skip, out_initial) ||
+          !ParseFlag(flag, argc, argv, &i_resume, skip, out_resume) ||
+          !ParseFlag(flag, argc, argv, &i, skip, out_retry)) {
+        return false;
+      }
     }
-
-    std::string *base64_field = FindField(out_config, kBase64Flags, flag);
-    if (base64_field != NULL) {
-      i++;
-      if (i >= argc) {
-        fprintf(stderr, "Missing parameter\n");
-        return false;
-      }
-      size_t len;
-      if (!EVP_DecodedLength(&len, strlen(argv[i]))) {
-        fprintf(stderr, "Invalid base64: %s\n", argv[i]);
-        return false;
-      }
-      std::unique_ptr<uint8_t[]> decoded(new uint8_t[len]);
-      if (!EVP_DecodeBase64(decoded.get(), &len, len,
-                            reinterpret_cast<const uint8_t *>(argv[i]),
-                            strlen(argv[i]))) {
-        fprintf(stderr, "Invalid base64: %s\n", argv[i]);
-        return false;
-      }
-      if (!skip) {
-        base64_field->assign(reinterpret_cast<const char *>(decoded.get()),
-                             len);
-      }
-      continue;
-    }
-
-    int *int_field = FindField(out_config, kIntFlags, flag);
-    if (int_field) {
-      i++;
-      if (i >= argc) {
-        fprintf(stderr, "Missing parameter\n");
-        return false;
-      }
-      if (!skip) {
-        *int_field = atoi(argv[i]);
-      }
-      continue;
-    }
-
-    std::vector<int> *int_vector_field =
-        FindField(out_config, kIntVectorFlags, flag);
-    if (int_vector_field) {
-      i++;
-      if (i >= argc) {
-        fprintf(stderr, "Missing parameter\n");
-        return false;
-      }
-
-      // Each instance of the flag adds to the list.
-      if (!skip) {
-        int_vector_field->push_back(atoi(argv[i]));
-      }
-      continue;
-    }
-
-    fprintf(stderr, "Unknown argument: %s\n", flag);
-    return false;
   }
 
   return true;
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index c63a1cb..8bc8892 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -53,6 +53,7 @@
   std::string host_name;
   std::string advertise_alpn;
   std::string expected_alpn;
+  std::string expected_late_alpn;
   std::string expected_advertised_alpn;
   std::string select_alpn;
   bool decline_alpn = false;
@@ -141,7 +142,8 @@
   bool enable_ed25519 = false;
 };
 
-bool ParseConfig(int argc, char **argv, bool is_resume, TestConfig *out_config);
+bool ParseConfig(int argc, char **argv, TestConfig *out_initial,
+                 TestConfig *out_resume, TestConfig *out_retry);
 
 
 #endif  // HEADER_TEST_CONFIG
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index f44933f..6fdfb26 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -94,6 +94,12 @@
         hs->wait = ssl_hs_ok;
         return -1;
 
+      case ssl_hs_early_data_rejected:
+        ssl->rwstate = SSL_EARLY_DATA_REJECTED;
+        /* Cause |SSL_write| to start failing immediately. */
+        hs->can_early_write = 0;
+        return -1;
+
       case ssl_hs_ok:
         break;
     }
@@ -378,9 +384,9 @@
     goto err;
   }
 
-  int al;
-  if (!tls12_check_peer_sigalg(ssl, &al, signature_algorithm)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     goto err;
   }
   hs->new_session->peer_signature_algorithm = signature_algorithm;
@@ -527,8 +533,7 @@
   return 0;
 }
 
-enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs,
-                                                           int is_first_run) {
+enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   enum ssl_private_key_result_t ret = ssl_private_key_failure;
   uint8_t *msg = NULL;
@@ -558,20 +563,15 @@
     goto err;
   }
 
-  enum ssl_private_key_result_t sign_result;
-  if (is_first_run) {
-    if (!tls13_get_cert_verify_signature_input(
-            hs, &msg, &msg_len,
-            ssl->server ? ssl_cert_verify_server : ssl_cert_verify_client)) {
-      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 (!tls13_get_cert_verify_signature_input(
+          hs, &msg, &msg_len,
+          ssl->server ? ssl_cert_verify_server : ssl_cert_verify_client)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    goto err;
   }
 
+  enum ssl_private_key_result_t sign_result = ssl_private_key_sign(
+      hs, sig, &sig_len, max_sig_len, signature_algorithm, msg, msg_len);
   if (sign_result != ssl_private_key_success) {
     ret = sign_result;
     goto err;
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index 6de51e5..92ea4f8 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -33,6 +33,7 @@
   state_send_second_client_hello,
   state_process_server_hello,
   state_process_encrypted_extensions,
+  state_continue_second_server_flight,
   state_process_certificate_request,
   state_process_server_certificate,
   state_process_server_certificate_verify,
@@ -40,7 +41,6 @@
   state_send_end_of_early_data,
   state_send_client_certificate,
   state_send_client_certificate_verify,
-  state_complete_client_certificate_verify,
   state_complete_second_flight,
   state_done,
 };
@@ -141,13 +141,15 @@
 
   hs->received_hello_retry_request = 1;
   hs->tls13_state = state_send_second_client_hello;
+  /* 0-RTT is rejected if we receive a HelloRetryRequest. */
+  if (hs->in_early_data) {
+    return ssl_hs_early_data_rejected;
+  }
   return ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_send_second_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  /* TODO(svaldez): Ensure that we set can_early_write to false since 0-RTT is
-   * rejected if we receive a HelloRetryRequest. */
   if (!ssl->method->set_write_state(ssl, NULL) ||
       !ssl_write_client_hello(hs)) {
     return ssl_hs_error;
@@ -259,6 +261,7 @@
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
+    ssl_set_session(ssl, NULL);
 
     /* Resumption incorporates fresh key material, so refresh the timeout. */
     ssl_session_renew_timeout(ssl, hs->new_session,
@@ -358,9 +361,9 @@
   }
 
   if (ssl->early_data_accepted) {
-    if (ssl->session->cipher != hs->new_session->cipher ||
-        ssl->session->early_alpn_len != ssl->s3->alpn_selected_len ||
-        OPENSSL_memcmp(ssl->session->early_alpn, ssl->s3->alpn_selected,
+    if (hs->early_session->cipher != hs->new_session->cipher ||
+        hs->early_session->early_alpn_len != ssl->s3->alpn_selected_len ||
+        OPENSSL_memcmp(hs->early_session->early_alpn, ssl->s3->alpn_selected,
                        ssl->s3->alpn_selected_len) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ALPN_MISMATCH_ON_EARLY_DATA);
       return ssl_hs_error;
@@ -371,15 +374,18 @@
     }
   }
 
-  /* Release offered session now that it is no longer needed. */
-  if (ssl->s3->session_reused) {
-    ssl_set_session(ssl, NULL);
-  }
-
   if (!ssl_hash_current_message(hs)) {
     return ssl_hs_error;
   }
 
+  hs->tls13_state = state_continue_second_server_flight;
+  if (hs->in_early_data && !ssl->early_data_accepted) {
+    return ssl_hs_early_data_rejected;
+  }
+  return ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_continue_second_server_flight(SSL_HANDSHAKE *hs) {
   hs->tls13_state = state_process_certificate_request;
   return ssl_hs_read_message;
 }
@@ -485,11 +491,13 @@
 
 static enum ssl_hs_wait_t do_send_end_of_early_data(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  /* TODO(svaldez): Stop sending early data. */
-  if (ssl->early_data_accepted &&
-      !ssl->method->add_alert(ssl, SSL3_AL_WARNING,
-                              TLS1_AD_END_OF_EARLY_DATA)) {
-    return ssl_hs_error;
+
+  if (ssl->early_data_accepted) {
+    hs->can_early_write = 0;
+    if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING,
+                                TLS1_AD_END_OF_EARLY_DATA)) {
+      return ssl_hs_error;
+    }
   }
 
   if (hs->early_data_offered &&
@@ -534,8 +542,7 @@
   return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs,
-                                                            int is_first_run) {
+static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   /* Don't send CertificateVerify if there is no certificate. */
   if (!ssl_has_certificate(ssl)) {
@@ -543,13 +550,13 @@
     return ssl_hs_ok;
   }
 
-  switch (tls13_add_certificate_verify(hs, is_first_run)) {
+  switch (tls13_add_certificate_verify(hs)) {
     case ssl_private_key_success:
       hs->tls13_state = state_complete_second_flight;
       return ssl_hs_ok;
 
     case ssl_private_key_retry:
-      hs->tls13_state = state_complete_client_certificate_verify;
+      hs->tls13_state = state_send_client_certificate_verify;
       return ssl_hs_private_key_operation;
 
     case ssl_private_key_failure:
@@ -618,6 +625,9 @@
       case state_process_encrypted_extensions:
         ret = do_process_encrypted_extensions(hs);
         break;
+      case state_continue_second_server_flight:
+        ret = do_continue_second_server_flight(hs);
+        break;
       case state_process_certificate_request:
         ret = do_process_certificate_request(hs);
         break;
@@ -637,10 +647,7 @@
         ret = do_send_client_certificate(hs);
         break;
       case state_send_client_certificate_verify:
-        ret = do_send_client_certificate_verify(hs, 1 /* first run */);
-        break;
-      case state_complete_client_certificate_verify:
-        ret = do_send_client_certificate_verify(hs, 0 /* complete */);
+        ret = do_send_client_certificate_verify(hs);
         break;
       case state_complete_second_flight:
         ret = do_complete_second_flight(hs);
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 9e8513c..0a5e1a2 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -36,7 +36,6 @@
   state_process_second_client_hello,
   state_send_server_hello,
   state_send_server_certificate_verify,
-  state_complete_server_certificate_verify,
   state_send_server_finished,
   state_read_second_client_flight,
   state_process_end_of_early_data,
@@ -583,15 +582,14 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL_HANDSHAKE *hs,
-                                                            int is_first_run) {
-  switch (tls13_add_certificate_verify(hs, is_first_run)) {
+static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL_HANDSHAKE *hs) {
+  switch (tls13_add_certificate_verify(hs)) {
     case ssl_private_key_success:
       hs->tls13_state = state_send_server_finished;
       return ssl_hs_ok;
 
     case ssl_private_key_retry:
-      hs->tls13_state = state_complete_server_certificate_verify;
+      hs->tls13_state = state_send_server_certificate_verify;
       return ssl_hs_private_key_operation;
 
     case ssl_private_key_failure:
@@ -657,6 +655,7 @@
     }
     hs->can_early_write = 1;
     hs->can_early_read = 1;
+    hs->in_early_data = 1;
     hs->tls13_state = state_process_end_of_early_data;
     return ssl_hs_read_end_of_early_data;
   }
@@ -801,10 +800,7 @@
         ret = do_send_server_hello(hs);
         break;
       case state_send_server_certificate_verify:
-        ret = do_send_server_certificate_verify(hs, 1 /* first run */);
-      break;
-      case state_complete_server_certificate_verify:
-        ret = do_send_server_certificate_verify(hs, 0 /* complete */);
+        ret = do_send_server_certificate_verify(hs);
         break;
       case state_send_server_finished:
         ret = do_send_server_finished(hs);
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index e67e0b4..a5bbe93 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -398,14 +398,12 @@
   out[0] = type;
   out[1] = wire_version >> 8;
   out[2] = wire_version & 0xff;
-  out += 3;
-  max_out -= 3;
 
   /* Write the ciphertext, leaving two bytes for the length. */
   size_t ciphertext_len;
-  if (!SSL_AEAD_CTX_seal(ssl->s3->aead_write_ctx, out + 2, &ciphertext_len,
-                         max_out - 2, type, wire_version,
-                         ssl->s3->write_sequence, in, in_len) ||
+  if (!SSL_AEAD_CTX_seal(ssl->s3->aead_write_ctx, out + SSL3_RT_HEADER_LENGTH,
+                         &ciphertext_len, max_out - SSL3_RT_HEADER_LENGTH, type,
+                         wire_version, ssl->s3->write_sequence, in, in_len) ||
       !ssl_record_sequence_update(ssl->s3->write_sequence, 8)) {
     return 0;
   }
@@ -415,8 +413,8 @@
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return 0;
   }
-  out[0] = ciphertext_len >> 8;
-  out[1] = ciphertext_len & 0xff;
+  out[3] = ciphertext_len >> 8;
+  out[4] = ciphertext_len & 0xff;
 
   *out_len = SSL3_RT_HEADER_LENGTH + ciphertext_len;