SSL_CONFIG: new struct for sheddable handshake configuration.

|SSL_CONFIG| is a container for bits of configuration that are
unneeded after the handshake completes.  By default it is retained for
the life of the |SSL|, but it may be shed at the caller's option by
calling SSL_set_shed_handshake_config().  This is incompatible with
renegotiation, and with SSL_clear().

|SSL_CONFIG| is reachable by |ssl->config| and by |hs->config|.  The
latter is always non-NULL.  To avoid null checks, I've changed the
signature of a number of functions from |SSL*| arguments to
|SSL_HANDSHAKE*| arguments.

When configuration has been shed, setters that touch |SSL_CONFIG|
return an error value if that is possible.  Setters that return |void|
do nothing.

Getters that request |SSL_CONFIG| values will fail with an |assert| if
the configuration has been shed.  When asserts are compiled out, they
will return an error value.

The aim of this commit is to simplify analysis of split-handshakes by
making it obvious that some bits of state have no effects beyond the
handshake.  It also cuts down on memory usage.

Of note: |SSL_CTX| is still reachable after the configuration has been
shed, and a couple things need to be retained only for the sake of
post-handshake hooks.  Perhaps these can be fixed in time.

Change-Id: Idf09642e0518945b81a1e9fcd7331cc9cf7cc2d6
Bug: 123
Reviewed-on: https://boringssl-review.googlesource.com/27644
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 77bd4de..2b87914 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1899,7 +1899,7 @@
                                               size_t sid_ctx_len);
 
 // SSL_get0_session_id_context returns a pointer to |ssl|'s session ID context
-// and sets |*out_len| to its length.
+// and sets |*out_len| to its length.  It returns NULL on error.
 OPENSSL_EXPORT const uint8_t *SSL_get0_session_id_context(const SSL *ssl,
                                                           size_t *out_len);
 
@@ -2303,7 +2303,7 @@
 OPENSSL_EXPORT int SSL_CTX_get_verify_mode(const SSL_CTX *ctx);
 
 // SSL_get_verify_mode returns |ssl|'s verify mode, set by |SSL_CTX_set_verify|
-// or |SSL_set_verify|.
+// or |SSL_set_verify|.  It returns -1 on error.
 OPENSSL_EXPORT int SSL_get_verify_mode(const SSL *ssl);
 
 // SSL_CTX_get_verify_callback returns the callback set by
@@ -3303,6 +3303,13 @@
 OPENSSL_EXPORT void SSL_CTX_set_current_time_cb(
     SSL_CTX *ctx, void (*cb)(const SSL *ssl, struct timeval *out_clock));
 
+// SSL_set_shed_handshake_config allows some of the configuration of |ssl| to be
+// freed after its handshake completes.  When configuration shedding is enabled,
+// it is an error to call APIs that query the state that was shed, and it is an
+// error to call |SSL_clear|.  Additionally, if renegotiation is enabled,
+// renegotiation attempts will be rejected.
+OPENSSL_EXPORT void SSL_set_shed_handshake_config(SSL *ssl, int enable);
+
 enum ssl_renegotiate_mode_t {
   ssl_renegotiate_never = 0,
   ssl_renegotiate_once,
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index 2cbbaeb..89fca2f 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -55,7 +55,7 @@
     return false;
   }
 
-  ssl->handoff = false;
+  s3->hs->config->handoff = false;
   return true;
 }
 
@@ -94,7 +94,7 @@
     s3->hs->transcript.Update(transcript);
     s3->is_v2_hello = true;
   }
-  ssl->handback = true;
+  s3->hs->handback = true;
 
   return true;
 }
diff --git a/ssl/handshake.cc b/ssl/handshake.cc
index 00a2cc5..3962618 100644
--- a/ssl/handshake.cc
+++ b/ssl/handshake.cc
@@ -146,7 +146,9 @@
       ticket_expected(false),
       extended_master_secret(false),
       pending_private_key_op(false),
-      grease_seeded(false) {
+      grease_seeded(false),
+      handback(false) {
+  assert(ssl);
 }
 
 SSL_HANDSHAKE::~SSL_HANDSHAKE() {
@@ -159,6 +161,11 @@
       !hs->transcript.Init()) {
     return nullptr;
   }
+  hs->config = ssl->config;
+  if (!hs->config) {
+    assert(hs->config);
+    return nullptr;
+  }
   return hs;
 }
 
@@ -189,7 +196,8 @@
   static const size_t kMaxMessageLen = 16384;
 
   if (SSL_in_init(ssl)) {
-    if ((!ssl->server || (ssl->verify_mode & SSL_VERIFY_PEER)) &&
+    SSL_CONFIG *config = ssl->config;  // SSL_in_init() implies not NULL.
+    if ((!ssl->server || (config->verify_mode & SSL_VERIFY_PEER)) &&
         kMaxMessageLen < ssl->max_cert_list) {
       return ssl->max_cert_list;
     }
@@ -327,15 +335,15 @@
 
   uint8_t alert = SSL_AD_CERTIFICATE_UNKNOWN;
   enum ssl_verify_result_t ret;
-  if (ssl->custom_verify_callback != nullptr) {
-    ret = ssl->custom_verify_callback(ssl, &alert);
+  if (hs->config->custom_verify_callback != nullptr) {
+    ret = hs->config->custom_verify_callback(ssl, &alert);
     switch (ret) {
       case ssl_verify_ok:
         hs->new_session->verify_result = X509_V_OK;
         break;
       case ssl_verify_invalid:
         // If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
-        if (ssl->verify_mode == SSL_VERIFY_NONE) {
+        if (hs->config->verify_mode == SSL_VERIFY_NONE) {
           ERR_clear_error();
           ret = ssl_verify_ok;
         }
@@ -346,7 +354,7 @@
     }
   } else {
     ret = ssl->ctx->x509_method->session_verify_cert_chain(
-              hs->new_session.get(), ssl, &alert)
+              hs->new_session.get(), hs, &alert)
               ? ssl_verify_ok
               : ssl_verify_invalid;
   }
@@ -475,12 +483,13 @@
   return 1;
 }
 
-bool ssl_output_cert_chain(SSL *ssl) {
+bool ssl_output_cert_chain(SSL_HANDSHAKE *hs) {
   ScopedCBB cbb;
   CBB body;
-  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CERTIFICATE) ||
-      !ssl_add_cert_chain(ssl, &body) ||
-      !ssl_add_message_cbb(ssl, cbb.get())) {
+  if (!hs->ssl->method->init_message(hs->ssl, cbb.get(), &body,
+                                     SSL3_MT_CERTIFICATE) ||
+      !ssl_add_cert_chain(hs, &body) ||
+      !ssl_add_message_cbb(hs->ssl, cbb.get())) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index fbef2e1..e9b0eed 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -199,13 +199,13 @@
 
 // ssl_get_client_disabled sets |*out_mask_a| and |*out_mask_k| to masks of
 // disabled algorithms.
-static void ssl_get_client_disabled(SSL *ssl, uint32_t *out_mask_a,
+static void ssl_get_client_disabled(SSL_HANDSHAKE *hs, uint32_t *out_mask_a,
                                     uint32_t *out_mask_k) {
   *out_mask_a = 0;
   *out_mask_k = 0;
 
   // PSK requires a client callback.
-  if (ssl->psk_client_callback == NULL) {
+  if (hs->config->psk_client_callback == NULL) {
     *out_mask_a |= SSL_aPSK;
     *out_mask_k |= SSL_kPSK;
   }
@@ -214,7 +214,7 @@
 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);
+  ssl_get_client_disabled(hs, &mask_a, &mask_k);
 
   CBB child;
   if (!CBB_add_u16_length_prefixed(out, &child)) {
@@ -390,7 +390,7 @@
   ssl->s3->session_reused = false;
 
   // Freeze the version range.
-  if (!ssl_get_version_range(ssl, &hs->min_version, &hs->max_version)) {
+  if (!ssl_get_version_range(hs, &hs->min_version, &hs->max_version)) {
     return ssl_hs_error;
   }
 
@@ -659,7 +659,7 @@
 
   // The cipher must be allowed in the selected version and enabled.
   uint32_t mask_a, mask_k;
-  ssl_get_client_disabled(ssl, &mask_a, &mask_k);
+  ssl_get_client_disabled(hs, &mask_a, &mask_k);
   if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
       SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
@@ -680,7 +680,7 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
-    if (!ssl_session_is_context_valid(ssl, ssl->session)) {
+    if (!ssl_session_is_context_valid(hs, ssl->session)) {
       // This is actually a client application bug.
       OPENSSL_PUT_ERROR(SSL,
                         SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
@@ -970,7 +970,7 @@
     hs->new_session->group_id = group_id;
 
     // Ensure the group is consistent with preferences.
-    if (!tls1_check_group_id(ssl, group_id)) {
+    if (!tls1_check_group_id(hs, group_id)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
@@ -1176,8 +1176,8 @@
   }
 
   // Call cert_cb to update the certificate.
-  if (ssl->cert->cert_cb != NULL) {
-    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+  if (hs->config->cert->cert_cb != NULL) {
+    int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
     if (rv == 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
@@ -1189,7 +1189,7 @@
     }
   }
 
-  if (!ssl_has_certificate(ssl)) {
+  if (!ssl_has_certificate(hs->config)) {
     // Without a client certificate, the handshake buffer may be released.
     hs->transcript.FreeBuffer();
 
@@ -1205,7 +1205,7 @@
   }
 
   if (!ssl_on_certificate_selected(hs) ||
-      !ssl_output_cert_chain(ssl)) {
+      !ssl_output_cert_chain(hs)) {
     return ssl_hs_error;
   }
 
@@ -1234,16 +1234,16 @@
   unsigned psk_len = 0;
   uint8_t psk[PSK_MAX_PSK_LEN];
   if (alg_a & SSL_aPSK) {
-    if (ssl->psk_client_callback == NULL) {
+    if (hs->config->psk_client_callback == NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_CLIENT_CB);
       return ssl_hs_error;
     }
 
     char identity[PSK_MAX_IDENTITY_LEN + 1];
     OPENSSL_memset(identity, 0, sizeof(identity));
-    psk_len =
-        ssl->psk_client_callback(ssl, hs->peer_psk_identity_hint.get(),
-                                 identity, sizeof(identity), psk, sizeof(psk));
+    psk_len = hs->config->psk_client_callback(
+        ssl, hs->peer_psk_identity_hint.get(), identity, sizeof(identity), psk,
+        sizeof(psk));
     if (psk_len == 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
@@ -1373,12 +1373,12 @@
 static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
-  if (!hs->cert_request || !ssl_has_certificate(ssl)) {
+  if (!hs->cert_request || !ssl_has_certificate(hs->config)) {
     hs->state = state_send_client_finished;
     return ssl_hs_ok;
   }
 
-  assert(ssl_has_private_key(ssl));
+  assert(ssl_has_private_key(hs->config));
   ScopedCBB cbb;
   CBB body, child;
   if (!ssl->method->init_message(ssl, cbb.get(), &body,
@@ -1410,7 +1410,7 @@
   // The SSL3 construction for CertificateVerify does not decompose into a
   // single final digest and signature, and must be special-cased.
   if (ssl_protocol_version(ssl) == SSL3_VERSION) {
-    if (ssl->cert->key_method != NULL) {
+    if (hs->config->cert->key_method != NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY);
       return ssl_hs_error;
     }
@@ -1423,7 +1423,7 @@
     }
 
     UniquePtr<EVP_PKEY_CTX> pctx(
-        EVP_PKEY_CTX_new(ssl->cert->privatekey.get(), nullptr));
+        EVP_PKEY_CTX_new(hs->config->cert->privatekey.get(), nullptr));
     if (!pctx ||
         !EVP_PKEY_sign_init(pctx.get()) ||
         !EVP_PKEY_sign(pctx.get(), ptr, &sig_len, digest, digest_len)) {
@@ -1459,11 +1459,11 @@
   SSL *const ssl = hs->ssl;
   // Resolve Channel ID first, before any non-idempotent operations.
   if (ssl->s3->tlsext_channel_id_valid) {
-    if (!ssl_do_channel_id_callback(ssl)) {
+    if (!ssl_do_channel_id_callback(hs)) {
       return ssl_hs_error;
     }
 
-    if (ssl->tlsext_channel_id_private == NULL) {
+    if (hs->config->tlsext_channel_id_private == NULL) {
       hs->state = state_send_client_finished;
       return ssl_hs_channel_id_lookup;
     }
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 6404cc9..ac8d46b 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -303,11 +303,10 @@
 static void ssl_get_compatible_server_ciphers(SSL_HANDSHAKE *hs,
                                               uint32_t *out_mask_k,
                                               uint32_t *out_mask_a) {
-  SSL *const ssl = hs->ssl;
   uint32_t mask_k = 0;
   uint32_t mask_a = 0;
 
-  if (ssl_has_certificate(ssl)) {
+  if (ssl_has_certificate(hs->config)) {
     mask_a |= ssl_cipher_auth_mask_for_key(hs->local_pubkey.get());
     if (EVP_PKEY_id(hs->local_pubkey.get()) == EVP_PKEY_RSA) {
       mask_k |= SSL_kRSA;
@@ -321,7 +320,7 @@
   }
 
   // PSK requires a server callback.
-  if (ssl->psk_server_callback != NULL) {
+  if (hs->config->psk_server_callback != NULL) {
     mask_k |= SSL_kPSK;
     mask_a |= SSL_aPSK;
   }
@@ -417,7 +416,7 @@
     return ssl_hs_error;
   }
 
-  if (ssl->handoff) {
+  if (hs->config->handoff) {
     return ssl_hs_handoff;
   }
 
@@ -446,7 +445,7 @@
   }
 
   // Freeze the version range after the early callback.
-  if (!ssl_get_version_range(ssl, &hs->min_version, &hs->max_version)) {
+  if (!ssl_get_version_range(hs, &hs->min_version, &hs->max_version)) {
     return ssl_hs_error;
   }
 
@@ -494,8 +493,8 @@
   }
 
   // Call |cert_cb| to update server certificates if required.
-  if (ssl->cert->cert_cb != NULL) {
-    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+  if (hs->config->cert->cert_cb != NULL) {
+    int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
     if (rv == 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
@@ -523,8 +522,9 @@
 
   // Negotiate the cipher suite. This must be done after |cert_cb| so the
   // certificate is finalized.
-  hs->new_cipher =
-      ssl3_choose_cipher(hs, &client_hello, ssl_get_cipher_preferences(ssl));
+  SSLCipherPreferenceList *prefs =
+      hs->config->cipher_list ? hs->config->cipher_list : ssl->ctx->cipher_list;
+  hs->new_cipher = ssl3_choose_cipher(hs, &client_hello, prefs);
   if (hs->new_cipher == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
@@ -562,7 +562,7 @@
   UniquePtr<SSL_SESSION> session;
   bool tickets_supported = false, renew_ticket = false;
   enum ssl_hs_wait_t wait = ssl_get_prev_session(
-      ssl, &session, &tickets_supported, &renew_ticket, &client_hello);
+      hs, &session, &tickets_supported, &renew_ticket, &client_hello);
   if (wait != ssl_hs_ok) {
     return wait;
   }
@@ -614,9 +614,9 @@
     hs->new_session->cipher = hs->new_cipher;
 
     // Determine whether to request a client certificate.
-    hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+    hs->cert_request = !!(hs->config->verify_mode & SSL_VERIFY_PEER);
     // Only request a certificate if Channel ID isn't negotiated.
-    if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
+    if ((hs->config->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
         ssl->s3->tlsext_channel_id_valid) {
       hs->cert_request = false;
     }
@@ -650,7 +650,7 @@
 
   // Handback includes the whole handshake transcript, so we cannot free the
   // transcript buffer in the handback case.
-  if (!hs->cert_request && !hs->ssl->handback) {
+  if (!hs->cert_request && !hs->handback) {
     hs->transcript.FreeBuffer();
   }
 
@@ -733,12 +733,12 @@
   ScopedCBB cbb;
 
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-    if (!ssl_has_certificate(ssl)) {
+    if (!ssl_has_certificate(hs->config)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
       return ssl_hs_error;
     }
 
-    if (!ssl_output_cert_chain(ssl)) {
+    if (!ssl_output_cert_chain(hs)) {
       return ssl_hs_error;
     }
 
@@ -748,9 +748,10 @@
                                      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.get()),
-                         CRYPTO_BUFFER_len(ssl->cert->ocsp_response.get())) ||
+          !CBB_add_bytes(
+              &ocsp_response,
+              CRYPTO_BUFFER_data(hs->config->cert->ocsp_response.get()),
+              CRYPTO_BUFFER_len(hs->config->cert->ocsp_response.get())) ||
           !ssl_add_message_cbb(ssl, cbb.get())) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
         return ssl_hs_error;
@@ -762,8 +763,7 @@
   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)) {
-
+      ((alg_a & SSL_aPSK) && hs->config->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;
@@ -775,10 +775,11 @@
 
     // PSK ciphers begin with an identity hint.
     if (alg_a & SSL_aPSK) {
-      size_t len =
-          (ssl->psk_identity_hint == NULL) ? 0 : strlen(ssl->psk_identity_hint);
+      size_t len = (hs->config->psk_identity_hint == NULL)
+                       ? 0
+                       : strlen(hs->config->psk_identity_hint);
       if (!CBB_add_u16_length_prefixed(cbb.get(), &child) ||
-          !CBB_add_bytes(&child, (const uint8_t *)ssl->psk_identity_hint,
+          !CBB_add_bytes(&child, (const uint8_t *)hs->config->psk_identity_hint,
                          len)) {
         return ssl_hs_error;
       }
@@ -837,7 +838,7 @@
 
   // Add a signature.
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-    if (!ssl_has_private_key(ssl)) {
+    if (!ssl_has_private_key(hs->config)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
     }
@@ -908,7 +909,7 @@
         (ssl_protocol_version(ssl) >= TLS1_2_VERSION &&
          (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb) ||
           !tls12_add_verify_sigalgs(ssl, &sigalgs_cbb, true /* certs */))) ||
-        !ssl_add_client_CA_list(ssl, &body) ||
+        !ssl_add_client_CA_list(hs, &body) ||
         !ssl_add_message_cbb(ssl, cbb.get())) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       return ssl_hs_error;
@@ -929,8 +930,8 @@
 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->handback && hs->new_cipher->algorithm_mkey == SSL_kECDHE) {
+    return ssl_hs_handback;
   }
   if (!hs->cert_request) {
     hs->state = state12_verify_client_certificate;
@@ -947,7 +948,7 @@
         msg.type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
       // In SSL 3.0, the Certificate message is omitted to signal no
       // certificate.
-      if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+      if (hs->config->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         return ssl_hs_error;
@@ -973,7 +974,7 @@
   uint8_t alert = SSL_AD_DECODE_ERROR;
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
   if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey,
-                            ssl->retain_only_sha256_of_client_certs
+                            hs->config->retain_only_sha256_of_client_certs
                                 ? hs->new_session->peer_sha256
                                 : NULL,
                             &certificate_msg, ssl->ctx->pool)) {
@@ -1002,7 +1003,7 @@
       return ssl_hs_error;
     }
 
-    if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
+    if (hs->config->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) {
       // Fail for TLS only if we required a certificate
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
@@ -1012,7 +1013,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;
-  } else if (ssl->retain_only_sha256_of_client_certs) {
+  } else if (hs->config->retain_only_sha256_of_client_certs) {
     // The hash will have been filled in.
     hs->new_session->peer_sha256_valid = 1;
   }
@@ -1187,7 +1188,7 @@
   // 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) {
+    if (hs->config->psk_server_callback == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
@@ -1195,7 +1196,7 @@
 
     // Look up the key for the identity.
     uint8_t psk[PSK_MAX_PSK_LEN];
-    unsigned psk_len = ssl->psk_server_callback(
+    unsigned psk_len = hs->config->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);
@@ -1471,7 +1472,7 @@
                                    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_encrypt_ticket(hs, &ticket, session) ||
         !ssl_add_message_cbb(ssl, cbb.get())) {
       return ssl_hs_error;
     }
@@ -1494,14 +1495,15 @@
 static enum ssl_hs_wait_t do_finish_server_handshake(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
-  if (ssl->handback) {
+  if (hs->handback) {
     return ssl_hs_handback;
   }
 
   ssl->method->on_handshake_complete(ssl);
 
   // If we aren't retaining peer certificates then we can discard it now.
-  if (hs->new_session != NULL && ssl->retain_only_sha256_of_client_certs) {
+  if (hs->new_session != NULL &&
+      hs->config->retain_only_sha256_of_client_certs) {
     sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
     hs->new_session->certs = NULL;
     ssl->ctx->x509_method->session_clear(hs->new_session.get());
diff --git a/ssl/internal.h b/ssl/internal.h
index b258589..20c060a 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -175,6 +175,7 @@
 
 namespace bssl {
 
+struct SSL_CONFIG;
 struct SSL_HANDSHAKE;
 struct SSL_PROTOCOL_METHOD;
 
@@ -371,7 +372,7 @@
 
 // ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
 // minimum and maximum enabled protocol versions, respectively.
-bool ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+bool ssl_get_version_range(const SSL_HANDSHAKE *hs, uint16_t *out_min_version,
                            uint16_t *out_max_version);
 
 // ssl_supports_version returns whether |hs| supports |version|.
@@ -908,9 +909,9 @@
 
 // Private key operations.
 
-// ssl_has_private_key returns one if |ssl| has a private key
-// configured and zero otherwise.
-int ssl_has_private_key(const SSL *ssl);
+// ssl_has_private_key returns one if |cfg| has a private key configured and
+// zero otherwise.
+int ssl_has_private_key(const SSL_CONFIG *cfg);
 
 // ssl_private_key_* perform the corresponding operation on
 // |SSL_PRIVATE_KEY_METHOD|. If there is a custom private key configured, they
@@ -1175,7 +1176,7 @@
 
 // ssl_has_certificate returns one if a certificate and private key are
 // configured and zero otherwise.
-int ssl_has_certificate(const SSL *ssl);
+int ssl_has_certificate(const SSL_CONFIG *cfg);
 
 // ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
 // by a TLS Certificate message. On success, it advances |cbs| and returns
@@ -1194,10 +1195,10 @@
                           uint8_t *out_leaf_sha256, CBS *cbs,
                           CRYPTO_BUFFER_POOL *pool);
 
-// ssl_add_cert_chain adds |ssl|'s certificate chain to |cbb| in the format used
-// by a TLS Certificate message. If there is no certificate chain, it emits an
-// empty certificate list. It returns one on success and zero on error.
-int ssl_add_cert_chain(SSL *ssl, CBB *cbb);
+// ssl_add_cert_chain adds |hs->ssl|'s certificate chain to |cbb| in the format
+// used by a TLS Certificate message. If there is no certificate chain, it emits
+// an empty certificate list. It returns one on success and zero on error.
+int ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb);
 
 // ssl_cert_check_digital_signature_key_usage parses the DER-encoded, X.509
 // certificate in |in| and returns one if doesn't specify a key usage or, if it
@@ -1219,12 +1220,12 @@
                                                             CBS *cbs);
 
 // ssl_has_client_CAs returns there are configured CAs.
-bool ssl_has_client_CAs(SSL *ssl);
+bool ssl_has_client_CAs(const SSL_CONFIG *cfg);
 
 // ssl_add_client_CA_list adds the configured CA list to |cbb| in the format
 // used by a TLS CertificateRequest message. It returns one on success and zero
 // on error.
-int ssl_add_client_CA_list(SSL *ssl, CBB *cbb);
+int ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb);
 
 // ssl_check_leaf_certificate returns one if |pkey| and |leaf| are suitable as
 // a server's leaf certificate for |hs|. Otherwise, it returns zero and pushes
@@ -1379,6 +1380,9 @@
   // ssl is a non-owning pointer to the parent |SSL| object.
   SSL *ssl;
 
+  // config is a non-owning pointer to the handshake configuration.
+  SSL_CONFIG *config;
+
   // wait contains the operation the handshake is currently blocking on or
   // |ssl_hs_ok| if none.
   enum ssl_hs_wait_t wait = ssl_hs_ok;
@@ -1610,6 +1614,11 @@
   // should be echoed in a ServerHello, or zero if no extension should be
   // echoed.
   uint16_t dummy_pq_padding_len = 0;
+
+  // 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;
 };
 
 UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl);
@@ -1698,8 +1707,9 @@
     enum ssl_cert_verify_context_t cert_verify_context);
 
 // ssl_is_alpn_protocol_allowed returns whether |protocol| is a valid server
-// selection for |ssl|'s client preferences.
-bool ssl_is_alpn_protocol_allowed(const SSL *ssl, Span<const uint8_t> protocol);
+// selection for |hs->ssl|'s client preferences.
+bool ssl_is_alpn_protocol_allowed(const SSL_HANDSHAKE *hs,
+                                  Span<const uint8_t> protocol);
 
 // ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns
 // true on successful negotiation or if nothing was negotiated. It returns false
@@ -1727,8 +1737,7 @@
 
 enum ssl_hs_wait_t ssl_get_finished(SSL_HANDSHAKE *hs);
 bool ssl_send_finished(SSL_HANDSHAKE *hs);
-bool ssl_output_cert_chain(SSL *ssl);
-
+bool ssl_output_cert_chain(SSL_HANDSHAKE *hs);
 
 // SSLKEYLOGFILE functions.
 
@@ -1869,9 +1878,6 @@
   // ticket key. Only sessions with a matching value will be accepted.
   uint8_t sid_ctx_length = 0;
   uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH] = {0};
-
-  // If enable_early_data is true, early data can be sent and accepted.
-  bool enable_early_data:1;
 };
 
 // |SSL_PROTOCOL_METHOD| abstracts between TLS and DTLS.
@@ -2253,6 +2259,9 @@
   // ClientHello and pause the handshake in such a way that |SSL_get_error|
   // returns |SSL_HANDOFF|.
   bool handoff:1;
+
+  // If enable_early_data is true, early data can be sent and accepted.
+  bool enable_early_data : 1;
 };
 
 // An ssl_shutdown_t describes the shutdown state of one end of the connection,
@@ -2580,147 +2589,90 @@
   unsigned timeout_duration_ms = 0;
 };
 
-// SSLConnection backs the public |SSL| type. Due to compatibility constraints,
-// it is a base class for |ssl_st|.
-struct SSLConnection {
-  // method is the method table corresponding to the current protocol (DTLS or
-  // TLS).
-  const SSL_PROTOCOL_METHOD *method;
+// SSL_CONFIG contains configuration bits that can be shed after the handshake
+// completes.  Objects of this type are not shared; they are unique to a
+// particular |SSL|.
+//
+// See SSL_shed_handshake_config() for more about the conditions under which
+// configuration can be shed.
+struct SSL_CONFIG {
+  explicit SSL_CONFIG(SSL *ssl_arg);
+  ~SSL_CONFIG();
 
-  // version is the protocol version.
-  uint16_t version;
+  // ssl is a non-owning pointer to the parent |SSL| object.
+  SSL *const ssl = nullptr;
 
   // 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;
+  uint16_t conf_max_version = 0;
 
   // 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 conf_min_version = 0;
 
-  uint16_t max_send_fragment;
-
-  // There are 2 BIO's even though they are normally both the same. This is so
-  // data can be read and written to different handlers
-
-  BIO *rbio;  // used by SSL_read
-  BIO *wbio;  // used by SSL_write
-
-  // do_handshake runs the handshake. On completion, it returns |ssl_hs_ok|.
-  // Otherwise, it returns a value corresponding to what operation is needed to
-  // progress.
-  enum ssl_hs_wait_t (*do_handshake)(SSL_HANDSHAKE *hs);
-
-  SSL3_STATE *s3;   // SSLv3 variables
-  DTLS1_STATE *d1;  // DTLSv1 variables
-
-  // callback that allows applications to peek at protocol messages
-  void (*msg_callback)(int write_p, int version, int content_type,
-                       const void *buf, size_t len, SSL *ssl, void *arg);
-  void *msg_callback_arg;
-
-  X509_VERIFY_PARAM *param;
+  X509_VERIFY_PARAM *param = nullptr;
 
   // crypto
-  SSLCipherPreferenceList *cipher_list;
-
-  // session info
+  SSLCipherPreferenceList *cipher_list = nullptr;
 
   // This is used to hold the local certificate used (i.e. the server
   // certificate for a server or the client certificate for a client).
-  CERT *cert;
-
-  // initial_timeout_duration_ms is the default DTLS timeout duration in
-  // milliseconds. It's used to initialize the timer any time it's restarted.
-  unsigned initial_timeout_duration_ms;
-
-  // tls13_variant is the variant of TLS 1.3 we are using for this
-  // configuration.
-  enum tls13_variant_t tls13_variant;
-
-  // session is the configured session to be offered by the client. This session
-  // is immutable.
-  SSL_SESSION *session;
+  CERT *cert = nullptr;
 
   int (*verify_callback)(int ok,
-                         X509_STORE_CTX *ctx);  // fail if callback returns 0
+                         X509_STORE_CTX *ctx) =
+      nullptr;  // fail if callback returns 0
 
-  enum ssl_verify_result_t (*custom_verify_callback)(SSL *ssl,
-                                                     uint8_t *out_alert);
-
-  void (*info_callback)(const SSL *ssl, int type, int value);
-
+  enum ssl_verify_result_t (*custom_verify_callback)(
+      SSL *ssl, uint8_t *out_alert) = nullptr;
   // Server-only: psk_identity_hint is the identity hint to send in
   // PSK-based key exchanges.
-  char *psk_identity_hint;
+  char *psk_identity_hint = nullptr;
 
   unsigned int (*psk_client_callback)(SSL *ssl, const char *hint,
                                       char *identity,
                                       unsigned int max_identity_len,
-                                      uint8_t *psk, unsigned int max_psk_len);
+                                      uint8_t *psk,
+                                      unsigned int max_psk_len) = nullptr;
   unsigned int (*psk_server_callback)(SSL *ssl, const char *identity,
-                                      uint8_t *psk, unsigned int max_psk_len);
-
-  SSL_CTX *ctx;
-
-  // extra application data
-  CRYPTO_EX_DATA ex_data;
+                                      uint8_t *psk,
+                                      unsigned int max_psk_len) = nullptr;
 
   // for server side, keep the list of CA_dn we can use
-  STACK_OF(CRYPTO_BUFFER) *client_CA;
+  STACK_OF(CRYPTO_BUFFER) *client_CA = nullptr;
 
   // cached_x509_client_CA is a cache of parsed versions of the elements of
   // |client_CA|.
-  STACK_OF(X509_NAME) *cached_x509_client_CA;
+  STACK_OF(X509_NAME) *cached_x509_client_CA = nullptr;
 
-  uint32_t options;  // protocol behaviour
-  uint32_t mode;     // API behaviour
-  uint32_t max_cert_list;
-  uint16_t dummy_pq_padding_len;
-  char *tlsext_hostname;
-  size_t supported_group_list_len;
-  uint16_t *supported_group_list;  // our list
+  uint16_t dummy_pq_padding_len = 0;
+  size_t supported_group_list_len = 0;
+  uint16_t *supported_group_list = nullptr;  // our list
 
   // session_ctx is the |SSL_CTX| used for the session cache and related
   // settings.
-  SSL_CTX *session_ctx;
-
-  // srtp_profiles is the list of configured SRTP protection profiles for
-  // DTLS-SRTP.
-  STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles;
+  SSL_CTX *session_ctx = nullptr;
 
   // The client's Channel ID private key.
-  EVP_PKEY *tlsext_channel_id_private;
+  EVP_PKEY *tlsext_channel_id_private = nullptr;
 
   // For a client, this contains the list of supported protocols in wire
   // format.
-  uint8_t *alpn_client_proto_list;
-  unsigned alpn_client_proto_list_len;
+  uint8_t *alpn_client_proto_list = nullptr;
+  unsigned alpn_client_proto_list_len = 0;
 
   // Contains a list of supported Token Binding key parameters.
-  uint8_t *token_binding_params;
-  size_t token_binding_params_len;
+  uint8_t *token_binding_params = nullptr;
+  size_t token_binding_params_len = 0;
 
   // Contains the QUIC transport params that this endpoint will send.
-  uint8_t *quic_transport_params;
-  size_t quic_transport_params_len;
-
-  // renegotiate_mode controls how peer renegotiation attempts are handled.
-  enum ssl_renegotiate_mode_t renegotiate_mode;
+  uint8_t *quic_transport_params = nullptr;
+  size_t quic_transport_params_len = 0;
 
   // verify_mode is a bitmask of |SSL_VERIFY_*| values.
-  uint8_t verify_mode;
-
-  // server is true iff the this SSL* is the server half. Note: before the SSL*
-  // is initialized by either SSL_set_accept_state or SSL_set_connect_state,
-  // the side is not determined. In this state, server is always false.
-  bool server:1;
-
-  // quiet_shutdown is true if the connection should not send a close_notify on
-  // shutdown.
-  bool quiet_shutdown:1;
+  uint8_t verify_mode = SSL_VERIFY_NONE;
 
   // Enable signed certificate time stamps. Currently client only.
   bool signed_cert_timestamps_enabled:1;
@@ -2745,15 +2697,97 @@
   // 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;
+  // shed_handshake_config indicates that the handshake config (this object!)
+  // should be freed after the handshake completes.
+  bool shed_handshake_config : 1;
+};
+
+// SSLConnection backs the public |SSL| type. Due to compatibility constraints,
+// it is a base class for |ssl_st|.
+struct SSLConnection {
+  // method is the method table corresponding to the current protocol (DTLS or
+  // TLS).
+  const SSL_PROTOCOL_METHOD *method;
+
+  // config is a container for handshake configuration.  Accesses to this field
+  // should check for nullptr, since configuration may be shed after the
+  // handshake completes.  (If you have the |SSL_HANDSHAKE| object at hand, use
+  // that instead, and skip the null check.)
+  SSL_CONFIG *config;
+
+  // version is the protocol version.
+  uint16_t version;
+
+  uint16_t max_send_fragment;
+
+  // There are 2 BIO's even though they are normally both the same. This is so
+  // data can be read and written to different handlers
+
+  BIO *rbio;  // used by SSL_read
+  BIO *wbio;  // used by SSL_write
+
+  // do_handshake runs the handshake. On completion, it returns |ssl_hs_ok|.
+  // Otherwise, it returns a value corresponding to what operation is needed to
+  // progress.
+  enum ssl_hs_wait_t (*do_handshake)(SSL_HANDSHAKE *hs);
+
+  SSL3_STATE *s3;   // SSLv3 variables
+  DTLS1_STATE *d1;  // DTLSv1 variables
+
+  // callback that allows applications to peek at protocol messages
+  void (*msg_callback)(int write_p, int version, int content_type,
+                       const void *buf, size_t len, SSL *ssl, void *arg);
+  void *msg_callback_arg;
+
+  // session info
+
+  // initial_timeout_duration_ms is the default DTLS timeout duration in
+  // milliseconds. It's used to initialize the timer any time it's restarted.
+  unsigned initial_timeout_duration_ms;
+
+  // tls13_variant is the variant of TLS 1.3 we are using for this
+  // configuration.
+  enum tls13_variant_t tls13_variant;
+
+  // session is the configured session to be offered by the client. This session
+  // is immutable.
+  SSL_SESSION *session;
+
+  void (*info_callback)(const SSL *ssl, int type, int value);
+
+  SSL_CTX *ctx;
+
+  // extra application data
+  CRYPTO_EX_DATA ex_data;
+
+  uint32_t options;  // protocol behaviour
+  uint32_t mode;     // API behaviour
+  uint32_t max_cert_list;
+  char *tlsext_hostname;
+
+  // srtp_profiles is the list of configured SRTP protection profiles for
+  // DTLS-SRTP.
+  STACK_OF(SRTP_PROTECTION_PROFILE) * srtp_profiles;
+
+  // renegotiate_mode controls how peer renegotiation attempts are handled.
+  enum ssl_renegotiate_mode_t renegotiate_mode;
+
+  // server is true iff the this SSL* is the server half. Note: before the SSL*
+  // is initialized by either SSL_set_accept_state or SSL_set_connect_state,
+  // the side is not determined. In this state, server is always false.
+  bool server : 1;
+
+  // quiet_shutdown is true if the connection should not send a close_notify on
+  // shutdown.
+  bool quiet_shutdown : 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.
   bool did_dummy_pq_padding:1;
+
+  // If enable_early_data is true, early data can be sent and accepted.
+  bool enable_early_data : 1;
 };
 
 // From draft-ietf-tls-tls13-18, used in determining PSK modes.
@@ -2780,7 +2814,7 @@
                                        const EVP_PKEY *privkey);
 int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey);
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
-int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
+int ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out, const SSL_SESSION *session);
 int ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx);
 
 // ssl_session_new returns a newly-allocated blank |SSL_SESSION| or nullptr on
@@ -2799,8 +2833,9 @@
 int ssl_session_serialize(const SSL_SESSION *in, CBB *cbb);
 
 // ssl_session_is_context_valid returns one if |session|'s session ID context
-// matches the one set on |ssl| and zero otherwise.
-int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session);
+// matches the one set on |hs| and zero otherwise.
+int ssl_session_is_context_valid(const SSL_HANDSHAKE *hs,
+                                 const SSL_SESSION *session);
 
 // ssl_session_is_time_valid returns one if |session| is still valid and zero if
 // it has expired.
@@ -2827,7 +2862,7 @@
 // |ssl_hs_pending_session| and should be called again. If a ticket could not be
 // decrypted immediately it returns |ssl_hs_pending_ticket| and should also
 // be called again. Otherwise, it returns |ssl_hs_error|.
-enum ssl_hs_wait_t ssl_get_prev_session(SSL *ssl,
+enum ssl_hs_wait_t ssl_get_prev_session(SSL_HANDSHAKE *hs,
                                         UniquePtr<SSL_SESSION> *out_session,
                                         bool *out_tickets_supported,
                                         bool *out_renew_ticket,
@@ -2856,10 +2891,6 @@
 void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session,
                                uint32_t timeout);
 
-// ssl_get_cipher_preferences returns the cipher preference list for TLS 1.2 and
-// below.
-const SSLCipherPreferenceList *ssl_get_cipher_preferences(const SSL *ssl);
-
 void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
 
 int ssl_send_alert(SSL *ssl, int level, int desc);
@@ -2952,11 +2983,11 @@
                                 Span<const uint8_t> premaster);
 
 // tls1_get_grouplist returns the locally-configured group preference list.
-Span<const uint16_t> tls1_get_grouplist(const SSL *ssl);
+Span<const uint16_t> tls1_get_grouplist(const SSL_HANDSHAKE *ssl);
 
 // tls1_check_group_id returns one if |group_id| is consistent with
 // locally-configured group preferences.
-int tls1_check_group_id(const SSL *ssl, uint16_t group_id);
+int tls1_check_group_id(const SSL_HANDSHAKE *ssl, uint16_t group_id);
 
 // tls1_get_shared_group sets |*out_group_id| to the first preferred shared
 // group between client and server preferences and returns one. If none may be
@@ -3000,9 +3031,9 @@
 //       Retry later.
 //   |ssl_ticket_aead_error|: an error occured that is fatal to the connection.
 enum ssl_ticket_aead_result_t ssl_process_ticket(
-    SSL *ssl, UniquePtr<SSL_SESSION> *out_session, bool *out_renew_ticket,
-    const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id,
-    size_t session_id_len);
+    SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
+    bool *out_renew_ticket, const uint8_t *ticket, size_t ticket_len,
+    const uint8_t *session_id, size_t session_id_len);
 
 // tls1_verify_channel_id processes |msg| as a Channel ID message, and verifies
 // the signature. If the key is valid, it saves the Channel ID and returns
@@ -3021,11 +3052,11 @@
 
 int tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs);
 
-// ssl_do_channel_id_callback checks runs |ssl->ctx->channel_id_cb| if
+// ssl_do_channel_id_callback checks runs |hs->ssl->ctx->channel_id_cb| if
 // necessary. It returns one on success and zero on fatal error. Note that, on
-// success, |ssl->tlsext_channel_id_private| may be unset, in which case the
+// success, |hs->ssl->tlsext_channel_id_private| may be unset, in which case the
 // operation should be retried later.
-int ssl_do_channel_id_callback(SSL *ssl);
+int ssl_do_channel_id_callback(SSL_HANDSHAKE *hs);
 
 // ssl_can_write returns one if |ssl| is allowed to write and zero otherwise.
 int ssl_can_write(const SSL *ssl);
@@ -3097,22 +3128,23 @@
   // session_verify_cert_chain verifies the certificate chain in |session|,
   // sets |session->verify_result| and returns one on success or zero on
   // error.
-  int (*session_verify_cert_chain)(SSL_SESSION *session, SSL *ssl,
+  int (*session_verify_cert_chain)(SSL_SESSION *session,
+                                   bssl::SSL_HANDSHAKE *ssl,
                                    uint8_t *out_alert);
 
   // hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|.
   void (*hs_flush_cached_ca_names)(bssl::SSL_HANDSHAKE *hs);
-  // ssl_new does any neccessary initialisation of |ssl|. It returns one on
+  // ssl_new does any neccessary initialisation of |hs|. It returns one on
   // success or zero on error.
-  int (*ssl_new)(SSL *ssl);
+  int (*ssl_new)(bssl::SSL_HANDSHAKE *hs);
   // ssl_free frees anything created by |ssl_new|.
-  void (*ssl_free)(SSL *ssl);
+  void (*ssl_config_free)(bssl::SSL_CONFIG *cfg);
   // ssl_flush_cached_client_CA drops any cached |X509_NAME|s from |ssl|.
-  void (*ssl_flush_cached_client_CA)(SSL *ssl);
+  void (*ssl_flush_cached_client_CA)(bssl::SSL_CONFIG *cfg);
   // ssl_auto_chain_if_needed runs the deprecated auto-chaining logic if
   // necessary. On success, it updates |ssl|'s certificate configuration as
   // needed and returns one. Otherwise, it returns zero.
-  int (*ssl_auto_chain_if_needed)(SSL *ssl);
+  int (*ssl_auto_chain_if_needed)(bssl::SSL_HANDSHAKE *hs);
   // ssl_ctx_new does any neccessary initialisation of |ctx|. It returns one on
   // success or zero on error.
   int (*ssl_ctx_new)(SSL_CTX *ctx);
diff --git a/ssl/s3_lib.cc b/ssl/s3_lib.cc
index b1fc5fb..d6a25b7 100644
--- a/ssl/s3_lib.cc
+++ b/ssl/s3_lib.cc
@@ -215,12 +215,4 @@
   ssl->s3 = NULL;
 }
 
-const SSLCipherPreferenceList *ssl_get_cipher_preferences(const SSL *ssl) {
-  if (ssl->cipher_list != NULL) {
-    return ssl->cipher_list;
-  }
-
-  return ssl->ctx->cipher_list;
-}
-
 }  // namespace bssl
diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc
index 20b4514..730280c 100644
--- a/ssl/ssl_cert.cc
+++ b/ssl/ssl_cert.cc
@@ -136,7 +136,7 @@
 namespace bssl {
 
 CERT::CERT(const SSL_X509_METHOD *x509_method_arg)
-    : x509_method(x509_method_arg), enable_early_data(false) {}
+    : x509_method(x509_method_arg) {}
 
 CERT::~CERT() {
   ssl_cert_clear_certs(this);
@@ -192,8 +192,6 @@
   ret->sid_ctx_length = cert->sid_ctx_length;
   OPENSSL_memcpy(ret->sid_ctx, cert->sid_ctx, sizeof(ret->sid_ctx));
 
-  ret->enable_early_data = cert->enable_early_data;
-
   return ret;
 }
 
@@ -342,10 +340,10 @@
   return 1;
 }
 
-int ssl_has_certificate(const SSL *ssl) {
-  return ssl->cert->chain != nullptr &&
-         sk_CRYPTO_BUFFER_value(ssl->cert->chain.get(), 0) != nullptr &&
-         ssl_has_private_key(ssl);
+int ssl_has_certificate(const SSL_CONFIG *cfg) {
+  return cfg->cert->chain != nullptr &&
+         sk_CRYPTO_BUFFER_value(cfg->cert->chain.get(), 0) != nullptr &&
+         ssl_has_private_key(cfg);
 }
 
 bool ssl_parse_cert_chain(uint8_t *out_alert,
@@ -412,8 +410,8 @@
   return true;
 }
 
-int ssl_add_cert_chain(SSL *ssl, CBB *cbb) {
-  if (!ssl_has_certificate(ssl)) {
+int ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb) {
+  if (!ssl_has_certificate(hs->config)) {
     return CBB_add_u24(cbb, 0);
   }
 
@@ -423,7 +421,7 @@
     return 0;
   }
 
-  STACK_OF(CRYPTO_BUFFER) *chain = ssl->cert->chain.get();
+  STACK_OF(CRYPTO_BUFFER) *chain = hs->config->cert->chain.get();
   for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain); i++) {
     CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(chain, i);
     CBB child;
@@ -673,10 +671,10 @@
   return ret;
 }
 
-bool ssl_has_client_CAs(SSL *ssl) {
-  STACK_OF(CRYPTO_BUFFER) *names = ssl->client_CA;
+bool ssl_has_client_CAs(const SSL_CONFIG *cfg) {
+  STACK_OF(CRYPTO_BUFFER) *names = cfg->client_CA;
   if (names == NULL) {
-    names = ssl->ctx->client_CA;
+    names = cfg->ssl->ctx->client_CA;
   }
   if (names == NULL) {
     return false;
@@ -684,15 +682,15 @@
   return sk_CRYPTO_BUFFER_num(names) > 0;
 }
 
-int ssl_add_client_CA_list(SSL *ssl, CBB *cbb) {
+int ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb) {
   CBB child, name_cbb;
   if (!CBB_add_u16_length_prefixed(cbb, &child)) {
     return 0;
   }
 
-  STACK_OF(CRYPTO_BUFFER) *names = ssl->client_CA;
+  STACK_OF(CRYPTO_BUFFER) *names = hs->config->client_CA;
   if (names == NULL) {
-    names = ssl->ctx->client_CA;
+    names = hs->ssl->ctx->client_CA;
   }
   if (names == NULL) {
     return CBB_flush(cbb);
@@ -711,8 +709,7 @@
 
 int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
                                const CRYPTO_BUFFER *leaf) {
-  SSL *const ssl = hs->ssl;
-  assert(ssl_protocol_version(ssl) < TLS1_3_VERSION);
+  assert(ssl_protocol_version(hs->ssl) < TLS1_3_VERSION);
 
   // Check the certificate's type matches the cipher.
   if (!(hs->new_cipher->algorithm_auth & ssl_cipher_auth_mask_for_key(pkey))) {
@@ -740,7 +737,7 @@
     uint16_t group_id;
     if (!ssl_nid_to_group_id(
             &group_id, EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key))) ||
-        !tls1_check_group_id(ssl, group_id) ||
+        !tls1_check_group_id(hs, group_id) ||
         EC_KEY_get_conv_form(ec_key) != POINT_CONVERSION_UNCOMPRESSED) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECC_CERT);
       return 0;
@@ -752,18 +749,18 @@
 
 int ssl_on_certificate_selected(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_has_certificate(ssl)) {
+  if (!ssl_has_certificate(hs->config)) {
     // Nothing to do.
     return 1;
   }
 
-  if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(ssl)) {
+  if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(hs)) {
     return 0;
   }
 
   CBS leaf;
-  CRYPTO_BUFFER_init_CBS(sk_CRYPTO_BUFFER_value(ssl->cert->chain.get(), 0),
-                         &leaf);
+  CRYPTO_BUFFER_init_CBS(
+      sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0), &leaf);
 
   hs->local_pubkey = ssl_cert_parse_pubkey(&leaf);
   return hs->local_pubkey != NULL;
@@ -776,7 +773,10 @@
 int SSL_set_chain_and_key(SSL *ssl, CRYPTO_BUFFER *const *certs,
                           size_t num_certs, EVP_PKEY *privkey,
                           const SSL_PRIVATE_KEY_METHOD *privkey_method) {
-  return cert_set_chain_and_key(ssl->cert, certs, num_certs, privkey,
+  if (!ssl->config) {
+    return 0;
+  }
+  return cert_set_chain_and_key(ssl->config->cert, certs, num_certs, privkey,
                                 privkey_method);
 }
 
@@ -799,11 +799,11 @@
 
 int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
   UniquePtr<CRYPTO_BUFFER> buffer(CRYPTO_BUFFER_new(der, der_len, NULL));
-  if (!buffer) {
+  if (!buffer || !ssl->config) {
     return 0;
   }
 
-  return ssl_set_cert(ssl->cert, std::move(buffer));
+  return ssl_set_cert(ssl->config->cert, std::move(buffer));
 }
 
 void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg),
@@ -812,7 +812,10 @@
 }
 
 void SSL_set_cert_cb(SSL *ssl, int (*cb)(SSL *ssl, void *arg), void *arg) {
-  ssl_cert_set_cert_cb(ssl->cert, cb, arg);
+  if (!ssl->config) {
+    return;
+  }
+  ssl_cert_set_cert_cb(ssl->config->cert, cb, arg);
 }
 
 STACK_OF(CRYPTO_BUFFER) *SSL_get0_peer_certificates(const SSL *ssl) {
@@ -852,7 +855,10 @@
 
 int SSL_set_signed_cert_timestamp_list(SSL *ssl, const uint8_t *list,
                                        size_t list_len) {
-  return set_signed_cert_timestamp_list(ssl->cert, list, list_len);
+  if (!ssl->config) {
+    return 0;
+  }
+  return set_signed_cert_timestamp_list(ssl->config->cert, list, list_len);
 }
 
 int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
@@ -864,9 +870,12 @@
 
 int SSL_set_ocsp_response(SSL *ssl, const uint8_t *response,
                           size_t response_len) {
-  ssl->cert->ocsp_response.reset(
+  if (!ssl->config) {
+    return 0;
+  }
+  ssl->config->cert->ocsp_response.reset(
       CRYPTO_BUFFER_new(response, response_len, nullptr));
-  return ssl->cert->ocsp_response != nullptr;
+  return ssl->config->cert->ocsp_response != nullptr;
 }
 
 void SSL_CTX_set0_client_CAs(SSL_CTX *ctx, STACK_OF(CRYPTO_BUFFER) *name_list) {
@@ -876,7 +885,10 @@
 }
 
 void SSL_set0_client_CAs(SSL *ssl, STACK_OF(CRYPTO_BUFFER) *name_list) {
-  ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
-  sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free);
-  ssl->client_CA = name_list;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl->config);
+  sk_CRYPTO_BUFFER_pop_free(ssl->config->client_CA, CRYPTO_BUFFER_free);
+  ssl->config->client_CA = name_list;
 }
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 78a1860..1e5b3d1 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -274,7 +274,7 @@
 
 void ssl_update_cache(SSL_HANDSHAKE *hs, int mode) {
   SSL *const ssl = hs->ssl;
-  SSL_CTX *ctx = ssl->session_ctx;
+  SSL_CTX *ctx = hs->config->session_ctx;
   // Never cache sessions with empty session IDs.
   if (ssl->s3->established_session->session_id_length == 0 ||
       ssl->s3->established_session->not_resumable ||
@@ -648,8 +648,12 @@
   }
   OPENSSL_memset(ssl, 0, sizeof(SSL));
 
-  ssl->conf_min_version = ctx->conf_min_version;
-  ssl->conf_max_version = ctx->conf_max_version;
+  ssl->config = New<SSL_CONFIG>(ssl);
+  if (ssl->config == nullptr) {
+    goto err;
+  }
+  ssl->config->conf_min_version = ctx->conf_min_version;
+  ssl->config->conf_max_version = ctx->conf_max_version;
   ssl->tls13_variant = ctx->tls13_variant;
 
   // RFC 6347 states that implementations SHOULD use an initial timer value of
@@ -660,47 +664,44 @@
   ssl->mode = ctx->mode;
   ssl->max_cert_list = ctx->max_cert_list;
 
-  ssl->cert = ssl_cert_dup(ctx->cert).release();
-  if (ssl->cert == NULL) {
+  ssl->config->cert = ssl_cert_dup(ctx->cert).release();
+  if (ssl->config->cert == NULL) {
     goto err;
   }
 
   ssl->msg_callback = ctx->msg_callback;
   ssl->msg_callback_arg = ctx->msg_callback_arg;
-  ssl->verify_mode = ctx->verify_mode;
-  ssl->verify_callback = ctx->default_verify_callback;
-  ssl->custom_verify_callback = ctx->custom_verify_callback;
-  ssl->retain_only_sha256_of_client_certs =
+  ssl->config->verify_mode = ctx->verify_mode;
+  ssl->config->verify_callback = ctx->default_verify_callback;
+  ssl->config->custom_verify_callback = ctx->custom_verify_callback;
+  ssl->config->retain_only_sha256_of_client_certs =
       ctx->retain_only_sha256_of_client_certs;
 
   ssl->quiet_shutdown = ctx->quiet_shutdown;
   ssl->max_send_fragment = ctx->max_send_fragment;
+  ssl->enable_early_data = ctx->enable_early_data;
 
   SSL_CTX_up_ref(ctx);
   ssl->ctx = ctx;
   SSL_CTX_up_ref(ctx);
-  ssl->session_ctx = ctx;
-
-  if (!ssl->ctx->x509_method->ssl_new(ssl)) {
-    goto err;
-  }
+  ssl->config->session_ctx = ctx;
 
   if (ctx->supported_group_list) {
-    ssl->supported_group_list = (uint16_t *)BUF_memdup(
+    ssl->config->supported_group_list = (uint16_t *)BUF_memdup(
         ctx->supported_group_list, ctx->supported_group_list_len * 2);
-    if (!ssl->supported_group_list) {
+    if (!ssl->config->supported_group_list) {
       goto err;
     }
-    ssl->supported_group_list_len = ctx->supported_group_list_len;
+    ssl->config->supported_group_list_len = ctx->supported_group_list_len;
   }
 
   if (ctx->alpn_client_proto_list) {
-    ssl->alpn_client_proto_list = (uint8_t *)BUF_memdup(
+    ssl->config->alpn_client_proto_list = (uint8_t *)BUF_memdup(
         ctx->alpn_client_proto_list, ctx->alpn_client_proto_list_len);
-    if (ssl->alpn_client_proto_list == NULL) {
+    if (ssl->config->alpn_client_proto_list == NULL) {
       goto err;
     }
-    ssl->alpn_client_proto_list_len = ctx->alpn_client_proto_list_len;
+    ssl->config->alpn_client_proto_list_len = ctx->alpn_client_proto_list_len;
   }
 
   ssl->method = ctx->method;
@@ -709,27 +710,31 @@
     goto err;
   }
 
+  if (!ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) {
+    goto err;
+  }
+
   CRYPTO_new_ex_data(&ssl->ex_data);
 
-  ssl->psk_identity_hint = NULL;
   if (ctx->psk_identity_hint) {
-    ssl->psk_identity_hint = BUF_strdup(ctx->psk_identity_hint);
-    if (ssl->psk_identity_hint == NULL) {
+    ssl->config->psk_identity_hint = BUF_strdup(ctx->psk_identity_hint);
+    if (ssl->config->psk_identity_hint == NULL) {
       goto err;
     }
   }
-  ssl->psk_client_callback = ctx->psk_client_callback;
-  ssl->psk_server_callback = ctx->psk_server_callback;
+  ssl->config->psk_client_callback = ctx->psk_client_callback;
+  ssl->config->psk_server_callback = ctx->psk_server_callback;
 
-  ssl->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled;
+  ssl->config->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled;
   if (ctx->tlsext_channel_id_private) {
     EVP_PKEY_up_ref(ctx->tlsext_channel_id_private);
-    ssl->tlsext_channel_id_private = ctx->tlsext_channel_id_private;
+    ssl->config->tlsext_channel_id_private = ctx->tlsext_channel_id_private;
   }
 
-  ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled;
-  ssl->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
-  ssl->handoff = ctx->handoff;
+  ssl->config->signed_cert_timestamps_enabled =
+      ctx->signed_cert_timestamps_enabled;
+  ssl->config->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
+  ssl->config->handoff = ctx->handoff;
 
   return ssl;
 
@@ -740,36 +745,49 @@
   return NULL;
 }
 
+SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg)
+    : ssl(ssl_arg),
+      signed_cert_timestamps_enabled(false),
+      ocsp_stapling_enabled(false),
+      tlsext_channel_id_enabled(false),
+      retain_only_sha256_of_client_certs(false),
+      handoff(false),
+      shed_handshake_config(false) {
+  assert(ssl);
+}
+
+SSL_CONFIG::~SSL_CONFIG() {
+  if (ssl->ctx != nullptr) {
+    ssl->ctx->x509_method->ssl_config_free(this);
+  }
+  Delete(cipher_list);
+  Delete(cert);
+  SSL_CTX_free(session_ctx);
+  OPENSSL_free(supported_group_list);
+  OPENSSL_free(alpn_client_proto_list);
+  OPENSSL_free(token_binding_params);
+  OPENSSL_free(quic_transport_params);
+  EVP_PKEY_free(tlsext_channel_id_private);
+  OPENSSL_free(psk_identity_hint);
+  sk_CRYPTO_BUFFER_pop_free(client_CA, CRYPTO_BUFFER_free);
+}
+
 void SSL_free(SSL *ssl) {
   if (ssl == NULL) {
     return;
   }
 
-  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);
   BIO_free_all(ssl->wbio);
 
-  // add extra stuff
-  Delete(ssl->cipher_list);
+  Delete(ssl->config);
+  ssl->config = NULL;
 
   SSL_SESSION_free(ssl->session);
 
-  Delete(ssl->cert);
-
   OPENSSL_free(ssl->tlsext_hostname);
-  SSL_CTX_free(ssl->session_ctx);
-  OPENSSL_free(ssl->supported_group_list);
-  OPENSSL_free(ssl->alpn_client_proto_list);
-  OPENSSL_free(ssl->token_binding_params);
-  OPENSSL_free(ssl->quic_transport_params);
-  EVP_PKEY_free(ssl->tlsext_channel_id_private);
-  OPENSSL_free(ssl->psk_identity_hint);
-  sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free);
   sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles);
 
   if (ssl->method != NULL) {
@@ -863,6 +881,12 @@
 
   // Destroy the handshake object if the handshake has completely finished.
   if (!early_return) {
+    if (hs->config->shed_handshake_config) {
+      assert(ssl->config == hs->config);
+      Delete(ssl->config);
+      ssl->config = nullptr;
+      hs->config = nullptr;
+    }
     ssl->s3->hs.reset();
   }
 
@@ -923,6 +947,11 @@
       break;
   }
 
+  // Reject renegotiation when the handshake config has been shed.
+  if (!ssl->config) {
+    goto no_renegotiation;
+  }
+
   // Renegotiation is only supported at quiescent points in the application
   // protocol, namely in HTTPS, just before reading the HTTP response. Require
   // the record-layer be idle and avoid complexities of sending a handshake
@@ -1161,11 +1190,15 @@
 
 int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
                                   size_t params_len) {
-  ssl->quic_transport_params = (uint8_t *)BUF_memdup(params, params_len);
-  if (!ssl->quic_transport_params) {
+  if (!ssl->config) {
     return 0;
   }
-  ssl->quic_transport_params_len = params_len;
+  ssl->config->quic_transport_params =
+      (uint8_t *)BUF_memdup(params, params_len);
+  if (!ssl->config->quic_transport_params) {
+    return 0;
+  }
+  ssl->config->quic_transport_params_len = params_len;
   return 1;
 }
 
@@ -1177,7 +1210,7 @@
 }
 
 void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled) {
-  ctx->cert->enable_early_data = !!enabled;
+  ctx->enable_early_data = !!enabled;
 }
 
 void SSL_CTX_set_tls13_variant(SSL_CTX *ctx, enum tls13_variant_t variant) {
@@ -1189,7 +1222,7 @@
 }
 
 void SSL_set_early_data_enabled(SSL *ssl, int enabled) {
-  ssl->cert->enable_early_data = !!enabled;
+  ssl->enable_early_data = !!enabled;
 }
 
 int SSL_in_early_data(const SSL *ssl) {
@@ -1436,15 +1469,28 @@
 
 int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx,
                                size_t sid_ctx_len) {
-  return set_session_id_context(ssl->cert, sid_ctx, sid_ctx_len);
+  if (!ssl->config) {
+    return 0;
+  }
+  return set_session_id_context(ssl->config->cert, sid_ctx, sid_ctx_len);
 }
 
 const uint8_t *SSL_get0_session_id_context(const SSL *ssl, size_t *out_len) {
-  *out_len = ssl->cert->sid_ctx_length;
-  return ssl->cert->sid_ctx;
+  if (!ssl->config) {
+    assert(ssl->config);
+    *out_len = 0;
+    return NULL;
+  }
+  *out_len = ssl->config->cert->sid_ctx_length;
+  return ssl->config->cert->sid_ctx;
 }
 
-void SSL_certs_clear(SSL *ssl) { ssl_cert_clear_certs(ssl->cert); }
+void SSL_certs_clear(SSL *ssl) {
+  if (!ssl->config) {
+    return;
+  }
+  ssl_cert_clear_certs(ssl->config->cert);
+}
 
 int SSL_get_fd(const SSL *ssl) { return SSL_get_rfd(ssl); }
 
@@ -1557,7 +1603,13 @@
                        ssl->s3->previous_server_finished_len);
 }
 
-int SSL_get_verify_mode(const SSL *ssl) { return ssl->verify_mode; }
+int SSL_get_verify_mode(const SSL *ssl) {
+  if (!ssl->config) {
+    assert(ssl->config);
+    return -1;
+  }
+  return ssl->config->verify_mode;
+}
 
 int SSL_get_extms_support(const SSL *ssl) {
   // TLS 1.3 does not require extended master secret and always reports as
@@ -1601,7 +1653,11 @@
 
 // Fix this function so that it takes an optional type parameter
 int SSL_check_private_key(const SSL *ssl) {
-  return ssl_cert_check_private_key(ssl->cert, ssl->cert->privatekey.get());
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_cert_check_private_key(ssl->config->cert,
+                                    ssl->config->cert->privatekey.get());
 }
 
 long SSL_get_default_timeout(const SSL *ssl) {
@@ -1775,8 +1831,11 @@
 }
 
 int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) {
-  return tls1_set_curves(&ssl->supported_group_list,
-                         &ssl->supported_group_list_len, curves,
+  if (!ssl->config) {
+    return 0;
+  }
+  return tls1_set_curves(&ssl->config->supported_group_list,
+                         &ssl->config->supported_group_list_len, curves,
                          curves_len);
 }
 
@@ -1786,8 +1845,11 @@
 }
 
 int SSL_set1_curves_list(SSL *ssl, const char *curves) {
-  return tls1_set_curves_list(&ssl->supported_group_list,
-                              &ssl->supported_group_list_len, curves);
+  if (!ssl->config) {
+    return 0;
+  }
+  return tls1_set_curves_list(&ssl->config->supported_group_list,
+                              &ssl->config->supported_group_list_len, curves);
 }
 
 uint16_t SSL_get_curve_id(const SSL *ssl) {
@@ -1824,9 +1886,13 @@
   if (ssl == NULL) {
     return NULL;
   }
+  if (ssl->config == NULL) {
+    assert(ssl->config);
+    return NULL;
+  }
 
-  const SSLCipherPreferenceList *prefs = ssl_get_cipher_preferences(ssl);
-  return prefs == nullptr ? nullptr : prefs->ciphers.get();
+  return ssl->config->cipher_list ? ssl->config->cipher_list->ciphers.get()
+      : ssl->ctx->cipher_list->ciphers.get();
 }
 
 const char *SSL_get_cipher_list(const SSL *ssl, int n) {
@@ -1856,11 +1922,19 @@
 }
 
 int SSL_set_cipher_list(SSL *ssl, const char *str) {
-  return ssl_create_cipher_list(&ssl->cipher_list, str, false /* not strict */);
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_create_cipher_list(&ssl->config->cipher_list, str,
+                                false /* not strict */);
 }
 
 int SSL_set_strict_cipher_list(SSL *ssl, const char *str) {
-  return ssl_create_cipher_list(&ssl->cipher_list, str, true /* strict */);
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_create_cipher_list(&ssl->config->cipher_list, str,
+                                true /* strict */);
 }
 
 const char *SSL_get_servername(const SSL *ssl, const int type) {
@@ -1894,8 +1968,11 @@
 void SSL_set_custom_verify(
     SSL *ssl, int mode,
     enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)) {
-  ssl->verify_mode = mode;
-  ssl->custom_verify_callback = callback;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->verify_mode = mode;
+  ssl->config->custom_verify_callback = callback;
 }
 
 void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) {
@@ -1903,7 +1980,10 @@
 }
 
 void SSL_enable_signed_cert_timestamps(SSL *ssl) {
-  ssl->signed_cert_timestamps_enabled = true;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->signed_cert_timestamps_enabled = true;
 }
 
 void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx) {
@@ -1911,7 +1991,10 @@
 }
 
 void SSL_enable_ocsp_stapling(SSL *ssl) {
-  ssl->ocsp_stapling_enabled = true;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->ocsp_stapling_enabled = true;
 }
 
 void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out,
@@ -2040,12 +2123,16 @@
 }
 
 int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) {
-  OPENSSL_free(ssl->alpn_client_proto_list);
-  ssl->alpn_client_proto_list = (uint8_t *)BUF_memdup(protos, protos_len);
-  if (!ssl->alpn_client_proto_list) {
+  if (!ssl->config) {
+    return 0;
+  }
+  OPENSSL_free(ssl->config->alpn_client_proto_list);
+  ssl->config->alpn_client_proto_list =
+      (uint8_t *)BUF_memdup(protos, protos_len);
+  if (!ssl->config->alpn_client_proto_list) {
     return 1;
   }
-  ssl->alpn_client_proto_list_len = protos_len;
+  ssl->config->alpn_client_proto_list_len = protos_len;
 
   return 0;
 }
@@ -2084,7 +2171,10 @@
 }
 
 void SSL_set_tls_channel_id_enabled(SSL *ssl, int enabled) {
-  ssl->tlsext_channel_id_enabled = !!enabled;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->tlsext_channel_id_enabled = !!enabled;
 }
 
 int SSL_enable_tls_channel_id(SSL *ssl) {
@@ -2114,15 +2204,18 @@
 }
 
 int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) {
+  if (!ssl->config) {
+    return 0;
+  }
   if (!is_p256_key(private_key)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
     return 0;
   }
 
-  EVP_PKEY_free(ssl->tlsext_channel_id_private);
+  EVP_PKEY_free(ssl->config->tlsext_channel_id_private);
   EVP_PKEY_up_ref(private_key);
-  ssl->tlsext_channel_id_private = private_key;
-  ssl->tlsext_channel_id_enabled = true;
+  ssl->config->tlsext_channel_id_private = private_key;
+  ssl->config->tlsext_channel_id_enabled = true;
 
   return 1;
 }
@@ -2137,16 +2230,19 @@
 }
 
 int SSL_set_token_binding_params(SSL *ssl, const uint8_t *params, size_t len) {
+  if (!ssl->config) {
+    return 0;
+  }
   if (len > 256) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return 0;
   }
-  OPENSSL_free(ssl->token_binding_params);
-  ssl->token_binding_params = (uint8_t *)BUF_memdup(params, len);
-  if (!ssl->token_binding_params) {
+  OPENSSL_free(ssl->config->token_binding_params);
+  ssl->config->token_binding_params = (uint8_t *)BUF_memdup(params, len);
+  if (!ssl->config->token_binding_params) {
     return 0;
   }
-  ssl->token_binding_params_len = len;
+  ssl->config->token_binding_params_len = len;
   return 1;
 }
 
@@ -2168,8 +2264,12 @@
 }
 
 EVP_PKEY *SSL_get_privatekey(const SSL *ssl) {
-  if (ssl->cert != NULL) {
-    return ssl->cert->privatekey.get();
+  if (!ssl->config) {
+    assert(ssl->config);
+    return NULL;
+  }
+  if (ssl->config->cert != NULL) {
+    return ssl->config->cert->privatekey.get();
   }
 
   return NULL;
@@ -2244,6 +2344,9 @@
 SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { return ssl->ctx; }
 
 SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) {
+  if (!ssl->config) {
+    return NULL;
+  }
   if (ssl->ctx == ctx) {
     return ssl->ctx;
   }
@@ -2255,15 +2358,16 @@
   }
 
   if (ctx == NULL) {
-    ctx = ssl->session_ctx;
+    ctx = ssl->config->session_ctx;
   }
 
-  Delete(ssl->cert);
-  ssl->cert = ssl_cert_dup(ctx->cert).release();
+  Delete(ssl->config->cert);
+  ssl->config->cert = ssl_cert_dup(ctx->cert).release();
 
   SSL_CTX_up_ref(ctx);
   SSL_CTX_free(ssl->ctx);
   ssl->ctx = ctx;
+  ssl->enable_early_data = ssl->ctx->enable_early_data;
 
   return ssl->ctx;
 }
@@ -2374,14 +2478,21 @@
 }
 
 int SSL_use_psk_identity_hint(SSL *ssl, const char *identity_hint) {
-  return use_psk_identity_hint(&ssl->psk_identity_hint, identity_hint);
+  if (!ssl->config) {
+    return 0;
+  }
+  return use_psk_identity_hint(&ssl->config->psk_identity_hint, identity_hint);
 }
 
 const char *SSL_get_psk_identity_hint(const SSL *ssl) {
   if (ssl == NULL) {
     return NULL;
   }
-  return ssl->psk_identity_hint;
+  if (ssl->config == NULL) {
+    assert(ssl->config);
+    return NULL;
+  }
+  return ssl->config->psk_identity_hint;
 }
 
 const char *SSL_get_psk_identity(const SSL *ssl) {
@@ -2399,7 +2510,10 @@
     SSL *ssl, unsigned (*cb)(SSL *ssl, const char *hint, char *identity,
                              unsigned max_identity_len, uint8_t *psk,
                              unsigned max_psk_len)) {
-  ssl->psk_client_callback = cb;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->psk_client_callback = cb;
 }
 
 void SSL_CTX_set_psk_client_callback(
@@ -2412,7 +2526,10 @@
 void SSL_set_psk_server_callback(
     SSL *ssl, unsigned (*cb)(SSL *ssl, const char *identity, uint8_t *psk,
                              unsigned max_psk_len)) {
-  ssl->psk_server_callback = cb;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->psk_server_callback = cb;
 }
 
 void SSL_CTX_set_psk_server_callback(
@@ -2422,11 +2539,14 @@
 }
 
 int SSL_set_dummy_pq_padding_size(SSL *ssl, size_t num_bytes) {
+  if (!ssl->config) {
+    return 0;
+  }
   if (num_bytes > 0xffff) {
     return 0;
   }
 
-  ssl->dummy_pq_padding_len = num_bytes;
+  ssl->config->dummy_pq_padding_len = num_bytes;
   return 1;
 }
 
@@ -2605,7 +2725,10 @@
 }
 
 void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl, int enabled) {
-  ssl->retain_only_sha256_of_client_certs = !!enabled;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->retain_only_sha256_of_client_certs = !!enabled;
 }
 
 void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) {
@@ -2626,7 +2749,18 @@
 
 int SSL_is_draft_downgrade(const SSL *ssl) { return ssl->s3->draft_downgrade; }
 
+void SSL_set_shed_handshake_config(SSL *ssl, int enable) {
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->shed_handshake_config = !!enable;
+}
+
 int SSL_clear(SSL *ssl) {
+  if (!ssl->config) {
+    return 0;  // SSL_clear may not be used after shedding config.
+  }
+
   // In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
   // established session to be offered the next time around. wpa_supplicant
   // depends on this behavior, so emulate it.
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc
index bba03b7..0fcd805 100644
--- a/ssl/ssl_privkey.cc
+++ b/ssl/ssl_privkey.cc
@@ -134,8 +134,8 @@
   return NULL;
 }
 
-int ssl_has_private_key(const SSL *ssl) {
-  return ssl->cert->privatekey != nullptr || ssl->cert->key_method != nullptr;
+int ssl_has_private_key(const SSL_CONFIG *cfg) {
+  return cfg->cert->privatekey != nullptr || cfg->cert->key_method != nullptr;
 }
 
 static int pkey_supports_algorithm(const SSL *ssl, EVP_PKEY *pkey,
@@ -196,13 +196,13 @@
     SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, size_t max_out,
     uint16_t sigalg, Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
-  if (ssl->cert->key_method != NULL) {
+  if (hs->config->cert->key_method != NULL) {
     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);
+      ret = hs->config->cert->key_method->complete(ssl, out, out_len, max_out);
     } else {
-      ret = ssl->cert->key_method->sign(ssl, out, out_len, max_out, sigalg,
-                                        in.data(), in.size());
+      ret = hs->config->cert->key_method->sign(ssl, out, out_len, max_out,
+                                               sigalg, in.data(), in.size());
     }
     if (ret == ssl_private_key_failure) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PRIVATE_KEY_OPERATION_FAILED);
@@ -213,7 +213,7 @@
 
   *out_len = max_out;
   ScopedEVP_MD_CTX ctx;
-  if (!setup_ctx(ssl, ctx.get(), ssl->cert->privatekey.get(), sigalg,
+  if (!setup_ctx(ssl, ctx.get(), hs->config->cert->privatekey.get(), sigalg,
                  0 /* sign */) ||
       !EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) {
     return ssl_private_key_failure;
@@ -236,13 +236,13 @@
                                                       size_t max_out,
                                                       Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
-  if (ssl->cert->key_method != NULL) {
+  if (hs->config->cert->key_method != NULL) {
     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);
+      ret = hs->config->cert->key_method->complete(ssl, out, out_len, max_out);
     } else {
-      ret = ssl->cert->key_method->decrypt(ssl, out, out_len, max_out,
-                                           in.data(), in.size());
+      ret = hs->config->cert->key_method->decrypt(ssl, out, out_len, max_out,
+                                                  in.data(), in.size());
     }
     if (ret == ssl_private_key_failure) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PRIVATE_KEY_OPERATION_FAILED);
@@ -251,7 +251,7 @@
     return ret;
   }
 
-  RSA *rsa = EVP_PKEY_get0_RSA(ssl->cert->privatekey.get());
+  RSA *rsa = EVP_PKEY_get0_RSA(hs->config->cert->privatekey.get());
   if (rsa == NULL) {
     // Decrypt operations are only supported for RSA keys.
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -294,7 +294,7 @@
 using namespace bssl;
 
 int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) {
-  if (rsa == NULL) {
+  if (rsa == NULL || ssl->config == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
@@ -306,7 +306,7 @@
     return 0;
   }
 
-  return ssl_set_pkey(ssl->cert, pkey.get());
+  return ssl_set_pkey(ssl->config->cert, pkey.get());
 }
 
 int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
@@ -320,12 +320,12 @@
 }
 
 int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) {
-  if (pkey == NULL) {
+  if (pkey == NULL || ssl->config == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
 
-  return ssl_set_pkey(ssl->cert, pkey);
+  return ssl_set_pkey(ssl->config->cert, pkey);
 }
 
 int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *der,
@@ -400,7 +400,10 @@
 
 void SSL_set_private_key_method(SSL *ssl,
                                 const SSL_PRIVATE_KEY_METHOD *key_method) {
-  ssl->cert->key_method = key_method;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->cert->key_method = key_method;
 }
 
 void SSL_CTX_set_private_key_method(SSL_CTX *ctx,
@@ -482,7 +485,10 @@
 
 int SSL_set_signing_algorithm_prefs(SSL *ssl, const uint16_t *prefs,
                                     size_t num_prefs) {
-  return ssl->cert->sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs));
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl->config->cert->sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs));
 }
 
 int SSL_CTX_set_verify_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs,
diff --git a/ssl/ssl_session.cc b/ssl/ssl_session.cc
index 272fc55..b992e9a 100644
--- a/ssl/ssl_session.cc
+++ b/ssl/ssl_session.cc
@@ -381,13 +381,13 @@
   if (version >= TLS1_3_VERSION) {
     // TLS 1.3 uses tickets as authenticators, so we are willing to use them for
     // longer.
-    session->timeout = ssl->session_ctx->session_psk_dhe_timeout;
+    session->timeout = hs->config->session_ctx->session_psk_dhe_timeout;
     session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
   } else {
     // TLS 1.2 resumption does not incorporate new key material, so we use a
     // much shorter timeout.
-    session->timeout = ssl->session_ctx->session_timeout;
-    session->auth_timeout = ssl->session_ctx->session_timeout;
+    session->timeout = hs->config->session_ctx->session_timeout;
+    session->auth_timeout = hs->config->session_ctx->session_timeout;
   }
 
   if (is_server) {
@@ -405,13 +405,13 @@
     session->session_id_length = 0;
   }
 
-  if (ssl->cert->sid_ctx_length > sizeof(session->sid_ctx)) {
+  if (hs->config->cert->sid_ctx_length > sizeof(session->sid_ctx)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
-  OPENSSL_memcpy(session->sid_ctx, ssl->cert->sid_ctx,
-                 ssl->cert->sid_ctx_length);
-  session->sid_ctx_length = ssl->cert->sid_ctx_length;
+  OPENSSL_memcpy(session->sid_ctx, hs->config->cert->sid_ctx,
+                 hs->config->cert->sid_ctx_length);
+  session->sid_ctx_length = hs->config->cert->sid_ctx_length;
 
   // The session is marked not resumable until it is completely filled in.
   session->not_resumable = 1;
@@ -475,7 +475,7 @@
   return 1;
 }
 
-static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out,
+static int ssl_encrypt_ticket_with_cipher_ctx(SSL_HANDSHAKE *hs, CBB *out,
                                               const uint8_t *session_buf,
                                               size_t session_len) {
   ScopedEVP_CIPHER_CTX ctx;
@@ -493,11 +493,11 @@
 
   // Initialize HMAC and cipher contexts. If callback present it does all the
   // work otherwise use generated values from parent ctx.
-  SSL_CTX *tctx = ssl->session_ctx;
+  SSL_CTX *tctx = hs->config->session_ctx;
   uint8_t iv[EVP_MAX_IV_LENGTH];
   uint8_t key_name[16];
   if (tctx->tlsext_ticket_key_cb != NULL) {
-    if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, ctx.get(), hctx.get(),
+    if (tctx->tlsext_ticket_key_cb(hs->ssl, key_name, iv, ctx.get(), hctx.get(),
                                    1 /* encrypt */) < 0) {
       return 0;
     }
@@ -554,11 +554,12 @@
   return 1;
 }
 
-static int ssl_encrypt_ticket_with_method(SSL *ssl, CBB *out,
+static int ssl_encrypt_ticket_with_method(SSL_HANDSHAKE *hs, CBB *out,
                                           const uint8_t *session_buf,
                                           size_t session_len) {
-  const SSL_TICKET_AEAD_METHOD *method = ssl->session_ctx->ticket_aead_method;
-  const size_t max_overhead = method->max_overhead(ssl);
+  const SSL_TICKET_AEAD_METHOD *method =
+      hs->config->session_ctx->ticket_aead_method;
+  const size_t max_overhead = method->max_overhead(hs->ssl);
   const size_t max_out = session_len + max_overhead;
   if (max_out < max_overhead) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
@@ -571,7 +572,8 @@
   }
 
   size_t out_len;
-  if (!method->seal(ssl, ptr, &out_len, max_out, session_buf, session_len)) {
+  if (!method->seal(hs->ssl, ptr, &out_len, max_out, session_buf,
+                    session_len)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_TICKET_ENCRYPTION_FAILED);
     return 0;
   }
@@ -583,7 +585,8 @@
   return 1;
 }
 
-int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
+int ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out,
+                       const SSL_SESSION *session) {
   // Serialize the SSL_SESSION to be encoded into the ticket.
   uint8_t *session_buf = NULL;
   size_t session_len;
@@ -592,25 +595,25 @@
   }
 
   int ret = 0;
-  if (ssl->session_ctx->ticket_aead_method) {
-    ret = ssl_encrypt_ticket_with_method(ssl, out, session_buf, session_len);
+  if (hs->config->session_ctx->ticket_aead_method) {
+    ret = ssl_encrypt_ticket_with_method(hs, out, session_buf, session_len);
   } else {
-    ret =
-        ssl_encrypt_ticket_with_cipher_ctx(ssl, out, session_buf, session_len);
+    ret = ssl_encrypt_ticket_with_cipher_ctx(hs, out, session_buf, session_len);
   }
 
   OPENSSL_free(session_buf);
   return ret;
 }
 
-int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session) {
+int ssl_session_is_context_valid(const SSL_HANDSHAKE *hs,
+                                 const SSL_SESSION *session) {
   if (session == NULL) {
     return 0;
   }
 
-  return session->sid_ctx_length == ssl->cert->sid_ctx_length &&
-         OPENSSL_memcmp(session->sid_ctx, ssl->cert->sid_ctx,
-                        ssl->cert->sid_ctx_length) == 0;
+  return session->sid_ctx_length == hs->config->cert->sid_ctx_length &&
+         OPENSSL_memcmp(session->sid_ctx, hs->config->cert->sid_ctx,
+                        hs->config->cert->sid_ctx_length) == 0;
 }
 
 int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session) {
@@ -632,14 +635,14 @@
 int ssl_session_is_resumable(const SSL_HANDSHAKE *hs,
                              const SSL_SESSION *session) {
   const SSL *const ssl = hs->ssl;
-  return ssl_session_is_context_valid(ssl, session) &&
+  return ssl_session_is_context_valid(hs, session) &&
          // The session must have been created by the same type of end point as
          // we're now using it with.
          ssl->server == session->is_server &&
          // The session must not be expired.
          ssl_session_is_time_valid(ssl, session) &&
          /* Only resume if the session's version matches the negotiated
-           * version. */
+          * version. */
          ssl->version == session->ssl_version &&
          // Only resume if the session's cipher matches the negotiated one.
          hs->new_cipher == session->cipher &&
@@ -649,14 +652,14 @@
          ((sk_CRYPTO_BUFFER_num(session->certs) == 0 &&
            !session->peer_sha256_valid) ||
           session->peer_sha256_valid ==
-              ssl->retain_only_sha256_of_client_certs);
+              hs->config->retain_only_sha256_of_client_certs);
 }
 
 // ssl_lookup_session looks up |session_id| in the session cache and sets
 // |*out_session| to an |SSL_SESSION| object if found.
 static enum ssl_hs_wait_t ssl_lookup_session(
-    SSL *ssl, UniquePtr<SSL_SESSION> *out_session, const uint8_t *session_id,
-    size_t session_id_len) {
+    SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
+    const uint8_t *session_id, size_t session_id_len) {
   out_session->reset();
 
   if (session_id_len == 0 || session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
@@ -665,15 +668,16 @@
 
   UniquePtr<SSL_SESSION> session;
   // Try the internal cache, if it exists.
-  if (!(ssl->session_ctx->session_cache_mode &
+  if (!(hs->config->session_ctx->session_cache_mode &
         SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
     SSL_SESSION data;
-    data.ssl_version = ssl->version;
+    data.ssl_version = hs->ssl->version;
     data.session_id_length = session_id_len;
     OPENSSL_memcpy(data.session_id, session_id, session_id_len);
 
-    MutexReadLock lock(&ssl->session_ctx->lock);
-    session.reset(lh_SSL_SESSION_retrieve(ssl->session_ctx->sessions, &data));
+    MutexReadLock lock(&hs->config->session_ctx->lock);
+    session.reset(
+        lh_SSL_SESSION_retrieve(hs->config->session_ctx->sessions, &data));
     if (session) {
       // |lh_SSL_SESSION_retrieve| returns a non-owning pointer.
       SSL_SESSION_up_ref(session.get());
@@ -682,10 +686,10 @@
   }
 
   // Fall back to the external cache, if it exists.
-  if (!session && ssl->session_ctx->get_session_cb != nullptr) {
+  if (!session && hs->config->session_ctx->get_session_cb != nullptr) {
     int copy = 1;
-    session.reset(ssl->session_ctx->get_session_cb(ssl, session_id,
-                                                   session_id_len, &copy));
+    session.reset(hs->config->session_ctx->get_session_cb(
+        hs->ssl, session_id, session_id_len, &copy));
     if (!session) {
       return ssl_hs_ok;
     }
@@ -704,15 +708,15 @@
     }
 
     // Add the externally cached session to the internal cache if necessary.
-    if (!(ssl->session_ctx->session_cache_mode &
+    if (!(hs->config->session_ctx->session_cache_mode &
           SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
-      SSL_CTX_add_session(ssl->session_ctx, session.get());
+      SSL_CTX_add_session(hs->config->session_ctx, session.get());
     }
   }
 
-  if (session && !ssl_session_is_time_valid(ssl, session.get())) {
+  if (session && !ssl_session_is_time_valid(hs->ssl, session.get())) {
     // The session was from the cache, so remove it.
-    SSL_CTX_remove_session(ssl->session_ctx, session.get());
+    SSL_CTX_remove_session(hs->config->session_ctx, session.get());
     session.reset();
   }
 
@@ -720,13 +724,13 @@
   return ssl_hs_ok;
 }
 
-enum ssl_hs_wait_t ssl_get_prev_session(SSL *ssl,
+enum ssl_hs_wait_t ssl_get_prev_session(SSL_HANDSHAKE *hs,
                                         UniquePtr<SSL_SESSION> *out_session,
                                         bool *out_tickets_supported,
                                         bool *out_renew_ticket,
                                         const SSL_CLIENT_HELLO *client_hello) {
   // This is used only by servers.
-  assert(ssl->server);
+  assert(hs->ssl->server);
   UniquePtr<SSL_SESSION> session;
   bool renew_ticket = false;
 
@@ -734,12 +738,12 @@
   const uint8_t *ticket = NULL;
   size_t ticket_len = 0;
   const bool tickets_supported =
-      !(SSL_get_options(ssl) & SSL_OP_NO_TICKET) &&
-      ssl->version > SSL3_VERSION &&
+      !(SSL_get_options(hs->ssl) & SSL_OP_NO_TICKET) &&
+      hs->ssl->version > SSL3_VERSION &&
       SSL_early_callback_ctx_extension_get(
           client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len);
   if (tickets_supported && ticket_len > 0) {
-    switch (ssl_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
+    switch (ssl_process_ticket(hs, &session, &renew_ticket, ticket, ticket_len,
                                client_hello->session_id,
                                client_hello->session_id_len)) {
       case ssl_ticket_aead_success:
@@ -755,7 +759,7 @@
   } else {
     // The client didn't send a ticket, so the session ID is a real ID.
     enum ssl_hs_wait_t lookup_ret = ssl_lookup_session(
-        ssl, &session, client_hello->session_id, client_hello->session_id_len);
+        hs, &session, client_hello->session_id, client_hello->session_id_len);
     if (lookup_ret != ssl_hs_ok) {
       return lookup_ret;
     }
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 5fb4fb4..c7032af 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1538,7 +1538,8 @@
                                    bssl::UniquePtr<SSL> *out_server,
                                    SSL_CTX *client_ctx, SSL_CTX *server_ctx,
                                    const ClientConfig &config = ClientConfig(),
-                                   bool do_handshake = true) {
+                                   bool do_handshake = true,
+                                   bool shed_handshake_config = true) {
   bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
   if (!client || !server) {
     return false;
@@ -1562,6 +1563,9 @@
   SSL_set_bio(client.get(), bio1, bio1);
   SSL_set_bio(server.get(), bio2, bio2);
 
+  SSL_set_shed_handshake_config(client.get(), shed_handshake_config);
+  SSL_set_shed_handshake_config(server.get(), shed_handshake_config);
+
   if (do_handshake && !CompleteHandshakes(client.get(), server.get())) {
     return false;
   }
@@ -1608,7 +1612,8 @@
 
   bool Connect(const ClientConfig &config = ClientConfig()) {
     return ConnectClientAndServer(&client_, &server_, client_ctx_.get(),
-                                  server_ctx_.get(), config);
+                                  server_ctx_.get(), config, true,
+                                  shed_handshake_config_);
   }
 
   uint16_t version() const { return GetParam().version; }
@@ -1617,6 +1622,7 @@
     return GetParam().ssl_method == VersionParam::is_dtls;
   }
 
+  bool shed_handshake_config_ = true;
   bssl::UniquePtr<SSL> client_, server_;
   bssl::UniquePtr<SSL_CTX> server_ctx_, client_ctx_;
   bssl::UniquePtr<X509> cert_;
@@ -2729,6 +2735,7 @@
     return;
   }
 
+  shed_handshake_config_ = false;
   ASSERT_TRUE(Connect());
 
   EXPECT_FALSE(SSL_session_reused(client_.get()));
@@ -2746,6 +2753,25 @@
   EXPECT_TRUE(SSL_session_reused(server_.get()));
 }
 
+TEST_P(SSLVersionTest, SSLClearFailsWithShedding) {
+  shed_handshake_config_ = false;
+  ASSERT_TRUE(Connect());
+  ASSERT_TRUE(CompleteHandshakes(client_.get(), server_.get()));
+
+  // Reset everything.
+  ASSERT_TRUE(SSL_clear(client_.get()));
+  ASSERT_TRUE(SSL_clear(server_.get()));
+
+  // Now enable shedding, and connect a second time.
+  shed_handshake_config_ = true;
+  ASSERT_TRUE(Connect());
+  ASSERT_TRUE(CompleteHandshakes(client_.get(), server_.get()));
+
+  // |SSL_clear| should now fail.
+  ASSERT_FALSE(SSL_clear(client_.get()));
+  ASSERT_FALSE(SSL_clear(server_.get()));
+}
+
 static bool ChainsEqual(STACK_OF(X509) * chain,
                         const std::vector<X509 *> &expected) {
   if (sk_X509_num(chain) != expected.size()) {
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc
index 73ea26f..0e28620 100644
--- a/ssl/ssl_versions.cc
+++ b/ssl/ssl_versions.cc
@@ -207,20 +207,20 @@
     {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
 };
 
-bool ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+bool ssl_get_version_range(const SSL_HANDSHAKE *hs, uint16_t *out_min_version,
                            uint16_t *out_max_version) {
   // For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
   // DTLS 1.0 should be mapped to TLS 1.1.
-  uint32_t options = ssl->options;
-  if (SSL_is_dtls(ssl)) {
+  uint32_t options = hs->ssl->options;
+  if (SSL_is_dtls(hs->ssl)) {
     options &= ~SSL_OP_NO_TLSv1_1;
     if (options & SSL_OP_NO_DTLSv1) {
       options |= SSL_OP_NO_TLSv1_1;
     }
   }
 
-  uint16_t min_version = ssl->conf_min_version;
-  uint16_t max_version = ssl->conf_max_version;
+  uint16_t min_version = hs->config->conf_min_version;
+  uint16_t max_version = hs->config->conf_max_version;
 
   // OpenSSL's API for controlling versions entails blacklisting individual
   // protocols. This has two problems. First, on the client, the protocol can
@@ -373,11 +373,17 @@
 }
 
 int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
-  return set_min_version(ssl->method, &ssl->conf_min_version, version);
+  if (!ssl->config) {
+    return 0;
+  }
+  return set_min_version(ssl->method, &ssl->config->conf_min_version, version);
 }
 
 int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
-  return set_max_version(ssl->method, &ssl->conf_max_version, version);
+  if (!ssl->config) {
+    return 0;
+  }
+  return set_max_version(ssl->method, &ssl->config->conf_max_version, version);
 }
 
 int SSL_version(const SSL *ssl) {
diff --git a/ssl/ssl_x509.cc b/ssl/ssl_x509.cc
index cb35339..b9ea170 100644
--- a/ssl/ssl_x509.cc
+++ b/ssl/ssl_x509.cc
@@ -349,7 +349,7 @@
 }
 
 static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session,
-                                                     SSL *ssl,
+                                                     SSL_HANDSHAKE *hs,
                                                      uint8_t *out_alert) {
   *out_alert = SSL_AD_INTERNAL_ERROR;
   STACK_OF(X509) *const cert_chain = session->x509_chain;
@@ -357,9 +357,10 @@
     return 0;
   }
 
-  X509_STORE *verify_store = ssl->ctx->cert_store;
-  if (ssl->cert->verify_store != NULL) {
-    verify_store = ssl->cert->verify_store;
+  SSL_CTX *ssl_ctx = hs->ssl->ctx;
+  X509_STORE *verify_store = ssl_ctx->cert_store;
+  if (hs->config->cert->verify_store != NULL) {
+    verify_store = hs->config->cert->verify_store;
   }
 
   X509 *leaf = sk_X509_value(cert_chain, 0);
@@ -368,8 +369,8 @@
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return 0;
   }
-  if (!X509_STORE_CTX_set_ex_data(ctx.get(),
-                                  SSL_get_ex_data_X509_STORE_CTX_idx(), ssl)) {
+  if (!X509_STORE_CTX_set_ex_data(
+          ctx.get(), SSL_get_ex_data_X509_STORE_CTX_idx(), hs->ssl)) {
     return 0;
   }
 
@@ -377,19 +378,20 @@
   // context: if its a server it will verify SSL client certificates or vice
   // versa.
   X509_STORE_CTX_set_default(ctx.get(),
-                             ssl->server ? "ssl_client" : "ssl_server");
+                             hs->ssl->server ? "ssl_client" : "ssl_server");
 
   // Anything non-default in "param" should overwrite anything in the ctx.
-  X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()), ssl->param);
+  X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()),
+                         hs->config->param);
 
-  if (ssl->verify_callback) {
-    X509_STORE_CTX_set_verify_cb(ctx.get(), ssl->verify_callback);
+  if (hs->config->verify_callback) {
+    X509_STORE_CTX_set_verify_cb(ctx.get(), hs->config->verify_callback);
   }
 
   int verify_ret;
-  if (ssl->ctx->app_verify_callback != NULL) {
+  if (ssl_ctx->app_verify_callback != NULL) {
     verify_ret =
-        ssl->ctx->app_verify_callback(ctx.get(), ssl->ctx->app_verify_arg);
+        ssl_ctx->app_verify_callback(ctx.get(), ssl_ctx->app_verify_arg);
   } else {
     verify_ret = X509_verify_cert(ctx.get());
   }
@@ -397,7 +399,7 @@
   session->verify_result = ctx->error;
 
   // If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
-  if (verify_ret <= 0 && ssl->verify_mode != SSL_VERIFY_NONE) {
+  if (verify_ret <= 0 && hs->config->verify_mode != SSL_VERIFY_NONE) {
     *out_alert = SSL_alert_from_verify_result(ctx->error);
     return 0;
   }
@@ -411,44 +413,45 @@
   hs->cached_x509_ca_names = NULL;
 }
 
-static int ssl_crypto_x509_ssl_new(SSL *ssl) {
-  ssl->param = X509_VERIFY_PARAM_new();
-  if (ssl->param == NULL) {
+static int ssl_crypto_x509_ssl_new(SSL_HANDSHAKE *hs) {
+  hs->config->param = X509_VERIFY_PARAM_new();
+  if (hs->config->param == NULL) {
     return 0;
   }
-  X509_VERIFY_PARAM_inherit(ssl->param, ssl->ctx->param);
+  X509_VERIFY_PARAM_inherit(hs->config->param, hs->ssl->ctx->param);
   return 1;
 }
 
-static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL *ssl) {
-  sk_X509_NAME_pop_free(ssl->cached_x509_client_CA, X509_NAME_free);
-  ssl->cached_x509_client_CA = NULL;
+static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL_CONFIG *cfg) {
+  sk_X509_NAME_pop_free(cfg->cached_x509_client_CA, X509_NAME_free);
+  cfg->cached_x509_client_CA = NULL;
 }
 
-static void ssl_crypto_x509_ssl_free(SSL *ssl) {
-  ssl_crypto_x509_ssl_flush_cached_client_CA(ssl);
-  X509_VERIFY_PARAM_free(ssl->param);
+static void ssl_crypto_x509_ssl_config_free(SSL_CONFIG *cfg) {
+  sk_X509_NAME_pop_free(cfg->cached_x509_client_CA, X509_NAME_free);
+  cfg->cached_x509_client_CA = NULL;
+  X509_VERIFY_PARAM_free(cfg->param);
 }
 
-static int ssl_crypto_x509_ssl_auto_chain_if_needed(SSL *ssl) {
+static int ssl_crypto_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
   // Only build a chain if there are no intermediates configured and the feature
   // isn't disabled.
-  if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
-      !ssl_has_certificate(ssl) ||
-      ssl->cert->chain == nullptr ||
-      sk_CRYPTO_BUFFER_num(ssl->cert->chain.get()) > 1) {
+  if ((hs->ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
+      !ssl_has_certificate(hs->config) || hs->config->cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(hs->config->cert->chain.get()) > 1) {
     return 1;
   }
 
   UniquePtr<X509> leaf(X509_parse_from_buffer(
-      sk_CRYPTO_BUFFER_value(ssl->cert->chain.get(), 0)));
+      sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0)));
   if (!leaf) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return 0;
   }
 
   ScopedX509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(ctx.get(), ssl->ctx->cert_store, leaf.get(), NULL)) {
+  if (!X509_STORE_CTX_init(ctx.get(), hs->ssl->ctx->cert_store, leaf.get(),
+                           NULL)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return 0;
   }
@@ -460,11 +463,11 @@
   // Remove the leaf from the generated chain.
   X509_free(sk_X509_shift(ctx->chain));
 
-  if (!ssl_cert_set_chain(ssl->cert, ctx->chain)) {
+  if (!ssl_cert_set_chain(hs->config->cert, ctx->chain)) {
     return 0;
   }
 
-  ssl_crypto_x509_cert_flush_cached_chain(ssl->cert);
+  ssl_crypto_x509_cert_flush_cached_chain(hs->config->cert);
 
   return 1;
 }
@@ -499,7 +502,7 @@
   ssl_crypto_x509_session_verify_cert_chain,
   ssl_crypto_x509_hs_flush_cached_ca_names,
   ssl_crypto_x509_ssl_new,
-  ssl_crypto_x509_ssl_free,
+  ssl_crypto_x509_ssl_config_free,
   ssl_crypto_x509_ssl_flush_cached_client_CA,
   ssl_crypto_x509_ssl_auto_chain_if_needed,
   ssl_crypto_x509_ssl_ctx_new,
@@ -578,7 +581,10 @@
 
 int SSL_set_purpose(SSL *ssl, int purpose) {
   check_ssl_x509_method(ssl);
-  return X509_VERIFY_PARAM_set_purpose(ssl->param, purpose);
+  if (!ssl->config) {
+    return 0;
+  }
+  return X509_VERIFY_PARAM_set_purpose(ssl->config->param, purpose);
 }
 
 int SSL_CTX_set_trust(SSL_CTX *ctx, int trust) {
@@ -588,7 +594,10 @@
 
 int SSL_set_trust(SSL *ssl, int trust) {
   check_ssl_x509_method(ssl);
-  return X509_VERIFY_PARAM_set_trust(ssl->param, trust);
+  if (!ssl->config) {
+    return 0;
+  }
+  return X509_VERIFY_PARAM_set_trust(ssl->config->param, trust);
 }
 
 int SSL_CTX_set1_param(SSL_CTX *ctx, const X509_VERIFY_PARAM *param) {
@@ -598,7 +607,10 @@
 
 int SSL_set1_param(SSL *ssl, const X509_VERIFY_PARAM *param) {
   check_ssl_x509_method(ssl);
-  return X509_VERIFY_PARAM_set1(ssl->param, param);
+  if (!ssl->config) {
+    return 0;
+  }
+  return X509_VERIFY_PARAM_set1(ssl->config->param, param);
 }
 
 X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) {
@@ -608,17 +620,29 @@
 
 X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) {
   check_ssl_x509_method(ssl);
-  return ssl->param;
+  if (!ssl->config) {
+    assert(ssl->config);
+    return 0;
+  }
+  return ssl->config->param;
 }
 
 int SSL_get_verify_depth(const SSL *ssl) {
   check_ssl_x509_method(ssl);
-  return X509_VERIFY_PARAM_get_depth(ssl->param);
+  if (!ssl->config) {
+    assert(ssl->config);
+    return 0;
+  }
+  return X509_VERIFY_PARAM_get_depth(ssl->config->param);
 }
 
 int (*SSL_get_verify_callback(const SSL *ssl))(int, X509_STORE_CTX *) {
   check_ssl_x509_method(ssl);
-  return ssl->verify_callback;
+  if (!ssl->config) {
+    assert(ssl->config);
+    return 0;
+  }
+  return ssl->config->verify_callback;
 }
 
 int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) {
@@ -640,15 +664,21 @@
 void SSL_set_verify(SSL *ssl, int mode,
                     int (*callback)(int ok, X509_STORE_CTX *store_ctx)) {
   check_ssl_x509_method(ssl);
-  ssl->verify_mode = mode;
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->verify_mode = mode;
   if (callback != NULL) {
-    ssl->verify_callback = callback;
+    ssl->config->verify_callback = callback;
   }
 }
 
 void SSL_set_verify_depth(SSL *ssl, int depth) {
   check_ssl_x509_method(ssl);
-  X509_VERIFY_PARAM_set_depth(ssl->param, depth);
+  if (!ssl->config) {
+    return;
+  }
+  X509_VERIFY_PARAM_set_depth(ssl->config->param, depth);
 }
 
 void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx,
@@ -726,7 +756,10 @@
 
 int SSL_use_certificate(SSL *ssl, X509 *x) {
   check_ssl_x509_method(ssl);
-  return ssl_use_certificate(ssl->cert, x);
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_use_certificate(ssl->config->cert, x);
 }
 
 int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) {
@@ -764,7 +797,11 @@
 
 X509 *SSL_get_certificate(const SSL *ssl) {
   check_ssl_x509_method(ssl);
-  return ssl_cert_get0_leaf(ssl->cert);
+  if (!ssl->config) {
+    assert(ssl->config);
+    return 0;
+  }
+  return ssl_cert_get0_leaf(ssl->config->cert);
 }
 
 X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
@@ -846,12 +883,18 @@
 
 int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) {
   check_ssl_x509_method(ssl);
-  return ssl_cert_set0_chain(ssl->cert, chain);
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_cert_set0_chain(ssl->config->cert, chain);
 }
 
 int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) {
   check_ssl_x509_method(ssl);
-  return ssl_cert_set1_chain(ssl->cert, chain);
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_cert_set1_chain(ssl->config->cert, chain);
 }
 
 int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) {
@@ -871,12 +914,18 @@
 
 int SSL_add0_chain_cert(SSL *ssl, X509 *x509) {
   check_ssl_x509_method(ssl);
-  return ssl_cert_add0_chain_cert(ssl->cert, x509);
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_cert_add0_chain_cert(ssl->config->cert, x509);
 }
 
 int SSL_add1_chain_cert(SSL *ssl, X509 *x509) {
   check_ssl_x509_method(ssl);
-  return ssl_cert_add1_chain_cert(ssl->cert, x509);
+  if (!ssl->config) {
+    return 0;
+  }
+  return ssl_cert_add1_chain_cert(ssl->config->cert, x509);
 }
 
 int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) {
@@ -942,12 +991,16 @@
 
 int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
   check_ssl_x509_method(ssl);
-  if (!ssl_cert_cache_chain_certs(ssl->cert)) {
+  if (!ssl->config) {
+    assert(ssl->config);
+    return 0;
+  }
+  if (!ssl_cert_cache_chain_certs(ssl->config->cert)) {
     *out_chain = NULL;
     return 0;
   }
 
-  *out_chain = ssl->cert->x509_chain;
+  *out_chain = ssl->config->cert->x509_chain;
   return 1;
 }
 
@@ -1022,8 +1075,11 @@
 
 void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list) {
   check_ssl_x509_method(ssl);
-  ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl);
-  set_client_CA_list(&ssl->client_CA, name_list, ssl->ctx->pool);
+  if (!ssl->config) {
+    return;
+  }
+  ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl->config);
+  set_client_CA_list(&ssl->config->client_CA, name_list, ssl->ctx->pool);
   sk_X509_NAME_pop_free(name_list, X509_NAME_free);
 }
 
@@ -1068,6 +1124,10 @@
 
 STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl) {
   check_ssl_x509_method(ssl);
+  if (!ssl->config) {
+    assert(ssl->config);
+    return NULL;
+  }
   // For historical reasons, this function is used both to query configuration
   // state on a server as well as handshake state on a client. However, whether
   // |ssl| is a client or server is not known until explicitly configured with
@@ -1082,9 +1142,10 @@
     return NULL;
   }
 
-  if (ssl->client_CA != NULL) {
+  if (ssl->config->client_CA != NULL) {
     return buffer_names_to_x509(
-        ssl->client_CA, (STACK_OF(X509_NAME) **)&ssl->cached_x509_client_CA);
+        ssl->config->client_CA,
+        (STACK_OF(X509_NAME) **)&ssl->config->cached_x509_client_CA);
   }
   return SSL_CTX_get_client_CA_list(ssl->ctx);
 }
@@ -1140,11 +1201,14 @@
 
 int SSL_add_client_CA(SSL *ssl, X509 *x509) {
   check_ssl_x509_method(ssl);
-  if (!add_client_CA(&ssl->client_CA, x509, ssl->ctx->pool)) {
+  if (!ssl->config) {
+    return 0;
+  }
+  if (!add_client_CA(&ssl->config->client_CA, x509, ssl->ctx->pool)) {
     return 0;
   }
 
-  ssl_crypto_x509_ssl_flush_cached_client_CA(ssl);
+  ssl_crypto_x509_ssl_flush_cached_client_CA(ssl->config);
   return 1;
 }
 
@@ -1159,7 +1223,13 @@
 }
 
 static int do_client_cert_cb(SSL *ssl, void *arg) {
-  if (ssl_has_certificate(ssl) || ssl->ctx->client_cert_cb == NULL) {
+  // Should only be called during handshake, but check to be sure.
+  if (!ssl->config) {
+    assert(ssl->config);
+    return -1;
+  }
+  if (ssl_has_certificate(ssl->config) ||
+      ssl->ctx->client_cert_cb == NULL) {
     return 1;
   }
 
@@ -1223,12 +1293,18 @@
 
 int SSL_set0_verify_cert_store(SSL *ssl, X509_STORE *store) {
   check_ssl_x509_method(ssl);
-  return set_cert_store(&ssl->cert->verify_store, store, 0);
+  if (!ssl->config) {
+    return 0;
+  }
+  return set_cert_store(&ssl->config->cert->verify_store, store, 0);
 }
 
 int SSL_set1_verify_cert_store(SSL *ssl, X509_STORE *store) {
   check_ssl_x509_method(ssl);
-  return set_cert_store(&ssl->cert->verify_store, store, 1);
+  if (!ssl->config) {
+    return 0;
+  }
+  return set_cert_store(&ssl->config->cert->verify_store, store, 1);
 }
 
 int SSL_alert_from_verify_result(long result) {
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc
index a821246..eb6d90b 100644
--- a/ssl/t1_lib.cc
+++ b/ssl/t1_lib.cc
@@ -292,10 +292,10 @@
     SSL_CURVE_SECP384R1,
 };
 
-Span<const uint16_t> tls1_get_grouplist(const SSL *ssl) {
-  if (ssl->supported_group_list != nullptr) {
-    return MakeConstSpan(ssl->supported_group_list,
-                         ssl->supported_group_list_len);
+Span<const uint16_t> tls1_get_grouplist(const SSL_HANDSHAKE *hs) {
+  if (hs->config->supported_group_list != nullptr) {
+    return MakeConstSpan(hs->config->supported_group_list,
+                         hs->config->supported_group_list_len);
   }
   return Span<const uint16_t>(kDefaultGroups);
 }
@@ -313,7 +313,7 @@
   // support our favoured group. Thus we do not special-case an emtpy
   // |peer_supported_group_list|.
 
-  Span<const uint16_t> groups = tls1_get_grouplist(ssl);
+  Span<const uint16_t> groups = tls1_get_grouplist(hs);
   Span<const uint16_t> pref, supp;
   if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
     pref = groups;
@@ -399,8 +399,8 @@
   return 0;
 }
 
-int tls1_check_group_id(const SSL *ssl, uint16_t group_id) {
-  for (uint16_t supported : tls1_get_grouplist(ssl)) {
+int tls1_check_group_id(const SSL_HANDSHAKE *hs, uint16_t group_id) {
+  for (uint16_t supported : tls1_get_grouplist(hs)) {
     if (supported == group_id) {
       return 1;
     }
@@ -1101,8 +1101,7 @@
 // https://tools.ietf.org/html/rfc6066#section-8
 
 static bool ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (!ssl->ocsp_stapling_enabled) {
+  if (!hs->config->ocsp_stapling_enabled) {
     return true;
   }
 
@@ -1166,8 +1165,7 @@
 static bool ext_ocsp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
-      !hs->ocsp_stapling_requested ||
-      ssl->cert->ocsp_response == NULL ||
+      !hs->ocsp_stapling_requested || hs->config->cert->ocsp_response == NULL ||
       ssl->s3->session_reused ||
       !ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
     return true;
@@ -1308,8 +1306,7 @@
 // https://tools.ietf.org/html/rfc6962#section-3.3.1
 
 static bool ext_sct_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (!ssl->signed_cert_timestamps_enabled) {
+  if (!hs->config->signed_cert_timestamps_enabled) {
     return true;
   }
 
@@ -1336,7 +1333,7 @@
 
   // If this is false then we should never have sent the SCT extension in the
   // ClientHello and thus this function should never have been called.
-  assert(ssl->signed_cert_timestamps_enabled);
+  assert(hs->config->signed_cert_timestamps_enabled);
 
   if (!ssl_is_sct_list_valid(contents)) {
     *out_alert = SSL_AD_DECODE_ERROR;
@@ -1378,9 +1375,8 @@
 static bool ext_sct_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   // The extension shouldn't be sent when resuming sessions.
-  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
-      ssl->s3->session_reused ||
-      ssl->cert->signed_cert_timestamp_list == NULL) {
+  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION || ssl->s3->session_reused ||
+      hs->config->cert->signed_cert_timestamp_list == NULL) {
     return true;
   }
 
@@ -1389,8 +1385,10 @@
          CBB_add_u16_length_prefixed(out, &contents) &&
          CBB_add_bytes(
              &contents,
-             CRYPTO_BUFFER_data(ssl->cert->signed_cert_timestamp_list.get()),
-             CRYPTO_BUFFER_len(ssl->cert->signed_cert_timestamp_list.get())) &&
+             CRYPTO_BUFFER_data(
+                 hs->config->cert->signed_cert_timestamp_list.get()),
+             CRYPTO_BUFFER_len(
+                 hs->config->cert->signed_cert_timestamp_list.get())) &&
          CBB_flush(out);
 }
 
@@ -1401,7 +1399,7 @@
 
 static bool ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl->alpn_client_proto_list == NULL ||
+  if (hs->config->alpn_client_proto_list == NULL ||
       ssl->s3->initial_handshake_complete) {
     return true;
   }
@@ -1410,8 +1408,8 @@
   if (!CBB_add_u16(out, TLSEXT_TYPE_application_layer_protocol_negotiation) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &proto_list) ||
-      !CBB_add_bytes(&proto_list, ssl->alpn_client_proto_list,
-                     ssl->alpn_client_proto_list_len) ||
+      !CBB_add_bytes(&proto_list, hs->config->alpn_client_proto_list,
+                     hs->config->alpn_client_proto_list_len) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -1427,7 +1425,7 @@
   }
 
   assert(!ssl->s3->initial_handshake_complete);
-  assert(ssl->alpn_client_proto_list != NULL);
+  assert(hs->config->alpn_client_proto_list != NULL);
 
   if (hs->next_proto_neg_seen) {
     // NPN and ALPN may not be negotiated in the same connection.
@@ -1448,7 +1446,7 @@
     return false;
   }
 
-  if (!ssl_is_alpn_protocol_allowed(ssl, protocol_name)) {
+  if (!ssl_is_alpn_protocol_allowed(hs, protocol_name)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     return false;
@@ -1462,20 +1460,20 @@
   return true;
 }
 
-bool ssl_is_alpn_protocol_allowed(const SSL *ssl,
+bool ssl_is_alpn_protocol_allowed(const SSL_HANDSHAKE *hs,
                                   Span<const uint8_t> protocol) {
-  if (ssl->alpn_client_proto_list == nullptr) {
+  if (hs->config->alpn_client_proto_list == nullptr) {
     return false;
   }
 
-  if (ssl->ctx->allow_unknown_alpn_protos) {
+  if (hs->ssl->ctx->allow_unknown_alpn_protos) {
     return true;
   }
 
   // Check that the protocol name is one of the ones we advertised.
   CBS client_protocol_name_list, client_protocol_name;
-  CBS_init(&client_protocol_name_list, ssl->alpn_client_proto_list,
-           ssl->alpn_client_proto_list_len);
+  CBS_init(&client_protocol_name_list, hs->config->alpn_client_proto_list,
+           hs->config->alpn_client_proto_list_len);
   while (CBS_len(&client_protocol_name_list) > 0) {
     if (!CBS_get_u8_length_prefixed(&client_protocol_name_list,
                                     &client_protocol_name)) {
@@ -1575,8 +1573,7 @@
 
 static bool ext_channel_id_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (!ssl->tlsext_channel_id_enabled ||
-      SSL_is_dtls(ssl)) {
+  if (!hs->config->tlsext_channel_id_enabled || SSL_is_dtls(ssl)) {
     return true;
   }
 
@@ -1597,7 +1594,7 @@
   }
 
   assert(!SSL_is_dtls(ssl));
-  assert(ssl->tlsext_channel_id_enabled);
+  assert(hs->config->tlsext_channel_id_enabled);
 
   if (CBS_len(contents) != 0) {
     return false;
@@ -1611,8 +1608,7 @@
                                              uint8_t *out_alert,
                                              CBS *contents) {
   SSL *const ssl = hs->ssl;
-  if (contents == NULL ||
-      !ssl->tlsext_channel_id_enabled ||
+  if (contents == NULL || !hs->config->tlsext_channel_id_enabled ||
       SSL_is_dtls(ssl)) {
     return true;
   }
@@ -2069,7 +2065,7 @@
 
 static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (!ssl->cert->enable_early_data ||
+  if (!ssl->enable_early_data ||
       // Session must be 0-RTT capable.
       ssl->session == NULL ||
       ssl_session_protocol_version(ssl->session) < TLS1_3_VERSION ||
@@ -2080,8 +2076,8 @@
       // avoid reporting a confusing value in |SSL_get0_alpn_selected|.
       (ssl->session->early_alpn_len != 0 &&
        !ssl_is_alpn_protocol_allowed(
-           ssl, MakeConstSpan(ssl->session->early_alpn,
-                              ssl->session->early_alpn_len)))) {
+           hs, MakeConstSpan(ssl->session->early_alpn,
+                             ssl->session->early_alpn_len)))) {
     return true;
   }
 
@@ -2191,7 +2187,7 @@
     }
 
     // Predict the most preferred group.
-    Span<const uint16_t> groups = tls1_get_grouplist(ssl);
+    Span<const uint16_t> groups = tls1_get_grouplist(hs);
     if (groups.empty()) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_GROUPS_SPECIFIED);
       return false;
@@ -2422,7 +2418,7 @@
 }
 
 static bool ext_dummy_pq_padding_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  const size_t len = hs->ssl->dummy_pq_padding_len;
+  const size_t len = hs->config->dummy_pq_padding_len;
   if (len == 0) {
     return true;
   }
@@ -2437,7 +2433,7 @@
     return true;
   }
 
-  if (CBS_len(contents) != hs->ssl->dummy_pq_padding_len) {
+  if (CBS_len(contents) != hs->config->dummy_pq_padding_len) {
     return false;
   }
 
@@ -2485,7 +2481,7 @@
     return false;
   }
 
-  for (uint16_t group : tls1_get_grouplist(ssl)) {
+  for (uint16_t group : tls1_get_grouplist(hs)) {
     if (!CBB_add_u16(&groups_bytes, group)) {
       return false;
     }
@@ -2556,7 +2552,7 @@
 
 static bool ext_token_binding_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
-  if (ssl->token_binding_params == nullptr || SSL_is_dtls(ssl)) {
+  if (hs->config->token_binding_params == nullptr || SSL_is_dtls(ssl)) {
     return true;
   }
 
@@ -2565,8 +2561,8 @@
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16(&contents, kTokenBindingMaxVersion) ||
       !CBB_add_u8_length_prefixed(&contents, &params) ||
-      !CBB_add_bytes(&params, ssl->token_binding_params,
-                     ssl->token_binding_params_len) ||
+      !CBB_add_bytes(&params, hs->config->token_binding_params,
+                     hs->config->token_binding_params_len) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -2606,8 +2602,8 @@
     return true;
   }
 
-  for (size_t i = 0; i < ssl->token_binding_params_len; ++i) {
-    if (param == ssl->token_binding_params[i]) {
+  for (size_t i = 0; i < hs->config->token_binding_params_len; ++i) {
+    if (param == hs->config->token_binding_params[i]) {
       ssl->s3->negotiated_token_binding_param = param;
       ssl->s3->token_binding_negotiated = true;
       return true;
@@ -2619,15 +2615,16 @@
 }
 
 // select_tb_param looks for the first token binding param in
-// |ssl->token_binding_params| that is also in |params| and puts it in
-// |ssl->negotiated_token_binding_param|. It returns true if a token binding
+// |hs->ssl->token_binding_params| that is also in |params| and puts it in
+// |hs->ssl->negotiated_token_binding_param|. It returns true if a token binding
 // param is found, and false otherwise.
-static bool select_tb_param(SSL *ssl, Span<const uint8_t> peer_params) {
-  for (size_t i = 0; i < ssl->token_binding_params_len; ++i) {
-    uint8_t tb_param = ssl->token_binding_params[i];
+static bool select_tb_param(SSL_HANDSHAKE *hs,
+                            Span<const uint8_t> peer_params) {
+  for (size_t i = 0; i < hs->config->token_binding_params_len; ++i) {
+    uint8_t tb_param = hs->config->token_binding_params[i];
     for (uint8_t peer_param : peer_params) {
       if (tb_param == peer_param) {
-        ssl->s3->negotiated_token_binding_param = tb_param;
+        hs->ssl->s3->negotiated_token_binding_param = tb_param;
         return true;
       }
     }
@@ -2639,7 +2636,7 @@
                                                 uint8_t *out_alert,
                                                 CBS *contents) {
   SSL *const ssl = hs->ssl;
-  if (contents == nullptr || ssl->token_binding_params == nullptr) {
+  if (contents == nullptr || hs->config->token_binding_params == nullptr) {
     return true;
   }
 
@@ -2663,7 +2660,7 @@
   // version. Otherwise, use the client's version.
   hs->negotiated_token_binding_version =
       std::min(version, kTokenBindingMaxVersion);
-  if (!select_tb_param(ssl, params)) {
+  if (!select_tb_param(hs, params)) {
     return true;
   }
 
@@ -2695,16 +2692,15 @@
 
 static bool ext_quic_transport_params_add_clienthello(SSL_HANDSHAKE *hs,
                                                       CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (!ssl->quic_transport_params || hs->max_version <= TLS1_2_VERSION) {
+  if (!hs->config->quic_transport_params || hs->max_version <= TLS1_2_VERSION) {
     return true;
   }
 
   CBB contents;
   if (!CBB_add_u16(out, TLSEXT_TYPE_quic_transport_parameters) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
-      !CBB_add_bytes(&contents, ssl->quic_transport_params,
-                     ssl->quic_transport_params_len) ||
+      !CBB_add_bytes(&contents, hs->config->quic_transport_params,
+                     hs->config->quic_transport_params_len) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -2731,7 +2727,7 @@
                                                         uint8_t *out_alert,
                                                         CBS *contents) {
   SSL *const ssl = hs->ssl;
-  if (!contents || !ssl->quic_transport_params) {
+  if (!contents || !hs->config->quic_transport_params) {
     return true;
   }
   // Ignore the extension before TLS 1.3.
@@ -2744,16 +2740,15 @@
 
 static bool ext_quic_transport_params_add_serverhello(SSL_HANDSHAKE *hs,
                                                       CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (!ssl->quic_transport_params) {
+  if (!hs->config->quic_transport_params) {
     return true;
   }
 
   CBB contents;
   if (!CBB_add_u16(out, TLSEXT_TYPE_quic_transport_parameters) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
-      !CBB_add_bytes(&contents, ssl->quic_transport_params,
-                     ssl->quic_transport_params_len) ||
+      !CBB_add_bytes(&contents, hs->config->quic_transport_params,
+                     hs->config->quic_transport_params_len) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -3322,9 +3317,9 @@
   if (ssl->ctx->tlsext_servername_callback != 0) {
     ret = ssl->ctx->tlsext_servername_callback(ssl, &al,
                                                ssl->ctx->tlsext_servername_arg);
-  } else if (ssl->session_ctx->tlsext_servername_callback != 0) {
-    ret = ssl->session_ctx->tlsext_servername_callback(
-        ssl, &al, ssl->session_ctx->tlsext_servername_arg);
+  } else if (hs->config->session_ctx->tlsext_servername_callback != 0) {
+    ret = hs->config->session_ctx->tlsext_servername_callback(
+        ssl, &al, hs->config->session_ctx->tlsext_servername_arg);
   }
 
   switch (ret) {
@@ -3407,14 +3402,14 @@
 }
 
 static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_cb(
-    SSL *ssl, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
+    SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
     const uint8_t *ticket, size_t ticket_len) {
   assert(ticket_len >= SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH);
   ScopedEVP_CIPHER_CTX cipher_ctx;
   ScopedHMAC_CTX hmac_ctx;
   const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN;
-  int cb_ret = ssl->session_ctx->tlsext_ticket_key_cb(
-      ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, cipher_ctx.get(),
+  int cb_ret = hs->config->session_ctx->tlsext_ticket_key_cb(
+      hs->ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, cipher_ctx.get(),
       hmac_ctx.get(), 0 /* decrypt */);
   if (cb_ret < 0) {
     return ssl_ticket_aead_error;
@@ -3430,10 +3425,10 @@
 }
 
 static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_ticket_keys(
-    SSL *ssl, uint8_t **out, size_t *out_len, const uint8_t *ticket,
+    SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, const uint8_t *ticket,
     size_t ticket_len) {
   assert(ticket_len >= SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH);
-  SSL_CTX *ctx = ssl->session_ctx;
+  SSL_CTX *ctx = hs->config->session_ctx;
 
   // Rotate the ticket key if necessary.
   if (!ssl_ctx_rotate_ticket_encryption_key(ctx)) {
@@ -3470,7 +3465,7 @@
 }
 
 static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_method(
-    SSL *ssl, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
+    SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, bool *out_renew_ticket,
     const uint8_t *ticket, size_t ticket_len) {
   uint8_t *plaintext = (uint8_t *)OPENSSL_malloc(ticket_len);
   if (plaintext == NULL) {
@@ -3480,8 +3475,8 @@
 
   size_t plaintext_len;
   const enum ssl_ticket_aead_result_t result =
-      ssl->session_ctx->ticket_aead_method->open(
-          ssl, plaintext, &plaintext_len, ticket_len, ticket, ticket_len);
+      hs->config->session_ctx->ticket_aead_method->open(
+          hs->ssl, plaintext, &plaintext_len, ticket_len, ticket, ticket_len);
 
   if (result == ssl_ticket_aead_success) {
     *out = plaintext;
@@ -3494,13 +3489,13 @@
 }
 
 enum ssl_ticket_aead_result_t ssl_process_ticket(
-    SSL *ssl, UniquePtr<SSL_SESSION> *out_session, bool *out_renew_ticket,
-    const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id,
-    size_t session_id_len) {
+    SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
+    bool *out_renew_ticket, const uint8_t *ticket, size_t ticket_len,
+    const uint8_t *session_id, size_t session_id_len) {
   *out_renew_ticket = false;
   out_session->reset();
 
-  if ((SSL_get_options(ssl) & SSL_OP_NO_TICKET) ||
+  if ((SSL_get_options(hs->ssl) & SSL_OP_NO_TICKET) ||
       session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
     return ssl_ticket_aead_ignore_ticket;
   }
@@ -3508,9 +3503,9 @@
   uint8_t *plaintext = NULL;
   size_t plaintext_len;
   enum ssl_ticket_aead_result_t result;
-  if (ssl->session_ctx->ticket_aead_method != NULL) {
+  if (hs->config->session_ctx->ticket_aead_method != NULL) {
     result = ssl_decrypt_ticket_with_method(
-        ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len);
+        hs, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len);
   } else {
     // Ensure there is room for the key name and the largest IV
     // |tlsext_ticket_key_cb| may try to consume. The real limit may be lower,
@@ -3519,12 +3514,12 @@
     if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) {
       return ssl_ticket_aead_ignore_ticket;
     }
-    if (ssl->session_ctx->tlsext_ticket_key_cb != NULL) {
-      result = ssl_decrypt_ticket_with_cb(ssl, &plaintext, &plaintext_len,
+    if (hs->config->session_ctx->tlsext_ticket_key_cb != NULL) {
+      result = ssl_decrypt_ticket_with_cb(hs, &plaintext, &plaintext_len,
                                           out_renew_ticket, ticket, ticket_len);
     } else {
       result = ssl_decrypt_ticket_with_ticket_keys(
-          ssl, &plaintext, &plaintext_len, ticket, ticket_len);
+          hs, &plaintext, &plaintext_len, ticket, ticket_len);
     }
   }
 
@@ -3534,7 +3529,7 @@
 
   // Decode the session.
   UniquePtr<SSL_SESSION> session(
-      SSL_SESSION_from_bytes(plaintext, plaintext_len, ssl->ctx));
+      SSL_SESSION_from_bytes(plaintext, plaintext_len, hs->ssl->ctx));
   OPENSSL_free(plaintext);
 
   if (!session) {
@@ -3575,7 +3570,7 @@
 
 bool tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) {
   SSL *const ssl = hs->ssl;
-  CERT *cert = ssl->cert;
+  CERT *cert = hs->config->cert;
 
   // Before TLS 1.2, the signature algorithm isn't negotiated as part of the
   // handshake.
@@ -3691,14 +3686,13 @@
 }
 
 bool tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb) {
-  SSL *const ssl = hs->ssl;
   uint8_t digest[EVP_MAX_MD_SIZE];
   size_t digest_len;
   if (!tls1_channel_id_hash(hs, digest, &digest_len)) {
     return false;
   }
 
-  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
+  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(hs->config->tlsext_channel_id_private);
   if (ec_key == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
@@ -3801,20 +3795,20 @@
   return 1;
 }
 
-int ssl_do_channel_id_callback(SSL *ssl) {
-  if (ssl->tlsext_channel_id_private != NULL ||
-      ssl->ctx->channel_id_cb == NULL) {
+int ssl_do_channel_id_callback(SSL_HANDSHAKE *hs) {
+  if (hs->config->tlsext_channel_id_private != NULL ||
+      hs->ssl->ctx->channel_id_cb == NULL) {
     return 1;
   }
 
   EVP_PKEY *key = NULL;
-  ssl->ctx->channel_id_cb(ssl, &key);
+  hs->ssl->ctx->channel_id_cb(hs->ssl, &key);
   if (key == NULL) {
     // The caller should try again later.
     return 1;
   }
 
-  int ret = SSL_set1_tls_channel_id(ssl, key);
+  int ret = SSL_set1_tls_channel_id(hs->ssl, key);
   EVP_PKEY_free(key);
   return ret;
 }
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index f1531bd..dae759c 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -2066,11 +2066,14 @@
   if (config->install_ddos_callback) {
     SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback);
   }
+  SSL_set_shed_handshake_config(ssl.get(), true);
   if (config->renegotiate_once) {
     SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once);
+    SSL_set_shed_handshake_config(ssl.get(), config->shed_despite_renegotiate);
   }
   if (config->renegotiate_freely) {
     SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely);
+    SSL_set_shed_handshake_config(ssl.get(), config->shed_despite_renegotiate);
   }
   if (config->renegotiate_ignore) {
     SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore);
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 866ad53..d85efca 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -8209,6 +8209,19 @@
 		},
 	})
 
+	// Renegotiation should be rejected if the handshake config has been shed.
+	testCases = append(testCases, testCase{
+		name: "Renegotiate-HandshakeConfigShed",
+		config: Config{
+			MaxVersion: VersionTLS12,
+		},
+		renegotiate:        1,
+		flags:              []string{"-renegotiate-freely", "-shed-despite-renegotiate"},
+		shouldFail:         true,
+		expectedError:      ":NO_RENEGOTIATION:",
+		expectedLocalError: "remote error: no renegotiation",
+	})
+
 	// Renegotiation is not allowed at SSL 3.0.
 	testCases = append(testCases, testCase{
 		name: "Renegotiate-Client-SSL3",
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index f74267d..9433e4c 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -98,6 +98,7 @@
   { "-verify-peer", &TestConfig::verify_peer },
   { "-verify-peer-if-no-obc", &TestConfig::verify_peer_if_no_obc },
   { "-expect-verify-result", &TestConfig::expect_verify_result },
+  { "-shed-despite-renegotiate", &TestConfig::shed_despite_renegotiate },
   { "-renegotiate-once", &TestConfig::renegotiate_once },
   { "-renegotiate-freely", &TestConfig::renegotiate_freely },
   { "-renegotiate-ignore", &TestConfig::renegotiate_ignore },
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 95f38e0..4a03fba 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -115,6 +115,7 @@
   bool renegotiate_once = false;
   bool renegotiate_freely = false;
   bool renegotiate_ignore = false;
+  bool shed_despite_renegotiate = false;
   int expect_peer_signature_algorithm = 0;
   bool p384_only = false;
   bool enable_all_curves = false;
diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc
index defdac1..4cd3209 100644
--- a/ssl/tls13_both.cc
+++ b/ssl/tls13_both.cc
@@ -123,7 +123,7 @@
   }
 
   const bool retain_sha256 =
-      ssl->server && ssl->retain_only_sha256_of_client_certs;
+      ssl->server && hs->config->retain_only_sha256_of_client_certs;
   UniquePtr<EVP_PKEY> pkey;
   while (CBS_len(&certificate_list) > 0) {
     CBS certificate, extensions;
@@ -184,7 +184,7 @@
     // All Certificate extensions are parsed, but only the leaf extensions are
     // stored.
     if (have_status_request) {
-      if (ssl->server || !ssl->ocsp_stapling_enabled) {
+      if (ssl->server || !hs->config->ocsp_stapling_enabled) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
         return 0;
@@ -213,7 +213,7 @@
     }
 
     if (have_sct) {
-      if (ssl->server || !ssl->signed_cert_timestamps_enabled) {
+      if (ssl->server || !hs->config->signed_cert_timestamps_enabled) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
         return 0;
@@ -363,11 +363,11 @@
     return 0;
   }
 
-  if (!ssl_has_certificate(ssl)) {
+  if (!ssl_has_certificate(hs->config)) {
     return ssl_add_message_cbb(ssl, cbb.get());
   }
 
-  CERT *cert = ssl->cert;
+  CERT *cert = hs->config->cert;
   CRYPTO_BUFFER *leaf_buf = sk_CRYPTO_BUFFER_value(cert->chain.get(), 0);
   CBB leaf, extensions;
   if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) ||
@@ -378,30 +378,33 @@
     return 0;
   }
 
-  if (hs->scts_requested && ssl->cert->signed_cert_timestamp_list != nullptr) {
+  if (hs->scts_requested &&
+      hs->config->cert->signed_cert_timestamp_list != nullptr) {
     CBB contents;
     if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) ||
         !CBB_add_u16_length_prefixed(&extensions, &contents) ||
         !CBB_add_bytes(
             &contents,
-            CRYPTO_BUFFER_data(ssl->cert->signed_cert_timestamp_list.get()),
-            CRYPTO_BUFFER_len(ssl->cert->signed_cert_timestamp_list.get())) ||
+            CRYPTO_BUFFER_data(
+                hs->config->cert->signed_cert_timestamp_list.get()),
+            CRYPTO_BUFFER_len(
+                hs->config->cert->signed_cert_timestamp_list.get())) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       return 0;
     }
   }
 
-  if (hs->ocsp_stapling_requested &&
-      ssl->cert->ocsp_response != NULL) {
+  if (hs->ocsp_stapling_requested && hs->config->cert->ocsp_response != NULL) {
     CBB contents, ocsp_response;
     if (!CBB_add_u16(&extensions, TLSEXT_TYPE_status_request) ||
         !CBB_add_u16_length_prefixed(&extensions, &contents) ||
         !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) ||
         !CBB_add_u24_length_prefixed(&contents, &ocsp_response) ||
-        !CBB_add_bytes(&ocsp_response,
-                       CRYPTO_BUFFER_data(ssl->cert->ocsp_response.get()),
-                       CRYPTO_BUFFER_len(ssl->cert->ocsp_response.get())) ||
+        !CBB_add_bytes(
+            &ocsp_response,
+            CRYPTO_BUFFER_data(hs->config->cert->ocsp_response.get()),
+            CRYPTO_BUFFER_len(hs->config->cert->ocsp_response.get())) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       return 0;
diff --git a/ssl/tls13_client.cc b/ssl/tls13_client.cc
index 6e328b8..3794043 100644
--- a/ssl/tls13_client.cc
+++ b/ssl/tls13_client.cc
@@ -157,7 +157,7 @@
     }
 
     // The group must be supported.
-    if (!tls1_check_group_id(ssl, group_id)) {
+    if (!tls1_check_group_id(hs, group_id)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       return ssl_hs_error;
@@ -316,7 +316,7 @@
       return ssl_hs_error;
     }
 
-    if (!ssl_session_is_context_valid(ssl, ssl->session)) {
+    if (!ssl_session_is_context_valid(hs, ssl->session)) {
       // This is actually a client application bug.
       OPENSSL_PUT_ERROR(SSL,
                         SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
@@ -335,7 +335,7 @@
 
     // Resumption incorporates fresh key material, so refresh the timeout.
     ssl_session_renew_timeout(ssl, hs->new_session.get(),
-                              ssl->session_ctx->session_psk_dhe_timeout);
+                              hs->config->session_ctx->session_psk_dhe_timeout);
   } else if (!ssl_get_new_session(hs, 0)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
@@ -629,8 +629,8 @@
   }
 
   // Call cert_cb to update the certificate.
-  if (ssl->cert->cert_cb != NULL) {
-    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+  if (hs->config->cert->cert_cb != NULL) {
+    int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
     if (rv == 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
@@ -652,9 +652,8 @@
 }
 
 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)) {
+  if (!ssl_has_certificate(hs->config)) {
     hs->tls13_state = state_complete_second_flight;
     return ssl_hs_ok;
   }
@@ -681,12 +680,12 @@
 
   // Send a Channel ID assertion if necessary.
   if (ssl->s3->tlsext_channel_id_valid) {
-    if (!ssl_do_channel_id_callback(ssl)) {
+    if (!ssl_do_channel_id_callback(hs)) {
       hs->tls13_state = state_complete_second_flight;
       return ssl_hs_error;
     }
 
-    if (ssl->tlsext_channel_id_private == NULL) {
+    if (hs->config->tlsext_channel_id_private == NULL) {
       return ssl_hs_channel_id_lookup;
     }
 
@@ -866,7 +865,7 @@
     return 0;
   }
 
-  if (have_early_data_info && ssl->cert->enable_early_data) {
+  if (have_early_data_info && ssl->enable_early_data) {
     if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) ||
         CBS_len(&early_data_info) != 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index fe0449d..5303f3f 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -169,7 +169,7 @@
       return 0;
     }
     session->ticket_age_add_valid = 1;
-    if (ssl->cert->enable_early_data) {
+    if (ssl->enable_early_data) {
       session->ticket_max_early_data = kMaxEarlyDataAccepted;
     }
 
@@ -186,12 +186,12 @@
         !CBB_add_bytes(&nonce_cbb, nonce, sizeof(nonce)) ||
         !CBB_add_u16_length_prefixed(&body, &ticket) ||
         !tls13_derive_session_psk(session.get(), nonce) ||
-        !ssl_encrypt_ticket(ssl, &ticket, session.get()) ||
+        !ssl_encrypt_ticket(hs, &ticket, session.get()) ||
         !CBB_add_u16_length_prefixed(&body, &extensions)) {
       return 0;
     }
 
-    if (ssl->cert->enable_early_data) {
+    if (ssl->enable_early_data) {
       CBB early_data_info;
       if (!CBB_add_u16(&extensions, TLSEXT_TYPE_early_data) ||
           !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
@@ -302,7 +302,7 @@
   bool unused_renew;
   UniquePtr<SSL_SESSION> session;
   enum ssl_ticket_aead_result_t ret =
-      ssl_process_ticket(ssl, &session, &unused_renew, CBS_data(&ticket),
+      ssl_process_ticket(hs, &session, &unused_renew, CBS_data(&ticket),
                          CBS_len(&ticket), NULL, 0);
   switch (ret) {
     case ssl_ticket_aead_success:
@@ -383,7 +383,7 @@
       hs->new_session =
           SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
 
-      if (ssl->cert->enable_early_data &&
+      if (ssl->enable_early_data &&
           // Early data must be acceptable for this ticket.
           session->ticket_max_early_data != 0 &&
           // The client must have offered early data.
@@ -408,8 +408,9 @@
       ssl->s3->session_reused = true;
 
       // Resumption incorporates fresh key material, so refresh the timeout.
-      ssl_session_renew_timeout(ssl, hs->new_session.get(),
-                                ssl->session_ctx->session_psk_dhe_timeout);
+      ssl_session_renew_timeout(
+          ssl, hs->new_session.get(),
+          hs->config->session_ctx->session_psk_dhe_timeout);
       break;
 
     case ssl_ticket_aead_error:
@@ -599,9 +600,9 @@
 
   if (!ssl->s3->session_reused) {
     // Determine whether to request a client certificate.
-    hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+    hs->cert_request = !!(hs->config->verify_mode & SSL_VERIFY_PEER);
     // Only request a certificate if Channel ID isn't negotiated.
-    if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
+    if ((hs->config->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
         ssl->s3->tlsext_channel_id_valid) {
       hs->cert_request = false;
     }
@@ -635,13 +636,13 @@
       }
     }
 
-    if (ssl_has_client_CAs(ssl)) {
+    if (ssl_has_client_CAs(hs->config)) {
       CBB ca_contents;
       if (!CBB_add_u16(&cert_request_extensions,
                        TLSEXT_TYPE_certificate_authorities) ||
           !CBB_add_u16_length_prefixed(&cert_request_extensions,
                                        &ca_contents) ||
-          !ssl_add_client_CA_list(ssl, &ca_contents) ||
+          !ssl_add_client_CA_list(hs, &ca_contents) ||
           !CBB_flush(&cert_request_extensions)) {
         return ssl_hs_error;
       }
@@ -654,7 +655,7 @@
 
   // Send the server Certificate message, if necessary.
   if (!ssl->s3->session_reused) {
-    if (!ssl_has_certificate(ssl)) {
+    if (!ssl_has_certificate(hs->config)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
       return ssl_hs_error;
     }
@@ -805,7 +806,7 @@
   }
 
   const int allow_anonymous =
-      (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
+      (hs->config->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
   SSLMessage msg;
   if (!ssl->method->get_message(ssl, &msg)) {
     return ssl_hs_read_message;
diff --git a/ssl/tls_method.cc b/ssl/tls_method.cc
index 2ad2817..d0adcdb 100644
--- a/ssl/tls_method.cc
+++ b/ssl/tls_method.cc
@@ -141,16 +141,18 @@
 }
 static void ssl_noop_x509_session_clear(SSL_SESSION *session) {}
 static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session,
-                                                   SSL *ssl,
+                                                   SSL_HANDSHAKE *hs,
                                                    uint8_t *out_alert) {
   return 0;
 }
 
 static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {}
-static int ssl_noop_x509_ssl_new(SSL *ctx) { return 1; }
-static void ssl_noop_x509_ssl_free(SSL *ctx) { }
-static void ssl_noop_x509_ssl_flush_cached_client_CA(SSL *ssl) {}
-static int ssl_noop_x509_ssl_auto_chain_if_needed(SSL *ssl) { return 1; }
+static int ssl_noop_x509_ssl_new(SSL_HANDSHAKE *hs) { return 1; }
+static void ssl_noop_x509_ssl_config_free(SSL_CONFIG *cfg) {}
+static void ssl_noop_x509_ssl_flush_cached_client_CA(SSL_CONFIG *cfg) {}
+static int ssl_noop_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
+  return 1;
+}
 static int ssl_noop_x509_ssl_ctx_new(SSL_CTX *ctx) { return 1; }
 static void ssl_noop_x509_ssl_ctx_free(SSL_CTX *ctx) { }
 static void ssl_noop_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {}
@@ -168,7 +170,7 @@
   ssl_noop_x509_session_verify_cert_chain,
   ssl_noop_x509_hs_flush_cached_ca_names,
   ssl_noop_x509_ssl_new,
-  ssl_noop_x509_ssl_free,
+  ssl_noop_x509_ssl_config_free,
   ssl_noop_x509_ssl_flush_cached_client_CA,
   ssl_noop_x509_ssl_auto_chain_if_needed,
   ssl_noop_x509_ssl_ctx_new,