Hand back ECDHE split handshakes after the first server message.

This changes the contract for split handshakes such that on the
receiving side, the connection is to be driven until it returns
|SSL_ERROR_HANDBACK|, rather than until SSL_do_handshake() returns
success.

Change-Id: Idd1ebfbd943d88474d7c934f4c0ae757ff3c0f37
Reviewed-on: https://boringssl-review.googlesource.com/26864
Commit-Queue: Matt Braithwaite <mab@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 8e7d5f5..2cc34cf 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -535,6 +535,7 @@
 #define SSL_ERROR_WANT_CERTIFICATE_VERIFY 16
 
 #define SSL_ERROR_HANDOFF 17
+#define SSL_ERROR_HANDBACK 18
 
 // SSL_set_mtu sets the |ssl|'s MTU in DTLS to |mtu|. It returns one on success
 // and zero on failure.
@@ -3926,6 +3927,7 @@
 #define SSL_EARLY_DATA_REJECTED 11
 #define SSL_CERTIFICATE_VERIFY 12
 #define SSL_HANDOFF 13
+#define SSL_HANDBACK 14
 
 // SSL_want returns one of the above values to determine what the most recent
 // operation on |ssl| was blocked on. Use |SSL_get_error| instead.
@@ -4478,10 +4480,10 @@
 // state of the connection.
 //
 // Elsewhere, a fresh |SSL| can be used with |SSL_apply_handoff| to continue
-// the connection. The connection from the client is fed into this |SSL| until
-// the handshake completes normally. At this point (and only at this point),
-// |SSL_serialize_handback| can be called to serialize the result of the
-// handshake.
+// the connection. The connection from the client is fed into this |SSL|, and
+// the handshake resumed. When the handshake stops again and |SSL_get_error|
+// indicates |SSL_ERROR_HANDBACK|, |SSL_serialize_handback| should be called to
+// serialize the state of the handshake again.
 //
 // Back at the first location, a fresh |SSL| can be used with
 // |SSL_apply_handback|. Then the client's connection can be processed mostly
@@ -4489,7 +4491,7 @@
 //
 // Lastly, when a connection is in the handoff state, whether or not
 // |SSL_serialize_handoff| is called, |SSL_decline_handoff| will move it back
-// into a normal state where the connection can procede without impact.
+// into a normal state where the connection can proceed without impact.
 //
 // WARNING: Currently only works with TLS 1.0–1.2.
 // WARNING: The serialisation formats are not yet stable: version skew may be
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index b19d443..dd73c83 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -93,17 +93,17 @@
   if (CBS_len(&transcript) != 0) {
     s3->hs->transcript.Update(transcript);
     s3->is_v2_hello = true;
-    ssl_do_msg_callback(ssl, 0 /* read */, 0 /* V2ClientHello */, transcript);
   }
+  ssl->handback = true;
 
   return true;
 }
 
 bool SSL_serialize_handback(const SSL *ssl, CBB *out) {
   if (!ssl->server ||
-      !ssl->s3->initial_handshake_complete ||
-      ssl->method->is_dtls ||
-      ssl->version < TLS1_VERSION) {
+      (ssl->s3->hs->state != state12_finish_server_handshake &&
+       ssl->s3->hs->state != state12_read_client_certificate) ||
+      ssl->method->is_dtls || ssl->version < TLS1_VERSION) {
     return false;
   }
 
@@ -115,14 +115,22 @@
 
   size_t iv_len = 0;
   const uint8_t *read_iv = nullptr, *write_iv = nullptr;
-  if (ssl->version == TLS1_VERSION &&
-      SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) &&
-      (!s3->aead_read_ctx->GetIV(&read_iv, &iv_len) ||
-       !s3->aead_write_ctx->GetIV(&write_iv, &iv_len))) {
-    return false;
+  Span<const uint8_t> transcript;
+  if (ssl->s3->hs->state == state12_finish_server_handshake) {
+    if (ssl->version == TLS1_VERSION &&
+        SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) &&
+        (!s3->aead_read_ctx->GetIV(&read_iv, &iv_len) ||
+         !s3->aead_write_ctx->GetIV(&write_iv, &iv_len))) {
+      return false;
+    }
+  } else {
+    transcript = s3->hs->transcript.buffer();
   }
 
-  CBB seq;
+  // TODO(mab): make sure everything is serialized.
+  CBB seq, key_share;
+  SSL_SESSION *session =
+      s3->session_reused ? ssl->session : s3->hs->new_session.get();
   if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&seq, kHandbackVersion) ||
       !CBB_add_asn1_uint64(&seq, ssl->version) ||
@@ -142,7 +150,7 @@
       !CBB_add_asn1_bool(&seq, s3->session_reused) ||
       !CBB_add_asn1_bool(&seq, s3->send_connection_binding) ||
       !CBB_add_asn1_bool(&seq, s3->tlsext_channel_id_valid) ||
-      !ssl_session_serialize(s3->established_session.get(), &seq) ||
+      !ssl_session_serialize(session, &seq) ||
       !CBB_add_asn1_octet_string(&seq, s3->next_proto_negotiated.data(),
                                  s3->next_proto_negotiated.size()) ||
       !CBB_add_asn1_octet_string(&seq, s3->alpn_selected.data(),
@@ -158,11 +166,22 @@
       !CBB_add_asn1_bool(&seq, ssl->quiet_shutdown) ||
       !CBB_add_asn1_bool(&seq, ssl->tlsext_channel_id_enabled) ||
       !CBB_add_asn1_bool(&seq, ssl->retain_only_sha256_of_client_certs) ||
-      !CBB_flush(out)) {
+      !CBB_add_asn1_bool(&seq, ssl->token_binding_negotiated) ||
+      !CBB_add_asn1_uint64(&seq, ssl->negotiated_token_binding_param) ||
+      !CBB_add_asn1_bool(&seq, s3->hs->next_proto_neg_seen) ||
+      !CBB_add_asn1_bool(&seq, s3->hs->cert_request) ||
+      !CBB_add_asn1_bool(&seq, s3->hs->extended_master_secret) ||
+      !CBB_add_asn1_bool(&seq, s3->hs->ticket_expected) ||
+      !CBB_add_asn1_uint64(&seq, SSL_CIPHER_get_id(s3->hs->new_cipher)) ||
+      !CBB_add_asn1_octet_string(&seq, transcript.data(), transcript.size()) ||
+      !CBB_add_asn1(&seq, &key_share, CBS_ASN1_SEQUENCE)) {
     return false;
   }
-
-  return true;
+  if (ssl->s3->hs->state == state12_read_client_certificate &&
+      !s3->hs->key_share->Serialize(&key_share)) {
+    return false;
+  }
+  return CBB_flush(out);
 }
 
 bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) {
@@ -173,11 +192,16 @@
 
   SSL3_STATE *const s3 = ssl->s3;
   uint64_t handback_version, version, conf_max_version, conf_min_version,
-      max_send_fragment, options, mode, max_cert_list;
+      max_send_fragment, options, mode, max_cert_list,
+      negotiated_token_binding_param, cipher;
+
   CBS seq, read_seq, write_seq, server_rand, client_rand, read_iv, write_iv,
-      next_proto, alpn, hostname, channel_id;
-  int session_reused, send_connection_binding, channel_id_valid,
-      quiet_shutdown, channel_id_enabled, retain_only_sha256;
+      next_proto, alpn, hostname, channel_id, transcript, key_share;
+  int session_reused, send_connection_binding, channel_id_valid, quiet_shutdown,
+      channel_id_enabled, retain_only_sha256, cert_request,
+      extended_master_secret, ticket_expected, token_binding_negotiated,
+      next_proto_neg_seen;
+  SSL_SESSION *session = nullptr;
 
   CBS handback_cbs(handback);
   if (!CBS_get_asn1(&handback_cbs, &seq, CBS_ASN1_SEQUENCE) ||
@@ -210,11 +234,19 @@
     return false;
   }
 
-  s3->established_session =
-      SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool);
+  s3->hs = ssl_handshake_new(ssl);
+  if (session_reused) {
+    ssl->session =
+        SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool)
+            .release();
+    session = ssl->session;
+  } else {
+    s3->hs->new_session =
+        SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool);
+    session = s3->hs->new_session.get();
+  }
 
-  if (!s3->established_session ||
-      !CBS_get_asn1(&seq, &next_proto, CBS_ASN1_OCTETSTRING) ||
+  if (!session || !CBS_get_asn1(&seq, &next_proto, CBS_ASN1_OCTETSTRING) ||
       !CBS_get_asn1(&seq, &alpn, CBS_ASN1_OCTETSTRING) ||
       !CBS_get_asn1(&seq, &hostname, CBS_ASN1_OCTETSTRING) ||
       !CBS_get_asn1(&seq, &channel_id, CBS_ASN1_OCTETSTRING) ||
@@ -226,7 +258,22 @@
       !CBS_get_asn1_uint64(&seq, &max_cert_list) ||
       !CBS_get_asn1_bool(&seq, &quiet_shutdown) ||
       !CBS_get_asn1_bool(&seq, &channel_id_enabled) ||
-      !CBS_get_asn1_bool(&seq, &retain_only_sha256)) {
+      !CBS_get_asn1_bool(&seq, &retain_only_sha256) ||
+      !CBS_get_asn1_bool(&seq, &token_binding_negotiated) ||
+      !CBS_get_asn1_uint64(&seq, &negotiated_token_binding_param) ||
+      !CBS_get_asn1_bool(&seq, &next_proto_neg_seen) ||
+      !CBS_get_asn1_bool(&seq, &cert_request) ||
+      !CBS_get_asn1_bool(&seq, &extended_master_secret) ||
+      !CBS_get_asn1_bool(&seq, &ticket_expected) ||
+      !CBS_get_asn1_uint64(&seq, &cipher)) {
+    return false;
+  }
+  if ((s3->hs->new_cipher =
+           SSL_get_cipher_by_value(static_cast<uint16_t>(cipher))) == nullptr) {
+    return false;
+  }
+  if (!CBS_get_asn1(&seq, &transcript, CBS_ASN1_OCTETSTRING) ||
+      !CBS_get_asn1(&seq, &key_share, CBS_ASN1_SEQUENCE)) {
     return false;
   }
 
@@ -240,9 +287,9 @@
   ssl->mode = mode;
   ssl->max_cert_list = max_cert_list;
 
-  s3->hs.reset();
   s3->have_version = true;
-  s3->initial_handshake_complete = true;
+  s3->hs->state = CBS_len(&transcript) == 0 ? state12_finish_server_handshake
+                                            : state12_read_client_certificate;
   s3->session_reused = session_reused;
   s3->send_connection_binding = send_connection_binding;
   s3->tlsext_channel_id_valid = channel_id_valid;
@@ -263,23 +310,44 @@
   ssl->quiet_shutdown = quiet_shutdown;
   ssl->tlsext_channel_id_enabled = channel_id_enabled;
   ssl->retain_only_sha256_of_client_certs = retain_only_sha256;
+  ssl->token_binding_negotiated = token_binding_negotiated;
+  ssl->negotiated_token_binding_param =
+      static_cast<uint8_t>(negotiated_token_binding_param);
+  s3->hs->next_proto_neg_seen = next_proto_neg_seen;
+  s3->hs->wait = ssl_hs_flush;
+  s3->hs->extended_master_secret = extended_master_secret;
+  s3->hs->ticket_expected = ticket_expected;
+  s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
+  s3->hs->cert_request = cert_request;
 
-  Array<uint8_t> key_block;
-  if (!tls1_configure_aead(ssl, evp_aead_open, &key_block,
-                           s3->established_session->cipher, read_iv) ||
-      !tls1_configure_aead(ssl, evp_aead_seal, &key_block,
-                           s3->established_session->cipher, write_iv)) {
-    return false;
+  if (s3->hs->state == state12_finish_server_handshake) {
+    Array<uint8_t> key_block;
+    if (!tls1_configure_aead(ssl, evp_aead_open, &key_block, session->cipher,
+                             read_iv) ||
+        !tls1_configure_aead(ssl, evp_aead_seal, &key_block, session->cipher,
+                             write_iv)) {
+      return false;
+    }
+
+    if (!CBS_copy_bytes(&read_seq, s3->read_sequence,
+                        sizeof(s3->read_sequence)) ||
+        !CBS_copy_bytes(&write_seq, s3->write_sequence,
+                        sizeof(s3->write_sequence))) {
+      return false;
+    }
+  } else {
+    if (!s3->hs->transcript.Init() ||
+        !s3->hs->transcript.InitHash(ssl_protocol_version(ssl),
+                                     s3->hs->new_cipher) ||
+        !s3->hs->transcript.Update(transcript)) {
+      return false;
+    }
+    if ((s3->hs->key_share = SSLKeyShare::Create(&key_share)) == nullptr) {
+      return false;
+    }
   }
 
-  if (!CBS_copy_bytes(&read_seq, s3->read_sequence,
-                      sizeof(s3->read_sequence)) ||
-      !CBS_copy_bytes(&write_seq, s3->write_sequence,
-                      sizeof(s3->write_sequence))) {
-    return false;
-  }
-
-  return true;
+  return CBS_len(&seq) == 0;
 }
 
 }  // namespace bssl
diff --git a/ssl/handshake.cc b/ssl/handshake.cc
index 6432424..00a2cc5 100644
--- a/ssl/handshake.cc
+++ b/ssl/handshake.cc
@@ -565,6 +565,11 @@
         hs->wait = ssl_hs_ok;
         return -1;
 
+      case ssl_hs_handback:
+        ssl->s3->rwstate = SSL_HANDBACK;
+        hs->wait = ssl_hs_handback;
+        return -1;
+
       case ssl_hs_x509_lookup:
         ssl->s3->rwstate = SSL_X509_LOOKUP;
         hs->wait = ssl_hs_ok;
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index fa8a241..5f2f41f 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -172,30 +172,6 @@
 
 namespace bssl {
 
-enum ssl_server_hs_state_t {
-  state_start_accept = 0,
-  state_read_client_hello,
-  state_select_certificate,
-  state_tls13,
-  state_select_parameters,
-  state_send_server_hello,
-  state_send_server_certificate,
-  state_send_server_key_exchange,
-  state_send_server_hello_done,
-  state_read_client_certificate,
-  state_verify_client_certificate,
-  state_read_client_key_exchange,
-  state_read_client_certificate_verify,
-  state_read_change_cipher_spec,
-  state_process_change_cipher_spec,
-  state_read_next_proto,
-  state_read_channel_id,
-  state_read_client_finished,
-  state_send_server_finished,
-  state_finish_server_handshake,
-  state_done,
-};
-
 int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello,
                                            uint16_t id) {
   CBS cipher_suites;
@@ -425,7 +401,7 @@
 
 static enum ssl_hs_wait_t do_start_accept(SSL_HANDSHAKE *hs) {
   ssl_do_info_callback(hs->ssl, SSL_CB_HANDSHAKE_START, 1);
-  hs->state = state_read_client_hello;
+  hs->state = state12_read_client_hello;
   return ssl_hs_ok;
 }
 
@@ -505,7 +481,7 @@
     return ssl_hs_error;
   }
 
-  hs->state = state_select_certificate;
+  hs->state = state12_select_certificate;
   return ssl_hs_ok;
 }
 
@@ -536,7 +512,7 @@
 
   if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     // Jump to the TLS 1.3 state machine.
-    hs->state = state_tls13;
+    hs->state = state12_tls13;
     return ssl_hs_ok;
   }
 
@@ -555,14 +531,14 @@
     return ssl_hs_error;
   }
 
-  hs->state = state_select_parameters;
+  hs->state = state12_select_parameters;
   return ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_tls13(SSL_HANDSHAKE *hs) {
   enum ssl_hs_wait_t wait = tls13_server_handshake(hs);
   if (wait == ssl_hs_ok) {
-    hs->state = state_finish_server_handshake;
+    hs->state = state12_finish_server_handshake;
     return ssl_hs_ok;
   }
 
@@ -672,14 +648,15 @@
     return ssl_hs_error;
   }
 
-  // Release the handshake buffer if client authentication isn't required.
-  if (!hs->cert_request) {
+  // Handback includes the whole handshake transcript, so we cannot free the
+  // transcript buffer in the handback case.
+  if (!hs->cert_request && !hs->ssl->handback) {
     hs->transcript.FreeBuffer();
   }
 
   ssl->method->next_message(ssl);
 
-  hs->state = state_send_server_hello;
+  hs->state = state12_send_server_hello;
   return ssl_hs_ok;
 }
 
@@ -744,9 +721,9 @@
   }
 
   if (ssl->session != NULL) {
-    hs->state = state_send_server_finished;
+    hs->state = state12_send_server_finished;
   } else {
-    hs->state = state_send_server_certificate;
+    hs->state = state12_send_server_certificate;
   }
   return ssl_hs_ok;
 }
@@ -835,7 +812,7 @@
     }
   }
 
-  hs->state = state_send_server_key_exchange;
+  hs->state = state12_send_server_key_exchange;
   return ssl_hs_ok;
 }
 
@@ -843,7 +820,7 @@
   SSL *const ssl = hs->ssl;
 
   if (hs->server_params.size() == 0) {
-    hs->state = state_send_server_hello_done;
+    hs->state = state12_send_server_hello_done;
     return ssl_hs_ok;
   }
 
@@ -907,7 +884,7 @@
 
   hs->server_params.Reset();
 
-  hs->state = state_send_server_hello_done;
+  hs->state = state12_send_server_hello_done;
   return ssl_hs_ok;
 }
 
@@ -942,15 +919,18 @@
     return ssl_hs_error;
   }
 
-  hs->state = state_read_client_certificate;
+  hs->state = state12_read_client_certificate;
   return ssl_hs_flush;
 }
 
 static enum ssl_hs_wait_t do_read_client_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
+  if (ssl->handback && hs->new_cipher->algorithm_mkey == SSL_kECDHE) {
+     return ssl_hs_handback;
+  }
   if (!hs->cert_request) {
-    hs->state = state_verify_client_certificate;
+    hs->state = state12_verify_client_certificate;
     return ssl_hs_ok;
   }
 
@@ -973,7 +953,7 @@
       // OpenSSL returns X509_V_OK when no certificates are received. This is
       // classed by them as a bug, but it's assumed by at least NGINX.
       hs->new_session->verify_result = X509_V_OK;
-      hs->state = state_verify_client_certificate;
+      hs->state = state12_verify_client_certificate;
       return ssl_hs_ok;
     }
 
@@ -1035,7 +1015,7 @@
   }
 
   ssl->method->next_message(ssl);
-  hs->state = state_verify_client_certificate;
+  hs->state = state12_verify_client_certificate;
   return ssl_hs_ok;
 }
 
@@ -1051,7 +1031,7 @@
     }
   }
 
-  hs->state = state_read_client_key_exchange;
+  hs->state = state12_read_client_key_exchange;
   return ssl_hs_ok;
 }
 
@@ -1262,7 +1242,7 @@
   hs->new_session->extended_master_secret = hs->extended_master_secret;
 
   ssl->method->next_message(ssl);
-  hs->state = state_read_client_certificate_verify;
+  hs->state = state12_read_client_certificate_verify;
   return ssl_hs_ok;
 }
 
@@ -1273,7 +1253,7 @@
   // CertificateVerify is required if and only if there's a client certificate.
   if (!hs->peer_pubkey) {
     hs->transcript.FreeBuffer();
-    hs->state = state_read_change_cipher_spec;
+    hs->state = state12_read_change_cipher_spec;
     return ssl_hs_ok;
   }
 
@@ -1358,12 +1338,12 @@
   }
 
   ssl->method->next_message(ssl);
-  hs->state = state_read_change_cipher_spec;
+  hs->state = state12_read_change_cipher_spec;
   return ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_read_change_cipher_spec(SSL_HANDSHAKE *hs) {
-  hs->state = state_process_change_cipher_spec;
+  hs->state = state12_process_change_cipher_spec;
   return ssl_hs_read_change_cipher_spec;
 }
 
@@ -1372,7 +1352,7 @@
     return ssl_hs_error;
   }
 
-  hs->state = state_read_next_proto;
+  hs->state = state12_read_next_proto;
   return ssl_hs_ok;
 }
 
@@ -1380,7 +1360,7 @@
   SSL *const ssl = hs->ssl;
 
   if (!hs->next_proto_neg_seen) {
-    hs->state = state_read_channel_id;
+    hs->state = state12_read_channel_id;
     return ssl_hs_ok;
   }
 
@@ -1408,7 +1388,7 @@
   }
 
   ssl->method->next_message(ssl);
-  hs->state = state_read_channel_id;
+  hs->state = state12_read_channel_id;
   return ssl_hs_ok;
 }
 
@@ -1416,7 +1396,7 @@
   SSL *const ssl = hs->ssl;
 
   if (!ssl->s3->tlsext_channel_id_valid) {
-    hs->state = state_read_client_finished;
+    hs->state = state12_read_client_finished;
     return ssl_hs_ok;
   }
 
@@ -1432,7 +1412,7 @@
   }
 
   ssl->method->next_message(ssl);
-  hs->state = state_read_client_finished;
+  hs->state = state12_read_client_finished;
   return ssl_hs_ok;
 }
 
@@ -1444,9 +1424,9 @@
   }
 
   if (ssl->session != NULL) {
-    hs->state = state_finish_server_handshake;
+    hs->state = state12_finish_server_handshake;
   } else {
-    hs->state = state_send_server_finished;
+    hs->state = state12_send_server_finished;
   }
 
   // If this is a full handshake with ChannelID then record the handshake
@@ -1501,9 +1481,9 @@
   }
 
   if (ssl->session != NULL) {
-    hs->state = state_read_change_cipher_spec;
+    hs->state = state12_read_change_cipher_spec;
   } else {
-    hs->state = state_finish_server_handshake;
+    hs->state = state12_finish_server_handshake;
   }
   return ssl_hs_flush;
 }
@@ -1511,6 +1491,10 @@
 static enum ssl_hs_wait_t do_finish_server_handshake(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
+  if (ssl->handback) {
+    return ssl_hs_handback;
+  }
+
   ssl->method->on_handshake_complete(ssl);
 
   // If we aren't retaining peer certificates then we can discard it now.
@@ -1532,77 +1516,77 @@
   ssl->s3->initial_handshake_complete = true;
   ssl_update_cache(hs, SSL_SESS_CACHE_SERVER);
 
-  hs->state = state_done;
+  hs->state = state12_done;
   return ssl_hs_ok;
 }
 
 enum ssl_hs_wait_t ssl_server_handshake(SSL_HANDSHAKE *hs) {
-  while (hs->state != state_done) {
+  while (hs->state != state12_done) {
     enum ssl_hs_wait_t ret = ssl_hs_error;
-    enum ssl_server_hs_state_t state =
-        static_cast<enum ssl_server_hs_state_t>(hs->state);
+    enum tls12_server_hs_state_t state =
+        static_cast<enum tls12_server_hs_state_t>(hs->state);
     switch (state) {
-      case state_start_accept:
+      case state12_start_accept:
         ret = do_start_accept(hs);
         break;
-      case state_read_client_hello:
+      case state12_read_client_hello:
         ret = do_read_client_hello(hs);
         break;
-      case state_select_certificate:
+      case state12_select_certificate:
         ret = do_select_certificate(hs);
         break;
-      case state_tls13:
+      case state12_tls13:
         ret = do_tls13(hs);
         break;
-      case state_select_parameters:
+      case state12_select_parameters:
         ret = do_select_parameters(hs);
         break;
-      case state_send_server_hello:
+      case state12_send_server_hello:
         ret = do_send_server_hello(hs);
         break;
-      case state_send_server_certificate:
+      case state12_send_server_certificate:
         ret = do_send_server_certificate(hs);
         break;
-      case state_send_server_key_exchange:
+      case state12_send_server_key_exchange:
         ret = do_send_server_key_exchange(hs);
         break;
-      case state_send_server_hello_done:
+      case state12_send_server_hello_done:
         ret = do_send_server_hello_done(hs);
         break;
-      case state_read_client_certificate:
+      case state12_read_client_certificate:
         ret = do_read_client_certificate(hs);
         break;
-      case state_verify_client_certificate:
+      case state12_verify_client_certificate:
         ret = do_verify_client_certificate(hs);
         break;
-      case state_read_client_key_exchange:
+      case state12_read_client_key_exchange:
         ret = do_read_client_key_exchange(hs);
         break;
-      case state_read_client_certificate_verify:
+      case state12_read_client_certificate_verify:
         ret = do_read_client_certificate_verify(hs);
         break;
-      case state_read_change_cipher_spec:
+      case state12_read_change_cipher_spec:
         ret = do_read_change_cipher_spec(hs);
         break;
-      case state_process_change_cipher_spec:
+      case state12_process_change_cipher_spec:
         ret = do_process_change_cipher_spec(hs);
         break;
-      case state_read_next_proto:
+      case state12_read_next_proto:
         ret = do_read_next_proto(hs);
         break;
-      case state_read_channel_id:
+      case state12_read_channel_id:
         ret = do_read_channel_id(hs);
         break;
-      case state_read_client_finished:
+      case state12_read_client_finished:
         ret = do_read_client_finished(hs);
         break;
-      case state_send_server_finished:
+      case state12_send_server_finished:
         ret = do_send_server_finished(hs);
         break;
-      case state_finish_server_handshake:
+      case state12_finish_server_handshake:
         ret = do_finish_server_handshake(hs);
         break;
-      case state_done:
+      case state12_done:
         ret = ssl_hs_ok;
         break;
     }
@@ -1621,50 +1605,50 @@
 }
 
 const char *ssl_server_handshake_state(SSL_HANDSHAKE *hs) {
-  enum ssl_server_hs_state_t state =
-      static_cast<enum ssl_server_hs_state_t>(hs->state);
+  enum tls12_server_hs_state_t state =
+      static_cast<enum tls12_server_hs_state_t>(hs->state);
   switch (state) {
-    case state_start_accept:
+    case state12_start_accept:
       return "TLS server start_accept";
-    case state_read_client_hello:
+    case state12_read_client_hello:
       return "TLS server read_client_hello";
-    case state_select_certificate:
+    case state12_select_certificate:
       return "TLS server select_certificate";
-    case state_tls13:
+    case state12_tls13:
       return tls13_server_handshake_state(hs);
-    case state_select_parameters:
+    case state12_select_parameters:
       return "TLS server select_parameters";
-    case state_send_server_hello:
+    case state12_send_server_hello:
       return "TLS server send_server_hello";
-    case state_send_server_certificate:
+    case state12_send_server_certificate:
       return "TLS server send_server_certificate";
-    case state_send_server_key_exchange:
+    case state12_send_server_key_exchange:
       return "TLS server send_server_key_exchange";
-    case state_send_server_hello_done:
+    case state12_send_server_hello_done:
       return "TLS server send_server_hello_done";
-    case state_read_client_certificate:
+    case state12_read_client_certificate:
       return "TLS server read_client_certificate";
-    case state_verify_client_certificate:
+    case state12_verify_client_certificate:
       return "TLS server verify_client_certificate";
-    case state_read_client_key_exchange:
+    case state12_read_client_key_exchange:
       return "TLS server read_client_key_exchange";
-    case state_read_client_certificate_verify:
+    case state12_read_client_certificate_verify:
       return "TLS server read_client_certificate_verify";
-    case state_read_change_cipher_spec:
+    case state12_read_change_cipher_spec:
       return "TLS server read_change_cipher_spec";
-    case state_process_change_cipher_spec:
+    case state12_process_change_cipher_spec:
       return "TLS server process_change_cipher_spec";
-    case state_read_next_proto:
+    case state12_read_next_proto:
       return "TLS server read_next_proto";
-    case state_read_channel_id:
+    case state12_read_channel_id:
       return "TLS server read_channel_id";
-    case state_read_client_finished:
+    case state12_read_client_finished:
       return "TLS server read_client_finished";
-    case state_send_server_finished:
+    case state12_send_server_finished:
       return "TLS server send_server_finished";
-    case state_finish_server_handshake:
+    case state12_finish_server_handshake:
       return "TLS server finish_server_handshake";
-    case state_done:
+    case state12_done:
       return "TLS server done";
   }
 
diff --git a/ssl/internal.h b/ssl/internal.h
index d13d5f2..10716ea 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -929,6 +929,10 @@
   // nullptr on error.
   static UniquePtr<SSLKeyShare> Create(uint16_t group_id);
 
+  // Create deserializes an SSLKeyShare instance previously serialized by
+  // |Serialize|.
+  static UniquePtr<SSLKeyShare> Create(CBS *in);
+
   // GroupID returns the group ID.
   virtual uint16_t GroupID() const PURE_VIRTUAL;
 
@@ -952,6 +956,14 @@
   // send to the peer.
   virtual bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
                       Span<const uint8_t> peer_key) PURE_VIRTUAL;
+
+  // Serialize writes the state of the key exchange to |out|, returning true if
+  // successful and false otherwise.
+  virtual bool Serialize(CBB *out) { return false; }
+
+  // Deserialize initializes the state of the key exchange from |in|, returning
+  // true if successful and false otherwise.  It is called by |Create|.
+  virtual bool Deserialize(CBS *in) { return false; }
 };
 
 // ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it
@@ -1258,6 +1270,7 @@
   ssl_hs_flush,
   ssl_hs_certificate_selection_pending,
   ssl_hs_handoff,
+  ssl_hs_handback,
   ssl_hs_x509_lookup,
   ssl_hs_channel_id_lookup,
   ssl_hs_private_key_operation,
@@ -1280,6 +1293,30 @@
   ssl_grease_last_index = ssl_grease_ticket_extension,
 };
 
+enum tls12_server_hs_state_t {
+  state12_start_accept = 0,
+  state12_read_client_hello,
+  state12_select_certificate,
+  state12_tls13,
+  state12_select_parameters,
+  state12_send_server_hello,
+  state12_send_server_certificate,
+  state12_send_server_key_exchange,
+  state12_send_server_hello_done,
+  state12_read_client_certificate,
+  state12_verify_client_certificate,
+  state12_read_client_key_exchange,
+  state12_read_client_certificate_verify,
+  state12_read_change_cipher_spec,
+  state12_process_change_cipher_spec,
+  state12_read_next_proto,
+  state12_read_channel_id,
+  state12_read_client_finished,
+  state12_send_server_finished,
+  state12_finish_server_handshake,
+  state12_done,
+};
+
 struct SSL_HANDSHAKE {
   explicit SSL_HANDSHAKE(SSL *ssl);
   ~SSL_HANDSHAKE();
@@ -2676,6 +2713,11 @@
   // element of the same name and may be cleared if the handoff is declined.
   bool handoff:1;
 
+  // handback indicates that a server should pause the handshake after
+  // finishing operations that require private key material, in such a way that
+  // |SSL_get_error| returns |SSL_HANDBACK|.  It is set by |SSL_apply_handoff|.
+  bool handback : 1;
+
   // did_dummy_pq_padding is only valid for a client. In that context, it is
   // true iff the client observed the server echoing a dummy PQ padding
   // extension.
diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc
index 4d76bb2..2a076c3 100644
--- a/ssl/ssl_key_share.cc
+++ b/ssl/ssl_key_share.cc
@@ -124,6 +124,32 @@
     return true;
   }
 
+  bool Serialize(CBB *out) override {
+    assert(private_key_);
+    CBB cbb;
+    UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(nid_));
+    // Padding is added to avoid leaking the length.
+    size_t len = BN_num_bytes(EC_GROUP_get0_order(group.get()));
+    if (!CBB_add_asn1_uint64(out, group_id_) ||
+        !CBB_add_asn1(out, &cbb, CBS_ASN1_OCTETSTRING) ||
+        !BN_bn2cbb_padded(&cbb, len, private_key_.get()) ||
+        !CBB_flush(out)) {
+      return false;
+    }
+    return true;
+  }
+
+  bool Deserialize(CBS *in) override {
+    assert(!private_key_);
+    CBS private_key;
+    if (!CBS_get_asn1(in, &private_key, CBS_ASN1_OCTETSTRING)) {
+      return false;
+    }
+    private_key_.reset(BN_bin2bn(CBS_data(&private_key),
+                                 CBS_len(&private_key), nullptr));
+    return private_key_ != nullptr;
+  }
+
  private:
   UniquePtr<BIGNUM> private_key_;
   int nid_;
@@ -166,6 +192,21 @@
     return true;
   }
 
+  bool Serialize(CBB *out) override {
+    return (CBB_add_asn1_uint64(out, GroupID()) &&
+            CBB_add_asn1_octet_string(out, private_key_, sizeof(private_key_)));
+  }
+
+  bool Deserialize(CBS *in) override {
+    CBS key;
+    if (!CBS_get_asn1(in, &key, CBS_ASN1_OCTETSTRING) ||
+        CBS_len(&key) != sizeof(private_key_) ||
+        !CBS_copy_bytes(&key, private_key_, sizeof(private_key_))) {
+      return false;
+    }
+    return true;
+  }
+
  private:
   uint8_t private_key_[32];
 };
@@ -205,6 +246,19 @@
   }
 }
 
+UniquePtr<SSLKeyShare> SSLKeyShare::Create(CBS *in) {
+  uint64_t group;
+  if (!CBS_get_asn1_uint64(in, &group)) {
+    return nullptr;
+  }
+  UniquePtr<SSLKeyShare> key_share = Create(static_cast<uint64_t>(group));
+  if (!key_share->Deserialize(in)) {
+    return nullptr;
+  }
+  return key_share;
+}
+
+
 bool SSLKeyShare::Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
                          uint8_t *out_alert, Span<const uint8_t> peer_key) {
   *out_alert = SSL_AD_INTERNAL_ERROR;
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index ef79831..6312504 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -1274,6 +1274,9 @@
     case SSL_HANDOFF:
       return SSL_ERROR_HANDOFF;
 
+    case SSL_HANDBACK:
+      return SSL_ERROR_HANDBACK;
+
     case SSL_READING: {
       BIO *bio = SSL_get_rbio(ssl);
       if (BIO_should_read(bio)) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 9f77f14..12f044c 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -3949,9 +3949,13 @@
 
   int handshake_ret = SSL_do_handshake(handshaker.get());
   int handshake_err = SSL_get_error(handshaker.get(), handshake_ret);
-  ASSERT_EQ(handshake_err, SSL_ERROR_WANT_READ);
+  ASSERT_EQ(handshake_err, SSL_ERROR_HANDBACK);
 
-  ASSERT_TRUE(CompleteHandshakes(client.get(), handshaker.get()));
+  // Double-check that additional calls to |SSL_do_handshake| continue
+  // to get |SSL_ERRROR_HANDBACK|.
+  handshake_ret = SSL_do_handshake(handshaker.get());
+  handshake_err = SSL_get_error(handshaker.get(), handshake_ret);
+  ASSERT_EQ(handshake_err, SSL_ERROR_HANDBACK);
 
   ScopedCBB cbb_handback;
   Array<uint8_t> handback;
@@ -3963,6 +3967,7 @@
   ASSERT_TRUE(SSL_apply_handback(server2.get(), handback));
 
   MoveBIOs(server2.get(), handshaker.get());
+  ASSERT_TRUE(CompleteHandshakes(client.get(), server2.get()));
 
   uint8_t byte = 42;
   EXPECT_EQ(SSL_write(client.get(), &byte, 1), 1);
diff --git a/ssl/ssl_transcript.cc b/ssl/ssl_transcript.cc
index 2033dfd..345f9d3 100644
--- a/ssl/ssl_transcript.cc
+++ b/ssl/ssl_transcript.cc
@@ -368,10 +368,6 @@
     return true;
   }
 
-  // At this point, the handshake should have released the handshake buffer on
-  // its own.
-  assert(!buffer_);
-
   static const char kClientLabel[] = "client finished";
   static const char kServerLabel[] = "server finished";
   auto label = from_server
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index ae26ded..107de52 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1937,40 +1937,34 @@
   return fwrite(settings, settings_len, 1, file.get()) == 1;
 }
 
-static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
-                       bssl::UniquePtr<SSL> *ssl_uniqueptr,
-                       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 DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session,
-                         SSL_CTX *ssl_ctx, const TestConfig *config,
-                         const TestConfig *retry_config, bool is_resume,
-                         SSL_SESSION *session) {
+static bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, const TestConfig *config,
+                                   SSL_SESSION *session, bool is_resume,
+                                   std::unique_ptr<TestState> test_state) {
   bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx));
   if (!ssl) {
-    return false;
+    return nullptr;
   }
 
-  if (!SetTestConfig(ssl.get(), config) ||
-      !SetTestState(ssl.get(), std::unique_ptr<TestState>(new TestState))) {
-    return false;
+  if (!SetTestConfig(ssl.get(), config)) {
+    return nullptr;
   }
-
-  GetTestState(ssl.get())->is_resume = is_resume;
+  if (test_state != nullptr) {
+    if (!SetTestState(ssl.get(), std::move(test_state))) {
+      return nullptr;
+    }
+    GetTestState(ssl.get())->is_resume = is_resume;
+  }
 
   if (config->fallback_scsv &&
       !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
-    return false;
+    return nullptr;
   }
   // Install the certificate synchronously if nothing else will handle it.
   if (!config->use_early_callback &&
       !config->use_old_client_cert_callback &&
       !config->async &&
       !InstallCertificate(ssl.get())) {
-    return false;
+    return nullptr;
   }
   if (!config->use_old_client_cert_callback) {
     SSL_set_cert_cb(ssl.get(), CertCallback, nullptr);
@@ -2027,7 +2021,7 @@
       // The async case will be supplied by |ChannelIdCallback|.
       bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(config->send_channel_id);
       if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
-        return false;
+        return nullptr;
       }
     }
   }
@@ -2039,13 +2033,13 @@
   }
   if (!config->host_name.empty() &&
       !SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) {
-    return false;
+    return nullptr;
   }
   if (!config->advertise_alpn.empty() &&
       SSL_set_alpn_protos(ssl.get(),
                           (const uint8_t *)config->advertise_alpn.data(),
                           config->advertise_alpn.size()) != 0) {
-    return false;
+    return nullptr;
   }
   if (!config->psk.empty()) {
     SSL_set_psk_client_callback(ssl.get(), PskClientCallback);
@@ -2053,11 +2047,11 @@
   }
   if (!config->psk_identity.empty() &&
       !SSL_use_psk_identity_hint(ssl.get(), config->psk_identity.c_str())) {
-    return false;
+    return nullptr;
   }
   if (!config->srtp_profiles.empty() &&
       !SSL_set_srtp_profiles(ssl.get(), config->srtp_profiles.c_str())) {
-    return false;
+    return nullptr;
   }
   if (config->enable_ocsp_stapling) {
     SSL_enable_ocsp_stapling(ssl.get());
@@ -2067,11 +2061,11 @@
   }
   if (config->min_version != 0 &&
       !SSL_set_min_proto_version(ssl.get(), (uint16_t)config->min_version)) {
-    return false;
+    return nullptr;
   }
   if (config->max_version != 0 &&
       !SSL_set_max_proto_version(ssl.get(), (uint16_t)config->max_version)) {
-    return false;
+    return nullptr;
   }
   if (config->mtu != 0) {
     SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU);
@@ -2095,7 +2089,7 @@
   if (config->p384_only) {
     int nid = NID_secp384r1;
     if (!SSL_set1_curves(ssl.get(), &nid, 1)) {
-      return false;
+      return nullptr;
     }
   }
   if (config->enable_all_curves) {
@@ -2105,7 +2099,7 @@
     };
     if (!SSL_set1_curves(ssl.get(), kAllCurves,
                          OPENSSL_ARRAY_SIZE(kAllCurves))) {
-      return false;
+      return nullptr;
     }
   }
   if (config->initial_timeout_duration_ms > 0) {
@@ -2123,7 +2117,7 @@
   }
   if (config->dummy_pq_padding_len > 0 &&
       !SSL_set_dummy_pq_padding_size(ssl.get(), config->dummy_pq_padding_len)) {
-    return false;
+    return nullptr;
   }
   if (!config->quic_transport_params.empty()) {
     if (!SSL_set_quic_transport_params(
@@ -2131,10 +2125,55 @@
             reinterpret_cast<const uint8_t *>(
                 config->quic_transport_params.data()),
             config->quic_transport_params.size())) {
-      return false;
+      return nullptr;
     }
   }
 
+  if (session != NULL) {
+    if (!config->is_server) {
+      if (SSL_set_session(ssl.get(), session) != 1) {
+        return nullptr;
+      }
+    } else if (config->async) {
+      // The internal session cache is disabled, so install the session
+      // manually.
+      SSL_SESSION_up_ref(session);
+      GetTestState(ssl.get())->pending_session.reset(session);
+    }
+  }
+
+  if (SSL_get_current_cipher(ssl.get()) != nullptr) {
+    fprintf(stderr, "non-null cipher before handshake\n");
+    return nullptr;
+  }
+
+  return ssl;
+}
+
+static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
+                       bssl::UniquePtr<SSL> *ssl_uniqueptr,
+                       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 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 = NewSSL(ssl_ctx, config, session, is_resume,
+                                    std::unique_ptr<TestState>(new TestState));
+  if (!ssl) {
+    return false;
+  }
+  if (config->is_server) {
+    SSL_set_accept_state(ssl.get());
+  } else {
+    SSL_set_connect_state(ssl.get());
+  }
+
+
   int sock = Connect(config->port);
   if (sock == -1) {
     return false;
@@ -2167,30 +2206,6 @@
   SSL_set_bio(ssl.get(), bio.get(), bio.get());
   bio.release();  // SSL_set_bio takes ownership.
 
-  if (session != NULL) {
-    if (!config->is_server) {
-      if (SSL_set_session(ssl.get(), session) != 1) {
-        return false;
-      }
-    } else if (config->async) {
-      // The internal session cache is disabled, so install the session
-      // manually.
-      SSL_SESSION_up_ref(session);
-      GetTestState(ssl.get())->pending_session.reset(session);
-    }
-  }
-
-  if (SSL_get_current_cipher(ssl.get()) != nullptr) {
-    fprintf(stderr, "non-null cipher before handshake\n");
-    return false;
-  }
-
-  if (config->is_server) {
-    SSL_set_accept_state(ssl.get());
-  } else {
-    SSL_set_connect_state(ssl.get());
-  }
-
   bool ret = DoExchange(out_session, &ssl, 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.
@@ -2253,22 +2268,28 @@
   return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDOFF;
 }
 
+static bool HandbackReady(SSL *ssl, int ret) {
+  return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK;
+}
+
 static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
                        bssl::UniquePtr<SSL> *ssl_uniqueptr,
                        const TestConfig *config, bool is_resume,
                        bool is_retry) {
   int ret;
   SSL *ssl = ssl_uniqueptr->get();
+  SSL_CTX *session_ctx = ssl->ctx;
 
   if (!config->implicit_handshake) {
     if (config->handoff) {
-      bssl::UniquePtr<SSL_CTX> ctx_handoff(SSL_CTX_new(TLSv1_method()));
+      bssl::UniquePtr<SSL_CTX> ctx_handoff = SetupCtx(ssl->ctx, config);
       if (!ctx_handoff) {
         return false;
       }
       SSL_CTX_set_handoff_mode(ctx_handoff.get(), 1);
 
-      bssl::UniquePtr<SSL> ssl_handoff(SSL_new(ctx_handoff.get()));
+      bssl::UniquePtr<SSL> ssl_handoff =
+          NewSSL(ctx_handoff.get(), config, nullptr, false, nullptr);
       if (!ssl_handoff) {
         return false;
       }
@@ -2318,12 +2339,12 @@
       });
     } while (config->async && RetryAsync(ssl, ret));
 
-    if (ret != 1 ||
-        !CheckHandshakeProperties(ssl, is_resume, config)) {
-      return false;
-    }
-
     if (config->handoff) {
+      if (!HandbackReady(ssl, ret)) {
+        fprintf(stderr, "Connection failed to handback.\n");
+        return false;
+      }
+
       bssl::ScopedCBB cbb;
       bssl::Array<uint8_t> handback;
       if (!CBB_init(cbb.get(), 512) ||
@@ -2333,26 +2354,42 @@
         return false;
       }
 
-      bssl::UniquePtr<SSL_CTX> ctx_handback(SSL_CTX_new(TLSv1_method()));
-      SSL_CTX_set_msg_callback(ctx_handback.get(), MessageCallback);
-      bssl::UniquePtr<SSL> ssl_handback(SSL_new(ctx_handback.get()));
+      bssl::UniquePtr<SSL_CTX> ctx_handback = SetupCtx(ssl->ctx, config);
+      if (!ctx_handback) {
+        return false;
+      }
+      bssl::UniquePtr<SSL> ssl_handback =
+          NewSSL(ctx_handback.get(), config, nullptr, false, nullptr);
       if (!ssl_handback) {
         return false;
       }
-      if (!SSL_apply_handback(ssl_handback.get(), handback)) {
-        fprintf(stderr, "Applying handback failed.\n");
-        return false;
-      }
-
       MoveBIOs(ssl_handback.get(), ssl);
       if (!MoveExData(ssl_handback.get(), ssl)) {
         return false;
       }
 
+      if (!SSL_apply_handback(ssl_handback.get(), handback)) {
+        fprintf(stderr, "Applying handback failed.\n");
+        return false;
+      }
+
       *ssl_uniqueptr = std::move(ssl_handback);
       ssl = ssl_uniqueptr->get();
+
+      do {
+        ret = CheckIdempotentError("SSL_do_handshake", ssl, [&]() -> int {
+          return SSL_do_handshake(ssl);
+        });
+      } while (config->async && RetryAsync(ssl, ret));
     }
 
+    if (ret != 1 || !CheckHandshakeProperties(ssl, is_resume, config)) {
+      return false;
+    }
+
+    lh_SSL_SESSION_doall_arg(ssl->ctx->sessions, ssl_ctx_add_session,
+                             session_ctx);
+
     if (is_resume && !is_retry && !config->is_server &&
         config->expect_no_offer_early_data && SSL_in_early_data(ssl)) {
       fprintf(stderr, "Client unexpectedly offered early data.\n");