external/boringssl: Sync to 348f0d8db9c2a0eca0503ba654020209c579d552. am: 8437709157
am: 05e49fc79f

Change-Id: Ib0c7d49c20b9511327e60ab147940a127a6340d0
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index bcea3ff..b657833 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-9bbdf5832de8a2d395303c669b594fc61c791f4d
+348f0d8db9c2a0eca0503ba654020209c579d552
diff --git a/src/crypto/fipsmodule/ec/p256-x86_64_test.cc b/src/crypto/fipsmodule/ec/p256-x86_64_test.cc
index 4600c76..a802bfb 100644
--- a/src/crypto/fipsmodule/ec/p256-x86_64_test.cc
+++ b/src/crypto/fipsmodule/ec/p256-x86_64_test.cc
@@ -27,6 +27,7 @@
 #include <openssl/mem.h>
 
 #include "../bn/internal.h"
+#include "../../internal.h"
 #include "../../test/file_test.h"
 #include "../../test/test_util.h"
 #include "p256-x86_64.h"
@@ -39,7 +40,7 @@
 
 TEST(P256_X86_64Test, SelectW5) {
   // Fill a table with some garbage input.
-  P256_POINT table[16];
+  alignas(64) P256_POINT table[16];
   for (size_t i = 0; i < 16; i++) {
     OPENSSL_memset(table[i].X, 3 * i, sizeof(table[i].X));
     OPENSSL_memset(table[i].Y, 3 * i + 1, sizeof(table[i].Y));
@@ -64,7 +65,7 @@
 
 TEST(P256_X86_64Test, SelectW7) {
   // Fill a table with some garbage input.
-  P256_POINT_AFFINE table[64];
+  alignas(64) P256_POINT_AFFINE table[64];
   for (size_t i = 0; i < 64; i++) {
     OPENSSL_memset(table[i].X, 2 * i, sizeof(table[i].X));
     OPENSSL_memset(table[i].Y, 2 * i + 1, sizeof(table[i].Y));
diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h
index b3da0e8..dceaf03 100644
--- a/src/crypto/obj/obj_dat.h
+++ b/src/crypto/obj/obj_dat.h
@@ -57,7 +57,7 @@
 /* This file is generated by crypto/obj/objects.go. */
 
 
-#define NUM_NID 950
+#define NUM_NID 959
 
 static const uint8_t kObjectData[] = {
     /* NID_rsadsi */
@@ -3444,6 +3444,16 @@
     {"dh-cofactor-kdf", "dh-cofactor-kdf", NID_dh_cofactor_kdf, 0, NULL, 0},
     {"X25519", "X25519", NID_X25519, 0, NULL, 0},
     {"ED25519", "ED25519", NID_ED25519, 3, &kObjectData[6175], 0},
+    {"ChaCha20-Poly1305", "chacha20-poly1305", NID_chacha20_poly1305, 0, NULL,
+     0},
+    {"KxRSA", "kx-rsa", NID_kx_rsa, 0, NULL, 0},
+    {"KxECDHE", "kx-ecdhe", NID_kx_ecdhe, 0, NULL, 0},
+    {"KxPSK", "kx-psk", NID_kx_psk, 0, NULL, 0},
+    {"AuthRSA", "auth-rsa", NID_auth_rsa, 0, NULL, 0},
+    {"AuthECDSA", "auth-ecdsa", NID_auth_ecdsa, 0, NULL, 0},
+    {"AuthPSK", "auth-psk", NID_auth_psk, 0, NULL, 0},
+    {"KxANY", "kx-any", NID_kx_any, 0, NULL, 0},
+    {"AuthANY", "auth-any", NID_auth_any, 0, NULL, 0},
 };
 
 static const unsigned kNIDsInShortNameOrder[] = {
@@ -3474,6 +3484,10 @@
     426 /* AES-256-ECB */,
     428 /* AES-256-OFB */,
     914 /* AES-256-XTS */,
+    958 /* AuthANY */,
+    955 /* AuthECDSA */,
+    956 /* AuthPSK */,
+    954 /* AuthRSA */,
     91 /* BF-CBC */,
     93 /* BF-CFB */,
     92 /* BF-ECB */,
@@ -3505,6 +3519,7 @@
     13 /* CN */,
     141 /* CRLReason */,
     417 /* CSPName */,
+    950 /* ChaCha20-Poly1305 */,
     367 /* CrlID */,
     391 /* DC */,
     31 /* DES-CBC */,
@@ -3547,6 +3562,10 @@
     645 /* ITU-T */,
     646 /* JOINT-ISO-ITU-T */,
     773 /* KISA */,
+    957 /* KxANY */,
+    952 /* KxECDHE */,
+    953 /* KxPSK */,
+    951 /* KxRSA */,
     15 /* L */,
     856 /* LocalKeySet */,
     3 /* MD2 */,
@@ -4570,6 +4589,10 @@
     484 /* associatedDomain */,
     485 /* associatedName */,
     501 /* audio */,
+    958 /* auth-any */,
+    955 /* auth-ecdsa */,
+    956 /* auth-psk */,
+    954 /* auth-rsa */,
     882 /* authorityRevocationList */,
     91 /* bf-cbc */,
     93 /* bf-cfb */,
@@ -4640,6 +4663,7 @@
     677 /* certicom-arc */,
     517 /* certificate extensions */,
     883 /* certificateRevocationList */,
+    950 /* chacha20-poly1305 */,
     54 /* challengePassword */,
     407 /* characteristic-two-field */,
     395 /* clearance */,
@@ -4982,6 +5006,10 @@
     646 /* joint-iso-itu-t */,
     150 /* keyBag */,
     773 /* kisa */,
+    957 /* kx-any */,
+    952 /* kx-ecdhe */,
+    953 /* kx-psk */,
+    951 /* kx-rsa */,
     477 /* lastModifiedBy */,
     476 /* lastModifiedTime */,
     157 /* localKeyID */,
diff --git a/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num
index 572a01b..6dbc0f1 100644
--- a/src/crypto/obj/obj_mac.num
+++ b/src/crypto/obj/obj_mac.num
@@ -938,3 +938,12 @@
 dh_cofactor_kdf		947
 X25519		948
 ED25519		949
+chacha20_poly1305		950
+kx_rsa		951
+kx_ecdhe		952
+kx_psk		953
+auth_rsa		954
+auth_ecdsa		955
+auth_psk		956
+kx_any		957
+auth_any		958
diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt
index 03056de..f1a6395 100644
--- a/src/crypto/obj/objects.txt
+++ b/src/crypto/obj/objects.txt
@@ -1336,3 +1336,19 @@
 
 # See draft-ietf-curdle-pkix-04.
 1 3 101 112 : ED25519
+
+ : ChaCha20-Poly1305 : chacha20-poly1305
+
+# NIDs for TLS 1.2 cipher suite key exchanges.
+ : KxRSA : kx-rsa
+ : KxECDHE : kx-ecdhe
+ : KxPSK : kx-psk
+
+# NIDs for TLS 1.2 cipher suite authentication types.
+ : AuthRSA : auth-rsa
+ : AuthECDSA : auth-ecdsa
+ : AuthPSK : auth-psk
+
+# TLS 1.3 cipher suites do not specify key exchange or authentication.
+ : KxANY : kx-any
+ : AuthANY : auth-any
diff --git a/src/crypto/pem/pem_pk8.c b/src/crypto/pem/pem_pk8.c
index 550661d..15385ec 100644
--- a/src/crypto/pem/pem_pk8.c
+++ b/src/crypto/pem/pem_pk8.c
@@ -176,6 +176,7 @@
     }
     p8inf = PKCS8_decrypt(p8, psbuf, klen);
     X509_SIG_free(p8);
+    OPENSSL_cleanse(psbuf, klen);
     if (!p8inf)
         return NULL;
     ret = EVP_PKCS82PKEY(p8inf);
diff --git a/src/crypto/pem/pem_pkey.c b/src/crypto/pem/pem_pkey.c
index 058c031..9667550 100644
--- a/src/crypto/pem/pem_pkey.c
+++ b/src/crypto/pem/pem_pkey.c
@@ -114,6 +114,7 @@
         }
         p8inf = PKCS8_decrypt(p8, psbuf, klen);
         X509_SIG_free(p8);
+        OPENSSL_cleanse(psbuf, klen);
         if (!p8inf)
             goto p8err;
         ret = EVP_PKCS82PKEY(p8inf);
diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h
index bc0ee33..afeb2de 100644
--- a/src/include/openssl/nid.h
+++ b/src/include/openssl/nid.h
@@ -4198,6 +4198,42 @@
 #define NID_ED25519 949
 #define OBJ_ED25519 1L, 3L, 101L, 112L
 
+#define SN_chacha20_poly1305 "ChaCha20-Poly1305"
+#define LN_chacha20_poly1305 "chacha20-poly1305"
+#define NID_chacha20_poly1305 950
+
+#define SN_kx_rsa "KxRSA"
+#define LN_kx_rsa "kx-rsa"
+#define NID_kx_rsa 951
+
+#define SN_kx_ecdhe "KxECDHE"
+#define LN_kx_ecdhe "kx-ecdhe"
+#define NID_kx_ecdhe 952
+
+#define SN_kx_psk "KxPSK"
+#define LN_kx_psk "kx-psk"
+#define NID_kx_psk 953
+
+#define SN_auth_rsa "AuthRSA"
+#define LN_auth_rsa "auth-rsa"
+#define NID_auth_rsa 954
+
+#define SN_auth_ecdsa "AuthECDSA"
+#define LN_auth_ecdsa "auth-ecdsa"
+#define NID_auth_ecdsa 955
+
+#define SN_auth_psk "AuthPSK"
+#define LN_auth_psk "auth-psk"
+#define NID_auth_psk 956
+
+#define SN_kx_any "KxANY"
+#define LN_kx_any "kx-any"
+#define NID_kx_any 957
+
+#define SN_auth_any "AuthANY"
+#define LN_auth_any "auth-any"
+#define NID_auth_any 958
+
 
 #if defined(__cplusplus)
 } /* extern C */
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index a5ac325..63651b5 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -192,6 +192,10 @@
  * crypto/x509. */
 OPENSSL_EXPORT const SSL_METHOD *TLS_with_buffers_method(void);
 
+/* DTLS_with_buffers_method is like |DTLS_method|, but avoids all use of
+ * crypto/x509. */
+OPENSSL_EXPORT const SSL_METHOD *DTLS_with_buffers_method(void);
+
 /* SSL_CTX_new returns a newly-allocated |SSL_CTX| with default settings or NULL
  * on error. */
 OPENSSL_EXPORT SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);
@@ -1196,56 +1200,36 @@
  * get the cipher suite value. */
 OPENSSL_EXPORT uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *cipher);
 
-/* SSL_CIPHER_is_AES returns one if |cipher| uses AES (either GCM or CBC
- * mode). */
-OPENSSL_EXPORT int SSL_CIPHER_is_AES(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_has_SHA1_HMAC returns one if |cipher| uses HMAC-SHA1. */
-OPENSSL_EXPORT int SSL_CIPHER_has_SHA1_HMAC(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_has_SHA256_HMAC returns one if |cipher| uses HMAC-SHA256. */
-OPENSSL_EXPORT int SSL_CIPHER_has_SHA256_HMAC(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_has_SHA384_HMAC returns one if |cipher| uses HMAC-SHA384. */
-OPENSSL_EXPORT int SSL_CIPHER_has_SHA384_HMAC(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_is_AEAD returns one if |cipher| uses an AEAD cipher. */
-OPENSSL_EXPORT int SSL_CIPHER_is_AEAD(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_is_AESGCM returns one if |cipher| uses AES-GCM. */
-OPENSSL_EXPORT int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_is_AES128GCM returns one if |cipher| uses 128-bit AES-GCM. */
-OPENSSL_EXPORT int SSL_CIPHER_is_AES128GCM(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_is_AES128CBC returns one if |cipher| uses 128-bit AES in CBC
- * mode. */
-OPENSSL_EXPORT int SSL_CIPHER_is_AES128CBC(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_is_AES256CBC returns one if |cipher| uses 256-bit AES in CBC
- * mode. */
-OPENSSL_EXPORT int SSL_CIPHER_is_AES256CBC(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_is_CHACHA20POLY1305 returns one if |cipher| uses
- * CHACHA20_POLY1305. Note this includes both the RFC 7905 and
- * draft-agl-tls-chacha20poly1305-04 versions. */
-OPENSSL_EXPORT int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher);
-
-/* SSL_CIPHER_is_NULL returns one if |cipher| does not encrypt. */
-OPENSSL_EXPORT int SSL_CIPHER_is_NULL(const SSL_CIPHER *cipher);
+/* SSL_CIPHER_is_aead returns one if |cipher| uses an AEAD cipher. */
+OPENSSL_EXPORT int SSL_CIPHER_is_aead(const SSL_CIPHER *cipher);
 
 /* SSL_CIPHER_is_block_cipher returns one if |cipher| is a block cipher. */
 OPENSSL_EXPORT int SSL_CIPHER_is_block_cipher(const SSL_CIPHER *cipher);
 
-/* SSL_CIPHER_is_ECDSA returns one if |cipher| uses ECDSA. */
-OPENSSL_EXPORT int SSL_CIPHER_is_ECDSA(const SSL_CIPHER *cipher);
+/* SSL_CIPHER_get_cipher_nid returns the NID for |cipher|'s bulk
+ * cipher. Possible values are |NID_aes_128_gcm|, |NID_aes_256_gcm|,
+ * |NID_chacha20_poly1305|, |NID_aes_128_cbc|, |NID_aes_256_cbc|, and
+ * |NID_des_ede3_cbc|. */
+OPENSSL_EXPORT int SSL_CIPHER_get_cipher_nid(const SSL_CIPHER *cipher);
 
-/* SSL_CIPHER_is_ECDHE returns one if |cipher| uses ECDHE. */
-OPENSSL_EXPORT int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher);
+/* SSL_CIPHER_get_digest_nid returns the NID for |cipher|'s HMAC if it is a
+ * legacy cipher suite. For modern AEAD-based ciphers (see
+ * |SSL_CIPHER_is_aead|), it returns |NID_undef|.
+ *
+ * Note this function only returns the legacy HMAC digest, not the PRF hash. */
+OPENSSL_EXPORT int SSL_CIPHER_get_digest_nid(const SSL_CIPHER *cipher);
 
-/* SSL_CIPHER_is_static_RSA returns one if |cipher| uses the static RSA key
- * exchange. */
-OPENSSL_EXPORT int SSL_CIPHER_is_static_RSA(const SSL_CIPHER *cipher);
+/* SSL_CIPHER_get_kx_nid returns the NID for |cipher|'s key exchange. This may
+ * be |NID_kx_rsa|, |NID_kx_ecdhe|, or |NID_kx_psk| for TLS 1.2. In TLS 1.3,
+ * cipher suites do not specify the key exchange, so this function returns
+ * |NID_kx_any|. */
+OPENSSL_EXPORT int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_get_auth_nid returns the NID for |cipher|'s authentication
+ * type. This may be |NID_auth_rsa|, |NID_auth_ecdsa|, or |NID_auth_psk| for TLS
+ * 1.2. In TLS 1.3, cipher suites do not specify authentication, so this
+ * function returns |NID_auth_any|. */
+OPENSSL_EXPORT int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *cipher);
 
 /* SSL_CIPHER_get_min_version returns the minimum protocol version required
  * for |cipher|. */
@@ -1410,6 +1394,11 @@
  * preference. */
 OPENSSL_EXPORT STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx);
 
+/* SSL_CTX_cipher_in_group returns one if the |i|th cipher (see
+ * |SSL_CTX_get_ciphers|) is in the same equipreference group as the one
+ * following it and zero otherwise. */
+OPENSSL_EXPORT int SSL_CTX_cipher_in_group(const SSL_CTX *ctx, size_t i);
+
 /* SSL_get_ciphers returns the cipher list for |ssl|, in order of preference. */
 OPENSSL_EXPORT STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *ssl);
 
@@ -3974,6 +3963,64 @@
  * the session. */
 OPENSSL_EXPORT SSL_SESSION *SSL_get1_session(SSL *ssl);
 
+/* TODO(davidben): Convert all the callers of these old |SSL_CIPHER| functions
+ * and remove them. */
+
+/* SSL_CIPHER_is_AEAD calls |SSL_CIPHER_is_aead|. */
+OPENSSL_EXPORT int SSL_CIPHER_is_AEAD(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_AES returns one if |cipher| uses AES (either GCM or CBC
+ * mode). Use |SSL_CIPHER_get_cipher_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_AES(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_has_SHA1_HMAC returns one if |cipher| uses HMAC-SHA1. Use
+ * |SSL_CIPHER_get_digest_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_has_SHA1_HMAC(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_has_SHA256_HMAC returns one if |cipher| uses HMAC-SHA256. Use
+ * |SSL_CIPHER_get_digest_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_has_SHA256_HMAC(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_has_SHA384_HMAC returns one if |cipher| uses HMAC-SHA384. Use
+ * |SSL_CIPHER_get_digest_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_has_SHA384_HMAC(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_AESGCM returns one if |cipher| uses AES-GCM. Use
+ * |SSL_CIPHER_get_cipher_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_AES128GCM returns one if |cipher| uses 128-bit AES-GCM. Use
+ * |SSL_CIPHER_get_cipher_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_AES128GCM(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_AES128CBC returns one if |cipher| uses 128-bit AES in CBC
+ * mode. Use |SSL_CIPHER_get_cipher_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_AES128CBC(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_AES256CBC returns one if |cipher| uses 256-bit AES in CBC
+ * mode. Use |SSL_CIPHER_get_cipher_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_AES256CBC(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_CHACHA20POLY1305 returns one if |cipher| uses
+ * CHACHA20_POLY1305. Use |SSL_CIPHER_get_cipher_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_NULL returns one if |cipher| does not encrypt. Use
+ * |SSL_CIPHER_get_cipher_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_NULL(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_ECDSA returns one if |cipher| uses ECDSA. Use
+ * |SSL_CIPHER_get_auth_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_ECDSA(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_ECDHE returns one if |cipher| uses ECDHE. Use
+ * |SSL_CIPHER_get_kx_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_is_static_RSA returns one if |cipher| uses the static RSA key
+ * exchange. Use |SSL_CIPHER_get_kx_nid| instead. */
+OPENSSL_EXPORT int SSL_CIPHER_is_static_RSA(const SSL_CIPHER *cipher);
+
 
 /* Private structures.
  *
diff --git a/src/include/openssl/ssl3.h b/src/include/openssl/ssl3.h
index 39cd07b..f213dba 100644
--- a/src/include/openssl/ssl3.h
+++ b/src/include/openssl/ssl3.h
@@ -339,7 +339,6 @@
 #define SSL3_ST_SR_CLNT_HELLO_A (0x110 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CLNT_HELLO_B (0x111 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CLNT_HELLO_C (0x112 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_CLNT_HELLO_D (0x113 | SSL_ST_ACCEPT)
 /* write to client */
 #define SSL3_ST_SW_SRVR_HELLO_A (0x130 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_CERT_A (0x140 | SSL_ST_ACCEPT)
@@ -348,7 +347,6 @@
 /* read from client */
 #define SSL3_ST_SR_CERT_A (0x180 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_KEY_EXCH_A (0x190 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_KEY_EXCH_B (0x191 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CERT_VRFY_A (0x1A0 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CHANGE (0x1B0 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_NEXT_PROTO_A (0x210 | SSL_ST_ACCEPT)
diff --git a/src/ssl/d1_both.cc b/src/ssl/d1_both.cc
index 2fa0183..2538d28 100644
--- a/src/ssl/d1_both.cc
+++ b/src/ssl/d1_both.cc
@@ -298,9 +298,7 @@
   return frag;
 }
 
-/* dtls1_process_handshake_record reads a record for the handshake and processes
- * it. It returns one on success and 0 or -1 on error. */
-static int dtls1_process_handshake_record(SSL *ssl) {
+int dtls1_read_message(SSL *ssl) {
   SSL3_RECORD *rr = &ssl->s3->rrec;
   if (rr->length == 0) {
     int ret = dtls1_get_record(ssl);
@@ -419,63 +417,33 @@
   return 1;
 }
 
-int dtls1_get_message(SSL *ssl) {
-  if (ssl->s3->tmp.reuse_message) {
-    /* There must be a current message. */
-    assert(ssl->init_msg != NULL);
-    ssl->s3->tmp.reuse_message = 0;
-  } else {
-    dtls1_release_current_message(ssl, 0 /* don't free buffer */);
-  }
-
-  /* Process handshake records until the current message is ready. */
-  while (!dtls1_is_current_message_complete(ssl)) {
-    int ret = dtls1_process_handshake_record(ssl);
-    if (ret <= 0) {
-      return ret;
-    }
+bool dtls1_get_message(SSL *ssl, SSLMessage *out) {
+  if (!dtls1_is_current_message_complete(ssl)) {
+    return false;
   }
 
   hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
                                                  SSL_MAX_HANDSHAKE_FLIGHT];
-  assert(frag != NULL);
-  assert(frag->reassembly == NULL);
-  assert(ssl->d1->handshake_read_seq == frag->seq);
-
-  if (ssl->init_msg == NULL) {
+  out->type = frag->type;
+  CBS_init(&out->body, frag->data + DTLS1_HM_HEADER_LENGTH, frag->msg_len);
+  CBS_init(&out->raw, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len);
+  out->is_v2_hello = false;
+  if (!ssl->s3->has_message) {
     ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, frag->data,
                         frag->msg_len + DTLS1_HM_HEADER_LENGTH);
+    ssl->s3->has_message = 1;
   }
-
-  /* TODO(davidben): This function has a lot of implicit outputs. Simplify the
-   * |ssl_get_message| API. */
-  ssl->s3->tmp.message_type = frag->type;
-  ssl->init_msg = frag->data + DTLS1_HM_HEADER_LENGTH;
-  ssl->init_num = frag->msg_len;
-  return 1;
+  return true;
 }
 
-void dtls1_get_current_message(const SSL *ssl, CBS *out) {
-  assert(dtls1_is_current_message_complete(ssl));
-
-  hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq %
-                                                 SSL_MAX_HANDSHAKE_FLIGHT];
-  CBS_init(out, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len);
-}
-
-void dtls1_release_current_message(SSL *ssl, int free_buffer) {
-  if (ssl->init_msg == NULL) {
-    return;
-  }
-
+void dtls1_next_message(SSL *ssl) {
+  assert(ssl->s3->has_message);
   assert(dtls1_is_current_message_complete(ssl));
   size_t index = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
   dtls1_hm_fragment_free(ssl->d1->incoming_messages[index]);
   ssl->d1->incoming_messages[index] = NULL;
   ssl->d1->handshake_read_seq++;
-
-  ssl->init_msg = NULL;
-  ssl->init_num = 0;
+  ssl->s3->has_message = 0;
 }
 
 void dtls_clear_incoming_messages(SSL *ssl) {
@@ -489,7 +457,7 @@
   size_t current = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT;
   for (size_t i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) {
     /* Skip the current message. */
-    if (ssl->init_msg != NULL && i == current) {
+    if (ssl->s3->has_message && i == current) {
       assert(dtls1_is_current_message_complete(ssl));
       continue;
     }
@@ -519,7 +487,7 @@
 int dtls1_read_change_cipher_spec(SSL *ssl) {
   /* Process handshake records until there is a ChangeCipherSpec. */
   while (!ssl->d1->has_change_cipher_spec) {
-    int ret = dtls1_process_handshake_record(ssl);
+    int ret = dtls1_read_message(ssl);
     if (ret <= 0) {
       return ret;
     }
diff --git a/src/ssl/dtls_method.cc b/src/ssl/dtls_method.cc
index 947cfce..9189427 100644
--- a/src/ssl/dtls_method.cc
+++ b/src/ssl/dtls_method.cc
@@ -113,8 +113,8 @@
     dtls1_new,
     dtls1_free,
     dtls1_get_message,
-    dtls1_get_current_message,
-    dtls1_release_current_message,
+    dtls1_read_message,
+    dtls1_next_message,
     dtls1_read_app_data,
     dtls1_read_change_cipher_spec,
     dtls1_read_close_notify,
@@ -141,6 +141,15 @@
   return &kMethod;
 }
 
+const SSL_METHOD *DTLS_with_buffers_method(void) {
+  static const SSL_METHOD kMethod = {
+      0,
+      &kDTLSProtocolMethod,
+      &ssl_noop_x509_method,
+  };
+  return &kMethod;
+}
+
 /* Legacy version-locked methods. */
 
 const SSL_METHOD *DTLSv1_2_method(void) {
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 946316d..dd09797 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -492,7 +492,6 @@
 
       case SSL3_ST_FINISH_CLIENT_HANDSHAKE:
         ssl->method->on_handshake_complete(ssl);
-        ssl->method->release_current_message(ssl, 1 /* free_buffer */);
 
         SSL_SESSION_free(ssl->s3->established_session);
         if (ssl->session != NULL) {
@@ -767,21 +766,19 @@
 
 static int dtls1_get_hello_verify_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CBS hello_verify_request, cookie;
-  uint16_t server_version;
-
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (ssl->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) {
+  if (msg.type != DTLS1_MT_HELLO_VERIFY_REQUEST) {
     ssl->d1->send_cookie = false;
-    ssl->s3->tmp.reuse_message = 1;
     return 1;
   }
 
-  CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num);
+  CBS hello_verify_request = msg.body, cookie;
+  uint16_t server_version;
   if (!CBS_get_u16(&hello_verify_request, &server_version) ||
       !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
       CBS_len(&cookie) > sizeof(ssl->d1->cookie) ||
@@ -795,20 +792,21 @@
   ssl->d1->cookie_len = CBS_len(&cookie);
 
   ssl->d1->send_cookie = true;
+  ssl->method->next_message(ssl);
   return 1;
 }
 
-static int parse_server_version(SSL_HANDSHAKE *hs, uint16_t *out) {
+static int parse_server_version(SSL_HANDSHAKE *hs, uint16_t *out,
+                                const SSLMessage &msg) {
   SSL *const ssl = hs->ssl;
-  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO &&
-      ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) {
+  if (msg.type != SSL3_MT_SERVER_HELLO &&
+      msg.type != SSL3_MT_HELLO_RETRY_REQUEST) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     return 0;
   }
 
-  CBS server_hello;
-  CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
+  CBS server_hello = msg.body;
   if (!CBS_get_u16(&server_hello, out)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -817,8 +815,7 @@
 
   /* The server version may also be in the supported_versions extension if
    * applicable. */
-  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO ||
-      *out != TLS1_2_VERSION) {
+  if (msg.type != SSL3_MT_SERVER_HELLO || *out != TLS1_2_VERSION) {
     return 1;
   }
 
@@ -872,7 +869,8 @@
 
 static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     uint32_t err = ERR_peek_error();
     if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
@@ -889,7 +887,7 @@
   }
 
   uint16_t server_version;
-  if (!parse_server_version(hs, &server_version)) {
+  if (!parse_server_version(hs, &server_version, msg)) {
     return -1;
   }
 
@@ -925,14 +923,13 @@
 
   ssl_clear_tls13_state(hs);
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
     return -1;
   }
 
-  CBS server_hello, server_random, session_id;
+  CBS server_hello = msg.body, server_random, session_id;
   uint16_t cipher_suite;
   uint8_t compression_method;
-  CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
   if (!CBS_skip(&server_hello, 2 /* version */) ||
       !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
       !CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
@@ -970,8 +967,8 @@
                    CBS_len(&session_id));
   }
 
-  const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite);
-  if (c == NULL) {
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
+  if (cipher == NULL) {
     /* unknown cipher */
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -981,10 +978,10 @@
   /* 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);
-  if ((c->algorithm_mkey & mask_k) || (c->algorithm_auth & mask_a) ||
-      SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
-      SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl) ||
-      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, c)) {
+  if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
+      SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) ||
+      SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl) ||
+      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, cipher)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return -1;
@@ -996,7 +993,7 @@
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return -1;
     }
-    if (ssl->session->cipher != c) {
+    if (ssl->session->cipher != cipher) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return -1;
@@ -1009,14 +1006,14 @@
       return -1;
     }
   } else {
-    hs->new_session->cipher = c;
+    hs->new_session->cipher = cipher;
   }
-  hs->new_cipher = c;
+  hs->new_cipher = cipher;
 
   /* Now that the cipher is known, initialize the handshake hash and hash the
    * ServerHello. */
-  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl), c->algorithm_prf) ||
-      !ssl_hash_current_message(hs)) {
+  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl), hs->new_cipher) ||
+      !ssl_hash_message(hs, msg)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return -1;
   }
@@ -1061,27 +1058,27 @@
     return -1;
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
 static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE) ||
+      !ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  CBS cbs;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-
+  CBS body = msg.body;
   uint8_t alert = SSL_AD_DECODE_ERROR;
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
-  if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey, NULL, &cbs,
+  if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey, NULL, &body,
                             ssl->ctx->pool)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return -1;
@@ -1090,7 +1087,7 @@
   hs->new_session->certs = chain.release();
 
   if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0 ||
-      CBS_len(&cbs) != 0 ||
+      CBS_len(&body) != 0 ||
       !ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -1131,30 +1128,30 @@
     }
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
 static int ssl3_get_cert_status(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) {
+  if (msg.type != SSL3_MT_CERTIFICATE_STATUS) {
     /* A server may send status_request in ServerHello and then change
      * its mind about sending CertificateStatus. */
-    ssl->s3->tmp.reuse_message = 1;
     return 1;
   }
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  CBS certificate_status, ocsp_response;
+  CBS certificate_status = msg.body, ocsp_response;
   uint8_t status_type;
-  CBS_init(&certificate_status, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u8(&certificate_status, &status_type) ||
       status_type != TLSEXT_STATUSTYPE_ocsp ||
       !CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) ||
@@ -1172,17 +1169,19 @@
     return -1;
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
 static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) {
+  if (msg.type != SSL3_MT_SERVER_KEY_EXCHANGE) {
     /* Some ciphers (pure PSK) have an optional ServerKeyExchange message. */
     if (ssl_cipher_requires_server_key_exchange(hs->new_cipher)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
@@ -1190,22 +1189,16 @@
       return -1;
     }
 
-    ssl->s3->tmp.reuse_message = 1;
     return 1;
   }
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  /* Retain a copy of the original CBS to compute the signature over. */
-  CBS server_key_exchange;
-  CBS_init(&server_key_exchange, ssl->init_msg, ssl->init_num);
-  CBS server_key_exchange_orig = server_key_exchange;
-
   uint32_t alg_k = hs->new_cipher->algorithm_mkey;
   uint32_t alg_a = hs->new_cipher->algorithm_auth;
-
+  CBS server_key_exchange = msg.body;
   if (alg_a & SSL_aPSK) {
     CBS psk_identity_hint;
 
@@ -1281,11 +1274,11 @@
   }
 
   /* At this point, |server_key_exchange| contains the signature, if any, while
-   * |server_key_exchange_orig| contains the entire message. From that, derive
-   * a CBS containing just the parameter. */
+   * |msg.body| contains the entire message. From that, derive a CBS containing
+   * just the parameter. */
   CBS parameter;
-  CBS_init(&parameter, CBS_data(&server_key_exchange_orig),
-           CBS_len(&server_key_exchange_orig) - CBS_len(&server_key_exchange));
+  CBS_init(&parameter, CBS_data(&msg.body),
+           CBS_len(&msg.body) - CBS_len(&server_key_exchange));
 
   /* ServerKeyExchange should be signed by the server's public key. */
   if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
@@ -1360,35 +1353,34 @@
       return -1;
     }
   }
+
+  ssl->method->next_message(ssl);
   return 1;
 }
 
 static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int msg_ret = ssl->method->ssl_get_message(ssl);
-  if (msg_ret <= 0) {
-    return msg_ret;
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
   }
 
-  if (ssl->s3->tmp.message_type == SSL3_MT_SERVER_HELLO_DONE) {
-    ssl->s3->tmp.reuse_message = 1;
+  if (msg.type == SSL3_MT_SERVER_HELLO_DONE) {
     /* If we get here we don't need the handshake buffer as we won't be doing
      * client auth. */
     hs->transcript.FreeBuffer();
     return 1;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_REQUEST) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE_REQUEST) ||
+      !ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  CBS cbs;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-
   /* Get the certificate types. */
-  CBS certificate_types;
-  if (!CBS_get_u8_length_prefixed(&cbs, &certificate_types)) {
+  CBS body = msg.body, certificate_types;
+  if (!CBS_get_u8_length_prefixed(&body, &certificate_types)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return -1;
@@ -1402,7 +1394,7 @@
 
   if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     CBS supported_signature_algorithms;
-    if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
+    if (!CBS_get_u16_length_prefixed(&body, &supported_signature_algorithms) ||
         !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
@@ -1412,13 +1404,13 @@
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> ca_names =
-      ssl_parse_client_CA_list(ssl, &alert, &cbs);
+      ssl_parse_client_CA_list(ssl, &alert, &body);
   if (!ca_names) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return -1;
   }
 
-  if (CBS_len(&cbs) != 0) {
+  if (CBS_len(&body) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return -1;
@@ -1427,28 +1419,31 @@
   hs->cert_request = 1;
   hs->ca_names = std::move(ca_names);
   ssl->ctx->x509_method->hs_flush_cached_ca_names(hs);
+  ssl->method->next_message(ssl);
   return 1;
 }
 
 static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO_DONE) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO_DONE) ||
+      !ssl_hash_message(hs, msg)) {
     return -1;
   }
 
   /* ServerHelloDone is empty. */
-  if (ssl->init_num > 0) {
+  if (CBS_len(&msg.body) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return -1;
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
@@ -1793,19 +1788,19 @@
 
 static int ssl3_get_new_session_ticket(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_NEW_SESSION_TICKET) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_NEW_SESSION_TICKET) ||
+      !ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  CBS new_session_ticket, ticket;
+  CBS new_session_ticket = msg.body, ticket;
   uint32_t tlsext_tick_lifetime_hint;
-  CBS_init(&new_session_ticket, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u32(&new_session_ticket, &tlsext_tick_lifetime_hint) ||
       !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) ||
       CBS_len(&new_session_ticket) != 0) {
@@ -1819,6 +1814,7 @@
      * negotiating the extension. The value of |ticket_expected| is checked in
      * |ssl_update_cache| so is cleared here to avoid an unnecessary update. */
     hs->ticket_expected = 0;
+    ssl->method->next_message(ssl);
     return 1;
   }
 
@@ -1862,6 +1858,7 @@
     ssl->session = renewed_session.release();
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index 47fdc61..2d5b85e 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -172,7 +172,7 @@
 
 namespace bssl {
 
-static int ssl3_process_client_hello(SSL_HANDSHAKE *hs);
+static int ssl3_read_client_hello(SSL_HANDSHAKE *hs);
 static int ssl3_select_certificate(SSL_HANDSHAKE *hs);
 static int ssl3_select_parameters(SSL_HANDSHAKE *hs);
 static int ssl3_send_server_hello(SSL_HANDSHAKE *hs);
@@ -203,7 +203,7 @@
         break;
 
       case SSL3_ST_SR_CLNT_HELLO_A:
-        ret = ssl->method->ssl_get_message(ssl);
+        ret = ssl3_read_client_hello(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -211,24 +211,16 @@
         break;
 
       case SSL3_ST_SR_CLNT_HELLO_B:
-        ret = ssl3_process_client_hello(hs);
-        if (ret <= 0) {
-          goto end;
-        }
-        hs->state = SSL3_ST_SR_CLNT_HELLO_C;
-        break;
-
-      case SSL3_ST_SR_CLNT_HELLO_C:
         ret = ssl3_select_certificate(hs);
         if (ret <= 0) {
           goto end;
         }
         if (hs->state != SSL_ST_TLS13) {
-          hs->state = SSL3_ST_SR_CLNT_HELLO_D;
+          hs->state = SSL3_ST_SR_CLNT_HELLO_C;
         }
         break;
 
-      case SSL3_ST_SR_CLNT_HELLO_D:
+      case SSL3_ST_SR_CLNT_HELLO_C:
         ret = ssl3_select_parameters(hs);
         if (ret <= 0) {
           goto end;
@@ -304,7 +296,6 @@
         break;
 
       case SSL3_ST_SR_KEY_EXCH_A:
-      case SSL3_ST_SR_KEY_EXCH_B:
         ret = ssl3_get_client_key_exchange(hs);
         if (ret <= 0) {
           goto end;
@@ -418,7 +409,6 @@
 
       case SSL_ST_OK:
         ssl->method->on_handshake_complete(ssl);
-        ssl->method->release_current_message(ssl, 1 /* free_buffer */);
 
         /* If we aren't retaining peer certificates then we can discard it
          * now. */
@@ -687,15 +677,19 @@
   return nullptr;
 }
 
-static int ssl3_process_client_hello(SSL_HANDSHAKE *hs) {
+static int ssl3_read_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CLIENT_HELLO)) {
     return -1;
   }
 
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return -1;
@@ -760,6 +754,12 @@
 
 static int ssl3_select_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
+  }
+
   /* 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);
@@ -786,8 +786,7 @@
   }
 
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     return -1;
   }
 
@@ -806,9 +805,13 @@
 
 static int ssl3_select_parameters(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
+  }
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     return -1;
   }
 
@@ -914,9 +917,8 @@
 
   /* Now that all parameters are known, initialize the handshake hash and hash
    * the ClientHello. */
-  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl),
-                               hs->new_cipher->algorithm_prf) ||
-      !ssl_hash_current_message(hs)) {
+  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl), hs->new_cipher) ||
+      !ssl_hash_message(hs, msg)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return -1;
   }
@@ -926,6 +928,7 @@
     hs->transcript.FreeBuffer();
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
@@ -1177,14 +1180,15 @@
   SSL *const ssl = hs->ssl;
   assert(hs->cert_request);
 
-  int msg_ret = ssl->method->ssl_get_message(ssl);
-  if (msg_ret <= 0) {
-    return msg_ret;
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) {
+  if (msg.type != SSL3_MT_CERTIFICATE) {
     if (ssl->version == SSL3_VERSION &&
-        ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
+        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) {
@@ -1196,7 +1200,6 @@
       /* 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;
-      ssl->s3->tmp.reuse_message = 1;
       return 1;
     }
 
@@ -1205,13 +1208,11 @@
     return -1;
   }
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  CBS certificate_msg;
-  CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
-
+  CBS certificate_msg = msg.body;
   uint8_t alert = SSL_AD_DECODE_ERROR;
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
   if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey,
@@ -1254,36 +1255,32 @@
     /* 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;
-    return 1;
-  }
-
-  /* The hash will have been filled in. */
-  if (ssl->retain_only_sha256_of_client_certs) {
+  } else if (ssl->retain_only_sha256_of_client_certs) {
+    /* The hash will have been filled in. */
     hs->new_session->peer_sha256_valid = 1;
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
 static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CBS client_key_exchange;
   uint8_t *premaster_secret = NULL;
   size_t premaster_secret_len = 0;
   uint8_t *decrypt_buf = NULL;
 
-  if (hs->state == SSL3_ST_SR_KEY_EXCH_A) {
-    int ret = ssl->method->ssl_get_message(ssl);
-    if (ret <= 0) {
-      return ret;
-    }
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CLIENT_KEY_EXCHANGE)) {
     return -1;
   }
 
-  CBS_init(&client_key_exchange, ssl->init_msg, ssl->init_num);
+  CBS client_key_exchange = msg.body;
   uint32_t alg_k = hs->new_cipher->algorithm_mkey;
   uint32_t alg_a = hs->new_cipher->algorithm_auth;
 
@@ -1350,7 +1347,6 @@
         goto err;
       case ssl_private_key_retry:
         ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
-        hs->state = SSL3_ST_SR_KEY_EXCH_B;
         goto err;
     }
 
@@ -1488,7 +1484,7 @@
     premaster_secret_len = new_len;
   }
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     goto err;
   }
 
@@ -1502,6 +1498,7 @@
 
   OPENSSL_cleanse(premaster_secret, premaster_secret_len);
   OPENSSL_free(premaster_secret);
+  ssl->method->next_message(ssl);
   return 1;
 
 err:
@@ -1516,7 +1513,6 @@
 
 static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CBS certificate_verify, signature;
 
   /* Only RSA and ECDSA client certificates are supported, so a
    * CertificateVerify is required if and only if there's a client certificate.
@@ -1526,18 +1522,19 @@
     return 1;
   }
 
-  int msg_ret = ssl->method->ssl_get_message(ssl);
-  if (msg_ret <= 0) {
-    return msg_ret;
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE_VERIFY)) {
     return -1;
   }
 
-  CBS_init(&certificate_verify, ssl->init_msg, ssl->init_num);
+  CBS certificate_verify = msg.body, signature;
 
-  /* Determine the digest type if needbe. */
+  /* Determine the signature algorithm. */
   uint16_t signature_algorithm = 0;
   if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) {
     if (!CBS_get_u16(&certificate_verify, &signature_algorithm)) {
@@ -1603,10 +1600,11 @@
   /* The handshake buffer is no longer necessary, and we may hash the current
    * message.*/
   hs->transcript.FreeBuffer();
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return -1;
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
@@ -1614,47 +1612,50 @@
  * sets the next_proto member in s if found */
 static int ssl3_get_next_proto(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_NEXT_PROTO) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_NEXT_PROTO) ||
+      !ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  CBS next_protocol, selected_protocol, padding;
-  CBS_init(&next_protocol, ssl->init_msg, ssl->init_num);
+  CBS next_protocol = msg.body, selected_protocol, padding;
   if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) ||
       !CBS_get_u8_length_prefixed(&next_protocol, &padding) ||
       CBS_len(&next_protocol) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return 0;
+    return -1;
   }
 
   if (!CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated,
                 &ssl->s3->next_proto_negotiated_len)) {
-    return 0;
+    return -1;
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
 /* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
 static int ssl3_get_channel_id(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int msg_ret = ssl->method->ssl_get_message(ssl);
-  if (msg_ret <= 0) {
-    return msg_ret;
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
+  if (ret <= 0) {
+    return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
-      !tls1_verify_channel_id(hs) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CHANNEL_ID) ||
+      !tls1_verify_channel_id(hs, msg) ||
+      !ssl_hash_message(hs, msg)) {
     return -1;
   }
+  ssl->method->next_message(ssl);
   return 1;
 }
 
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 2d3557c..931ac82 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -338,10 +338,10 @@
                             size_t *out_fixed_iv_len, const SSL_CIPHER *cipher,
                             uint16_t version, int is_dtls);
 
-/* ssl_get_handshake_digest returns the |EVP_MD| corresponding to
- * |algorithm_prf| and the |version|. */
-const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf,
-                                       uint16_t version);
+/* ssl_get_handshake_digest returns the |EVP_MD| corresponding to |version| and
+ * |cipher|. */
+const EVP_MD *ssl_get_handshake_digest(uint16_t version,
+                                       const SSL_CIPHER *cipher);
 
 /* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in
  * |ssl_method|. It sets |*out_cipher_list| to a newly-allocated
@@ -397,7 +397,7 @@
    * the handshake transcript. Subsequent calls to |Update| will update the
    * rolling hash. It returns one on success and zero on failure. It is an error
    * to call this function after the handshake buffer is released. */
-  bool InitHash(uint16_t version, int algorithm_prf);
+  bool InitHash(uint16_t version, const SSL_CIPHER *cipher);
 
   const uint8_t *buffer_data() const {
     return reinterpret_cast<const uint8_t *>(buffer_->data);
@@ -825,6 +825,15 @@
 
 /* Handshake messages. */
 
+struct SSLMessage {
+  bool is_v2_hello;
+  uint8_t type;
+  CBS body;
+  /* raw is the entire serialized handshake message, including the TLS or DTLS
+   * message header. */
+  CBS raw;
+};
+
 /* SSL_MAX_HANDSHAKE_FLIGHT is the number of messages, including
  * ChangeCipherSpec, in the longest handshake flight. Currently this is the
  * client's second leg in a full handshake when client certificates, NPN, and
@@ -835,6 +844,11 @@
  * in a handshake message for |ssl|. */
 size_t ssl_max_handshake_message_len(const SSL *ssl);
 
+/* ssl_read_message reads a message for the old |BIO|-based state machine. On
+ * success, it returns one and sets |*out| to the current message. Otherwise, it
+ * returns <= 0. */
+int ssl_read_message(SSL *ssl, SSLMessage *out);
+
 /* dtls_clear_incoming_messages releases all buffered incoming messages. */
 void dtls_clear_incoming_messages(SSL *ssl);
 
@@ -1048,7 +1062,7 @@
  * up to the binders has a valid signature using the value of |session|'s
  * resumption secret. It returns 1 on success, and 0 on failure. */
 int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
-                            CBS *binders);
+                            const SSLMessage &msg, CBS *binders);
 
 
 /* Handshake functions. */
@@ -1058,7 +1072,6 @@
   ssl_hs_ok,
   ssl_hs_read_message,
   ssl_hs_flush,
-  ssl_hs_flush_and_read_message,
   ssl_hs_x509_lookup,
   ssl_hs_channel_id_lookup,
   ssl_hs_private_key_operation,
@@ -1307,9 +1320,9 @@
 /* ssl_handshake_free releases all memory associated with |hs|. */
 void ssl_handshake_free(SSL_HANDSHAKE *hs);
 
-/* ssl_check_message_type checks if the current message has type |type|. If so
- * it returns one. Otherwise, it sends an alert and returns zero. */
-int ssl_check_message_type(SSL *ssl, int type);
+/* ssl_check_message_type checks if |msg| has type |type|. If so it returns
+ * one. Otherwise, it sends an alert and returns zero. */
+int ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type);
 
 /* tls13_handshake runs the TLS 1.3 handshake. It returns one on success and <=
  * 0 on error. It sets |out_early_return| to one if we've completed the
@@ -1323,15 +1336,17 @@
 
 /* tls13_post_handshake processes a post-handshake message. It returns one on
  * success and zero on failure. */
-int tls13_post_handshake(SSL *ssl);
+int tls13_post_handshake(SSL *ssl, const SSLMessage &msg);
 
-int tls13_process_certificate(SSL_HANDSHAKE *hs, int allow_anonymous);
-int tls13_process_certificate_verify(SSL_HANDSHAKE *hs);
+int tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                              int allow_anonymous);
+int tls13_process_certificate_verify(SSL_HANDSHAKE *hs, const SSLMessage &msg);
 
-/* tls13_process_finished processes the current message as a Finished message
- * from the peer. If |use_saved_value| is one, the verify_data is compared
- * against |hs->expected_client_finished| rather than computed fresh. */
-int tls13_process_finished(SSL_HANDSHAKE *hs, int use_saved_value);
+/* tls13_process_finished processes |msg| as a Finished message from the
+ * peer. If |use_saved_value| is one, the verify_data is compared against
+ * |hs->expected_client_finished| rather than computed fresh. */
+int tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                           int use_saved_value);
 
 int tls13_add_certificate(SSL_HANDSHAKE *hs);
 
@@ -1341,7 +1356,7 @@
 enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs);
 
 int tls13_add_finished(SSL_HANDSHAKE *hs);
-int tls13_process_new_session_ticket(SSL *ssl);
+int tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg);
 
 int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t **out_secret,
                                         size_t *out_secret_len,
@@ -1419,8 +1434,8 @@
 
 /* ClientHello functions. */
 
-int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out, const uint8_t *in,
-                          size_t in_len);
+int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
+                          const SSLMessage &msg);
 
 int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
                                    CBS *out, uint16_t extension_type);
@@ -1554,6 +1569,10 @@
  * crypto/x509. */
 extern const SSL_X509_METHOD ssl_crypto_x509_method;
 
+/* ssl_noop_x509_method provides the |SSL_X509_METHOD| functions that avoid
+ * crypto/x509. */
+extern const SSL_X509_METHOD ssl_noop_x509_method;
+
 struct SSL3_RECORD {
   /* type is the record type. */
   uint8_t type;
@@ -1644,6 +1663,10 @@
    * V2ClientHello rather than received from the peer directly. */
   unsigned is_v2_hello:1;
 
+  /* has_message is true if the current handshake message has been returned
+   * at least once by |get_message| and false otherwise. */
+  unsigned has_message:1;
+
   /* initial_handshake_complete is true if the initial handshake has
    * completed. */
   unsigned initial_handshake_complete:1;
@@ -1707,10 +1730,6 @@
    * TODO(davidben): Move everything not needed after the handshake completes to
    * |hs| and remove this. */
   struct {
-    int message_type;
-
-    int reuse_message;
-
     uint8_t new_mac_secret_len;
     uint8_t new_key_len;
     uint8_t new_fixed_iv_len;
@@ -1891,11 +1910,6 @@
 
   BUF_MEM *init_buf; /* buffer used during init */
 
-  /* init_msg is a pointer to the current handshake message body. */
-  const uint8_t *init_msg;
-  /* init_num is the length of the current handshake message body. */
-  uint32_t init_num;
-
   SSL3_STATE *s3;  /* SSLv3 variables */
   DTLS1_STATE *d1; /* DTLSv1 variables */
 
@@ -2135,9 +2149,9 @@
 
 int ssl3_get_finished(SSL_HANDSHAKE *hs);
 int ssl3_send_alert(SSL *ssl, int level, int desc);
-int ssl3_get_message(SSL *ssl);
-void ssl3_get_current_message(const SSL *ssl, CBS *out);
-void ssl3_release_current_message(SSL *ssl, int free_buffer);
+bool ssl3_get_message(SSL *ssl, SSLMessage *out);
+int ssl3_read_message(SSL *ssl);
+void ssl3_next_message(SSL *ssl);
 
 int ssl3_send_finished(SSL_HANDSHAKE *hs);
 int ssl3_dispatch_alert(SSL *ssl);
@@ -2174,9 +2188,9 @@
  * the pending flight. It returns one on success and zero on error. */
 int ssl_add_message_cbb(SSL *ssl, CBB *cbb);
 
-/* ssl_hash_current_message incorporates the current handshake message into the
- * handshake hash. It returns one on success and zero on allocation failure. */
-int ssl_hash_current_message(SSL_HANDSHAKE *hs);
+/* ssl_hash_message incorporates |msg| into the handshake hash. It returns one
+ * on success and zero on allocation failure. */
+bool ssl_hash_message(SSL_HANDSHAKE *hs, const SSLMessage &msg);
 
 /* dtls1_get_record reads a new input record. On success, it places it in
  * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
@@ -2214,9 +2228,9 @@
 int dtls1_connect(SSL *ssl);
 void dtls1_free(SSL *ssl);
 
-int dtls1_get_message(SSL *ssl);
-void dtls1_get_current_message(const SSL *ssl, CBS *out);
-void dtls1_release_current_message(SSL *ssl, int free_buffer);
+bool dtls1_get_message(SSL *ssl, SSLMessage *out);
+int dtls1_read_message(SSL *ssl);
+void dtls1_next_message(SSL *ssl);
 int dtls1_dispatch_alert(SSL *ssl);
 
 int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which);
@@ -2278,10 +2292,10 @@
     const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id,
     size_t session_id_len);
 
-/* tls1_verify_channel_id processes the current message as a Channel ID message,
- * and verifies the signature. If the key is valid, it saves the Channel ID and
- * returns one. Otherwise, it returns zero. */
-int tls1_verify_channel_id(SSL_HANDSHAKE *hs);
+/* 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
+ * one. Otherwise, it returns zero. */
+int tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg);
 
 /* tls1_write_channel_id generates a Channel ID message and puts the output in
  * |cbb|. |ssl->tlsext_channel_id_private| must already be set before calling.
@@ -2355,21 +2369,19 @@
   char is_dtls;
   int (*ssl_new)(SSL *ssl);
   void (*ssl_free)(SSL *ssl);
-  /* ssl_get_message reads the next handshake message. On success, it returns
-   * one and sets |ssl->s3->tmp.message_type|, |ssl->init_msg|, and
-   * |ssl->init_num|. Otherwise, it returns <= 0. */
-  int (*ssl_get_message)(SSL *ssl);
-  /* get_current_message sets |*out| to the current handshake message. This
-   * includes the protocol-specific message header. */
-  void (*get_current_message)(const SSL *ssl, CBS *out);
-  /* release_current_message is called to release the current handshake message.
-   * If |free_buffer| is one, buffers will also be released. */
-  void (*release_current_message)(SSL *ssl, int free_buffer);
+  /* get_message sets |*out| to the current handshake message and returns true
+   * if one has been received. It returns false if more input is needed. */
+  bool (*get_message)(SSL *ssl, bssl::SSLMessage *out);
+  /* read_message reads additional handshake data for |get_message|. On success,
+   * it returns one. Otherwise, it returns <= 0. */
+  int (*read_message)(SSL *ssl);
+  /* next_message is called to release the current handshake message. */
+  void (*next_message)(SSL *ssl);
   /* read_app_data reads up to |len| bytes of application data into |buf|. On
    * success, it returns the number of bytes read. Otherwise, it returns <= 0
    * and sets |*out_got_handshake| to whether the failure was due to a
-   * post-handshake handshake message. If so, it fills in the current message as
-   * in |ssl_get_message|. */
+   * post-handshake handshake message. If so, any handshake messages consumed
+   * may be read with |get_message|. */
   int (*read_app_data)(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
                        int peek);
   int (*read_change_cipher_spec)(SSL *ssl);
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index 4d53d53..9c4aa7f 100644
--- a/src/ssl/s3_both.cc
+++ b/src/ssl/s3_both.cc
@@ -187,12 +187,11 @@
 
 void ssl_handshake_free(SSL_HANDSHAKE *hs) { Delete(hs); }
 
-int ssl_check_message_type(SSL *ssl, int type) {
-  if (ssl->s3->tmp.message_type != type) {
+int ssl_check_message_type(SSL *ssl, const SSLMessage &msg, int type) {
+  if (msg.type != type) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    ERR_add_error_dataf("got type %d, wanted type %d",
-                        ssl->s3->tmp.message_type, type);
+    ERR_add_error_dataf("got type %d, wanted type %d", msg.type, type);
     return 0;
   }
 
@@ -422,12 +421,13 @@
 
 int ssl3_get_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl);
+  SSLMessage msg;
+  int ret = ssl_read_message(ssl, &msg);
   if (ret <= 0) {
     return ret;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_FINISHED)) {
     return -1;
   }
 
@@ -437,12 +437,11 @@
   if (!hs->transcript.GetFinishedMAC(finished, &finished_len,
                                      SSL_get_session(ssl), !ssl->server,
                                      ssl3_protocol_version(ssl)) ||
-      !ssl_hash_current_message(hs)) {
+      !ssl_hash_message(hs, msg)) {
     return -1;
   }
 
-  int finished_ok = ssl->init_num == finished_len &&
-                    CRYPTO_memcmp(ssl->init_msg, finished, finished_len) == 0;
+  int finished_ok = CBS_mem_equal(&msg.body, finished, finished_len);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   finished_ok = 1;
 #endif
@@ -469,6 +468,7 @@
     }
   }
 
+  ssl->method->next_message(ssl);
   return 1;
 }
 
@@ -515,6 +515,16 @@
   return kMaxMessageLen;
 }
 
+int ssl_read_message(SSL *ssl, SSLMessage *out) {
+  while (!ssl->method->get_message(ssl, out)) {
+    int ret = ssl->method->read_message(ssl);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+  return 1;
+}
+
 static int extend_handshake_buffer(SSL *ssl, size_t length) {
   if (!BUF_MEM_reserve(ssl->init_buf, length)) {
     return -1;
@@ -682,7 +692,62 @@
   return 1;
 }
 
-int ssl3_get_message(SSL *ssl) {
+/* TODO(davidben): Remove |out_bytes_needed| and inline into |ssl3_get_message|
+ * when the entire record is copied into |init_buf|. */
+static bool parse_message(SSL *ssl, SSLMessage *out, size_t *out_bytes_needed) {
+  if (ssl->init_buf == NULL) {
+    *out_bytes_needed = 4;
+    return false;
+  }
+
+  CBS cbs;
+  uint32_t len;
+  CBS_init(&cbs, reinterpret_cast<const uint8_t *>(ssl->init_buf->data),
+           ssl->init_buf->length);
+  if (!CBS_get_u8(&cbs, &out->type) ||
+      !CBS_get_u24(&cbs, &len)) {
+    *out_bytes_needed = 4;
+    return false;
+  }
+
+  if (!CBS_get_bytes(&cbs, &out->body, len)) {
+    *out_bytes_needed = 4 + len;
+    return false;
+  }
+
+  CBS_init(&out->raw, reinterpret_cast<const uint8_t *>(ssl->init_buf->data),
+           4 + len);
+  out->is_v2_hello = ssl->s3->is_v2_hello;
+  if (!ssl->s3->has_message) {
+    if (!out->is_v2_hello) {
+      ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE,
+                          CBS_data(&out->raw), CBS_len(&out->raw));
+    }
+    ssl->s3->has_message = 1;
+  }
+  return true;
+}
+
+bool ssl3_get_message(SSL *ssl, SSLMessage *out) {
+  size_t unused;
+  return parse_message(ssl, out, &unused);
+}
+
+int ssl3_read_message(SSL *ssl) {
+  SSLMessage msg;
+  size_t bytes_needed;
+  if (parse_message(ssl, &msg, &bytes_needed)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
+  }
+
+  /* Enforce the limit so the peer cannot force us to buffer 16MB. */
+  if (bytes_needed > 4 + ssl_max_handshake_message_len(ssl)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
+    return -1;
+  }
+
   /* Re-create the handshake buffer if needed. */
   if (ssl->init_buf == NULL) {
     ssl->init_buf = BUF_MEM_new();
@@ -691,85 +756,45 @@
     }
   }
 
+  /* Bypass the record layer for the first message to handle V2ClientHello. */
   if (ssl->server && !ssl->s3->v2_hello_done) {
-    /* Bypass the record layer for the first message to handle V2ClientHello. */
     int ret = read_v2_client_hello(ssl);
-    if (ret <= 0) {
-      return ret;
+    if (ret > 0) {
+      ssl->s3->v2_hello_done = 1;
     }
-    ssl->s3->v2_hello_done = 1;
-  }
-
-  if (ssl->s3->tmp.reuse_message) {
-    /* There must be a current message. */
-    assert(ssl->init_msg != NULL);
-    ssl->s3->tmp.reuse_message = 0;
-  } else {
-    ssl3_release_current_message(ssl, 0 /* don't free buffer */);
-  }
-
-  /* Read the message header, if we haven't yet. */
-  int ret = extend_handshake_buffer(ssl, SSL3_HM_HEADER_LENGTH);
-  if (ret <= 0) {
     return ret;
   }
 
-  /* Parse out the length. Cap it so the peer cannot force us to buffer up to
-   * 2^24 bytes. */
-  const uint8_t *p = (uint8_t *)ssl->init_buf->data;
-  size_t msg_len = (((uint32_t)p[1]) << 16) | (((uint32_t)p[2]) << 8) | p[3];
-  if (msg_len > ssl_max_handshake_message_len(ssl)) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
-    return -1;
-  }
-
-  /* Read the message body, if we haven't yet. */
-  ret = extend_handshake_buffer(ssl, SSL3_HM_HEADER_LENGTH + msg_len);
-  if (ret <= 0) {
-    return ret;
-  }
-
-  /* We have now received a complete message. */
-  if (ssl->init_msg == NULL && !ssl->s3->is_v2_hello) {
-    ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE,
-                        ssl->init_buf->data, ssl->init_buf->length);
-  }
-
-  ssl->s3->tmp.message_type = ((const uint8_t *)ssl->init_buf->data)[0];
-  ssl->init_msg = (uint8_t*)ssl->init_buf->data + SSL3_HM_HEADER_LENGTH;
-  ssl->init_num = ssl->init_buf->length - SSL3_HM_HEADER_LENGTH;
-  return 1;
+  return extend_handshake_buffer(ssl, bytes_needed);
 }
 
-void ssl3_get_current_message(const SSL *ssl, CBS *out) {
-  CBS_init(out, (uint8_t *)ssl->init_buf->data, ssl->init_buf->length);
-}
-
-int ssl_hash_current_message(SSL_HANDSHAKE *hs) {
-  /* V2ClientHellos are hashed implicitly. */
-  if (hs->ssl->s3->is_v2_hello) {
-    return 1;
+bool ssl_hash_message(SSL_HANDSHAKE *hs, const SSLMessage &msg) {
+  /* V2ClientHello messages are pre-hashed. */
+  if (msg.is_v2_hello) {
+    return true;
   }
 
-  CBS cbs;
-  hs->ssl->method->get_current_message(hs->ssl, &cbs);
-  return hs->transcript.Update(CBS_data(&cbs), CBS_len(&cbs));
+  return hs->transcript.Update(CBS_data(&msg.raw), CBS_len(&msg.raw));
 }
 
-void ssl3_release_current_message(SSL *ssl, int free_buffer) {
-  if (ssl->init_msg != NULL) {
-    /* |init_buf| never contains data beyond the current message. */
-    assert(SSL3_HM_HEADER_LENGTH + ssl->init_num == ssl->init_buf->length);
-
-    /* Clear the current message. */
-    ssl->init_msg = NULL;
-    ssl->init_num = 0;
-    ssl->init_buf->length = 0;
-    ssl->s3->is_v2_hello = 0;
+void ssl3_next_message(SSL *ssl) {
+  SSLMessage msg;
+  if (!ssl3_get_message(ssl, &msg) ||
+      ssl->init_buf == NULL ||
+      ssl->init_buf->length < CBS_len(&msg.raw)) {
+    assert(0);
+    return;
   }
 
-  if (free_buffer) {
+  OPENSSL_memmove(ssl->init_buf->data, ssl->init_buf->data + CBS_len(&msg.raw),
+                  ssl->init_buf->length - CBS_len(&msg.raw));
+  ssl->init_buf->length -= CBS_len(&msg.raw);
+  ssl->s3->is_v2_hello = 0;
+  ssl->s3->has_message = 0;
+
+  /* Post-handshake messages are rare, so release the buffer after every
+   * message. During the handshake, |on_handshake_complete| will release it. */
+  if (!SSL_in_init(ssl) && ssl->init_buf->length == 0) {
     BUF_MEM_free(ssl->init_buf);
     ssl->init_buf = NULL;
   }
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index 262df6d..4f802df 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -377,8 +377,6 @@
   assert(!ssl->s3->aead_read_ctx->is_null_cipher());
   *out_got_handshake = 0;
 
-  ssl->method->release_current_message(ssl, 0 /* don't free buffer */);
-
   SSL3_RECORD *rr = &ssl->s3->rrec;
 
   for (;;) {
@@ -413,7 +411,7 @@
       }
 
       /* Parse post-handshake handshake messages. */
-      int ret = ssl3_get_message(ssl);
+      int ret = ssl3_read_message(ssl);
       if (ret <= 0) {
         return ret;
       }
diff --git a/src/ssl/ssl_cipher.cc b/src/ssl/ssl_cipher.cc
index f1a215f..de4a4b4 100644
--- a/src/ssl/ssl_cipher.cc
+++ b/src/ssl/ssl_cipher.cc
@@ -742,9 +742,9 @@
   return 1;
 }
 
-const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf,
-                                       uint16_t version) {
-  switch (algorithm_prf) {
+const EVP_MD *ssl_get_handshake_digest(uint16_t version,
+                                       const SSL_CIPHER *cipher) {
+  switch (cipher->algorithm_prf) {
     case SSL_HANDSHAKE_MAC_DEFAULT:
       return version >= TLS1_2_VERSION ? EVP_sha256() : EVP_md5_sha1();
     case SSL_HANDSHAKE_MAC_SHA256:
@@ -752,6 +752,7 @@
     case SSL_HANDSHAKE_MAC_SHA384:
       return EVP_sha384();
     default:
+      assert(0);
       return NULL;
   }
 }
@@ -1454,10 +1455,80 @@
   return (cipher->algorithm_mac & SSL_SHA384) != 0;
 }
 
-int SSL_CIPHER_is_AEAD(const SSL_CIPHER *cipher) {
+int SSL_CIPHER_is_aead(const SSL_CIPHER *cipher) {
   return (cipher->algorithm_mac & SSL_AEAD) != 0;
 }
 
+int SSL_CIPHER_get_cipher_nid(const SSL_CIPHER *cipher) {
+  switch (cipher->algorithm_enc) {
+    case SSL_eNULL:
+      return NID_undef;
+    case SSL_3DES:
+      return NID_des_ede3_cbc;
+    case SSL_AES128:
+      return NID_aes_128_cbc;
+    case SSL_AES256:
+      return NID_aes_256_cbc;
+    case SSL_AES128GCM:
+      return NID_aes_128_gcm;
+    case SSL_AES256GCM:
+      return NID_aes_256_gcm;
+    case SSL_CHACHA20POLY1305:
+      return NID_chacha20_poly1305;
+  }
+  assert(0);
+  return NID_undef;
+}
+
+int SSL_CIPHER_get_digest_nid(const SSL_CIPHER *cipher) {
+  switch (cipher->algorithm_mac) {
+    case SSL_AEAD:
+      return NID_undef;
+    case SSL_SHA1:
+      return NID_sha1;
+    case SSL_SHA256:
+      return NID_sha256;
+    case SSL_SHA384:
+      return NID_sha384;
+  }
+  assert(0);
+  return NID_undef;
+}
+
+int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *cipher) {
+  switch (cipher->algorithm_mkey) {
+    case SSL_kRSA:
+      return NID_kx_rsa;
+    case SSL_kECDHE:
+      return NID_kx_ecdhe;
+    case SSL_kPSK:
+      return NID_kx_psk;
+    case SSL_kGENERIC:
+      return NID_kx_any;
+  }
+  assert(0);
+  return NID_undef;
+}
+
+int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *cipher) {
+  switch (cipher->algorithm_auth) {
+    case SSL_aRSA:
+      return NID_auth_rsa;
+    case SSL_aECDSA:
+      return NID_auth_ecdsa;
+    case SSL_aPSK:
+      return NID_auth_psk;
+    case SSL_aGENERIC:
+      return NID_auth_any;
+  }
+  assert(0);
+  return NID_undef;
+}
+
+int SSL_CIPHER_is_AEAD(const SSL_CIPHER *cipher) {
+  return SSL_CIPHER_is_aead(cipher);
+}
+
 int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher) {
   return (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) != 0;
 }
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index 6611dd2..10128d8 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -833,7 +833,11 @@
   return SSL_do_handshake(ssl);
 }
 
-static int ssl_do_renegotiate(SSL *ssl) {
+static int ssl_do_post_handshake(SSL *ssl, const SSLMessage &msg) {
+  if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    return tls13_post_handshake(ssl, msg);
+  }
+
   /* We do not accept renegotiations as a server or SSL 3.0. SSL 3.0 will be
    * removed entirely in the future and requires retaining more data for
    * renegotiation_info. */
@@ -841,8 +845,7 @@
     goto no_renegotiation;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_HELLO_REQUEST ||
-      ssl->init_num != 0) {
+  if (msg.type != SSL3_MT_HELLO_REQUEST || CBS_len(&msg.body) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HELLO_REQUEST);
     return 0;
@@ -893,14 +896,6 @@
   return 0;
 }
 
-static int ssl_do_post_handshake(SSL *ssl) {
-  if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
-    return ssl_do_renegotiate(ssl);
-  }
-
-  return tls13_post_handshake(ssl);
-}
-
 static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) {
   ssl_reset_error_state(ssl);
 
@@ -938,11 +933,14 @@
       continue;
     }
 
-    /* Handle the post-handshake message and try again. */
-    if (!ssl_do_post_handshake(ssl)) {
-      return -1;
+    SSLMessage msg;
+    while (ssl->method->get_message(ssl, &msg)) {
+      /* Handle the post-handshake message and try again. */
+      if (!ssl_do_post_handshake(ssl, msg)) {
+        return -1;
+      }
+      ssl->method->next_message(ssl);
     }
-    ssl->method->release_current_message(ssl, 1 /* free buffer */);
   }
 }
 
@@ -1660,10 +1658,17 @@
   return 1;
 }
 
-OPENSSL_EXPORT STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx) {
+STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx) {
   return ctx->cipher_list->ciphers;
 }
 
+int SSL_CTX_cipher_in_group(const SSL_CTX *ctx, size_t i) {
+  if (i >= sk_SSL_CIPHER_num(ctx->cipher_list->ciphers)) {
+    return 0;
+  }
+  return ctx->cipher_list->in_group_flags[i];
+}
+
 STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *ssl) {
   if (ssl == NULL) {
     return NULL;
@@ -2466,8 +2471,6 @@
 
   BUF_MEM_free(ssl->init_buf);
   ssl->init_buf = NULL;
-  ssl->init_msg = NULL;
-  ssl->init_num = 0;
 
   /* The ssl->d1->mtu is simultaneously configuration (preserved across
    * clear) and connection-specific state (gets reset).
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index 1830723..a1c21dc 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -368,8 +368,8 @@
 }
 
 const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session) {
-  return ssl_get_handshake_digest(session->cipher->algorithm_prf,
-                                  SSL_SESSION_protocol_version(session));
+  return ssl_get_handshake_digest(SSL_SESSION_protocol_version(session),
+                                  session->cipher);
 }
 
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
diff --git a/src/ssl/ssl_stat.cc b/src/ssl/ssl_stat.cc
index 22149e2..56e4f2b 100644
--- a/src/ssl/ssl_stat.cc
+++ b/src/ssl/ssl_stat.cc
@@ -188,9 +188,6 @@
     case SSL3_ST_SR_KEY_EXCH_A:
       return "SSLv3 read client key exchange A";
 
-    case SSL3_ST_SR_KEY_EXCH_B:
-      return "SSLv3 read client key exchange B";
-
     case SSL3_ST_SR_CERT_VRFY_A:
       return "SSLv3 read certificate verify A";
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 4556fb7..898cd04 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -361,12 +361,13 @@
   ":X25519:P-256",
 };
 
-static std::string CipherListToString(ssl_cipher_preference_list_st *list) {
+static std::string CipherListToString(SSL_CTX *ctx) {
   bool in_group = false;
   std::string ret;
-  for (size_t i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) {
-    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(list->ciphers, i);
-    if (!in_group && list->in_group_flags[i]) {
+  const STACK_OF(SSL_CIPHER) *ciphers = SSL_CTX_get_ciphers(ctx);
+  for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
+    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
+    if (!in_group && SSL_CTX_cipher_in_group(ctx, i)) {
       ret += "\t[\n";
       in_group = true;
     }
@@ -376,7 +377,7 @@
     }
     ret += SSL_CIPHER_get_name(cipher);
     ret += "\n";
-    if (in_group && !list->in_group_flags[i]) {
+    if (in_group && !SSL_CTX_cipher_in_group(ctx, i)) {
       ret += "\t]\n";
       in_group = false;
     }
@@ -384,16 +385,17 @@
   return ret;
 }
 
-static bool CipherListsEqual(ssl_cipher_preference_list_st *list,
+static bool CipherListsEqual(SSL_CTX *ctx,
                              const std::vector<ExpectedCipher> &expected) {
-  if (sk_SSL_CIPHER_num(list->ciphers) != expected.size()) {
+  const STACK_OF(SSL_CIPHER) *ciphers = SSL_CTX_get_ciphers(ctx);
+  if (sk_SSL_CIPHER_num(ciphers) != expected.size()) {
     return false;
   }
 
   for (size_t i = 0; i < expected.size(); i++) {
-    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(list->ciphers, i);
+    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
     if (expected[i].id != SSL_CIPHER_get_id(cipher) ||
-        expected[i].in_group_flag != list->in_group_flags[i]) {
+        expected[i].in_group_flag != !!SSL_CTX_cipher_in_group(ctx, i)) {
       return false;
     }
   }
@@ -409,18 +411,18 @@
 
     // Test lax mode.
     ASSERT_TRUE(SSL_CTX_set_cipher_list(ctx.get(), t.rule));
-    EXPECT_TRUE(CipherListsEqual(ctx->cipher_list, t.expected))
+    EXPECT_TRUE(CipherListsEqual(ctx.get(), t.expected))
         << "Cipher rule evaluated to:\n"
-        << CipherListToString(ctx->cipher_list);
+        << CipherListToString(ctx.get());
 
     // Test strict mode.
     if (t.strict_fail) {
       EXPECT_FALSE(SSL_CTX_set_strict_cipher_list(ctx.get(), t.rule));
     } else {
       ASSERT_TRUE(SSL_CTX_set_strict_cipher_list(ctx.get(), t.rule));
-      EXPECT_TRUE(CipherListsEqual(ctx->cipher_list, t.expected))
+      EXPECT_TRUE(CipherListsEqual(ctx.get(), t.expected))
           << "Cipher rule evaluated to:\n"
-          << CipherListToString(ctx->cipher_list);
+          << CipherListToString(ctx.get());
     }
   }
 
@@ -439,9 +441,8 @@
     ASSERT_TRUE(ctx);
 
     ASSERT_TRUE(SSL_CTX_set_strict_cipher_list(ctx.get(), rule));
-    for (size_t i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) {
-      EXPECT_FALSE(SSL_CIPHER_is_NULL(
-          sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i)));
+    for (const SSL_CIPHER *cipher : SSL_CTX_get_ciphers(ctx.get())) {
+      EXPECT_FALSE(SSL_CIPHER_is_NULL(cipher));
     }
   }
 }
@@ -785,30 +786,119 @@
   ExpectDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &DTLSv1_2_method);
 }
 
-TEST(SSLTest, CipherGetStandardName) {
+TEST(SSLTest, CipherProperties) {
   static const struct {
     int id;
     const char *standard_name;
+    int cipher_nid;
+    int digest_nid;
+    int kx_nid;
+    int auth_nid;
   } kTests[] = {
-      {SSL3_CK_RSA_DES_192_CBC3_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
-      {TLS1_CK_RSA_WITH_AES_128_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"},
-      {TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
-       "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"},
-      {TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
-       "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
-      {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-       "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"},
-      {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-       "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"},
-      {TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-       "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"},
-      {TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-       "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"},
-      {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-       "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
-      {TLS1_CK_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384"},
-      {TLS1_CK_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256"},
-      {TLS1_CK_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256"},
+      {
+          SSL3_CK_RSA_DES_192_CBC3_SHA,
+          "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
+          NID_des_ede3_cbc,
+          NID_sha1,
+          NID_kx_rsa,
+          NID_auth_rsa,
+      },
+      {
+          TLS1_CK_RSA_WITH_AES_128_SHA,
+          "TLS_RSA_WITH_AES_128_CBC_SHA",
+          NID_aes_128_cbc,
+          NID_sha1,
+          NID_kx_rsa,
+          NID_auth_rsa,
+      },
+      {
+          TLS1_CK_PSK_WITH_AES_256_CBC_SHA,
+          "TLS_PSK_WITH_AES_256_CBC_SHA",
+          NID_aes_256_cbc,
+          NID_sha1,
+          NID_kx_psk,
+          NID_auth_psk,
+      },
+      {
+          TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
+          "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+          NID_aes_128_cbc,
+          NID_sha256,
+          NID_kx_ecdhe,
+          NID_auth_rsa,
+      },
+      {
+          TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
+          "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+          NID_aes_256_cbc,
+          NID_sha384,
+          NID_kx_ecdhe,
+          NID_auth_rsa,
+      },
+      {
+          TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+          "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+          NID_aes_128_gcm,
+          NID_undef,
+          NID_kx_ecdhe,
+          NID_auth_rsa,
+      },
+      {
+          TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+          "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+          NID_aes_128_gcm,
+          NID_undef,
+          NID_kx_ecdhe,
+          NID_auth_ecdsa,
+      },
+      {
+          TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+          "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+          NID_aes_256_gcm,
+          NID_undef,
+          NID_kx_ecdhe,
+          NID_auth_ecdsa,
+      },
+      {
+          TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+          "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA",
+          NID_aes_128_cbc,
+          NID_sha1,
+          NID_kx_ecdhe,
+          NID_auth_psk,
+      },
+      {
+          TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+          "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+          NID_chacha20_poly1305,
+          NID_undef,
+          NID_kx_ecdhe,
+          NID_auth_rsa,
+      },
+      {
+          TLS1_CK_AES_256_GCM_SHA384,
+          "TLS_AES_256_GCM_SHA384",
+          NID_aes_256_gcm,
+          NID_undef,
+          NID_kx_any,
+          NID_auth_any,
+      },
+      {
+          TLS1_CK_AES_128_GCM_SHA256,
+          "TLS_AES_128_GCM_SHA256",
+          NID_aes_128_gcm,
+          NID_undef,
+          NID_kx_any,
+          NID_auth_any,
+      },
+      {
+          TLS1_CK_CHACHA20_POLY1305_SHA256,
+          "TLS_CHACHA20_POLY1305_SHA256",
+          NID_chacha20_poly1305,
+          NID_undef,
+          NID_kx_any,
+          NID_auth_any,
+      },
   };
 
   for (const auto &t : kTests) {
@@ -821,6 +911,11 @@
     bssl::UniquePtr<char> rfc_name(SSL_CIPHER_get_rfc_name(cipher));
     ASSERT_TRUE(rfc_name);
     EXPECT_STREQ(t.standard_name, rfc_name.get());
+
+    EXPECT_EQ(t.cipher_nid, SSL_CIPHER_get_cipher_nid(cipher));
+    EXPECT_EQ(t.digest_nid, SSL_CIPHER_get_digest_nid(cipher));
+    EXPECT_EQ(t.kx_nid, SSL_CIPHER_get_kx_nid(cipher));
+    EXPECT_EQ(t.auth_nid, SSL_CIPHER_get_auth_nid(cipher));
   }
 }
 
diff --git a/src/ssl/ssl_transcript.cc b/src/ssl/ssl_transcript.cc
index 4a00d0f..2dfaf76 100644
--- a/src/ssl/ssl_transcript.cc
+++ b/src/ssl/ssl_transcript.cc
@@ -178,8 +178,8 @@
   return true;
 }
 
-bool SSLTranscript::InitHash(uint16_t version, int algorithm_prf) {
-  const EVP_MD *md = ssl_get_handshake_digest(algorithm_prf, version);
+bool SSLTranscript::InitHash(uint16_t version, const SSL_CIPHER *cipher) {
+  const EVP_MD *md = ssl_get_handshake_digest(version, cipher);
 
   /* To support SSL 3.0's Finished and CertificateVerify constructions,
    * EVP_md5_sha1() is split into MD5 and SHA-1 halves. When SSL 3.0 is removed,
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index 2349df0..d4a6ee9 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -483,25 +483,19 @@
 }
 
 int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) {
+  const SSL_SESSION *session = SSL_get_session(ssl);
   if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
-    return ssl3_prf(out, out_len, SSL_get_session(ssl)->master_key,
-                    SSL_get_session(ssl)->master_key_length,
-                    TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE,
-                    ssl->s3->server_random, SSL3_RANDOM_SIZE,
-                    ssl->s3->client_random, SSL3_RANDOM_SIZE);
+    return ssl3_prf(out, out_len, session->master_key,
+                    session->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
+                    TLS_MD_KEY_EXPANSION_CONST_SIZE, ssl->s3->server_random,
+                    SSL3_RANDOM_SIZE, ssl->s3->client_random, SSL3_RANDOM_SIZE);
   }
 
-  const EVP_MD *digest = ssl_get_handshake_digest(
-      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
-  if (digest == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-  return tls1_prf(digest, out, out_len, SSL_get_session(ssl)->master_key,
-                  SSL_get_session(ssl)->master_key_length,
-                  TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE,
-                  ssl->s3->server_random, SSL3_RANDOM_SIZE,
-                  ssl->s3->client_random, SSL3_RANDOM_SIZE);
+  const EVP_MD *digest = SSL_SESSION_get_digest(session);
+  return tls1_prf(digest, out, out_len, session->master_key,
+                  session->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
+                  TLS_MD_KEY_EXPANSION_CONST_SIZE, ssl->s3->server_random,
+                  SSL3_RANDOM_SIZE, ssl->s3->client_random, SSL3_RANDOM_SIZE);
 }
 
 int SSL_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
@@ -545,15 +539,11 @@
     OPENSSL_memcpy(seed + 2 * SSL3_RANDOM_SIZE + 2, context, context_len);
   }
 
-  const EVP_MD *digest = ssl_get_handshake_digest(
-      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
-  if (digest == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-  int ret = tls1_prf(digest, out, out_len, SSL_get_session(ssl)->master_key,
-                     SSL_get_session(ssl)->master_key_length, label, label_len,
-                     seed, seed_len, NULL, 0);
+  const SSL_SESSION *session = SSL_get_session(ssl);
+  const EVP_MD *digest = SSL_SESSION_get_digest(session);
+  int ret = tls1_prf(digest, out, out_len, session->master_key,
+                     session->master_key_length, label, label_len, seed,
+                     seed_len, NULL, 0);
   OPENSSL_free(seed);
   return ret;
 }
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 27a942a..39f4be6 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -204,12 +204,12 @@
   return ret;
 }
 
-int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out, const uint8_t *in,
-                          size_t in_len) {
+int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out,
+                          const SSLMessage &msg) {
   OPENSSL_memset(out, 0, sizeof(*out));
   out->ssl = ssl;
-  out->client_hello = in;
-  out->client_hello_len = in_len;
+  out->client_hello = CBS_data(&msg.body);
+  out->client_hello_len = CBS_len(&msg.body);
 
   CBS client_hello, random, session_id;
   CBS_init(&client_hello, out->client_hello, out->client_hello_len);
@@ -3276,14 +3276,12 @@
   return 0;
 }
 
-int tls1_verify_channel_id(SSL_HANDSHAKE *hs) {
+int tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg) {
   SSL *const ssl = hs->ssl;
-  uint16_t extension_type;
-  CBS extension, channel_id;
-
   /* A Channel ID handshake message is structured to contain multiple
    * extensions, but the only one that can be present is Channel ID. */
-  CBS_init(&channel_id, ssl->init_msg, ssl->init_num);
+  uint16_t extension_type;
+  CBS channel_id = msg.body, extension;
   if (!CBS_get_u16(&channel_id, &extension_type) ||
       !CBS_get_u16_length_prefixed(&channel_id, &extension) ||
       CBS_len(&channel_id) != 0 ||
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index b8c2785..b402f38 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -573,6 +573,10 @@
 	// end_of_early_data alert.
 	SkipEndOfEarlyData bool
 
+	// SkipCertificateVerify, if true causes peer to skip sending a
+	// CertificateVerify message after the Certificate message.
+	SkipCertificateVerify bool
+
 	// EarlyChangeCipherSpec causes the client to send an early
 	// ChangeCipherSpec message before the ClientKeyExchange. A value of
 	// zero disables this behavior. One and two configure variants for 0.9.8
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index 834be40..9fa49b5 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -2,8 +2,7 @@
   "DisabledTests": {
     "BadCBCPadding*": "Fuzzer mode has no CBC padding.",
 
-    "BadFinished-*": "Fuzzer mode ignores Finished checks.",
-    "FalseStart-BadFinished": "Fuzzer mode ignores Finished checks.",
+    "*BadFinished*": "Fuzzer mode ignores Finished checks.",
     "TrailingMessageData-*Finished*": "Fuzzer mode ignores Finished checks.",
 
     "DTLSIgnoreBadPackets*": "Fuzzer mode has no bad packets.",
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 33c1b12..7423726 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -974,8 +974,10 @@
 				certVerify.signatureAlgorithm = c.config.Bugs.SendSignatureAlgorithm
 			}
 
-			hs.writeClientHash(certVerify.marshal())
-			c.writeRecord(recordTypeHandshake, certVerify.marshal())
+			if !c.config.Bugs.SkipCertificateVerify {
+				hs.writeClientHash(certVerify.marshal())
+				c.writeRecord(recordTypeHandshake, certVerify.marshal())
+			}
 		}
 	}
 
@@ -1200,8 +1202,10 @@
 			return errors.New("tls: failed to sign handshake with client certificate: " + err.Error())
 		}
 
-		hs.writeClientHash(certVerify.marshal())
-		c.writeRecord(recordTypeHandshake, certVerify.marshal())
+		if !c.config.Bugs.SkipCertificateVerify {
+			hs.writeClientHash(certVerify.marshal())
+			c.writeRecord(recordTypeHandshake, certVerify.marshal())
+		}
 	}
 	// flushHandshake will be called in sendFinished.
 
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 614bb50..194244d 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -863,8 +863,10 @@
 			certVerify.signatureAlgorithm = config.Bugs.SendSignatureAlgorithm
 		}
 
-		hs.writeServerHash(certVerify.marshal())
-		c.writeRecord(recordTypeHandshake, certVerify.marshal())
+		if !config.Bugs.SkipCertificateVerify {
+			hs.writeServerHash(certVerify.marshal())
+			c.writeRecord(recordTypeHandshake, certVerify.marshal())
+		}
 	} else if hs.sessionState != nil {
 		// Pick up certificates from the session instead.
 		if len(hs.sessionState.certificates) > 0 {
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 9898101..7e64fe5 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -1512,6 +1512,24 @@
 		},
 		{
 			testType: serverTest,
+			name:     "ServerSkipCertificateVerify",
+			config: Config{
+				MaxVersion:   VersionTLS12,
+				Certificates: []Certificate{rsaChainCertificate},
+				Bugs: ProtocolBugs{
+					SkipCertificateVerify: true,
+				},
+			},
+			expectPeerCertificate: &rsaChainCertificate,
+			flags: []string{
+				"-require-any-client-certificate",
+			},
+			shouldFail:         true,
+			expectedError:      ":UNEXPECTED_RECORD:",
+			expectedLocalError: "remote error: unexpected message",
+		},
+		{
+			testType: serverTest,
 			name:     "Alert",
 			config: Config{
 				Bugs: ProtocolBugs{
@@ -4229,21 +4247,20 @@
 			testType: serverTest,
 			name:     "TLS13Experiment-EarlyData-Server",
 			config: Config{
-				MaxVersion:   VersionTLS13,
-				MinVersion:   VersionTLS13,
-				TLS13Variant: TLS13Experiment,
+				MaxVersion: VersionTLS13,
+				MinVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
 					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
 					ExpectEarlyDataAccepted: true,
 					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
 				},
 			},
+			tls13Variant:  TLS13Experiment,
 			messageCount:  2,
 			resumeSession: true,
 			flags: []string{
 				"-enable-early-data",
 				"-expect-accept-early-data",
-				"-tls13-variant", "1",
 			},
 		})
 
@@ -4251,21 +4268,20 @@
 			testType: serverTest,
 			name:     "TLS13RecordTypeExperiment-EarlyData-Server",
 			config: Config{
-				MaxVersion:   VersionTLS13,
-				MinVersion:   VersionTLS13,
-				TLS13Variant: TLS13RecordTypeExperiment,
+				MaxVersion: VersionTLS13,
+				MinVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
 					SendEarlyData:           [][]byte{{1, 2, 3, 4}},
 					ExpectEarlyDataAccepted: true,
 					ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
 				},
 			},
+			tls13Variant:  TLS13RecordTypeExperiment,
 			messageCount:  2,
 			resumeSession: true,
 			flags: []string{
 				"-enable-early-data",
 				"-expect-accept-early-data",
-				"-tls13-variant", "2",
 			},
 		})
 
@@ -10637,26 +10653,24 @@
 		testType: serverTest,
 		name:     "SkipEarlyData-TLS13Experiment",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			TLS13Variant: TLS13Experiment,
+			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
 				SendFakeEarlyDataLength: 4,
 			},
 		},
-		flags: []string{"-tls13-variant", "1"},
+		tls13Variant: TLS13Experiment,
 	})
 
 	testCases = append(testCases, testCase{
 		testType: serverTest,
 		name:     "SkipEarlyData-TLS13RecordTypeExperiment",
 		config: Config{
-			MaxVersion:   VersionTLS13,
-			TLS13Variant: TLS13RecordTypeExperiment,
+			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
 				SendFakeEarlyDataLength: 4,
 			},
 		},
-		flags: []string{"-tls13-variant", "2"},
+		tls13Variant: TLS13RecordTypeExperiment,
 	})
 
 	testCases = append(testCases, testCase{
@@ -11772,7 +11786,8 @@
 			Bugs: ProtocolBugs{
 				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
 				SendStrayEarlyHandshake: true,
-				ExpectEarlyDataAccepted: true},
+				ExpectEarlyDataAccepted: true,
+			},
 		},
 		resumeSession:      true,
 		shouldFail:         true,
@@ -11800,6 +11815,100 @@
 			"-expect-version", strconv.Itoa(VersionTLS13),
 		},
 	})
+
+	// Test that client and server both notice handshake errors after data
+	// has started flowing.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-EarlyData-Client-BadFinished",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+		},
+		resumeConfig: &Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			Bugs: ProtocolBugs{
+				BadFinished: true,
+			},
+		},
+		resumeSession: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-early-data-info",
+			"-expect-accept-early-data",
+		},
+		shouldFail:         true,
+		expectedError:      ":DIGEST_CHECK_FAILED:",
+		expectedLocalError: "remote error: error decrypting message",
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "TLS13-EarlyData-Server-BadFinished",
+		config: Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+		},
+		resumeConfig: &Config{
+			MaxVersion:       VersionTLS13,
+			MaxEarlyDataSize: 16384,
+			Bugs: ProtocolBugs{
+				SendEarlyData:           [][]byte{{1, 2, 3, 4}},
+				ExpectEarlyDataAccepted: true,
+				ExpectHalfRTTData:       [][]byte{{254, 253, 252, 251}},
+				BadFinished:             true,
+			},
+		},
+		resumeSession: true,
+		flags: []string{
+			"-enable-early-data",
+			"-expect-accept-early-data",
+		},
+		shouldFail:         true,
+		expectedError:      ":DIGEST_CHECK_FAILED:",
+		expectedLocalError: "remote error: error decrypting message",
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "TLS13-ServerSkipCertificateVerify",
+		config: Config{
+			MinVersion:   VersionTLS13,
+			MaxVersion:   VersionTLS13,
+			Certificates: []Certificate{rsaChainCertificate},
+			Bugs: ProtocolBugs{
+				SkipCertificateVerify: true,
+			},
+		},
+		expectPeerCertificate: &rsaChainCertificate,
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+			"-require-any-client-certificate",
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-ClientSkipCertificateVerify",
+		config: Config{
+			MinVersion:   VersionTLS13,
+			MaxVersion:   VersionTLS13,
+			Certificates: []Certificate{rsaChainCertificate},
+			Bugs: ProtocolBugs{
+				SkipCertificateVerify: true,
+			},
+		},
+		expectPeerCertificate: &rsaChainCertificate,
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaChainCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaChainKeyFile),
+		},
+		shouldFail:         true,
+		expectedError:      ":UNEXPECTED_MESSAGE:",
+		expectedLocalError: "remote error: unexpected message",
+	})
 }
 
 func addTLS13CipherPreferenceTests() {
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index 1c2e7f7..39e0cb3 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -46,21 +46,16 @@
         OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
         return -1;
 
-      case ssl_hs_flush:
-      case ssl_hs_flush_and_read_message: {
+      case ssl_hs_flush: {
         int ret = ssl->method->flush_flight(ssl);
         if (ret <= 0) {
           return ret;
         }
-        if (hs->wait != ssl_hs_flush_and_read_message) {
-          break;
-        }
-        hs->wait = ssl_hs_read_message;
-        SSL_FALLTHROUGH;
+        break;
       }
 
       case ssl_hs_read_message: {
-        int ret = ssl->method->ssl_get_message(ssl);
+        int ret = ssl->method->read_message(ssl);
         if (ret <= 0) {
           return ret;
         }
@@ -190,14 +185,14 @@
   return 1;
 }
 
-int tls13_process_certificate(SSL_HANDSHAKE *hs, int allow_anonymous) {
+int tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                              int allow_anonymous) {
   SSL *const ssl = hs->ssl;
-  CBS cbs, context, certificate_list;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u8_length_prefixed(&cbs, &context) ||
+  CBS body = msg.body, context, certificate_list;
+  if (!CBS_get_u8_length_prefixed(&body, &context) ||
       CBS_len(&context) != 0 ||
-      !CBS_get_u24_length_prefixed(&cbs, &certificate_list) ||
-      CBS_len(&cbs) != 0) {
+      !CBS_get_u24_length_prefixed(&body, &certificate_list) ||
+      CBS_len(&body) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return 0;
@@ -356,19 +351,18 @@
   return 1;
 }
 
-int tls13_process_certificate_verify(SSL_HANDSHAKE *hs) {
+int tls13_process_certificate_verify(SSL_HANDSHAKE *hs, const SSLMessage &msg) {
   SSL *const ssl = hs->ssl;
   if (hs->peer_pubkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
-  CBS cbs, signature;
+  CBS body = msg.body, signature;
   uint16_t signature_algorithm;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u16(&cbs, &signature_algorithm) ||
-      !CBS_get_u16_length_prefixed(&cbs, &signature) ||
-      CBS_len(&cbs) != 0) {
+  if (!CBS_get_u16(&body, &signature_algorithm) ||
+      !CBS_get_u16_length_prefixed(&body, &signature) ||
+      CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
@@ -381,19 +375,19 @@
   }
   hs->new_session->peer_signature_algorithm = signature_algorithm;
 
-  uint8_t *msg = NULL;
-  size_t msg_len;
+  uint8_t *input = NULL;
+  size_t input_len;
   if (!tls13_get_cert_verify_signature_input(
-          hs, &msg, &msg_len,
+          hs, &input, &input_len,
           ssl->server ? ssl_cert_verify_client : ssl_cert_verify_server)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return 0;
   }
-  UniquePtr<uint8_t> free_msg(msg);
+  UniquePtr<uint8_t> free_input(input);
 
   int sig_ok = ssl_public_key_verify(ssl, CBS_data(&signature),
                                      CBS_len(&signature), signature_algorithm,
-                                     hs->peer_pubkey.get(), msg, msg_len);
+                                     hs->peer_pubkey.get(), input, input_len);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   sig_ok = 1;
   ERR_clear_error();
@@ -407,7 +401,8 @@
   return 1;
 }
 
-int tls13_process_finished(SSL_HANDSHAKE *hs, int use_saved_value) {
+int tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                           int use_saved_value) {
   SSL *const ssl = hs->ssl;
   uint8_t verify_data_buf[EVP_MAX_MD_SIZE];
   const uint8_t *verify_data;
@@ -424,9 +419,7 @@
     verify_data = verify_data_buf;
   }
 
-  int finished_ok =
-      ssl->init_num == verify_data_len &&
-      CRYPTO_memcmp(verify_data, ssl->init_msg, verify_data_len) == 0;
+  int finished_ok = CBS_mem_equal(&msg.body, verify_data, verify_data_len);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   finished_ok = 1;
 #endif
@@ -584,12 +577,11 @@
   return 1;
 }
 
-static int tls13_receive_key_update(SSL *ssl) {
-  CBS cbs;
+static int tls13_receive_key_update(SSL *ssl, const SSLMessage &msg) {
+  CBS body = msg.body;
   uint8_t key_update_request;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u8(&cbs, &key_update_request) ||
-      CBS_len(&cbs) != 0 ||
+  if (!CBS_get_u8(&body, &key_update_request) ||
+      CBS_len(&body) != 0 ||
       (key_update_request != SSL_KEY_UPDATE_NOT_REQUESTED &&
        key_update_request != SSL_KEY_UPDATE_REQUESTED)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
@@ -605,9 +597,10 @@
   if (key_update_request == SSL_KEY_UPDATE_REQUESTED &&
       !ssl->s3->key_update_pending) {
     ScopedCBB cbb;
-    CBB body;
-    if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_KEY_UPDATE) ||
-        !CBB_add_u8(&body, SSL_KEY_UPDATE_NOT_REQUESTED) ||
+    CBB body_cbb;
+    if (!ssl->method->init_message(ssl, cbb.get(), &body_cbb,
+                                   SSL3_MT_KEY_UPDATE) ||
+        !CBB_add_u8(&body_cbb, SSL_KEY_UPDATE_NOT_REQUESTED) ||
         !ssl_add_message_cbb(ssl, cbb.get()) ||
         !tls13_rotate_traffic_key(ssl, evp_aead_seal)) {
       return 0;
@@ -623,8 +616,8 @@
   return 1;
 }
 
-int tls13_post_handshake(SSL *ssl) {
-  if (ssl->s3->tmp.message_type == SSL3_MT_KEY_UPDATE) {
+int tls13_post_handshake(SSL *ssl, const SSLMessage &msg) {
+  if (msg.type == SSL3_MT_KEY_UPDATE) {
     ssl->s3->key_update_count++;
     if (ssl->s3->key_update_count > kMaxKeyUpdates) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_KEY_UPDATES);
@@ -632,14 +625,13 @@
       return 0;
     }
 
-    return tls13_receive_key_update(ssl);
+    return tls13_receive_key_update(ssl, msg);
   }
 
   ssl->s3->key_update_count = 0;
 
-  if (ssl->s3->tmp.message_type == SSL3_MT_NEW_SESSION_TICKET &&
-      !ssl->server) {
-    return tls13_process_new_session_ticket(ssl);
+  if (msg.type == SSL3_MT_NEW_SESSION_TICKET && !ssl->server) {
+    return tls13_process_new_session_ticket(ssl, msg);
   }
 
   ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 2940265..83066be 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -33,16 +33,15 @@
 namespace bssl {
 
 enum client_hs_state_t {
-  state_process_hello_retry_request = 0,
+  state_read_hello_retry_request = 0,
   state_send_second_client_hello,
-  state_process_server_hello,
+  state_read_server_hello,
   state_process_change_cipher_spec,
-  state_process_encrypted_extensions,
-  state_continue_second_server_flight,
-  state_process_certificate_request,
-  state_process_server_certificate,
-  state_process_server_certificate_verify,
-  state_process_server_finished,
+  state_read_encrypted_extensions,
+  state_read_certificate_request,
+  state_read_server_certificate,
+  state_read_server_certificate_verify,
+  state_read_server_finished,
   state_send_end_of_early_data,
   state_send_client_certificate,
   state_send_client_certificate_verify,
@@ -52,21 +51,24 @@
 
 static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
 
-static enum ssl_hs_wait_t do_process_hello_retry_request(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) {
-    hs->tls13_state = state_process_server_hello;
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (msg.type != SSL3_MT_HELLO_RETRY_REQUEST) {
+    hs->tls13_state = state_read_server_hello;
     return ssl_hs_ok;
   }
 
-  CBS cbs, extensions;
+  CBS body = msg.body, extensions;
   uint16_t server_version;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u16(&cbs, &server_version) ||
-      !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
+  if (!CBS_get_u16(&body, &server_version) ||
+      !CBS_get_u16_length_prefixed(&body, &extensions) ||
       /* HelloRetryRequest may not be empty. */
       CBS_len(&extensions) == 0 ||
-      CBS_len(&cbs) != 0) {
+      CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -140,10 +142,11 @@
     hs->retry_group = group_id;
   }
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
+  ssl->method->next_message(ssl);
   hs->received_hello_retry_request = 1;
   hs->tls13_state = state_send_second_client_hello;
   /* 0-RTT is rejected if we receive a HelloRetryRequest. */
@@ -163,30 +166,33 @@
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_server_hello;
-  return ssl_hs_flush_and_read_message;
+  hs->tls13_state = state_read_server_hello;
+  return ssl_hs_flush;
 }
 
-static enum ssl_hs_wait_t do_process_server_hello(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
     return ssl_hs_error;
   }
 
-  CBS cbs, server_random, session_id, extensions;
+  CBS body = msg.body, server_random, session_id, extensions;
   uint16_t server_version;
   uint16_t cipher_suite;
   uint8_t compression_method;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u16(&cbs, &server_version) ||
-      !CBS_get_bytes(&cbs, &server_random, SSL3_RANDOM_SIZE) ||
+  if (!CBS_get_u16(&body, &server_version) ||
+      !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
       (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
-       !CBS_get_u8_length_prefixed(&cbs, &session_id)) ||
-      !CBS_get_u16(&cbs, &cipher_suite) ||
+       !CBS_get_u8_length_prefixed(&body, &session_id)) ||
+      !CBS_get_u16(&body, &cipher_suite) ||
       (ssl->version == TLS1_3_EXPERIMENT_VERSION &&
-       (!CBS_get_u8(&cbs, &compression_method) || compression_method != 0)) ||
-      !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
-      CBS_len(&cbs) != 0) {
+       (!CBS_get_u8(&body, &compression_method) || compression_method != 0)) ||
+      !CBS_get_u16_length_prefixed(&body, &extensions) ||
+      CBS_len(&body) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
@@ -337,10 +343,12 @@
   }
   OPENSSL_free(dhe_secret);
 
-  if (!ssl_hash_current_message(hs) ||
+  if (!ssl_hash_message(hs, msg) ||
       !tls13_derive_handshake_secrets(hs)) {
     return ssl_hs_error;
   }
+
+  ssl->method->next_message(ssl);
   hs->tls13_state = state_process_change_cipher_spec;
   return ssl->version == TLS1_3_EXPERIMENT_VERSION
              ? ssl_hs_read_change_cipher_spec
@@ -365,23 +373,26 @@
     }
   }
 
-  hs->tls13_state = state_process_encrypted_extensions;
-  return ssl_hs_read_message;
+  hs->tls13_state = state_read_encrypted_extensions;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_encrypted_extensions(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_encrypted_extensions(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_check_message_type(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS)) {
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_ENCRYPTED_EXTENSIONS)) {
     return ssl_hs_error;
   }
 
-  CBS cbs;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!ssl_parse_serverhello_tlsext(hs, &cbs)) {
+  CBS body = msg.body;
+  if (!ssl_parse_serverhello_tlsext(hs, &body)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     return ssl_hs_error;
   }
-  if (CBS_len(&cbs) != 0) {
+  if (CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -412,42 +423,42 @@
     }
   }
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_continue_second_server_flight;
+  ssl->method->next_message(ssl);
+  hs->tls13_state = state_read_certificate_request;
   if (hs->in_early_data && !ssl->early_data_accepted) {
     return ssl_hs_early_data_rejected;
   }
   return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_continue_second_server_flight(SSL_HANDSHAKE *hs) {
-  hs->tls13_state = state_process_certificate_request;
-  return ssl_hs_read_message;
-}
-
-static enum ssl_hs_wait_t do_process_certificate_request(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_certificate_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   /* CertificateRequest may only be sent in non-resumption handshakes. */
   if (ssl->s3->session_reused) {
-    hs->tls13_state = state_process_server_finished;
+    hs->tls13_state = state_read_server_finished;
     return ssl_hs_ok;
   }
 
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+
   /* CertificateRequest is optional. */
-  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) {
-    hs->tls13_state = state_process_server_certificate;
+  if (msg.type != SSL3_MT_CERTIFICATE_REQUEST) {
+    hs->tls13_state = state_read_server_certificate;
     return ssl_hs_ok;
   }
 
-  CBS cbs, context, supported_signature_algorithms;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u8_length_prefixed(&cbs, &context) ||
+  CBS body = msg.body, context, supported_signature_algorithms;
+  if (!CBS_get_u8_length_prefixed(&body, &context) ||
       /* The request context is always empty during the handshake. */
       CBS_len(&context) != 0 ||
-      !CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
+      !CBS_get_u16_length_prefixed(&body, &supported_signature_algorithms) ||
       CBS_len(&supported_signature_algorithms) == 0 ||
       !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -457,7 +468,7 @@
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> ca_names =
-      ssl_parse_client_CA_list(ssl, &alert, &cbs);
+      ssl_parse_client_CA_list(ssl, &alert, &body);
   if (!ca_names) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
@@ -465,8 +476,8 @@
 
   /* Ignore extensions. */
   CBS extensions;
-  if (!CBS_get_u16_length_prefixed(&cbs, &extensions) ||
-      CBS_len(&cbs) != 0) {
+  if (!CBS_get_u16_length_prefixed(&body, &extensions) ||
+      CBS_len(&body) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return ssl_hs_error;
@@ -476,60 +487,76 @@
   hs->ca_names = std::move(ca_names);
   ssl->ctx->x509_method->hs_flush_cached_ca_names(hs);
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_server_certificate;
-  return ssl_hs_read_message;
+  ssl->method->next_message(ssl);
+  hs->tls13_state = state_read_server_certificate;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_server_certificate(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
-      !tls13_process_certificate(hs, 0 /* certificate required */) ||
-      !ssl_hash_current_message(hs)) {
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE) ||
+      !tls13_process_certificate(hs, msg, 0 /* certificate required */) ||
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_server_certificate_verify;
-  return ssl_hs_read_message;
+  ssl->method->next_message(ssl);
+  hs->tls13_state = state_read_server_certificate_verify;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_server_certificate_verify(
+static enum ssl_hs_wait_t do_read_server_certificate_verify(
     SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
   switch (ssl_verify_peer_cert(hs)) {
     case ssl_verify_ok:
       break;
     case ssl_verify_invalid:
       return ssl_hs_error;
     case ssl_verify_retry:
-      hs->tls13_state = state_process_server_certificate_verify;
+      hs->tls13_state = state_read_server_certificate_verify;
       return ssl_hs_certificate_verify;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
-      !tls13_process_certificate_verify(hs) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE_VERIFY) ||
+      !tls13_process_certificate_verify(hs, msg) ||
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_server_finished;
-  return ssl_hs_read_message;
+  ssl->method->next_message(ssl);
+  hs->tls13_state = state_read_server_finished;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_server_finished(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_server_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
-      !tls13_process_finished(hs, 0 /* don't use saved value */) ||
-      !ssl_hash_current_message(hs) ||
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_FINISHED) ||
+      !tls13_process_finished(hs, msg, 0 /* don't use saved value */) ||
+      !ssl_hash_message(hs, msg) ||
       /* Update the secret to the master secret and derive traffic keys. */
       !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
       !tls13_derive_application_secrets(hs)) {
     return ssl_hs_error;
   }
 
+  ssl->method->next_message(ssl);
   hs->tls13_state = state_send_end_of_early_data;
   return ssl_hs_ok;
 }
@@ -662,35 +689,32 @@
     enum client_hs_state_t state =
         static_cast<enum client_hs_state_t>(hs->tls13_state);
     switch (state) {
-      case state_process_hello_retry_request:
-        ret = do_process_hello_retry_request(hs);
+      case state_read_hello_retry_request:
+        ret = do_read_hello_retry_request(hs);
         break;
       case state_send_second_client_hello:
         ret = do_send_second_client_hello(hs);
         break;
-      case state_process_server_hello:
-        ret = do_process_server_hello(hs);
+      case state_read_server_hello:
+        ret = do_read_server_hello(hs);
         break;
       case state_process_change_cipher_spec:
         ret = do_process_change_cipher_spec(hs);
         break;
-      case state_process_encrypted_extensions:
-        ret = do_process_encrypted_extensions(hs);
+      case state_read_encrypted_extensions:
+        ret = do_read_encrypted_extensions(hs);
         break;
-      case state_continue_second_server_flight:
-        ret = do_continue_second_server_flight(hs);
+      case state_read_certificate_request:
+        ret = do_read_certificate_request(hs);
         break;
-      case state_process_certificate_request:
-        ret = do_process_certificate_request(hs);
+      case state_read_server_certificate:
+        ret = do_read_server_certificate(hs);
         break;
-      case state_process_server_certificate:
-        ret = do_process_server_certificate(hs);
+      case state_read_server_certificate_verify:
+        ret = do_read_server_certificate_verify(hs);
         break;
-      case state_process_server_certificate_verify:
-        ret = do_process_server_certificate_verify(hs);
-        break;
-      case state_process_server_finished:
-        ret = do_process_server_finished(hs);
+      case state_read_server_finished:
+        ret = do_read_server_finished(hs);
         break;
       case state_send_end_of_early_data:
         ret = do_send_end_of_early_data(hs);
@@ -717,7 +741,7 @@
   return ssl_hs_ok;
 }
 
-int tls13_process_new_session_ticket(SSL *ssl) {
+int tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg) {
   UniquePtr<SSL_SESSION> session(SSL_SESSION_dup(ssl->s3->established_session,
                                                  SSL_SESSION_INCLUDE_NONAUTH));
   if (!session) {
@@ -727,14 +751,13 @@
   ssl_session_rebase_time(ssl, session.get());
 
   uint32_t server_timeout;
-  CBS cbs, ticket, extensions;
-  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u32(&cbs, &server_timeout) ||
-      !CBS_get_u32(&cbs, &session->ticket_age_add) ||
-      !CBS_get_u16_length_prefixed(&cbs, &ticket) ||
+  CBS body = msg.body, ticket, extensions;
+  if (!CBS_get_u32(&body, &server_timeout) ||
+      !CBS_get_u32(&body, &session->ticket_age_add) ||
+      !CBS_get_u16_length_prefixed(&body, &ticket) ||
       !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
-      !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
-      CBS_len(&cbs) != 0) {
+      !CBS_get_u16_length_prefixed(&body, &extensions) ||
+      CBS_len(&body) != 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return 0;
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index 1ae4849..39e80be 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -33,8 +33,8 @@
 namespace bssl {
 
 static int init_key_schedule(SSL_HANDSHAKE *hs, uint16_t version,
-                              int algorithm_prf) {
-  if (!hs->transcript.InitHash(version, algorithm_prf)) {
+                             const SSL_CIPHER *cipher) {
+  if (!hs->transcript.InitHash(version, cipher)) {
     return 0;
   }
 
@@ -47,8 +47,7 @@
 }
 
 int tls13_init_key_schedule(SSL_HANDSHAKE *hs) {
-  if (!init_key_schedule(hs, ssl3_protocol_version(hs->ssl),
-                         hs->new_cipher->algorithm_prf)) {
+  if (!init_key_schedule(hs, ssl3_protocol_version(hs->ssl), hs->new_cipher)) {
     return 0;
   }
 
@@ -59,7 +58,7 @@
 int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   return init_key_schedule(hs, SSL_SESSION_protocol_version(ssl->session),
-                           ssl->session->cipher->algorithm_prf);
+                           ssl->session->cipher);
 }
 
 int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
@@ -243,9 +242,6 @@
     "application traffic secret";
 
 int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
-  const EVP_MD *digest = ssl_get_handshake_digest(
-      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
-
   uint8_t *secret;
   size_t secret_len;
   if (direction == evp_aead_open) {
@@ -256,6 +252,7 @@
     secret_len = ssl->s3->write_traffic_secret_len;
   }
 
+  const EVP_MD *digest = SSL_SESSION_get_digest(SSL_get_session(ssl));
   if (!hkdf_expand_label(secret, digest, secret, secret_len,
                          (const uint8_t *)kTLS13LabelApplicationTraffic,
                          strlen(kTLS13LabelApplicationTraffic), NULL, 0,
@@ -323,15 +320,14 @@
                                  const char *label, size_t label_len,
                                  const uint8_t *context, size_t context_len,
                                  int use_context) {
-  const EVP_MD *digest = ssl_get_handshake_digest(
-      SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl));
-
   const uint8_t *hash = NULL;
   size_t hash_len = 0;
   if (use_context) {
     hash = context;
     hash_len = context_len;
   }
+
+  const EVP_MD *digest = SSL_SESSION_get_digest(SSL_get_session(ssl));
   return hkdf_expand_label(out, digest, ssl->s3->exporter_secret,
                            ssl->s3->exporter_secret_len, (const uint8_t *)label,
                            label_len, hash, hash_len, out_len);
@@ -402,23 +398,21 @@
 }
 
 int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
-                            CBS *binders) {
+                            const SSLMessage &msg, CBS *binders) {
   size_t hash_len = hs->transcript.DigestLen();
 
-  /* Get the full ClientHello, including message header. It must be large enough
-   * to exclude the binders. */
-  CBS message;
-  hs->ssl->method->get_current_message(hs->ssl, &message);
-  if (CBS_len(&message) < CBS_len(binders) + 2) {
+  /* The message must be large enough to exclude the binders. */
+  if (CBS_len(&msg.raw) < CBS_len(binders) + 2) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
-  /* Hash a ClientHello prefix up to the binders. For now, this assumes we only
-   * ever verify PSK binders on initial ClientHellos. */
+  /* Hash a ClientHello prefix up to the binders. This includes the header. For
+   * now, this assumes we only ever verify PSK binders on initial
+   * ClientHellos. */
   uint8_t context[EVP_MAX_MD_SIZE];
   unsigned context_len;
-  if (!EVP_Digest(CBS_data(&message), CBS_len(&message) - CBS_len(binders) - 2,
+  if (!EVP_Digest(CBS_data(&msg.raw), CBS_len(&msg.raw) - CBS_len(binders) - 2,
                   context, &context_len, hs->transcript.Digest(), NULL)) {
     return 0;
   }
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index 03f8bdd..2b802c4 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -42,17 +42,17 @@
   state_select_parameters = 0,
   state_select_session,
   state_send_hello_retry_request,
-  state_process_second_client_hello,
+  state_read_second_client_hello,
   state_send_server_hello,
   state_send_server_certificate_verify,
   state_send_server_finished,
   state_read_second_client_flight,
   state_process_change_cipher_spec,
   state_process_end_of_early_data,
-  state_process_client_certificate,
-  state_process_client_certificate_verify,
-  state_process_channel_id,
-  state_process_client_finished,
+  state_read_client_certificate,
+  state_read_client_certificate_verify,
+  state_read_channel_id,
+  state_read_client_finished,
   state_send_new_session_ticket,
   state_done,
 };
@@ -213,11 +213,12 @@
   /* At this point, most ClientHello extensions have already been processed by
    * the common handshake logic. Resolve the remaining non-PSK parameters. */
   SSL *const ssl = hs->ssl;
-
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num) ||
-      client_hello.session_id_len > sizeof(hs->session_id)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -246,7 +247,7 @@
   /* The PRF hash is now known. Set up the key schedule and hash the
    * ClientHello. */
   if (!tls13_init_key_schedule(hs) ||
-      !ssl_hash_current_message(hs)) {
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
@@ -256,7 +257,8 @@
 
 static enum ssl_ticket_aead_result_t select_session(
     SSL_HANDSHAKE *hs, uint8_t *out_alert, UniquePtr<SSL_SESSION> *out_session,
-    int32_t *out_ticket_age_skew, const SSL_CLIENT_HELLO *client_hello) {
+    int32_t *out_ticket_age_skew, const SSLMessage &msg,
+    const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
   *out_session = NULL;
 
@@ -331,7 +333,7 @@
       (int32_t)client_ticket_age - (int32_t)server_ticket_age;
 
   /* Check the PSK binder. */
-  if (!tls13_verify_psk_binder(hs, session.get(), &binders)) {
+  if (!tls13_verify_psk_binder(hs, session.get(), msg, &binders)) {
     *out_alert = SSL_AD_DECRYPT_ERROR;
     return ssl_ticket_aead_error;
   }
@@ -342,9 +344,12 @@
 
 static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -352,7 +357,7 @@
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
   UniquePtr<SSL_SESSION> session;
-  switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew,
+  switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew, msg,
                          &client_hello)) {
     case ssl_ticket_aead_ignore_ticket:
       assert(!session);
@@ -458,12 +463,14 @@
     if (need_retry) {
       ssl->early_data_accepted = 0;
       ssl->s3->skip_early_data = 1;
+      ssl->method->next_message(ssl);
       hs->tls13_state = state_send_hello_retry_request;
       return ssl_hs_ok;
     }
     return ssl_hs_error;
   }
 
+  ssl->method->next_message(ssl);
   hs->tls13_state = state_send_server_hello;
   return ssl_hs_ok;
 }
@@ -485,19 +492,21 @@
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_second_client_hello;
-  return ssl_hs_flush_and_read_message;
+  hs->tls13_state = state_read_second_client_hello;
+  return ssl_hs_flush;
 }
 
-static enum ssl_hs_wait_t do_process_second_client_hello(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_second_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CLIENT_HELLO)) {
     return ssl_hs_error;
   }
-
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -513,10 +522,11 @@
     return ssl_hs_error;
   }
 
-  if (!ssl_hash_current_message(hs)) {
+  if (!ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
+  ssl->method->next_message(ssl);
   hs->tls13_state = state_send_server_hello;
   return ssl_hs_ok;
 }
@@ -669,7 +679,8 @@
                          static_cast<uint8_t>(hs->hash_len)};
     if (!hs->transcript.Update(header, sizeof(header)) ||
         !hs->transcript.Update(hs->expected_client_finished, hs->hash_len) ||
-        !tls13_derive_resumption_secret(hs) || !add_new_session_tickets(hs)) {
+        !tls13_derive_resumption_secret(hs) ||
+        !add_new_session_tickets(hs)) {
       return ssl_hs_error;
     }
   }
@@ -713,12 +724,12 @@
                              hs->hash_len)) {
     return ssl_hs_error;
   }
-  hs->tls13_state = ssl->early_data_accepted ? state_process_client_finished
-                                             : state_process_client_certificate;
-  return ssl_hs_read_message;
+  hs->tls13_state = ssl->early_data_accepted ? state_read_client_finished
+                                             : state_read_client_certificate;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_client_certificate(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_client_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (!hs->cert_request) {
     /* OpenSSL returns X509_V_OK when no certificates are requested. This is
@@ -726,74 +737,94 @@
     hs->new_session->verify_result = X509_V_OK;
 
     /* Skip this state. */
-    hs->tls13_state = state_process_channel_id;
+    hs->tls13_state = state_read_channel_id;
     return ssl_hs_ok;
   }
 
   const int allow_anonymous =
       (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
-
-  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
-      !tls13_process_certificate(hs, allow_anonymous) ||
-      !ssl_hash_current_message(hs)) {
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE) ||
+      !tls13_process_certificate(hs, msg, allow_anonymous) ||
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_client_certificate_verify;
-  return ssl_hs_read_message;
+  ssl->method->next_message(ssl);
+  hs->tls13_state = state_read_client_certificate_verify;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_client_certificate_verify(
+static enum ssl_hs_wait_t do_read_client_certificate_verify(
     SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) {
     /* Skip this state. */
-    hs->tls13_state = state_process_channel_id;
+    hs->tls13_state = state_read_channel_id;
     return ssl_hs_ok;
   }
 
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+
   switch (ssl_verify_peer_cert(hs)) {
     case ssl_verify_ok:
       break;
     case ssl_verify_invalid:
       return ssl_hs_error;
     case ssl_verify_retry:
-      hs->tls13_state = state_process_client_certificate_verify;
+      hs->tls13_state = state_read_client_certificate_verify;
       return ssl_hs_certificate_verify;
   }
 
-  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
-      !tls13_process_certificate_verify(hs) ||
-      !ssl_hash_current_message(hs)) {
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CERTIFICATE_VERIFY) ||
+      !tls13_process_certificate_verify(hs, msg) ||
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_channel_id;
-  return ssl_hs_read_message;
+  ssl->method->next_message(ssl);
+  hs->tls13_state = state_read_channel_id;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_channel_id(SSL_HANDSHAKE *hs) {
-  if (!hs->ssl->s3->tlsext_channel_id_valid) {
-    hs->tls13_state = state_process_client_finished;
+static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  if (!ssl->s3->tlsext_channel_id_valid) {
+    hs->tls13_state = state_read_client_finished;
     return ssl_hs_ok;
   }
 
-  if (!ssl_check_message_type(hs->ssl, SSL3_MT_CHANNEL_ID) ||
-      !tls1_verify_channel_id(hs) ||
-      !ssl_hash_current_message(hs)) {
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_CHANNEL_ID) ||
+      !tls1_verify_channel_id(hs, msg) ||
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_process_client_finished;
-  return ssl_hs_read_message;
+  ssl->method->next_message(ssl);
+  hs->tls13_state = state_read_client_finished;
+  return ssl_hs_ok;
 }
 
-static enum ssl_hs_wait_t do_process_client_finished(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_read_client_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
+  SSLMessage msg;
+  if (!ssl->method->get_message(ssl, &msg)) {
+    return ssl_hs_read_message;
+  }
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_FINISHED) ||
       /* If early data was accepted, we've already computed the client Finished
        * and derived the resumption secret. */
-      !tls13_process_finished(hs, ssl->early_data_accepted) ||
+      !tls13_process_finished(hs, msg, ssl->early_data_accepted) ||
       /* evp_aead_seal keys have already been switched. */
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
                              hs->hash_len)) {
@@ -801,17 +832,19 @@
   }
 
   if (!ssl->early_data_accepted) {
-    if (!ssl_hash_current_message(hs) ||
+    if (!ssl_hash_message(hs, msg) ||
         !tls13_derive_resumption_secret(hs)) {
       return ssl_hs_error;
     }
 
     /* We send post-handshake tickets as part of the handshake in 1-RTT. */
     hs->tls13_state = state_send_new_session_ticket;
-    return ssl_hs_ok;
+  } else {
+    /* We already sent half-RTT tickets. */
+    hs->tls13_state = state_done;
   }
 
-  hs->tls13_state = state_done;
+  ssl->method->next_message(ssl);
   return ssl_hs_ok;
 }
 
@@ -846,8 +879,8 @@
       case state_send_hello_retry_request:
         ret = do_send_hello_retry_request(hs);
         break;
-      case state_process_second_client_hello:
-        ret = do_process_second_client_hello(hs);
+      case state_read_second_client_hello:
+        ret = do_read_second_client_hello(hs);
         break;
       case state_send_server_hello:
         ret = do_send_server_hello(hs);
@@ -867,17 +900,17 @@
       case state_process_change_cipher_spec:
         ret = do_process_change_cipher_spec(hs);
         break;
-      case state_process_client_certificate:
-        ret = do_process_client_certificate(hs);
+      case state_read_client_certificate:
+        ret = do_read_client_certificate(hs);
         break;
-      case state_process_client_certificate_verify:
-        ret = do_process_client_certificate_verify(hs);
+      case state_read_client_certificate_verify:
+        ret = do_read_client_certificate_verify(hs);
         break;
-      case state_process_channel_id:
-        ret = do_process_channel_id(hs);
+      case state_read_channel_id:
+        ret = do_read_channel_id(hs);
         break;
-      case state_process_client_finished:
-        ret = do_process_client_finished(hs);
+      case state_read_client_finished:
+        ret = do_read_client_finished(hs);
         break;
       case state_send_new_session_ticket:
         ret = do_send_new_session_ticket(hs);
diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc
index 02f5c07..2fe4be3 100644
--- a/src/ssl/tls_method.cc
+++ b/src/ssl/tls_method.cc
@@ -69,7 +69,21 @@
 
 static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; }
 
-static void ssl3_on_handshake_complete(SSL *ssl) {}
+static void ssl3_on_handshake_complete(SSL *ssl) {
+  /* The handshake should have released its final message. */
+  assert(!ssl->s3->has_message);
+
+  /* During the handshake, |init_buf| is retained. Release if it there is no
+   * excess in it.
+   *
+   * TODO(davidben): The second check is always true but will not be once we
+   * switch to copying the entire handshake record. Replace this comment with an
+   * explanation when that happens and a TODO to reject it. */
+  if (ssl->init_buf != NULL && ssl->init_buf->length == 0) {
+    BUF_MEM_free(ssl->init_buf);
+    ssl->init_buf = NULL;
+  }
+}
 
 static int ssl3_set_read_state(SSL *ssl, UniquePtr<SSLAEADContext> aead_ctx) {
   if (ssl->s3->rrec.length != 0) {
@@ -99,8 +113,8 @@
     ssl3_new,
     ssl3_free,
     ssl3_get_message,
-    ssl3_get_current_message,
-    ssl3_release_current_message,
+    ssl3_read_message,
+    ssl3_next_message,
     ssl3_read_app_data,
     ssl3_read_change_cipher_spec,
     ssl3_read_close_notify,
@@ -151,7 +165,7 @@
 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) {}
 
-static const SSL_X509_METHOD ssl_noop_x509_method = {
+const SSL_X509_METHOD ssl_noop_x509_method = {
   ssl_noop_x509_check_client_CA_names,
   ssl_noop_x509_clear,
   ssl_noop_x509_free,
diff --git a/src/tool/client.cc b/src/tool/client.cc
index f7a8259..d3a3115 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -297,6 +297,26 @@
   return cb(ssl.get(), sock);
 }
 
+static bool GetTLS13Variant(tls13_variant_t *out, const std::string &in) {
+  if (in == "draft") {
+    *out = tls13_default;
+    return true;
+  }
+  if (in == "experiment") {
+    *out = tls13_experiment;
+    return true;
+  }
+  if (in == "record-type") {
+    *out = tls13_record_type_experiment;
+    return true;
+  }
+  if (in == "no-session-id") {
+    *out = tls13_no_session_id_experiment;
+    return true;
+  }
+  return false;
+}
+
 bool Client(const std::vector<std::string> &args) {
   if (!InitSocketLibrary()) {
     return false;
@@ -464,9 +484,13 @@
   }
 
   if (args_map.count("-tls13-variant") != 0) {
-    SSL_CTX_set_tls13_variant(ctx.get(),
-                              static_cast<enum tls13_variant_t>(
-                                  atoi(args_map["-tls13-variant"].c_str())));
+    tls13_variant_t variant;
+    if (!GetTLS13Variant(&variant, args_map["-tls13-variant"])) {
+      fprintf(stderr, "Unknown TLS 1.3 variant: %s\n",
+              args_map["-tls13-variant"].c_str());
+      return false;
+    }
+    SSL_CTX_set_tls13_variant(ctx.get(), variant);
   }
 
   if (args_map.count("-ed25519") != 0) {