external/boringssl: sync to 7b8b9c17

This includes the following changes from BoringSSL :

7b8b9c1 Include 'asm' in the name of X25519 asm sources.
3202750 Update the fuzz tests for the server.
6544426 Fix a ** 0 mod 1 = 0 for real this time.
fe5f7c7 Only reserve EVP_MAX_MD_SIZE for the Finished, not twice of it.
0d56f88 Switch s to ssl everywhere.
974c7ba Route DHE through the SSL_ECDH abstraction as well.
4cc36ad Make it possible to tell what curve was used on the server.
4298d77 Implement draft-ietf-tls-curve25519-01 in C.
c18ef75 Allocate a NID for X25519.
3a2a480 Remove long-dead comment.
cba2b62 Implement draft-ietf-tls-curve25519-01 in Go.
ab14563 Bundle a copy of golang.org/x/crypto/curve25519 for testing.
a029ebc Switch the bundled poly1305 to relative imports.
64d9250 Completely remove P-224 from the TLS stack.
8c2b3bf Test all supported curves (including those off by default).
fc82512 Convert ssl3_send_cert_verify to CBB.
5fb18c6 Make MSVC happy.
2a0b391 Rewrite ssl3_send_server_key_exchange to use CBB.
d16bf34 Add a -lldb flag to runner.go.
af21bcf Remove other unnecessary BN_CTX allocations.
ae0eaaa Convert ssl3_send_client_key_exchange to CBB.
3ac4b3a Remove NO_ASM define that I accidently included in the previous commit.
e6c5402 Don't build X25519 asm code when NO_ASM is set.
77a173e Add x86-64 assembly for X25519.
c75c0ae Add #defines for ED25519 key and signature lengths.
48cce66 Tidy up ssl3_get_server_key_exchange slightly.
c1cc858 Check for EC_KEY_set_public_key error.
4cc671c Add CBB_reserve and CBB_did_write.
e13263d Resolve a few old TODOs.
841934f Remove stack macros for nonexistent types.
70ab223 Remove ASN1_R_MALLOC_FAILURE.
b965c63 Reject calls to X509_verify_cert that have not been reinitialised
3f5b43d Simplify RSA key exchange padding check.
3ef6085 Refuse to parse RSA pubkeys with invalid exponents.
afe57cb Add a tool to generate Ed25519 keys.
77c3c0b Enable Ed25519 when building with OPENSSL_SMALL.
9f897b2 Remove the stitched RC4-MD5 code and use the generic one.
1741a9d Save some mallocs in computing the MAC for e_tls.c.
df57163 Add RC4-SHA1 and DES-EDE3-CBC-SHA1 to bssl speed.
13414b3 Implement draft-ietf-tls-chacha20-poly1305-04.
3748990 Implement draft-ietf-tls-chacha20-poly1305-04 in Go.
2089fdd Implement RFC 7539 in Go.
86e412d Add client cert support to bssl client.
23a681b Fix build.
e320392 Rename the Go ChaCha20-Poly1305 implementation.
8ffab72 Point EVP_aead_chacha20_poly1305 at the standardized version.
fef6fb5 Fix ChaCha20-Poly1305 tests.
60a08ac Remove unreachable code to duplicate DH keys.
4ec0cce Slightly tweak some array allocations.
2936170 Fix memory leak in DSA redo case.
a01deee Make CBB_len relative to its argument.
77385bb Mark platform-specific HOST_[c2l|l2c] as (void).
6969971 Remove a dead prototype.
1b36716 Remove crypto/header_removed.h.
017231a Remove asm __asm__ define.
793c21e Make HOST_l2c return void.
0aff3ff Store the partial block as uint8_t, not uint32_t.
5a19d7d Use the straight-forward ROTATE macro.
78fefbf Reformat md32_common.h, part 2.
fea1137 Reformat md32_common.h, part 1.
871fff0 *_Update of length zero is legal.
d9f0671 Remove |need_record_splitting| from |SSL3_STATE|.
cd48038 Remove unused fields from SSL3_STATE.
7fc0100 Slightly simplify SSL3_RECORD.
ece5ba2 Reset ssl error codes.
a41280d Pull ChangeCipherSpec into the handshake state machine.
8fd5c23 Simplify fragmented HelloRequest state.
ef5dfd2 Add tests for malformed HelloRequests.
8411b24 Add tests for bad ChangeCipherSpecs.
502a843 Switch unrolled loop in BN_usub with memcpy.
c3ae38b Remove DH EVP_PKEY hooks.
7100ee9 Chromium's update.sh is dead, long live update.py
f28dd64 Fix flaky BadRSAClientKeyExchange-1 test.
4234885 Remove unused functions.
45dab25 Skip free callbacks on empty CRYPTO_EX_DATAs.
8a58933 Remove the CRYPTO_EX_new callback.
0abd6f2 Get struct timeval from sys/time.h.
1246670 Use UINT64_C in sha512.c table.
5ddffbb Make SSL_(CTX_)?set_tmp_ecdh call SSL_(CTX_)?set1_curves.
53e5c2c Remove SSL_(CTX_)?set_ecdh_callback.
756ad17 Initialize |one_index| in OAEP padding check.
1634a33 Convert rsa/padding.c to constant-time helpers.
b36a395 Add slightly better RSA key exchange tests.
0bd71eb Remove weird ret negation logic.
e9cddb8 Remove SSL_OP_LEGACY_SERVER_CONNECT.
3e052de Tighten SSL_OP_LEGACY_SERVER_CONNECT to align with RFC 5746.
03f0005 Remove SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER.
ef5e515 Remove SSL_OP_TLS_D5_BUG.
c100ef4 Limit depth of ASN1 parse printing.
2205093 Add a comment in SetTestState from bssl_shim.
6ae67df Don't leak Android hacks to other build platforms.
a0ef7b0 Enforce that |EC_KEY| private key is in [0, group->order).
533a273 Add |EC_METHOD| method for verifying public key order.
a3d9de0 Add |EC_GROUP_get0_order| to replace |EC_GROUP_get_order|.
8847856 Include <sys/time.h> in packeted_bio.h for 'timeval'
dca63cf Don't abort in |init_once| if |fcntl| returns ENOSYS
afd565f Add defines for SRTP profiles using GCM ciphers from RFC 7714.
902870e Gate SHA_CTX compatibility on !WINDOWS.
34aa55c Support the SHA_CTX hack without ANDROID.
6d9e5a7 Re-apply 75b833cc819a9d189adb0fdd56327bee600ff9e9
28243c0 Add PSS parameter check.
e701f16 bn/asm/x86_64-mont5.pl: fix carry propagating bug (CVE-2015-3193).
cb85298 Fix leak with ASN.1 combine.
c4f25ce Work around yaSSL bug.
c5eb467 Remove dead code in p256-x86_64.
758d127 Add get0 getters for EVP_PKEY.
fde89b4 avoid clashes with libc's 'open' in e_chacha20poly1305.c
60a45aa Remove reference to removed |RSA_FLAG_NO_CONSTTIME| flag.
81edc9b Do away with BN_LLONG in favor of BN_ULLONG.
e8fe07f Fix AES XTS mode key size.
93a5b44 Make CRYPTO_library_init use a CRYPTO_once_t.
bf76218 Remove the |ri| field of |BN_MONT_CTX|.
596ab10 s/BN_BITS/BN_BITS2/ in |BN_mod_inverse_ex|; remove |BN_BITS| & |BN_MASK|.
7af36e1 Share common definitions of |TOBN| and |BIGNUM_STATIC|.
ff2df33 Reformat the cipher suite table.
9f2e277 Remove strength_bits.
d6e9eec Remove algo_strength.
dcb6ef0 Remove algorithm_ssl.
d28f59c Switch the keylog BIO to a callback.
fba735c Register the *25519 tests as dependencies of all_tests.
f3376ac Remove |EC_POINTs_mul| & simplify p256-x86_64.
301efc8 Fix error handling in |p256-x86_64|.
e2136d9 Remove |EC_GROUP_precompute_mult| and |EC_KEY_precompute_mult|.
9b26297 Make |EC_GROUP_precompute_mult|/|EC_KEY_precompute_mult| no-ops.
5058d79 Remove p224-64 and p256-64 dead code for non-default generators.
b1b6229 Add NEON implementation of curve25519.
9e65d48 Allow |CRYPTO_is_NEON_capable| to be known at compile time, if possible.
3ac32b1 Fix curve25519 code for MSVC.
4fb0dc4 Add X25519 and Ed25519 support.
c324f17 Make sure pthread_once() succeeds.
9361243 Don't include <alloca.h>, it's no longer needed.
b00061c Add SSL_CIPHER_is_AES[128|256]CBC.
3a59611 size_t SSL*_use_*_ASN1.
b324159 Fix ssl3_send_server_key_exchange error path.
f584a5a Reset epoch state in one place.
2077cf9 Use UINT64_C instead of OPENSSL_U64.
af07365 Check for overflow when parsing a CBS with d2i_*.
780cd92 modes/asm/ghash-armv4.pl: extend Apple fix to all clang cases.
f9c77de Drop CBB allocation failure test.
a33915d Have |CBB_init| zero the |CBB| before any possible failures.
c5c85de Make RAND_seed read a byte of random data.
d9e2702 Don't encode or decode ∞.
e7806fd Remove point-on-curve check from |ec_GFp_simple_oct2point|.
20c3731 Become partially -Wmissing-variable-declarations-clean.
7308aaa Remove `EC_GFp_simple_method` (dead code).
f872951 Fix null pointer dereference when using "simple" EC.
8bde5d2 Remove the unused |Ni| member of |BN_MONT_CTX|.
ce7ae6f Enable AVX code for SHA-*.
9f1f04f Remove nistz256 dead code for non-default generators.
d7421eb Remove condition which always evaluates to true (size_t >= 0).
d386394 Test for underflow before subtraction.
ef14b2d Remove stl_compat.h.
cd24a39 Limit DHE groups to 4096-bit.
99fdfb9 Move curve check out of tls12_check_peer_sigalg.

Change-Id: Id2d7110569d250b1bae8f8ce7d4421a92f581a31
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index a5ad126..c82cb9b 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -26,6 +26,7 @@
   ssl_buffer.c
   ssl_cert.c
   ssl_cipher.c
+  ssl_ecdh.c
   ssl_file.c
   ssl_lib.c
   ssl_rsa.c
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index a940af6..ee4cbc9 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -301,8 +301,9 @@
   }
 
   static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
-  int ret = dtls1_write_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
-                              sizeof(kChangeCipherSpec), use_epoch);
+  int ret =
+      dtls1_write_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                        sizeof(kChangeCipherSpec), use_epoch);
   if (ret <= 0) {
     return ret;
   }
@@ -410,17 +411,17 @@
 
 /* dtls1_is_next_message_complete returns one if the next handshake message is
  * complete and zero otherwise. */
-static int dtls1_is_next_message_complete(SSL *s) {
-  pitem *item = pqueue_peek(s->d1->buffered_messages);
+static int dtls1_is_next_message_complete(SSL *ssl) {
+  pitem *item = pqueue_peek(ssl->d1->buffered_messages);
   if (item == NULL) {
     return 0;
   }
 
   hm_fragment *frag = (hm_fragment *)item->data;
-  assert(s->d1->handshake_read_seq <= frag->msg_header.seq);
+  assert(ssl->d1->handshake_read_seq <= frag->msg_header.seq);
 
-  return s->d1->handshake_read_seq == frag->msg_header.seq &&
-      frag->reassembly == NULL;
+  return ssl->d1->handshake_read_seq == frag->msg_header.seq &&
+         frag->reassembly == NULL;
 }
 
 /* dtls1_discard_fragment_body discards a handshake fragment body of length
@@ -428,11 +429,11 @@
  *
  * TODO(davidben): This function will go away when ssl_read_bytes is gone from
  * the DTLS side. */
-static int dtls1_discard_fragment_body(SSL *s, size_t frag_len) {
+static int dtls1_discard_fragment_body(SSL *ssl, size_t frag_len) {
   uint8_t discard[256];
   while (frag_len > 0) {
     size_t chunk = frag_len < sizeof(discard) ? frag_len : sizeof(discard);
-    int ret = dtls1_read_bytes(s, SSL3_RT_HANDSHAKE, discard, chunk, 0);
+    int ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, discard, chunk, 0);
     if (ret != (int) chunk) {
       return 0;
     }
@@ -446,12 +447,12 @@
  * queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It
  * returns NULL on failure. The caller does not take ownership of the result. */
 static hm_fragment *dtls1_get_buffered_message(
-    SSL *s, const struct hm_header_st *msg_hdr) {
+    SSL *ssl, const struct hm_header_st *msg_hdr) {
   uint8_t seq64be[8];
   memset(seq64be, 0, sizeof(seq64be));
   seq64be[6] = (uint8_t)(msg_hdr->seq >> 8);
   seq64be[7] = (uint8_t)msg_hdr->seq;
-  pitem *item = pqueue_find(s->d1->buffered_messages, seq64be);
+  pitem *item = pqueue_find(ssl->d1->buffered_messages, seq64be);
 
   hm_fragment *frag;
   if (item == NULL) {
@@ -467,7 +468,7 @@
       dtls1_hm_fragment_free(frag);
       return NULL;
     }
-    item = pqueue_insert(s->d1->buffered_messages, item);
+    item = pqueue_insert(ssl->d1->buffered_messages, item);
     /* |pqueue_insert| fails iff a duplicate item is inserted, but |item| cannot
      * be a duplicate. */
     assert(item != NULL);
@@ -479,7 +480,7 @@
       /* The new fragment must be compatible with the previous fragments from
        * this message. */
       OPENSSL_PUT_ERROR(SSL, SSL_R_FRAGMENT_MISMATCH);
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return NULL;
     }
   }
@@ -487,29 +488,29 @@
 }
 
 /* dtls1_max_handshake_message_len returns the maximum number of bytes
- * permitted in a DTLS handshake message for |s|. The minimum is 16KB, but may
+ * permitted in a DTLS handshake message for |ssl|. The minimum is 16KB, but may
  * be greater if the maximum certificate list size requires it. */
-static size_t dtls1_max_handshake_message_len(const SSL *s) {
+static size_t dtls1_max_handshake_message_len(const SSL *ssl) {
   size_t max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
-  if (max_len < s->max_cert_list) {
-    return s->max_cert_list;
+  if (max_len < ssl->max_cert_list) {
+    return ssl->max_cert_list;
   }
   return max_len;
 }
 
 /* dtls1_process_fragment reads a handshake fragment and processes it. It
  * returns one if a fragment was successfully processed and 0 or -1 on error. */
-static int dtls1_process_fragment(SSL *s) {
+static int dtls1_process_fragment(SSL *ssl) {
   /* Read handshake message header. */
   uint8_t header[DTLS1_HM_HEADER_LENGTH];
-  int ret = dtls1_read_bytes(s, SSL3_RT_HANDSHAKE, header,
+  int ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, header,
                              DTLS1_HM_HEADER_LENGTH, 0);
   if (ret <= 0) {
     return ret;
   }
   if (ret != DTLS1_HM_HEADER_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     return -1;
   }
 
@@ -518,30 +519,30 @@
   dtls1_get_message_header(header, &msg_hdr);
 
   /* TODO(davidben): dtls1_read_bytes is the wrong abstraction for DTLS. There
-   * should be no need to reach into |s->s3->rrec.length|. */
+   * should be no need to reach into |ssl->s3->rrec.length|. */
   const size_t frag_off = msg_hdr.frag_off;
   const size_t frag_len = msg_hdr.frag_len;
   const size_t msg_len = msg_hdr.msg_len;
   if (frag_off > msg_len || frag_off + frag_len < frag_off ||
       frag_off + frag_len > msg_len ||
-      msg_len > dtls1_max_handshake_message_len(s) ||
-      frag_len > s->s3->rrec.length) {
+      msg_len > dtls1_max_handshake_message_len(ssl) ||
+      frag_len > ssl->s3->rrec.length) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return -1;
   }
 
-  if (msg_hdr.seq < s->d1->handshake_read_seq ||
-      msg_hdr.seq > (unsigned)s->d1->handshake_read_seq +
+  if (msg_hdr.seq < ssl->d1->handshake_read_seq ||
+      msg_hdr.seq > (unsigned)ssl->d1->handshake_read_seq +
                     kHandshakeBufferSize) {
     /* Ignore fragments from the past, or ones too far in the future. */
-    if (!dtls1_discard_fragment_body(s, frag_len)) {
+    if (!dtls1_discard_fragment_body(ssl, frag_len)) {
       return -1;
     }
     return 1;
   }
 
-  hm_fragment *frag = dtls1_get_buffered_message(s, &msg_hdr);
+  hm_fragment *frag = dtls1_get_buffered_message(ssl, &msg_hdr);
   if (frag == NULL) {
     return -1;
   }
@@ -549,7 +550,7 @@
 
   if (frag->reassembly == NULL) {
     /* The message is already assembled. */
-    if (!dtls1_discard_fragment_body(s, frag_len)) {
+    if (!dtls1_discard_fragment_body(ssl, frag_len)) {
       return -1;
     }
     return 1;
@@ -557,11 +558,11 @@
   assert(msg_len > 0);
 
   /* Read the body of the fragment. */
-  ret = dtls1_read_bytes(s, SSL3_RT_HANDSHAKE, frag->fragment + frag_off,
+  ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, frag->fragment + frag_off,
                          frag_len, 0);
   if (ret != (int) frag_len) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return -1;
   }
   dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
@@ -572,7 +573,7 @@
 /* dtls1_get_message reads a handshake message of message type |msg_type| (any
  * if |msg_type| == -1), maximum acceptable body length |max|. Read an entire
  * handshake message. Handshake messages arrive in fragments. */
-long dtls1_get_message(SSL *s, int st1, int stn, int msg_type, long max,
+long dtls1_get_message(SSL *ssl, int st1, int stn, int msg_type, long max,
                        enum ssl_hash_message_t hash_message, int *ok) {
   pitem *item = NULL;
   hm_fragment *frag = NULL;
@@ -580,26 +581,26 @@
 
   /* s3->tmp is used to store messages that are unexpected, caused
    * by the absence of an optional handshake message */
-  if (s->s3->tmp.reuse_message) {
+  if (ssl->s3->tmp.reuse_message) {
     /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
      * ssl_dont_hash_message would have to have been applied to the previous
      * call. */
     assert(hash_message == ssl_hash_message);
-    s->s3->tmp.reuse_message = 0;
-    if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) {
+    ssl->s3->tmp.reuse_message = 0;
+    if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
       al = SSL_AD_UNEXPECTED_MESSAGE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
       goto f_err;
     }
     *ok = 1;
-    s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
-    s->init_num = (int)s->s3->tmp.message_size;
-    return s->init_num;
+    ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+    ssl->init_num = (int)ssl->s3->tmp.message_size;
+    return ssl->init_num;
   }
 
   /* Process fragments until one is found. */
-  while (!dtls1_is_next_message_complete(s)) {
-    int ret = dtls1_process_fragment(s);
+  while (!dtls1_is_next_message_complete(ssl)) {
+    int ret = dtls1_process_fragment(ssl);
     if (ret <= 0) {
       *ok = 0;
       return ret;
@@ -607,10 +608,10 @@
   }
 
   /* Read out the next complete handshake message. */
-  item = pqueue_pop(s->d1->buffered_messages);
+  item = pqueue_pop(ssl->d1->buffered_messages);
   assert(item != NULL);
   frag = (hm_fragment *)item->data;
-  assert(s->d1->handshake_read_seq == frag->msg_header.seq);
+  assert(ssl->d1->handshake_read_seq == frag->msg_header.seq);
   assert(frag->reassembly == NULL);
 
   if (frag->msg_header.msg_len > (size_t)max) {
@@ -622,10 +623,10 @@
   size_t len;
   CBB cbb;
   CBB_zero(&cbb);
-  if (!BUF_MEM_grow(s->init_buf,
-                    (size_t)frag->msg_header.msg_len +
-                    DTLS1_HM_HEADER_LENGTH) ||
-      !CBB_init_fixed(&cbb, (uint8_t *)s->init_buf->data, s->init_buf->max) ||
+  if (!BUF_MEM_grow(ssl->init_buf, (size_t)frag->msg_header.msg_len +
+                                       DTLS1_HM_HEADER_LENGTH) ||
+      !CBB_init_fixed(&cbb, (uint8_t *)ssl->init_buf->data,
+                      ssl->init_buf->max) ||
       !CBB_add_u8(&cbb, frag->msg_header.type) ||
       !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
       !CBB_add_u16(&cbb, frag->msg_header.seq) ||
@@ -639,38 +640,38 @@
   }
   assert(len == (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
 
-  s->d1->handshake_read_seq++;
+  ssl->d1->handshake_read_seq++;
 
   /* TODO(davidben): This function has a lot of implicit outputs. Simplify the
    * |ssl_get_message| API. */
-  s->s3->tmp.message_type = frag->msg_header.type;
-  s->s3->tmp.message_size = frag->msg_header.msg_len;
-  s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
-  s->init_num = frag->msg_header.msg_len;
+  ssl->s3->tmp.message_type = frag->msg_header.type;
+  ssl->s3->tmp.message_size = frag->msg_header.msg_len;
+  ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+  ssl->init_num = frag->msg_header.msg_len;
 
-  if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) {
+  if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
     al = SSL_AD_UNEXPECTED_MESSAGE;
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     goto f_err;
   }
-  if (hash_message == ssl_hash_message && !ssl3_hash_current_message(s)) {
+  if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
     goto err;
   }
-  if (s->msg_callback) {
-    s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data,
-                    s->init_num + DTLS1_HM_HEADER_LENGTH, s,
-                    s->msg_callback_arg);
+  if (ssl->msg_callback) {
+    ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
+                    ssl->init_num + DTLS1_HM_HEADER_LENGTH, ssl,
+                    ssl->msg_callback_arg);
   }
 
   pitem_free(item);
   dtls1_hm_fragment_free(frag);
 
-  s->state = stn;
+  ssl->state = stn;
   *ok = 1;
-  return s->init_num;
+  return ssl->init_num;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   pitem_free(item);
   dtls1_hm_fragment_free(frag);
@@ -678,25 +679,25 @@
   return -1;
 }
 
-int dtls1_read_failed(SSL *s, int code) {
+int dtls1_read_failed(SSL *ssl, int code) {
   if (code > 0) {
     assert(0);
     return 1;
   }
 
-  if (!dtls1_is_timer_expired(s)) {
+  if (!dtls1_is_timer_expired(ssl)) {
     /* not a timeout, none of our business, let higher layers handle this. In
      * fact, it's probably an error */
     return code;
   }
 
-  if (!SSL_in_init(s)) {
+  if (!SSL_in_init(ssl)) {
     /* done, no need to send a retransmit */
-    BIO_set_flags(SSL_get_rbio(s), BIO_FLAGS_READ);
+    BIO_set_flags(SSL_get_rbio(ssl), BIO_FLAGS_READ);
     return code;
   }
 
-  return DTLSv1_handle_timeout(s);
+  return DTLSv1_handle_timeout(ssl);
 }
 
 static uint16_t dtls1_get_queue_priority(uint16_t seq, int is_ccs) {
@@ -713,47 +714,47 @@
   return seq * 2 - is_ccs;
 }
 
-static int dtls1_retransmit_message(SSL *s, hm_fragment *frag) {
+static int dtls1_retransmit_message(SSL *ssl, hm_fragment *frag) {
   /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
    * (negotiated cipher) exist. */
-  assert(s->d1->w_epoch == 0 || s->d1->w_epoch == 1);
-  assert(frag->msg_header.epoch <= s->d1->w_epoch);
+  assert(ssl->d1->w_epoch == 0 || ssl->d1->w_epoch == 1);
+  assert(frag->msg_header.epoch <= ssl->d1->w_epoch);
   enum dtls1_use_epoch_t use_epoch = dtls1_use_current_epoch;
-  if (s->d1->w_epoch == 1 && frag->msg_header.epoch == 0) {
+  if (ssl->d1->w_epoch == 1 && frag->msg_header.epoch == 0) {
     use_epoch = dtls1_use_previous_epoch;
   }
 
   /* TODO(davidben): This cannot handle non-blocking writes. */
   int ret;
   if (frag->msg_header.is_ccs) {
-    ret = dtls1_write_change_cipher_spec(s, use_epoch);
+    ret = dtls1_write_change_cipher_spec(ssl, use_epoch);
   } else {
     /* Restore the message body.
      * TODO(davidben): Make this less stateful. */
-    memcpy(s->init_buf->data, frag->fragment,
+    memcpy(ssl->init_buf->data, frag->fragment,
            frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
-    s->init_num = frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH;
+    ssl->init_num = frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH;
 
-    dtls1_set_message_header(s, frag->msg_header.type,
+    dtls1_set_message_header(ssl, frag->msg_header.type,
                              frag->msg_header.msg_len, frag->msg_header.seq,
                              0, frag->msg_header.frag_len);
-    ret = dtls1_do_handshake_write(s, use_epoch);
+    ret = dtls1_do_handshake_write(ssl, use_epoch);
   }
 
   /* TODO(davidben): Check return value? */
-  (void)BIO_flush(SSL_get_wbio(s));
+  (void)BIO_flush(SSL_get_wbio(ssl));
   return ret;
 }
 
 
-int dtls1_retransmit_buffered_messages(SSL *s) {
-  pqueue sent = s->d1->sent_messages;
+int dtls1_retransmit_buffered_messages(SSL *ssl) {
+  pqueue sent = ssl->d1->sent_messages;
   piterator iter = pqueue_iterator(sent);
   pitem *item;
 
   for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) {
     hm_fragment *frag = (hm_fragment *)item->data;
-    if (dtls1_retransmit_message(s, frag) <= 0) {
+    if (dtls1_retransmit_message(ssl, frag) <= 0) {
       return -1;
     }
   }
@@ -789,28 +790,28 @@
   return 1;
 }
 
-int dtls1_buffer_message(SSL *s) {
+int dtls1_buffer_message(SSL *ssl) {
   /* this function is called immediately after a message has
    * been serialized */
-  assert(s->init_off == 0);
+  assert(ssl->init_off == 0);
 
-  hm_fragment *frag = dtls1_hm_fragment_new(s->init_num, 0);
+  hm_fragment *frag = dtls1_hm_fragment_new(ssl->init_num, 0);
   if (!frag) {
     return 0;
   }
 
-  memcpy(frag->fragment, s->init_buf->data, s->init_num);
+  memcpy(frag->fragment, ssl->init_buf->data, ssl->init_num);
 
-  assert(s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH ==
-         (unsigned int)s->init_num);
+  assert(ssl->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH ==
+         (unsigned int)ssl->init_num);
 
-  frag->msg_header.msg_len = s->d1->w_msg_hdr.msg_len;
-  frag->msg_header.seq = s->d1->w_msg_hdr.seq;
-  frag->msg_header.type = s->d1->w_msg_hdr.type;
+  frag->msg_header.msg_len = ssl->d1->w_msg_hdr.msg_len;
+  frag->msg_header.seq = ssl->d1->w_msg_hdr.seq;
+  frag->msg_header.type = ssl->d1->w_msg_hdr.type;
   frag->msg_header.frag_off = 0;
-  frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len;
+  frag->msg_header.frag_len = ssl->d1->w_msg_hdr.msg_len;
   frag->msg_header.is_ccs = 0;
-  frag->msg_header.epoch = s->d1->w_epoch;
+  frag->msg_header.epoch = ssl->d1->w_epoch;
 
   uint16_t priority = dtls1_get_queue_priority(frag->msg_header.seq,
                                                0 /* handshake */);
@@ -825,37 +826,37 @@
     return 0;
   }
 
-  pqueue_insert(s->d1->sent_messages, item);
+  pqueue_insert(ssl->d1->sent_messages, item);
   return 1;
 }
 
-int dtls1_send_change_cipher_spec(SSL *s, int a, int b) {
-  if (s->state == a) {
+int dtls1_send_change_cipher_spec(SSL *ssl, int a, int b) {
+  if (ssl->state == a) {
     /* Buffer the message to handle retransmits. */
-    s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
-    dtls1_buffer_change_cipher_spec(s, s->d1->handshake_write_seq);
-    s->state = b;
+    ssl->d1->handshake_write_seq = ssl->d1->next_handshake_write_seq;
+    dtls1_buffer_change_cipher_spec(ssl, ssl->d1->handshake_write_seq);
+    ssl->state = b;
   }
 
-  return dtls1_write_change_cipher_spec(s, dtls1_use_current_epoch);
+  return dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch);
 }
 
 /* call this function when the buffered messages are no longer needed */
-void dtls1_clear_record_buffer(SSL *s) {
+void dtls1_clear_record_buffer(SSL *ssl) {
   pitem *item;
 
-  for (item = pqueue_pop(s->d1->sent_messages); item != NULL;
-       item = pqueue_pop(s->d1->sent_messages)) {
+  for (item = pqueue_pop(ssl->d1->sent_messages); item != NULL;
+       item = pqueue_pop(ssl->d1->sent_messages)) {
     dtls1_hm_fragment_free((hm_fragment *)item->data);
     pitem_free(item);
   }
 }
 
 /* don't actually do the writing, wait till the MTU has been retrieved */
-void dtls1_set_message_header(SSL *s, uint8_t mt, unsigned long len,
+void dtls1_set_message_header(SSL *ssl, uint8_t mt, unsigned long len,
                               unsigned short seq_num, unsigned long frag_off,
                               unsigned long frag_len) {
-  struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr;
+  struct hm_header_st *msg_hdr = &ssl->d1->w_msg_hdr;
 
   msg_hdr->type = mt;
   msg_hdr->msg_len = len;
diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c
index b86655c..ad5eb50 100644
--- a/src/ssl/d1_clnt.c
+++ b/src/ssl/d1_clnt.c
@@ -131,354 +131,362 @@
 #include "internal.h"
 
 
-static int dtls1_get_hello_verify(SSL *s);
+static int dtls1_get_hello_verify(SSL *ssl);
 
-int dtls1_connect(SSL *s) {
+int dtls1_connect(SSL *ssl) {
   BUF_MEM *buf = NULL;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
   int ret = -1;
   int new_state, state, skip = 0;
 
-  assert(s->handshake_func == dtls1_connect);
-  assert(!s->server);
-  assert(SSL_IS_DTLS(s));
+  assert(ssl->handshake_func == dtls1_connect);
+  assert(!ssl->server);
+  assert(SSL_IS_DTLS(ssl));
 
   ERR_clear_error();
   ERR_clear_system_error();
 
-  if (s->info_callback != NULL) {
-    cb = s->info_callback;
-  } else if (s->ctx->info_callback != NULL) {
-    cb = s->ctx->info_callback;
+  if (ssl->info_callback != NULL) {
+    cb = ssl->info_callback;
+  } else if (ssl->ctx->info_callback != NULL) {
+    cb = ssl->ctx->info_callback;
   }
 
-  s->in_handshake++;
+  ssl->in_handshake++;
 
   for (;;) {
-    state = s->state;
+    state = ssl->state;
 
-    switch (s->state) {
+    switch (ssl->state) {
       case SSL_ST_CONNECT:
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_START, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
         }
 
-        if (s->init_buf == NULL) {
+        if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
           if (buf == NULL ||
               !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
             ret = -1;
             goto end;
           }
-          s->init_buf = buf;
+          ssl->init_buf = buf;
           buf = NULL;
         }
 
-        if (!ssl_init_wbio_buffer(s, 0)) {
+        if (!ssl_init_wbio_buffer(ssl, 0)) {
           ret = -1;
           goto end;
         }
 
         /* don't push the buffering BIO quite yet */
 
-        s->state = SSL3_ST_CW_CLNT_HELLO_A;
-        s->init_num = 0;
-        s->d1->send_cookie = 0;
-        s->hit = 0;
+        ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
+        ssl->init_num = 0;
+        ssl->d1->send_cookie = 0;
+        ssl->hit = 0;
         break;
 
       case SSL3_ST_CW_CLNT_HELLO_A:
       case SSL3_ST_CW_CLNT_HELLO_B:
-        s->shutdown = 0;
-        dtls1_start_timer(s);
-        ret = ssl3_send_client_hello(s);
+        ssl->shutdown = 0;
+        dtls1_start_timer(ssl);
+        ret = ssl3_send_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        if (s->d1->send_cookie) {
-          s->state = SSL3_ST_CW_FLUSH;
-          s->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
+        if (ssl->d1->send_cookie) {
+          ssl->state = SSL3_ST_CW_FLUSH;
+          ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
         } else {
-          s->state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
+          ssl->state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
         }
 
-        s->init_num = 0;
+        ssl->init_num = 0;
         /* turn on buffering for the next lot of output */
-        if (s->bbio != s->wbio) {
-          s->wbio = BIO_push(s->bbio, s->wbio);
+        if (ssl->bbio != ssl->wbio) {
+          ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
         }
 
         break;
 
       case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
       case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B:
-        ret = dtls1_get_hello_verify(s);
+        ret = dtls1_get_hello_verify(ssl);
         if (ret <= 0) {
           goto end;
         }
-        if (s->d1->send_cookie) {
+        if (ssl->d1->send_cookie) {
           /* start again, with a cookie */
-          dtls1_stop_timer(s);
-          s->state = SSL3_ST_CW_CLNT_HELLO_A;
+          dtls1_stop_timer(ssl);
+          ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
         } else {
-          s->state = SSL3_ST_CR_SRVR_HELLO_A;
+          ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_SRVR_HELLO_A:
       case SSL3_ST_CR_SRVR_HELLO_B:
-        ret = ssl3_get_server_hello(s);
+        ret = ssl3_get_server_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        if (s->hit) {
-          s->state = SSL3_ST_CR_FINISHED_A;
-          if (s->tlsext_ticket_expected) {
+        if (ssl->hit) {
+          ssl->state = SSL3_ST_CR_CHANGE;
+          if (ssl->tlsext_ticket_expected) {
             /* receive renewed session ticket */
-            s->state = SSL3_ST_CR_SESSION_TICKET_A;
+            ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
           }
         } else {
-          s->state = SSL3_ST_CR_CERT_A;
+          ssl->state = SSL3_ST_CR_CERT_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_A:
       case SSL3_ST_CR_CERT_B:
-        if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-          ret = ssl3_get_server_certificate(s);
+        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+          ret = ssl3_get_server_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
-          if (s->s3->tmp.certificate_status_expected) {
-            s->state = SSL3_ST_CR_CERT_STATUS_A;
+          if (ssl->s3->tmp.certificate_status_expected) {
+            ssl->state = SSL3_ST_CR_CERT_STATUS_A;
           } else {
-            s->state = SSL3_ST_VERIFY_SERVER_CERT;
+            ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
           }
         } else {
           skip = 1;
-          s->state = SSL3_ST_CR_KEY_EXCH_A;
+          ssl->state = SSL3_ST_CR_KEY_EXCH_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_VERIFY_SERVER_CERT:
-        ret = ssl3_verify_server_cert(s);
+        ret = ssl3_verify_server_cert(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        s->state = SSL3_ST_CR_KEY_EXCH_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_KEY_EXCH_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_KEY_EXCH_A:
       case SSL3_ST_CR_KEY_EXCH_B:
-        ret = ssl3_get_server_key_exchange(s);
+        ret = ssl3_get_server_key_exchange(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CR_CERT_REQ_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_CERT_REQ_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_REQ_A:
       case SSL3_ST_CR_CERT_REQ_B:
-        ret = ssl3_get_certificate_request(s);
+        ret = ssl3_get_certificate_request(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CR_SRVR_DONE_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_SRVR_DONE_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_SRVR_DONE_A:
       case SSL3_ST_CR_SRVR_DONE_B:
-        ret = ssl3_get_server_done(s);
+        ret = ssl3_get_server_done(ssl);
         if (ret <= 0) {
           goto end;
         }
-        dtls1_stop_timer(s);
-        if (s->s3->tmp.cert_req) {
-          s->s3->tmp.next_state = SSL3_ST_CW_CERT_A;
+        dtls1_stop_timer(ssl);
+        if (ssl->s3->tmp.cert_req) {
+          ssl->s3->tmp.next_state = SSL3_ST_CW_CERT_A;
         } else {
-          s->s3->tmp.next_state = SSL3_ST_CW_KEY_EXCH_A;
+          ssl->s3->tmp.next_state = SSL3_ST_CW_KEY_EXCH_A;
         }
-        s->init_num = 0;
-        s->state = s->s3->tmp.next_state;
+        ssl->init_num = 0;
+        ssl->state = ssl->s3->tmp.next_state;
         break;
 
       case SSL3_ST_CW_CERT_A:
       case SSL3_ST_CW_CERT_B:
       case SSL3_ST_CW_CERT_C:
       case SSL3_ST_CW_CERT_D:
-        dtls1_start_timer(s);
-        ret = ssl3_send_client_certificate(s);
+        dtls1_start_timer(ssl);
+        ret = ssl3_send_client_certificate(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CW_KEY_EXCH_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CW_KEY_EXCH_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_KEY_EXCH_A:
       case SSL3_ST_CW_KEY_EXCH_B:
-        dtls1_start_timer(s);
-        ret = ssl3_send_client_key_exchange(s);
+        dtls1_start_timer(ssl);
+        ret = ssl3_send_client_key_exchange(ssl);
         if (ret <= 0) {
           goto end;
         }
         /* For TLS, cert_req is set to 2, so a cert chain
          * of nothing is sent, but no verify packet is sent */
-        if (s->s3->tmp.cert_req == 1) {
-          s->state = SSL3_ST_CW_CERT_VRFY_A;
+        if (ssl->s3->tmp.cert_req == 1) {
+          ssl->state = SSL3_ST_CW_CERT_VRFY_A;
         } else {
-          s->state = SSL3_ST_CW_CHANGE_A;
-          s->s3->change_cipher_spec = 0;
+          ssl->state = SSL3_ST_CW_CHANGE_A;
         }
 
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CERT_VRFY_A:
       case SSL3_ST_CW_CERT_VRFY_B:
       case SSL3_ST_CW_CERT_VRFY_C:
-        dtls1_start_timer(s);
-        ret = ssl3_send_cert_verify(s);
+        dtls1_start_timer(ssl);
+        ret = ssl3_send_cert_verify(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CW_CHANGE_A;
-        s->init_num = 0;
-        s->s3->change_cipher_spec = 0;
+        ssl->state = SSL3_ST_CW_CHANGE_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CHANGE_A:
       case SSL3_ST_CW_CHANGE_B:
-        if (!s->hit) {
-          dtls1_start_timer(s);
+        if (!ssl->hit) {
+          dtls1_start_timer(ssl);
         }
-        ret = dtls1_send_change_cipher_spec(s, SSL3_ST_CW_CHANGE_A,
+        ret = dtls1_send_change_cipher_spec(ssl, SSL3_ST_CW_CHANGE_A,
                                             SSL3_ST_CW_CHANGE_B);
         if (ret <= 0) {
           goto end;
         }
 
-        s->state = SSL3_ST_CW_FINISHED_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CW_FINISHED_A;
+        ssl->init_num = 0;
 
-        s->session->cipher = s->s3->tmp.new_cipher;
-        if (!s->enc_method->setup_key_block(s) ||
-            !s->enc_method->change_cipher_state(
-                s, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+        ssl->session->cipher = ssl->s3->tmp.new_cipher;
+        if (!ssl->enc_method->setup_key_block(ssl) ||
+            !ssl->enc_method->change_cipher_state(
+                ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
         }
-
-        dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
         break;
 
       case SSL3_ST_CW_FINISHED_A:
       case SSL3_ST_CW_FINISHED_B:
-        if (!s->hit) {
-          dtls1_start_timer(s);
+        if (!ssl->hit) {
+          dtls1_start_timer(ssl);
         }
 
         ret =
-            ssl3_send_finished(s, SSL3_ST_CW_FINISHED_A, SSL3_ST_CW_FINISHED_B,
-                               s->enc_method->client_finished_label,
-                               s->enc_method->client_finished_label_len);
+            ssl3_send_finished(ssl, SSL3_ST_CW_FINISHED_A, SSL3_ST_CW_FINISHED_B,
+                               ssl->enc_method->client_finished_label,
+                               ssl->enc_method->client_finished_label_len);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CW_FLUSH;
+        ssl->state = SSL3_ST_CW_FLUSH;
 
-        if (s->hit) {
-          s->s3->tmp.next_state = SSL_ST_OK;
+        if (ssl->hit) {
+          ssl->s3->tmp.next_state = SSL_ST_OK;
         } else {
           /* Allow NewSessionTicket if ticket expected */
-          if (s->tlsext_ticket_expected) {
-            s->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A;
+          if (ssl->tlsext_ticket_expected) {
+            ssl->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A;
           } else {
-            s->s3->tmp.next_state = SSL3_ST_CR_FINISHED_A;
+            ssl->s3->tmp.next_state = SSL3_ST_CR_CHANGE;
           }
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_SESSION_TICKET_A:
       case SSL3_ST_CR_SESSION_TICKET_B:
-        ret = ssl3_get_new_session_ticket(s);
+        ret = ssl3_get_new_session_ticket(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CR_FINISHED_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_CHANGE;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_STATUS_A:
       case SSL3_ST_CR_CERT_STATUS_B:
-        ret = ssl3_get_cert_status(s);
+        ret = ssl3_get_cert_status(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_VERIFY_SERVER_CERT;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
+        ssl->init_num = 0;
+        break;
+
+      case SSL3_ST_CR_CHANGE:
+        ret = ssl->method->ssl_read_change_cipher_spec(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
+
+        if (!ssl3_do_change_cipher_spec(ssl)) {
+          ret = -1;
+          goto end;
+        }
+        ssl->state = SSL3_ST_CR_FINISHED_A;
         break;
 
       case SSL3_ST_CR_FINISHED_A:
       case SSL3_ST_CR_FINISHED_B:
-        s->d1->change_cipher_spec_ok = 1;
         ret =
-            ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B);
+            ssl3_get_finished(ssl, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B);
         if (ret <= 0) {
           goto end;
         }
-        dtls1_stop_timer(s);
+        dtls1_stop_timer(ssl);
 
-        if (s->hit) {
-          s->state = SSL3_ST_CW_CHANGE_A;
+        if (ssl->hit) {
+          ssl->state = SSL3_ST_CW_CHANGE_A;
         } else {
-          s->state = SSL_ST_OK;
+          ssl->state = SSL_ST_OK;
         }
 
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_FLUSH:
-        s->rwstate = SSL_WRITING;
-        if (BIO_flush(s->wbio) <= 0) {
+        ssl->rwstate = SSL_WRITING;
+        if (BIO_flush(ssl->wbio) <= 0) {
           ret = -1;
           goto end;
         }
-        s->rwstate = SSL_NOTHING;
-        s->state = s->s3->tmp.next_state;
+        ssl->rwstate = SSL_NOTHING;
+        ssl->state = ssl->s3->tmp.next_state;
         break;
 
       case SSL_ST_OK:
         /* clean a few things up */
-        ssl3_cleanup_key_block(s);
+        ssl3_cleanup_key_block(ssl);
 
         /* Remove write buffering now. */
-        ssl_free_wbio_buffer(s);
+        ssl_free_wbio_buffer(ssl);
 
-        s->init_num = 0;
-        s->s3->initial_handshake_complete = 1;
+        ssl->init_num = 0;
+        ssl->s3->initial_handshake_complete = 1;
 
-        ssl_update_cache(s, SSL_SESS_CACHE_CLIENT);
+        ssl_update_cache(ssl, SSL_SESS_CACHE_CLIENT);
 
         ret = 1;
 
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         }
 
         /* done with handshaking */
-        s->d1->handshake_read_seq = 0;
-        s->d1->next_handshake_write_seq = 0;
+        ssl->d1->handshake_read_seq = 0;
+        ssl->d1->next_handshake_write_seq = 0;
         goto end;
 
       default:
@@ -488,36 +496,36 @@
     }
 
     /* did we do anything? */
-    if (!s->s3->tmp.reuse_message && !skip) {
-      if ((cb != NULL) && (s->state != state)) {
-        new_state = s->state;
-        s->state = state;
-        cb(s, SSL_CB_CONNECT_LOOP, 1);
-        s->state = new_state;
+    if (!ssl->s3->tmp.reuse_message && !skip) {
+      if ((cb != NULL) && (ssl->state != state)) {
+        new_state = ssl->state;
+        ssl->state = state;
+        cb(ssl, SSL_CB_CONNECT_LOOP, 1);
+        ssl->state = new_state;
       }
     }
     skip = 0;
   }
 
 end:
-  s->in_handshake--;
+  ssl->in_handshake--;
 
   BUF_MEM_free(buf);
   if (cb != NULL) {
-    cb(s, SSL_CB_CONNECT_EXIT, ret);
+    cb(ssl, SSL_CB_CONNECT_EXIT, ret);
   }
   return ret;
 }
 
-static int dtls1_get_hello_verify(SSL *s) {
+static int dtls1_get_hello_verify(SSL *ssl) {
   long n;
   int al, ok = 0;
   CBS hello_verify_request, cookie;
   uint16_t server_version;
 
-  n = s->method->ssl_get_message(
-      s, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B,
-      -1,
+  n = ssl->method->ssl_get_message(
+      ssl, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A,
+      DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B, -1,
       /* Use the same maximum size as ssl3_get_server_hello. */
       20000, ssl_hash_message, &ok);
 
@@ -525,13 +533,13 @@
     return n;
   }
 
-  if (s->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) {
-    s->d1->send_cookie = 0;
-    s->s3->tmp.reuse_message = 1;
+  if (ssl->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) {
+    ssl->d1->send_cookie = 0;
+    ssl->s3->tmp.reuse_message = 1;
     return 1;
   }
 
-  CBS_init(&hello_verify_request, s->init_msg, n);
+  CBS_init(&hello_verify_request, ssl->init_msg, n);
 
   if (!CBS_get_u16(&hello_verify_request, &server_version) ||
       !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
@@ -541,18 +549,18 @@
     goto f_err;
   }
 
-  if (CBS_len(&cookie) > sizeof(s->d1->cookie)) {
+  if (CBS_len(&cookie) > sizeof(ssl->d1->cookie)) {
     al = SSL_AD_ILLEGAL_PARAMETER;
     goto f_err;
   }
 
-  memcpy(s->d1->cookie, CBS_data(&cookie), CBS_len(&cookie));
-  s->d1->cookie_len = CBS_len(&cookie);
+  memcpy(ssl->d1->cookie, CBS_data(&cookie), CBS_len(&cookie));
+  ssl->d1->cookie_len = CBS_len(&cookie);
 
-  s->d1->send_cookie = 1;
+  ssl->d1->send_cookie = 1;
   return 1;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
   return -1;
 }
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index 787ad9a..7f08b06 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -84,15 +84,15 @@
 
 static void get_current_time(const SSL *ssl, struct timeval *out_clock);
 
-int dtls1_new(SSL *s) {
+int dtls1_new(SSL *ssl) {
   DTLS1_STATE *d1;
 
-  if (!ssl3_new(s)) {
+  if (!ssl3_new(ssl)) {
     return 0;
   }
   d1 = OPENSSL_malloc(sizeof *d1);
   if (d1 == NULL) {
-    ssl3_free(s);
+    ssl3_free(ssl);
     return 0;
   }
   memset(d1, 0, sizeof *d1);
@@ -104,52 +104,52 @@
     pqueue_free(d1->buffered_messages);
     pqueue_free(d1->sent_messages);
     OPENSSL_free(d1);
-    ssl3_free(s);
+    ssl3_free(ssl);
     return 0;
   }
 
-  s->d1 = d1;
+  ssl->d1 = d1;
 
   /* Set the version to the highest version for DTLS. This controls the initial
-   * state of |s->enc_method| and what the API reports as the version prior to
+   * state of |ssl->enc_method| and what the API reports as the version prior to
    * negotiation.
    *
    * TODO(davidben): This is fragile and confusing. */
-  s->version = DTLS1_2_VERSION;
+  ssl->version = DTLS1_2_VERSION;
   return 1;
 }
 
-static void dtls1_clear_queues(SSL *s) {
+static void dtls1_clear_queues(SSL *ssl) {
   pitem *item = NULL;
   hm_fragment *frag = NULL;
 
-  while ((item = pqueue_pop(s->d1->buffered_messages)) != NULL) {
+  while ((item = pqueue_pop(ssl->d1->buffered_messages)) != NULL) {
     frag = (hm_fragment *)item->data;
     dtls1_hm_fragment_free(frag);
     pitem_free(item);
   }
 
-  while ((item = pqueue_pop(s->d1->sent_messages)) != NULL) {
+  while ((item = pqueue_pop(ssl->d1->sent_messages)) != NULL) {
     frag = (hm_fragment *)item->data;
     dtls1_hm_fragment_free(frag);
     pitem_free(item);
   }
 }
 
-void dtls1_free(SSL *s) {
-  ssl3_free(s);
+void dtls1_free(SSL *ssl) {
+  ssl3_free(ssl);
 
-  if (s == NULL || s->d1 == NULL) {
+  if (ssl == NULL || ssl->d1 == NULL) {
     return;
   }
 
-  dtls1_clear_queues(s);
+  dtls1_clear_queues(ssl);
 
-  pqueue_free(s->d1->buffered_messages);
-  pqueue_free(s->d1->sent_messages);
+  pqueue_free(ssl->d1->buffered_messages);
+  pqueue_free(ssl->d1->sent_messages);
 
-  OPENSSL_free(s->d1);
-  s->d1 = NULL;
+  OPENSSL_free(ssl->d1);
+  ssl->d1 = NULL;
 }
 
 int dtls1_supports_cipher(const SSL_CIPHER *cipher) {
@@ -158,19 +158,19 @@
   return cipher->algorithm_enc != SSL_RC4 && cipher->algorithm_enc != SSL_eNULL;
 }
 
-void dtls1_start_timer(SSL *s) {
+void dtls1_start_timer(SSL *ssl) {
   /* If timer is not set, initialize duration with 1 second */
-  if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) {
-    s->d1->timeout_duration = 1;
+  if (ssl->d1->next_timeout.tv_sec == 0 && ssl->d1->next_timeout.tv_usec == 0) {
+    ssl->d1->timeout_duration = 1;
   }
 
   /* Set timeout to current time */
-  get_current_time(s, &s->d1->next_timeout);
+  get_current_time(ssl, &ssl->d1->next_timeout);
 
   /* Add duration to current time */
-  s->d1->next_timeout.tv_sec += s->d1->timeout_duration;
-  BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
-           &s->d1->next_timeout);
+  ssl->d1->next_timeout.tv_sec += ssl->d1->timeout_duration;
+  BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
+           &ssl->d1->next_timeout);
 }
 
 int DTLSv1_get_timeout(const SSL *ssl, struct timeval *out) {
@@ -213,11 +213,11 @@
   return 1;
 }
 
-int dtls1_is_timer_expired(SSL *s) {
+int dtls1_is_timer_expired(SSL *ssl) {
   struct timeval timeleft;
 
   /* Get time left until timeout, return false if no timer running */
-  if (!DTLSv1_get_timeout(s, &timeleft)) {
+  if (!DTLSv1_get_timeout(ssl, &timeleft)) {
     return 0;
   }
 
@@ -230,39 +230,39 @@
   return 1;
 }
 
-void dtls1_double_timeout(SSL *s) {
-  s->d1->timeout_duration *= 2;
-  if (s->d1->timeout_duration > 60) {
-    s->d1->timeout_duration = 60;
+void dtls1_double_timeout(SSL *ssl) {
+  ssl->d1->timeout_duration *= 2;
+  if (ssl->d1->timeout_duration > 60) {
+    ssl->d1->timeout_duration = 60;
   }
-  dtls1_start_timer(s);
+  dtls1_start_timer(ssl);
 }
 
-void dtls1_stop_timer(SSL *s) {
+void dtls1_stop_timer(SSL *ssl) {
   /* Reset everything */
-  s->d1->num_timeouts = 0;
-  memset(&s->d1->next_timeout, 0, sizeof(struct timeval));
-  s->d1->timeout_duration = 1;
-  BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
-           &s->d1->next_timeout);
+  ssl->d1->num_timeouts = 0;
+  memset(&ssl->d1->next_timeout, 0, sizeof(struct timeval));
+  ssl->d1->timeout_duration = 1;
+  BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
+           &ssl->d1->next_timeout);
   /* Clear retransmission buffer */
-  dtls1_clear_record_buffer(s);
+  dtls1_clear_record_buffer(ssl);
 }
 
-int dtls1_check_timeout_num(SSL *s) {
-  s->d1->num_timeouts++;
+int dtls1_check_timeout_num(SSL *ssl) {
+  ssl->d1->num_timeouts++;
 
   /* Reduce MTU after 2 unsuccessful retransmissions */
-  if (s->d1->num_timeouts > DTLS1_MTU_TIMEOUTS &&
-      !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) {
-    long mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0,
+  if (ssl->d1->num_timeouts > DTLS1_MTU_TIMEOUTS &&
+      !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
+    long mtu = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0,
                         NULL);
     if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
-      s->d1->mtu = (unsigned)mtu;
+      ssl->d1->mtu = (unsigned)mtu;
     }
   }
 
-  if (s->d1->num_timeouts > DTLS1_MAX_TIMEOUTS) {
+  if (ssl->d1->num_timeouts > DTLS1_MAX_TIMEOUTS) {
     /* fail the connection, enough alerts have been sent */
     OPENSSL_PUT_ERROR(SSL, SSL_R_READ_TIMEOUT_EXPIRED);
     return -1;
@@ -307,21 +307,22 @@
 #endif
 }
 
-int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) {
-  uint8_t *message = (uint8_t *)s->init_buf->data;
-  const struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr;
+int dtls1_set_handshake_header(SSL *ssl, int htype, unsigned long len) {
+  uint8_t *message = (uint8_t *)ssl->init_buf->data;
+  const struct hm_header_st *msg_hdr = &ssl->d1->w_msg_hdr;
   uint8_t serialised_header[DTLS1_HM_HEADER_LENGTH];
   uint8_t *p = serialised_header;
 
-  s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
-  s->d1->next_handshake_write_seq++;
+  ssl->d1->handshake_write_seq = ssl->d1->next_handshake_write_seq;
+  ssl->d1->next_handshake_write_seq++;
 
-  dtls1_set_message_header(s, htype, len, s->d1->handshake_write_seq, 0, len);
-  s->init_num = (int)len + DTLS1_HM_HEADER_LENGTH;
-  s->init_off = 0;
+  dtls1_set_message_header(ssl, htype, len, ssl->d1->handshake_write_seq, 0,
+                           len);
+  ssl->init_num = (int)len + DTLS1_HM_HEADER_LENGTH;
+  ssl->init_off = 0;
 
   /* Buffer the message to handle re-xmits */
-  dtls1_buffer_message(s);
+  dtls1_buffer_message(ssl);
 
   /* Add the new message to the handshake hash. Serialize the message
    * header as if it were a single fragment. */
@@ -330,11 +331,11 @@
   s2n(msg_hdr->seq, p);
   l2n3(0, p);
   l2n3(msg_hdr->msg_len, p);
-  return ssl3_update_handshake_hash(s, serialised_header,
+  return ssl3_update_handshake_hash(ssl, serialised_header,
                                     sizeof(serialised_header)) &&
-         ssl3_update_handshake_hash(s, message + DTLS1_HM_HEADER_LENGTH, len);
+         ssl3_update_handshake_hash(ssl, message + DTLS1_HM_HEADER_LENGTH, len);
 }
 
-int dtls1_handshake_write(SSL *s) {
-  return dtls1_do_handshake_write(s, dtls1_use_current_epoch);
+int dtls1_handshake_write(SSL *ssl) {
+  return dtls1_do_handshake_write(ssl, dtls1_use_current_epoch);
 }
diff --git a/src/ssl/d1_meth.c b/src/ssl/d1_meth.c
index 4dc3404..49a2595 100644
--- a/src/ssl/d1_meth.c
+++ b/src/ssl/d1_meth.c
@@ -67,6 +67,7 @@
     dtls1_connect,
     dtls1_get_message,
     dtls1_read_app_data,
+    dtls1_read_change_cipher_spec,
     dtls1_read_close_notify,
     dtls1_write_app_data,
     dtls1_dispatch_alert,
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index 8cabdd4..f2ade3a 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -124,7 +124,7 @@
 #include "internal.h"
 
 
-static int do_dtls1_write(SSL *s, int type, const uint8_t *buf,
+static int do_dtls1_write(SSL *ssl, int type, const uint8_t *buf,
                           unsigned int len, enum dtls1_use_epoch_t use_epoch);
 
 /* dtls1_get_record reads a new input record. On success, it places it in
@@ -164,7 +164,6 @@
       SSL3_RECORD *rr = &ssl->s3->rrec;
       rr->type = type;
       rr->length = (uint16_t)len;
-      rr->off = 0;
       rr->data = out;
       return 1;
 
@@ -190,6 +189,29 @@
   return dtls1_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek);
 }
 
+int dtls1_read_change_cipher_spec(SSL *ssl) {
+  uint8_t byte;
+  int ret = dtls1_read_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, &byte,
+                             1 /* len */, 0 /* no peek */);
+  if (ret <= 0) {
+    return ret;
+  }
+  assert(ret == 1);
+
+  if (ssl->s3->rrec.length != 0 || byte != SSL3_MT_CCS) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return -1;
+  }
+
+  if (ssl->msg_callback != NULL) {
+    ssl->msg_callback(0, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, 1,
+                      ssl, ssl->msg_callback_arg);
+  }
+
+  return 1;
+}
+
 void dtls1_read_close_notify(SSL *ssl) {
   /* Bidirectional shutdown doesn't make sense for an unordered transport. DTLS
    * alerts also aren't delivered reliably, so we may even time out because the
@@ -201,44 +223,31 @@
 /* Return up to 'len' payload bytes received in 'type' records.
  * 'type' is one of the following:
  *
- *   -  SSL3_RT_HANDSHAKE (when ssl3_get_message calls us)
- *   -  SSL3_RT_APPLICATION_DATA (when ssl3_read calls us)
+ *   -  SSL3_RT_HANDSHAKE (when dtls1_get_message calls us)
+ *   -  SSL3_RT_CHANGE_CIPHER_SPEC (when dtls1_read_change_cipher_spec calls us)
+ *   -  SSL3_RT_APPLICATION_DATA (when dtls1_read_app_data calls us)
  *
- * If we don't have stored data to work from, read a SSL/TLS record first
- * (possibly multiple records if we still don't have anything to return).
+ * If we don't have stored data to work from, read a DTLS record first (possibly
+ * multiple records if we still don't have anything to return).
  *
  * This function must handle any surprises the peer may have for us, such as
- * Alert records (e.g. close_notify), ChangeCipherSpec records (not really
- * a surprise, but handled as if it were), or renegotiation requests.
- * Also if record payloads contain fragments too small to process, we store
- * them until there is enough for the respective protocol (the record protocol
- * may use arbitrary fragmentation and even interleaving):
- *     Change cipher spec protocol
- *             just 1 byte needed, no need for keeping anything stored
- *     Alert protocol
- *             2 bytes needed (AlertLevel, AlertDescription)
- *     Handshake protocol
- *             4 bytes needed (HandshakeType, uint24 length) -- we just have
- *             to detect unexpected Client Hello and Hello Request messages
- *             here, anything else is handled by higher layers
- *     Application data protocol
- *             none of our business
- */
-int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) {
+ * Alert records (e.g. close_notify) and out of records. */
+int dtls1_read_bytes(SSL *ssl, int type, unsigned char *buf, int len, int peek) {
   int al, i, ret;
   unsigned int n;
   SSL3_RECORD *rr;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
 
-  if ((type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE) ||
+  if ((type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE &&
+       type != SSL3_RT_CHANGE_CIPHER_SPEC) ||
       (peek && type != SSL3_RT_APPLICATION_DATA)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  if (!s->in_handshake && SSL_in_init(s)) {
+  if (!ssl->in_handshake && SSL_in_init(ssl)) {
     /* type == SSL3_RT_APPLICATION_DATA */
-    i = s->handshake_func(s);
+    i = ssl->handshake_func(ssl);
     if (i < 0) {
       return i;
     }
@@ -249,24 +258,24 @@
   }
 
 start:
-  s->rwstate = SSL_NOTHING;
+  ssl->rwstate = SSL_NOTHING;
 
-  /* s->s3->rrec.type     - is the type of record
-   * s->s3->rrec.data     - data
-   * s->s3->rrec.off      - offset into 'data' for next read
-   * s->s3->rrec.length   - number of bytes. */
-  rr = &s->s3->rrec;
+  /* ssl->s3->rrec.type     - is the type of record
+   * ssl->s3->rrec.data     - data
+   * ssl->s3->rrec.off      - offset into 'data' for next read
+   * ssl->s3->rrec.length   - number of bytes. */
+  rr = &ssl->s3->rrec;
 
   /* Check for timeout */
-  if (DTLSv1_handle_timeout(s) > 0) {
+  if (DTLSv1_handle_timeout(ssl) > 0) {
     goto start;
   }
 
   /* get new packet if necessary */
   if (rr->length == 0) {
-    ret = dtls1_get_record(s);
+    ret = dtls1_get_record(ssl);
     if (ret <= 0) {
-      ret = dtls1_read_failed(s, ret);
+      ret = dtls1_read_failed(ssl, ret);
       /* anything other than a timeout is an error */
       if (ret <= 0) {
         return ret;
@@ -278,31 +287,20 @@
 
   /* we now have a packet which can be read and processed */
 
-  /* |change_cipher_spec is set when we receive a ChangeCipherSpec and reset by
-   * ssl3_get_finished. */
-  if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE &&
-      rr->type != SSL3_RT_ALERT) {
-    /* We now have an unexpected record between CCS and Finished. Most likely
-     * the packets were reordered on their way. DTLS is unreliable, so drop the
-     * packet and expect the peer to retransmit. */
-    rr->length = 0;
-    goto start;
-  }
-
   /* If the other end has shut down, throw anything we read away (even in
    * 'peek' mode) */
-  if (s->shutdown & SSL_RECEIVED_SHUTDOWN) {
+  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
     rr->length = 0;
-    s->rwstate = SSL_NOTHING;
+    ssl->rwstate = SSL_NOTHING;
     return 0;
   }
 
 
-  if (type == rr->type) { /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */
-    /* make sure that we are not getting application data when we
-     * are doing a handshake for the first time */
-    if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) &&
-        (s->aead_read_ctx == NULL)) {
+  if (type == rr->type) {
+    /* Make sure that we are not getting application data when we
+     * are doing a handshake for the first time. */
+    if (SSL_in_init(ssl) && (type == SSL3_RT_APPLICATION_DATA) &&
+        (ssl->aead_read_ctx == NULL)) {
       /* TODO(davidben): Is this check redundant with the handshake_func
        * check? */
       al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -325,14 +323,13 @@
       n = (unsigned int)len;
     }
 
-    memcpy(buf, &(rr->data[rr->off]), n);
+    memcpy(buf, rr->data, n);
     if (!peek) {
       rr->length -= n;
-      rr->off += n;
+      rr->data += n;
       if (rr->length == 0) {
-        rr->off = 0;
         /* The record has been consumed, so we may now clear the buffer. */
-        ssl_read_buffer_discard(s);
+        ssl_read_buffer_discard(ssl);
       }
     }
 
@@ -351,41 +348,42 @@
       goto f_err;
     }
 
-    if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_ALERT, &rr->data[rr->off], 2, s,
-                      s->msg_callback_arg);
+    if (ssl->msg_callback) {
+      ssl->msg_callback(0, ssl->version, SSL3_RT_ALERT, rr->data, 2, ssl,
+                      ssl->msg_callback_arg);
     }
-    const uint8_t alert_level = rr->data[rr->off++];
-    const uint8_t alert_descr = rr->data[rr->off++];
+    const uint8_t alert_level = rr->data[0];
+    const uint8_t alert_descr = rr->data[1];
     rr->length -= 2;
+    rr->data += 2;
 
-    if (s->info_callback != NULL) {
-      cb = s->info_callback;
-    } else if (s->ctx->info_callback != NULL) {
-      cb = s->ctx->info_callback;
+    if (ssl->info_callback != NULL) {
+      cb = ssl->info_callback;
+    } else if (ssl->ctx->info_callback != NULL) {
+      cb = ssl->ctx->info_callback;
     }
 
     if (cb != NULL) {
       uint16_t alert = (alert_level << 8) | alert_descr;
-      cb(s, SSL_CB_READ_ALERT, alert);
+      cb(ssl, SSL_CB_READ_ALERT, alert);
     }
 
     if (alert_level == SSL3_AL_WARNING) {
-      s->s3->warn_alert = alert_descr;
+      ssl->s3->warn_alert = alert_descr;
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
-        s->shutdown |= SSL_RECEIVED_SHUTDOWN;
+        ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
         return 0;
       }
     } else if (alert_level == SSL3_AL_FATAL) {
       char tmp[16];
 
-      s->rwstate = SSL_NOTHING;
-      s->s3->fatal_alert = alert_descr;
+      ssl->rwstate = SSL_NOTHING;
+      ssl->s3->fatal_alert = alert_descr;
       OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
       BIO_snprintf(tmp, sizeof tmp, "%d", alert_descr);
       ERR_add_error_data(2, "SSL alert number ", tmp);
-      s->shutdown |= SSL_RECEIVED_SHUTDOWN;
-      SSL_CTX_remove_session(s->ctx, s->session);
+      ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+      SSL_CTX_remove_session(ssl->ctx, ssl->session);
       return 0;
     } else {
       al = SSL_AD_ILLEGAL_PARAMETER;
@@ -396,89 +394,74 @@
     goto start;
   }
 
-  if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) {
-    /* 'Change Cipher Spec' is just a single byte, so we know exactly what the
-     * record payload has to look like */
-    if (rr->length != 1 || rr->off != 0 || rr->data[0] != SSL3_MT_CCS) {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
-      goto f_err;
-    }
-
+  /* Cross-epoch records are discarded, but we may receive out-of-order
+   * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec
+   * before the appropriate point in the handshake. Those must be silently
+   * discarded.
+   *
+   * However, only allow the out-of-order records in the correct epoch.
+   * Application data must come in the encrypted epoch, and ChangeCipherSpec in
+   * the unencrypted epoch (we never renegotiate). Other cases fall through and
+   * fail with a fatal error. */
+  if ((rr->type == SSL3_RT_APPLICATION_DATA && ssl->aead_read_ctx != NULL) ||
+      (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC && ssl->aead_read_ctx == NULL)) {
     rr->length = 0;
-
-    if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s,
-                      s->msg_callback_arg);
-    }
-
-    /* We can't process a CCS now, because previous handshake
-     * messages are still missing, so just drop it.
-     */
-    if (!s->d1->change_cipher_spec_ok) {
-      goto start;
-    }
-
-    s->d1->change_cipher_spec_ok = 0;
-
-    s->s3->change_cipher_spec = 1;
-    if (!ssl3_do_change_cipher_spec(s)) {
-      goto err;
-    }
-
-    /* do this whenever CCS is processed */
-    dtls1_reset_seq_numbers(s, SSL3_CC_READ);
-
     goto start;
   }
 
-  /* Unexpected handshake message. It may be a retransmitted Finished (the only
-   * post-CCS message). Otherwise, it's a pre-CCS handshake message from an
-   * unsupported renegotiation attempt. */
-  if (rr->type == SSL3_RT_HANDSHAKE && !s->in_handshake) {
+  if (rr->type == SSL3_RT_HANDSHAKE) {
+    if (type != SSL3_RT_APPLICATION_DATA) {
+      /* Out-of-order handshake record while looking for ChangeCipherSpec. Drop
+       * it silently. */
+      assert(type == SSL3_RT_CHANGE_CIPHER_SPEC);
+      rr->length = 0;
+      goto start;
+    }
+
+    /* Parse the first fragment header to determine if this is a pre-CCS or
+     * post-CCS handshake record. DTLS resets handshake message numbers on each
+     * handshake, so renegotiations and retransmissions are ambiguous. */
     if (rr->length < DTLS1_HM_HEADER_LENGTH) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
       goto f_err;
     }
     struct hm_header_st msg_hdr;
-    dtls1_get_message_header(&rr->data[rr->off], &msg_hdr);
+    dtls1_get_message_header(rr->data, &msg_hdr);
 
-    /* Ignore a stray Finished from the previous handshake. */
     if (msg_hdr.type == SSL3_MT_FINISHED) {
       if (msg_hdr.frag_off == 0) {
         /* Retransmit our last flight of messages. If the peer sends the second
          * Finished, they may not have received ours. Only do this for the
          * first fragment, in case the Finished was fragmented. */
-        if (dtls1_check_timeout_num(s) < 0) {
+        if (dtls1_check_timeout_num(ssl) < 0) {
           return -1;
         }
 
-        dtls1_retransmit_buffered_messages(s);
+        dtls1_retransmit_buffered_messages(ssl);
       }
 
       rr->length = 0;
       goto start;
     }
-  }
 
-  /* We already handled these. */
-  assert(rr->type != SSL3_RT_CHANGE_CIPHER_SPEC && rr->type != SSL3_RT_ALERT);
+    /* Otherwise, this is a pre-CCS handshake message from an unsupported
+     * renegotiation attempt. Fall through to the error path. */
+  }
 
   al = SSL_AD_UNEXPECTED_MESSAGE;
   OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
-err:
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
   return -1;
 }
 
-int dtls1_write_app_data(SSL *s, const void *buf_, int len) {
+int dtls1_write_app_data(SSL *ssl, const void *buf_, int len) {
   int i;
 
-  if (SSL_in_init(s) && !s->in_handshake) {
-    i = s->handshake_func(s);
+  if (SSL_in_init(ssl) && !ssl->in_handshake) {
+    i = ssl->handshake_func(ssl);
     if (i < 0) {
       return i;
     }
@@ -493,33 +476,33 @@
     return -1;
   }
 
-  i = dtls1_write_bytes(s, SSL3_RT_APPLICATION_DATA, buf_, len,
+  i = dtls1_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf_, len,
                         dtls1_use_current_epoch);
   return i;
 }
 
 /* Call this to write data in records of type 'type' It will return <= 0 if not
  * all data has been sent or non-blocking IO. */
-int dtls1_write_bytes(SSL *s, int type, const void *buf, int len,
+int dtls1_write_bytes(SSL *ssl, int type, const void *buf, int len,
                       enum dtls1_use_epoch_t use_epoch) {
   int i;
 
   assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
-  s->rwstate = SSL_NOTHING;
-  i = do_dtls1_write(s, type, buf, len, use_epoch);
+  ssl->rwstate = SSL_NOTHING;
+  i = do_dtls1_write(ssl, type, buf, len, use_epoch);
   return i;
 }
 
-static int do_dtls1_write(SSL *s, int type, const uint8_t *buf,
+static int do_dtls1_write(SSL *ssl, int type, const uint8_t *buf,
                           unsigned int len, enum dtls1_use_epoch_t use_epoch) {
   /* There should never be a pending write buffer in DTLS. One can't write half
    * a datagram, so the write buffer is always dropped in
    * |ssl_write_buffer_flush|. */
-  assert(!ssl_write_buffer_is_pending(s));
+  assert(!ssl_write_buffer_is_pending(ssl));
 
   /* If we have an alert to send, lets send it */
-  if (s->s3->alert_dispatch) {
-    int ret = s->method->ssl_dispatch_alert(s);
+  if (ssl->s3->alert_dispatch) {
+    int ret = ssl->method->ssl_dispatch_alert(ssl);
     if (ret <= 0) {
       return ret;
     }
@@ -535,78 +518,61 @@
     return 0;
   }
 
-  size_t max_out = len + ssl_max_seal_overhead(s);
+  size_t max_out = len + ssl_max_seal_overhead(ssl);
   uint8_t *out;
   size_t ciphertext_len;
-  if (!ssl_write_buffer_init(s, &out, max_out) ||
-      !dtls_seal_record(s, out, &ciphertext_len, max_out, type, buf, len,
+  if (!ssl_write_buffer_init(ssl, &out, max_out) ||
+      !dtls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len,
                         use_epoch)) {
-    ssl_write_buffer_clear(s);
+    ssl_write_buffer_clear(ssl);
     return -1;
   }
-  ssl_write_buffer_set_len(s, ciphertext_len);
+  ssl_write_buffer_set_len(ssl, ciphertext_len);
 
-  int ret = ssl_write_buffer_flush(s);
+  int ret = ssl_write_buffer_flush(ssl);
   if (ret <= 0) {
     return ret;
   }
   return (int)len;
 }
 
-int dtls1_dispatch_alert(SSL *s) {
+int dtls1_dispatch_alert(SSL *ssl) {
   int i, j;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
   uint8_t buf[DTLS1_AL_HEADER_LENGTH];
   uint8_t *ptr = &buf[0];
 
-  s->s3->alert_dispatch = 0;
+  ssl->s3->alert_dispatch = 0;
 
   memset(buf, 0x00, sizeof(buf));
-  *ptr++ = s->s3->send_alert[0];
-  *ptr++ = s->s3->send_alert[1];
+  *ptr++ = ssl->s3->send_alert[0];
+  *ptr++ = ssl->s3->send_alert[1];
 
-  i = do_dtls1_write(s, SSL3_RT_ALERT, &buf[0], sizeof(buf),
+  i = do_dtls1_write(ssl, SSL3_RT_ALERT, &buf[0], sizeof(buf),
                      dtls1_use_current_epoch);
   if (i <= 0) {
-    s->s3->alert_dispatch = 1;
+    ssl->s3->alert_dispatch = 1;
   } else {
-    if (s->s3->send_alert[0] == SSL3_AL_FATAL) {
-      (void)BIO_flush(s->wbio);
+    if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
+      (void)BIO_flush(ssl->wbio);
     }
 
-    if (s->msg_callback) {
-      s->msg_callback(1, s->version, SSL3_RT_ALERT, s->s3->send_alert, 2, s,
-                      s->msg_callback_arg);
+    if (ssl->msg_callback) {
+      ssl->msg_callback(1, ssl->version, SSL3_RT_ALERT, ssl->s3->send_alert, 2,
+                        ssl, ssl->msg_callback_arg);
     }
 
-    if (s->info_callback != NULL) {
-      cb = s->info_callback;
-    } else if (s->ctx->info_callback != NULL) {
-      cb = s->ctx->info_callback;
+    if (ssl->info_callback != NULL) {
+      cb = ssl->info_callback;
+    } else if (ssl->ctx->info_callback != NULL) {
+      cb = ssl->ctx->info_callback;
     }
 
     if (cb != NULL) {
-      j = (s->s3->send_alert[0] << 8) | s->s3->send_alert[1];
-      cb(s, SSL_CB_WRITE_ALERT, j);
+      j = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
+      cb(ssl, SSL_CB_WRITE_ALERT, j);
     }
   }
 
   return i;
 }
-
-void dtls1_reset_seq_numbers(SSL *s, int rw) {
-  uint8_t *seq;
-  unsigned int seq_bytes = sizeof(s->s3->read_sequence);
-
-  if (rw & SSL3_CC_READ) {
-    seq = s->s3->read_sequence;
-    s->d1->r_epoch++;
-    memset(&s->d1->bitmap, 0, sizeof(DTLS1_BITMAP));
-  } else {
-    seq = s->s3->write_sequence;
-    memcpy(s->d1->last_write_sequence, seq, sizeof(s->s3->write_sequence));
-    s->d1->w_epoch++;
-  }
-
-  memset(seq, 0x00, seq_bytes);
-}
diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c
index 115bd70..5dba8ef 100644
--- a/src/ssl/d1_srtp.c
+++ b/src/ssl/d1_srtp.c
@@ -133,6 +133,12 @@
     {
         "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32,
     },
+    {
+        "SRTP_AEAD_AES_128_GCM", SRTP_AEAD_AES_128_GCM,
+    },
+    {
+        "SRTP_AEAD_AES_256_GCM", SRTP_AEAD_AES_256_GCM,
+    },
     {0, 0},
 };
 
diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c
index f1e8826..3ba9411 100644
--- a/src/ssl/d1_srvr.c
+++ b/src/ssl/d1_srvr.c
@@ -130,131 +130,131 @@
 #include "internal.h"
 
 
-int dtls1_accept(SSL *s) {
+int dtls1_accept(SSL *ssl) {
   BUF_MEM *buf = NULL;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
   uint32_t alg_a;
   int ret = -1;
   int new_state, state, skip = 0;
 
-  assert(s->handshake_func == dtls1_accept);
-  assert(s->server);
-  assert(SSL_IS_DTLS(s));
+  assert(ssl->handshake_func == dtls1_accept);
+  assert(ssl->server);
+  assert(SSL_IS_DTLS(ssl));
 
   ERR_clear_error();
   ERR_clear_system_error();
 
-  if (s->info_callback != NULL) {
-    cb = s->info_callback;
-  } else if (s->ctx->info_callback != NULL) {
-    cb = s->ctx->info_callback;
+  if (ssl->info_callback != NULL) {
+    cb = ssl->info_callback;
+  } else if (ssl->ctx->info_callback != NULL) {
+    cb = ssl->ctx->info_callback;
   }
 
-  s->in_handshake++;
+  ssl->in_handshake++;
 
   for (;;) {
-    state = s->state;
+    state = ssl->state;
 
-    switch (s->state) {
+    switch (ssl->state) {
       case SSL_ST_ACCEPT:
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_START, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
         }
 
-        if (s->init_buf == NULL) {
+        if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
           if (buf == NULL || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
             ret = -1;
             goto end;
           }
-          s->init_buf = buf;
+          ssl->init_buf = buf;
           buf = NULL;
         }
 
-        s->init_num = 0;
+        ssl->init_num = 0;
 
-        if (!ssl_init_wbio_buffer(s, 1)) {
+        if (!ssl_init_wbio_buffer(ssl, 1)) {
           ret = -1;
           goto end;
         }
 
-        if (!ssl3_init_handshake_buffer(s)) {
+        if (!ssl3_init_handshake_buffer(ssl)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
           ret = -1;
           goto end;
         }
 
-        s->state = SSL3_ST_SR_CLNT_HELLO_A;
+        ssl->state = SSL3_ST_SR_CLNT_HELLO_A;
         break;
 
       case SSL3_ST_SR_CLNT_HELLO_A:
       case SSL3_ST_SR_CLNT_HELLO_B:
       case SSL3_ST_SR_CLNT_HELLO_C:
       case SSL3_ST_SR_CLNT_HELLO_D:
-        s->shutdown = 0;
-        ret = ssl3_get_client_hello(s);
+        ssl->shutdown = 0;
+        ret = ssl3_get_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
-        dtls1_stop_timer(s);
-        s->state = SSL3_ST_SW_SRVR_HELLO_A;
-        s->init_num = 0;
+        dtls1_stop_timer(ssl);
+        ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_HELLO_A:
       case SSL3_ST_SW_SRVR_HELLO_B:
-        dtls1_start_timer(s);
-        ret = ssl3_send_server_hello(s);
+        dtls1_start_timer(ssl);
+        ret = ssl3_send_server_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        if (s->hit) {
-          if (s->tlsext_ticket_expected) {
-            s->state = SSL3_ST_SW_SESSION_TICKET_A;
+        if (ssl->hit) {
+          if (ssl->tlsext_ticket_expected) {
+            ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
           } else {
-            s->state = SSL3_ST_SW_CHANGE_A;
+            ssl->state = SSL3_ST_SW_CHANGE_A;
           }
         } else {
-          s->state = SSL3_ST_SW_CERT_A;
+          ssl->state = SSL3_ST_SW_CERT_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_A:
       case SSL3_ST_SW_CERT_B:
-        if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-          dtls1_start_timer(s);
-          ret = ssl3_send_server_certificate(s);
+        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+          dtls1_start_timer(ssl);
+          ret = ssl3_send_server_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
-          if (s->s3->tmp.certificate_status_expected) {
-            s->state = SSL3_ST_SW_CERT_STATUS_A;
+          if (ssl->s3->tmp.certificate_status_expected) {
+            ssl->state = SSL3_ST_SW_CERT_STATUS_A;
           } else {
-            s->state = SSL3_ST_SW_KEY_EXCH_A;
+            ssl->state = SSL3_ST_SW_KEY_EXCH_A;
           }
         } else {
           skip = 1;
-          s->state = SSL3_ST_SW_KEY_EXCH_A;
+          ssl->state = SSL3_ST_SW_KEY_EXCH_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_STATUS_A:
       case SSL3_ST_SW_CERT_STATUS_B:
-        ret = ssl3_send_certificate_status(s);
+        ret = ssl3_send_certificate_status(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SW_KEY_EXCH_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_KEY_EXCH_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_KEY_EXCH_A:
       case SSL3_ST_SW_KEY_EXCH_B:
       case SSL3_ST_SW_KEY_EXCH_C:
-        alg_a = s->s3->tmp.new_cipher->algorithm_auth;
+        alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
 
         /* Send a ServerKeyExchange message if:
          * - The key exchange is ephemeral or anonymous
@@ -264,10 +264,10 @@
          * TODO(davidben): This logic is currently duplicated
          * in s3_srvr.c. Fix this. In the meantime, keep them
          * in sync. */
-        if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher) ||
-            ((alg_a & SSL_aPSK) && s->psk_identity_hint)) {
-          dtls1_start_timer(s);
-          ret = ssl3_send_server_key_exchange(s);
+        if (ssl_cipher_requires_server_key_exchange(ssl->s3->tmp.new_cipher) ||
+            ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
+          dtls1_start_timer(ssl);
+          ret = ssl3_send_server_key_exchange(ssl);
           if (ret <= 0) {
             goto end;
           }
@@ -275,176 +275,187 @@
           skip = 1;
         }
 
-        s->state = SSL3_ST_SW_CERT_REQ_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_CERT_REQ_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_REQ_A:
       case SSL3_ST_SW_CERT_REQ_B:
-        if (s->s3->tmp.cert_request) {
-          dtls1_start_timer(s);
-          ret = ssl3_send_certificate_request(s);
+        if (ssl->s3->tmp.cert_request) {
+          dtls1_start_timer(ssl);
+          ret = ssl3_send_certificate_request(ssl);
           if (ret <= 0) {
             goto end;
           }
         } else {
           skip = 1;
         }
-        s->state = SSL3_ST_SW_SRVR_DONE_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_SRVR_DONE_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_DONE_A:
       case SSL3_ST_SW_SRVR_DONE_B:
-        dtls1_start_timer(s);
-        ret = ssl3_send_server_done(s);
+        dtls1_start_timer(ssl);
+        ret = ssl3_send_server_done(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
-        s->state = SSL3_ST_SW_FLUSH;
-        s->init_num = 0;
+        ssl->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
+        ssl->state = SSL3_ST_SW_FLUSH;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_FLUSH:
-        s->rwstate = SSL_WRITING;
-        if (BIO_flush(s->wbio) <= 0) {
+        ssl->rwstate = SSL_WRITING;
+        if (BIO_flush(ssl->wbio) <= 0) {
           ret = -1;
           goto end;
         }
-        s->rwstate = SSL_NOTHING;
-        s->state = s->s3->tmp.next_state;
+        ssl->rwstate = SSL_NOTHING;
+        ssl->state = ssl->s3->tmp.next_state;
         break;
 
       case SSL3_ST_SR_CERT_A:
       case SSL3_ST_SR_CERT_B:
-        if (s->s3->tmp.cert_request) {
-          ret = ssl3_get_client_certificate(s);
+        if (ssl->s3->tmp.cert_request) {
+          ret = ssl3_get_client_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
         }
-        s->init_num = 0;
-        s->state = SSL3_ST_SR_KEY_EXCH_A;
+        ssl->init_num = 0;
+        ssl->state = SSL3_ST_SR_KEY_EXCH_A;
         break;
 
       case SSL3_ST_SR_KEY_EXCH_A:
       case SSL3_ST_SR_KEY_EXCH_B:
       case SSL3_ST_SR_KEY_EXCH_C:
-        ret = ssl3_get_client_key_exchange(s);
+        ret = ssl3_get_client_key_exchange(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SR_CERT_VRFY_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SR_CERT_VRFY_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SR_CERT_VRFY_A:
       case SSL3_ST_SR_CERT_VRFY_B:
-        ret = ssl3_get_cert_verify(s);
+        ret = ssl3_get_cert_verify(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SR_FINISHED_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SR_CHANGE;
+        ssl->init_num = 0;
         break;
 
-      case SSL3_ST_SR_FINISHED_A:
-      case SSL3_ST_SR_FINISHED_B:
-        s->d1->change_cipher_spec_ok = 1;
-        ret =
-            ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B);
+      case SSL3_ST_SR_CHANGE:
+        ret = ssl->method->ssl_read_change_cipher_spec(ssl);
         if (ret <= 0) {
           goto end;
         }
-        dtls1_stop_timer(s);
-        if (s->hit) {
-          s->state = SSL_ST_OK;
-        } else if (s->tlsext_ticket_expected) {
-          s->state = SSL3_ST_SW_SESSION_TICKET_A;
-        } else {
-          s->state = SSL3_ST_SW_CHANGE_A;
-        }
-        s->init_num = 0;
-        break;
 
-      case SSL3_ST_SW_SESSION_TICKET_A:
-      case SSL3_ST_SW_SESSION_TICKET_B:
-        ret = ssl3_send_new_session_ticket(s);
-        if (ret <= 0) {
-          goto end;
-        }
-        s->state = SSL3_ST_SW_CHANGE_A;
-        s->init_num = 0;
-        break;
-
-      case SSL3_ST_SW_CHANGE_A:
-      case SSL3_ST_SW_CHANGE_B:
-        s->session->cipher = s->s3->tmp.new_cipher;
-        if (!s->enc_method->setup_key_block(s)) {
+        if (!ssl3_do_change_cipher_spec(ssl)) {
           ret = -1;
           goto end;
         }
 
-        ret = dtls1_send_change_cipher_spec(s, SSL3_ST_SW_CHANGE_A,
+        ssl->state = SSL3_ST_SR_FINISHED_A;
+        break;
+
+      case SSL3_ST_SR_FINISHED_A:
+      case SSL3_ST_SR_FINISHED_B:
+        ret = ssl3_get_finished(ssl, SSL3_ST_SR_FINISHED_A,
+                                SSL3_ST_SR_FINISHED_B);
+        if (ret <= 0) {
+          goto end;
+        }
+        dtls1_stop_timer(ssl);
+        if (ssl->hit) {
+          ssl->state = SSL_ST_OK;
+        } else if (ssl->tlsext_ticket_expected) {
+          ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
+        } else {
+          ssl->state = SSL3_ST_SW_CHANGE_A;
+        }
+        ssl->init_num = 0;
+        break;
+
+      case SSL3_ST_SW_SESSION_TICKET_A:
+      case SSL3_ST_SW_SESSION_TICKET_B:
+        ret = ssl3_send_new_session_ticket(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
+        ssl->state = SSL3_ST_SW_CHANGE_A;
+        ssl->init_num = 0;
+        break;
+
+      case SSL3_ST_SW_CHANGE_A:
+      case SSL3_ST_SW_CHANGE_B:
+        ssl->session->cipher = ssl->s3->tmp.new_cipher;
+        if (!ssl->enc_method->setup_key_block(ssl)) {
+          ret = -1;
+          goto end;
+        }
+
+        ret = dtls1_send_change_cipher_spec(ssl, SSL3_ST_SW_CHANGE_A,
                                             SSL3_ST_SW_CHANGE_B);
 
         if (ret <= 0) {
           goto end;
         }
 
-        s->state = SSL3_ST_SW_FINISHED_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_FINISHED_A;
+        ssl->init_num = 0;
 
-        if (!s->enc_method->change_cipher_state(
-                s, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+        if (!ssl->enc_method->change_cipher_state(
+                ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
           goto end;
         }
-
-        dtls1_reset_seq_numbers(s, SSL3_CC_WRITE);
         break;
 
       case SSL3_ST_SW_FINISHED_A:
       case SSL3_ST_SW_FINISHED_B:
-        ret =
-            ssl3_send_finished(s, SSL3_ST_SW_FINISHED_A, SSL3_ST_SW_FINISHED_B,
-                               s->enc_method->server_finished_label,
-                               s->enc_method->server_finished_label_len);
+        ret = ssl3_send_finished(ssl, SSL3_ST_SW_FINISHED_A,
+                                 SSL3_ST_SW_FINISHED_B,
+                                 ssl->enc_method->server_finished_label,
+                                 ssl->enc_method->server_finished_label_len);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SW_FLUSH;
-        if (s->hit) {
-          s->s3->tmp.next_state = SSL3_ST_SR_FINISHED_A;
+        ssl->state = SSL3_ST_SW_FLUSH;
+        if (ssl->hit) {
+          ssl->s3->tmp.next_state = SSL3_ST_SR_CHANGE;
         } else {
-          s->s3->tmp.next_state = SSL_ST_OK;
+          ssl->s3->tmp.next_state = SSL_ST_OK;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL_ST_OK:
-        ssl3_cleanup_key_block(s);
+        ssl3_cleanup_key_block(ssl);
 
         /* remove buffering on output */
-        ssl_free_wbio_buffer(s);
+        ssl_free_wbio_buffer(ssl);
 
-        s->init_num = 0;
-        s->s3->initial_handshake_complete = 1;
+        ssl->init_num = 0;
+        ssl->s3->initial_handshake_complete = 1;
 
-        ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
+        ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
 
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         }
 
         ret = 1;
 
         /* done handshaking, next message is client hello */
-        s->d1->handshake_read_seq = 0;
+        ssl->d1->handshake_read_seq = 0;
         /* next message is server hello */
-        s->d1->handshake_write_seq = 0;
-        s->d1->next_handshake_write_seq = 0;
+        ssl->d1->handshake_write_seq = 0;
+        ssl->d1->next_handshake_write_seq = 0;
         goto end;
 
       default:
@@ -453,22 +464,22 @@
         goto end;
     }
 
-    if (!s->s3->tmp.reuse_message && !skip) {
-      if (cb != NULL && s->state != state) {
-        new_state = s->state;
-        s->state = state;
-        cb(s, SSL_CB_ACCEPT_LOOP, 1);
-        s->state = new_state;
+    if (!ssl->s3->tmp.reuse_message && !skip) {
+      if (cb != NULL && ssl->state != state) {
+        new_state = ssl->state;
+        ssl->state = state;
+        cb(ssl, SSL_CB_ACCEPT_LOOP, 1);
+        ssl->state = new_state;
       }
     }
     skip = 0;
   }
 
 end:
-  s->in_handshake--;
+  ssl->in_handshake--;
   BUF_MEM_free(buf);
   if (cb != NULL) {
-    cb(s, SSL_CB_ACCEPT_EXIT, ret);
+    cb(ssl, SSL_CB_ACCEPT_EXIT, ret);
   }
   return ret;
 }
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 520131e..5acb598 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -155,7 +155,7 @@
 #include <winsock2.h>
 #pragma warning(pop)
 #else
-#include <sys/types.h>
+#include <sys/time.h>
 #endif
 
 
@@ -183,6 +183,7 @@
 #define SSL_AES256GCM 0x00000020L
 #define SSL_CHACHA20POLY1305_OLD 0x00000040L
 #define SSL_eNULL 0x00000080L
+#define SSL_CHACHA20POLY1305 0x00000100L
 
 #define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM)
 
@@ -194,15 +195,6 @@
 /* SSL_AEAD is set for all AEADs. */
 #define SSL_AEAD 0x00000010L
 
-/* Bits for |algorithm_ssl| (protocol version). These denote the first protocol
- * version which introduced the cipher.
- *
- * TODO(davidben): These are extremely confusing, both in code and in
- * cipher rules. Try to remove them. */
-#define SSL_SSLV3 0x00000002L
-#define SSL_TLSV1 SSL_SSLV3
-#define SSL_TLSV1_2 0x00000004L
-
 /* Bits for |algorithm_prf| (handshake digest). */
 #define SSL_HANDSHAKE_MAC_DEFAULT 0x1
 #define SSL_HANDSHAKE_MAC_SHA256 0x2
@@ -212,11 +204,6 @@
  * one, update the table in ssl_cipher.c. */
 #define SSL_MAX_DIGEST 4
 
-/* Bits for |algo_strength|, cipher strength information. */
-#define SSL_MEDIUM 0x00000001L
-#define SSL_HIGH 0x00000002L
-#define SSL_FIPS 0x00000004L
-
 /* ssl_cipher_get_evp_aead sets |*out_aead| to point to the correct EVP_AEAD
  * object for |cipher| protocol version |version|. It sets |*out_mac_secret_len|
  * and |*out_fixed_iv_len| to the MAC key length and fixed IV length,
@@ -280,7 +267,7 @@
   EVP_AEAD_CTX ctx;
   /* fixed_nonce contains any bytes of the nonce that are fixed for all
    * records. */
-  uint8_t fixed_nonce[8];
+  uint8_t fixed_nonce[12];
   uint8_t fixed_nonce_len, variable_nonce_len;
   /* variable_nonce_included_in_record is non-zero if the variable nonce
    * for a record is included as a prefix before the ciphertext. */
@@ -295,6 +282,9 @@
   /* omit_version_in_ad is non-zero if the version should be omitted
    * in the AEAD's ad parameter. */
   char omit_version_in_ad;
+  /* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the
+   * variable nonce rather than prepended. */
+  char xor_fixed_nonce;
 } /* SSL_AEAD_CTX */;
 
 /* SSL_AEAD_CTX_new creates a newly-allocated |SSL_AEAD_CTX| using the supplied
@@ -517,13 +507,69 @@
 void ssl3_free_handshake_buffer(SSL *ssl);
 
 /* ssl3_free_handshake_hash releases the handshake hash. */
-void ssl3_free_handshake_hash(SSL *s);
+void ssl3_free_handshake_hash(SSL *ssl);
 
 /* ssl3_update_handshake_hash adds |in| to the handshake buffer and handshake
  * hash, whichever is enabled. It returns one on success and zero on failure. */
 int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len);
 
 
+/* ECDH curves. */
+
+#define SSL_CURVE_SECP256R1 23
+#define SSL_CURVE_SECP384R1 24
+#define SSL_CURVE_SECP521R1 25
+#define SSL_CURVE_ECDH_X25519 29
+
+/* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for
+ * TLS. */
+struct ssl_ecdh_method_st {
+  int nid;
+  uint16_t curve_id;
+  const char name[8];
+
+  /* cleanup releases state in |ctx|. */
+  void (*cleanup)(SSL_ECDH_CTX *ctx);
+
+  /* generate_keypair generates a keypair and writes the public value to
+   * |out_public_key|. It returns one on success and zero on error. */
+  int (*generate_keypair)(SSL_ECDH_CTX *ctx, CBB *out_public_key);
+
+  /* compute_secret performs a key exchange against |peer_key| and, on
+   * success, returns one and sets |*out_secret| and |*out_secret_len| to
+   * a newly-allocated buffer containing the shared secret. The caller must
+   * release this buffer with |OPENSSL_free|. Otherwise, it returns zero and
+   * sets |*out_alert| to an alert to send to the peer. */
+  int (*compute_secret)(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+                        size_t *out_secret_len, uint8_t *out_alert,
+                        const uint8_t *peer_key, size_t peer_key_len);
+} /* SSL_ECDH_METHOD */;
+
+/* ssl_nid_to_curve_id looks up the curve corresponding to |nid|. On success, it
+ * sets |*out_curve_id| to the curve ID and returns one. Otherwise, it returns
+ * zero. */
+int ssl_nid_to_curve_id(uint16_t *out_curve_id, int nid);
+
+/* SSL_ECDH_CTX_init sets up |ctx| for use with curve |curve_id|. It returns one
+ * on success and zero on error. */
+int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t curve_id);
+
+/* SSL_ECDH_CTX_init_for_dhe sets up |ctx| for use with legacy DHE-based ciphers
+ * where the server specifies a group. It takes ownership of |params|. */
+void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params);
+
+/* SSL_ECDH_CTX_cleanup releases memory associated with |ctx|. It is legal to
+ * call it in the zero state. */
+void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx);
+
+/* The following functions call the corresponding method of
+ * |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out_public_key);
+int SSL_ECDH_CTX_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+                                size_t *out_secret_len, uint8_t *out_alert,
+                                const uint8_t *peer_key, size_t peer_key_len);
+
+
 /* Transport buffers. */
 
 /* ssl_read_buffer returns a pointer to contents of the read buffer. */
@@ -688,24 +734,15 @@
 #define TLSEXT_CHANNEL_ID_SIZE 128
 
 /* Check if an SSL structure is using DTLS */
-#define SSL_IS_DTLS(s) (s->method->is_dtls)
+#define SSL_IS_DTLS(ssl) (ssl->method->is_dtls)
 /* See if we need explicit IV */
-#define SSL_USE_EXPLICIT_IV(s) \
-  (s->enc_method->enc_flags & SSL_ENC_FLAG_EXPLICIT_IV)
+#define SSL_USE_EXPLICIT_IV(ssl) \
+  (ssl->enc_method->enc_flags & SSL_ENC_FLAG_EXPLICIT_IV)
 /* See if we use signature algorithms extension and signature algorithm before
  * signatures. */
-#define SSL_USE_SIGALGS(s) (s->enc_method->enc_flags & SSL_ENC_FLAG_SIGALGS)
-
-/* SSL_kRSA <- RSA_ENC | (RSA_TMP & RSA_SIGN) |
- * 	    <- (EXPORT & (RSA_ENC | RSA_TMP) & RSA_SIGN)
- * SSL_kDH  <- DH_ENC & (RSA_ENC | RSA_SIGN | DSA_SIGN)
- * SSL_kDHE <- RSA_ENC | RSA_SIGN | DSA_SIGN
- * SSL_aRSA <- RSA_ENC | RSA_SIGN
- * SSL_aDSS <- DSA_SIGN */
+#define SSL_USE_SIGALGS(ssl) (ssl->enc_method->enc_flags & SSL_ENC_FLAG_SIGALGS)
 
 /* From RFC4492, used in encoding the curve type in ECParameters */
-#define EXPLICIT_PRIME_CURVE_TYPE 1
-#define EXPLICIT_CHAR2_CURVE_TYPE 2
 #define NAMED_CURVE_TYPE 3
 
 enum ssl_hash_message_t {
@@ -740,14 +777,6 @@
   DH *dh_tmp;
   DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
 
-  /* ecdh_nid, if not |NID_undef|, is the NID of the curve to use for ephemeral
-   * ECDH keys. If unset, |ecdh_tmp_cb| is consulted. */
-  int ecdh_nid;
-  /* ecdh_tmp_cb is a callback for selecting the curve to use for ephemeral ECDH
-   * keys. If NULL, a curve is selected automatically. See
-   * |SSL_CTX_set_tmp_ecdh_callback|. */
-  EC_KEY *(*ecdh_tmp_cb)(SSL *ssl, int is_export, int keysize);
-
   /* peer_sigalgs are the algorithm/hash pairs that the peer supports. These
    * are taken from the contents of signature algorithms extension for a server
    * or from the CertificateRequest for a client. */
@@ -785,26 +814,27 @@
 struct ssl_protocol_method_st {
   /* is_dtls is one if the protocol is DTLS and zero otherwise. */
   char is_dtls;
-  int (*ssl_new)(SSL *s);
-  void (*ssl_free)(SSL *s);
-  int (*ssl_accept)(SSL *s);
-  int (*ssl_connect)(SSL *s);
-  long (*ssl_get_message)(SSL *s, int header_state, int body_state,
+  int (*ssl_new)(SSL *ssl);
+  void (*ssl_free)(SSL *ssl);
+  int (*ssl_accept)(SSL *ssl);
+  int (*ssl_connect)(SSL *ssl);
+  long (*ssl_get_message)(SSL *ssl, int header_state, int body_state,
                           int msg_type, long max,
                           enum ssl_hash_message_t hash_message, int *ok);
-  int (*ssl_read_app_data)(SSL *s, uint8_t *buf, int len, int peek);
-  void (*ssl_read_close_notify)(SSL *s);
-  int (*ssl_write_app_data)(SSL *s, const void *buf_, int len);
-  int (*ssl_dispatch_alert)(SSL *s);
+  int (*ssl_read_app_data)(SSL *ssl, uint8_t *buf, int len, int peek);
+  int (*ssl_read_change_cipher_spec)(SSL *ssl);
+  void (*ssl_read_close_notify)(SSL *ssl);
+  int (*ssl_write_app_data)(SSL *ssl, const void *buf_, int len);
+  int (*ssl_dispatch_alert)(SSL *ssl);
   /* supports_cipher returns one if |cipher| is supported by this protocol and
    * zero otherwise. */
   int (*supports_cipher)(const SSL_CIPHER *cipher);
   /* Handshake header length */
   unsigned int hhlen;
   /* Set the handshake header */
-  int (*set_handshake_header)(SSL *s, int type, unsigned long len);
+  int (*set_handshake_header)(SSL *ssl, int type, unsigned long len);
   /* Write out handshake message */
-  int (*do_write)(SSL *s);
+  int (*do_write)(SSL *ssl);
 };
 
 /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
@@ -828,12 +858,12 @@
   unsigned int enc_flags;
 };
 
-#define SSL_HM_HEADER_LENGTH(s) s->method->hhlen
-#define ssl_handshake_start(s) \
-  (((uint8_t *)s->init_buf->data) + s->method->hhlen)
-#define ssl_set_handshake_header(s, htype, len) \
-  s->method->set_handshake_header(s, htype, len)
-#define ssl_do_write(s) s->method->do_write(s)
+#define SSL_HM_HEADER_LENGTH(ssl) ssl->method->hhlen
+#define ssl_handshake_start(ssl) \
+  (((uint8_t *)ssl->init_buf->data) + ssl->method->hhlen)
+#define ssl_set_handshake_header(ssl, htype, len) \
+  ssl->method->set_handshake_header(ssl, htype, len)
+#define ssl_do_write(ssl) ssl->method->do_write(ssl)
 
 /* Values for enc_flags */
 
@@ -933,8 +963,6 @@
 
   /* Timeout duration */
   unsigned short timeout_duration;
-
-  unsigned int change_cipher_spec_ok;
 } DTLS1_STATE;
 
 extern const SSL3_ENC_METHOD TLSv1_enc_data;
@@ -943,8 +971,8 @@
 extern const SSL3_ENC_METHOD SSLv3_enc_data;
 extern const SRTP_PROTECTION_PROFILE kSRTPProfiles[];
 
-void ssl_clear_cipher_ctx(SSL *s);
-int ssl_clear_bad_session(SSL *s);
+void ssl_clear_cipher_ctx(SSL *ssl);
+int ssl_clear_bad_session(SSL *ssl);
 CERT *ssl_cert_new(void);
 CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
@@ -967,14 +995,10 @@
     SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket,
     const struct ssl_early_callback_ctx *ctx);
 
-STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs);
-struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_dup(
-    struct ssl_cipher_preference_list_st *cipher_list);
+STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs);
 void ssl_cipher_preference_list_free(
     struct ssl_cipher_preference_list_st *cipher_list);
-struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers(
-    STACK_OF(SSL_CIPHER) *ciphers);
-struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s);
+struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl);
 
 int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain);
 int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain);
@@ -984,42 +1008,42 @@
                           int (*cb)(SSL *ssl, void *arg), void *arg);
 
 int ssl_verify_cert_chain(SSL *ssl, STACK_OF(X509) *cert_chain);
-int ssl_add_cert_chain(SSL *s, unsigned long *l);
+int ssl_add_cert_chain(SSL *ssl, unsigned long *l);
 void ssl_update_cache(SSL *ssl, int mode);
 
 /* ssl_get_compatible_server_ciphers determines the key exchange and
  * authentication cipher suite masks compatible with the server configuration
- * and current ClientHello parameters of |s|. It sets |*out_mask_k| to the key
+ * and current ClientHello parameters of |ssl|. It sets |*out_mask_k| to the key
  * exchange mask and |*out_mask_a| to the authentication mask. */
-void ssl_get_compatible_server_ciphers(SSL *s, uint32_t *out_mask_k,
+void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
                                        uint32_t *out_mask_a);
 
-STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *s);
+STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *ssl);
 int ssl_verify_alarm_type(long type);
 
 /* ssl_fill_hello_random fills a client_random or server_random field of length
  * |len|. It returns one on success and zero on failure. */
 int ssl_fill_hello_random(uint8_t *out, size_t len, int is_server);
 
-int ssl3_send_server_certificate(SSL *s);
-int ssl3_send_new_session_ticket(SSL *s);
-int ssl3_send_certificate_status(SSL *s);
-int ssl3_get_finished(SSL *s, int state_a, int state_b);
-int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b);
-int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret,
+int ssl3_send_server_certificate(SSL *ssl);
+int ssl3_send_new_session_ticket(SSL *ssl);
+int ssl3_send_certificate_status(SSL *ssl);
+int ssl3_get_finished(SSL *ssl, int state_a, int state_b);
+int ssl3_send_change_cipher_spec(SSL *ssl, int state_a, int state_b);
+int ssl3_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
              size_t secret_len, const char *label, size_t label_len,
              const uint8_t *seed1, size_t seed1_len,
              const uint8_t *seed2, size_t seed2_len);
-void ssl3_cleanup_key_block(SSL *s);
-int ssl3_do_write(SSL *s, int type);
-int ssl3_send_alert(SSL *s, int level, int desc);
-int ssl3_get_req_cert_type(SSL *s, uint8_t *p);
-long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type,
+void ssl3_cleanup_key_block(SSL *ssl);
+int ssl3_do_write(SSL *ssl, int type);
+int ssl3_send_alert(SSL *ssl, int level, int desc);
+int ssl3_get_req_cert_type(SSL *ssl, uint8_t *p);
+long ssl3_get_message(SSL *ssl, int header_state, int body_state, int msg_type,
                       long max, enum ssl_hash_message_t hash_message, int *ok);
 
 /* ssl3_hash_current_message incorporates the current handshake message into the
  * handshake hash. It returns one on success and zero on allocation failure. */
-int ssl3_hash_current_message(SSL *s);
+int ssl3_hash_current_message(SSL *ssl);
 
 /* ssl3_cert_verify_hash writes the CertificateVerify hash into the bytes
  * pointed to by |out| and writes the number of bytes to |*out_len|. |out| must
@@ -1027,29 +1051,29 @@
  * for the hash function, otherwise the hash function depends on |pkey_type|
  * and is written to |*out_md|. It returns one on success and zero on
  * failure. */
-int ssl3_cert_verify_hash(SSL *s, uint8_t *out, size_t *out_len,
+int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len,
                           const EVP_MD **out_md, int pkey_type);
 
-int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen);
+int ssl3_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
 int ssl3_supports_cipher(const SSL_CIPHER *cipher);
-int ssl3_dispatch_alert(SSL *s);
-int ssl3_expect_change_cipher_spec(SSL *s);
+int ssl3_dispatch_alert(SSL *ssl);
 int ssl3_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek);
+int ssl3_read_change_cipher_spec(SSL *ssl);
 void ssl3_read_close_notify(SSL *ssl);
-int ssl3_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek);
+int ssl3_read_bytes(SSL *ssl, int type, uint8_t *buf, int len, int peek);
 int ssl3_write_app_data(SSL *ssl, const void *buf, int len);
-int ssl3_write_bytes(SSL *s, int type, const void *buf, int len);
-int ssl3_final_finish_mac(SSL *s, const char *sender, int slen, uint8_t *p);
-int ssl3_cert_verify_mac(SSL *s, int md_nid, uint8_t *p);
-int ssl3_output_cert_chain(SSL *s);
+int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
+int ssl3_final_finish_mac(SSL *ssl, const char *sender, int slen, uint8_t *p);
+int ssl3_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p);
+int ssl3_output_cert_chain(SSL *ssl);
 const SSL_CIPHER *ssl3_choose_cipher(
     SSL *ssl, STACK_OF(SSL_CIPHER) *clnt,
     struct ssl_cipher_preference_list_st *srvr);
 
-int ssl3_new(SSL *s);
-void ssl3_free(SSL *s);
-int ssl3_accept(SSL *s);
-int ssl3_connect(SSL *s);
+int ssl3_new(SSL *ssl);
+void ssl3_free(SSL *ssl);
+int ssl3_accept(SSL *ssl);
+int ssl3_connect(SSL *ssl);
 
 /* ssl3_record_sequence_update increments the sequence number in |seq|. It
  * returns one on success and zero on wraparound. */
@@ -1057,102 +1081,102 @@
 
 int ssl3_do_change_cipher_spec(SSL *ssl);
 
-int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len);
-int ssl3_handshake_write(SSL *s);
+int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len);
+int ssl3_handshake_write(SSL *ssl);
 
-int dtls1_do_handshake_write(SSL *s, enum dtls1_use_epoch_t use_epoch);
+int dtls1_do_handshake_write(SSL *ssl, enum dtls1_use_epoch_t use_epoch);
 int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek);
+int dtls1_read_change_cipher_spec(SSL *ssl);
 void dtls1_read_close_notify(SSL *ssl);
-int dtls1_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek);
-void dtls1_set_message_header(SSL *s, uint8_t mt, unsigned long len,
+int dtls1_read_bytes(SSL *ssl, int type, uint8_t *buf, int len, int peek);
+void dtls1_set_message_header(SSL *ssl, uint8_t mt, unsigned long len,
                               unsigned short seq_num, unsigned long frag_off,
                               unsigned long frag_len);
 
-int dtls1_write_app_data(SSL *s, const void *buf, int len);
-int dtls1_write_bytes(SSL *s, int type, const void *buf, int len,
+int dtls1_write_app_data(SSL *ssl, const void *buf, int len);
+int dtls1_write_bytes(SSL *ssl, int type, const void *buf, int len,
                       enum dtls1_use_epoch_t use_epoch);
 
-int dtls1_send_change_cipher_spec(SSL *s, int a, int b);
-int dtls1_send_finished(SSL *s, int a, int b, const char *sender, int slen);
-int dtls1_read_failed(SSL *s, int code);
-int dtls1_buffer_message(SSL *s);
-int dtls1_retransmit_buffered_messages(SSL *s);
-void dtls1_clear_record_buffer(SSL *s);
+int dtls1_send_change_cipher_spec(SSL *ssl, int a, int b);
+int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
+int dtls1_read_failed(SSL *ssl, int code);
+int dtls1_buffer_message(SSL *ssl);
+int dtls1_retransmit_buffered_messages(SSL *ssl);
+void dtls1_clear_record_buffer(SSL *ssl);
 void dtls1_get_message_header(uint8_t *data, struct hm_header_st *msg_hdr);
-void dtls1_reset_seq_numbers(SSL *s, int rw);
-int dtls1_check_timeout_num(SSL *s);
-int dtls1_set_handshake_header(SSL *s, int type, unsigned long len);
-int dtls1_handshake_write(SSL *s);
+int dtls1_check_timeout_num(SSL *ssl);
+int dtls1_set_handshake_header(SSL *ssl, int type, unsigned long len);
+int dtls1_handshake_write(SSL *ssl);
 
 int dtls1_supports_cipher(const SSL_CIPHER *cipher);
-void dtls1_start_timer(SSL *s);
-void dtls1_stop_timer(SSL *s);
-int dtls1_is_timer_expired(SSL *s);
-void dtls1_double_timeout(SSL *s);
+void dtls1_start_timer(SSL *ssl);
+void dtls1_stop_timer(SSL *ssl);
+int dtls1_is_timer_expired(SSL *ssl);
+void dtls1_double_timeout(SSL *ssl);
 unsigned int dtls1_min_mtu(void);
 void dtls1_hm_fragment_free(hm_fragment *frag);
 
 /* some client-only functions */
 int ssl3_send_client_hello(SSL *ssl);
-int ssl3_get_server_hello(SSL *s);
-int ssl3_get_certificate_request(SSL *s);
-int ssl3_get_new_session_ticket(SSL *s);
-int ssl3_get_cert_status(SSL *s);
-int ssl3_get_server_done(SSL *s);
-int ssl3_send_cert_verify(SSL *s);
-int ssl3_send_client_certificate(SSL *s);
-int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey);
-int ssl3_send_client_key_exchange(SSL *s);
-int ssl3_get_server_key_exchange(SSL *s);
-int ssl3_get_server_certificate(SSL *s);
+int ssl3_get_server_hello(SSL *ssl);
+int ssl3_get_certificate_request(SSL *ssl);
+int ssl3_get_new_session_ticket(SSL *ssl);
+int ssl3_get_cert_status(SSL *ssl);
+int ssl3_get_server_done(SSL *ssl);
+int ssl3_send_cert_verify(SSL *ssl);
+int ssl3_send_client_certificate(SSL *ssl);
+int ssl_do_client_cert_cb(SSL *ssl, X509 **px509, EVP_PKEY **ppkey);
+int ssl3_send_client_key_exchange(SSL *ssl);
+int ssl3_get_server_key_exchange(SSL *ssl);
+int ssl3_get_server_certificate(SSL *ssl);
 int ssl3_send_next_proto(SSL *ssl);
 int ssl3_send_channel_id(SSL *ssl);
-int ssl3_verify_server_cert(SSL *s);
+int ssl3_verify_server_cert(SSL *ssl);
 
 /* some server-only functions */
-int ssl3_get_initial_bytes(SSL *s);
-int ssl3_get_v2_client_hello(SSL *s);
-int ssl3_get_client_hello(SSL *s);
+int ssl3_get_initial_bytes(SSL *ssl);
+int ssl3_get_v2_client_hello(SSL *ssl);
+int ssl3_get_client_hello(SSL *ssl);
 int ssl3_send_server_hello(SSL *ssl);
-int ssl3_send_server_key_exchange(SSL *s);
-int ssl3_send_certificate_request(SSL *s);
-int ssl3_send_server_done(SSL *s);
-int ssl3_get_client_certificate(SSL *s);
-int ssl3_get_client_key_exchange(SSL *s);
-int ssl3_get_cert_verify(SSL *s);
-int ssl3_get_next_proto(SSL *s);
-int ssl3_get_channel_id(SSL *s);
+int ssl3_send_server_key_exchange(SSL *ssl);
+int ssl3_send_certificate_request(SSL *ssl);
+int ssl3_send_server_done(SSL *ssl);
+int ssl3_get_client_certificate(SSL *ssl);
+int ssl3_get_client_key_exchange(SSL *ssl);
+int ssl3_get_cert_verify(SSL *ssl);
+int ssl3_get_next_proto(SSL *ssl);
+int ssl3_get_channel_id(SSL *ssl);
 
-int dtls1_new(SSL *s);
-int dtls1_accept(SSL *s);
-int dtls1_connect(SSL *s);
-void dtls1_free(SSL *s);
+int dtls1_new(SSL *ssl);
+int dtls1_accept(SSL *ssl);
+int dtls1_connect(SSL *ssl);
+void dtls1_free(SSL *ssl);
 
-long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max,
+long dtls1_get_message(SSL *ssl, int st1, int stn, int mt, long max,
                        enum ssl_hash_message_t hash_message, int *ok);
-int dtls1_dispatch_alert(SSL *s);
+int dtls1_dispatch_alert(SSL *ssl);
 
-int ssl_init_wbio_buffer(SSL *s, int push);
-void ssl_free_wbio_buffer(SSL *s);
+int ssl_init_wbio_buffer(SSL *ssl, int push);
+void ssl_free_wbio_buffer(SSL *ssl);
 
-/* tls1_prf computes the TLS PRF function for |s| as described in RFC 5246,
+/* tls1_prf computes the TLS PRF function for |ssl| as described in RFC 5246,
  * section 5 and RFC 2246 section 5. It writes |out_len| bytes to |out|, using
  * |secret| as the secret and |label| as the label. |seed1| and |seed2| are
  * concatenated to form the seed parameter. It returns one on success and zero
  * on failure. */
-int tls1_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret,
+int tls1_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
              size_t secret_len, const char *label, size_t label_len,
              const uint8_t *seed1, size_t seed1_len,
              const uint8_t *seed2, size_t seed2_len);
 
-int tls1_change_cipher_state(SSL *s, int which);
-int tls1_setup_key_block(SSL *s);
-int tls1_handshake_digest(SSL *s, uint8_t *out, size_t out_len);
-int tls1_final_finish_mac(SSL *s, const char *str, int slen, uint8_t *p);
-int tls1_cert_verify_mac(SSL *s, int md_nid, uint8_t *p);
-int tls1_generate_master_secret(SSL *s, uint8_t *out, const uint8_t *premaster,
+int tls1_change_cipher_state(SSL *ssl, int which);
+int tls1_setup_key_block(SSL *ssl);
+int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
+int tls1_final_finish_mac(SSL *ssl, const char *str, int slen, uint8_t *p);
+int tls1_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p);
+int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
                                 size_t premaster_len);
-int tls1_export_keying_material(SSL *s, uint8_t *out, size_t out_len,
+int tls1_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
                                 const char *label, size_t label_len,
                                 const uint8_t *context, size_t context_len,
                                 int use_context);
@@ -1160,24 +1184,16 @@
 int ssl3_alert_code(int code);
 
 char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx);
-int tls1_ec_curve_id2nid(uint16_t curve_id);
-int tls1_ec_nid2curve_id(uint16_t *out_curve_id, int nid);
 
-/* tls1_ec_curve_id2name returns a human-readable name for the
- * curve specified by the TLS curve id in |curve_id|. If the
- * curve is unknown, it returns NULL. */
-const char* tls1_ec_curve_id2name(uint16_t curve_id);
+/* tls1_check_curve_id returns one if |curve_id| is consistent with both our
+ * and the peer's curve preferences. Note: if called as the client, only our
+ * preferences are checked; the peer (the server) does not send preferences. */
+int tls1_check_curve_id(SSL *ssl, uint16_t curve_id);
 
-/* tls1_check_curve parses ECParameters out of |cbs|, modifying it. It
- * checks the curve is one of our preferences and writes the
- * NamedCurve value to |*out_curve_id|. It returns one on success and
- * zero on error. */
-int tls1_check_curve(SSL *s, CBS *cbs, uint16_t *out_curve_id);
-
-/* tls1_get_shared_curve returns the NID of the first preferred shared curve
- * between client and server preferences. If none can be found, it returns
- * NID_undef. */
-int tls1_get_shared_curve(SSL *s);
+/* tls1_get_shared_curve sets |*out_curve_id| to the first preferred shared
+ * curve between client and server preferences and returns one. If none may be
+ * found, it returns zero. */
+int tls1_get_shared_curve(SSL *ssl, uint16_t *out_curve_id);
 
 /* tls1_set_curves converts the array of |ncurves| NIDs pointed to by |curves|
  * into a newly allocated array of TLS curve IDs. On success, the function
@@ -1189,14 +1205,7 @@
 /* tls1_check_ec_cert returns one if |x| is an ECC certificate with curve and
  * point format compatible with the client's preferences. Otherwise it returns
  * zero. */
-int tls1_check_ec_cert(SSL *s, X509 *x);
-
-/* tls1_check_ec_tmp_key returns one if the EC temporary key is compatible with
- * client extensions and zero otherwise. */
-int tls1_check_ec_tmp_key(SSL *s);
-
-int tls1_shared_list(SSL *s, const uint8_t *l1, size_t l1len, const uint8_t *l2,
-                     size_t l2len, int nmatch);
+int tls1_check_ec_cert(SSL *ssl, X509 *x);
 
 /* ssl_add_clienthello_tlsext writes ClientHello extensions to |out|. It
  * returns one on success and zero on failure. The |header_len| argument is the
@@ -1205,8 +1214,8 @@
 int ssl_add_clienthello_tlsext(SSL *ssl, CBB *out, size_t header_len);
 
 int ssl_add_serverhello_tlsext(SSL *ssl, CBB *out);
-int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs);
-int ssl_parse_serverhello_tlsext(SSL *s, CBS *cbs);
+int ssl_parse_clienthello_tlsext(SSL *ssl, CBS *cbs);
+int ssl_parse_serverhello_tlsext(SSL *ssl, CBS *cbs);
 
 #define tlsext_tick_md EVP_sha256
 
@@ -1220,10 +1229,11 @@
                        size_t ticket_len, const uint8_t *session_id,
                        size_t session_id_len);
 
-/* tls12_get_sigandhash assembles the SignatureAndHashAlgorithm corresponding to
- * |ssl|'s private key and |md|. The two-byte value is written to |p|. It
+/* tls12_add_sigandhash assembles the SignatureAndHashAlgorithm corresponding to
+ * |ssl|'s private key and |md|. The two-byte value is written to |out|. It
  * returns one on success and zero on failure. */
-int tls12_get_sigandhash(SSL *ssl, uint8_t *p, const EVP_MD *md);
+int tls12_add_sigandhash(SSL *ssl, CBB *out, const EVP_MD *md);
+
 int tls12_get_sigid(int pkey_type);
 const EVP_MD *tls12_get_hash(uint8_t hash_alg);
 
@@ -1232,50 +1242,50 @@
  * one on success and zero on failure. */
 int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len);
 
-int tls1_record_handshake_hashes_for_channel_id(SSL *s);
+int tls1_record_handshake_hashes_for_channel_id(SSL *ssl);
 
-/* ssl_ctx_log_rsa_client_key_exchange logs |premaster| to |ctx|, if logging is
- * enabled. It returns one on success and zero on failure. The entry is
- * identified by the first 8 bytes of |encrypted_premaster|. */
-int ssl_ctx_log_rsa_client_key_exchange(SSL_CTX *ctx,
-                                        const uint8_t *encrypted_premaster,
-                                        size_t encrypted_premaster_len,
-                                        const uint8_t *premaster,
-                                        size_t premaster_len);
+/* ssl_log_rsa_client_key_exchange logs |premaster|, if logging is enabled for
+ * |ssl|. It returns one on success and zero on failure. The entry is identified
+ * by the first 8 bytes of |encrypted_premaster|. */
+int ssl_log_rsa_client_key_exchange(const SSL *ssl,
+                                    const uint8_t *encrypted_premaster,
+                                    size_t encrypted_premaster_len,
+                                    const uint8_t *premaster,
+                                    size_t premaster_len);
 
-/* ssl_ctx_log_master_secret logs |master| to |ctx|, if logging is enabled. It
+/* ssl_log_master_secret logs |master|, if logging is enabled for |ssl|. It
  * returns one on success and zero on failure. The entry is identified by
  * |client_random|. */
-int ssl_ctx_log_master_secret(SSL_CTX *ctx, const uint8_t *client_random,
-                              size_t client_random_len, const uint8_t *master,
-                              size_t master_len);
+int ssl_log_master_secret(const SSL *ssl, const uint8_t *client_random,
+                          size_t client_random_len, const uint8_t *master,
+                          size_t master_len);
 
-/* ssl3_can_false_start returns one if |s| is allowed to False Start and zero
+/* ssl3_can_false_start returns one if |ssl| is allowed to False Start and zero
  * otherwise. */
-int ssl3_can_false_start(const SSL *s);
+int ssl3_can_false_start(const SSL *ssl);
 
 /* ssl3_get_enc_method returns the SSL3_ENC_METHOD corresponding to
  * |version|. */
 const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version);
 
 /* ssl3_get_max_server_version returns the maximum SSL/TLS version number
- * supported by |s| as a server, or zero if all versions are disabled. */
-uint16_t ssl3_get_max_server_version(const SSL *s);
+ * supported by |ssl| as a server, or zero if all versions are disabled. */
+uint16_t ssl3_get_max_server_version(const SSL *ssl);
 
-/* ssl3_get_mutual_version selects the protocol version on |s| for a client
+/* ssl3_get_mutual_version selects the protocol version on |ssl| for a client
  * which advertises |client_version|. If no suitable version exists, it returns
  * zero. */
-uint16_t ssl3_get_mutual_version(SSL *s, uint16_t client_version);
+uint16_t ssl3_get_mutual_version(SSL *ssl, uint16_t client_version);
 
 /* ssl3_get_max_client_version returns the maximum protocol version configured
  * for the client. It is guaranteed that the set of allowed versions at or below
  * this maximum version is contiguous. If all versions are disabled, it returns
  * zero. */
-uint16_t ssl3_get_max_client_version(SSL *s);
+uint16_t ssl3_get_max_client_version(SSL *ssl);
 
 /* ssl3_is_version_enabled returns one if |version| is an enabled protocol
- * version for |s| and zero otherwise. */
-int ssl3_is_version_enabled(SSL *s, uint16_t version);
+ * version for |ssl| and zero otherwise. */
+int ssl3_is_version_enabled(SSL *ssl, uint16_t version);
 
 /* ssl3_version_from_wire maps |wire_version| to a protocol version. For
  * SSLv3/TLS, the version is returned as-is. For DTLS, the corresponding TLS
@@ -1284,16 +1294,16 @@
  *
  * TODO(davidben): To normalize some DTLS-specific code, move away from using
  * the wire version except at API boundaries. */
-uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version);
+uint16_t ssl3_version_from_wire(SSL *ssl, uint16_t wire_version);
 
-uint32_t ssl_get_algorithm_prf(SSL *s);
-int tls1_parse_peer_sigalgs(SSL *s, const CBS *sigalgs);
+uint32_t ssl_get_algorithm_prf(SSL *ssl);
+int tls1_parse_peer_sigalgs(SSL *ssl, const CBS *sigalgs);
 
 /* tls1_choose_signing_digest returns a digest for use with |ssl|'s private key
  * based on the peer's preferences the digests supported. */
 const EVP_MD *tls1_choose_signing_digest(SSL *ssl);
 
-size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs);
+size_t tls12_get_psigalgs(SSL *ssl, const uint8_t **psigs);
 
 /* tls12_check_peer_sigalg checks that |hash| and |signature| are consistent
  * with |pkey| and |ssl|'s sent, supported signature algorithms and, if so,
@@ -1301,6 +1311,6 @@
  * returns 0 and writes an alert into |*out_alert|. */
 int tls12_check_peer_sigalg(SSL *ssl, const EVP_MD **out_md, int *out_alert,
                             uint8_t hash, uint8_t signature, EVP_PKEY *pkey);
-void ssl_set_client_disabled(SSL *s);
+void ssl_set_client_disabled(SSL *ssl);
 
 #endif /* OPENSSL_HEADER_SSL_INTERNAL_H */
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 31e36c7..01a7e8c 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -130,127 +130,119 @@
 #include "internal.h"
 
 
-/* ssl3_do_write sends |s->init_buf| in records of type 'type'
+/* ssl3_do_write sends |ssl->init_buf| in records of type 'type'
  * (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC). It returns -1 on error, 1
  * on success or zero if the transmission is still incomplete. */
-int ssl3_do_write(SSL *s, int type) {
+int ssl3_do_write(SSL *ssl, int type) {
   int n;
 
-  n = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], s->init_num);
+  n = ssl3_write_bytes(ssl, type, &ssl->init_buf->data[ssl->init_off],
+                       ssl->init_num);
   if (n < 0) {
     return -1;
   }
 
-  if (n == s->init_num) {
-    if (s->msg_callback) {
-      s->msg_callback(1, s->version, type, s->init_buf->data,
-                      (size_t)(s->init_off + s->init_num), s,
-                      s->msg_callback_arg);
+  if (n == ssl->init_num) {
+    if (ssl->msg_callback) {
+      ssl->msg_callback(1, ssl->version, type, ssl->init_buf->data,
+                      (size_t)(ssl->init_off + ssl->init_num), ssl,
+                      ssl->msg_callback_arg);
     }
     return 1;
   }
 
-  s->init_off += n;
-  s->init_num -= n;
+  ssl->init_off += n;
+  ssl->init_num -= n;
   return 0;
 }
 
-int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen) {
+int ssl3_send_finished(SSL *ssl, int a, int b, const char *sender, int slen) {
   uint8_t *p;
   int n;
 
-  if (s->state == a) {
-    p = ssl_handshake_start(s);
+  if (ssl->state == a) {
+    p = ssl_handshake_start(ssl);
 
-    n = s->enc_method->final_finish_mac(s, sender, slen, s->s3->tmp.finish_md);
+    n = ssl->enc_method->final_finish_mac(ssl, sender, slen,
+                                          ssl->s3->tmp.finish_md);
     if (n == 0) {
       return 0;
     }
-    s->s3->tmp.finish_md_len = n;
-    memcpy(p, s->s3->tmp.finish_md, n);
+    ssl->s3->tmp.finish_md_len = n;
+    memcpy(p, ssl->s3->tmp.finish_md, n);
 
     /* Log the master secret, if logging is enabled. */
-    if (!ssl_ctx_log_master_secret(s->ctx, s->s3->client_random,
-                                   SSL3_RANDOM_SIZE, s->session->master_key,
-                                   s->session->master_key_length)) {
+    if (!ssl_log_master_secret(ssl, ssl->s3->client_random, SSL3_RANDOM_SIZE,
+                               ssl->session->master_key,
+                               ssl->session->master_key_length)) {
       return 0;
     }
 
     /* Copy the finished so we can use it for renegotiation checks */
-    if (s->server) {
+    if (ssl->server) {
       assert(n <= EVP_MAX_MD_SIZE);
-      memcpy(s->s3->previous_server_finished, s->s3->tmp.finish_md, n);
-      s->s3->previous_server_finished_len = n;
+      memcpy(ssl->s3->previous_server_finished, ssl->s3->tmp.finish_md, n);
+      ssl->s3->previous_server_finished_len = n;
     } else {
       assert(n <= EVP_MAX_MD_SIZE);
-      memcpy(s->s3->previous_client_finished, s->s3->tmp.finish_md, n);
-      s->s3->previous_client_finished_len = n;
+      memcpy(ssl->s3->previous_client_finished, ssl->s3->tmp.finish_md, n);
+      ssl->s3->previous_client_finished_len = n;
     }
 
-    if (!ssl_set_handshake_header(s, SSL3_MT_FINISHED, n)) {
+    if (!ssl_set_handshake_header(ssl, SSL3_MT_FINISHED, n)) {
       return 0;
     }
-    s->state = b;
+    ssl->state = b;
   }
 
   /* SSL3_ST_SEND_xxxxxx_HELLO_B */
-  return ssl_do_write(s);
+  return ssl_do_write(ssl);
 }
 
 /* ssl3_take_mac calculates the Finished MAC for the handshakes messages seen
  * so far. */
-static void ssl3_take_mac(SSL *s) {
+static void ssl3_take_mac(SSL *ssl) {
   const char *sender;
   int slen;
 
   /* If no new cipher setup then return immediately: other functions will set
    * the appropriate error. */
-  if (s->s3->tmp.new_cipher == NULL) {
+  if (ssl->s3->tmp.new_cipher == NULL) {
     return;
   }
 
-  if (s->state & SSL_ST_CONNECT) {
-    sender = s->enc_method->server_finished_label;
-    slen = s->enc_method->server_finished_label_len;
+  if (ssl->state & SSL_ST_CONNECT) {
+    sender = ssl->enc_method->server_finished_label;
+    slen = ssl->enc_method->server_finished_label_len;
   } else {
-    sender = s->enc_method->client_finished_label;
-    slen = s->enc_method->client_finished_label_len;
+    sender = ssl->enc_method->client_finished_label;
+    slen = ssl->enc_method->client_finished_label_len;
   }
 
-  s->s3->tmp.peer_finish_md_len = s->enc_method->final_finish_mac(
-      s, sender, slen, s->s3->tmp.peer_finish_md);
+  ssl->s3->tmp.peer_finish_md_len = ssl->enc_method->final_finish_mac(
+      ssl, sender, slen, ssl->s3->tmp.peer_finish_md);
 }
 
-int ssl3_get_finished(SSL *s, int a, int b) {
+int ssl3_get_finished(SSL *ssl, int a, int b) {
   int al, finished_len, ok;
   long message_len;
   uint8_t *p;
 
-  message_len =
-      s->method->ssl_get_message(s, a, b, SSL3_MT_FINISHED, EVP_MAX_MD_SIZE,
-                                 ssl_dont_hash_message, &ok);
+  message_len = ssl->method->ssl_get_message(
+      ssl, a, b, SSL3_MT_FINISHED, EVP_MAX_MD_SIZE, ssl_dont_hash_message, &ok);
 
   if (!ok) {
     return message_len;
   }
 
   /* Snapshot the finished hash before incorporating the new message. */
-  ssl3_take_mac(s);
-  if (!ssl3_hash_current_message(s)) {
+  ssl3_take_mac(ssl);
+  if (!ssl3_hash_current_message(ssl)) {
     goto err;
   }
 
-  /* If this occurs, we have missed a message.
-   * TODO(davidben): Is this check now redundant with SSL3_FLAGS_EXPECT_CCS? */
-  if (!s->s3->change_cipher_spec) {
-    al = SSL_AD_UNEXPECTED_MESSAGE;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_A_FIN_BEFORE_A_CCS);
-    goto f_err;
-  }
-  s->s3->change_cipher_spec = 0;
-
-  p = s->init_msg;
-  finished_len = s->s3->tmp.peer_finish_md_len;
+  p = ssl->init_msg;
+  finished_len = ssl->s3->tmp.peer_finish_md_len;
 
   if (finished_len != message_len) {
     al = SSL_AD_DECODE_ERROR;
@@ -258,134 +250,137 @@
     goto f_err;
   }
 
-  if (CRYPTO_memcmp(p, s->s3->tmp.peer_finish_md, finished_len) != 0) {
+  if (CRYPTO_memcmp(p, ssl->s3->tmp.peer_finish_md, finished_len) != 0) {
     al = SSL_AD_DECRYPT_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
     goto f_err;
   }
 
   /* Copy the finished so we can use it for renegotiation checks */
-  if (s->server) {
+  if (ssl->server) {
     assert(finished_len <= EVP_MAX_MD_SIZE);
-    memcpy(s->s3->previous_client_finished, s->s3->tmp.peer_finish_md, finished_len);
-    s->s3->previous_client_finished_len = finished_len;
+    memcpy(ssl->s3->previous_client_finished, ssl->s3->tmp.peer_finish_md,
+           finished_len);
+    ssl->s3->previous_client_finished_len = finished_len;
   } else {
     assert(finished_len <= EVP_MAX_MD_SIZE);
-    memcpy(s->s3->previous_server_finished, s->s3->tmp.peer_finish_md, finished_len);
-    s->s3->previous_server_finished_len = finished_len;
+    memcpy(ssl->s3->previous_server_finished, ssl->s3->tmp.peer_finish_md,
+           finished_len);
+    ssl->s3->previous_server_finished_len = finished_len;
   }
 
   return 1;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   return 0;
 }
 
 /* for these 2 messages, we need to
- * ssl->enc_read_ctx			re-init
- * ssl->s3->read_sequence		zero
- * ssl->s3->read_mac_secret		re-init
- * ssl->session->read_sym_enc		assign
- * ssl->session->read_compression	assign
- * ssl->session->read_hash		assign */
-int ssl3_send_change_cipher_spec(SSL *s, int a, int b) {
-  if (s->state == a) {
-    *((uint8_t *)s->init_buf->data) = SSL3_MT_CCS;
-    s->init_num = 1;
-    s->init_off = 0;
+ * ssl->enc_read_ctx      re-init
+ * ssl->s3->read_sequence   zero
+ * ssl->s3->read_mac_secret   re-init
+ * ssl->session->read_sym_enc   assign
+ * ssl->session->read_compression assign
+ * ssl->session->read_hash    assign */
+int ssl3_send_change_cipher_spec(SSL *ssl, int a, int b) {
+  if (ssl->state == a) {
+    *((uint8_t *)ssl->init_buf->data) = SSL3_MT_CCS;
+    ssl->init_num = 1;
+    ssl->init_off = 0;
 
-    s->state = b;
+    ssl->state = b;
   }
 
   /* SSL3_ST_CW_CHANGE_B */
-  return ssl3_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC);
+  return ssl3_do_write(ssl, SSL3_RT_CHANGE_CIPHER_SPEC);
 }
 
-int ssl3_output_cert_chain(SSL *s) {
+int ssl3_output_cert_chain(SSL *ssl) {
   uint8_t *p;
-  unsigned long l = 3 + SSL_HM_HEADER_LENGTH(s);
+  unsigned long l = 3 + SSL_HM_HEADER_LENGTH(ssl);
 
-  if (!ssl_add_cert_chain(s, &l)) {
+  if (!ssl_add_cert_chain(ssl, &l)) {
     return 0;
   }
 
-  l -= 3 + SSL_HM_HEADER_LENGTH(s);
-  p = ssl_handshake_start(s);
+  l -= 3 + SSL_HM_HEADER_LENGTH(ssl);
+  p = ssl_handshake_start(ssl);
   l2n3(l, p);
   l += 3;
-  return ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE, l);
+  return ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, l);
 }
 
 /* Obtain handshake message of message type |msg_type| (any if |msg_type| == -1),
  * maximum acceptable body length |max|. The first four bytes (msg_type and
- * length) are read in state |header_state|, the body is read in state |body_state|. */
-long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type,
+ * length) are read in state |header_state|, the body is read in state
+ * |body_state|. */
+long ssl3_get_message(SSL *ssl, int header_state, int body_state, int msg_type,
                       long max, enum ssl_hash_message_t hash_message, int *ok) {
   uint8_t *p;
   unsigned long l;
   long n;
   int al;
 
-  if (s->s3->tmp.reuse_message) {
+  if (ssl->s3->tmp.reuse_message) {
     /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
      * ssl_dont_hash_message would have to have been applied to the previous
      * call. */
     assert(hash_message == ssl_hash_message);
-    s->s3->tmp.reuse_message = 0;
-    if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) {
+    ssl->s3->tmp.reuse_message = 0;
+    if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
       al = SSL_AD_UNEXPECTED_MESSAGE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
       goto f_err;
     }
     *ok = 1;
-    s->state = body_state;
-    s->init_msg = (uint8_t *)s->init_buf->data + 4;
-    s->init_num = (int)s->s3->tmp.message_size;
-    return s->init_num;
+    ssl->state = body_state;
+    ssl->init_msg = (uint8_t *)ssl->init_buf->data + 4;
+    ssl->init_num = (int)ssl->s3->tmp.message_size;
+    return ssl->init_num;
   }
 
-  p = (uint8_t *)s->init_buf->data;
+  p = (uint8_t *)ssl->init_buf->data;
 
-  if (s->state == header_state) {
-    assert(s->init_num < 4);
+  if (ssl->state == header_state) {
+    assert(ssl->init_num < 4);
 
     for (;;) {
-      while (s->init_num < 4) {
-        int bytes_read = ssl3_read_bytes(s, SSL3_RT_HANDSHAKE, &p[s->init_num],
-                                         4 - s->init_num, 0);
+      while (ssl->init_num < 4) {
+        int bytes_read = ssl3_read_bytes(
+            ssl, SSL3_RT_HANDSHAKE, &p[ssl->init_num], 4 - ssl->init_num, 0);
         if (bytes_read <= 0) {
           *ok = 0;
           return bytes_read;
         }
-        s->init_num += bytes_read;
+        ssl->init_num += bytes_read;
       }
 
       static const uint8_t kHelloRequest[4] = {SSL3_MT_HELLO_REQUEST, 0, 0, 0};
-      if (s->server || memcmp(p, kHelloRequest, sizeof(kHelloRequest)) != 0) {
+      if (ssl->server || memcmp(p, kHelloRequest, sizeof(kHelloRequest)) != 0) {
         break;
       }
 
       /* The server may always send 'Hello Request' messages -- we are doing
        * a handshake anyway now, so ignore them if their format is correct.
        * Does not count for 'Finished' MAC. */
-      s->init_num = 0;
+      ssl->init_num = 0;
 
-      if (s->msg_callback) {
-        s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, p, 4, s,
-                        s->msg_callback_arg);
+      if (ssl->msg_callback) {
+        ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, p, 4, ssl,
+                        ssl->msg_callback_arg);
       }
     }
 
-    /* s->init_num == 4 */
+    /* ssl->init_num == 4 */
 
     if (msg_type >= 0 && *p != msg_type) {
       al = SSL_AD_UNEXPECTED_MESSAGE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
       goto f_err;
     }
-    s->s3->tmp.message_type = *(p++);
+    ssl->s3->tmp.message_type = *(p++);
 
     n2l3(p, l);
     if (l > (unsigned long)max) {
@@ -394,57 +389,57 @@
       goto f_err;
     }
 
-    if (l && !BUF_MEM_grow_clean(s->init_buf, l + 4)) {
+    if (l && !BUF_MEM_grow_clean(ssl->init_buf, l + 4)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
       goto err;
     }
-    s->s3->tmp.message_size = l;
-    s->state = body_state;
+    ssl->s3->tmp.message_size = l;
+    ssl->state = body_state;
 
-    s->init_msg = (uint8_t *)s->init_buf->data + 4;
-    s->init_num = 0;
+    ssl->init_msg = (uint8_t *)ssl->init_buf->data + 4;
+    ssl->init_num = 0;
   }
 
   /* next state (body_state) */
-  p = s->init_msg;
-  n = s->s3->tmp.message_size - s->init_num;
+  p = ssl->init_msg;
+  n = ssl->s3->tmp.message_size - ssl->init_num;
   while (n > 0) {
-    int bytes_read = ssl3_read_bytes(s, SSL3_RT_HANDSHAKE, &p[s->init_num], n,
-                                     0);
+    int bytes_read =
+        ssl3_read_bytes(ssl, SSL3_RT_HANDSHAKE, &p[ssl->init_num], n, 0);
     if (bytes_read <= 0) {
-      s->rwstate = SSL_READING;
+      ssl->rwstate = SSL_READING;
       *ok = 0;
       return bytes_read;
     }
-    s->init_num += bytes_read;
+    ssl->init_num += bytes_read;
     n -= bytes_read;
   }
 
   /* Feed this message into MAC computation. */
-  if (hash_message == ssl_hash_message && !ssl3_hash_current_message(s)) {
+  if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
     goto err;
   }
-  if (s->msg_callback) {
-    s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data,
-                    (size_t)s->init_num + 4, s, s->msg_callback_arg);
+  if (ssl->msg_callback) {
+    ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
+                    (size_t)ssl->init_num + 4, ssl, ssl->msg_callback_arg);
   }
   *ok = 1;
-  return s->init_num;
+  return ssl->init_num;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 
 err:
   *ok = 0;
   return -1;
 }
 
-int ssl3_hash_current_message(SSL *s) {
+int ssl3_hash_current_message(SSL *ssl) {
   /* The handshake header (different size between DTLS and TLS) is included in
    * the hash. */
-  size_t header_len = s->init_msg - (uint8_t *)s->init_buf->data;
-  return ssl3_update_handshake_hash(s, (uint8_t *)s->init_buf->data,
-                                    s->init_num + header_len);
+  size_t header_len = ssl->init_msg - (uint8_t *)ssl->init_buf->data;
+  return ssl3_update_handshake_hash(ssl, (uint8_t *)ssl->init_buf->data,
+                                    ssl->init_num + header_len);
 }
 
 /* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that
@@ -452,19 +447,19 @@
 OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE > MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH,
                        combined_tls_hash_fits_in_max);
 
-int ssl3_cert_verify_hash(SSL *s, uint8_t *out, size_t *out_len,
+int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len,
                           const EVP_MD **out_md, int pkey_type) {
   /* For TLS v1.2 send signature algorithm and signature using
    * agreed digest and cached handshake records. Otherwise, use
    * SHA1 or MD5 + SHA1 depending on key type.  */
-  if (SSL_USE_SIGALGS(s)) {
+  if (SSL_USE_SIGALGS(ssl)) {
     EVP_MD_CTX mctx;
     unsigned len;
 
     EVP_MD_CTX_init(&mctx);
     if (!EVP_DigestInit_ex(&mctx, *out_md, NULL) ||
-        !EVP_DigestUpdate(&mctx, s->s3->handshake_buffer->data,
-                          s->s3->handshake_buffer->length) ||
+        !EVP_DigestUpdate(&mctx, ssl->s3->handshake_buffer->data,
+                          ssl->s3->handshake_buffer->length) ||
         !EVP_DigestFinal(&mctx, out, &len)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB);
       EVP_MD_CTX_cleanup(&mctx);
@@ -472,15 +467,15 @@
     }
     *out_len = len;
   } else if (pkey_type == EVP_PKEY_RSA) {
-    if (s->enc_method->cert_verify_mac(s, NID_md5, out) == 0 ||
-        s->enc_method->cert_verify_mac(s, NID_sha1, out + MD5_DIGEST_LENGTH) ==
-            0) {
+    if (ssl->enc_method->cert_verify_mac(ssl, NID_md5, out) == 0 ||
+        ssl->enc_method->cert_verify_mac(ssl, NID_sha1,
+                                         out + MD5_DIGEST_LENGTH) == 0) {
       return 0;
     }
     *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH;
     *out_md = EVP_md5_sha1();
   } else if (pkey_type == EVP_PKEY_EC) {
-    if (s->enc_method->cert_verify_mac(s, NID_sha1, out) == 0) {
+    if (ssl->enc_method->cert_verify_mac(ssl, NID_sha1, out) == 0) {
       return 0;
     }
     *out_len = SHA_DIGEST_LENGTH;
diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c
index 843403b..5f68037 100644
--- a/src/ssl/s3_clnt.c
+++ b/src/ssl/s3_clnt.c
@@ -172,37 +172,37 @@
 #include "../crypto/dh/internal.h"
 
 
-int ssl3_connect(SSL *s) {
+int ssl3_connect(SSL *ssl) {
   BUF_MEM *buf = NULL;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
   int ret = -1;
   int new_state, state, skip = 0;
 
-  assert(s->handshake_func == ssl3_connect);
-  assert(!s->server);
-  assert(!SSL_IS_DTLS(s));
+  assert(ssl->handshake_func == ssl3_connect);
+  assert(!ssl->server);
+  assert(!SSL_IS_DTLS(ssl));
 
   ERR_clear_error();
   ERR_clear_system_error();
 
-  if (s->info_callback != NULL) {
-    cb = s->info_callback;
-  } else if (s->ctx->info_callback != NULL) {
-    cb = s->ctx->info_callback;
+  if (ssl->info_callback != NULL) {
+    cb = ssl->info_callback;
+  } else if (ssl->ctx->info_callback != NULL) {
+    cb = ssl->ctx->info_callback;
   }
 
-  s->in_handshake++;
+  ssl->in_handshake++;
 
   for (;;) {
-    state = s->state;
+    state = ssl->state;
 
-    switch (s->state) {
+    switch (ssl->state) {
       case SSL_ST_CONNECT:
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_START, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
         }
 
-        if (s->init_buf == NULL) {
+        if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
           if (buf == NULL ||
               !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
@@ -210,124 +210,124 @@
             goto end;
           }
 
-          s->init_buf = buf;
+          ssl->init_buf = buf;
           buf = NULL;
         }
 
-        if (!ssl_init_wbio_buffer(s, 0)) {
+        if (!ssl_init_wbio_buffer(ssl, 0)) {
           ret = -1;
           goto end;
         }
 
         /* don't push the buffering BIO quite yet */
 
-        if (!ssl3_init_handshake_buffer(s)) {
+        if (!ssl3_init_handshake_buffer(ssl)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
           ret = -1;
           goto end;
         }
 
-        s->state = SSL3_ST_CW_CLNT_HELLO_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CLNT_HELLO_A:
       case SSL3_ST_CW_CLNT_HELLO_B:
-        s->shutdown = 0;
-        ret = ssl3_send_client_hello(s);
+        ssl->shutdown = 0;
+        ret = ssl3_send_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CR_SRVR_HELLO_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
+        ssl->init_num = 0;
 
         /* turn on buffering for the next lot of output */
-        if (s->bbio != s->wbio) {
-          s->wbio = BIO_push(s->bbio, s->wbio);
+        if (ssl->bbio != ssl->wbio) {
+          ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
         }
 
         break;
 
       case SSL3_ST_CR_SRVR_HELLO_A:
       case SSL3_ST_CR_SRVR_HELLO_B:
-        ret = ssl3_get_server_hello(s);
+        ret = ssl3_get_server_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        if (s->hit) {
-          s->state = SSL3_ST_CR_CHANGE;
-          if (s->tlsext_ticket_expected) {
+        if (ssl->hit) {
+          ssl->state = SSL3_ST_CR_CHANGE;
+          if (ssl->tlsext_ticket_expected) {
             /* receive renewed session ticket */
-            s->state = SSL3_ST_CR_SESSION_TICKET_A;
+            ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
           }
         } else {
-          s->state = SSL3_ST_CR_CERT_A;
+          ssl->state = SSL3_ST_CR_CERT_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_A:
       case SSL3_ST_CR_CERT_B:
-        if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-          ret = ssl3_get_server_certificate(s);
+        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+          ret = ssl3_get_server_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
-          if (s->s3->tmp.certificate_status_expected) {
-            s->state = SSL3_ST_CR_CERT_STATUS_A;
+          if (ssl->s3->tmp.certificate_status_expected) {
+            ssl->state = SSL3_ST_CR_CERT_STATUS_A;
           } else {
-            s->state = SSL3_ST_VERIFY_SERVER_CERT;
+            ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
           }
         } else {
           skip = 1;
-          s->state = SSL3_ST_CR_KEY_EXCH_A;
+          ssl->state = SSL3_ST_CR_KEY_EXCH_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_VERIFY_SERVER_CERT:
-        ret = ssl3_verify_server_cert(s);
+        ret = ssl3_verify_server_cert(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        s->state = SSL3_ST_CR_KEY_EXCH_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_KEY_EXCH_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_KEY_EXCH_A:
       case SSL3_ST_CR_KEY_EXCH_B:
-        ret = ssl3_get_server_key_exchange(s);
+        ret = ssl3_get_server_key_exchange(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CR_CERT_REQ_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_CERT_REQ_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_REQ_A:
       case SSL3_ST_CR_CERT_REQ_B:
-        ret = ssl3_get_certificate_request(s);
+        ret = ssl3_get_certificate_request(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CR_SRVR_DONE_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_SRVR_DONE_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_SRVR_DONE_A:
       case SSL3_ST_CR_SRVR_DONE_B:
-        ret = ssl3_get_server_done(s);
+        ret = ssl3_get_server_done(ssl);
         if (ret <= 0) {
           goto end;
         }
-        if (s->s3->tmp.cert_req) {
-          s->state = SSL3_ST_CW_CERT_A;
+        if (ssl->s3->tmp.cert_req) {
+          ssl->state = SSL3_ST_CW_CERT_A;
         } else {
-          s->state = SSL3_ST_CW_KEY_EXCH_A;
+          ssl->state = SSL3_ST_CW_KEY_EXCH_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
 
         break;
 
@@ -335,65 +335,63 @@
       case SSL3_ST_CW_CERT_B:
       case SSL3_ST_CW_CERT_C:
       case SSL3_ST_CW_CERT_D:
-        ret = ssl3_send_client_certificate(s);
+        ret = ssl3_send_client_certificate(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CW_KEY_EXCH_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CW_KEY_EXCH_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_KEY_EXCH_A:
       case SSL3_ST_CW_KEY_EXCH_B:
-        ret = ssl3_send_client_key_exchange(s);
+        ret = ssl3_send_client_key_exchange(ssl);
         if (ret <= 0) {
           goto end;
         }
         /* For TLS, cert_req is set to 2, so a cert chain
          * of nothing is sent, but no verify packet is sent */
-        if (s->s3->tmp.cert_req == 1) {
-          s->state = SSL3_ST_CW_CERT_VRFY_A;
+        if (ssl->s3->tmp.cert_req == 1) {
+          ssl->state = SSL3_ST_CW_CERT_VRFY_A;
         } else {
-          s->state = SSL3_ST_CW_CHANGE_A;
-          s->s3->change_cipher_spec = 0;
+          ssl->state = SSL3_ST_CW_CHANGE_A;
         }
 
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CERT_VRFY_A:
       case SSL3_ST_CW_CERT_VRFY_B:
       case SSL3_ST_CW_CERT_VRFY_C:
-        ret = ssl3_send_cert_verify(s);
+        ret = ssl3_send_cert_verify(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CW_CHANGE_A;
-        s->init_num = 0;
-        s->s3->change_cipher_spec = 0;
+        ssl->state = SSL3_ST_CW_CHANGE_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CHANGE_A:
       case SSL3_ST_CW_CHANGE_B:
-        ret = ssl3_send_change_cipher_spec(s, SSL3_ST_CW_CHANGE_A,
+        ret = ssl3_send_change_cipher_spec(ssl, SSL3_ST_CW_CHANGE_A,
                                            SSL3_ST_CW_CHANGE_B);
         if (ret <= 0) {
           goto end;
         }
 
-        s->state = SSL3_ST_CW_FINISHED_A;
-        if (s->s3->tlsext_channel_id_valid) {
-          s->state = SSL3_ST_CW_CHANNEL_ID_A;
+        ssl->state = SSL3_ST_CW_FINISHED_A;
+        if (ssl->s3->tlsext_channel_id_valid) {
+          ssl->state = SSL3_ST_CW_CHANNEL_ID_A;
         }
-        if (s->s3->next_proto_neg_seen) {
-          s->state = SSL3_ST_CW_NEXT_PROTO_A;
+        if (ssl->s3->next_proto_neg_seen) {
+          ssl->state = SSL3_ST_CW_NEXT_PROTO_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
 
-        s->session->cipher = s->s3->tmp.new_cipher;
-        if (!s->enc_method->setup_key_block(s) ||
-            !s->enc_method->change_cipher_state(
-                s, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+        ssl->session->cipher = ssl->s3->tmp.new_cipher;
+        if (!ssl->enc_method->setup_key_block(ssl) ||
+            !ssl->enc_method->change_cipher_state(
+                ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -402,162 +400,165 @@
 
       case SSL3_ST_CW_NEXT_PROTO_A:
       case SSL3_ST_CW_NEXT_PROTO_B:
-        ret = ssl3_send_next_proto(s);
+        ret = ssl3_send_next_proto(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        if (s->s3->tlsext_channel_id_valid) {
-          s->state = SSL3_ST_CW_CHANNEL_ID_A;
+        if (ssl->s3->tlsext_channel_id_valid) {
+          ssl->state = SSL3_ST_CW_CHANNEL_ID_A;
         } else {
-          s->state = SSL3_ST_CW_FINISHED_A;
+          ssl->state = SSL3_ST_CW_FINISHED_A;
         }
         break;
 
       case SSL3_ST_CW_CHANNEL_ID_A:
       case SSL3_ST_CW_CHANNEL_ID_B:
-        ret = ssl3_send_channel_id(s);
+        ret = ssl3_send_channel_id(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CW_FINISHED_A;
+        ssl->state = SSL3_ST_CW_FINISHED_A;
         break;
 
       case SSL3_ST_CW_FINISHED_A:
       case SSL3_ST_CW_FINISHED_B:
-        ret =
-            ssl3_send_finished(s, SSL3_ST_CW_FINISHED_A, SSL3_ST_CW_FINISHED_B,
-                               s->enc_method->client_finished_label,
-                               s->enc_method->client_finished_label_len);
+        ret = ssl3_send_finished(ssl, SSL3_ST_CW_FINISHED_A,
+                                 SSL3_ST_CW_FINISHED_B,
+                                 ssl->enc_method->client_finished_label,
+                                 ssl->enc_method->client_finished_label_len);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CW_FLUSH;
+        ssl->state = SSL3_ST_CW_FLUSH;
 
-        if (s->hit) {
-          s->s3->tmp.next_state = SSL_ST_OK;
+        if (ssl->hit) {
+          ssl->s3->tmp.next_state = SSL_ST_OK;
         } else {
           /* This is a non-resumption handshake. If it involves ChannelID, then
            * record the handshake hashes at this point in the session so that
            * any resumption of this session with ChannelID can sign those
            * hashes. */
-          ret = tls1_record_handshake_hashes_for_channel_id(s);
+          ret = tls1_record_handshake_hashes_for_channel_id(ssl);
           if (ret <= 0) {
             goto end;
           }
-          if ((SSL_get_mode(s) & SSL_MODE_ENABLE_FALSE_START) &&
-              ssl3_can_false_start(s) &&
+          if ((SSL_get_mode(ssl) & SSL_MODE_ENABLE_FALSE_START) &&
+              ssl3_can_false_start(ssl) &&
               /* No False Start on renegotiation (would complicate the state
                * machine). */
-              !s->s3->initial_handshake_complete) {
-            s->s3->tmp.next_state = SSL3_ST_FALSE_START;
+              !ssl->s3->initial_handshake_complete) {
+            ssl->s3->tmp.next_state = SSL3_ST_FALSE_START;
           } else {
             /* Allow NewSessionTicket if ticket expected */
-            if (s->tlsext_ticket_expected) {
-              s->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A;
+            if (ssl->tlsext_ticket_expected) {
+              ssl->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A;
             } else {
-              s->s3->tmp.next_state = SSL3_ST_CR_CHANGE;
+              ssl->s3->tmp.next_state = SSL3_ST_CR_CHANGE;
             }
           }
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_SESSION_TICKET_A:
       case SSL3_ST_CR_SESSION_TICKET_B:
-        ret = ssl3_get_new_session_ticket(s);
+        ret = ssl3_get_new_session_ticket(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_CR_CHANGE;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_CR_CHANGE;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_STATUS_A:
       case SSL3_ST_CR_CERT_STATUS_B:
-        ret = ssl3_get_cert_status(s);
+        ret = ssl3_get_cert_status(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_VERIFY_SERVER_CERT;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CHANGE:
-        /* At this point, the next message must be entirely behind a
-         * ChangeCipherSpec. */
-        if (!ssl3_expect_change_cipher_spec(s)) {
+        ret = ssl->method->ssl_read_change_cipher_spec(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
+
+        if (!ssl3_do_change_cipher_spec(ssl)) {
           ret = -1;
           goto end;
         }
-        s->state = SSL3_ST_CR_FINISHED_A;
+        ssl->state = SSL3_ST_CR_FINISHED_A;
         break;
 
       case SSL3_ST_CR_FINISHED_A:
       case SSL3_ST_CR_FINISHED_B:
-        ret =
-            ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B);
+        ret = ssl3_get_finished(ssl, SSL3_ST_CR_FINISHED_A,
+                                SSL3_ST_CR_FINISHED_B);
         if (ret <= 0) {
           goto end;
         }
 
-        if (s->hit) {
-          s->state = SSL3_ST_CW_CHANGE_A;
+        if (ssl->hit) {
+          ssl->state = SSL3_ST_CW_CHANGE_A;
         } else {
-          s->state = SSL_ST_OK;
+          ssl->state = SSL_ST_OK;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_FLUSH:
-        s->rwstate = SSL_WRITING;
-        if (BIO_flush(s->wbio) <= 0) {
+        ssl->rwstate = SSL_WRITING;
+        if (BIO_flush(ssl->wbio) <= 0) {
           ret = -1;
           goto end;
         }
-        s->rwstate = SSL_NOTHING;
-        s->state = s->s3->tmp.next_state;
+        ssl->rwstate = SSL_NOTHING;
+        ssl->state = ssl->s3->tmp.next_state;
         break;
 
       case SSL3_ST_FALSE_START:
         /* Allow NewSessionTicket if ticket expected */
-        if (s->tlsext_ticket_expected) {
-          s->state = SSL3_ST_CR_SESSION_TICKET_A;
+        if (ssl->tlsext_ticket_expected) {
+          ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
         } else {
-          s->state = SSL3_ST_CR_CHANGE;
+          ssl->state = SSL3_ST_CR_CHANGE;
         }
-        s->s3->tmp.in_false_start = 1;
+        ssl->s3->tmp.in_false_start = 1;
 
-        ssl_free_wbio_buffer(s);
+        ssl_free_wbio_buffer(ssl);
         ret = 1;
         goto end;
 
       case SSL_ST_OK:
         /* clean a few things up */
-        ssl3_cleanup_key_block(s);
+        ssl3_cleanup_key_block(ssl);
 
-        BUF_MEM_free(s->init_buf);
-        s->init_buf = NULL;
+        BUF_MEM_free(ssl->init_buf);
+        ssl->init_buf = NULL;
 
         /* Remove write buffering now. */
-        ssl_free_wbio_buffer(s);
+        ssl_free_wbio_buffer(ssl);
 
-        const int is_initial_handshake = !s->s3->initial_handshake_complete;
+        const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
 
-        s->init_num = 0;
-        s->s3->tmp.in_false_start = 0;
-        s->s3->initial_handshake_complete = 1;
+        ssl->init_num = 0;
+        ssl->s3->tmp.in_false_start = 0;
+        ssl->s3->initial_handshake_complete = 1;
 
         if (is_initial_handshake) {
           /* Renegotiations do not participate in session resumption. */
-          ssl_update_cache(s, SSL_SESS_CACHE_CLIENT);
+          ssl_update_cache(ssl, SSL_SESS_CACHE_CLIENT);
         }
 
         ret = 1;
-        /* s->server=0; */
+        /* ssl->server=0; */
 
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         }
 
         goto end;
@@ -568,22 +569,22 @@
         goto end;
     }
 
-    if (!s->s3->tmp.reuse_message && !skip) {
-      if (cb != NULL && s->state != state) {
-        new_state = s->state;
-        s->state = state;
-        cb(s, SSL_CB_CONNECT_LOOP, 1);
-        s->state = new_state;
+    if (!ssl->s3->tmp.reuse_message && !skip) {
+      if (cb != NULL && ssl->state != state) {
+        new_state = ssl->state;
+        ssl->state = state;
+        cb(ssl, SSL_CB_CONNECT_LOOP, 1);
+        ssl->state = new_state;
       }
     }
     skip = 0;
   }
 
 end:
-  s->in_handshake--;
+  ssl->in_handshake--;
   BUF_MEM_free(buf);
   if (cb != NULL) {
-    cb(s, SSL_CB_CONNECT_EXIT, ret);
+    cb(ssl, SSL_CB_CONNECT_EXIT, ret);
   }
   return ret;
 }
@@ -735,17 +736,17 @@
   return -1;
 }
 
-int ssl3_get_server_hello(SSL *s) {
+int ssl3_get_server_hello(SSL *ssl) {
   STACK_OF(SSL_CIPHER) *sk;
   const SSL_CIPHER *c;
-  CERT *ct = s->cert;
+  CERT *ct = ssl->cert;
   int al = SSL_AD_INTERNAL_ERROR, ok;
   long n;
   CBS server_hello, server_random, session_id;
   uint16_t server_version, cipher_suite;
   uint8_t compression_method;
 
-  n = s->method->ssl_get_message(s, SSL3_ST_CR_SRVR_HELLO_A,
+  n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_SRVR_HELLO_A,
                                  SSL3_ST_CR_SRVR_HELLO_B, SSL3_MT_SERVER_HELLO,
                                  20000, /* ?? */
                                  ssl_hash_message, &ok);
@@ -765,7 +766,7 @@
     return n;
   }
 
-  CBS_init(&server_hello, s->init_msg, n);
+  CBS_init(&server_hello, ssl->init_msg, n);
 
   if (!CBS_get_u16(&server_hello, &server_version) ||
       !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
@@ -778,55 +779,56 @@
     goto f_err;
   }
 
-  assert(s->s3->have_version == s->s3->initial_handshake_complete);
-  if (!s->s3->have_version) {
-    if (!ssl3_is_version_enabled(s, server_version)) {
+  assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete);
+  if (!ssl->s3->have_version) {
+    if (!ssl3_is_version_enabled(ssl, server_version)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
-      s->version = server_version;
+      ssl->version = server_version;
       /* Mark the version as fixed so the record-layer version is not clamped
        * to TLS 1.0. */
-      s->s3->have_version = 1;
+      ssl->s3->have_version = 1;
       al = SSL_AD_PROTOCOL_VERSION;
       goto f_err;
     }
-    s->version = server_version;
-    s->enc_method = ssl3_get_enc_method(server_version);
-    assert(s->enc_method != NULL);
-    /* At this point, the connection's version is known and s->version is
+    ssl->version = server_version;
+    ssl->enc_method = ssl3_get_enc_method(server_version);
+    assert(ssl->enc_method != NULL);
+    /* At this point, the connection's version is known and ssl->version is
      * fixed. Begin enforcing the record-layer version. */
-    s->s3->have_version = 1;
-  } else if (server_version != s->version) {
+    ssl->s3->have_version = 1;
+  } else if (server_version != ssl->version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
     al = SSL_AD_PROTOCOL_VERSION;
     goto f_err;
   }
 
   /* Copy over the server random. */
-  memcpy(s->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE);
+  memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE);
 
-  assert(s->session == NULL || s->session->session_id_length > 0);
-  if (!s->s3->initial_handshake_complete && s->session != NULL &&
-      CBS_mem_equal(&session_id, s->session->session_id,
-                    s->session->session_id_length)) {
-    if (s->sid_ctx_length != s->session->sid_ctx_length ||
-        memcmp(s->session->sid_ctx, s->sid_ctx, s->sid_ctx_length)) {
+  assert(ssl->session == NULL || ssl->session->session_id_length > 0);
+  if (!ssl->s3->initial_handshake_complete && ssl->session != NULL &&
+      CBS_mem_equal(&session_id, ssl->session->session_id,
+                    ssl->session->session_id_length)) {
+    if (ssl->sid_ctx_length != ssl->session->sid_ctx_length ||
+        memcmp(ssl->session->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length)) {
       /* actually a client application bug */
       al = SSL_AD_ILLEGAL_PARAMETER;
       OPENSSL_PUT_ERROR(SSL,
                         SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
       goto f_err;
     }
-    s->hit = 1;
+    ssl->hit = 1;
   } else {
     /* The session wasn't resumed. Create a fresh SSL_SESSION to
      * fill out. */
-    s->hit = 0;
-    if (!ssl_get_new_session(s, 0 /* client */)) {
+    ssl->hit = 0;
+    if (!ssl_get_new_session(ssl, 0 /* client */)) {
       goto f_err;
     }
     /* Note: session_id could be empty. */
-    s->session->session_id_length = CBS_len(&session_id);
-    memcpy(s->session->session_id, CBS_data(&session_id), CBS_len(&session_id));
+    ssl->session->session_id_length = CBS_len(&session_id);
+    memcpy(ssl->session->session_id, CBS_data(&session_id),
+           CBS_len(&session_id));
   }
 
   c = SSL_get_cipher_by_value(cipher_suite);
@@ -838,15 +840,15 @@
   }
   /* If the cipher is disabled then we didn't sent it in the ClientHello, so if
    * the server selected it, it's an error. */
-  if ((c->algorithm_mkey & ct->mask_k) ||
-      (c->algorithm_auth & ct->mask_a) ||
-      SSL_CIPHER_get_min_version(c) > ssl3_version_from_wire(s, s->version)) {
+  if ((c->algorithm_mkey & ct->mask_k) || (c->algorithm_auth & ct->mask_a) ||
+      SSL_CIPHER_get_min_version(c) >
+          ssl3_version_from_wire(ssl, ssl->version)) {
     al = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     goto f_err;
   }
 
-  sk = ssl_get_ciphers_by_id(s);
+  sk = ssl_get_ciphers_by_id(ssl);
   if (!sk_SSL_CIPHER_find(sk, NULL, c)) {
     /* we did not say we would use this cipher */
     al = SSL_AD_ILLEGAL_PARAMETER;
@@ -854,30 +856,30 @@
     goto f_err;
   }
 
-  if (s->hit) {
-    if (s->session->cipher != c) {
+  if (ssl->hit) {
+    if (ssl->session->cipher != c) {
       al = SSL_AD_ILLEGAL_PARAMETER;
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
       goto f_err;
     }
-    if (s->session->ssl_version != s->version) {
+    if (ssl->session->ssl_version != ssl->version) {
       al = SSL_AD_ILLEGAL_PARAMETER;
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
       goto f_err;
     }
   }
-  s->s3->tmp.new_cipher = c;
+  ssl->s3->tmp.new_cipher = c;
 
   /* Now that the cipher is known, initialize the handshake hash. */
-  if (!ssl3_init_handshake_hash(s)) {
+  if (!ssl3_init_handshake_hash(ssl)) {
     goto f_err;
   }
 
   /* If doing a full handshake with TLS 1.2, the server may request a client
    * certificate which requires hashing the handshake transcript under a
    * different hash. Otherwise, the handshake buffer may be released. */
-  if (!SSL_USE_SIGALGS(s) || s->hit) {
-    ssl3_free_handshake_buffer(s);
+  if (!SSL_USE_SIGALGS(ssl) || ssl->hit) {
+    ssl3_free_handshake_buffer(ssl);
   }
 
   /* Only the NULL compression algorithm is supported. */
@@ -888,7 +890,7 @@
   }
 
   /* TLS extensions */
-  if (!ssl_parse_serverhello_tlsext(s, &server_hello)) {
+  if (!ssl_parse_serverhello_tlsext(ssl, &server_hello)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     goto err;
   }
@@ -901,10 +903,11 @@
     goto f_err;
   }
 
-  if (s->hit &&
-      s->s3->tmp.extended_master_secret != s->session->extended_master_secret) {
+  if (ssl->hit &&
+      ssl->s3->tmp.extended_master_secret !=
+          ssl->session->extended_master_secret) {
     al = SSL_AD_HANDSHAKE_FAILURE;
-    if (s->session->extended_master_secret) {
+    if (ssl->session->extended_master_secret) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION);
     } else {
       OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION);
@@ -915,16 +918,15 @@
   return 1;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   return -1;
 }
 
-/* ssl3_check_certificate_for_cipher returns one if |leaf| is a suitable server
- * certificate type for |cipher|. Otherwise, it returns zero and pushes an error
- * on the error queue. */
-static int ssl3_check_certificate_for_cipher(X509 *leaf,
-                                             const SSL_CIPHER *cipher) {
+/* ssl3_check_leaf_certificate returns one if |leaf| is a suitable leaf server
+ * certificate for |ssl|. Otherwise, it returns zero and pushes an error on the
+ * error queue. */
+static int ssl3_check_leaf_certificate(SSL *ssl, X509 *leaf) {
   int ret = 0;
   EVP_PKEY *pkey = X509_get_pubkey(leaf);
   if (pkey == NULL) {
@@ -932,6 +934,7 @@
   }
 
   /* Check the certificate's type matches the cipher. */
+  const SSL_CIPHER *cipher = ssl->s3->tmp.new_cipher;
   int expected_type = ssl_cipher_get_key_type(cipher);
   assert(expected_type != EVP_PKEY_NONE);
   if (pkey->type != expected_type) {
@@ -939,9 +942,9 @@
     goto err;
   }
 
-  /* TODO(davidben): This behavior is preserved from upstream. Should key usages
-   * be checked in other cases as well? */
   if (cipher->algorithm_auth & SSL_aECDSA) {
+    /* TODO(davidben): This behavior is preserved from upstream. Should key
+     * usages be checked in other cases as well? */
     /* This call populates the ex_flags field correctly */
     X509_check_purpose(leaf, -1, 0);
     if ((leaf->ex_flags & EXFLAG_KUSAGE) &&
@@ -949,6 +952,11 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING);
       goto err;
     }
+
+    if (!tls1_check_ec_cert(ssl, leaf)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECC_CERT);
+      goto err;
+    }
   }
 
   ret = 1;
@@ -958,7 +966,7 @@
   return ret;
 }
 
-int ssl3_get_server_certificate(SSL *s) {
+int ssl3_get_server_certificate(SSL *ssl) {
   int al, ok, ret = -1;
   unsigned long n;
   X509 *x = NULL;
@@ -967,15 +975,15 @@
   CBS cbs, certificate_list;
   const uint8_t *data;
 
-  n = s->method->ssl_get_message(s, SSL3_ST_CR_CERT_A, SSL3_ST_CR_CERT_B,
-                                 SSL3_MT_CERTIFICATE, (long)s->max_cert_list,
+  n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_CERT_A, SSL3_ST_CR_CERT_B,
+                                 SSL3_MT_CERTIFICATE, (long)ssl->max_cert_list,
                                  ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
   }
 
-  CBS_init(&cbs, s->init_msg, n);
+  CBS_init(&cbs, ssl->init_msg, n);
 
   sk = sk_X509_new_null();
   if (sk == NULL) {
@@ -998,8 +1006,9 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
       goto f_err;
     }
+    /* A u24 length cannot overflow a long. */
     data = CBS_data(&certificate);
-    x = d2i_X509(NULL, &data, CBS_len(&certificate));
+    x = d2i_X509(NULL, &data, (long)CBS_len(&certificate));
     if (x == NULL) {
       al = SSL_AD_BAD_CERTIFICATE;
       OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
@@ -1018,27 +1027,27 @@
   }
 
   X509 *leaf = sk_X509_value(sk, 0);
-  if (!ssl3_check_certificate_for_cipher(leaf, s->s3->tmp.new_cipher)) {
+  if (!ssl3_check_leaf_certificate(ssl, leaf)) {
     al = SSL_AD_ILLEGAL_PARAMETER;
     goto f_err;
   }
 
   /* NOTE: Unlike the server half, the client's copy of |cert_chain| includes
    * the leaf. */
-  sk_X509_pop_free(s->session->cert_chain, X509_free);
-  s->session->cert_chain = sk;
+  sk_X509_pop_free(ssl->session->cert_chain, X509_free);
+  ssl->session->cert_chain = sk;
   sk = NULL;
 
-  X509_free(s->session->peer);
-  s->session->peer = X509_up_ref(leaf);
+  X509_free(ssl->session->peer);
+  ssl->session->peer = X509_up_ref(leaf);
 
-  s->session->verify_result = s->verify_result;
+  ssl->session->verify_result = ssl->verify_result;
 
   ret = 1;
 
   if (0) {
   f_err:
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
   }
 
 err:
@@ -1048,53 +1057,51 @@
   return ret;
 }
 
-int ssl3_get_server_key_exchange(SSL *s) {
+int ssl3_get_server_key_exchange(SSL *ssl) {
   EVP_MD_CTX md_ctx;
   int al, ok;
   long n, alg_k, alg_a;
   EVP_PKEY *pkey = NULL;
   const EVP_MD *md = NULL;
-  RSA *rsa = NULL;
   DH *dh = NULL;
   EC_KEY *ecdh = NULL;
-  BN_CTX *bn_ctx = NULL;
   EC_POINT *srvr_ecpoint = NULL;
   CBS server_key_exchange, server_key_exchange_orig, parameter;
 
   /* use same message size as in ssl3_get_certificate_request() as
    * ServerKeyExchange message may be skipped */
-  n = s->method->ssl_get_message(s, SSL3_ST_CR_KEY_EXCH_A,
-                                 SSL3_ST_CR_KEY_EXCH_B, -1, s->max_cert_list,
+  n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_KEY_EXCH_A,
+                                 SSL3_ST_CR_KEY_EXCH_B, -1, ssl->max_cert_list,
                                  ssl_hash_message, &ok);
   if (!ok) {
     return n;
   }
 
-  if (s->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) {
-    if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher)) {
+  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) {
+    if (ssl_cipher_requires_server_key_exchange(ssl->s3->tmp.new_cipher)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       return -1;
     }
 
     /* In plain PSK ciphersuite, ServerKeyExchange may be omitted to send no
      * identity hint. */
-    if (s->s3->tmp.new_cipher->algorithm_auth & SSL_aPSK) {
+    if (ssl->s3->tmp.new_cipher->algorithm_auth & SSL_aPSK) {
       /* TODO(davidben): This should be reset in one place with the rest of the
        * handshake state. */
-      OPENSSL_free(s->s3->tmp.peer_psk_identity_hint);
-      s->s3->tmp.peer_psk_identity_hint = NULL;
+      OPENSSL_free(ssl->s3->tmp.peer_psk_identity_hint);
+      ssl->s3->tmp.peer_psk_identity_hint = NULL;
     }
-    s->s3->tmp.reuse_message = 1;
+    ssl->s3->tmp.reuse_message = 1;
     return 1;
   }
 
   /* Retain a copy of the original CBS to compute the signature over. */
-  CBS_init(&server_key_exchange, s->init_msg, n);
+  CBS_init(&server_key_exchange, ssl->init_msg, n);
   server_key_exchange_orig = server_key_exchange;
 
-  alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-  alg_a = s->s3->tmp.new_cipher->algorithm_auth;
+  alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
+  alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
   EVP_MD_CTX_init(&md_ctx);
 
   if (alg_a & SSL_aPSK) {
@@ -1123,7 +1130,7 @@
     }
 
     /* Save the identity hint as a C string. */
-    if (!CBS_strdup(&psk_identity_hint, &s->s3->tmp.peer_psk_identity_hint)) {
+    if (!CBS_strdup(&psk_identity_hint, &ssl->s3->tmp.peer_psk_identity_hint)) {
       al = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto f_err;
@@ -1132,7 +1139,6 @@
 
   if (alg_k & SSL_kDHE) {
     CBS dh_p, dh_g, dh_Ys;
-
     if (!CBS_get_u16_length_prefixed(&server_key_exchange, &dh_p) ||
         CBS_len(&dh_p) == 0 ||
         !CBS_get_u16_length_prefixed(&server_key_exchange, &dh_g) ||
@@ -1146,84 +1152,69 @@
 
     dh = DH_new();
     if (dh == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
       goto err;
     }
 
-    if ((dh->p = BN_bin2bn(CBS_data(&dh_p), CBS_len(&dh_p), NULL)) == NULL ||
-        (dh->g = BN_bin2bn(CBS_data(&dh_g), CBS_len(&dh_g), NULL)) == NULL ||
-        (dh->pub_key = BN_bin2bn(CBS_data(&dh_Ys), CBS_len(&dh_Ys), NULL)) ==
-            NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_BN_LIB);
+    dh->p = BN_bin2bn(CBS_data(&dh_p), CBS_len(&dh_p), NULL);
+    dh->g = BN_bin2bn(CBS_data(&dh_g), CBS_len(&dh_g), NULL);
+    if (dh->p == NULL || dh->g == NULL) {
       goto err;
     }
 
-    s->session->key_exchange_info = DH_num_bits(dh);
-    if (s->session->key_exchange_info < 1024) {
+    ssl->session->key_exchange_info = DH_num_bits(dh);
+    if (ssl->session->key_exchange_info < 1024) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_DH_P_LENGTH);
       goto err;
-    }
-    DH_free(s->s3->tmp.peer_dh_tmp);
-    s->s3->tmp.peer_dh_tmp = dh;
-    dh = NULL;
-  } else if (alg_k & SSL_kECDHE) {
-    uint16_t curve_id;
-    int curve_nid = 0;
-    const EC_GROUP *group;
-    CBS point;
-
-    /* Extract elliptic curve parameters and the server's ephemeral ECDH public
-     * key.  Check curve is one of our preferences, if not server has sent an
-     * invalid curve. */
-    if (!tls1_check_curve(s, &server_key_exchange, &curve_id)) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-      goto f_err;
-    }
-
-    curve_nid = tls1_ec_curve_id2nid(curve_id);
-    if (curve_nid == 0) {
-      al = SSL_AD_INTERNAL_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS);
-      goto f_err;
-    }
-
-    ecdh = EC_KEY_new_by_curve_name(curve_nid);
-    s->session->key_exchange_info = curve_id;
-    if (ecdh == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB);
+    } else if (ssl->session->key_exchange_info > 4096) {
+      /* Overly large DHE groups are prohibitively expensive, so enforce a limit
+       * to prevent a server from causing us to perform too expensive of a
+       * computation. */
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DH_P_TOO_LONG);
       goto err;
     }
 
-    group = EC_KEY_get0_group(ecdh);
+    SSL_ECDH_CTX_init_for_dhe(&ssl->s3->tmp.ecdh_ctx, dh);
+    dh = NULL;
 
-    /* Next, get the encoded ECPoint */
-    if (!CBS_get_u8_length_prefixed(&server_key_exchange, &point)) {
+    /* Save the peer public key for later. */
+    size_t peer_key_len;
+    if (!CBS_stow(&dh_Ys, &ssl->s3->tmp.peer_key, &peer_key_len)) {
+      goto err;
+    }
+    /* |dh_Ys| has a u16 length prefix, so this fits in a |uint16_t|. */
+    assert(sizeof(ssl->s3->tmp.peer_key_len) == 2 && peer_key_len <= 0xffff);
+    ssl->s3->tmp.peer_key_len = (uint16_t)peer_key_len;
+  } else if (alg_k & SSL_kECDHE) {
+    /* Parse the server parameters. */
+    uint8_t curve_type;
+    uint16_t curve_id;
+    CBS point;
+    if (!CBS_get_u8(&server_key_exchange, &curve_type) ||
+        curve_type != NAMED_CURVE_TYPE ||
+        !CBS_get_u16(&server_key_exchange, &curve_id) ||
+        !CBS_get_u8_length_prefixed(&server_key_exchange, &point)) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto f_err;
     }
+    ssl->session->key_exchange_info = curve_id;
 
-    if (((srvr_ecpoint = EC_POINT_new(group)) == NULL) ||
-        ((bn_ctx = BN_CTX_new()) == NULL)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-
-    if (!EC_POINT_oct2point(group, srvr_ecpoint, CBS_data(&point),
-                            CBS_len(&point), bn_ctx)) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+    /* Ensure the curve is consistent with preferences. */
+    if (!tls1_check_curve_id(ssl, curve_id)) {
+      al = SSL_AD_ILLEGAL_PARAMETER;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       goto f_err;
     }
-    EC_KEY_set_public_key(ecdh, srvr_ecpoint);
-    EC_KEY_free(s->s3->tmp.peer_ecdh_tmp);
-    s->s3->tmp.peer_ecdh_tmp = ecdh;
-    ecdh = NULL;
-    BN_CTX_free(bn_ctx);
-    bn_ctx = NULL;
-    EC_POINT_free(srvr_ecpoint);
-    srvr_ecpoint = NULL;
+
+    /* Initialize ECDH and save the peer public key for later. */
+    size_t peer_key_len;
+    if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id) ||
+        !CBS_stow(&point, &ssl->s3->tmp.peer_key, &peer_key_len)) {
+      goto err;
+    }
+    /* |point| has a u8 length prefix, so this fits in a |uint16_t|. */
+    assert(sizeof(ssl->s3->tmp.peer_key_len) == 2 && peer_key_len <= 0xffff);
+    ssl->s3->tmp.peer_key_len = (uint16_t)peer_key_len;
   } else if (!(alg_k & SSL_kPSK)) {
     al = SSL_AD_UNEXPECTED_MESSAGE;
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
@@ -1237,13 +1228,13 @@
            CBS_len(&server_key_exchange_orig) - CBS_len(&server_key_exchange));
 
   /* ServerKeyExchange should be signed by the server's public key. */
-  if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-    pkey = X509_get_pubkey(s->session->peer);
+  if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+    pkey = X509_get_pubkey(ssl->session->peer);
     if (pkey == NULL) {
       goto err;
     }
 
-    if (SSL_USE_SIGALGS(s)) {
+    if (SSL_USE_SIGALGS(ssl)) {
       uint8_t hash, signature;
       if (!CBS_get_u8(&server_key_exchange, &hash) ||
           !CBS_get_u8(&server_key_exchange, &signature)) {
@@ -1251,10 +1242,10 @@
         OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
         goto f_err;
       }
-      if (!tls12_check_peer_sigalg(s, &md, &al, hash, signature, pkey)) {
+      if (!tls12_check_peer_sigalg(ssl, &md, &al, hash, signature, pkey)) {
         goto f_err;
       }
-      s->s3->tmp.server_key_exchange_hash = hash;
+      ssl->s3->tmp.server_key_exchange_hash = hash;
     } else if (pkey->type == EVP_PKEY_RSA) {
       md = EVP_md5_sha1();
     } else {
@@ -1271,9 +1262,9 @@
     }
 
     if (!EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) ||
-        !EVP_DigestVerifyUpdate(&md_ctx, s->s3->client_random,
+        !EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->client_random,
                                 SSL3_RANDOM_SIZE) ||
-        !EVP_DigestVerifyUpdate(&md_ctx, s->s3->server_random,
+        !EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->server_random,
                                 SSL3_RANDOM_SIZE) ||
         !EVP_DigestVerifyUpdate(&md_ctx, CBS_data(&parameter),
                                 CBS_len(&parameter)) ||
@@ -1299,12 +1290,10 @@
   return 1;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   EVP_PKEY_free(pkey);
-  RSA_free(rsa);
   DH_free(dh);
-  BN_CTX_free(bn_ctx);
   EC_POINT_free(srvr_ecpoint);
   EC_KEY_free(ecdh);
   EVP_MD_CTX_cleanup(&md_ctx);
@@ -1315,7 +1304,7 @@
   return X509_NAME_cmp(*a, *b);
 }
 
-int ssl3_get_certificate_request(SSL *s) {
+int ssl3_get_certificate_request(SSL *ssl) {
   int ok, ret = 0;
   unsigned long n;
   X509_NAME *xn = NULL;
@@ -1325,31 +1314,31 @@
   CBS certificate_authorities;
   const uint8_t *data;
 
-  n = s->method->ssl_get_message(s, SSL3_ST_CR_CERT_REQ_A,
-                                 SSL3_ST_CR_CERT_REQ_B, -1, s->max_cert_list,
+  n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_CERT_REQ_A,
+                                 SSL3_ST_CR_CERT_REQ_B, -1, ssl->max_cert_list,
                                  ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
   }
 
-  s->s3->tmp.cert_req = 0;
+  ssl->s3->tmp.cert_req = 0;
 
-  if (s->s3->tmp.message_type == SSL3_MT_SERVER_DONE) {
-    s->s3->tmp.reuse_message = 1;
+  if (ssl->s3->tmp.message_type == SSL3_MT_SERVER_DONE) {
+    ssl->s3->tmp.reuse_message = 1;
     /* If we get here we don't need the handshake buffer as we won't be doing
      * client auth. */
-    ssl3_free_handshake_buffer(s);
+    ssl3_free_handshake_buffer(ssl);
     return 1;
   }
 
-  if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) {
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_MESSAGE_TYPE);
     goto err;
   }
 
-  CBS_init(&cbs, s->init_msg, n);
+  CBS_init(&cbs, ssl->init_msg, n);
 
   ca_sk = sk_X509_NAME_new(ca_dn_cmp);
   if (ca_sk == NULL) {
@@ -1359,22 +1348,22 @@
 
   /* get the certificate types */
   if (!CBS_get_u8_length_prefixed(&cbs, &certificate_types)) {
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     goto err;
   }
 
-  if (!CBS_stow(&certificate_types, &s->s3->tmp.certificate_types,
-                &s->s3->tmp.num_certificate_types)) {
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+  if (!CBS_stow(&certificate_types, &ssl->s3->tmp.certificate_types,
+                &ssl->s3->tmp.num_certificate_types)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     goto err;
   }
 
-  if (SSL_USE_SIGALGS(s)) {
+  if (SSL_USE_SIGALGS(ssl)) {
     CBS supported_signature_algorithms;
     if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) ||
-        !tls1_parse_peer_sigalgs(s, &supported_signature_algorithms)) {
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+        !tls1_parse_peer_sigalgs(ssl, &supported_signature_algorithms)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto err;
     }
@@ -1382,7 +1371,7 @@
 
   /* get the CA RDNs */
   if (!CBS_get_u16_length_prefixed(&cbs, &certificate_authorities)) {
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH);
     goto err;
   }
@@ -1391,28 +1380,29 @@
     CBS distinguished_name;
     if (!CBS_get_u16_length_prefixed(&certificate_authorities,
                                      &distinguished_name)) {
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CA_DN_TOO_LONG);
       goto err;
     }
 
     data = CBS_data(&distinguished_name);
 
-    xn = d2i_X509_NAME(NULL, &data, CBS_len(&distinguished_name));
+    /* A u16 length cannot overflow a long. */
+    xn = d2i_X509_NAME(NULL, &data, (long)CBS_len(&distinguished_name));
     if (xn == NULL) {
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
       goto err;
     }
 
     if (!CBS_skip(&distinguished_name, data - CBS_data(&distinguished_name))) {
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       goto err;
     }
 
     if (CBS_len(&distinguished_name) != 0) {
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CA_DN_LENGTH_MISMATCH);
       goto err;
     }
@@ -1424,9 +1414,9 @@
   }
 
   /* we should setup a certificate to return.... */
-  s->s3->tmp.cert_req = 1;
-  sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free);
-  s->s3->tmp.ca_names = ca_sk;
+  ssl->s3->tmp.cert_req = 1;
+  sk_X509_NAME_pop_free(ssl->s3->tmp.ca_names, X509_NAME_free);
+  ssl->s3->tmp.ca_names = ca_sk;
   ca_sk = NULL;
 
   ret = 1;
@@ -1436,10 +1426,10 @@
   return ret;
 }
 
-int ssl3_get_new_session_ticket(SSL *s) {
+int ssl3_get_new_session_ticket(SSL *ssl) {
   int ok, al;
-  long n = s->method->ssl_get_message(
-      s, SSL3_ST_CR_SESSION_TICKET_A, SSL3_ST_CR_SESSION_TICKET_B,
+  long n = ssl->method->ssl_get_message(
+      ssl, SSL3_ST_CR_SESSION_TICKET_A, SSL3_ST_CR_SESSION_TICKET_B,
       SSL3_MT_NEWSESSION_TICKET, 16384, ssl_hash_message, &ok);
 
   if (!ok) {
@@ -1448,7 +1438,7 @@
 
   CBS new_session_ticket, ticket;
   uint32_t ticket_lifetime_hint;
-  CBS_init(&new_session_ticket, s->init_msg, n);
+  CBS_init(&new_session_ticket, ssl->init_msg, n);
   if (!CBS_get_u32(&new_session_ticket, &ticket_lifetime_hint) ||
       !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) ||
       CBS_len(&new_session_ticket) != 0) {
@@ -1462,17 +1452,17 @@
      * negotiating the extension. The value of |tlsext_ticket_expected| is
      * checked in |ssl_update_cache| so is cleared here to avoid an unnecessary
      * update. */
-    s->tlsext_ticket_expected = 0;
+    ssl->tlsext_ticket_expected = 0;
     return 1;
   }
 
-  if (s->hit) {
+  if (ssl->hit) {
     /* The server is sending a new ticket for an existing session. Sessions are
      * immutable once established, so duplicate all but the ticket of the
      * existing session. */
     uint8_t *bytes;
     size_t bytes_len;
-    if (!SSL_SESSION_to_bytes_for_ticket(s->session, &bytes, &bytes_len)) {
+    if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &bytes, &bytes_len)) {
       goto err;
     }
     SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len);
@@ -1483,55 +1473,55 @@
       goto err;
     }
 
-    SSL_SESSION_free(s->session);
-    s->session = new_session;
+    SSL_SESSION_free(ssl->session);
+    ssl->session = new_session;
   }
 
-  if (!CBS_stow(&ticket, &s->session->tlsext_tick,
-                &s->session->tlsext_ticklen)) {
+  if (!CBS_stow(&ticket, &ssl->session->tlsext_tick,
+                &ssl->session->tlsext_ticklen)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
   }
-  s->session->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
+  ssl->session->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
 
   /* Generate a session ID for this session based on the session ticket. We use
    * the session ID mechanism for detecting ticket resumption. This also fits in
    * with assumptions elsewhere in OpenSSL.*/
-  if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), s->session->session_id,
-                  &s->session->session_id_length, EVP_sha256(), NULL)) {
+  if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), ssl->session->session_id,
+                  &ssl->session->session_id_length, EVP_sha256(), NULL)) {
     goto err;
   }
 
   return 1;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   return -1;
 }
 
-int ssl3_get_cert_status(SSL *s) {
+int ssl3_get_cert_status(SSL *ssl) {
   int ok, al;
   long n;
   CBS certificate_status, ocsp_response;
   uint8_t status_type;
 
-  n = s->method->ssl_get_message(
-      s, SSL3_ST_CR_CERT_STATUS_A, SSL3_ST_CR_CERT_STATUS_B,
+  n = ssl->method->ssl_get_message(
+      ssl, SSL3_ST_CR_CERT_STATUS_A, SSL3_ST_CR_CERT_STATUS_B,
       -1, 16384, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
   }
 
-  if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) {
+  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) {
     /* A server may send status_request in ServerHello and then change
      * its mind about sending CertificateStatus. */
-    s->s3->tmp.reuse_message = 1;
+    ssl->s3->tmp.reuse_message = 1;
     return 1;
   }
 
-  CBS_init(&certificate_status, s->init_msg, n);
+  CBS_init(&certificate_status, ssl->init_msg, n);
   if (!CBS_get_u8(&certificate_status, &status_type) ||
       status_type != TLSEXT_STATUSTYPE_ocsp ||
       !CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) ||
@@ -1542,8 +1532,8 @@
     goto f_err;
   }
 
-  if (!CBS_stow(&ocsp_response, &s->session->ocsp_response,
-                &s->session->ocsp_response_length)) {
+  if (!CBS_stow(&ocsp_response, &ssl->session->ocsp_response,
+                &ssl->session->ocsp_response_length)) {
     al = SSL_AD_INTERNAL_ERROR;
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto f_err;
@@ -1551,15 +1541,15 @@
   return 1;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
   return -1;
 }
 
-int ssl3_get_server_done(SSL *s) {
+int ssl3_get_server_done(SSL *ssl) {
   int ok;
   long n;
 
-  n = s->method->ssl_get_message(s, SSL3_ST_CR_SRVR_DONE_A,
+  n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_SRVR_DONE_A,
                                  SSL3_ST_CR_SRVR_DONE_B, SSL3_MT_SERVER_DONE,
                                  30, /* should be very small, like 0 :-) */
                                  ssl_hash_message, &ok);
@@ -1570,7 +1560,7 @@
 
   if (n > 0) {
     /* should contain no data */
-    ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH);
     return -1;
   }
@@ -1581,415 +1571,298 @@
 OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned),
                        SIZE_T_IS_SMALLER_THAN_UNSIGNED);
 
-int ssl3_send_client_key_exchange(SSL *s) {
-  uint8_t *p;
-  int n = 0;
-  uint32_t alg_k;
-  uint32_t alg_a;
-  uint8_t *q;
-  EVP_PKEY *pkey = NULL;
-  EC_KEY *clnt_ecdh = NULL;
-  const EC_POINT *srvr_ecpoint = NULL;
-  EVP_PKEY *srvr_pub_pkey = NULL;
-  uint8_t *encodedPoint = NULL;
-  int encoded_pt_len = 0;
-  BN_CTX *bn_ctx = NULL;
-  unsigned int psk_len = 0;
-  uint8_t psk[PSK_MAX_PSK_LEN];
+int ssl3_send_client_key_exchange(SSL *ssl) {
+  if (ssl->state == SSL3_ST_CW_KEY_EXCH_B) {
+    return ssl_do_write(ssl);
+  }
+  assert(ssl->state == SSL3_ST_CW_KEY_EXCH_A);
+
   uint8_t *pms = NULL;
   size_t pms_len = 0;
-
-  if (s->state == SSL3_ST_CW_KEY_EXCH_A) {
-    p = ssl_handshake_start(s);
-
-    alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-    alg_a = s->s3->tmp.new_cipher->algorithm_auth;
-
-    /* If using a PSK key exchange, prepare the pre-shared key. */
-    if (alg_a & SSL_aPSK) {
-      char identity[PSK_MAX_IDENTITY_LEN + 1];
-      size_t identity_len;
-
-      if (s->psk_client_callback == NULL) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_CLIENT_CB);
-        goto err;
-      }
-
-      memset(identity, 0, sizeof(identity));
-      psk_len =
-          s->psk_client_callback(s, s->s3->tmp.peer_psk_identity_hint, identity,
-                                 sizeof(identity), psk, sizeof(psk));
-      if (psk_len > PSK_MAX_PSK_LEN) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        goto err;
-      } else if (psk_len == 0) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
-        ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
-        goto err;
-      }
-
-      identity_len = OPENSSL_strnlen(identity, sizeof(identity));
-      if (identity_len > PSK_MAX_IDENTITY_LEN) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        goto err;
-      }
-
-      OPENSSL_free(s->session->psk_identity);
-      s->session->psk_identity = BUF_strdup(identity);
-      if (s->session->psk_identity == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-
-      /* Write out psk_identity. */
-      s2n(identity_len, p);
-      memcpy(p, identity, identity_len);
-      p += identity_len;
-      n = 2 + identity_len;
-    }
-
-    /* Depending on the key exchange method, compute |pms| and |pms_len|. */
-    if (alg_k & SSL_kRSA) {
-      RSA *rsa;
-      size_t enc_pms_len;
-
-      pms_len = SSL_MAX_MASTER_KEY_LENGTH;
-      pms = OPENSSL_malloc(pms_len);
-      if (pms == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-
-      pkey = X509_get_pubkey(s->session->peer);
-      if (pkey == NULL ||
-          pkey->type != EVP_PKEY_RSA ||
-          pkey->pkey.rsa == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        EVP_PKEY_free(pkey);
-        goto err;
-      }
-
-      s->session->key_exchange_info = EVP_PKEY_bits(pkey);
-      rsa = pkey->pkey.rsa;
-      EVP_PKEY_free(pkey);
-
-      pms[0] = s->client_version >> 8;
-      pms[1] = s->client_version & 0xff;
-      if (!RAND_bytes(&pms[2], SSL_MAX_MASTER_KEY_LENGTH - 2)) {
-        goto err;
-      }
-
-      s->session->master_key_length = SSL_MAX_MASTER_KEY_LENGTH;
-
-      q = p;
-      /* In TLS and beyond, reserve space for the length prefix. */
-      if (s->version > SSL3_VERSION) {
-        p += 2;
-        n += 2;
-      }
-      if (!RSA_encrypt(rsa, &enc_pms_len, p, RSA_size(rsa), pms, pms_len,
-                       RSA_PKCS1_PADDING)) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_RSA_ENCRYPT);
-        goto err;
-      }
-      n += enc_pms_len;
-
-      /* Log the premaster secret, if logging is enabled. */
-      if (!ssl_ctx_log_rsa_client_key_exchange(s->ctx, p, enc_pms_len, pms,
-                                               pms_len)) {
-        goto err;
-      }
-
-      /* Fill in the length prefix. */
-      if (s->version > SSL3_VERSION) {
-        s2n(enc_pms_len, q);
-      }
-    } else if (alg_k & SSL_kDHE) {
-      DH *dh_srvr, *dh_clnt;
-      int dh_len;
-      size_t pub_len;
-
-      if (s->s3->tmp.peer_dh_tmp == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        goto err;
-      }
-      dh_srvr = s->s3->tmp.peer_dh_tmp;
-
-      /* generate a new random key */
-      dh_clnt = DHparams_dup(dh_srvr);
-      if (dh_clnt == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
-        goto err;
-      }
-      if (!DH_generate_key(dh_clnt)) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
-        DH_free(dh_clnt);
-        goto err;
-      }
-
-      pms_len = DH_size(dh_clnt);
-      pms = OPENSSL_malloc(pms_len);
-      if (pms == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        DH_free(dh_clnt);
-        goto err;
-      }
-
-      dh_len = DH_compute_key(pms, dh_srvr->pub_key, dh_clnt);
-      if (dh_len <= 0) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
-        DH_free(dh_clnt);
-        goto err;
-      }
-      pms_len = dh_len;
-
-      /* send off the data */
-      pub_len = BN_num_bytes(dh_clnt->pub_key);
-      s2n(pub_len, p);
-      BN_bn2bin(dh_clnt->pub_key, p);
-      n += 2 + pub_len;
-
-      DH_free(dh_clnt);
-    } else if (alg_k & SSL_kECDHE) {
-      const EC_GROUP *srvr_group = NULL;
-      EC_KEY *tkey;
-      int ecdh_len;
-
-      if (s->s3->tmp.peer_ecdh_tmp == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        goto err;
-      }
-
-      tkey = s->s3->tmp.peer_ecdh_tmp;
-
-      srvr_group = EC_KEY_get0_group(tkey);
-      srvr_ecpoint = EC_KEY_get0_public_key(tkey);
-      if (srvr_group == NULL || srvr_ecpoint == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        goto err;
-      }
-
-      clnt_ecdh = EC_KEY_new();
-      if (clnt_ecdh == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-
-      if (!EC_KEY_set_group(clnt_ecdh, srvr_group)) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB);
-        goto err;
-      }
-
-      /* Generate a new ECDH key pair */
-      if (!EC_KEY_generate_key(clnt_ecdh)) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB);
-        goto err;
-      }
-
-      unsigned field_size = EC_GROUP_get_degree(srvr_group);
-      if (field_size == 0) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB);
-        goto err;
-      }
-
-      pms_len = (field_size + 7) / 8;
-      pms = OPENSSL_malloc(pms_len);
-      if (pms == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-
-      ecdh_len = ECDH_compute_key(pms, pms_len, srvr_ecpoint, clnt_ecdh, NULL);
-      if (ecdh_len <= 0) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB);
-        goto err;
-      }
-      pms_len = ecdh_len;
-
-      /* First check the size of encoding and allocate memory accordingly. */
-      encoded_pt_len =
-          EC_POINT_point2oct(srvr_group, EC_KEY_get0_public_key(clnt_ecdh),
-                             POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
-
-      encodedPoint =
-          (uint8_t *)OPENSSL_malloc(encoded_pt_len * sizeof(uint8_t));
-      bn_ctx = BN_CTX_new();
-      if (encodedPoint == NULL || bn_ctx == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-
-      /* Encode the public key */
-      encoded_pt_len = EC_POINT_point2oct(
-          srvr_group, EC_KEY_get0_public_key(clnt_ecdh),
-          POINT_CONVERSION_UNCOMPRESSED, encodedPoint, encoded_pt_len, bn_ctx);
-
-      *p = encoded_pt_len; /* length of encoded point */
-      /* Encoded point will be copied here */
-      p += 1;
-      n += 1;
-      /* copy the point */
-      memcpy(p, encodedPoint, encoded_pt_len);
-      /* increment n to account for length field */
-      n += encoded_pt_len;
-
-      /* Free allocated memory */
-      BN_CTX_free(bn_ctx);
-      bn_ctx = NULL;
-      OPENSSL_free(encodedPoint);
-      encodedPoint = NULL;
-      EC_KEY_free(clnt_ecdh);
-      clnt_ecdh = NULL;
-      EVP_PKEY_free(srvr_pub_pkey);
-      srvr_pub_pkey = NULL;
-    } else if (alg_k & SSL_kPSK) {
-      /* For plain PSK, other_secret is a block of 0s with the same length as
-       * the pre-shared key. */
-      pms_len = psk_len;
-      pms = OPENSSL_malloc(pms_len);
-      if (pms == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-      memset(pms, 0, pms_len);
-    } else {
-      ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-
-    /* For a PSK cipher suite, other_secret is combined with the pre-shared
-     * key. */
-    if (alg_a & SSL_aPSK) {
-      CBB cbb, child;
-      uint8_t *new_pms;
-      size_t new_pms_len;
-
-      CBB_zero(&cbb);
-      if (!CBB_init(&cbb, 2 + psk_len + 2 + pms_len) ||
-          !CBB_add_u16_length_prefixed(&cbb, &child) ||
-          !CBB_add_bytes(&child, pms, pms_len) ||
-          !CBB_add_u16_length_prefixed(&cbb, &child) ||
-          !CBB_add_bytes(&child, psk, psk_len) ||
-          !CBB_finish(&cbb, &new_pms, &new_pms_len)) {
-        CBB_cleanup(&cbb);
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-      OPENSSL_cleanse(pms, pms_len);
-      OPENSSL_free(pms);
-      pms = new_pms;
-      pms_len = new_pms_len;
-    }
-
-    /* The message must be added to the finished hash before calculating the
-     * master secret. */
-    if (!ssl_set_handshake_header(s, SSL3_MT_CLIENT_KEY_EXCHANGE, n)) {
-      goto err;
-    }
-    s->state = SSL3_ST_CW_KEY_EXCH_B;
-
-    s->session->master_key_length = s->enc_method->generate_master_secret(
-        s, s->session->master_key, pms, pms_len);
-    if (s->session->master_key_length == 0) {
-      goto err;
-    }
-    s->session->extended_master_secret = s->s3->tmp.extended_master_secret;
-    OPENSSL_cleanse(pms, pms_len);
-    OPENSSL_free(pms);
+  CBB cbb;
+  if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
+                      ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) {
+    goto err;
   }
 
+  uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
+  uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
+
+  /* If using a PSK key exchange, prepare the pre-shared key. */
+  unsigned psk_len = 0;
+  uint8_t psk[PSK_MAX_PSK_LEN];
+  if (alg_a & SSL_aPSK) {
+    if (ssl->psk_client_callback == NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_CLIENT_CB);
+      goto err;
+    }
+
+    char identity[PSK_MAX_IDENTITY_LEN + 1];
+    memset(identity, 0, sizeof(identity));
+    psk_len = ssl->psk_client_callback(
+        ssl, ssl->s3->tmp.peer_psk_identity_hint, identity, sizeof(identity),
+        psk, sizeof(psk));
+    if (psk_len == 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+      goto err;
+    }
+    assert(psk_len <= PSK_MAX_PSK_LEN);
+
+    OPENSSL_free(ssl->session->psk_identity);
+    ssl->session->psk_identity = BUF_strdup(identity);
+    if (ssl->session->psk_identity == NULL) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+
+    /* Write out psk_identity. */
+    CBB child;
+    if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+        !CBB_add_bytes(&child, (const uint8_t *)identity,
+                       OPENSSL_strnlen(identity, sizeof(identity))) ||
+        !CBB_flush(&cbb)) {
+      goto err;
+    }
+  }
+
+  /* Depending on the key exchange method, compute |pms| and |pms_len|. */
+  if (alg_k & SSL_kRSA) {
+    pms_len = SSL_MAX_MASTER_KEY_LENGTH;
+    pms = OPENSSL_malloc(pms_len);
+    if (pms == NULL) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+
+    EVP_PKEY *pkey = X509_get_pubkey(ssl->session->peer);
+    if (pkey == NULL) {
+      goto err;
+    }
+
+    RSA *rsa = EVP_PKEY_get0_RSA(pkey);
+    if (rsa == NULL) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      EVP_PKEY_free(pkey);
+      goto err;
+    }
+
+    ssl->session->key_exchange_info = EVP_PKEY_bits(pkey);
+    EVP_PKEY_free(pkey);
+
+    pms[0] = ssl->client_version >> 8;
+    pms[1] = ssl->client_version & 0xff;
+    if (!RAND_bytes(&pms[2], SSL_MAX_MASTER_KEY_LENGTH - 2)) {
+      goto err;
+    }
+
+    CBB child, *enc_pms = &cbb;
+    size_t enc_pms_len;
+    /* In TLS, there is a length prefix. */
+    if (ssl->version > SSL3_VERSION) {
+      if (!CBB_add_u16_length_prefixed(&cbb, &child)) {
+        goto err;
+      }
+      enc_pms = &child;
+    }
+
+    uint8_t *ptr;
+    if (!CBB_reserve(enc_pms, &ptr, RSA_size(rsa)) ||
+        !RSA_encrypt(rsa, &enc_pms_len, ptr, RSA_size(rsa), pms, pms_len,
+                     RSA_PKCS1_PADDING) ||
+        /* Log the premaster secret, if logging is enabled. */
+        !ssl_log_rsa_client_key_exchange(ssl, ptr, enc_pms_len, pms, pms_len) ||
+        !CBB_did_write(enc_pms, enc_pms_len) ||
+        !CBB_flush(&cbb)) {
+      goto err;
+    }
+  } else if (alg_k & (SSL_kECDHE|SSL_kDHE)) {
+    /* Generate a keypair and serialize the public half. ECDHE uses a u8 length
+     * prefix while DHE uses u16. */
+    CBB child;
+    int child_ok;
+    if (alg_k & SSL_kECDHE) {
+      child_ok = CBB_add_u8_length_prefixed(&cbb, &child);
+    } else {
+      child_ok = CBB_add_u16_length_prefixed(&cbb, &child);
+    }
+
+    if (!child_ok ||
+        !SSL_ECDH_CTX_generate_keypair(&ssl->s3->tmp.ecdh_ctx, &child) ||
+        !CBB_flush(&cbb)) {
+      goto err;
+    }
+
+    /* Compute the premaster. */
+    uint8_t alert;
+    if (!SSL_ECDH_CTX_compute_secret(&ssl->s3->tmp.ecdh_ctx, &pms, &pms_len,
+                                     &alert, ssl->s3->tmp.peer_key,
+                                     ssl->s3->tmp.peer_key_len)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+      goto err;
+    }
+
+    /* The key exchange state may now be discarded. */
+    SSL_ECDH_CTX_cleanup(&ssl->s3->tmp.ecdh_ctx);
+    OPENSSL_free(ssl->s3->tmp.peer_key);
+    ssl->s3->tmp.peer_key = NULL;
+  } else if (alg_k & SSL_kPSK) {
+    /* For plain PSK, other_secret is a block of 0s with the same length as
+     * the pre-shared key. */
+    pms_len = psk_len;
+    pms = OPENSSL_malloc(pms_len);
+    if (pms == NULL) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    memset(pms, 0, pms_len);
+  } else {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    goto err;
+  }
+
+  /* For a PSK cipher suite, other_secret is combined with the pre-shared
+   * key. */
+  if (alg_a & SSL_aPSK) {
+    CBB pms_cbb, child;
+    uint8_t *new_pms;
+    size_t new_pms_len;
+
+    CBB_zero(&pms_cbb);
+    if (!CBB_init(&pms_cbb, 2 + psk_len + 2 + pms_len) ||
+        !CBB_add_u16_length_prefixed(&pms_cbb, &child) ||
+        !CBB_add_bytes(&child, pms, pms_len) ||
+        !CBB_add_u16_length_prefixed(&pms_cbb, &child) ||
+        !CBB_add_bytes(&child, psk, psk_len) ||
+        !CBB_finish(&pms_cbb, &new_pms, &new_pms_len)) {
+      CBB_cleanup(&pms_cbb);
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    OPENSSL_cleanse(pms, pms_len);
+    OPENSSL_free(pms);
+    pms = new_pms;
+    pms_len = new_pms_len;
+  }
+
+  /* The message must be added to the finished hash before calculating the
+   * master secret. */
+  size_t length;
+  if (!CBB_finish(&cbb, NULL, &length) ||
+      !ssl_set_handshake_header(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE, length)) {
+    goto err;
+  }
+  ssl->state = SSL3_ST_CW_KEY_EXCH_B;
+
+  ssl->session->master_key_length = ssl->enc_method->generate_master_secret(
+      ssl, ssl->session->master_key, pms, pms_len);
+  if (ssl->session->master_key_length == 0) {
+    goto err;
+  }
+  ssl->session->extended_master_secret = ssl->s3->tmp.extended_master_secret;
+  OPENSSL_cleanse(pms, pms_len);
+  OPENSSL_free(pms);
+
   /* SSL3_ST_CW_KEY_EXCH_B */
-  return s->method->do_write(s);
+  return ssl_do_write(ssl);
 
 err:
-  BN_CTX_free(bn_ctx);
-  OPENSSL_free(encodedPoint);
-  EC_KEY_free(clnt_ecdh);
-  EVP_PKEY_free(srvr_pub_pkey);
-  if (pms) {
+  if (pms != NULL) {
     OPENSSL_cleanse(pms, pms_len);
     OPENSSL_free(pms);
   }
   return -1;
 }
 
-int ssl3_send_cert_verify(SSL *s) {
-  if (s->state == SSL3_ST_CW_CERT_VRFY_A ||
-      s->state == SSL3_ST_CW_CERT_VRFY_B) {
-    enum ssl_private_key_result_t sign_result;
-    uint8_t *p = ssl_handshake_start(s);
-    size_t signature_length = 0;
-    unsigned long n = 0;
-    assert(ssl_has_private_key(s));
-
-    if (s->state == SSL3_ST_CW_CERT_VRFY_A) {
-      uint8_t *buf = (uint8_t *)s->init_buf->data;
-      const EVP_MD *md = NULL;
-      uint8_t digest[EVP_MAX_MD_SIZE];
-      size_t digest_length;
-
-      /* Write out the digest type if need be. */
-      if (SSL_USE_SIGALGS(s)) {
-        md = tls1_choose_signing_digest(s);
-        if (!tls12_get_sigandhash(s, p, md)) {
-          OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-          return -1;
-        }
-        p += 2;
-        n += 2;
-      }
-
-      /* Compute the digest. */
-      const int pkey_type = ssl_private_key_type(s);
-      if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey_type)) {
-        return -1;
-      }
-
-      /* The handshake buffer is no longer necessary. */
-      ssl3_free_handshake_buffer(s);
-
-      /* Sign the digest. */
-      signature_length = ssl_private_key_max_signature_len(s);
-      if (p + 2 + signature_length > buf + SSL3_RT_MAX_PLAIN_LENGTH) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
-        return -1;
-      }
-
-      s->rwstate = SSL_PRIVATE_KEY_OPERATION;
-      sign_result = ssl_private_key_sign(s, &p[2], &signature_length,
-                                         signature_length, md, digest,
-                                         digest_length);
-    } else {
-      if (SSL_USE_SIGALGS(s)) {
-        /* The digest has already been selected and written. */
-        p += 2;
-        n += 2;
-      }
-      signature_length = ssl_private_key_max_signature_len(s);
-      s->rwstate = SSL_PRIVATE_KEY_OPERATION;
-      sign_result = ssl_private_key_sign_complete(s, &p[2], &signature_length,
-                                                  signature_length);
-    }
-
-    if (sign_result == ssl_private_key_retry) {
-      s->state = SSL3_ST_CW_CERT_VRFY_B;
-      return -1;
-    }
-    s->rwstate = SSL_NOTHING;
-    if (sign_result != ssl_private_key_success) {
-      return -1;
-    }
-
-    s2n(signature_length, p);
-    n += signature_length + 2;
-    if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_VERIFY, n)) {
-      return -1;
-    }
-    s->state = SSL3_ST_CW_CERT_VRFY_C;
+int ssl3_send_cert_verify(SSL *ssl) {
+  if (ssl->state == SSL3_ST_CW_CERT_VRFY_C) {
+    return ssl_do_write(ssl);
   }
 
-  return ssl_do_write(s);
+  CBB cbb, child;
+  if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
+                      ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) {
+    goto err;
+  }
+
+  assert(ssl_has_private_key(ssl));
+
+  const size_t max_sig_len = ssl_private_key_max_signature_len(ssl);
+  size_t sig_len;
+  enum ssl_private_key_result_t sign_result;
+  if (ssl->state == SSL3_ST_CW_CERT_VRFY_A) {
+    /* Select and write out the digest type in TLS 1.2. */
+    const EVP_MD *md = NULL;
+    if (SSL_USE_SIGALGS(ssl)) {
+      md = tls1_choose_signing_digest(ssl);
+      if (!tls12_add_sigandhash(ssl, &cbb, md)) {
+        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+        goto err;
+      }
+    }
+
+    /* Compute the digest. In TLS 1.1 and below, the digest type is also
+     * selected here. */
+    uint8_t digest[EVP_MAX_MD_SIZE];
+    size_t digest_len;
+    if (!ssl3_cert_verify_hash(ssl, digest, &digest_len, &md,
+                               ssl_private_key_type(ssl))) {
+      goto err;
+    }
+
+    /* The handshake buffer is no longer necessary. */
+    ssl3_free_handshake_buffer(ssl);
+
+    /* Sign the digest. */
+    uint8_t *ptr;
+    if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+        !CBB_reserve(&child, &ptr, max_sig_len)) {
+      goto err;
+    }
+    sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len, md,
+                                       digest, digest_len);
+  } else {
+    assert(ssl->state == SSL3_ST_CW_CERT_VRFY_B);
+
+    /* Skip over the already written signature algorithm and retry the
+     * signature. */
+    uint8_t *ptr;
+    if ((SSL_USE_SIGALGS(ssl) && !CBB_did_write(&cbb, 2)) ||
+        !CBB_add_u16_length_prefixed(&cbb, &child) ||
+        !CBB_reserve(&child, &ptr, max_sig_len)) {
+      goto err;
+    }
+    sign_result =
+        ssl_private_key_sign_complete(ssl, ptr, &sig_len, max_sig_len);
+  }
+
+  switch (sign_result) {
+    case ssl_private_key_success:
+      ssl->rwstate = SSL_NOTHING;
+      break;
+    case ssl_private_key_failure:
+      ssl->rwstate = SSL_NOTHING;
+      goto err;
+    case ssl_private_key_retry:
+      ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+      ssl->state = SSL3_ST_CW_CERT_VRFY_B;
+      goto err;
+  }
+
+  size_t length;
+  if (!CBB_did_write(&child, sig_len) ||
+      !CBB_finish(&cbb, NULL, &length) ||
+      !ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_VERIFY, length)) {
+    goto err;
+  }
+
+  ssl->state = SSL3_ST_CW_CERT_VRFY_C;
+  return ssl_do_write(ssl);
+
+err:
+  CBB_cleanup(&cbb);
+  return -1;
 }
 
 /* ssl3_has_client_certificate returns true if a client certificate is
@@ -1998,47 +1871,47 @@
   return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl);
 }
 
-int ssl3_send_client_certificate(SSL *s) {
+int ssl3_send_client_certificate(SSL *ssl) {
   X509 *x509 = NULL;
   EVP_PKEY *pkey = NULL;
   int i;
 
-  if (s->state == SSL3_ST_CW_CERT_A) {
+  if (ssl->state == SSL3_ST_CW_CERT_A) {
     /* Let cert callback update client certificates if required */
-    if (s->cert->cert_cb) {
-      i = s->cert->cert_cb(s, s->cert->cert_cb_arg);
+    if (ssl->cert->cert_cb) {
+      i = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
       if (i < 0) {
-        s->rwstate = SSL_X509_LOOKUP;
+        ssl->rwstate = SSL_X509_LOOKUP;
         return -1;
       }
       if (i == 0) {
-        ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return 0;
       }
-      s->rwstate = SSL_NOTHING;
+      ssl->rwstate = SSL_NOTHING;
     }
 
-    if (ssl3_has_client_certificate(s)) {
-      s->state = SSL3_ST_CW_CERT_C;
+    if (ssl3_has_client_certificate(ssl)) {
+      ssl->state = SSL3_ST_CW_CERT_C;
     } else {
-      s->state = SSL3_ST_CW_CERT_B;
+      ssl->state = SSL3_ST_CW_CERT_B;
     }
   }
 
   /* We need to get a client cert */
-  if (s->state == SSL3_ST_CW_CERT_B) {
+  if (ssl->state == SSL3_ST_CW_CERT_B) {
     /* If we get an error, we need to:
      *   ssl->rwstate=SSL_X509_LOOKUP; return(-1);
      * We then get retried later */
-    i = ssl_do_client_cert_cb(s, &x509, &pkey);
+    i = ssl_do_client_cert_cb(ssl, &x509, &pkey);
     if (i < 0) {
-      s->rwstate = SSL_X509_LOOKUP;
+      ssl->rwstate = SSL_X509_LOOKUP;
       return -1;
     }
-    s->rwstate = SSL_NOTHING;
+    ssl->rwstate = SSL_NOTHING;
     if (i == 1 && pkey != NULL && x509 != NULL) {
-      s->state = SSL3_ST_CW_CERT_B;
-      if (!SSL_use_certificate(s, x509) || !SSL_use_PrivateKey(s, pkey)) {
+      ssl->state = SSL3_ST_CW_CERT_B;
+      if (!SSL_use_certificate(ssl, x509) || !SSL_use_PrivateKey(ssl, pkey)) {
         i = 0;
       }
     } else if (i == 1) {
@@ -2048,42 +1921,42 @@
 
     X509_free(x509);
     EVP_PKEY_free(pkey);
-    if (i && !ssl3_has_client_certificate(s)) {
+    if (i && !ssl3_has_client_certificate(ssl)) {
       i = 0;
     }
     if (i == 0) {
-      if (s->version == SSL3_VERSION) {
-        s->s3->tmp.cert_req = 0;
-        ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
+      if (ssl->version == SSL3_VERSION) {
+        ssl->s3->tmp.cert_req = 0;
+        ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
         return 1;
       } else {
-        s->s3->tmp.cert_req = 2;
+        ssl->s3->tmp.cert_req = 2;
         /* There is no client certificate, so the handshake buffer may be
          * released. */
-        ssl3_free_handshake_buffer(s);
+        ssl3_free_handshake_buffer(ssl);
       }
     }
 
     /* Ok, we have a cert */
-    s->state = SSL3_ST_CW_CERT_C;
+    ssl->state = SSL3_ST_CW_CERT_C;
   }
 
-  if (s->state == SSL3_ST_CW_CERT_C) {
-    if (s->s3->tmp.cert_req == 2) {
+  if (ssl->state == SSL3_ST_CW_CERT_C) {
+    if (ssl->s3->tmp.cert_req == 2) {
       /* Send an empty Certificate message. */
-      uint8_t *p = ssl_handshake_start(s);
+      uint8_t *p = ssl_handshake_start(ssl);
       l2n3(0, p);
-      if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE, 3)) {
+      if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) {
         return -1;
       }
-    } else if (!ssl3_output_cert_chain(s)) {
+    } else if (!ssl3_output_cert_chain(ssl)) {
       return -1;
     }
-    s->state = SSL3_ST_CW_CERT_D;
+    ssl->state = SSL3_ST_CW_CERT_D;
   }
 
   /* SSL3_ST_CW_CERT_D */
-  return ssl_do_write(s);
+  return ssl_do_write(ssl);
 }
 
 int ssl3_send_next_proto(SSL *ssl) {
@@ -2117,12 +1990,6 @@
   return ssl_do_write(ssl);
 }
 
-static int write_32_byte_big_endian(CBB *out, const BIGNUM *in) {
-  uint8_t *ptr;
-  return CBB_add_space(out, &ptr, 32) &&
-         BN_bn2bin_padded(ptr, 32, in);
-}
-
 int ssl3_send_channel_id(SSL *ssl) {
   if (ssl->state == SSL3_ST_CW_CHANNEL_ID_B) {
     return ssl_do_write(ssl);
@@ -2148,13 +2015,13 @@
   }
   ssl->rwstate = SSL_NOTHING;
 
-  if (EVP_PKEY_id(ssl->tlsext_channel_id_private) != EVP_PKEY_EC) {
+  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
+  if (ec_key == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
   int ret = -1;
-  EC_KEY *ec_key = ssl->tlsext_channel_id_private->pkey.ec;
   BIGNUM *x = BN_new();
   BIGNUM *y = BN_new();
   ECDSA_SIG *sig = NULL;
@@ -2183,10 +2050,10 @@
                       ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl)) ||
       !CBB_add_u16(&cbb, TLSEXT_TYPE_channel_id) ||
       !CBB_add_u16_length_prefixed(&cbb, &child) ||
-      !write_32_byte_big_endian(&child, x) ||
-      !write_32_byte_big_endian(&child, y) ||
-      !write_32_byte_big_endian(&child, sig->r) ||
-      !write_32_byte_big_endian(&child, sig->s) ||
+      !BN_bn2cbb_padded(&child, 32, x) ||
+      !BN_bn2cbb_padded(&child, 32, y) ||
+      !BN_bn2cbb_padded(&child, 32, sig->r) ||
+      !BN_bn2cbb_padded(&child, 32, sig->s) ||
       !CBB_finish(&cbb, NULL, &length) ||
       !ssl_set_handshake_header(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS, length)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -2211,15 +2078,15 @@
   return ssl->ctx->client_cert_cb(ssl, out_x509, out_pkey);
 }
 
-int ssl3_verify_server_cert(SSL *s) {
-  int ret = ssl_verify_cert_chain(s, s->session->cert_chain);
-  if (s->verify_mode != SSL_VERIFY_NONE && ret <= 0) {
-    int al = ssl_verify_alarm_type(s->verify_result);
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+int ssl3_verify_server_cert(SSL *ssl) {
+  int ret = ssl_verify_cert_chain(ssl, ssl->session->cert_chain);
+  if (ssl->verify_mode != SSL_VERIFY_NONE && ret <= 0) {
+    int al = ssl_verify_alarm_type(ssl->verify_result);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
     OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
   } else {
     ret = 1;
-    ERR_clear_error(); /* but we keep s->verify_result */
+    ERR_clear_error(); /* but we keep ssl->verify_result */
   }
 
   return ret;
diff --git a/src/ssl/s3_enc.c b/src/ssl/s3_enc.c
index aa0d717..89d861a 100644
--- a/src/ssl/s3_enc.c
+++ b/src/ssl/s3_enc.c
@@ -162,10 +162,10 @@
     0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
 };
 
-static int ssl3_handshake_mac(SSL *s, int md_nid, const char *sender, int len,
+static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender, int len,
                               uint8_t *p);
 
-int ssl3_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret,
+int ssl3_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
              size_t secret_len, const char *label, size_t label_len,
              const uint8_t *seed1, size_t seed1_len,
              const uint8_t *seed2, size_t seed2_len) {
@@ -228,13 +228,13 @@
   return 1;
 }
 
-void ssl3_cleanup_key_block(SSL *s) {
-  if (s->s3->tmp.key_block != NULL) {
-    OPENSSL_cleanse(s->s3->tmp.key_block, s->s3->tmp.key_block_length);
-    OPENSSL_free(s->s3->tmp.key_block);
-    s->s3->tmp.key_block = NULL;
+void ssl3_cleanup_key_block(SSL *ssl) {
+  if (ssl->s3->tmp.key_block != NULL) {
+    OPENSSL_cleanse(ssl->s3->tmp.key_block, ssl->s3->tmp.key_block_length);
+    OPENSSL_free(ssl->s3->tmp.key_block);
+    ssl->s3->tmp.key_block = NULL;
   }
-  s->s3->tmp.key_block_length = 0;
+  ssl->s3->tmp.key_block_length = 0;
 }
 
 int ssl3_init_handshake_buffer(SSL *ssl) {
@@ -309,20 +309,20 @@
   return 1;
 }
 
-int ssl3_cert_verify_mac(SSL *s, int md_nid, uint8_t *p) {
-  return ssl3_handshake_mac(s, md_nid, NULL, 0, p);
+int ssl3_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p) {
+  return ssl3_handshake_mac(ssl, md_nid, NULL, 0, p);
 }
 
-int ssl3_final_finish_mac(SSL *s, const char *sender, int len, uint8_t *p) {
+int ssl3_final_finish_mac(SSL *ssl, const char *sender, int len, uint8_t *p) {
   int ret, sha1len;
-  ret = ssl3_handshake_mac(s, NID_md5, sender, len, p);
+  ret = ssl3_handshake_mac(ssl, NID_md5, sender, len, p);
   if (ret == 0) {
     return 0;
   }
 
   p += ret;
 
-  sha1len = ssl3_handshake_mac(s, NID_sha1, sender, len, p);
+  sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, len, p);
   if (sha1len == 0) {
     return 0;
   }
@@ -331,7 +331,7 @@
   return ret;
 }
 
-static int ssl3_handshake_mac(SSL *s, int md_nid, const char *sender, int len,
+static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender, int len,
                               uint8_t *p) {
   unsigned int ret;
   size_t npad, n;
@@ -341,9 +341,9 @@
   const EVP_MD_CTX *ctx_template;
 
   if (md_nid == NID_md5) {
-    ctx_template = &s->s3->handshake_md5;
-  } else if (md_nid == EVP_MD_CTX_type(&s->s3->handshake_hash)) {
-    ctx_template = &s->s3->handshake_hash;
+    ctx_template = &ssl->s3->handshake_md5;
+  } else if (md_nid == EVP_MD_CTX_type(&ssl->s3->handshake_hash)) {
+    ctx_template = &ssl->s3->handshake_hash;
   } else {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_REQUIRED_DIGEST);
     return 0;
@@ -362,7 +362,8 @@
   if (sender != NULL) {
     EVP_DigestUpdate(&ctx, sender, len);
   }
-  EVP_DigestUpdate(&ctx, s->session->master_key, s->session->master_key_length);
+  EVP_DigestUpdate(&ctx, ssl->session->master_key,
+                   ssl->session->master_key_length);
   EVP_DigestUpdate(&ctx, ssl3_pad_1, npad);
   EVP_DigestFinal_ex(&ctx, md_buf, &i);
 
@@ -371,7 +372,8 @@
     OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
     return 0;
   }
-  EVP_DigestUpdate(&ctx, s->session->master_key, s->session->master_key_length);
+  EVP_DigestUpdate(&ctx, ssl->session->master_key,
+                   ssl->session->master_key_length);
   EVP_DigestUpdate(&ctx, ssl3_pad_2, npad);
   EVP_DigestUpdate(&ctx, md_buf, i);
   EVP_DigestFinal_ex(&ctx, p, &ret);
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 7bf223d..64f9f8c 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -181,21 +181,23 @@
   return 1;
 }
 
-int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len) {
-  uint8_t *p = (uint8_t *)s->init_buf->data;
+int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len) {
+  uint8_t *p = (uint8_t *)ssl->init_buf->data;
   *(p++) = htype;
   l2n3(len, p);
-  s->init_num = (int)len + SSL3_HM_HEADER_LENGTH;
-  s->init_off = 0;
+  ssl->init_num = (int)len + SSL3_HM_HEADER_LENGTH;
+  ssl->init_off = 0;
 
   /* Add the message to the handshake hash. */
-  return ssl3_update_handshake_hash(s, (uint8_t *)s->init_buf->data,
-                                    s->init_num);
+  return ssl3_update_handshake_hash(ssl, (uint8_t *)ssl->init_buf->data,
+                                    ssl->init_num);
 }
 
-int ssl3_handshake_write(SSL *s) { return ssl3_do_write(s, SSL3_RT_HANDSHAKE); }
+int ssl3_handshake_write(SSL *ssl) {
+  return ssl3_do_write(ssl, SSL3_RT_HANDSHAKE);
+}
 
-int ssl3_new(SSL *s) {
+int ssl3_new(SSL *ssl) {
   SSL3_STATE *s3;
 
   s3 = OPENSSL_malloc(sizeof *s3);
@@ -207,43 +209,41 @@
   EVP_MD_CTX_init(&s3->handshake_hash);
   EVP_MD_CTX_init(&s3->handshake_md5);
 
-  s->s3 = s3;
+  ssl->s3 = s3;
 
   /* Set the version to the highest supported version for TLS. This controls the
-   * initial state of |s->enc_method| and what the API reports as the version
+   * initial state of |ssl->enc_method| and what the API reports as the version
    * prior to negotiation.
    *
    * TODO(davidben): This is fragile and confusing. */
-  s->version = TLS1_2_VERSION;
+  ssl->version = TLS1_2_VERSION;
   return 1;
 err:
   return 0;
 }
 
-void ssl3_free(SSL *s) {
-  if (s == NULL || s->s3 == NULL) {
+void ssl3_free(SSL *ssl) {
+  if (ssl == NULL || ssl->s3 == NULL) {
     return;
   }
 
-  ssl3_cleanup_key_block(s);
-  ssl_read_buffer_clear(s);
-  ssl_write_buffer_clear(s);
-  DH_free(s->s3->tmp.dh);
-  EC_KEY_free(s->s3->tmp.ecdh);
+  ssl3_cleanup_key_block(ssl);
+  ssl_read_buffer_clear(ssl);
+  ssl_write_buffer_clear(ssl);
+  SSL_ECDH_CTX_cleanup(&ssl->s3->tmp.ecdh_ctx);
+  OPENSSL_free(ssl->s3->tmp.peer_key);
 
-  sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free);
-  OPENSSL_free(s->s3->tmp.certificate_types);
-  OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
-  OPENSSL_free(s->s3->tmp.peer_psk_identity_hint);
-  DH_free(s->s3->tmp.peer_dh_tmp);
-  EC_KEY_free(s->s3->tmp.peer_ecdh_tmp);
-  ssl3_free_handshake_buffer(s);
-  ssl3_free_handshake_hash(s);
-  OPENSSL_free(s->s3->alpn_selected);
+  sk_X509_NAME_pop_free(ssl->s3->tmp.ca_names, X509_NAME_free);
+  OPENSSL_free(ssl->s3->tmp.certificate_types);
+  OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
+  OPENSSL_free(ssl->s3->tmp.peer_psk_identity_hint);
+  ssl3_free_handshake_buffer(ssl);
+  ssl3_free_handshake_hash(ssl);
+  OPENSSL_free(ssl->s3->alpn_selected);
 
-  OPENSSL_cleanse(s->s3, sizeof *s->s3);
-  OPENSSL_free(s->s3);
-  s->s3 = NULL;
+  OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3);
+  OPENSSL_free(ssl->s3);
+  ssl->s3 = NULL;
 }
 
 int SSL_session_reused(const SSL *ssl) {
@@ -299,8 +299,8 @@
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-  ctx->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
-  return 1;
+  int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+  return SSL_CTX_set1_curves(ctx, &nid, 1);
 }
 
 int SSL_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key) {
@@ -308,8 +308,8 @@
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-  ssl->cert->ecdh_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
-  return 1;
+  int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+  return SSL_set1_curves(ssl, &nid, 1);
 }
 
 int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) {
@@ -335,8 +335,9 @@
 }
 
 int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) {
-  if (EVP_PKEY_id(private_key) != EVP_PKEY_EC ||
-      EC_GROUP_get_curve_name(EC_KEY_get0_group(private_key->pkey.ec)) !=
+  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(private_key);
+  if (ec_key == NULL ||
+      EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) !=
           NID_X9_62_prime256v1) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
     return 0;
@@ -446,30 +447,30 @@
   return 1;
 }
 
-struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s) {
-  if (s->cipher_list != NULL) {
-    return s->cipher_list;
+struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl) {
+  if (ssl->cipher_list != NULL) {
+    return ssl->cipher_list;
   }
 
-  if (s->version >= TLS1_1_VERSION && s->ctx != NULL &&
-      s->ctx->cipher_list_tls11 != NULL) {
-    return s->ctx->cipher_list_tls11;
+  if (ssl->version >= TLS1_1_VERSION && ssl->ctx != NULL &&
+      ssl->ctx->cipher_list_tls11 != NULL) {
+    return ssl->ctx->cipher_list_tls11;
   }
 
-  if (s->version >= TLS1_VERSION && s->ctx != NULL &&
-      s->ctx->cipher_list_tls10 != NULL) {
-    return s->ctx->cipher_list_tls10;
+  if (ssl->version >= TLS1_VERSION && ssl->ctx != NULL &&
+      ssl->ctx->cipher_list_tls10 != NULL) {
+    return ssl->ctx->cipher_list_tls10;
   }
 
-  if (s->ctx != NULL && s->ctx->cipher_list != NULL) {
-    return s->ctx->cipher_list;
+  if (ssl->ctx != NULL && ssl->ctx->cipher_list != NULL) {
+    return ssl->ctx->cipher_list;
   }
 
   return NULL;
 }
 
 const SSL_CIPHER *ssl3_choose_cipher(
-    SSL *s, STACK_OF(SSL_CIPHER) *clnt,
+    SSL *ssl, STACK_OF(SSL_CIPHER) *clnt,
     struct ssl_cipher_preference_list_st *server_pref) {
   const SSL_CIPHER *c, *ret = NULL;
   STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow;
@@ -486,7 +487,7 @@
    * such value exists yet. */
   int group_min = -1;
 
-  if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
+  if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
     prio = srvr;
     in_group_flags = server_pref->in_group_flags;
     allow = clnt;
@@ -496,7 +497,7 @@
     allow = srvr;
   }
 
-  ssl_get_compatible_server_ciphers(s, &mask_k, &mask_a);
+  ssl_get_compatible_server_ciphers(ssl, &mask_k, &mask_a);
 
   for (i = 0; i < sk_SSL_CIPHER_num(prio); i++) {
     c = sk_SSL_CIPHER_value(prio, i);
@@ -504,7 +505,8 @@
     ok = 1;
 
     /* Check the TLS version. */
-    if (SSL_CIPHER_get_min_version(c) > ssl3_version_from_wire(s, s->version)) {
+    if (SSL_CIPHER_get_min_version(c) >
+        ssl3_version_from_wire(ssl, ssl->version)) {
       ok = 0;
     }
 
@@ -540,7 +542,7 @@
   return ret;
 }
 
-int ssl3_get_req_cert_type(SSL *s, uint8_t *p) {
+int ssl3_get_req_cert_type(SSL *ssl, uint8_t *p) {
   int ret = 0;
   const uint8_t *sig;
   size_t i, siglen;
@@ -548,7 +550,7 @@
   int have_ecdsa_sign = 0;
 
   /* get configured sigalgs */
-  siglen = tls12_get_psigalgs(s, &sig);
+  siglen = tls12_get_psigalgs(ssl, &sig);
   for (i = 0; i < siglen; i += 2, sig += 2) {
     switch (sig[1]) {
       case TLSEXT_signature_rsa:
@@ -567,7 +569,7 @@
 
   /* ECDSA certs can be used with RSA cipher suites as well so we don't need to
    * check for SSL_kECDH or SSL_kECDHE. */
-  if (s->version >= TLS1_VERSION && have_ecdsa_sign) {
+  if (ssl->version >= TLS1_VERSION && have_ecdsa_sign) {
       p[ret++] = TLS_CT_ECDSA_SIGN;
   }
 
@@ -576,9 +578,9 @@
 
 /* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and
  * handshake macs if required. */
-uint32_t ssl_get_algorithm_prf(SSL *s) {
-  uint32_t algorithm_prf = s->s3->tmp.new_cipher->algorithm_prf;
-  if (s->enc_method->enc_flags & SSL_ENC_FLAG_SHA256_PRF &&
+uint32_t ssl_get_algorithm_prf(SSL *ssl) {
+  uint32_t algorithm_prf = ssl->s3->tmp.new_cipher->algorithm_prf;
+  if (ssl->enc_method->enc_flags & SSL_ENC_FLAG_SHA256_PRF &&
       algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT) {
     return SSL_HANDSHAKE_MAC_SHA256;
   }
diff --git a/src/ssl/s3_meth.c b/src/ssl/s3_meth.c
index 01c1101..b60b5f2 100644
--- a/src/ssl/s3_meth.c
+++ b/src/ssl/s3_meth.c
@@ -67,6 +67,7 @@
     ssl3_connect,
     ssl3_get_message,
     ssl3_read_app_data,
+    ssl3_read_change_cipher_spec,
     ssl3_read_close_notify,
     ssl3_write_app_data,
     ssl3_dispatch_alert,
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 7416d0e..4c1133c 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -122,7 +122,7 @@
 #include "internal.h"
 
 
-static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned len);
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len);
 
 /* kMaxWarningAlerts is the number of consecutive warning alerts that will be
  * processed. */
@@ -158,7 +158,6 @@
       SSL3_RECORD *rr = &ssl->s3->rrec;
       rr->type = type;
       rr->length = (uint16_t)len;
-      rr->off = 0;
       rr->data = out;
       return 1;
 
@@ -189,18 +188,18 @@
 
 /* Call this to write data in records of type |type|. It will return <= 0 if
  * not all data has been sent or non-blocking IO. */
-int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len) {
+int ssl3_write_bytes(SSL *ssl, int type, const void *buf_, int len) {
   const uint8_t *buf = buf_;
   unsigned int tot, n, nw;
   int i;
 
-  s->rwstate = SSL_NOTHING;
-  assert(s->s3->wnum <= INT_MAX);
-  tot = s->s3->wnum;
-  s->s3->wnum = 0;
+  ssl->rwstate = SSL_NOTHING;
+  assert(ssl->s3->wnum <= INT_MAX);
+  tot = ssl->s3->wnum;
+  ssl->s3->wnum = 0;
 
-  if (!s->in_handshake && SSL_in_init(s) && !SSL_in_false_start(s)) {
-    i = s->handshake_func(s);
+  if (!ssl->in_handshake && SSL_in_init(ssl) && !SSL_in_false_start(ssl)) {
+    i = ssl->handshake_func(ssl);
     if (i < 0) {
       return i;
     }
@@ -226,21 +225,21 @@
   for (;;) {
     /* max contains the maximum number of bytes that we can put into a
      * record. */
-    unsigned max = s->max_send_fragment;
+    unsigned max = ssl->max_send_fragment;
     if (n > max) {
       nw = max;
     } else {
       nw = n;
     }
 
-    i = do_ssl3_write(s, type, &buf[tot], nw);
+    i = do_ssl3_write(ssl, type, &buf[tot], nw);
     if (i <= 0) {
-      s->s3->wnum = tot;
+      ssl->s3->wnum = tot;
       return i;
     }
 
     if (i == (int)n || (type == SSL3_RT_APPLICATION_DATA &&
-                        (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
+                        (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
       return tot + i;
     }
 
@@ -249,33 +248,33 @@
   }
 }
 
-static int ssl3_write_pending(SSL *s, int type, const uint8_t *buf,
+static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *buf,
                               unsigned int len) {
-  if (s->s3->wpend_tot > (int)len ||
-      (s->s3->wpend_buf != buf &&
-       !(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) ||
-      s->s3->wpend_type != type) {
+  if (ssl->s3->wpend_tot > (int)len ||
+      (ssl->s3->wpend_buf != buf &&
+       !(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) ||
+      ssl->s3->wpend_type != type) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
     return -1;
   }
 
-  int ret = ssl_write_buffer_flush(s);
+  int ret = ssl_write_buffer_flush(ssl);
   if (ret <= 0) {
     return ret;
   }
-  return s->s3->wpend_ret;
+  return ssl->s3->wpend_ret;
 }
 
 /* do_ssl3_write writes an SSL record of the given type. */
-static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned len) {
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) {
   /* If there is still data from the previous record, flush it. */
-  if (ssl_write_buffer_is_pending(s)) {
-    return ssl3_write_pending(s, type, buf, len);
+  if (ssl_write_buffer_is_pending(ssl)) {
+    return ssl3_write_pending(ssl, type, buf, len);
   }
 
   /* If we have an alert to send, lets send it */
-  if (s->s3->alert_dispatch) {
-    int ret = s->method->ssl_dispatch_alert(s);
+  if (ssl->s3->alert_dispatch) {
+    int ret = ssl->method->ssl_dispatch_alert(ssl);
     if (ret <= 0) {
       return ret;
     }
@@ -291,49 +290,57 @@
     return 0;
   }
 
-  size_t max_out = len + ssl_max_seal_overhead(s);
+  size_t max_out = len + ssl_max_seal_overhead(ssl);
   if (max_out < len) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return -1;
   }
   uint8_t *out;
   size_t ciphertext_len;
-  if (!ssl_write_buffer_init(s, &out, max_out) ||
-      !tls_seal_record(s, out, &ciphertext_len, max_out, type, buf, len)) {
+  if (!ssl_write_buffer_init(ssl, &out, max_out) ||
+      !tls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len)) {
     return -1;
   }
-  ssl_write_buffer_set_len(s, ciphertext_len);
+  ssl_write_buffer_set_len(ssl, ciphertext_len);
 
   /* memorize arguments so that ssl3_write_pending can detect bad write retries
    * later */
-  s->s3->wpend_tot = len;
-  s->s3->wpend_buf = buf;
-  s->s3->wpend_type = type;
-  s->s3->wpend_ret = len;
+  ssl->s3->wpend_tot = len;
+  ssl->s3->wpend_buf = buf;
+  ssl->s3->wpend_type = type;
+  ssl->s3->wpend_ret = len;
 
   /* we now just need to write the buffer */
-  return ssl3_write_pending(s, type, buf, len);
-}
-
-/* ssl3_expect_change_cipher_spec informs the record layer that a
- * ChangeCipherSpec record is required at this point. If a Handshake record is
- * received before ChangeCipherSpec, the connection will fail. Moreover, if
- * there are unprocessed handshake bytes, the handshake will also fail and the
- * function returns zero. Otherwise, the function returns one. */
-int ssl3_expect_change_cipher_spec(SSL *s) {
-  if (s->s3->handshake_fragment_len > 0 || s->s3->tmp.reuse_message) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNPROCESSED_HANDSHAKE_DATA);
-    return 0;
-  }
-
-  s->s3->flags |= SSL3_FLAGS_EXPECT_CCS;
-  return 1;
+  return ssl3_write_pending(ssl, type, buf, len);
 }
 
 int ssl3_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) {
   return ssl3_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek);
 }
 
+int ssl3_read_change_cipher_spec(SSL *ssl) {
+  uint8_t byte;
+  int ret = ssl3_read_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, 1 /* len */,
+                            0 /* no peek */);
+  if (ret <= 0) {
+    return ret;
+  }
+  assert(ret == 1);
+
+  if (ssl->s3->rrec.length != 0 || byte != SSL3_MT_CCS) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return -1;
+  }
+
+  if (ssl->msg_callback != NULL) {
+    ssl->msg_callback(0, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, 1,
+                      ssl, ssl->msg_callback_arg);
+  }
+
+  return 1;
+}
+
 void ssl3_read_close_notify(SSL *ssl) {
   ssl3_read_bytes(ssl, 0, NULL, 0, 0);
 }
@@ -358,72 +365,36 @@
  * 'type' is one of the following:
  *
  *   -  SSL3_RT_HANDSHAKE (when ssl3_get_message calls us)
- *   -  SSL3_RT_APPLICATION_DATA (when ssl3_read calls us)
+ *   -  SSL3_RT_CHANGE_CIPHER_SPEC (when ssl3_read_change_cipher_spec calls us)
+ *   -  SSL3_RT_APPLICATION_DATA (when ssl3_read_app_data calls us)
  *   -  0 (during a shutdown, no data has to be returned)
  *
  * If we don't have stored data to work from, read a SSL/TLS record first
  * (possibly multiple records if we still don't have anything to return).
  *
  * This function must handle any surprises the peer may have for us, such as
- * Alert records (e.g. close_notify), ChangeCipherSpec records (not really
- * a surprise, but handled as if it were), or renegotiation requests.
- * Also if record payloads contain fragments too small to process, we store
- * them until there is enough for the respective protocol (the record protocol
- * may use arbitrary fragmentation and even interleaving):
- *     Change cipher spec protocol
- *             just 1 byte needed, no need for keeping anything stored
- *     Alert protocol
- *             2 bytes needed (AlertLevel, AlertDescription)
- *     Handshake protocol
- *             4 bytes needed (HandshakeType, uint24 length) -- we just have
- *             to detect unexpected Client Hello and Hello Request messages
- *             here, anything else is handled by higher layers
- *     Application data protocol
- *             none of our business
- */
-int ssl3_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek) {
+ * Alert records (e.g. close_notify) or renegotiation requests. */
+int ssl3_read_bytes(SSL *ssl, int type, uint8_t *buf, int len, int peek) {
   int al, i, ret;
   unsigned int n;
   SSL3_RECORD *rr;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
 
-  if ((type && type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE) ||
+  if ((type && type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE &&
+       type != SSL3_RT_CHANGE_CIPHER_SPEC) ||
       (peek && type != SSL3_RT_APPLICATION_DATA)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  if (type == SSL3_RT_HANDSHAKE && s->s3->handshake_fragment_len > 0) {
-    /* (partially) satisfy request from storage */
-    uint8_t *src = s->s3->handshake_fragment;
-    uint8_t *dst = buf;
-    unsigned int k;
-
-    /* peek == 0 */
-    n = 0;
-    while (len > 0 && s->s3->handshake_fragment_len > 0) {
-      *dst++ = *src++;
-      len--;
-      s->s3->handshake_fragment_len--;
-      n++;
-    }
-    /* move any remaining fragment bytes: */
-    for (k = 0; k < s->s3->handshake_fragment_len; k++) {
-      s->s3->handshake_fragment[k] = *src++;
-    }
-    return n;
-  }
-
-  /* Now s->s3->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */
-
   /* This may require multiple iterations. False Start will cause
-   * |s->handshake_func| to signal success one step early, but the handshake
+   * |ssl->handshake_func| to signal success one step early, but the handshake
    * must be completely finished before other modes are accepted.
    *
    * TODO(davidben): Move this check up to a higher level. */
-  while (!s->in_handshake && SSL_in_init(s)) {
+  while (!ssl->in_handshake && SSL_in_init(ssl)) {
     assert(type == SSL3_RT_APPLICATION_DATA);
-    i = s->handshake_func(s);
+    i = ssl->handshake_func(ssl);
     if (i < 0) {
       return i;
     }
@@ -434,17 +405,17 @@
   }
 
 start:
-  s->rwstate = SSL_NOTHING;
+  ssl->rwstate = SSL_NOTHING;
 
-  /* s->s3->rrec.type    - is the type of record
-   * s->s3->rrec.data    - data
-   * s->s3->rrec.off     - offset into 'data' for next read
-   * s->s3->rrec.length  - number of bytes. */
-  rr = &s->s3->rrec;
+  /* ssl->s3->rrec.type    - is the type of record
+   * ssl->s3->rrec.data    - data
+   * ssl->s3->rrec.off     - offset into 'data' for next read
+   * ssl->s3->rrec.length  - number of bytes. */
+  rr = &ssl->s3->rrec;
 
   /* get new packet if necessary */
   if (rr->length == 0) {
-    ret = ssl3_get_record(s);
+    ret = ssl3_get_record(ssl);
     if (ret <= 0) {
       return ret;
     }
@@ -452,39 +423,21 @@
 
   /* we now have a packet which can be read and processed */
 
-  /* |change_cipher_spec is set when we receive a ChangeCipherSpec and reset by
-   * ssl3_get_finished. */
-  if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE &&
-      rr->type != SSL3_RT_ALERT) {
-    al = SSL_AD_UNEXPECTED_MESSAGE;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED);
-    goto f_err;
-  }
-
-  /* If we are expecting a ChangeCipherSpec, it is illegal to receive a
-   * Handshake record. */
-  if (rr->type == SSL3_RT_HANDSHAKE && (s->s3->flags & SSL3_FLAGS_EXPECT_CCS)) {
-    al = SSL_AD_UNEXPECTED_MESSAGE;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_HANDSHAKE_RECORD_BEFORE_CCS);
-    goto f_err;
-  }
-
   /* If the other end has shut down, throw anything we read away (even in
    * 'peek' mode) */
-  if (s->shutdown & SSL_RECEIVED_SHUTDOWN) {
+  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
     rr->length = 0;
-    s->rwstate = SSL_NOTHING;
+    ssl->rwstate = SSL_NOTHING;
     return 0;
   }
 
   if (type != 0 && type == rr->type) {
-    s->s3->warning_alert_count = 0;
+    ssl->s3->warning_alert_count = 0;
 
-    /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */
-    /* make sure that we are not getting application data when we are doing a
-     * handshake for the first time */
-    if (SSL_in_init(s) && type == SSL3_RT_APPLICATION_DATA &&
-        s->aead_read_ctx == NULL) {
+    /* Make sure that we are not getting application data when we are doing a
+     * handshake for the first time. */
+    if (SSL_in_init(ssl) && type == SSL3_RT_APPLICATION_DATA &&
+        ssl->aead_read_ctx == NULL) {
       /* TODO(davidben): Is this check redundant with the handshake_func
        * check? */
       al = SSL_AD_UNEXPECTED_MESSAGE;
@@ -507,14 +460,13 @@
       n = (unsigned int)len;
     }
 
-    memcpy(buf, &(rr->data[rr->off]), n);
+    memcpy(buf, rr->data, n);
     if (!peek) {
       rr->length -= n;
-      rr->off += n;
+      rr->data += n;
       if (rr->length == 0) {
-        rr->off = 0;
         /* The record has been consumed, so we may now clear the buffer. */
-        ssl_read_buffer_discard(s);
+        ssl_read_buffer_discard(ssl);
       }
     }
 
@@ -523,45 +475,40 @@
 
   /* Process unexpected records. */
 
-  if (rr->type == SSL3_RT_HANDSHAKE) {
+  if (type == SSL3_RT_APPLICATION_DATA && rr->type == SSL3_RT_HANDSHAKE) {
     /* If peer renegotiations are disabled, all out-of-order handshake records
      * are fatal. Renegotiations as a server are never supported. */
-    if (s->server || !ssl3_can_renegotiate(s)) {
+    if (ssl->server || !ssl3_can_renegotiate(ssl)) {
       al = SSL_AD_NO_RENEGOTIATION;
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
       goto f_err;
     }
 
-    /* HelloRequests may be fragmented across multiple records. */
-    const size_t size = sizeof(s->s3->handshake_fragment);
-    const size_t avail = size - s->s3->handshake_fragment_len;
-    const size_t todo = (rr->length < avail) ? rr->length : avail;
-    memcpy(s->s3->handshake_fragment + s->s3->handshake_fragment_len,
-           &rr->data[rr->off], todo);
-    rr->off += todo;
-    rr->length -= todo;
-    s->s3->handshake_fragment_len += todo;
-    if (s->s3->handshake_fragment_len < size) {
-      goto start; /* fragment was too small */
+    /* This must be a HelloRequest, possibly fragmented over multiple records.
+     * Consume data from the handshake protocol until it is complete. */
+    static const uint8_t kHelloRequest[] = {SSL3_MT_HELLO_REQUEST, 0, 0, 0};
+    while (ssl->s3->hello_request_len < sizeof(kHelloRequest)) {
+      if (rr->length == 0) {
+        /* Get a new record. */
+        goto start;
+      }
+      if (rr->data[0] != kHelloRequest[ssl->s3->hello_request_len]) {
+        al = SSL_AD_DECODE_ERROR;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HELLO_REQUEST);
+        goto f_err;
+      }
+      rr->data++;
+      rr->length--;
+      ssl->s3->hello_request_len++;
+    }
+    ssl->s3->hello_request_len = 0;
+
+    if (ssl->msg_callback) {
+      ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, kHelloRequest,
+                      sizeof(kHelloRequest), ssl, ssl->msg_callback_arg);
     }
 
-    /* Parse out and consume a HelloRequest. */
-    if (s->s3->handshake_fragment[0] != SSL3_MT_HELLO_REQUEST ||
-        s->s3->handshake_fragment[1] != 0 ||
-        s->s3->handshake_fragment[2] != 0 ||
-        s->s3->handshake_fragment[3] != 0) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HELLO_REQUEST);
-      goto f_err;
-    }
-    s->s3->handshake_fragment_len = 0;
-
-    if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE,
-                      s->s3->handshake_fragment, 4, s, s->msg_callback_arg);
-    }
-
-    if (!SSL_is_init_finished(s) || !s->s3->initial_handshake_complete) {
+    if (!SSL_is_init_finished(ssl) || !ssl->s3->initial_handshake_complete) {
       /* This cannot happen. If a handshake is in progress, |type| must be
        * |SSL3_RT_HANDSHAKE|. */
       assert(0);
@@ -569,7 +516,7 @@
       goto err;
     }
 
-    if (s->renegotiate_mode == ssl_renegotiate_ignore) {
+    if (ssl->renegotiate_mode == ssl_renegotiate_ignore) {
       goto start;
     }
 
@@ -577,16 +524,16 @@
      * protocol, namely in HTTPS, just before reading the HTTP response. Require
      * the record-layer be idle and avoid complexities of sending a handshake
      * record while an application_data record is being written. */
-    if (ssl_write_buffer_is_pending(s)) {
+    if (ssl_write_buffer_is_pending(ssl)) {
       al = SSL_AD_NO_RENEGOTIATION;
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION);
       goto f_err;
     }
 
     /* Begin a new handshake. */
-    s->s3->total_renegotiations++;
-    s->state = SSL_ST_CONNECT;
-    i = s->handshake_func(s);
+    ssl->s3->total_renegotiations++;
+    ssl->state = SSL_ST_CONNECT;
+    i = ssl->handshake_func(ssl);
     if (i < 0) {
       return i;
     }
@@ -609,29 +556,30 @@
       goto f_err;
     }
 
-    if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_ALERT, &rr->data[rr->off], 2, s,
-                      s->msg_callback_arg);
+    if (ssl->msg_callback) {
+      ssl->msg_callback(0, ssl->version, SSL3_RT_ALERT, rr->data, 2, ssl,
+                        ssl->msg_callback_arg);
     }
-    const uint8_t alert_level = rr->data[rr->off++];
-    const uint8_t alert_descr = rr->data[rr->off++];
+    const uint8_t alert_level = rr->data[0];
+    const uint8_t alert_descr = rr->data[1];
     rr->length -= 2;
+    rr->data += 2;
 
-    if (s->info_callback != NULL) {
-      cb = s->info_callback;
-    } else if (s->ctx->info_callback != NULL) {
-      cb = s->ctx->info_callback;
+    if (ssl->info_callback != NULL) {
+      cb = ssl->info_callback;
+    } else if (ssl->ctx->info_callback != NULL) {
+      cb = ssl->ctx->info_callback;
     }
 
     if (cb != NULL) {
       uint16_t alert = (alert_level << 8) | alert_descr;
-      cb(s, SSL_CB_READ_ALERT, alert);
+      cb(ssl, SSL_CB_READ_ALERT, alert);
     }
 
     if (alert_level == SSL3_AL_WARNING) {
-      s->s3->warn_alert = alert_descr;
+      ssl->s3->warn_alert = alert_descr;
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
-        s->shutdown |= SSL_RECEIVED_SHUTDOWN;
+        ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
         return 0;
       }
 
@@ -648,8 +596,8 @@
         goto f_err;
       }
 
-      s->s3->warning_alert_count++;
-      if (s->s3->warning_alert_count > kMaxWarningAlerts) {
+      ssl->s3->warning_alert_count++;
+      if (ssl->s3->warning_alert_count > kMaxWarningAlerts) {
         al = SSL_AD_UNEXPECTED_MESSAGE;
         OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_WARNING_ALERTS);
         goto f_err;
@@ -657,13 +605,13 @@
     } else if (alert_level == SSL3_AL_FATAL) {
       char tmp[16];
 
-      s->rwstate = SSL_NOTHING;
-      s->s3->fatal_alert = alert_descr;
+      ssl->rwstate = SSL_NOTHING;
+      ssl->s3->fatal_alert = alert_descr;
       OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
       BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr);
       ERR_add_error_data(2, "SSL alert number ", tmp);
-      s->shutdown |= SSL_RECEIVED_SHUTDOWN;
-      SSL_CTX_remove_session(s->ctx, s->session);
+      ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+      SSL_CTX_remove_session(ssl->ctx, ssl->session);
       return 0;
     } else {
       al = SSL_AD_ILLEGAL_PARAMETER;
@@ -674,97 +622,54 @@
     goto start;
   }
 
-  if (s->shutdown & SSL_SENT_SHUTDOWN) {
+  if (ssl->shutdown & SSL_SENT_SHUTDOWN) {
     /* close_notify has been sent, so discard all records other than alerts. */
     rr->length = 0;
     goto start;
   }
 
-  if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) {
-    /* 'Change Cipher Spec' is just a single byte, so we know exactly what the
-     * record payload has to look like */
-    if (rr->length != 1 || rr->off != 0 || rr->data[0] != SSL3_MT_CCS) {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
-      goto f_err;
-    }
-
-    /* Check we have a cipher to change to */
-    if (s->s3->tmp.new_cipher == NULL) {
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CCS_RECEIVED_EARLY);
-      goto f_err;
-    }
-
-    if (!(s->s3->flags & SSL3_FLAGS_EXPECT_CCS)) {
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CCS_RECEIVED_EARLY);
-      goto f_err;
-    }
-
-    s->s3->flags &= ~SSL3_FLAGS_EXPECT_CCS;
-
-    rr->length = 0;
-
-    if (s->msg_callback) {
-      s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s,
-                      s->msg_callback_arg);
-    }
-
-    s->s3->change_cipher_spec = 1;
-    if (!ssl3_do_change_cipher_spec(s)) {
-      goto err;
-    } else {
-      goto start;
-    }
-  }
-
-  /* We already handled these. */
-  assert(rr->type != SSL3_RT_CHANGE_CIPHER_SPEC && rr->type != SSL3_RT_ALERT &&
-         rr->type != SSL3_RT_HANDSHAKE);
-
   al = SSL_AD_UNEXPECTED_MESSAGE;
   OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
   return -1;
 }
 
-int ssl3_do_change_cipher_spec(SSL *s) {
+int ssl3_do_change_cipher_spec(SSL *ssl) {
   int i;
 
-  if (s->state & SSL_ST_ACCEPT) {
+  if (ssl->state & SSL_ST_ACCEPT) {
     i = SSL3_CHANGE_CIPHER_SERVER_READ;
   } else {
     i = SSL3_CHANGE_CIPHER_CLIENT_READ;
   }
 
-  if (s->s3->tmp.key_block == NULL) {
-    if (s->session == NULL || s->session->master_key_length == 0) {
+  if (ssl->s3->tmp.key_block == NULL) {
+    if (ssl->session == NULL || ssl->session->master_key_length == 0) {
       /* might happen if dtls1_read_bytes() calls this */
       OPENSSL_PUT_ERROR(SSL, SSL_R_CCS_RECEIVED_EARLY);
       return 0;
     }
 
-    s->session->cipher = s->s3->tmp.new_cipher;
-    if (!s->enc_method->setup_key_block(s)) {
+    ssl->session->cipher = ssl->s3->tmp.new_cipher;
+    if (!ssl->enc_method->setup_key_block(ssl)) {
       return 0;
     }
   }
 
-  if (!s->enc_method->change_cipher_state(s, i)) {
+  if (!ssl->enc_method->change_cipher_state(ssl, i)) {
     return 0;
   }
 
   return 1;
 }
 
-int ssl3_send_alert(SSL *s, int level, int desc) {
+int ssl3_send_alert(SSL *ssl, int level, int desc) {
   /* Map tls/ssl alert value to correct one */
-  desc = s->enc_method->alert_value(desc);
-  if (s->version == SSL3_VERSION && desc == SSL_AD_PROTOCOL_VERSION) {
+  desc = ssl->enc_method->alert_value(desc);
+  if (ssl->version == SSL3_VERSION && desc == SSL_AD_PROTOCOL_VERSION) {
     /* SSL 3.0 does not have protocol_version alerts */
     desc = SSL_AD_HANDSHAKE_FAILURE;
   }
@@ -773,17 +678,17 @@
   }
 
   /* If a fatal one, remove from cache */
-  if (level == 2 && s->session != NULL) {
-    SSL_CTX_remove_session(s->ctx, s->session);
+  if (level == 2 && ssl->session != NULL) {
+    SSL_CTX_remove_session(ssl->ctx, ssl->session);
   }
 
-  s->s3->alert_dispatch = 1;
-  s->s3->send_alert[0] = level;
-  s->s3->send_alert[1] = desc;
-  if (!ssl_write_buffer_is_pending(s)) {
+  ssl->s3->alert_dispatch = 1;
+  ssl->s3->send_alert[0] = level;
+  ssl->s3->send_alert[1] = desc;
+  if (!ssl_write_buffer_is_pending(ssl)) {
     /* Nothing is being written out, so the alert may be dispatched
      * immediately. */
-    return s->method->ssl_dispatch_alert(s);
+    return ssl->method->ssl_dispatch_alert(ssl);
   }
 
   /* else data is still being written out, we will get written some time in the
@@ -791,35 +696,35 @@
   return -1;
 }
 
-int ssl3_dispatch_alert(SSL *s) {
+int ssl3_dispatch_alert(SSL *ssl) {
   int i, j;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
 
-  s->s3->alert_dispatch = 0;
-  i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], 2);
+  ssl->s3->alert_dispatch = 0;
+  i = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2);
   if (i <= 0) {
-    s->s3->alert_dispatch = 1;
+    ssl->s3->alert_dispatch = 1;
   } else {
     /* Alert sent to BIO.  If it is important, flush it now. If the message
      * does not get sent due to non-blocking IO, we will not worry too much. */
-    if (s->s3->send_alert[0] == SSL3_AL_FATAL) {
-      BIO_flush(s->wbio);
+    if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
+      BIO_flush(ssl->wbio);
     }
 
-    if (s->msg_callback) {
-      s->msg_callback(1, s->version, SSL3_RT_ALERT, s->s3->send_alert, 2, s,
-                      s->msg_callback_arg);
+    if (ssl->msg_callback) {
+      ssl->msg_callback(1, ssl->version, SSL3_RT_ALERT, ssl->s3->send_alert, 2,
+                        ssl, ssl->msg_callback_arg);
     }
 
-    if (s->info_callback != NULL) {
-      cb = s->info_callback;
-    } else if (s->ctx->info_callback != NULL) {
-      cb = s->ctx->info_callback;
+    if (ssl->info_callback != NULL) {
+      cb = ssl->info_callback;
+    } else if (ssl->ctx->info_callback != NULL) {
+      cb = ssl->ctx->info_callback;
     }
 
     if (cb != NULL) {
-      j = (s->s3->send_alert[0] << 8) | s->s3->send_alert[1];
-      cb(s, SSL_CB_WRITE_ALERT, j);
+      j = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
+      cb(ssl, SSL_CB_WRITE_ALERT, j);
     }
   }
 
diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c
index 8cfa0e6..49a1a95 100644
--- a/src/ssl/s3_srvr.c
+++ b/src/ssl/s3_srvr.c
@@ -174,154 +174,154 @@
 #include "../crypto/dh/internal.h"
 
 
-int ssl3_accept(SSL *s) {
+int ssl3_accept(SSL *ssl) {
   BUF_MEM *buf = NULL;
   uint32_t alg_a;
   void (*cb)(const SSL *ssl, int type, int value) = NULL;
   int ret = -1;
   int new_state, state, skip = 0;
 
-  assert(s->handshake_func == ssl3_accept);
-  assert(s->server);
-  assert(!SSL_IS_DTLS(s));
+  assert(ssl->handshake_func == ssl3_accept);
+  assert(ssl->server);
+  assert(!SSL_IS_DTLS(ssl));
 
   ERR_clear_error();
   ERR_clear_system_error();
 
-  if (s->info_callback != NULL) {
-    cb = s->info_callback;
-  } else if (s->ctx->info_callback != NULL) {
-    cb = s->ctx->info_callback;
+  if (ssl->info_callback != NULL) {
+    cb = ssl->info_callback;
+  } else if (ssl->ctx->info_callback != NULL) {
+    cb = ssl->ctx->info_callback;
   }
 
-  s->in_handshake++;
+  ssl->in_handshake++;
 
-  if (s->cert == NULL) {
+  if (ssl->cert == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
     return -1;
   }
 
   for (;;) {
-    state = s->state;
+    state = ssl->state;
 
-    switch (s->state) {
+    switch (ssl->state) {
       case SSL_ST_ACCEPT:
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_START, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
         }
 
-        if (s->init_buf == NULL) {
+        if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
           if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
             ret = -1;
             goto end;
           }
-          s->init_buf = buf;
+          ssl->init_buf = buf;
           buf = NULL;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
 
         /* Enable a write buffer. This groups handshake messages within a flight
          * into a single write. */
-        if (!ssl_init_wbio_buffer(s, 1)) {
+        if (!ssl_init_wbio_buffer(ssl, 1)) {
           ret = -1;
           goto end;
         }
 
-        if (!ssl3_init_handshake_buffer(s)) {
+        if (!ssl3_init_handshake_buffer(ssl)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
           ret = -1;
           goto end;
         }
 
-        if (!s->s3->have_version) {
-          s->state = SSL3_ST_SR_INITIAL_BYTES;
+        if (!ssl->s3->have_version) {
+          ssl->state = SSL3_ST_SR_INITIAL_BYTES;
         } else {
-          s->state = SSL3_ST_SR_CLNT_HELLO_A;
+          ssl->state = SSL3_ST_SR_CLNT_HELLO_A;
         }
         break;
 
       case SSL3_ST_SR_INITIAL_BYTES:
-        ret = ssl3_get_initial_bytes(s);
+        ret = ssl3_get_initial_bytes(ssl);
         if (ret <= 0) {
           goto end;
         }
-        /* ssl3_get_initial_bytes sets s->state to one of
+        /* ssl3_get_initial_bytes sets ssl->state to one of
          * SSL3_ST_SR_V2_CLIENT_HELLO or SSL3_ST_SR_CLNT_HELLO_A on success. */
         break;
 
       case SSL3_ST_SR_V2_CLIENT_HELLO:
-        ret = ssl3_get_v2_client_hello(s);
+        ret = ssl3_get_v2_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SR_CLNT_HELLO_A;
+        ssl->state = SSL3_ST_SR_CLNT_HELLO_A;
         break;
 
       case SSL3_ST_SR_CLNT_HELLO_A:
       case SSL3_ST_SR_CLNT_HELLO_B:
       case SSL3_ST_SR_CLNT_HELLO_C:
       case SSL3_ST_SR_CLNT_HELLO_D:
-        s->shutdown = 0;
-        ret = ssl3_get_client_hello(s);
+        ssl->shutdown = 0;
+        ret = ssl3_get_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SW_SRVR_HELLO_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_HELLO_A:
       case SSL3_ST_SW_SRVR_HELLO_B:
-        ret = ssl3_send_server_hello(s);
+        ret = ssl3_send_server_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
-        if (s->hit) {
-          if (s->tlsext_ticket_expected) {
-            s->state = SSL3_ST_SW_SESSION_TICKET_A;
+        if (ssl->hit) {
+          if (ssl->tlsext_ticket_expected) {
+            ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
           } else {
-            s->state = SSL3_ST_SW_CHANGE_A;
+            ssl->state = SSL3_ST_SW_CHANGE_A;
           }
         } else {
-          s->state = SSL3_ST_SW_CERT_A;
+          ssl->state = SSL3_ST_SW_CERT_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_A:
       case SSL3_ST_SW_CERT_B:
-        if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-          ret = ssl3_send_server_certificate(s);
+        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+          ret = ssl3_send_server_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
-          if (s->s3->tmp.certificate_status_expected) {
-            s->state = SSL3_ST_SW_CERT_STATUS_A;
+          if (ssl->s3->tmp.certificate_status_expected) {
+            ssl->state = SSL3_ST_SW_CERT_STATUS_A;
           } else {
-            s->state = SSL3_ST_SW_KEY_EXCH_A;
+            ssl->state = SSL3_ST_SW_KEY_EXCH_A;
           }
         } else {
           skip = 1;
-          s->state = SSL3_ST_SW_KEY_EXCH_A;
+          ssl->state = SSL3_ST_SW_KEY_EXCH_A;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_STATUS_A:
       case SSL3_ST_SW_CERT_STATUS_B:
-        ret = ssl3_send_certificate_status(s);
+        ret = ssl3_send_certificate_status(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SW_KEY_EXCH_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_KEY_EXCH_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_KEY_EXCH_A:
       case SSL3_ST_SW_KEY_EXCH_B:
       case SSL3_ST_SW_KEY_EXCH_C:
-        alg_a = s->s3->tmp.new_cipher->algorithm_auth;
+        alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
 
         /* Send a ServerKeyExchange message if:
          * - The key exchange is ephemeral or anonymous
@@ -330,9 +330,9 @@
          *
          * TODO(davidben): This logic is currently duplicated in d1_srvr.c. Fix
          * this. In the meantime, keep them in sync. */
-        if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher) ||
-            ((alg_a & SSL_aPSK) && s->psk_identity_hint)) {
-          ret = ssl3_send_server_key_exchange(s);
+        if (ssl_cipher_requires_server_key_exchange(ssl->s3->tmp.new_cipher) ||
+            ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
+          ret = ssl3_send_server_key_exchange(ssl);
           if (ret <= 0) {
             goto end;
           }
@@ -340,33 +340,33 @@
           skip = 1;
         }
 
-        s->state = SSL3_ST_SW_CERT_REQ_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_CERT_REQ_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_REQ_A:
       case SSL3_ST_SW_CERT_REQ_B:
-        if (s->s3->tmp.cert_request) {
-          ret = ssl3_send_certificate_request(s);
+        if (ssl->s3->tmp.cert_request) {
+          ret = ssl3_send_certificate_request(ssl);
           if (ret <= 0) {
             goto end;
           }
         } else {
           skip = 1;
         }
-        s->state = SSL3_ST_SW_SRVR_DONE_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_SRVR_DONE_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_DONE_A:
       case SSL3_ST_SW_SRVR_DONE_B:
-        ret = ssl3_send_server_done(s);
+        ret = ssl3_send_server_done(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
-        s->state = SSL3_ST_SW_FLUSH;
-        s->init_num = 0;
+        ssl->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
+        ssl->state = SSL3_ST_SW_FLUSH;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_FLUSH:
@@ -375,151 +375,149 @@
          * in PR#1939. The proposed fix doesn't completely resolve this issue
          * as buggy implementations of BIO_CTRL_PENDING still exist. So instead
          * we just flush unconditionally. */
-        s->rwstate = SSL_WRITING;
-        if (BIO_flush(s->wbio) <= 0) {
+        ssl->rwstate = SSL_WRITING;
+        if (BIO_flush(ssl->wbio) <= 0) {
           ret = -1;
           goto end;
         }
-        s->rwstate = SSL_NOTHING;
+        ssl->rwstate = SSL_NOTHING;
 
-        s->state = s->s3->tmp.next_state;
+        ssl->state = ssl->s3->tmp.next_state;
         break;
 
       case SSL3_ST_SR_CERT_A:
       case SSL3_ST_SR_CERT_B:
-        if (s->s3->tmp.cert_request) {
-          ret = ssl3_get_client_certificate(s);
+        if (ssl->s3->tmp.cert_request) {
+          ret = ssl3_get_client_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
         }
-        s->init_num = 0;
-        s->state = SSL3_ST_SR_KEY_EXCH_A;
+        ssl->init_num = 0;
+        ssl->state = SSL3_ST_SR_KEY_EXCH_A;
         break;
 
       case SSL3_ST_SR_KEY_EXCH_A:
       case SSL3_ST_SR_KEY_EXCH_B:
       case SSL3_ST_SR_KEY_EXCH_C:
-        ret = ssl3_get_client_key_exchange(s);
+        ret = ssl3_get_client_key_exchange(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SR_CERT_VRFY_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SR_CERT_VRFY_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SR_CERT_VRFY_A:
       case SSL3_ST_SR_CERT_VRFY_B:
-        ret = ssl3_get_cert_verify(s);
+        ret = ssl3_get_cert_verify(ssl);
         if (ret <= 0) {
           goto end;
         }
 
-        s->state = SSL3_ST_SR_CHANGE;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SR_CHANGE;
+        ssl->init_num = 0;
         break;
 
-      case SSL3_ST_SR_CHANGE: {
-        char next_proto_neg = 0;
-        char channel_id = 0;
-        next_proto_neg = s->s3->next_proto_neg_seen;
-        channel_id = s->s3->tlsext_channel_id_valid;
+      case SSL3_ST_SR_CHANGE:
+        ret = ssl->method->ssl_read_change_cipher_spec(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
 
-        /* At this point, the next message must be entirely behind a
-         * ChangeCipherSpec. */
-        if (!ssl3_expect_change_cipher_spec(s)) {
+        if (!ssl3_do_change_cipher_spec(ssl)) {
           ret = -1;
           goto end;
         }
-        if (next_proto_neg) {
-          s->state = SSL3_ST_SR_NEXT_PROTO_A;
-        } else if (channel_id) {
-          s->state = SSL3_ST_SR_CHANNEL_ID_A;
+
+        if (ssl->s3->next_proto_neg_seen) {
+          ssl->state = SSL3_ST_SR_NEXT_PROTO_A;
+        } else if (ssl->s3->tlsext_channel_id_valid) {
+          ssl->state = SSL3_ST_SR_CHANNEL_ID_A;
         } else {
-          s->state = SSL3_ST_SR_FINISHED_A;
+          ssl->state = SSL3_ST_SR_FINISHED_A;
         }
         break;
-      }
 
       case SSL3_ST_SR_NEXT_PROTO_A:
       case SSL3_ST_SR_NEXT_PROTO_B:
-        ret = ssl3_get_next_proto(s);
+        ret = ssl3_get_next_proto(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->init_num = 0;
-        if (s->s3->tlsext_channel_id_valid) {
-          s->state = SSL3_ST_SR_CHANNEL_ID_A;
+        ssl->init_num = 0;
+        if (ssl->s3->tlsext_channel_id_valid) {
+          ssl->state = SSL3_ST_SR_CHANNEL_ID_A;
         } else {
-          s->state = SSL3_ST_SR_FINISHED_A;
+          ssl->state = SSL3_ST_SR_FINISHED_A;
         }
         break;
 
       case SSL3_ST_SR_CHANNEL_ID_A:
       case SSL3_ST_SR_CHANNEL_ID_B:
-        ret = ssl3_get_channel_id(s);
+        ret = ssl3_get_channel_id(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->init_num = 0;
-        s->state = SSL3_ST_SR_FINISHED_A;
+        ssl->init_num = 0;
+        ssl->state = SSL3_ST_SR_FINISHED_A;
         break;
 
       case SSL3_ST_SR_FINISHED_A:
       case SSL3_ST_SR_FINISHED_B:
-        ret =
-            ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B);
+        ret = ssl3_get_finished(ssl, SSL3_ST_SR_FINISHED_A,
+                                SSL3_ST_SR_FINISHED_B);
         if (ret <= 0) {
           goto end;
         }
 
-        if (s->hit) {
-          s->state = SSL_ST_OK;
-        } else if (s->tlsext_ticket_expected) {
-          s->state = SSL3_ST_SW_SESSION_TICKET_A;
+        if (ssl->hit) {
+          ssl->state = SSL_ST_OK;
+        } else if (ssl->tlsext_ticket_expected) {
+          ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
         } else {
-          s->state = SSL3_ST_SW_CHANGE_A;
+          ssl->state = SSL3_ST_SW_CHANGE_A;
         }
         /* If this is a full handshake with ChannelID then record the hashshake
-         * hashes in |s->session| in case we need them to verify a ChannelID
+         * hashes in |ssl->session| in case we need them to verify a ChannelID
          * signature on a resumption of this session in the future. */
-        if (!s->hit && s->s3->tlsext_channel_id_valid) {
-          ret = tls1_record_handshake_hashes_for_channel_id(s);
+        if (!ssl->hit && ssl->s3->tlsext_channel_id_valid) {
+          ret = tls1_record_handshake_hashes_for_channel_id(ssl);
           if (ret <= 0) {
             goto end;
           }
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SESSION_TICKET_A:
       case SSL3_ST_SW_SESSION_TICKET_B:
-        ret = ssl3_send_new_session_ticket(s);
+        ret = ssl3_send_new_session_ticket(ssl);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SW_CHANGE_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_CHANGE_A;
+        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CHANGE_A:
       case SSL3_ST_SW_CHANGE_B:
-        s->session->cipher = s->s3->tmp.new_cipher;
-        if (!s->enc_method->setup_key_block(s)) {
+        ssl->session->cipher = ssl->s3->tmp.new_cipher;
+        if (!ssl->enc_method->setup_key_block(ssl)) {
           ret = -1;
           goto end;
         }
 
-        ret = ssl3_send_change_cipher_spec(s, SSL3_ST_SW_CHANGE_A,
+        ret = ssl3_send_change_cipher_spec(ssl, SSL3_ST_SW_CHANGE_A,
                                            SSL3_ST_SW_CHANGE_B);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SW_FINISHED_A;
-        s->init_num = 0;
+        ssl->state = SSL3_ST_SW_FINISHED_A;
+        ssl->init_num = 0;
 
-        if (!s->enc_method->change_cipher_state(
-                s, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+        if (!ssl->enc_method->change_cipher_state(
+                ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
           goto end;
         }
@@ -527,49 +525,49 @@
 
       case SSL3_ST_SW_FINISHED_A:
       case SSL3_ST_SW_FINISHED_B:
-        ret =
-            ssl3_send_finished(s, SSL3_ST_SW_FINISHED_A, SSL3_ST_SW_FINISHED_B,
-                               s->enc_method->server_finished_label,
-                               s->enc_method->server_finished_label_len);
+        ret = ssl3_send_finished(ssl, SSL3_ST_SW_FINISHED_A,
+                                 SSL3_ST_SW_FINISHED_B,
+                                 ssl->enc_method->server_finished_label,
+                                 ssl->enc_method->server_finished_label_len);
         if (ret <= 0) {
           goto end;
         }
-        s->state = SSL3_ST_SW_FLUSH;
-        if (s->hit) {
-          s->s3->tmp.next_state = SSL3_ST_SR_CHANGE;
+        ssl->state = SSL3_ST_SW_FLUSH;
+        if (ssl->hit) {
+          ssl->s3->tmp.next_state = SSL3_ST_SR_CHANGE;
         } else {
-          s->s3->tmp.next_state = SSL_ST_OK;
+          ssl->s3->tmp.next_state = SSL_ST_OK;
         }
-        s->init_num = 0;
+        ssl->init_num = 0;
         break;
 
       case SSL_ST_OK:
         /* clean a few things up */
-        ssl3_cleanup_key_block(s);
+        ssl3_cleanup_key_block(ssl);
 
-        BUF_MEM_free(s->init_buf);
-        s->init_buf = NULL;
+        BUF_MEM_free(ssl->init_buf);
+        ssl->init_buf = NULL;
 
         /* remove buffering on output */
-        ssl_free_wbio_buffer(s);
+        ssl_free_wbio_buffer(ssl);
 
-        s->init_num = 0;
+        ssl->init_num = 0;
 
         /* If we aren't retaining peer certificates then we can discard it
          * now. */
-        if (s->ctx->retain_only_sha256_of_client_certs) {
-          X509_free(s->session->peer);
-          s->session->peer = NULL;
-          sk_X509_pop_free(s->session->cert_chain, X509_free);
-          s->session->cert_chain = NULL;
+        if (ssl->ctx->retain_only_sha256_of_client_certs) {
+          X509_free(ssl->session->peer);
+          ssl->session->peer = NULL;
+          sk_X509_pop_free(ssl->session->cert_chain, X509_free);
+          ssl->session->cert_chain = NULL;
         }
 
-        s->s3->initial_handshake_complete = 1;
+        ssl->s3->initial_handshake_complete = 1;
 
-        ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
+        ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
 
         if (cb != NULL) {
-          cb(s, SSL_CB_HANDSHAKE_DONE, 1);
+          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         }
 
         ret = 1;
@@ -581,34 +579,35 @@
         goto end;
     }
 
-    if (!s->s3->tmp.reuse_message && !skip && cb != NULL && s->state != state) {
-      new_state = s->state;
-      s->state = state;
-      cb(s, SSL_CB_ACCEPT_LOOP, 1);
-      s->state = new_state;
+    if (!ssl->s3->tmp.reuse_message && !skip && cb != NULL &&
+        ssl->state != state) {
+      new_state = ssl->state;
+      ssl->state = state;
+      cb(ssl, SSL_CB_ACCEPT_LOOP, 1);
+      ssl->state = new_state;
     }
     skip = 0;
   }
 
 end:
-  s->in_handshake--;
+  ssl->in_handshake--;
   BUF_MEM_free(buf);
   if (cb != NULL) {
-    cb(s, SSL_CB_ACCEPT_EXIT, ret);
+    cb(ssl, SSL_CB_ACCEPT_EXIT, ret);
   }
   return ret;
 }
 
-int ssl3_get_initial_bytes(SSL *s) {
+int ssl3_get_initial_bytes(SSL *ssl) {
   /* Read the first 5 bytes, the size of the TLS record header. This is
    * sufficient to detect a V2ClientHello and ensures that we never read beyond
    * the first record. */
-  int ret = ssl_read_buffer_extend_to(s, SSL3_RT_HEADER_LENGTH);
+  int ret = ssl_read_buffer_extend_to(ssl, SSL3_RT_HEADER_LENGTH);
   if (ret <= 0) {
     return ret;
   }
-  assert(ssl_read_buffer_len(s) == SSL3_RT_HEADER_LENGTH);
-  const uint8_t *p = ssl_read_buffer(s);
+  assert(ssl_read_buffer_len(ssl) == SSL3_RT_HEADER_LENGTH);
+  const uint8_t *p = ssl_read_buffer(ssl);
 
   /* Some dedicated error codes for protocol mixups should the application wish
    * to interpret them differently. (These do not overlap with ClientHello or
@@ -629,16 +628,16 @@
   if ((p[0] & 0x80) && p[2] == SSL2_MT_CLIENT_HELLO &&
       p[3] >= SSL3_VERSION_MAJOR) {
     /* This is a V2ClientHello. */
-    s->state = SSL3_ST_SR_V2_CLIENT_HELLO;
+    ssl->state = SSL3_ST_SR_V2_CLIENT_HELLO;
     return 1;
   }
 
   /* Fall through to the standard logic. */
-  s->state = SSL3_ST_SR_CLNT_HELLO_A;
+  ssl->state = SSL3_ST_SR_CLNT_HELLO_A;
   return 1;
 }
 
-int ssl3_get_v2_client_hello(SSL *s) {
+int ssl3_get_v2_client_hello(SSL *ssl) {
   const uint8_t *p;
   int ret;
   CBS v2_client_hello, cipher_specs, session_id, challenge;
@@ -649,8 +648,8 @@
   uint8_t random[SSL3_RANDOM_SIZE];
 
   /* Determine the length of the V2ClientHello. */
-  assert(ssl_read_buffer_len(s) >= SSL3_RT_HEADER_LENGTH);
-  p = ssl_read_buffer(s);
+  assert(ssl_read_buffer_len(ssl) >= SSL3_RT_HEADER_LENGTH);
+  p = ssl_read_buffer(ssl);
   msg_length = ((p[0] & 0x7f) << 8) | p[1];
   if (msg_length > (1024 * 4)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE);
@@ -665,22 +664,22 @@
   }
 
   /* Read the remainder of the V2ClientHello. */
-  ret = ssl_read_buffer_extend_to(s, 2 + msg_length);
+  ret = ssl_read_buffer_extend_to(ssl, 2 + msg_length);
   if (ret <= 0) {
     return ret;
   }
-  assert(ssl_read_buffer_len(s) == msg_length + 2);
-  CBS_init(&v2_client_hello, ssl_read_buffer(s) + 2, msg_length);
+  assert(ssl_read_buffer_len(ssl) == msg_length + 2);
+  CBS_init(&v2_client_hello, ssl_read_buffer(ssl) + 2, msg_length);
 
   /* The V2ClientHello without the length is incorporated into the handshake
    * hash. */
-  if (!ssl3_update_handshake_hash(s, CBS_data(&v2_client_hello),
+  if (!ssl3_update_handshake_hash(ssl, CBS_data(&v2_client_hello),
                                   CBS_len(&v2_client_hello))) {
     return -1;
   }
-  if (s->msg_callback) {
-    s->msg_callback(0, SSL2_VERSION, 0, CBS_data(&v2_client_hello),
-                    CBS_len(&v2_client_hello), s, s->msg_callback_arg);
+  if (ssl->msg_callback) {
+    ssl->msg_callback(0, SSL2_VERSION, 0, CBS_data(&v2_client_hello),
+                    CBS_len(&v2_client_hello), ssl, ssl->msg_callback_arg);
   }
 
   if (!CBS_get_u8(&v2_client_hello, &msg_type) ||
@@ -711,8 +710,8 @@
 
   /* Write out an equivalent SSLv3 ClientHello. */
   CBB_zero(&client_hello);
-  if (!CBB_init_fixed(&client_hello, (uint8_t *)s->init_buf->data,
-                      s->init_buf->max) ||
+  if (!CBB_init_fixed(&client_hello, (uint8_t *)ssl->init_buf->data,
+                      ssl->init_buf->max) ||
       !CBB_add_u8(&client_hello, SSL3_MT_CLIENT_HELLO) ||
       !CBB_add_u24_length_prefixed(&client_hello, &hello_body) ||
       !CBB_add_u16(&hello_body, version) ||
@@ -754,19 +753,19 @@
   }
 
   /* Mark the message for "re"-use by the version-specific method. */
-  s->s3->tmp.reuse_message = 1;
-  s->s3->tmp.message_type = SSL3_MT_CLIENT_HELLO;
+  ssl->s3->tmp.reuse_message = 1;
+  ssl->s3->tmp.message_type = SSL3_MT_CLIENT_HELLO;
   /* The handshake message header is 4 bytes. */
-  s->s3->tmp.message_size = len - 4;
+  ssl->s3->tmp.message_size = len - 4;
 
   /* Consume and discard the V2ClientHello. */
-  ssl_read_buffer_consume(s, 2 + msg_length);
-  ssl_read_buffer_discard(s);
+  ssl_read_buffer_consume(ssl, 2 + msg_length);
+  ssl_read_buffer_discard(ssl);
 
   return 1;
 }
 
-int ssl3_get_client_hello(SSL *s) {
+int ssl3_get_client_hello(SSL *ssl) {
   int ok, al = SSL_AD_INTERNAL_ERROR, ret = -1;
   long n;
   const SSL_CIPHER *c;
@@ -781,11 +780,11 @@
    * and we get SSLv3, we will respond with TLSv1, This down switching should
    * be handled by a different method. If we are SSLv3, we will respond with
    * SSLv3, even if prompted with TLSv1. */
-  switch (s->state) {
+  switch (ssl->state) {
     case SSL3_ST_SR_CLNT_HELLO_A:
     case SSL3_ST_SR_CLNT_HELLO_B:
-      n = s->method->ssl_get_message(
-          s, SSL3_ST_SR_CLNT_HELLO_A, SSL3_ST_SR_CLNT_HELLO_B,
+      n = ssl->method->ssl_get_message(
+          ssl, SSL3_ST_SR_CLNT_HELLO_A, SSL3_ST_SR_CLNT_HELLO_B,
           SSL3_MT_CLIENT_HELLO, SSL3_RT_MAX_PLAIN_LENGTH,
           ssl_hash_message, &ok);
 
@@ -793,18 +792,18 @@
         return n;
       }
 
-      s->state = SSL3_ST_SR_CLNT_HELLO_C;
+      ssl->state = SSL3_ST_SR_CLNT_HELLO_C;
       /* fallthrough */
     case SSL3_ST_SR_CLNT_HELLO_C:
     case SSL3_ST_SR_CLNT_HELLO_D:
       /* We have previously parsed the ClientHello message, and can't call
        * ssl_get_message again without hashing the message into the Finished
        * digest again. */
-      n = s->init_num;
+      n = ssl->init_num;
 
       memset(&early_ctx, 0, sizeof(early_ctx));
-      early_ctx.ssl = s;
-      early_ctx.client_hello = s->init_msg;
+      early_ctx.ssl = ssl;
+      early_ctx.client_hello = ssl->init_msg;
       early_ctx.client_hello_len = n;
       if (!ssl_early_callback_init(&early_ctx)) {
         al = SSL_AD_DECODE_ERROR;
@@ -812,12 +811,12 @@
         goto f_err;
       }
 
-      if (s->state == SSL3_ST_SR_CLNT_HELLO_C &&
-          s->ctx->select_certificate_cb != NULL) {
-        s->state = SSL3_ST_SR_CLNT_HELLO_D;
-        switch (s->ctx->select_certificate_cb(&early_ctx)) {
+      if (ssl->state == SSL3_ST_SR_CLNT_HELLO_C &&
+          ssl->ctx->select_certificate_cb != NULL) {
+        ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
+        switch (ssl->ctx->select_certificate_cb(&early_ctx)) {
           case 0:
-            s->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
+            ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
             goto err;
 
           case -1:
@@ -830,7 +829,7 @@
             /* fallthrough */;
         }
       }
-      s->state = SSL3_ST_SR_CLNT_HELLO_D;
+      ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
       break;
 
     default:
@@ -838,7 +837,7 @@
       return -1;
   }
 
-  CBS_init(&client_hello, s->init_msg, n);
+  CBS_init(&client_hello, ssl->init_msg, n);
   if (!CBS_get_u16(&client_hello, &client_version) ||
       !CBS_get_bytes(&client_hello, &client_random, SSL3_RANDOM_SIZE) ||
       !CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
@@ -850,12 +849,12 @@
 
   /* use version from inside client hello, not from record header (may differ:
    * see RFC 2246, Appendix E, second paragraph) */
-  s->client_version = client_version;
+  ssl->client_version = client_version;
 
   /* Load the client random. */
-  memcpy(s->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE);
+  memcpy(ssl->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE);
 
-  if (SSL_IS_DTLS(s)) {
+  if (SSL_IS_DTLS(ssl)) {
     CBS cookie;
 
     if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) ||
@@ -871,40 +870,40 @@
    *
    * TODO(davidben): Clean up the order of events around ClientHello
    * processing. */
-  if (!s->s3->have_version) {
+  if (!ssl->s3->have_version) {
     /* Select version to use */
-    uint16_t version = ssl3_get_mutual_version(s, client_version);
+    uint16_t version = ssl3_get_mutual_version(ssl, client_version);
     if (version == 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
-      s->version = s->client_version;
+      ssl->version = ssl->client_version;
       al = SSL_AD_PROTOCOL_VERSION;
       goto f_err;
     }
-    s->version = version;
-    s->enc_method = ssl3_get_enc_method(version);
-    assert(s->enc_method != NULL);
-    /* At this point, the connection's version is known and |s->version| is
+    ssl->version = version;
+    ssl->enc_method = ssl3_get_enc_method(version);
+    assert(ssl->enc_method != NULL);
+    /* At this point, the connection's version is known and |ssl->version| is
      * fixed. Begin enforcing the record-layer version. */
-    s->s3->have_version = 1;
-  } else if (SSL_IS_DTLS(s) ? (s->client_version > s->version)
-                            : (s->client_version < s->version)) {
+    ssl->s3->have_version = 1;
+  } else if (SSL_IS_DTLS(ssl) ? (ssl->client_version > ssl->version)
+                            : (ssl->client_version < ssl->version)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
     al = SSL_AD_PROTOCOL_VERSION;
     goto f_err;
   }
 
-  s->hit = 0;
+  ssl->hit = 0;
   int send_new_ticket = 0;
-  switch (ssl_get_prev_session(s, &session, &send_new_ticket, &early_ctx)) {
+  switch (ssl_get_prev_session(ssl, &session, &send_new_ticket, &early_ctx)) {
     case ssl_session_success:
       break;
     case ssl_session_error:
       goto err;
     case ssl_session_retry:
-      s->rwstate = SSL_PENDING_SESSION;
+      ssl->rwstate = SSL_PENDING_SESSION;
       goto err;
   }
-  s->tlsext_ticket_expected = send_new_ticket;
+  ssl->tlsext_ticket_expected = send_new_ticket;
 
   /* The EMS state is needed when making the resumption decision, but
    * extensions are not normally parsed until later. This detects the EMS
@@ -913,7 +912,7 @@
   const uint8_t *ems_data;
   size_t ems_len;
   int have_extended_master_secret =
-      s->version != SSL3_VERSION &&
+      ssl->version != SSL3_VERSION &&
       SSL_early_callback_ctx_extension_get(&early_ctx,
                                            TLSEXT_TYPE_extended_master_secret,
                                            &ems_data, &ems_len) &&
@@ -929,34 +928,35 @@
       goto f_err;
     }
 
-    s->hit =
+    ssl->hit =
         /* Only resume if the session's version matches the negotiated version:
          * most clients do not accept a mismatch. */
-        s->version == session->ssl_version &&
+        ssl->version == session->ssl_version &&
         /* If the client offers the EMS extension, but the previous session
          * didn't use it, then negotiate a new session. */
         have_extended_master_secret == session->extended_master_secret;
   }
 
-  if (s->hit) {
+  if (ssl->hit) {
     /* Use the new session. */
-    SSL_SESSION_free(s->session);
-    s->session = session;
+    SSL_SESSION_free(ssl->session);
+    ssl->session = session;
     session = NULL;
 
-    s->verify_result = s->session->verify_result;
+    ssl->verify_result = ssl->session->verify_result;
   } else {
-    if (!ssl_get_new_session(s, 1 /* server */)) {
+    if (!ssl_get_new_session(ssl, 1 /* server */)) {
       goto err;
     }
 
     /* Clear the session ID if we want the session to be single-use. */
-    if (!(s->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
-      s->session->session_id_length = 0;
+    if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
+      ssl->session->session_id_length = 0;
     }
   }
 
-  if (s->ctx->dos_protection_cb != NULL && s->ctx->dos_protection_cb(&early_ctx) == 0) {
+  if (ssl->ctx->dos_protection_cb != NULL &&
+      ssl->ctx->dos_protection_cb(&early_ctx) == 0) {
     /* Connection rejected for DOS reasons. */
     al = SSL_AD_ACCESS_DENIED;
     OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
@@ -973,16 +973,16 @@
     goto f_err;
   }
 
-  ciphers = ssl_bytes_to_cipher_list(s, &cipher_suites);
+  ciphers = ssl_bytes_to_cipher_list(ssl, &cipher_suites);
   if (ciphers == NULL) {
     goto err;
   }
 
   /* If it is a hit, check that the cipher is in the list. */
-  if (s->hit) {
+  if (ssl->hit) {
     size_t j;
     int found_cipher = 0;
-    uint32_t id = s->session->cipher->id;
+    uint32_t id = ssl->session->cipher->id;
 
     for (j = 0; j < sk_SSL_CIPHER_num(ciphers); j++) {
       c = sk_SSL_CIPHER_value(ciphers, j);
@@ -1010,8 +1010,8 @@
   }
 
   /* TLS extensions. */
-  if (s->version >= SSL3_VERSION &&
-      !ssl_parse_clienthello_tlsext(s, &client_hello)) {
+  if (ssl->version >= SSL3_VERSION &&
+      !ssl_parse_clienthello_tlsext(ssl, &client_hello)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     goto err;
   }
@@ -1024,14 +1024,14 @@
     goto f_err;
   }
 
-  if (have_extended_master_secret != s->s3->tmp.extended_master_secret) {
+  if (have_extended_master_secret != ssl->s3->tmp.extended_master_secret) {
     al = SSL_AD_INTERNAL_ERROR;
     OPENSSL_PUT_ERROR(SSL, SSL_R_EMS_STATE_INCONSISTENT);
     goto f_err;
   }
 
   /* Given ciphers and SSL_get_ciphers, we must pick a cipher */
-  if (!s->hit) {
+  if (!ssl->hit) {
     if (ciphers == NULL) {
       al = SSL_AD_ILLEGAL_PARAMETER;
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_PASSED);
@@ -1039,54 +1039,54 @@
     }
 
     /* Let cert callback update server certificates if required */
-    if (s->cert->cert_cb) {
-      int rv = s->cert->cert_cb(s, s->cert->cert_cb_arg);
+    if (ssl->cert->cert_cb) {
+      int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
       if (rv == 0) {
         al = SSL_AD_INTERNAL_ERROR;
         OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
         goto f_err;
       }
       if (rv < 0) {
-        s->rwstate = SSL_X509_LOOKUP;
+        ssl->rwstate = SSL_X509_LOOKUP;
         goto err;
       }
-      s->rwstate = SSL_NOTHING;
+      ssl->rwstate = SSL_NOTHING;
     }
-    c = ssl3_choose_cipher(s, ciphers, ssl_get_cipher_preferences(s));
+    c = ssl3_choose_cipher(ssl, ciphers, ssl_get_cipher_preferences(ssl));
 
     if (c == NULL) {
       al = SSL_AD_HANDSHAKE_FAILURE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
       goto f_err;
     }
-    s->s3->tmp.new_cipher = c;
+    ssl->s3->tmp.new_cipher = c;
 
     /* Determine whether to request a client certificate. */
-    s->s3->tmp.cert_request = !!(s->verify_mode & SSL_VERIFY_PEER);
+    ssl->s3->tmp.cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
     /* Only request a certificate if Channel ID isn't negotiated. */
-    if ((s->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
-        s->s3->tlsext_channel_id_valid) {
-      s->s3->tmp.cert_request = 0;
+    if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
+        ssl->s3->tlsext_channel_id_valid) {
+      ssl->s3->tmp.cert_request = 0;
     }
     /* Plain PSK forbids Certificate and CertificateRequest. */
-    if (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK) {
-      s->s3->tmp.cert_request = 0;
+    if (ssl->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK) {
+      ssl->s3->tmp.cert_request = 0;
     }
   } else {
     /* Session-id reuse */
-    s->s3->tmp.new_cipher = s->session->cipher;
-    s->s3->tmp.cert_request = 0;
+    ssl->s3->tmp.new_cipher = ssl->session->cipher;
+    ssl->s3->tmp.cert_request = 0;
   }
 
   /* Now that the cipher is known, initialize the handshake hash. */
-  if (!ssl3_init_handshake_hash(s)) {
+  if (!ssl3_init_handshake_hash(ssl)) {
     goto f_err;
   }
 
   /* In TLS 1.2, client authentication requires hashing the handshake transcript
    * under a different hash. Otherwise, release the handshake buffer. */
-  if (!SSL_USE_SIGALGS(s) || !s->s3->tmp.cert_request) {
-    ssl3_free_handshake_buffer(s);
+  if (!SSL_USE_SIGALGS(ssl) || !ssl->s3->tmp.cert_request) {
+    ssl3_free_handshake_buffer(ssl);
   }
 
   /* we now have the following setup;
@@ -1095,17 +1095,15 @@
    * ciphers            - the clients prefered list of ciphers
    * compression        - basically ignored right now
    * ssl version is set - sslv3
-   * s->session         - The ssl session has been setup.
-   * s->hit             - session reuse flag
-   * s->tmp.new_cipher  - the new cipher to use. */
+   * ssl->session         - The ssl session has been setup.
+   * ssl->hit             - session reuse flag
+   * ssl->tmp.new_cipher  - the new cipher to use. */
 
-  if (ret < 0) {
-    ret = -ret;
-  }
+  ret = 1;
 
   if (0) {
   f_err:
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
   }
 
 err:
@@ -1191,324 +1189,203 @@
   return ssl_do_write(ssl);
 }
 
-int ssl3_send_server_done(SSL *s) {
-  if (s->state == SSL3_ST_SW_SRVR_DONE_A) {
-    if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_DONE, 0)) {
+int ssl3_send_server_done(SSL *ssl) {
+  if (ssl->state == SSL3_ST_SW_SRVR_DONE_A) {
+    if (!ssl_set_handshake_header(ssl, SSL3_MT_SERVER_DONE, 0)) {
       return -1;
     }
-    s->state = SSL3_ST_SW_SRVR_DONE_B;
+    ssl->state = SSL3_ST_SW_SRVR_DONE_B;
   }
 
   /* SSL3_ST_SW_SRVR_DONE_B */
-  return ssl_do_write(s);
+  return ssl_do_write(ssl);
 }
 
-int ssl3_send_server_key_exchange(SSL *s) {
-  DH *dh = NULL, *dhp;
-  EC_KEY *ecdh = NULL;
-  uint8_t *encodedPoint = NULL;
-  int encodedlen = 0;
-  uint16_t curve_id = 0;
-  BN_CTX *bn_ctx = NULL;
-  const char *psk_identity_hint = NULL;
-  size_t psk_identity_hint_len = 0;
-  size_t sig_len;
-  size_t max_sig_len;
-  uint8_t *p, *d;
-  int al, i;
-  uint32_t alg_k;
-  uint32_t alg_a;
-  int n;
-  CERT *cert;
-  BIGNUM *r[4];
-  int nr[4];
-  BUF_MEM *buf;
-  EVP_MD_CTX md_ctx;
-
-  if (s->state == SSL3_ST_SW_KEY_EXCH_C) {
-    return ssl_do_write(s);
+int ssl3_send_server_key_exchange(SSL *ssl) {
+  if (ssl->state == SSL3_ST_SW_KEY_EXCH_C) {
+    return ssl_do_write(ssl);
   }
 
-  if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-    if (!ssl_has_private_key(s)) {
-      al = SSL_AD_INTERNAL_ERROR;
-      goto f_err;
-    }
-    max_sig_len = ssl_private_key_max_signature_len(s);
-  } else {
-    max_sig_len = 0;
+  CBB cbb, child;
+  if (!CBB_init_fixed(&cbb, ssl_handshake_start(ssl),
+                      ssl->init_buf->max - SSL_HM_HEADER_LENGTH(ssl))) {
+    goto err;
   }
 
-  EVP_MD_CTX_init(&md_ctx);
-  enum ssl_private_key_result_t sign_result;
-  if (s->state == SSL3_ST_SW_KEY_EXCH_A) {
-    alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-    alg_a = s->s3->tmp.new_cipher->algorithm_auth;
-    cert = s->cert;
+  if (ssl->state == SSL3_ST_SW_KEY_EXCH_A) {
+    /* This is the first iteration, so write parameters. */
+    uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
+    uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
 
-    buf = s->init_buf;
-
-    r[0] = r[1] = r[2] = r[3] = NULL;
-    n = 0;
+    /* PSK ciphers begin with an identity hint. */
     if (alg_a & SSL_aPSK) {
-      /* size for PSK identity hint */
-      psk_identity_hint = s->psk_identity_hint;
-      if (psk_identity_hint) {
-        psk_identity_hint_len = strlen(psk_identity_hint);
-      } else {
-        psk_identity_hint_len = 0;
+      size_t len =
+          (ssl->psk_identity_hint == NULL) ? 0 : strlen(ssl->psk_identity_hint);
+      if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+          !CBB_add_bytes(&child, (const uint8_t *)ssl->psk_identity_hint,
+                         len)) {
+        goto err;
       }
-      n += 2 + psk_identity_hint_len;
     }
 
     if (alg_k & SSL_kDHE) {
-      dhp = cert->dh_tmp;
-      if (dhp == NULL && s->cert->dh_tmp_cb != NULL) {
-        dhp = s->cert->dh_tmp_cb(s, 0, 1024);
+      /* Determine the group to use. */
+      DH *params = ssl->cert->dh_tmp;
+      if (params == NULL && ssl->cert->dh_tmp_cb != NULL) {
+        params = ssl->cert->dh_tmp_cb(ssl, 0, 1024);
       }
-      if (dhp == NULL) {
-        al = SSL_AD_HANDSHAKE_FAILURE;
+      if (params == NULL) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_DH_KEY);
-        goto f_err;
-      }
-
-      if (s->s3->tmp.dh != NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         goto err;
       }
-      dh = DHparams_dup(dhp);
+      ssl->session->key_exchange_info = DH_num_bits(params);
+
+      /* Set up DH, generate a key, and emit the public half. */
+      DH *dh = DHparams_dup(params);
       if (dh == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
-        goto err;
-      }
-      s->s3->tmp.dh = dh;
-
-      if (!DH_generate_key(dh)) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
         goto err;
       }
 
-      r[0] = dh->p;
-      r[1] = dh->g;
-      r[2] = dh->pub_key;
+      SSL_ECDH_CTX_init_for_dhe(&ssl->s3->tmp.ecdh_ctx, dh);
+      if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+          !BN_bn2cbb_padded(&child, BN_num_bytes(params->p), params->p) ||
+          !CBB_add_u16_length_prefixed(&cbb, &child) ||
+          !BN_bn2cbb_padded(&child, BN_num_bytes(params->g), params->g) ||
+          !CBB_add_u16_length_prefixed(&cbb, &child) ||
+          !SSL_ECDH_CTX_generate_keypair(&ssl->s3->tmp.ecdh_ctx, &child)) {
+        goto err;
+      }
     } else if (alg_k & SSL_kECDHE) {
       /* Determine the curve to use. */
-      int nid = NID_undef;
-      if (cert->ecdh_nid != NID_undef) {
-        nid = cert->ecdh_nid;
-      } else if (cert->ecdh_tmp_cb != NULL) {
-        /* Note: |ecdh_tmp_cb| does NOT pass ownership of the result
-         * to the caller. */
-        EC_KEY *template = s->cert->ecdh_tmp_cb(s, 0, 1024);
-        if (template != NULL && EC_KEY_get0_group(template) != NULL) {
-          nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(template));
-        }
-      } else {
-        nid = tls1_get_shared_curve(s);
-      }
-      if (nid == NID_undef) {
-        al = SSL_AD_HANDSHAKE_FAILURE;
+      uint16_t curve_id;
+      if (!tls1_get_shared_curve(ssl, &curve_id)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY);
-        goto f_err;
-      }
-
-      if (s->s3->tmp.ecdh != NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         goto err;
       }
-      ecdh = EC_KEY_new_by_curve_name(nid);
-      if (ecdh == NULL) {
+      ssl->session->key_exchange_info = curve_id;
+
+      /* Set up ECDH, generate a key, and emit the public half. */
+      if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id) ||
+          !CBB_add_u8(&cbb, NAMED_CURVE_TYPE) ||
+          !CBB_add_u16(&cbb, curve_id) ||
+          !CBB_add_u8_length_prefixed(&cbb, &child) ||
+          !SSL_ECDH_CTX_generate_keypair(&ssl->s3->tmp.ecdh_ctx, &child)) {
         goto err;
       }
-      s->s3->tmp.ecdh = ecdh;
-
-      if (!EC_KEY_generate_key(ecdh)) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB);
-        goto err;
-      }
-
-      /* We only support ephemeral ECDH keys over named (not generic) curves. */
-      const EC_GROUP *group = EC_KEY_get0_group(ecdh);
-      if (!tls1_ec_nid2curve_id(&curve_id, EC_GROUP_get_curve_name(group))) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
-        goto err;
-      }
-
-      /* Encode the public key. First check the size of encoding and allocate
-       * memory accordingly. */
-      encodedlen =
-          EC_POINT_point2oct(group, EC_KEY_get0_public_key(ecdh),
-                             POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
-
-      encodedPoint = (uint8_t *)OPENSSL_malloc(encodedlen * sizeof(uint8_t));
-      bn_ctx = BN_CTX_new();
-      if (encodedPoint == NULL || bn_ctx == NULL) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-        goto err;
-      }
-
-      encodedlen = EC_POINT_point2oct(group, EC_KEY_get0_public_key(ecdh),
-                                      POINT_CONVERSION_UNCOMPRESSED,
-                                      encodedPoint, encodedlen, bn_ctx);
-
-      if (encodedlen == 0) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB);
-        goto err;
-      }
-
-      BN_CTX_free(bn_ctx);
-      bn_ctx = NULL;
-
-      /* We only support named (not generic) curves in ECDH ephemeral key
-       * exchanges. In this situation, we need four additional bytes to encode
-       * the entire ServerECDHParams structure. */
-      n += 4 + encodedlen;
-
-      /* We'll generate the serverKeyExchange message explicitly so we can set
-       * these to NULLs */
-      r[0] = NULL;
-      r[1] = NULL;
-      r[2] = NULL;
-      r[3] = NULL;
-    } else if (!(alg_k & SSL_kPSK)) {
-      al = SSL_AD_HANDSHAKE_FAILURE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE);
-      goto f_err;
+    } else {
+      assert(alg_k & SSL_kPSK);
     }
 
-    for (i = 0; i < 4 && r[i] != NULL; i++) {
-      nr[i] = BN_num_bytes(r[i]);
-      n += 2 + nr[i];
-    }
+    /* Otherwise, restore |cbb| from the previous iteration.
+     * TODO(davidben): When |ssl->init_buf| is gone, come up with a simpler
+     * pattern. Probably keep the |CBB| around in the handshake state. */
+  } else if (!CBB_did_write(&cbb, ssl->init_num - SSL_HM_HEADER_LENGTH(ssl))) {
+    goto err;
+  }
 
-    if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + max_sig_len)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_LIB_BUF);
+  /* Add a signature. */
+  if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+    if (!ssl_has_private_key(ssl)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       goto err;
     }
-    d = p = ssl_handshake_start(s);
 
-    for (i = 0; i < 4 && r[i] != NULL; i++) {
-      s2n(nr[i], p);
-      BN_bn2bin(r[i], p);
-      p += nr[i];
-    }
-
-    /* Note: ECDHE PSK ciphersuites use SSL_kECDHE and SSL_aPSK. When one of
-     * them is used, the server key exchange record needs to have both the
-     * psk_identity_hint and the ServerECDHParams. */
-    if (alg_a & SSL_aPSK) {
-      /* copy PSK identity hint (if provided) */
-      s2n(psk_identity_hint_len, p);
-      if (psk_identity_hint_len > 0) {
-        memcpy(p, psk_identity_hint, psk_identity_hint_len);
-        p += psk_identity_hint_len;
+    const size_t max_sig_len = ssl_private_key_max_signature_len(ssl);
+    size_t sig_len;
+    enum ssl_private_key_result_t sign_result;
+    if (ssl->state == SSL3_ST_SW_KEY_EXCH_A) {
+      /* This is the first iteration, so set up the signature. Sample the
+       * parameter length before adding a signature algorithm. */
+      if (!CBB_flush(&cbb)) {
+        goto err;
       }
-    }
-
-    if (alg_k & SSL_kECDHE) {
-      /* We only support named (not generic) curves. In this situation, the
-       * serverKeyExchange message has:
-       * [1 byte CurveType], [2 byte CurveName]
-       * [1 byte length of encoded point], followed by
-       * the actual encoded point itself. */
-      *(p++) = NAMED_CURVE_TYPE;
-      *(p++) = (uint8_t)(curve_id >> 8);
-      *(p++) = (uint8_t)(curve_id & 0xff);
-      *(p++) = encodedlen;
-      memcpy(p, encodedPoint, encodedlen);
-      p += encodedlen;
-      OPENSSL_free(encodedPoint);
-      encodedPoint = NULL;
-    }
-
-    if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-      /* n is the length of the params, they start at d and p points to
-       * the space at the end. */
-      const EVP_MD *md;
-      uint8_t digest[EVP_MAX_MD_SIZE];
-      unsigned int digest_length;
-
-      const int pkey_type = ssl_private_key_type(s);
+      size_t params_len = CBB_len(&cbb);
 
       /* Determine signature algorithm. */
-      if (SSL_USE_SIGALGS(s)) {
-        md = tls1_choose_signing_digest(s);
-        if (!tls12_get_sigandhash(s, p, md)) {
-          /* Should never happen */
-          al = SSL_AD_INTERNAL_ERROR;
+      const EVP_MD *md;
+      if (SSL_USE_SIGALGS(ssl)) {
+        md = tls1_choose_signing_digest(ssl);
+        if (!tls12_add_sigandhash(ssl, &cbb, md)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-          goto f_err;
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+          goto err;
         }
-        p += 2;
-      } else if (pkey_type == EVP_PKEY_RSA) {
+      } else if (ssl_private_key_type(ssl) == EVP_PKEY_RSA) {
         md = EVP_md5_sha1();
       } else {
         md = EVP_sha1();
       }
 
-      if (!EVP_DigestInit_ex(&md_ctx, md, NULL) ||
-          !EVP_DigestUpdate(&md_ctx, s->s3->client_random, SSL3_RANDOM_SIZE) ||
-          !EVP_DigestUpdate(&md_ctx, s->s3->server_random, SSL3_RANDOM_SIZE) ||
-          !EVP_DigestUpdate(&md_ctx, d, n) ||
-          !EVP_DigestFinal_ex(&md_ctx, digest, &digest_length)) {
-        OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
+      /* Compute the digest and sign it. */
+      uint8_t digest[EVP_MAX_MD_SIZE];
+      unsigned digest_len = 0;
+      EVP_MD_CTX md_ctx;
+      EVP_MD_CTX_init(&md_ctx);
+      int digest_ret =
+          EVP_DigestInit_ex(&md_ctx, md, NULL) &&
+          EVP_DigestUpdate(&md_ctx, ssl->s3->client_random, SSL3_RANDOM_SIZE) &&
+          EVP_DigestUpdate(&md_ctx, ssl->s3->server_random, SSL3_RANDOM_SIZE) &&
+          EVP_DigestUpdate(&md_ctx, CBB_data(&cbb), params_len) &&
+          EVP_DigestFinal_ex(&md_ctx, digest, &digest_len);
+      EVP_MD_CTX_cleanup(&md_ctx);
+      uint8_t *ptr;
+      if (!digest_ret ||
+          !CBB_add_u16_length_prefixed(&cbb, &child) ||
+          !CBB_reserve(&child, &ptr, max_sig_len)) {
         goto err;
       }
-
-      sign_result = ssl_private_key_sign(s, &p[2], &sig_len, max_sig_len,
-                                         EVP_MD_CTX_md(&md_ctx), digest,
-                                         digest_length);
+      sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len, md,
+                                         digest, digest_len);
     } else {
-      /* This key exchange doesn't involve a signature. */
-      sign_result = ssl_private_key_success;
-      sig_len = 0;
+      assert(ssl->state == SSL3_ST_SW_KEY_EXCH_B);
+
+      /* Retry the signature. */
+      uint8_t *ptr;
+      if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+          !CBB_reserve(&child, &ptr, max_sig_len)) {
+        goto err;
+      }
+      sign_result =
+          ssl_private_key_sign_complete(ssl, ptr, &sig_len, max_sig_len);
     }
-  } else {
-    assert(s->state == SSL3_ST_SW_KEY_EXCH_B);
-    /* Restore |p|. */
-    p = ssl_handshake_start(s) + s->init_num - SSL_HM_HEADER_LENGTH(s);
-    sign_result = ssl_private_key_sign_complete(s, &p[2], &sig_len,
-                                                max_sig_len);
+
+    switch (sign_result) {
+      case ssl_private_key_success:
+        ssl->rwstate = SSL_NOTHING;
+        if (!CBB_did_write(&child, sig_len)) {
+          goto err;
+        }
+        break;
+      case ssl_private_key_failure:
+        ssl->rwstate = SSL_NOTHING;
+        goto err;
+      case ssl_private_key_retry:
+        /* Discard the unfinished signature and save the state of |cbb| for the
+         * next iteration. */
+        CBB_discard_child(&cbb);
+        ssl->init_num = SSL_HM_HEADER_LENGTH(ssl) + CBB_len(&cbb);
+        ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        ssl->state = SSL3_ST_SW_KEY_EXCH_B;
+        goto err;
+    }
   }
 
-  switch (sign_result) {
-    case ssl_private_key_success:
-      s->rwstate = SSL_NOTHING;
-      break;
-    case ssl_private_key_failure:
-      s->rwstate = SSL_NOTHING;
-      goto err;
-    case ssl_private_key_retry:
-      s->rwstate = SSL_PRIVATE_KEY_OPERATION;
-      /* Stash away |p|. */
-      s->init_num = p - ssl_handshake_start(s) + SSL_HM_HEADER_LENGTH(s);
-      s->state = SSL3_ST_SW_KEY_EXCH_B;
-      goto err;
-  }
-
-  if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) {
-    s2n(sig_len, p);
-    p += sig_len;
-  }
-  if (!ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE,
-                                p - ssl_handshake_start(s))) {
+  size_t length;
+  if (!CBB_finish(&cbb, NULL, &length) ||
+      !ssl_set_handshake_header(ssl, SSL3_MT_SERVER_KEY_EXCHANGE, length)) {
     goto err;
   }
-  s->state = SSL3_ST_SW_KEY_EXCH_C;
+  ssl->state = SSL3_ST_SW_KEY_EXCH_C;
+  return ssl_do_write(ssl);
 
-  EVP_MD_CTX_cleanup(&md_ctx);
-  return ssl_do_write(s);
-
-f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
 err:
-  OPENSSL_free(encodedPoint);
-  BN_CTX_free(bn_ctx);
-  EVP_MD_CTX_cleanup(&md_ctx);
+  CBB_cleanup(&cbb);
   return -1;
 }
 
-int ssl3_send_certificate_request(SSL *s) {
+int ssl3_send_certificate_request(SSL *ssl) {
   uint8_t *p, *d;
   size_t i;
   int j, nl, off, n;
@@ -1516,21 +1393,21 @@
   X509_NAME *name;
   BUF_MEM *buf;
 
-  if (s->state == SSL3_ST_SW_CERT_REQ_A) {
-    buf = s->init_buf;
+  if (ssl->state == SSL3_ST_SW_CERT_REQ_A) {
+    buf = ssl->init_buf;
 
-    d = p = ssl_handshake_start(s);
+    d = p = ssl_handshake_start(ssl);
 
     /* get the list of acceptable cert types */
     p++;
-    n = ssl3_get_req_cert_type(s, p);
+    n = ssl3_get_req_cert_type(ssl, p);
     d[0] = n;
     p += n;
     n++;
 
-    if (SSL_USE_SIGALGS(s)) {
+    if (SSL_USE_SIGALGS(ssl)) {
       const uint8_t *psigs;
-      nl = tls12_get_psigalgs(s, &psigs);
+      nl = tls12_get_psigalgs(ssl, &psigs);
       s2n(nl, p);
       memcpy(p, psigs, nl);
       p += nl;
@@ -1541,17 +1418,17 @@
     p += 2;
     n += 2;
 
-    sk = SSL_get_client_CA_list(s);
+    sk = SSL_get_client_CA_list(ssl);
     nl = 0;
     if (sk != NULL) {
       for (i = 0; i < sk_X509_NAME_num(sk); i++) {
         name = sk_X509_NAME_value(sk, i);
         j = i2d_X509_NAME(name, NULL);
-        if (!BUF_MEM_grow_clean(buf, SSL_HM_HEADER_LENGTH(s) + n + j + 2)) {
+        if (!BUF_MEM_grow_clean(buf, SSL_HM_HEADER_LENGTH(ssl) + n + j + 2)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
           goto err;
         }
-        p = ssl_handshake_start(s) + n;
+        p = ssl_handshake_start(ssl) + n;
         s2n(j, p);
         i2d_X509_NAME(name, &p);
         n += 2 + j;
@@ -1560,33 +1437,23 @@
     }
 
     /* else no CA names */
-    p = ssl_handshake_start(s) + off;
+    p = ssl_handshake_start(ssl) + off;
     s2n(nl, p);
 
-    if (!ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_REQUEST, n)) {
+    if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE_REQUEST, n)) {
       goto err;
     }
-    s->state = SSL3_ST_SW_CERT_REQ_B;
+    ssl->state = SSL3_ST_SW_CERT_REQ_B;
   }
 
   /* SSL3_ST_SW_CERT_REQ_B */
-  return ssl_do_write(s);
+  return ssl_do_write(ssl);
 
 err:
   return -1;
 }
 
-static struct CRYPTO_STATIC_MUTEX g_d5_bug_lock = CRYPTO_STATIC_MUTEX_INIT;
-static uint64_t g_d5_bug_use_count = 0;
-
-uint64_t OPENSSL_get_d5_bug_use_count(void) {
-  CRYPTO_STATIC_MUTEX_lock_read(&g_d5_bug_lock);
-  uint64_t ret = g_d5_bug_use_count;
-  CRYPTO_STATIC_MUTEX_unlock(&g_d5_bug_lock);
-  return ret;
-}
-
-int ssl3_get_client_key_exchange(SSL *s) {
+int ssl3_get_client_key_exchange(SSL *ssl) {
   int al;
   CBS client_key_exchange;
   uint32_t alg_k;
@@ -1594,30 +1461,24 @@
   uint8_t *premaster_secret = NULL;
   size_t premaster_secret_len = 0;
   uint8_t *decrypt_buf = NULL;
-  BIGNUM *pub = NULL;
-  DH *dh_srvr;
 
-  EC_KEY *srvr_ecdh = NULL;
-  EVP_PKEY *clnt_pub_pkey = NULL;
-  EC_POINT *clnt_ecpoint = NULL;
-  BN_CTX *bn_ctx = NULL;
-  unsigned int psk_len = 0;
+  unsigned psk_len = 0;
   uint8_t psk[PSK_MAX_PSK_LEN];
 
-  if (s->state == SSL3_ST_SR_KEY_EXCH_A ||
-      s->state == SSL3_ST_SR_KEY_EXCH_B) {
+  if (ssl->state == SSL3_ST_SR_KEY_EXCH_A ||
+      ssl->state == SSL3_ST_SR_KEY_EXCH_B) {
     int ok;
-    const long n = s->method->ssl_get_message(
-        s, SSL3_ST_SR_KEY_EXCH_A, SSL3_ST_SR_KEY_EXCH_B,
+    const long n = ssl->method->ssl_get_message(
+        ssl, SSL3_ST_SR_KEY_EXCH_A, SSL3_ST_SR_KEY_EXCH_B,
         SSL3_MT_CLIENT_KEY_EXCHANGE, 2048 /* ??? */, ssl_hash_message, &ok);
     if (!ok) {
       return n;
     }
   }
 
-  CBS_init(&client_key_exchange, s->init_msg, s->init_num);
-  alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
-  alg_a = s->s3->tmp.new_cipher->algorithm_auth;
+  CBS_init(&client_key_exchange, ssl->init_msg, ssl->init_num);
+  alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
+  alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
 
   /* If using a PSK key exchange, prepare the pre-shared key. */
   if (alg_a & SSL_aPSK) {
@@ -1632,7 +1493,7 @@
       goto f_err;
     }
 
-    if (s->psk_server_callback == NULL) {
+    if (ssl->psk_server_callback == NULL) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_SERVER_CB);
       al = SSL_AD_INTERNAL_ERROR;
       goto f_err;
@@ -1645,15 +1506,15 @@
       goto f_err;
     }
 
-    if (!CBS_strdup(&psk_identity, &s->session->psk_identity)) {
+    if (!CBS_strdup(&psk_identity, &ssl->session->psk_identity)) {
       al = SSL_AD_INTERNAL_ERROR;
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto f_err;
     }
 
     /* Look up the key for the identity. */
-    psk_len =
-        s->psk_server_callback(s, s->session->psk_identity, psk, sizeof(psk));
+    psk_len = ssl->psk_server_callback(ssl, ssl->session->psk_identity, psk,
+                                       sizeof(psk));
     if (psk_len > PSK_MAX_PSK_LEN) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       al = SSL_AD_INTERNAL_ERROR;
@@ -1669,13 +1530,8 @@
   /* Depending on the key exchange method, compute |premaster_secret| and
    * |premaster_secret_len|. */
   if (alg_k & SSL_kRSA) {
-    CBS encrypted_premaster_secret;
-    uint8_t rand_premaster_secret[SSL_MAX_MASTER_KEY_LENGTH];
-    uint8_t good;
-    size_t decrypt_len, premaster_index, j;
-    const size_t rsa_size = ssl_private_key_max_signature_len(s);
-
     /* Allocate a buffer large enough for an RSA decryption. */
+    const size_t rsa_size = ssl_private_key_max_signature_len(ssl);
     decrypt_buf = OPENSSL_malloc(rsa_size);
     if (decrypt_buf == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
@@ -1683,265 +1539,131 @@
     }
 
     enum ssl_private_key_result_t decrypt_result;
-    if (s->state == SSL3_ST_SR_KEY_EXCH_B) {
-      if (!ssl_has_private_key(s) || ssl_private_key_type(s) != EVP_PKEY_RSA) {
+    size_t decrypt_len;
+    if (ssl->state == SSL3_ST_SR_KEY_EXCH_B) {
+      if (!ssl_has_private_key(ssl) ||
+          ssl_private_key_type(ssl) != EVP_PKEY_RSA) {
         al = SSL_AD_HANDSHAKE_FAILURE;
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_RSA_CERTIFICATE);
         goto f_err;
       }
-      /* TLS and [incidentally] DTLS{0xFEFF} */
-      if (s->version > SSL3_VERSION) {
-        CBS copy = client_key_exchange;
+      CBS encrypted_premaster_secret;
+      if (ssl->version > SSL3_VERSION) {
         if (!CBS_get_u16_length_prefixed(&client_key_exchange,
                                          &encrypted_premaster_secret) ||
             CBS_len(&client_key_exchange) != 0) {
-          if (!(s->options & SSL_OP_TLS_D5_BUG)) {
-            al = SSL_AD_DECODE_ERROR;
-            OPENSSL_PUT_ERROR(SSL,
-                              SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG);
-            goto f_err;
-          } else {
-            CRYPTO_STATIC_MUTEX_lock_write(&g_d5_bug_lock);
-            g_d5_bug_use_count++;
-            CRYPTO_STATIC_MUTEX_unlock(&g_d5_bug_lock);
-
-            encrypted_premaster_secret = copy;
-          }
+          al = SSL_AD_DECODE_ERROR;
+          OPENSSL_PUT_ERROR(SSL,
+                            SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG);
+          goto f_err;
         }
       } else {
         encrypted_premaster_secret = client_key_exchange;
       }
 
-      /* Reject overly short RSA keys because we want to be sure that the buffer
-       * size makes it safe to iterate over the entire size of a premaster
-       * secret (SSL_MAX_MASTER_KEY_LENGTH). The actual expected size is larger
-       * due to RSA padding, but the bound is sufficient to be safe. */
-      if (rsa_size < SSL_MAX_MASTER_KEY_LENGTH) {
-        al = SSL_AD_DECRYPT_ERROR;
-        OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
-        goto f_err;
-      }
-
       /* Decrypt with no padding. PKCS#1 padding will be removed as part of the
        * timing-sensitive code below. */
       decrypt_result = ssl_private_key_decrypt(
-          s, decrypt_buf, &decrypt_len, rsa_size,
+          ssl, decrypt_buf, &decrypt_len, rsa_size,
           CBS_data(&encrypted_premaster_secret),
           CBS_len(&encrypted_premaster_secret));
     } else {
-      assert(s->state == SSL3_ST_SR_KEY_EXCH_C);
+      assert(ssl->state == SSL3_ST_SR_KEY_EXCH_C);
       /* Complete async decrypt. */
       decrypt_result = ssl_private_key_decrypt_complete(
-          s, decrypt_buf, &decrypt_len, rsa_size);
+          ssl, decrypt_buf, &decrypt_len, rsa_size);
     }
 
     switch (decrypt_result) {
       case ssl_private_key_success:
-        s->rwstate = SSL_NOTHING;
+        ssl->rwstate = SSL_NOTHING;
         break;
       case ssl_private_key_failure:
-        s->rwstate = SSL_NOTHING;
+        ssl->rwstate = SSL_NOTHING;
         goto err;
       case ssl_private_key_retry:
-        s->rwstate = SSL_PRIVATE_KEY_OPERATION;
-        s->state = SSL3_ST_SR_KEY_EXCH_C;
+        ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
+        ssl->state = SSL3_ST_SR_KEY_EXCH_C;
         goto err;
     }
 
-    if (decrypt_len != rsa_size) {
-      /* This should never happen, but do a check so we do not read
-       * uninitialized memory. */
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
+    assert(decrypt_len == rsa_size);
 
-    /* Remove the PKCS#1 padding and adjust |decrypt_len| as appropriate.
-     * |good| will be 0xff if the premaster is acceptable and zero otherwise.
-     * */
-    good =
-        constant_time_eq_int_8(RSA_message_index_PKCS1_type_2(
-                                   decrypt_buf, decrypt_len, &premaster_index),
-                               1);
-    decrypt_len = decrypt_len - premaster_index;
-
-    /* decrypt_len should be SSL_MAX_MASTER_KEY_LENGTH. */
-    good &= constant_time_eq_8(decrypt_len, SSL_MAX_MASTER_KEY_LENGTH);
-
-    /* Copy over the unpadded premaster. Whatever the value of
-     * |decrypt_good_mask|, copy as if the premaster were the right length. It
-     * is important the memory access pattern be constant. */
-    premaster_secret =
-        BUF_memdup(decrypt_buf + (rsa_size - SSL_MAX_MASTER_KEY_LENGTH),
-                   SSL_MAX_MASTER_KEY_LENGTH);
+    /* Prepare a random premaster, to be used on invalid padding. See RFC 5246,
+     * section 7.4.7.1. */
+    premaster_secret_len = SSL_MAX_MASTER_KEY_LENGTH;
+    premaster_secret = OPENSSL_malloc(premaster_secret_len);
     if (premaster_secret == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
       goto err;
     }
+    if (!RAND_bytes(premaster_secret, premaster_secret_len)) {
+      goto err;
+    }
+
+    /* The smallest padded premaster is 11 bytes of overhead. Small keys are
+     * publicly invalid. */
+    if (decrypt_len < 11 + premaster_secret_len) {
+      al = SSL_AD_DECRYPT_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+      goto f_err;
+    }
+
+    /* Check the padding. See RFC 3447, section 7.2.2. */
+    size_t padding_len = decrypt_len - premaster_secret_len;
+    uint8_t good = constant_time_eq_int_8(decrypt_buf[0], 0) &
+                   constant_time_eq_int_8(decrypt_buf[1], 2);
+    size_t i;
+    for (i = 2; i < padding_len - 1; i++) {
+      good &= ~constant_time_is_zero_8(decrypt_buf[i]);
+    }
+    good &= constant_time_is_zero_8(decrypt_buf[padding_len - 1]);
+
+    /* The premaster secret must begin with |client_version|. This too must be
+     * checked in constant time (http://eprint.iacr.org/2003/052/). */
+    good &= constant_time_eq_8(decrypt_buf[padding_len],
+                               (unsigned)(ssl->client_version >> 8));
+    good &= constant_time_eq_8(decrypt_buf[padding_len + 1],
+                               (unsigned)(ssl->client_version & 0xff));
+
+    /* Select, in constant time, either the decrypted premaster or the random
+     * premaster based on |good|. */
+    for (i = 0; i < premaster_secret_len; i++) {
+      premaster_secret[i] = constant_time_select_8(
+          good, decrypt_buf[padding_len + i], premaster_secret[i]);
+    }
+
     OPENSSL_free(decrypt_buf);
     decrypt_buf = NULL;
-
-    /* If the version in the decrypted pre-master secret is correct then
-     * version_good will be 0xff, otherwise it'll be zero. The
-     * Klima-Pokorny-Rosa extension of Bleichenbacher's attack
-     * (http://eprint.iacr.org/2003/052/) exploits the version number check as
-     * a "bad version oracle". Thus version checks are done in constant time
-     * and are treated like any other decryption error. */
-    good &= constant_time_eq_8(premaster_secret[0],
-                               (unsigned)(s->client_version >> 8));
-    good &= constant_time_eq_8(premaster_secret[1],
-                               (unsigned)(s->client_version & 0xff));
-
-    /* We must not leak whether a decryption failure occurs because of
-     * Bleichenbacher's attack on PKCS #1 v1.5 RSA padding (see RFC 2246,
-     * section 7.4.7.1). The code follows that advice of the TLS RFC and
-     * generates a random premaster secret for the case that the decrypt
-     * fails. See https://tools.ietf.org/html/rfc5246#section-7.4.7.1 */
-    if (!RAND_bytes(rand_premaster_secret, sizeof(rand_premaster_secret))) {
-      goto err;
+  } else if (alg_k & (SSL_kECDHE|SSL_kDHE)) {
+    /* Parse the ClientKeyExchange. ECDHE uses a u8 length prefix while DHE uses
+     * u16. */
+    CBS peer_key;
+    int peer_key_ok;
+    if (alg_k & SSL_kECDHE) {
+      peer_key_ok = CBS_get_u8_length_prefixed(&client_key_exchange, &peer_key);
+    } else {
+      peer_key_ok =
+          CBS_get_u16_length_prefixed(&client_key_exchange, &peer_key);
     }
 
-    /* Now copy rand_premaster_secret over premaster_secret using
-     * decrypt_good_mask. */
-    for (j = 0; j < sizeof(rand_premaster_secret); j++) {
-      premaster_secret[j] = constant_time_select_8(good, premaster_secret[j],
-                                                   rand_premaster_secret[j]);
-    }
-
-    premaster_secret_len = sizeof(rand_premaster_secret);
-  } else if (alg_k & SSL_kDHE) {
-    CBS dh_Yc;
-    int dh_len;
-
-    if (!CBS_get_u16_length_prefixed(&client_key_exchange, &dh_Yc) ||
-        CBS_len(&dh_Yc) == 0 || CBS_len(&client_key_exchange) != 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG);
-      al = SSL_R_DECODE_ERROR;
-      goto f_err;
-    }
-
-    if (s->s3->tmp.dh == NULL) {
-      al = SSL_AD_HANDSHAKE_FAILURE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_DH_KEY);
-      goto f_err;
-    }
-    dh_srvr = s->s3->tmp.dh;
-
-    pub = BN_bin2bn(CBS_data(&dh_Yc), CBS_len(&dh_Yc), NULL);
-    if (pub == NULL) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BN_LIB);
-      goto err;
-    }
-
-    /* Allocate a buffer for the premaster secret. */
-    premaster_secret = OPENSSL_malloc(DH_size(dh_srvr));
-    if (premaster_secret == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      BN_clear_free(pub);
-      goto err;
-    }
-
-    dh_len = DH_compute_key(premaster_secret, pub, dh_srvr);
-    if (dh_len <= 0) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
-      BN_clear_free(pub);
-      goto err;
-    }
-
-    DH_free(s->s3->tmp.dh);
-    s->s3->tmp.dh = NULL;
-    BN_clear_free(pub);
-    pub = NULL;
-
-    premaster_secret_len = dh_len;
-  } else if (alg_k & SSL_kECDHE) {
-    int ecdh_len;
-    const EC_KEY *tkey;
-    const EC_GROUP *group;
-    const BIGNUM *priv_key;
-    CBS ecdh_Yc;
-
-    /* initialize structures for server's ECDH key pair */
-    srvr_ecdh = EC_KEY_new();
-    if (srvr_ecdh == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-
-    /* Use the ephermeral values we saved when generating the ServerKeyExchange
-     * msg. */
-    tkey = s->s3->tmp.ecdh;
-
-    group = EC_KEY_get0_group(tkey);
-    priv_key = EC_KEY_get0_private_key(tkey);
-
-    if (!EC_KEY_set_group(srvr_ecdh, group) ||
-        !EC_KEY_set_private_key(srvr_ecdh, priv_key)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB);
-      goto err;
-    }
-
-    /* Let's get client's public key */
-    clnt_ecpoint = EC_POINT_new(group);
-    if (clnt_ecpoint == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-
-    /* Get client's public key from encoded point in the ClientKeyExchange
-     * message. */
-    if (!CBS_get_u8_length_prefixed(&client_key_exchange, &ecdh_Yc) ||
-        CBS_len(&client_key_exchange) != 0) {
+    if (!peer_key_ok || CBS_len(&client_key_exchange) != 0) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto f_err;
     }
 
-    bn_ctx = BN_CTX_new();
-    if (bn_ctx == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
+    /* Compute the premaster. */
+    uint8_t alert;
+    if (!SSL_ECDH_CTX_compute_secret(&ssl->s3->tmp.ecdh_ctx, &premaster_secret,
+                                     &premaster_secret_len, &alert,
+                                     CBS_data(&peer_key), CBS_len(&peer_key))) {
+      al = alert;
+      goto f_err;
     }
 
-    if (!EC_POINT_oct2point(group, clnt_ecpoint, CBS_data(&ecdh_Yc),
-                            CBS_len(&ecdh_Yc), bn_ctx)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_EC_LIB);
-      goto err;
-    }
-
-    /* Allocate a buffer for both the secret and the PSK. */
-    unsigned field_size = EC_GROUP_get_degree(group);
-    if (field_size == 0) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB);
-      goto err;
-    }
-
-    ecdh_len = (field_size + 7) / 8;
-    premaster_secret = OPENSSL_malloc(ecdh_len);
-    if (premaster_secret == NULL) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-
-    /* Compute the shared pre-master secret */
-    ecdh_len = ECDH_compute_key(premaster_secret, ecdh_len, clnt_ecpoint,
-                                srvr_ecdh, NULL);
-    if (ecdh_len <= 0) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_ECDH_LIB);
-      goto err;
-    }
-
-    EVP_PKEY_free(clnt_pub_pkey);
-    clnt_pub_pkey = NULL;
-    EC_POINT_free(clnt_ecpoint);
-    clnt_ecpoint = NULL;
-    EC_KEY_free(srvr_ecdh);
-    srvr_ecdh = NULL;
-    BN_CTX_free(bn_ctx);
-    bn_ctx = NULL;
-    EC_KEY_free(s->s3->tmp.ecdh);
-    s->s3->tmp.ecdh = NULL;
-
-    premaster_secret_len = ecdh_len;
+    /* The key exchange state may now be discarded. */
+    SSL_ECDH_CTX_cleanup(&ssl->s3->tmp.ecdh_ctx);
   } else if (alg_k & SSL_kPSK) {
     /* For plain PSK, other_secret is a block of 0s with the same length as the
      * pre-shared key. */
@@ -1984,40 +1706,34 @@
   }
 
   /* Compute the master secret */
-  s->session->master_key_length = s->enc_method->generate_master_secret(
-      s, s->session->master_key, premaster_secret, premaster_secret_len);
-  if (s->session->master_key_length == 0) {
+  ssl->session->master_key_length = ssl->enc_method->generate_master_secret(
+      ssl, ssl->session->master_key, premaster_secret, premaster_secret_len);
+  if (ssl->session->master_key_length == 0) {
     goto err;
   }
-  s->session->extended_master_secret = s->s3->tmp.extended_master_secret;
+  ssl->session->extended_master_secret = ssl->s3->tmp.extended_master_secret;
 
   OPENSSL_cleanse(premaster_secret, premaster_secret_len);
   OPENSSL_free(premaster_secret);
   return 1;
 
 f_err:
-  ssl3_send_alert(s, SSL3_AL_FATAL, al);
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
 err:
-  if (premaster_secret) {
-    if (premaster_secret_len) {
-      OPENSSL_cleanse(premaster_secret, premaster_secret_len);
-    }
+  if (premaster_secret != NULL) {
+    OPENSSL_cleanse(premaster_secret, premaster_secret_len);
     OPENSSL_free(premaster_secret);
   }
   OPENSSL_free(decrypt_buf);
-  EVP_PKEY_free(clnt_pub_pkey);
-  EC_POINT_free(clnt_ecpoint);
-  EC_KEY_free(srvr_ecdh);
-  BN_CTX_free(bn_ctx);
 
   return -1;
 }
 
-int ssl3_get_cert_verify(SSL *s) {
+int ssl3_get_cert_verify(SSL *ssl) {
   int al, ok, ret = 0;
   long n;
   CBS certificate_verify, signature;
-  X509 *peer = s->session->peer;
+  X509 *peer = ssl->session->peer;
   EVP_PKEY *pkey = NULL;
   const EVP_MD *md = NULL;
   uint8_t digest[EVP_MAX_MD_SIZE];
@@ -2028,12 +1744,12 @@
    * CertificateVerify is required if and only if there's a client certificate.
    * */
   if (peer == NULL) {
-    ssl3_free_handshake_buffer(s);
+    ssl3_free_handshake_buffer(ssl);
     return 1;
   }
 
-  n = s->method->ssl_get_message(
-      s, SSL3_ST_SR_CERT_VRFY_A, SSL3_ST_SR_CERT_VRFY_B,
+  n = ssl->method->ssl_get_message(
+      ssl, SSL3_ST_SR_CERT_VRFY_A, SSL3_ST_SR_CERT_VRFY_B,
       SSL3_MT_CERTIFICATE_VERIFY, SSL3_RT_MAX_PLAIN_LENGTH,
       ssl_dont_hash_message, &ok);
 
@@ -2053,10 +1769,10 @@
     goto f_err;
   }
 
-  CBS_init(&certificate_verify, s->init_msg, n);
+  CBS_init(&certificate_verify, ssl->init_msg, n);
 
   /* Determine the digest type if needbe. */
-  if (SSL_USE_SIGALGS(s)) {
+  if (SSL_USE_SIGALGS(ssl)) {
     uint8_t hash, signature_type;
     if (!CBS_get_u8(&certificate_verify, &hash) ||
         !CBS_get_u8(&certificate_verify, &signature_type)) {
@@ -2064,20 +1780,20 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto f_err;
     }
-    if (!tls12_check_peer_sigalg(s, &md, &al, hash, signature_type, pkey)) {
+    if (!tls12_check_peer_sigalg(ssl, &md, &al, hash, signature_type, pkey)) {
       goto f_err;
     }
   }
 
   /* Compute the digest. */
-  if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey->type)) {
+  if (!ssl3_cert_verify_hash(ssl, digest, &digest_length, &md, pkey->type)) {
     goto err;
   }
 
   /* The handshake buffer is no longer necessary, and we may hash the current
    * message.*/
-  ssl3_free_handshake_buffer(s);
-  if (!ssl3_hash_current_message(s)) {
+  ssl3_free_handshake_buffer(ssl);
+  if (!ssl3_hash_current_message(ssl)) {
     goto err;
   }
 
@@ -2106,7 +1822,7 @@
 
   if (0) {
   f_err:
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
   }
 
 err:
@@ -2116,7 +1832,7 @@
   return ret;
 }
 
-int ssl3_get_client_certificate(SSL *s) {
+int ssl3_get_client_certificate(SSL *ssl) {
   int i, ok, al, ret = -1;
   X509 *x = NULL;
   unsigned long n;
@@ -2125,40 +1841,41 @@
   CBS certificate_msg, certificate_list;
   int is_first_certificate = 1;
 
-  n = s->method->ssl_get_message(s, SSL3_ST_SR_CERT_A, SSL3_ST_SR_CERT_B, -1,
-                                 (long)s->max_cert_list, ssl_hash_message, &ok);
+  n = ssl->method->ssl_get_message(ssl, SSL3_ST_SR_CERT_A, SSL3_ST_SR_CERT_B,
+                                   -1, (long)ssl->max_cert_list,
+                                   ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
   }
 
-  if (s->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
-    if ((s->verify_mode & SSL_VERIFY_PEER) &&
-        (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
+  if (ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
+    if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
+        (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
       al = SSL_AD_HANDSHAKE_FAILURE;
       goto f_err;
     }
 
     /* If tls asked for a client cert, the client must return a 0 list */
-    if (s->version > SSL3_VERSION && s->s3->tmp.cert_request) {
+    if (ssl->version > SSL3_VERSION && ssl->s3->tmp.cert_request) {
       OPENSSL_PUT_ERROR(SSL,
                         SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST);
       al = SSL_AD_UNEXPECTED_MESSAGE;
       goto f_err;
     }
-    s->s3->tmp.reuse_message = 1;
+    ssl->s3->tmp.reuse_message = 1;
 
     return 1;
   }
 
-  if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE) {
+  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) {
     al = SSL_AD_UNEXPECTED_MESSAGE;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_MESSAGE_TYPE);
     goto f_err;
   }
 
-  CBS_init(&certificate_msg, s->init_msg, n);
+  CBS_init(&certificate_msg, ssl->init_msg, n);
 
   sk = sk_X509_new_null();
   if (sk == NULL) {
@@ -2183,18 +1900,19 @@
       goto f_err;
     }
 
-    if (is_first_certificate && s->ctx->retain_only_sha256_of_client_certs) {
+    if (is_first_certificate && ssl->ctx->retain_only_sha256_of_client_certs) {
       /* If this is the first certificate, and we don't want to keep peer
        * certificates in memory, then we hash it right away. */
       SHA256_Init(&sha256);
       SHA256_Update(&sha256, CBS_data(&certificate), CBS_len(&certificate));
-      SHA256_Final(s->session->peer_sha256, &sha256);
-      s->session->peer_sha256_valid = 1;
+      SHA256_Final(ssl->session->peer_sha256, &sha256);
+      ssl->session->peer_sha256_valid = 1;
     }
     is_first_certificate = 0;
 
+    /* A u24 length cannot overflow a long. */
     data = CBS_data(&certificate);
-    x = d2i_X509(NULL, &data, CBS_len(&certificate));
+    x = d2i_X509(NULL, &data, (long)CBS_len(&certificate));
     if (x == NULL) {
       al = SSL_AD_BAD_CERTIFICATE;
       OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
@@ -2214,35 +1932,35 @@
 
   if (sk_X509_num(sk) <= 0) {
     /* No client certificate so the handshake buffer may be discarded. */
-    ssl3_free_handshake_buffer(s);
+    ssl3_free_handshake_buffer(ssl);
 
     /* TLS does not mind 0 certs returned */
-    if (s->version == SSL3_VERSION) {
+    if (ssl->version == SSL3_VERSION) {
       al = SSL_AD_HANDSHAKE_FAILURE;
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED);
       goto f_err;
-    } else if ((s->verify_mode & SSL_VERIFY_PEER) &&
-             (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
+    } else if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
+             (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
       /* Fail for TLS only if we required a certificate */
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
       al = SSL_AD_HANDSHAKE_FAILURE;
       goto f_err;
     }
   } else {
-    i = ssl_verify_cert_chain(s, sk);
+    i = ssl_verify_cert_chain(ssl, sk);
     if (i <= 0) {
-      al = ssl_verify_alarm_type(s->verify_result);
+      al = ssl_verify_alarm_type(ssl->verify_result);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
       goto f_err;
     }
   }
 
-  X509_free(s->session->peer);
-  s->session->peer = sk_X509_shift(sk);
-  s->session->verify_result = s->verify_result;
+  X509_free(ssl->session->peer);
+  ssl->session->peer = sk_X509_shift(sk);
+  ssl->session->verify_result = ssl->verify_result;
 
-  sk_X509_pop_free(s->session->cert_chain, X509_free);
-  s->session->cert_chain = sk;
+  sk_X509_pop_free(ssl->session->cert_chain, X509_free);
+  ssl->session->cert_chain = sk;
   /* Inconsistency alert: cert_chain does *not* include the peer's own
    * certificate, while we do include it in s3_clnt.c */
 
@@ -2252,7 +1970,7 @@
 
   if (0) {
   f_err:
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
   }
 
 err:
@@ -2261,20 +1979,20 @@
   return ret;
 }
 
-int ssl3_send_server_certificate(SSL *s) {
-  if (s->state == SSL3_ST_SW_CERT_A) {
-    if (!ssl3_output_cert_chain(s)) {
+int ssl3_send_server_certificate(SSL *ssl) {
+  if (ssl->state == SSL3_ST_SW_CERT_A) {
+    if (!ssl3_output_cert_chain(ssl)) {
       return 0;
     }
-    s->state = SSL3_ST_SW_CERT_B;
+    ssl->state = SSL3_ST_SW_CERT_B;
   }
 
   /* SSL3_ST_SW_CERT_B */
-  return ssl_do_write(s);
+  return ssl_do_write(ssl);
 }
 
 /* send a new session ticket (not necessarily for a new session) */
-int ssl3_send_new_session_ticket(SSL *s) {
+int ssl3_send_new_session_ticket(SSL *ssl) {
   int ret = -1;
   uint8_t *session = NULL;
   size_t session_len;
@@ -2284,11 +2002,11 @@
   EVP_CIPHER_CTX_init(&ctx);
   HMAC_CTX_init(&hctx);
 
-  if (s->state == SSL3_ST_SW_SESSION_TICKET_A) {
+  if (ssl->state == SSL3_ST_SW_SESSION_TICKET_A) {
     uint8_t *p, *macstart;
     int len;
     unsigned int hlen;
-    SSL_CTX *tctx = s->initial_ctx;
+    SSL_CTX *tctx = ssl->initial_ctx;
     uint8_t iv[EVP_MAX_IV_LENGTH];
     uint8_t key_name[16];
     /* The maximum overhead of encrypting the session is 16 (key name) + IV +
@@ -2297,7 +2015,8 @@
         16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
 
     /* Serialize the SSL_SESSION to be encoded into the ticket. */
-    if (!SSL_SESSION_to_bytes_for_ticket(s->session, &session, &session_len)) {
+    if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &session,
+                                         &session_len)) {
       goto err;
     }
 
@@ -2310,7 +2029,7 @@
       OPENSSL_free(session);
       session = NULL;
 
-      p = ssl_handshake_start(s);
+      p = ssl_handshake_start(ssl);
       /* Emit ticket_lifetime_hint. */
       l2n(0, p);
       /* Emit ticket. */
@@ -2318,26 +2037,26 @@
       memcpy(p, kTicketPlaceholder, placeholder_len);
       p += placeholder_len;
 
-      len = p - ssl_handshake_start(s);
-      if (!ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len)) {
+      len = p - ssl_handshake_start(ssl);
+      if (!ssl_set_handshake_header(ssl, SSL3_MT_NEWSESSION_TICKET, len)) {
         goto err;
       }
-      s->state = SSL3_ST_SW_SESSION_TICKET_B;
-      return ssl_do_write(s);
+      ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
+      return ssl_do_write(ssl);
     }
 
     /* Grow buffer if need be: the length calculation is as follows:
      * handshake_header_length + 4 (ticket lifetime hint) + 2 (ticket length) +
      * max_ticket_overhead + * session_length */
-    if (!BUF_MEM_grow(s->init_buf, SSL_HM_HEADER_LENGTH(s) + 6 +
+    if (!BUF_MEM_grow(ssl->init_buf, SSL_HM_HEADER_LENGTH(ssl) + 6 +
                                        max_ticket_overhead + session_len)) {
       goto err;
     }
-    p = ssl_handshake_start(s);
+    p = ssl_handshake_start(ssl);
     /* Initialize HMAC and cipher contexts. If callback present it does all the
      * work otherwise use generated values from parent ctx. */
     if (tctx->tlsext_ticket_key_cb) {
-      if (tctx->tlsext_ticket_key_cb(s, key_name, iv, &ctx, &hctx,
+      if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx,
                                      1 /* encrypt */) < 0) {
         goto err;
       }
@@ -2355,7 +2074,7 @@
     /* Ticket lifetime hint (advisory only): We leave this unspecified for
      * resumed session (for simplicity), and guess that tickets for new
      * sessions will live as long as their sessions. */
-    l2n(s->hit ? 0 : s->session->timeout, p);
+    l2n(ssl->hit ? 0 : ssl->session->timeout, p);
 
     /* Skip ticket length for now */
     p += 2;
@@ -2384,18 +2103,18 @@
     p += hlen;
     /* Now write out lengths: p points to end of data written */
     /* Total length */
-    len = p - ssl_handshake_start(s);
+    len = p - ssl_handshake_start(ssl);
     /* Skip ticket lifetime hint */
-    p = ssl_handshake_start(s) + 4;
+    p = ssl_handshake_start(ssl) + 4;
     s2n(len - 6, p);
-    if (!ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len)) {
+    if (!ssl_set_handshake_header(ssl, SSL3_MT_NEWSESSION_TICKET, len)) {
       goto err;
     }
-    s->state = SSL3_ST_SW_SESSION_TICKET_B;
+    ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
   }
 
   /* SSL3_ST_SW_SESSION_TICKET_B */
-  ret = ssl_do_write(s);
+  ret = ssl_do_write(ssl);
 
 err:
   OPENSSL_free(session);
@@ -2406,19 +2125,19 @@
 
 /* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
  * sets the next_proto member in s if found */
-int ssl3_get_next_proto(SSL *s) {
+int ssl3_get_next_proto(SSL *ssl) {
   int ok;
   long n;
   CBS next_protocol, selected_protocol, padding;
 
   /* Clients cannot send a NextProtocol message if we didn't see the extension
    * in their ClientHello */
-  if (!s->s3->next_proto_neg_seen) {
+  if (!ssl->s3->next_proto_neg_seen) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION);
     return -1;
   }
 
-  n = s->method->ssl_get_message(s, SSL3_ST_SR_NEXT_PROTO_A,
+  n = ssl->method->ssl_get_message(ssl, SSL3_ST_SR_NEXT_PROTO_A,
                                  SSL3_ST_SR_NEXT_PROTO_B, SSL3_MT_NEXT_PROTO,
                                  514, /* See the payload format below */
                                  ssl_hash_message, &ok);
@@ -2427,18 +2146,7 @@
     return n;
   }
 
-  /* s->state doesn't reflect whether ChangeCipherSpec has been received in
-   * this handshake, but s->s3->change_cipher_spec does (will be reset by
-   * ssl3_get_finished).
-   *
-   * TODO(davidben): Is this check now redundant with
-   * SSL3_FLAGS_EXPECT_CCS? */
-  if (!s->s3->change_cipher_spec) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS);
-    return -1;
-  }
-
-  CBS_init(&next_protocol, s->init_msg, n);
+  CBS_init(&next_protocol, ssl->init_msg, n);
 
   /* The payload looks like:
    *   uint8 proto_len;
@@ -2448,8 +2156,8 @@
   if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) ||
       !CBS_get_u8_length_prefixed(&next_protocol, &padding) ||
       CBS_len(&next_protocol) != 0 ||
-      !CBS_stow(&selected_protocol, &s->next_proto_negotiated,
-                &s->next_proto_negotiated_len)) {
+      !CBS_stow(&selected_protocol, &ssl->next_proto_negotiated,
+                &ssl->next_proto_negotiated_len)) {
     return 0;
   }
 
@@ -2457,7 +2165,7 @@
 }
 
 /* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
-int ssl3_get_channel_id(SSL *s) {
+int ssl3_get_channel_id(SSL *ssl) {
   int ret = -1, ok;
   long n;
   uint8_t channel_id_hash[EVP_MAX_MD_SIZE];
@@ -2471,8 +2179,8 @@
   BIGNUM x, y;
   CBS encrypted_extensions, extension;
 
-  n = s->method->ssl_get_message(
-      s, SSL3_ST_SR_CHANNEL_ID_A, SSL3_ST_SR_CHANNEL_ID_B,
+  n = ssl->method->ssl_get_message(
+      ssl, SSL3_ST_SR_CHANNEL_ID_A, SSL3_ST_SR_CHANNEL_ID_B,
       SSL3_MT_ENCRYPTED_EXTENSIONS, 2 + 2 + TLSEXT_CHANNEL_ID_SIZE,
       ssl_dont_hash_message, &ok);
 
@@ -2482,26 +2190,16 @@
 
   /* Before incorporating the EncryptedExtensions message to the handshake
    * hash, compute the hash that should have been signed. */
-  if (!tls1_channel_id_hash(s, channel_id_hash, &channel_id_hash_len)) {
+  if (!tls1_channel_id_hash(ssl, channel_id_hash, &channel_id_hash_len)) {
     return -1;
   }
   assert(channel_id_hash_len == SHA256_DIGEST_LENGTH);
 
-  if (!ssl3_hash_current_message(s)) {
+  if (!ssl3_hash_current_message(ssl)) {
     return -1;
   }
 
-  /* s->state doesn't reflect whether ChangeCipherSpec has been received in
-   * this handshake, but s->s3->change_cipher_spec does (will be reset by
-   * ssl3_get_finished).
-   *
-   * TODO(davidben): Is this check now redundant with SSL3_FLAGS_EXPECT_CCS? */
-  if (!s->s3->change_cipher_spec) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_CHANNEL_ID_BEFORE_A_CCS);
-    return -1;
-  }
-
-  CBS_init(&encrypted_extensions, s->init_msg, n);
+  CBS_init(&encrypted_extensions, ssl->init_msg, n);
 
   /* EncryptedExtensions could include multiple extensions, but the only
    * extension that could be negotiated is ChannelID, so there can only be one
@@ -2547,7 +2245,8 @@
   }
 
   point = EC_POINT_new(p256);
-  if (!point || !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
+  if (!point ||
+      !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
     goto err;
   }
 
@@ -2561,11 +2260,11 @@
    * were called. */
   if (!ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
-    s->s3->tlsext_channel_id_valid = 0;
+    ssl->s3->tlsext_channel_id_valid = 0;
     goto err;
   }
 
-  memcpy(s->s3->tlsext_channel_id, p, 64);
+  memcpy(ssl->s3->tlsext_channel_id, p, 64);
   ret = 1;
 
 err:
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index f9001c7..8829679 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -74,17 +74,20 @@
   assert(EVP_AEAD_nonce_length(aead) <= EVP_AEAD_MAX_NONCE_LENGTH);
   aead_ctx->variable_nonce_len = (uint8_t)EVP_AEAD_nonce_length(aead);
   if (mac_key_len == 0) {
-    /* For a real AEAD, the IV is the fixed part of the nonce. */
-    if (fixed_iv_len > sizeof(aead_ctx->fixed_nonce) ||
-        fixed_iv_len > aead_ctx->variable_nonce_len) {
-      SSL_AEAD_CTX_free(aead_ctx);
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-    aead_ctx->variable_nonce_len -= fixed_iv_len;
-
+    assert(fixed_iv_len <= sizeof(aead_ctx->fixed_nonce));
     memcpy(aead_ctx->fixed_nonce, fixed_iv, fixed_iv_len);
     aead_ctx->fixed_nonce_len = fixed_iv_len;
+
+    if (cipher->algorithm_enc & SSL_CHACHA20POLY1305) {
+      /* The fixed nonce into the actual nonce (the sequence number). */
+      aead_ctx->xor_fixed_nonce = 1;
+      aead_ctx->variable_nonce_len = 8;
+    } else {
+      /* The fixed IV is prepended to the nonce. */
+      assert(fixed_iv_len <= aead_ctx->variable_nonce_len);
+      aead_ctx->variable_nonce_len -= fixed_iv_len;
+    }
+
     /* AES-GCM uses an explicit nonce. */
     if (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) {
       aead_ctx->variable_nonce_included_in_record = 1;
@@ -176,8 +179,17 @@
   /* Assemble the nonce. */
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
   size_t nonce_len = 0;
-  memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
-  nonce_len += aead->fixed_nonce_len;
+
+  /* Prepend the fixed nonce, or left-pad with zeros if XORing. */
+  if (aead->xor_fixed_nonce) {
+    nonce_len = aead->fixed_nonce_len - aead->variable_nonce_len;
+    memset(nonce, 0, nonce_len);
+  } else {
+    memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
+    nonce_len += aead->fixed_nonce_len;
+  }
+
+  /* Add the variable nonce. */
   if (aead->variable_nonce_included_in_record) {
     if (in_len < aead->variable_nonce_len) {
       /* Publicly invalid. */
@@ -193,6 +205,15 @@
   }
   nonce_len += aead->variable_nonce_len;
 
+  /* XOR the fixed nonce, if necessary. */
+  if (aead->xor_fixed_nonce) {
+    assert(nonce_len == aead->fixed_nonce_len);
+    size_t i;
+    for (i = 0; i < aead->fixed_nonce_len; i++) {
+      nonce[i] ^= aead->fixed_nonce[i];
+    }
+  }
+
   return EVP_AEAD_CTX_open(&aead->ctx, out, out_len, max_out, nonce, nonce_len,
                            in, in_len, ad, ad_len);
 }
@@ -219,8 +240,17 @@
   /* Assemble the nonce. */
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
   size_t nonce_len = 0;
-  memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
-  nonce_len += aead->fixed_nonce_len;
+
+  /* Prepend the fixed nonce, or left-pad with zeros if XORing. */
+  if (aead->xor_fixed_nonce) {
+    nonce_len = aead->fixed_nonce_len - aead->variable_nonce_len;
+    memset(nonce, 0, nonce_len);
+  } else {
+    memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
+    nonce_len += aead->fixed_nonce_len;
+  }
+
+  /* Select the variable nonce. */
   if (aead->random_variable_nonce) {
     assert(aead->variable_nonce_included_in_record);
     if (!RAND_bytes(nonce + nonce_len, aead->variable_nonce_len)) {
@@ -230,13 +260,14 @@
     /* When sending we use the sequence number as the variable part of the
      * nonce. */
     assert(aead->variable_nonce_len == 8);
-    memcpy(nonce + nonce_len, ad, aead->variable_nonce_len);
+    memcpy(nonce + nonce_len, seqnum, aead->variable_nonce_len);
   }
   nonce_len += aead->variable_nonce_len;
 
   /* Emit the variable nonce if included in the record. */
   size_t extra_len = 0;
   if (aead->variable_nonce_included_in_record) {
+    assert(!aead->xor_fixed_nonce);
     if (max_out < aead->variable_nonce_len) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
       return 0;
@@ -251,6 +282,15 @@
     max_out -= aead->variable_nonce_len;
   }
 
+  /* XOR the fixed nonce, if necessary. */
+  if (aead->xor_fixed_nonce) {
+    assert(nonce_len == aead->fixed_nonce_len);
+    size_t i;
+    for (i = 0; i < aead->fixed_nonce_len; i++) {
+      nonce[i] ^= aead->fixed_nonce[i];
+    }
+  }
+
   if (!EVP_AEAD_CTX_seal(&aead->ctx, out, out_len, max_out, nonce, nonce_len,
                          in, in_len, ad, ad_len)) {
     return 0;
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index 0ad4a11..5ec33eb 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -492,8 +492,12 @@
 }
 
 static X509 *parse_x509(CBS *cbs) {
+  if (CBS_len(cbs) > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
+    return NULL;
+  }
   const uint8_t *ptr = CBS_data(cbs);
-  X509 *ret = d2i_X509(NULL, &ptr, CBS_len(cbs));
+  X509 *ret = d2i_X509(NULL, &ptr, (long)CBS_len(cbs));
   if (ret == NULL) {
     return NULL;
   }
diff --git a/src/ssl/ssl_buffer.c b/src/ssl/ssl_buffer.c
index f1abc53..7fd74e4 100644
--- a/src/ssl/ssl_buffer.c
+++ b/src/ssl/ssl_buffer.c
@@ -70,8 +70,8 @@
   memset(buf, 0, sizeof(SSL3_BUFFER));
 }
 
-OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH +
-                           SSL3_RT_MAX_EXTRA <= 0xffff,
+OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH <=
+                           0xffff,
                        maximum_read_buffer_too_large);
 
 /* setup_read_buffer initializes the read buffer if not already initialized. It
@@ -90,9 +90,6 @@
   } else {
     cap += SSL3_RT_HEADER_LENGTH;
   }
-  if (ssl->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) {
-    cap += SSL3_RT_MAX_EXTRA;
-  }
 
   return setup_buffer(buf, header_len, cap);
 }
@@ -131,9 +128,6 @@
   SSL3_BUFFER *buf = &ssl->s3->read_buffer;
 
   if (len > buf->cap) {
-    /* This may occur if |SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER| was toggled after
-     * |setup_read_buffer| was called. Stay within bounds, but do not attempt to
-     * recover. */
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
     return -1;
   }
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 4094b27..4952cfd 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -166,28 +166,9 @@
       OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
       goto err;
     }
-    if (cert->dh_tmp->priv_key) {
-      BIGNUM *b = BN_dup(cert->dh_tmp->priv_key);
-      if (!b) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_BN_LIB);
-        goto err;
-      }
-      ret->dh_tmp->priv_key = b;
-    }
-    if (cert->dh_tmp->pub_key) {
-      BIGNUM *b = BN_dup(cert->dh_tmp->pub_key);
-      if (!b) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_BN_LIB);
-        goto err;
-      }
-      ret->dh_tmp->pub_key = b;
-    }
   }
   ret->dh_tmp_cb = cert->dh_tmp_cb;
 
-  ret->ecdh_nid = cert->ecdh_nid;
-  ret->ecdh_tmp_cb = cert->ecdh_tmp_cb;
-
   if (cert->x509 != NULL) {
     ret->x509 = X509_up_ref(cert->x509);
   }
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index e87835f..77fa8fa 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -155,33 +155,50 @@
 
 
 /* kCiphers is an array of all supported ciphers, sorted by id. */
-const SSL_CIPHER kCiphers[] = {
+static const SSL_CIPHER kCiphers[] = {
     /* The RSA ciphers */
     /* Cipher 02 */
     {
-     SSL3_TXT_RSA_NULL_SHA, SSL3_CK_RSA_NULL_SHA, SSL_kRSA, SSL_aRSA,
-     SSL_eNULL, SSL_SHA1, SSL_SSLV3, SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT, 0, 0,
+     SSL3_TXT_RSA_NULL_SHA,
+     SSL3_CK_RSA_NULL_SHA,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_eNULL,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 04 */
     {
-     SSL3_TXT_RSA_RC4_128_MD5, SSL3_CK_RSA_RC4_128_MD5, SSL_kRSA, SSL_aRSA,
-     SSL_RC4, SSL_MD5, SSL_SSLV3, SSL_MEDIUM,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     SSL3_TXT_RSA_RC4_128_MD5,
+     SSL3_CK_RSA_RC4_128_MD5,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_RC4,
+     SSL_MD5,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 05 */
     {
-     SSL3_TXT_RSA_RC4_128_SHA, SSL3_CK_RSA_RC4_128_SHA, SSL_kRSA, SSL_aRSA,
-     SSL_RC4, SSL_SHA1, SSL_SSLV3, SSL_MEDIUM,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     SSL3_TXT_RSA_RC4_128_SHA,
+     SSL3_CK_RSA_RC4_128_SHA,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_RC4,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 0A */
     {
-     SSL3_TXT_RSA_DES_192_CBC3_SHA, SSL3_CK_RSA_DES_192_CBC3_SHA, SSL_kRSA,
-     SSL_aRSA, SSL_3DES, SSL_SHA1, SSL_SSLV3, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 112, 168,
+     SSL3_TXT_RSA_DES_192_CBC3_SHA,
+     SSL3_CK_RSA_DES_192_CBC3_SHA,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_3DES,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
 
@@ -189,30 +206,46 @@
 
     /* Cipher 2F */
     {
-     TLS1_TXT_RSA_WITH_AES_128_SHA, TLS1_CK_RSA_WITH_AES_128_SHA, SSL_kRSA,
-     SSL_aRSA, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     TLS1_TXT_RSA_WITH_AES_128_SHA,
+     TLS1_CK_RSA_WITH_AES_128_SHA,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_AES128,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 33 */
     {
-     TLS1_TXT_DHE_RSA_WITH_AES_128_SHA, TLS1_CK_DHE_RSA_WITH_AES_128_SHA,
-     SSL_kDHE, SSL_aRSA, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     TLS1_TXT_DHE_RSA_WITH_AES_128_SHA,
+     TLS1_CK_DHE_RSA_WITH_AES_128_SHA,
+     SSL_kDHE,
+     SSL_aRSA,
+     SSL_AES128,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 35 */
     {
-     TLS1_TXT_RSA_WITH_AES_256_SHA, TLS1_CK_RSA_WITH_AES_256_SHA, SSL_kRSA,
-     SSL_aRSA, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 256, 256,
+     TLS1_TXT_RSA_WITH_AES_256_SHA,
+     TLS1_CK_RSA_WITH_AES_256_SHA,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_AES256,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 39 */
     {
-     TLS1_TXT_DHE_RSA_WITH_AES_256_SHA, TLS1_CK_DHE_RSA_WITH_AES_256_SHA,
-     SSL_kDHE, SSL_aRSA, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 256, 256,
+     TLS1_TXT_DHE_RSA_WITH_AES_256_SHA,
+     TLS1_CK_DHE_RSA_WITH_AES_256_SHA,
+     SSL_kDHE,
+     SSL_aRSA,
+     SSL_AES256,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
 
@@ -220,55 +253,81 @@
 
     /* Cipher 3C */
     {
-     TLS1_TXT_RSA_WITH_AES_128_SHA256, TLS1_CK_RSA_WITH_AES_128_SHA256,
-     SSL_kRSA, SSL_aRSA, SSL_AES128, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256, 128, 128,
+     TLS1_TXT_RSA_WITH_AES_128_SHA256,
+     TLS1_CK_RSA_WITH_AES_128_SHA256,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_AES128,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
     },
 
     /* Cipher 3D */
     {
-     TLS1_TXT_RSA_WITH_AES_256_SHA256, TLS1_CK_RSA_WITH_AES_256_SHA256,
-     SSL_kRSA, SSL_aRSA, SSL_AES256, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256, 256, 256,
+     TLS1_TXT_RSA_WITH_AES_256_SHA256,
+     TLS1_CK_RSA_WITH_AES_256_SHA256,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_AES256,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
     },
 
     /* Cipher 67 */
     {
      TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256,
-     TLS1_CK_DHE_RSA_WITH_AES_128_SHA256, SSL_kDHE, SSL_aRSA, SSL_AES128,
-     SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA256, 128, 128,
+     TLS1_CK_DHE_RSA_WITH_AES_128_SHA256,
+     SSL_kDHE,
+     SSL_aRSA,
+     SSL_AES128,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
     },
 
     /* Cipher 6B */
     {
      TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256,
-     TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, SSL_kDHE, SSL_aRSA, SSL_AES256,
-     SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA256, 256, 256,
+     TLS1_CK_DHE_RSA_WITH_AES_256_SHA256,
+     SSL_kDHE,
+     SSL_aRSA,
+     SSL_AES256,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
     },
 
     /* PSK cipher suites. */
 
     /* Cipher 8A */
     {
-     TLS1_TXT_PSK_WITH_RC4_128_SHA, TLS1_CK_PSK_WITH_RC4_128_SHA, SSL_kPSK,
-     SSL_aPSK, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     TLS1_TXT_PSK_WITH_RC4_128_SHA,
+     TLS1_CK_PSK_WITH_RC4_128_SHA,
+     SSL_kPSK,
+     SSL_aPSK,
+     SSL_RC4,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 8C */
     {
-     TLS1_TXT_PSK_WITH_AES_128_CBC_SHA, TLS1_CK_PSK_WITH_AES_128_CBC_SHA,
-     SSL_kPSK, SSL_aPSK, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     TLS1_TXT_PSK_WITH_AES_128_CBC_SHA,
+     TLS1_CK_PSK_WITH_AES_128_CBC_SHA,
+     SSL_kPSK,
+     SSL_aPSK,
+     SSL_AES128,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher 8D */
     {
-     TLS1_TXT_PSK_WITH_AES_256_CBC_SHA, TLS1_CK_PSK_WITH_AES_256_CBC_SHA,
-     SSL_kPSK, SSL_aPSK, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 256, 256,
+     TLS1_TXT_PSK_WITH_AES_256_CBC_SHA,
+     TLS1_CK_PSK_WITH_AES_256_CBC_SHA,
+     SSL_kPSK,
+     SSL_aPSK,
+     SSL_AES256,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* GCM ciphersuites from RFC5288 */
@@ -276,84 +335,111 @@
     /* Cipher 9C */
     {
      TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, SSL_kRSA, SSL_aRSA, SSL_AES128GCM,
-     SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_RSA_WITH_AES_128_GCM_SHA256,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_AES128GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA256,
-     128, 128,
     },
 
     /* Cipher 9D */
     {
      TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_RSA_WITH_AES_256_GCM_SHA384, SSL_kRSA, SSL_aRSA, SSL_AES256GCM,
-     SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_RSA_WITH_AES_256_GCM_SHA384,
+     SSL_kRSA,
+     SSL_aRSA,
+     SSL_AES256GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA384,
-     256, 256,
     },
 
     /* Cipher 9E */
     {
      TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kDHE, SSL_aRSA, SSL_AES128GCM,
-     SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256,
+     SSL_kDHE,
+     SSL_aRSA,
+     SSL_AES128GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA256,
-     128, 128,
     },
 
     /* Cipher 9F */
     {
      TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kDHE, SSL_aRSA, SSL_AES256GCM,
-     SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384,
+     SSL_kDHE,
+     SSL_aRSA,
+     SSL_AES256GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA384,
-     256, 256,
     },
 
     /* Cipher C007 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA,
-     TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_kECDHE, SSL_aECDSA, SSL_RC4,
-     SSL_SHA1, SSL_TLSV1, SSL_MEDIUM, SSL_HANDSHAKE_MAC_DEFAULT, 128,
-     128,
+     TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_RC4,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher C009 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_kECDHE, SSL_aECDSA,
-     SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_AES128,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher C00A */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_kECDHE, SSL_aECDSA,
-     SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 256, 256,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_AES256,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher C011 */
     {
-     TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA, TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA,
-     SSL_kECDHE, SSL_aRSA, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA,
+     TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_RC4,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher C013 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA,
-     TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_kECDHE, SSL_aRSA, SSL_AES128,
-     SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_AES128,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher C014 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-     TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_kECDHE, SSL_aRSA, SSL_AES256,
-     SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 256, 256,
+     TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_AES256,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
 
@@ -362,33 +448,45 @@
     /* Cipher C023 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, SSL_kECDHE, SSL_aECDSA,
-     SSL_AES128, SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA256, 128, 128,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_AES128,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
     },
 
     /* Cipher C024 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, SSL_kECDHE, SSL_aECDSA,
-     SSL_AES256, SSL_SHA384, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA384, 256, 256,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_AES256,
+     SSL_SHA384,
+     SSL_HANDSHAKE_MAC_SHA384,
     },
 
     /* Cipher C027 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256,
-     TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, SSL_kECDHE, SSL_aRSA, SSL_AES128,
-     SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA256, 128, 128,
+     TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_AES128,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
     },
 
     /* Cipher C028 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384,
-     TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, SSL_kECDHE, SSL_aRSA, SSL_AES256,
-     SSL_SHA384, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_SHA384, 256, 256,
+     TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_AES256,
+     SSL_SHA384,
+     SSL_HANDSHAKE_MAC_SHA384,
     },
 
 
@@ -397,37 +495,45 @@
     /* Cipher C02B */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_kECDHE, SSL_aECDSA,
-     SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_AES128GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA256,
-     128, 128,
     },
 
     /* Cipher C02C */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_kECDHE, SSL_aECDSA,
-     SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_AES256GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA384,
-     256, 256,
     },
 
     /* Cipher C02F */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
-     TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kECDHE, SSL_aRSA,
-     SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_AES128GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA256,
-     128, 128,
     },
 
     /* Cipher C030 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
-     TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kECDHE, SSL_aRSA,
-     SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
+     TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_AES256GCM,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA384,
-     256, 256,
     },
 
     /* ECDHE-PSK cipher suites. */
@@ -436,37 +542,80 @@
     {
      TLS1_TXT_ECDHE_PSK_WITH_AES_128_CBC_SHA,
      TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA,
-     SSL_kECDHE, SSL_aPSK, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 128, 128,
+     SSL_kECDHE,
+     SSL_aPSK,
+     SSL_AES128,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
     /* Cipher C036 */
     {
      TLS1_TXT_ECDHE_PSK_WITH_AES_256_CBC_SHA,
      TLS1_CK_ECDHE_PSK_WITH_AES_256_CBC_SHA,
-     SSL_kECDHE, SSL_aPSK, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT, 256, 256,
+     SSL_kECDHE,
+     SSL_aPSK,
+     SSL_AES256,
+     SSL_SHA1,
+     SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     /* ChaCha20-Poly1305 cipher suites. */
 
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
     {
      TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_OLD,
-     TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, SSL_kECDHE, SSL_aRSA,
-     SSL_CHACHA20POLY1305_OLD, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
+     TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_CHACHA20POLY1305_OLD,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA256,
-     256, 256,
     },
 
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_OLD,
-     TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, SSL_kECDHE, SSL_aECDSA,
-     SSL_CHACHA20POLY1305_OLD, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH,
+     TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_CHACHA20POLY1305_OLD,
+     SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA256,
-     256, 256,
     },
 #endif
+
+    /* Cipher CCA8 */
+    {
+     TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    /* Cipher CCA9 */
+    {
+     TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    /* Cipher CCAB */
+    {
+     TLS1_TXT_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kECDHE,
+     SSL_aPSK,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
 };
 
 static const size_t kCiphersLen = sizeof(kCiphers) / sizeof(kCiphers[0]);
@@ -496,13 +645,15 @@
   uint32_t algorithm_auth;
   uint32_t algorithm_enc;
   uint32_t algorithm_mac;
-  uint32_t algorithm_ssl;
-  uint32_t algo_strength;
+
+  /* min_version, if non-zero, matches all ciphers which were added in that
+   * particular protocol version. */
+  uint16_t min_version;
 } CIPHER_ALIAS;
 
 static const CIPHER_ALIAS kCipherAliases[] = {
     /* "ALL" doesn't include eNULL (must be specifically enabled) */
-    {"ALL", ~0u, ~0u, ~SSL_eNULL, ~0u, ~0u, ~0u},
+    {"ALL", ~0u, ~0u, ~SSL_eNULL, ~0u, 0},
 
     /* The "COMPLEMENTOFDEFAULT" rule is omitted. It matches nothing. */
 
@@ -510,58 +661,59 @@
      * (some of those using only a single bit here combine
      * multiple key exchange algs according to the RFCs,
      * e.g. kEDH combines DHE_DSS and DHE_RSA) */
-    {"kRSA", SSL_kRSA, ~0u, ~0u, ~0u, ~0u, ~0u},
+    {"kRSA", SSL_kRSA, ~0u, ~0u, ~0u, 0},
 
-    {"kDHE", SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"kEDH", SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"DH", SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+    {"kDHE", SSL_kDHE, ~0u, ~0u, ~0u, 0},
+    {"kEDH", SSL_kDHE, ~0u, ~0u, ~0u, 0},
+    {"DH", SSL_kDHE, ~0u, ~0u, ~0u, 0},
 
-    {"kECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"kEECDH", SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"ECDH", SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
+    {"kECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
+    {"kEECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
+    {"ECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
 
-    {"kPSK", SSL_kPSK, ~0u, ~0u, ~0u, ~0u, ~0u},
+    {"kPSK", SSL_kPSK, ~0u, ~0u, ~0u, 0},
 
     /* server authentication aliases */
-    {"aRSA", ~0u, SSL_aRSA, ~SSL_eNULL, ~0u, ~0u, ~0u},
-    {"aECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u},
-    {"ECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u},
-    {"aPSK", ~0u, SSL_aPSK, ~0u, ~0u, ~0u, ~0u},
+    {"aRSA", ~0u, SSL_aRSA, ~SSL_eNULL, ~0u, 0},
+    {"aECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0},
+    {"ECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0},
+    {"aPSK", ~0u, SSL_aPSK, ~0u, ~0u, 0},
 
     /* aliases combining key exchange and server authentication */
-    {"DHE", SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"EDH", SSL_kDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"ECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"EECDH", SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u},
-    {"RSA", SSL_kRSA, SSL_aRSA, ~SSL_eNULL, ~0u, ~0u, ~0u},
-    {"PSK", SSL_kPSK, SSL_aPSK, ~0u, ~0u, ~0u, ~0u},
+    {"DHE", SSL_kDHE, ~0u, ~0u, ~0u, 0},
+    {"EDH", SSL_kDHE, ~0u, ~0u, ~0u, 0},
+    {"ECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
+    {"EECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
+    {"RSA", SSL_kRSA, SSL_aRSA, ~SSL_eNULL, ~0u, 0},
+    {"PSK", SSL_kPSK, SSL_aPSK, ~0u, ~0u, 0},
 
     /* symmetric encryption aliases */
-    {"3DES", ~0u, ~0u, SSL_3DES, ~0u, ~0u, ~0u},
-    {"RC4", ~0u, ~0u, SSL_RC4, ~0u, ~0u, ~0u},
-    {"AES128", ~0u, ~0u, SSL_AES128 | SSL_AES128GCM, ~0u, ~0u, ~0u},
-    {"AES256", ~0u, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, ~0u, ~0u},
-    {"AES", ~0u, ~0u, SSL_AES, ~0u, ~0u, ~0u},
-    {"AESGCM", ~0u, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, ~0u, ~0u},
-    {"CHACHA20", ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, ~0u, ~0u},
+    {"3DES", ~0u, ~0u, SSL_3DES, ~0u, 0},
+    {"RC4", ~0u, ~0u, SSL_RC4, ~0u, 0},
+    {"AES128", ~0u, ~0u, SSL_AES128 | SSL_AES128GCM, ~0u, 0},
+    {"AES256", ~0u, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, 0},
+    {"AES", ~0u, ~0u, SSL_AES, ~0u, 0},
+    {"AESGCM", ~0u, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, 0},
+    {"CHACHA20", ~0u, ~0u, SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD, ~0u,
+     0},
 
     /* MAC aliases */
-    {"MD5", ~0u, ~0u, ~0u, SSL_MD5, ~0u, ~0u},
-    {"SHA1", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, ~0u, ~0u},
-    {"SHA", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, ~0u, ~0u},
-    {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, ~0u, ~0u},
-    {"SHA384", ~0u, ~0u, ~0u, SSL_SHA384, ~0u, ~0u},
+    {"MD5", ~0u, ~0u, ~0u, SSL_MD5, 0},
+    {"SHA1", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0},
+    {"SHA", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0},
+    {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, 0},
+    {"SHA384", ~0u, ~0u, ~0u, SSL_SHA384, 0},
 
-    /* protocol version aliases */
-    {"SSLv3", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL_SSLV3, ~0u},
-    {"TLSv1", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL_TLSV1, ~0u},
-    {"TLSv1.2", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL_TLSV1_2, ~0u},
+    /* Legacy protocol minimum version aliases. "TLSv1" is intentionally the
+     * same as "SSLv3". */
+    {"SSLv3", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
+    {"TLSv1", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
+    {"TLSv1.2", ~0u, ~0u, ~SSL_eNULL, ~0u, TLS1_2_VERSION},
 
-    /* strength classes */
-    {"MEDIUM", ~0u, ~0u, ~0u, ~0u, ~0u, SSL_MEDIUM},
-    {"HIGH", ~0u, ~0u, ~0u, ~0u, ~0u, SSL_HIGH},
-    /* FIPS 140-2 approved ciphersuite */
-    {"FIPS", ~0u, ~0u, ~SSL_eNULL, ~0u, ~0u, SSL_FIPS},
+    /* Legacy strength classes. */
+    {"MEDIUM", ~0u, ~0u, SSL_RC4, ~0u, 0},
+    {"HIGH", ~0u, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
+    {"FIPS", ~0u, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
 };
 
 static const size_t kCipherAliasesLen =
@@ -618,6 +770,11 @@
       return 1;
 #endif
 
+    case SSL_CHACHA20POLY1305:
+      *out_aead = EVP_aead_chacha20_poly1305();
+      *out_fixed_iv_len = 12;
+      return 1;
+
     case SSL_RC4:
       switch (cipher->algorithm_mac) {
         case SSL_MD5:
@@ -838,19 +995,18 @@
  * - Otherwise, if |strength_bits| is non-negative, it selects ciphers
  *   of that strength.
  * - Otherwise, it selects ciphers that match each bitmasks in |alg_*| and
- *   |algo_strength|. */
+ *   |min_version|. */
 static void ssl_cipher_apply_rule(
     uint32_t cipher_id, uint32_t alg_mkey, uint32_t alg_auth,
-    uint32_t alg_enc, uint32_t alg_mac, uint32_t alg_ssl,
-    uint32_t algo_strength, int rule, int strength_bits, int in_group,
-    CIPHER_ORDER **head_p, CIPHER_ORDER **tail_p) {
+    uint32_t alg_enc, uint32_t alg_mac, uint16_t min_version, int rule,
+    int strength_bits, int in_group, CIPHER_ORDER **head_p,
+    CIPHER_ORDER **tail_p) {
   CIPHER_ORDER *head, *tail, *curr, *next, *last;
   const SSL_CIPHER *cp;
   int reverse = 0;
 
-  if (cipher_id == 0 && strength_bits == -1 &&
-      (alg_mkey == 0 || alg_auth == 0 || alg_enc == 0 || alg_mac == 0 ||
-       alg_ssl == 0 || algo_strength == 0)) {
+  if (cipher_id == 0 && strength_bits == -1 && min_version == 0 &&
+      (alg_mkey == 0 || alg_auth == 0 || alg_enc == 0 || alg_mac == 0)) {
     /* The rule matches nothing, so bail early. */
     return;
   }
@@ -892,15 +1048,15 @@
         continue;
       }
     } else if (strength_bits >= 0) {
-      if (strength_bits != cp->strength_bits) {
+      if (strength_bits != SSL_CIPHER_get_bits(cp, NULL)) {
         continue;
       }
     } else if (!(alg_mkey & cp->algorithm_mkey) ||
                !(alg_auth & cp->algorithm_auth) ||
                !(alg_enc & cp->algorithm_enc) ||
                !(alg_mac & cp->algorithm_mac) ||
-               !(alg_ssl & cp->algorithm_ssl) ||
-               !(algo_strength & cp->algo_strength)) {
+               (min_version != 0 &&
+                SSL_CIPHER_get_min_version(cp) != min_version)) {
       continue;
     }
 
@@ -969,8 +1125,9 @@
   max_strength_bits = 0;
   curr = *head_p;
   while (curr != NULL) {
-    if (curr->active && curr->cipher->strength_bits > max_strength_bits) {
-      max_strength_bits = curr->cipher->strength_bits;
+    if (curr->active &&
+        SSL_CIPHER_get_bits(curr->cipher, NULL) > max_strength_bits) {
+      max_strength_bits = SSL_CIPHER_get_bits(curr->cipher, NULL);
     }
     curr = curr->next;
   }
@@ -986,7 +1143,7 @@
   curr = *head_p;
   while (curr != NULL) {
     if (curr->active) {
-      number_uses[curr->cipher->strength_bits]++;
+      number_uses[SSL_CIPHER_get_bits(curr->cipher, NULL)]++;
     }
     curr = curr->next;
   }
@@ -994,8 +1151,7 @@
   /* Go through the list of used strength_bits values in descending order. */
   for (i = max_strength_bits; i >= 0; i--) {
     if (number_uses[i] > 0) {
-      ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ORD, i, 0, head_p,
-                            tail_p);
+      ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, CIPHER_ORD, i, 0, head_p, tail_p);
     }
   }
 
@@ -1007,9 +1163,10 @@
                                       const char *rule_str,
                                       CIPHER_ORDER **head_p,
                                       CIPHER_ORDER **tail_p) {
-  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength;
+  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac;
+  uint16_t min_version;
   const char *l, *buf;
-  int multi, rule, retval, ok, in_group = 0, has_group = 0;
+  int multi, skip_rule, rule, retval, ok, in_group = 0, has_group = 0;
   size_t j, buf_len;
   uint32_t cipher_id;
   char ch;
@@ -1090,8 +1247,8 @@
     alg_auth = ~0u;
     alg_enc = ~0u;
     alg_mac = ~0u;
-    alg_ssl = ~0u;
-    algo_strength = ~0u;
+    min_version = 0;
+    skip_rule = 0;
 
     for (;;) {
       ch = *l;
@@ -1135,13 +1292,18 @@
             alg_auth &= kCipherAliases[j].algorithm_auth;
             alg_enc &= kCipherAliases[j].algorithm_enc;
             alg_mac &= kCipherAliases[j].algorithm_mac;
-            alg_ssl &= kCipherAliases[j].algorithm_ssl;
-            algo_strength &= kCipherAliases[j].algo_strength;
+
+            if (min_version != 0 &&
+                min_version != kCipherAliases[j].min_version) {
+              skip_rule = 1;
+            } else {
+              min_version = kCipherAliases[j].min_version;
+            }
             break;
           }
         }
         if (j == kCipherAliasesLen) {
-          alg_mkey = alg_auth = alg_enc = alg_mac = alg_ssl = algo_strength = 0;
+          skip_rule = 1;
         }
       }
 
@@ -1153,6 +1315,29 @@
       multi = 1;
     }
 
+    /* If one of the CHACHA20_POLY1305 variants is selected, include the other
+     * as well. They have the same name to avoid requiring changes in
+     * configuration. Apply this transformation late so that the cipher name
+     * still behaves as an exact name and not an alias in multipart rules.
+     *
+     * This is temporary and will be removed when the pre-standard construction
+     * is removed. */
+    if (cipher_id == TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD ||
+        cipher_id == TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) {
+      cipher_id = 0;
+      alg_mkey = SSL_kECDHE;
+      alg_auth = SSL_aRSA;
+      alg_enc = SSL_CHACHA20POLY1305|SSL_CHACHA20POLY1305_OLD;
+      alg_mac = SSL_AEAD;
+    } else if (cipher_id == TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD ||
+               cipher_id == TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) {
+      cipher_id = 0;
+      alg_mkey = SSL_kECDHE;
+      alg_auth = SSL_aECDSA;
+      alg_enc = SSL_CHACHA20POLY1305|SSL_CHACHA20POLY1305_OLD;
+      alg_mac = SSL_AEAD;
+    }
+
     /* Ok, we have the rule, now apply it. */
     if (rule == CIPHER_SPECIAL) {
       /* special command */
@@ -1172,10 +1357,9 @@
       while (*l != '\0' && !ITEM_SEP(*l)) {
         l++;
       }
-    } else {
+    } else if (!skip_rule) {
       ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac,
-                            alg_ssl, algo_strength, rule, -1, in_group, head_p,
-                            tail_p);
+                            min_version, rule, -1, in_group, head_p, tail_p);
     }
   }
 
@@ -1221,56 +1405,61 @@
 
   /* Everything else being equal, prefer ECDHE_ECDSA then ECDHE_RSA over other
    * key exchange mechanisms */
-  ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, ~0u, ~0u, ~0u, ~0u,
-                        CIPHER_ADD, -1, 0, &head, &tail);
-  ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_ADD, -1,
+  ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, ~0u, ~0u, 0, CIPHER_ADD, -1,
                         0, &head, &tail);
-  ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_DEL, -1,
-                        0, &head, &tail);
+  ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0,
+                        &head, &tail);
+  ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, 0, CIPHER_DEL, -1, 0,
+                        &head, &tail);
 
   /* Order the bulk ciphers. First the preferred AEAD ciphers. We prefer
    * CHACHA20 unless there is hardware support for fast and constant-time
-   * AES_GCM. */
+   * AES_GCM. Of the two CHACHA20 variants, the new one is preferred over the
+   * old one. */
   if (EVP_has_aes_hardware()) {
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
+                          &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0,
+                          &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0, CIPHER_ADD,
                           -1, 0, &head, &tail);
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
-                          -1, 0, &head, &tail);
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, ~0u, ~0u,
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, 0,
                           CIPHER_ADD, -1, 0, &head, &tail);
   } else {
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, ~0u, ~0u,
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0, CIPHER_ADD,
+                          -1, 0, &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, 0,
                           CIPHER_ADD, -1, 0, &head, &tail);
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
-                          -1, 0, &head, &tail);
-    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, ~0u, ~0u, CIPHER_ADD,
-                          -1, 0, &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
+                          &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0,
+                          &head, &tail);
   }
 
   /* Then the legacy non-AEAD ciphers: AES_256_CBC, AES-128_CBC, RC4_128_SHA,
    * RC4_128_MD5, 3DES_EDE_CBC_SHA. */
-  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256, ~0u, ~0u, ~0u, CIPHER_ADD, -1,
-                        0, &head, &tail);
-  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128, ~0u, ~0u, ~0u, CIPHER_ADD, -1,
-                        0, &head, &tail);
-  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, ~SSL_MD5, ~0u, ~0u, CIPHER_ADD,
-                        -1, 0, &head, &tail);
-  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, SSL_MD5, ~0u, ~0u, CIPHER_ADD, -1,
-                        0, &head, &tail);
-  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_3DES, ~0u, ~0u, ~0u, CIPHER_ADD, -1, 0,
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256, ~0u, 0, CIPHER_ADD, -1, 0,
                         &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128, ~0u, 0, CIPHER_ADD, -1, 0,
+                        &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, ~SSL_MD5, 0, CIPHER_ADD, -1, 0,
+                        &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_RC4, SSL_MD5, 0, CIPHER_ADD, -1, 0,
+                        &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_3DES, ~0u, 0, CIPHER_ADD, -1, 0, &head,
+                        &tail);
 
   /* Temporarily enable everything else for sorting */
-  ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_ADD, -1, 0,
-                        &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0, &head,
+                        &tail);
 
   /* Move ciphers without forward secrecy to the end. */
-  ssl_cipher_apply_rule(0, ~(SSL_kDHE | SSL_kECDHE), ~0u, ~0u, ~0u, ~0u, ~0u,
+  ssl_cipher_apply_rule(0, ~(SSL_kDHE | SSL_kECDHE), ~0u, ~0u, ~0u, 0,
                         CIPHER_ORD, -1, 0, &head, &tail);
 
   /* Now disable everything (maintaining the ordering!) */
-  ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, ~0u, ~0u, CIPHER_DEL, -1, 0,
-                        &head, &tail);
+  ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, 0, CIPHER_DEL, -1, 0, &head,
+                        &tail);
 
   /* If the rule_string begins with DEFAULT, apply the default rule before
    * using the (possibly available) additional rules. */
@@ -1396,8 +1585,17 @@
   return (cipher->algorithm_enc & SSL_AES128GCM) != 0;
 }
 
+int SSL_CIPHER_is_AES128CBC(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_enc & SSL_AES128) != 0;
+}
+
+int SSL_CIPHER_is_AES256CBC(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_enc & SSL_AES256) != 0;
+}
+
 int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher) {
-  return (cipher->algorithm_enc & SSL_CHACHA20POLY1305_OLD) != 0;
+  return (cipher->algorithm_enc &
+          (SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD)) != 0;
 }
 
 int SSL_CIPHER_is_NULL(const SSL_CIPHER *cipher) {
@@ -1418,8 +1616,14 @@
   return (cipher->algorithm_auth & SSL_aECDSA) != 0;
 }
 
+int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mkey & SSL_kECDHE) != 0;
+}
+
 uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher) {
-  if (cipher->algorithm_ssl & SSL_TLSV1_2) {
+  if (cipher->algorithm_prf != SSL_HANDSHAKE_MAC_DEFAULT) {
+    /* Cipher suites before TLS 1.2 use the default PRF, while all those added
+     * afterwards specify a particular hash. */
     return TLS1_2_VERSION;
   }
   return SSL3_VERSION;
@@ -1489,6 +1693,7 @@
       return "AES_128_GCM";
     case SSL_AES256GCM:
       return "AES_256_GCM";
+    case SSL_CHACHA20POLY1305:
     case SSL_CHACHA20POLY1305_OLD:
       return "CHACHA20_POLY1305";
       break;
@@ -1554,32 +1759,57 @@
     return 0;
   }
 
-  if (out_alg_bits != NULL) {
-    *out_alg_bits = cipher->alg_bits;
+  int alg_bits, strength_bits;
+  switch (cipher->algorithm_enc) {
+    case SSL_AES128:
+    case SSL_AES128GCM:
+    case SSL_RC4:
+      alg_bits = 128;
+      strength_bits = 128;
+      break;
+
+    case SSL_AES256:
+    case SSL_AES256GCM:
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+    case SSL_CHACHA20POLY1305_OLD:
+#endif
+    case SSL_CHACHA20POLY1305:
+      alg_bits = 256;
+      strength_bits = 256;
+      break;
+
+    case SSL_3DES:
+      alg_bits = 168;
+      strength_bits = 112;
+      break;
+
+    case SSL_eNULL:
+      alg_bits = 0;
+      strength_bits = 0;
+      break;
+
+    default:
+      assert(0);
+      alg_bits = 0;
+      strength_bits = 0;
   }
-  return cipher->strength_bits;
+
+  if (out_alg_bits != NULL) {
+    *out_alg_bits = alg_bits;
+  }
+  return strength_bits;
 }
 
 const char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf,
                                    int len) {
-  const char *ver;
   const char *kx, *au, *enc, *mac;
-  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl;
-  static const char *format = "%-23s %s Kx=%-8s Au=%-4s Enc=%-9s Mac=%-4s\n";
+  uint32_t alg_mkey, alg_auth, alg_enc, alg_mac;
+  static const char *format = "%-23s Kx=%-8s Au=%-4s Enc=%-9s Mac=%-4s\n";
 
   alg_mkey = cipher->algorithm_mkey;
   alg_auth = cipher->algorithm_auth;
   alg_enc = cipher->algorithm_enc;
   alg_mac = cipher->algorithm_mac;
-  alg_ssl = cipher->algorithm_ssl;
-
-  if (alg_ssl & SSL_SSLV3) {
-    ver = "SSLv3";
-  } else if (alg_ssl & SSL_TLSV1_2) {
-    ver = "TLSv1.2";
-  } else {
-    ver = "unknown";
-  }
 
   switch (alg_mkey) {
     case SSL_kRSA:
@@ -1646,6 +1876,10 @@
       break;
 
     case SSL_CHACHA20POLY1305_OLD:
+      enc = "ChaCha20-Poly1305-Old";
+      break;
+
+    case SSL_CHACHA20POLY1305:
       enc = "ChaCha20-Poly1305";
       break;
 
@@ -1694,7 +1928,7 @@
     return "Buffer too small";
   }
 
-  BIO_snprintf(buf, len, format, cipher->name, ver, kx, au, enc, mac);
+  BIO_snprintf(buf, len, format, cipher->name, kx, au, enc, mac);
   return buf;
 }
 
diff --git a/src/ssl/ssl_ecdh.c b/src/ssl/ssl_ecdh.c
new file mode 100644
index 0000000..45c5b26
--- /dev/null
+++ b/src/ssl/ssl_ecdh.c
@@ -0,0 +1,383 @@
+/* Copyright (c) 2015, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/bytestring.h>
+#include <openssl/curve25519.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "internal.h"
+
+
+/* |EC_POINT| implementation. */
+
+static void ssl_ec_point_cleanup(SSL_ECDH_CTX *ctx) {
+  BIGNUM *private_key = (BIGNUM *)ctx->data;
+  BN_clear_free(private_key);
+}
+
+static int ssl_ec_point_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+  assert(ctx->data == NULL);
+  BIGNUM *private_key = BN_new();
+  if (private_key == NULL) {
+    return 0;
+  }
+  ctx->data = private_key;
+
+  /* Set up a shared |BN_CTX| for all operations. */
+  BN_CTX *bn_ctx = BN_CTX_new();
+  if (bn_ctx == NULL) {
+    return 0;
+  }
+  BN_CTX_start(bn_ctx);
+
+  int ret = 0;
+  EC_POINT *public_key = NULL;
+  EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid);
+  if (group == NULL) {
+    goto err;
+  }
+
+  /* Generate a private key. */
+  const BIGNUM *order = EC_GROUP_get0_order(group);
+  do {
+    if (!BN_rand_range(private_key, order)) {
+      goto err;
+    }
+  } while (BN_is_zero(private_key));
+
+  /* Compute the corresponding public key. */
+  public_key = EC_POINT_new(group);
+  if (public_key == NULL ||
+      !EC_POINT_mul(group, public_key, private_key, NULL, NULL, bn_ctx)) {
+    goto err;
+  }
+
+  /* Serialize the public key. */
+  size_t len = EC_POINT_point2oct(
+      group, public_key, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx);
+  uint8_t *ptr;
+  if (len == 0 ||
+      !CBB_add_space(out, &ptr, len) ||
+      EC_POINT_point2oct(group, public_key, POINT_CONVERSION_UNCOMPRESSED, ptr,
+                         len, bn_ctx) != len) {
+    goto err;
+  }
+
+  ret = 1;
+
+err:
+  EC_GROUP_free(group);
+  EC_POINT_free(public_key);
+  BN_CTX_end(bn_ctx);
+  BN_CTX_free(bn_ctx);
+  return ret;
+}
+
+int ssl_ec_point_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+                                size_t *out_secret_len, uint8_t *out_alert,
+                                const uint8_t *peer_key, size_t peer_key_len) {
+  BIGNUM *private_key = (BIGNUM *)ctx->data;
+  assert(private_key != NULL);
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+
+  /* Set up a shared |BN_CTX| for all operations. */
+  BN_CTX *bn_ctx = BN_CTX_new();
+  if (bn_ctx == NULL) {
+    return 0;
+  }
+  BN_CTX_start(bn_ctx);
+
+  int ret = 0;
+  EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid);
+  EC_POINT *peer_point = NULL, *result = NULL;
+  uint8_t *secret = NULL;
+  if (group == NULL) {
+    goto err;
+  }
+
+  /* Compute the x-coordinate of |peer_key| * |private_key|. */
+  peer_point = EC_POINT_new(group);
+  result = EC_POINT_new(group);
+  if (peer_point == NULL || result == NULL) {
+    goto err;
+  }
+  BIGNUM *x = BN_CTX_get(bn_ctx);
+  if (x == NULL) {
+    goto err;
+  }
+  if (!EC_POINT_oct2point(group, peer_point, peer_key, peer_key_len, bn_ctx)) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    goto err;
+  }
+  if (!EC_POINT_mul(group, result, NULL, peer_point, private_key, bn_ctx) ||
+      !EC_POINT_get_affine_coordinates_GFp(group, result, x, NULL, bn_ctx)) {
+    goto err;
+  }
+
+  /* Encode the x-coordinate left-padded with zeros. */
+  size_t secret_len = (EC_GROUP_get_degree(group) + 7) / 8;
+  secret = OPENSSL_malloc(secret_len);
+  if (secret == NULL || !BN_bn2bin_padded(secret, secret_len, x)) {
+    goto err;
+  }
+
+  *out_secret = secret;
+  *out_secret_len = secret_len;
+  secret = NULL;
+  ret = 1;
+
+err:
+  EC_GROUP_free(group);
+  EC_POINT_free(peer_point);
+  EC_POINT_free(result);
+  BN_CTX_end(bn_ctx);
+  BN_CTX_free(bn_ctx);
+  OPENSSL_free(secret);
+  return ret;
+}
+
+
+/* X25119 implementation. */
+
+static void ssl_x25519_cleanup(SSL_ECDH_CTX *ctx) {
+  if (ctx->data == NULL) {
+    return;
+  }
+  OPENSSL_cleanse(ctx->data, 32);
+  OPENSSL_free(ctx->data);
+}
+
+static int ssl_x25519_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+  assert(ctx->data == NULL);
+
+  ctx->data = OPENSSL_malloc(32);
+  if (ctx->data == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  uint8_t public_key[32];
+  X25519_keypair(public_key, (uint8_t *)ctx->data);
+  return CBB_add_bytes(out, public_key, sizeof(public_key));
+}
+
+static int ssl_x25519_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+                                     size_t *out_secret_len, uint8_t *out_alert,
+                                     const uint8_t *peer_key,
+                                     size_t peer_key_len) {
+  assert(ctx->data != NULL);
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+
+  uint8_t *secret = OPENSSL_malloc(32);
+  if (secret == NULL) {
+    return 0;
+  }
+
+  if (peer_key_len != 32 ||
+      !X25519(secret, (uint8_t *)ctx->data, peer_key)) {
+    OPENSSL_free(secret);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+    return 0;
+  }
+
+  *out_secret = secret;
+  *out_secret_len = 32;
+  return 1;
+}
+
+
+/* Legacy DHE-based implementation. */
+
+static void ssl_dhe_cleanup(SSL_ECDH_CTX *ctx) {
+  DH_free((DH *)ctx->data);
+}
+
+static int ssl_dhe_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+  DH *dh = (DH *)ctx->data;
+  /* The group must have been initialized already, but not the key. */
+  assert(dh != NULL);
+  assert(dh->priv_key == NULL);
+
+  /* Due to a bug in yaSSL, the public key must be zero padded to the size of
+   * the prime. */
+  return DH_generate_key(dh) &&
+         BN_bn2cbb_padded(out, BN_num_bytes(dh->p), dh->pub_key);
+}
+
+static int ssl_dhe_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+                                  size_t *out_secret_len, uint8_t *out_alert,
+                                  const uint8_t *peer_key,
+                                  size_t peer_key_len) {
+  DH *dh = (DH *)ctx->data;
+  assert(dh != NULL);
+  assert(dh->priv_key != NULL);
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+
+  int secret_len = 0;
+  uint8_t *secret = NULL;
+  BIGNUM *peer_point = BN_bin2bn(peer_key, peer_key_len, NULL);
+  if (peer_point == NULL) {
+    goto err;
+  }
+
+  secret = OPENSSL_malloc(DH_size(dh));
+  if (secret == NULL) {
+    goto err;
+  }
+  secret_len = DH_compute_key(secret, peer_point, dh);
+  if (secret_len <= 0) {
+    goto err;
+  }
+
+  *out_secret = secret;
+  *out_secret_len = (size_t)secret_len;
+  BN_free(peer_point);
+  return 1;
+
+err:
+  if (secret_len > 0) {
+    OPENSSL_cleanse(secret, (size_t)secret_len);
+  }
+  OPENSSL_free(secret);
+  BN_free(peer_point);
+  return 0;
+}
+
+static const SSL_ECDH_METHOD kDHEMethod = {
+    NID_undef, 0, "",
+    ssl_dhe_cleanup,
+    ssl_dhe_generate_keypair,
+    ssl_dhe_compute_secret,
+};
+
+
+static const SSL_ECDH_METHOD kMethods[] = {
+    {
+        NID_X9_62_prime256v1,
+        SSL_CURVE_SECP256R1,
+        "P-256",
+        ssl_ec_point_cleanup,
+        ssl_ec_point_generate_keypair,
+        ssl_ec_point_compute_secret,
+    },
+    {
+        NID_secp384r1,
+        SSL_CURVE_SECP384R1,
+        "P-384",
+        ssl_ec_point_cleanup,
+        ssl_ec_point_generate_keypair,
+        ssl_ec_point_compute_secret,
+    },
+    {
+        NID_secp521r1,
+        SSL_CURVE_SECP521R1,
+        "P-521",
+        ssl_ec_point_cleanup,
+        ssl_ec_point_generate_keypair,
+        ssl_ec_point_compute_secret,
+    },
+    {
+        NID_x25519,
+        SSL_CURVE_ECDH_X25519,
+        "X25519",
+        ssl_x25519_cleanup,
+        ssl_x25519_generate_keypair,
+        ssl_x25519_compute_secret,
+    },
+};
+
+static const SSL_ECDH_METHOD *method_from_curve_id(uint16_t curve_id) {
+  size_t i;
+  for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); i++) {
+    if (kMethods[i].curve_id == curve_id) {
+      return &kMethods[i];
+    }
+  }
+  return NULL;
+}
+
+static const SSL_ECDH_METHOD *method_from_nid(int nid) {
+  size_t i;
+  for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); i++) {
+    if (kMethods[i].nid == nid) {
+      return &kMethods[i];
+    }
+  }
+  return NULL;
+}
+
+const char* SSL_get_curve_name(uint16_t curve_id) {
+  const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
+  if (method == NULL) {
+    return NULL;
+  }
+  return method->name;
+}
+
+int ssl_nid_to_curve_id(uint16_t *out_curve_id, int nid) {
+  const SSL_ECDH_METHOD *method = method_from_nid(nid);
+  if (method == NULL) {
+    return 0;
+  }
+  *out_curve_id = method->curve_id;
+  return 1;
+}
+
+int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t curve_id) {
+  SSL_ECDH_CTX_cleanup(ctx);
+
+  const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
+  if (method == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
+    return 0;
+  }
+  ctx->method = method;
+  return 1;
+}
+
+void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params) {
+  SSL_ECDH_CTX_cleanup(ctx);
+
+  ctx->method = &kDHEMethod;
+  ctx->data = params;
+}
+
+void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) {
+  if (ctx->method == NULL) {
+    return;
+  }
+  ctx->method->cleanup(ctx);
+  ctx->method = NULL;
+  ctx->data = NULL;
+}
+
+int SSL_ECDH_CTX_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out_public_key) {
+  return ctx->method->generate_keypair(ctx, out_public_key);
+}
+
+int SSL_ECDH_CTX_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+                                size_t *out_secret_len, uint8_t *out_alert,
+                                const uint8_t *peer_key, size_t peer_key_len) {
+  return ctx->method->compute_secret(ctx, out_secret, out_secret_len, out_alert,
+                                     peer_key, peer_key_len);
+}
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index c78a91a..08578a6 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -274,7 +274,7 @@
     goto err;
   }
 
-  CRYPTO_new_ex_data(&g_ex_data_class_ssl_ctx, ret, &ret->ex_data);
+  CRYPTO_new_ex_data(&ret->ex_data);
 
   ret->max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH;
 
@@ -285,10 +285,6 @@
     ret->options |= SSL_OP_NO_TICKET;
   }
 
-  /* Default is to connect to non-RI servers. When RI is more widely deployed
-   * might change this. */
-  ret->options |= SSL_OP_LEGACY_SERVER_CONNECT;
-
   /* Lock the SSL_CTX to the specified version, for compatibility with legacy
    * uses of SSL_METHOD. */
   if (method->version != 0) {
@@ -343,14 +339,11 @@
   OPENSSL_free(ctx->ocsp_response);
   OPENSSL_free(ctx->signed_cert_timestamp_list);
   EVP_PKEY_free(ctx->tlsext_channel_id_private);
-  BIO_free(ctx->keylog_bio);
 
   OPENSSL_free(ctx);
 }
 
 SSL *SSL_new(SSL_CTX *ctx) {
-  SSL *s;
-
   if (ctx == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_CTX);
     return NULL;
@@ -360,100 +353,101 @@
     return NULL;
   }
 
-  s = (SSL *)OPENSSL_malloc(sizeof(SSL));
-  if (s == NULL) {
+  SSL *ssl = (SSL *)OPENSSL_malloc(sizeof(SSL));
+  if (ssl == NULL) {
     goto err;
   }
-  memset(s, 0, sizeof(SSL));
+  memset(ssl, 0, sizeof(SSL));
 
-  s->min_version = ctx->min_version;
-  s->max_version = ctx->max_version;
+  ssl->min_version = ctx->min_version;
+  ssl->max_version = ctx->max_version;
 
-  s->options = ctx->options;
-  s->mode = ctx->mode;
-  s->max_cert_list = ctx->max_cert_list;
+  ssl->options = ctx->options;
+  ssl->mode = ctx->mode;
+  ssl->max_cert_list = ctx->max_cert_list;
 
-  s->cert = ssl_cert_dup(ctx->cert);
-  if (s->cert == NULL) {
+  ssl->cert = ssl_cert_dup(ctx->cert);
+  if (ssl->cert == NULL) {
     goto err;
   }
 
-  s->msg_callback = ctx->msg_callback;
-  s->msg_callback_arg = ctx->msg_callback_arg;
-  s->verify_mode = ctx->verify_mode;
-  s->sid_ctx_length = ctx->sid_ctx_length;
-  assert(s->sid_ctx_length <= sizeof s->sid_ctx);
-  memcpy(&s->sid_ctx, &ctx->sid_ctx, sizeof(s->sid_ctx));
-  s->verify_callback = ctx->default_verify_callback;
+  ssl->msg_callback = ctx->msg_callback;
+  ssl->msg_callback_arg = ctx->msg_callback_arg;
+  ssl->verify_mode = ctx->verify_mode;
+  ssl->sid_ctx_length = ctx->sid_ctx_length;
+  assert(ssl->sid_ctx_length <= sizeof ssl->sid_ctx);
+  memcpy(&ssl->sid_ctx, &ctx->sid_ctx, sizeof(ssl->sid_ctx));
+  ssl->verify_callback = ctx->default_verify_callback;
 
-  s->param = X509_VERIFY_PARAM_new();
-  if (!s->param) {
+  ssl->param = X509_VERIFY_PARAM_new();
+  if (!ssl->param) {
     goto err;
   }
-  X509_VERIFY_PARAM_inherit(s->param, ctx->param);
-  s->quiet_shutdown = ctx->quiet_shutdown;
-  s->max_send_fragment = ctx->max_send_fragment;
+  X509_VERIFY_PARAM_inherit(ssl->param, ctx->param);
+  ssl->quiet_shutdown = ctx->quiet_shutdown;
+  ssl->max_send_fragment = ctx->max_send_fragment;
 
   CRYPTO_refcount_inc(&ctx->references);
-  s->ctx = ctx;
+  ssl->ctx = ctx;
   CRYPTO_refcount_inc(&ctx->references);
-  s->initial_ctx = ctx;
+  ssl->initial_ctx = ctx;
 
   if (ctx->tlsext_ellipticcurvelist) {
-    s->tlsext_ellipticcurvelist =
+    ssl->tlsext_ellipticcurvelist =
         BUF_memdup(ctx->tlsext_ellipticcurvelist,
                    ctx->tlsext_ellipticcurvelist_length * 2);
-    if (!s->tlsext_ellipticcurvelist) {
+    if (!ssl->tlsext_ellipticcurvelist) {
       goto err;
     }
-    s->tlsext_ellipticcurvelist_length = ctx->tlsext_ellipticcurvelist_length;
+    ssl->tlsext_ellipticcurvelist_length = ctx->tlsext_ellipticcurvelist_length;
   }
 
-  if (s->ctx->alpn_client_proto_list) {
-    s->alpn_client_proto_list = BUF_memdup(s->ctx->alpn_client_proto_list,
-                                           s->ctx->alpn_client_proto_list_len);
-    if (s->alpn_client_proto_list == NULL) {
+  if (ssl->ctx->alpn_client_proto_list) {
+    ssl->alpn_client_proto_list = BUF_memdup(
+        ssl->ctx->alpn_client_proto_list, ssl->ctx->alpn_client_proto_list_len);
+    if (ssl->alpn_client_proto_list == NULL) {
       goto err;
     }
-    s->alpn_client_proto_list_len = s->ctx->alpn_client_proto_list_len;
+    ssl->alpn_client_proto_list_len = ssl->ctx->alpn_client_proto_list_len;
   }
 
-  s->verify_result = X509_V_OK;
-  s->method = ctx->method;
+  ssl->verify_result = X509_V_OK;
+  ssl->method = ctx->method;
 
-  if (!s->method->ssl_new(s)) {
+  if (!ssl->method->ssl_new(ssl)) {
     goto err;
   }
-  s->enc_method = ssl3_get_enc_method(s->version);
-  assert(s->enc_method != NULL);
+  ssl->enc_method = ssl3_get_enc_method(ssl->version);
+  assert(ssl->enc_method != NULL);
 
-  s->rwstate = SSL_NOTHING;
+  ssl->rwstate = SSL_NOTHING;
 
-  CRYPTO_new_ex_data(&g_ex_data_class_ssl, s, &s->ex_data);
+  CRYPTO_new_ex_data(&ssl->ex_data);
 
-  s->psk_identity_hint = NULL;
+  ssl->psk_identity_hint = NULL;
   if (ctx->psk_identity_hint) {
-    s->psk_identity_hint = BUF_strdup(ctx->psk_identity_hint);
-    if (s->psk_identity_hint == NULL) {
+    ssl->psk_identity_hint = BUF_strdup(ctx->psk_identity_hint);
+    if (ssl->psk_identity_hint == NULL) {
       goto err;
     }
   }
-  s->psk_client_callback = ctx->psk_client_callback;
-  s->psk_server_callback = ctx->psk_server_callback;
+  ssl->psk_client_callback = ctx->psk_client_callback;
+  ssl->psk_server_callback = ctx->psk_server_callback;
 
-  s->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled;
+  ssl->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled;
   if (ctx->tlsext_channel_id_private) {
-    s->tlsext_channel_id_private =
+    ssl->tlsext_channel_id_private =
         EVP_PKEY_up_ref(ctx->tlsext_channel_id_private);
   }
 
-  s->signed_cert_timestamps_enabled = s->ctx->signed_cert_timestamps_enabled;
-  s->ocsp_stapling_enabled = s->ctx->ocsp_stapling_enabled;
+  ssl->signed_cert_timestamps_enabled =
+      ssl->ctx->signed_cert_timestamps_enabled;
+  ssl->ocsp_stapling_enabled = ssl->ctx->ocsp_stapling_enabled;
 
-  return s;
+  return ssl;
 
 err:
-  SSL_free(s);
+  SSL_free(ssl);
   OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
 
   return NULL;
@@ -745,8 +739,8 @@
       /* This one doesn't make too much sense ... We never try to write to the
        * rbio, and an application program where rbio and wbio are separate
        * couldn't even know what it should wait for. However if we ever set
-       * s->rwstate incorrectly (so that we have SSL_want_read(s) instead of
-       * SSL_want_write(s)) and rbio and wbio *are* the same, this test works
+       * ssl->rwstate incorrectly (so that we have SSL_want_read(ssl) instead of
+       * SSL_want_write(ssl)) and rbio and wbio *are* the same, this test works
        * around that bug; so it might be safer to keep it. */
       return SSL_ERROR_WANT_WRITE;
     }
@@ -976,61 +970,6 @@
   OPENSSL_free(cipher_list);
 }
 
-struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_dup(
-    struct ssl_cipher_preference_list_st *cipher_list) {
-  struct ssl_cipher_preference_list_st *ret = NULL;
-  size_t n = sk_SSL_CIPHER_num(cipher_list->ciphers);
-
-  ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));
-  if (!ret) {
-    goto err;
-  }
-
-  ret->ciphers = NULL;
-  ret->in_group_flags = NULL;
-  ret->ciphers = sk_SSL_CIPHER_dup(cipher_list->ciphers);
-  if (!ret->ciphers) {
-    goto err;
-  }
-  ret->in_group_flags = BUF_memdup(cipher_list->in_group_flags, n);
-  if (!ret->in_group_flags) {
-    goto err;
-  }
-
-  return ret;
-
-err:
-  ssl_cipher_preference_list_free(ret);
-  return NULL;
-}
-
-struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers(
-    STACK_OF(SSL_CIPHER) *ciphers) {
-  struct ssl_cipher_preference_list_st *ret = NULL;
-  size_t n = sk_SSL_CIPHER_num(ciphers);
-
-  ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st));
-  if (!ret) {
-    goto err;
-  }
-  ret->ciphers = NULL;
-  ret->in_group_flags = NULL;
-  ret->ciphers = sk_SSL_CIPHER_dup(ciphers);
-  if (!ret->ciphers) {
-    goto err;
-  }
-  ret->in_group_flags = OPENSSL_malloc(n);
-  if (!ret->in_group_flags) {
-    goto err;
-  }
-  memset(ret->in_group_flags, 0, n);
-  return ret;
-
-err:
-  ssl_cipher_preference_list_free(ret);
-  return NULL;
-}
-
 X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) { return ctx->param; }
 
 X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) { return ssl->param; }
@@ -1169,11 +1108,11 @@
 
 int SSL_CTX_get_read_ahead(const SSL_CTX *ctx) { return 0; }
 
-int SSL_get_read_ahead(const SSL *s) { return 0; }
+int SSL_get_read_ahead(const SSL *ssl) { return 0; }
 
 void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { }
 
-void SSL_set_read_ahead(SSL *s, int yes) { }
+void SSL_set_read_ahead(SSL *ssl, int yes) { }
 
 int SSL_pending(const SSL *ssl) {
   if (ssl->s3->rrec.type != SSL3_RT_APPLICATION_DATA) {
@@ -1334,17 +1273,17 @@
 
 /* return a STACK of the ciphers available for the SSL and in order of
  * algorithm id */
-STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *s) {
-  if (s == NULL) {
+STACK_OF(SSL_CIPHER) *ssl_get_ciphers_by_id(SSL *ssl) {
+  if (ssl == NULL) {
     return NULL;
   }
 
-  if (s->cipher_list_by_id != NULL) {
-    return s->cipher_list_by_id;
+  if (ssl->cipher_list_by_id != NULL) {
+    return ssl->cipher_list_by_id;
   }
 
-  if (s->ctx != NULL && s->ctx->cipher_list_by_id != NULL) {
-    return s->ctx->cipher_list_by_id;
+  if (ssl->ctx != NULL && ssl->ctx->cipher_list_by_id != NULL) {
+    return ssl->ctx->cipher_list_by_id;
   }
 
   return NULL;
@@ -1435,13 +1374,13 @@
   return 1;
 }
 
-STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) {
+STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs) {
   CBS cipher_suites = *cbs;
   const SSL_CIPHER *c;
   STACK_OF(SSL_CIPHER) *sk;
 
-  if (s->s3) {
-    s->s3->send_connection_binding = 0;
+  if (ssl->s3) {
+    ssl->s3->send_connection_binding = 0;
   }
 
   if (CBS_len(&cipher_suites) % 2 != 0) {
@@ -1464,24 +1403,24 @@
     }
 
     /* Check for SCSV. */
-    if (s->s3 && cipher_suite == (SSL3_CK_SCSV & 0xffff)) {
+    if (ssl->s3 && cipher_suite == (SSL3_CK_SCSV & 0xffff)) {
       /* SCSV is fatal if renegotiating. */
-      if (s->s3->initial_handshake_complete) {
+      if (ssl->s3->initial_handshake_complete) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
-        ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         goto err;
       }
-      s->s3->send_connection_binding = 1;
+      ssl->s3->send_connection_binding = 1;
       continue;
     }
 
     /* Check for FALLBACK_SCSV. */
-    if (s->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff)) {
-      uint16_t max_version = ssl3_get_max_server_version(s);
-      if (SSL_IS_DTLS(s) ? (uint16_t)s->version > max_version
-                         : (uint16_t)s->version < max_version) {
+    if (ssl->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff)) {
+      uint16_t max_version = ssl3_get_max_server_version(ssl);
+      if (SSL_IS_DTLS(ssl) ? (uint16_t)ssl->version > max_version
+                         : (uint16_t)ssl->version < max_version) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
-        ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK);
         goto err;
       }
       continue;
@@ -1743,9 +1682,9 @@
   ssl_cert_set_cert_cb(ssl->cert, cb, arg);
 }
 
-void ssl_get_compatible_server_ciphers(SSL *s, uint32_t *out_mask_k,
+void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
                                        uint32_t *out_mask_a) {
-  CERT *c = s->cert;
+  CERT *c = ssl->cert;
   int have_rsa_cert = 0, dh_tmp;
   uint32_t mask_k, mask_a;
   int have_ecc_cert = 0, ecdsa_ok;
@@ -1753,10 +1692,10 @@
 
   dh_tmp = (c->dh_tmp != NULL || c->dh_tmp_cb != NULL);
 
-  if (s->cert->x509 != NULL && ssl_has_private_key(s)) {
-    if (ssl_private_key_type(s) == EVP_PKEY_RSA) {
+  if (ssl->cert->x509 != NULL && ssl_has_private_key(ssl)) {
+    if (ssl_private_key_type(ssl) == EVP_PKEY_RSA) {
       have_rsa_cert = 1;
-    } else if (ssl_private_key_type(s) == EVP_PKEY_EC) {
+    } else if (ssl_private_key_type(ssl) == EVP_PKEY_EC) {
       have_ecc_cert = 1;
     }
   }
@@ -1781,7 +1720,7 @@
     ecdsa_ok = (x->ex_flags & EXFLAG_KUSAGE)
                    ? (x->ex_kusage & X509v3_KU_DIGITAL_SIGNATURE)
                    : 1;
-    if (!tls1_check_ec_cert(s, x)) {
+    if (!tls1_check_ec_cert(ssl, x)) {
       ecdsa_ok = 0;
     }
     if (ecdsa_ok) {
@@ -1790,13 +1729,14 @@
   }
 
   /* If we are considering an ECC cipher suite that uses an ephemeral EC
-   * key, check it. */
-  if (tls1_check_ec_tmp_key(s)) {
+   * key, check for a shared curve. */
+  uint16_t unused;
+  if (tls1_get_shared_curve(ssl, &unused)) {
     mask_k |= SSL_kECDHE;
   }
 
   /* PSK requires a server callback. */
-  if (s->psk_server_callback != NULL) {
+  if (ssl->psk_server_callback != NULL) {
     mask_k |= SSL_kPSK;
     mask_a |= SSL_aPSK;
   }
@@ -1882,28 +1822,24 @@
   return ssl_get_version(session->ssl_version);
 }
 
-const char* SSL_get_curve_name(uint16_t curve_id) {
-  return tls1_ec_curve_id2name(curve_id);
+void ssl_clear_cipher_ctx(SSL *ssl) {
+  SSL_AEAD_CTX_free(ssl->aead_read_ctx);
+  ssl->aead_read_ctx = NULL;
+  SSL_AEAD_CTX_free(ssl->aead_write_ctx);
+  ssl->aead_write_ctx = NULL;
 }
 
-void ssl_clear_cipher_ctx(SSL *s) {
-  SSL_AEAD_CTX_free(s->aead_read_ctx);
-  s->aead_read_ctx = NULL;
-  SSL_AEAD_CTX_free(s->aead_write_ctx);
-  s->aead_write_ctx = NULL;
-}
-
-X509 *SSL_get_certificate(const SSL *s) {
-  if (s->cert != NULL) {
-    return s->cert->x509;
+X509 *SSL_get_certificate(const SSL *ssl) {
+  if (ssl->cert != NULL) {
+    return ssl->cert->x509;
   }
 
   return NULL;
 }
 
-EVP_PKEY *SSL_get_privatekey(const SSL *s) {
-  if (s->cert != NULL) {
-    return s->cert->privatekey;
+EVP_PKEY *SSL_get_privatekey(const SSL *ssl) {
+  if (ssl->cert != NULL) {
+    return ssl->cert->privatekey;
   }
 
   return NULL;
@@ -1932,23 +1868,23 @@
   return ssl->aead_write_ctx->cipher;
 }
 
-const COMP_METHOD *SSL_get_current_compression(SSL *s) { return NULL; }
+const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; }
 
-const COMP_METHOD *SSL_get_current_expansion(SSL *s) { return NULL; }
+const COMP_METHOD *SSL_get_current_expansion(SSL *ssl) { return NULL; }
 
-int ssl_init_wbio_buffer(SSL *s, int push) {
+int ssl_init_wbio_buffer(SSL *ssl, int push) {
   BIO *bbio;
 
-  if (s->bbio == NULL) {
+  if (ssl->bbio == NULL) {
     bbio = BIO_new(BIO_f_buffer());
     if (bbio == NULL) {
       return 0;
     }
-    s->bbio = bbio;
+    ssl->bbio = bbio;
   } else {
-    bbio = s->bbio;
-    if (s->bbio == s->wbio) {
-      s->wbio = BIO_pop(s->wbio);
+    bbio = ssl->bbio;
+    if (ssl->bbio == ssl->wbio) {
+      ssl->wbio = BIO_pop(ssl->wbio);
     }
   }
 
@@ -1959,30 +1895,30 @@
   }
 
   if (push) {
-    if (s->wbio != bbio) {
-      s->wbio = BIO_push(bbio, s->wbio);
+    if (ssl->wbio != bbio) {
+      ssl->wbio = BIO_push(bbio, ssl->wbio);
     }
   } else {
-    if (s->wbio == bbio) {
-      s->wbio = BIO_pop(bbio);
+    if (ssl->wbio == bbio) {
+      ssl->wbio = BIO_pop(bbio);
     }
   }
 
   return 1;
 }
 
-void ssl_free_wbio_buffer(SSL *s) {
-  if (s->bbio == NULL) {
+void ssl_free_wbio_buffer(SSL *ssl) {
+  if (ssl->bbio == NULL) {
     return;
   }
 
-  if (s->bbio == s->wbio) {
+  if (ssl->bbio == ssl->wbio) {
     /* remove buffering */
-    s->wbio = BIO_pop(s->wbio);
+    ssl->wbio = BIO_pop(ssl->wbio);
   }
 
-  BIO_free(s->bbio);
-  s->bbio = NULL;
+  BIO_free(ssl->bbio);
+  ssl->bbio = NULL;
 }
 
 void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) {
@@ -2065,11 +2001,11 @@
 
 long SSL_get_verify_result(const SSL *ssl) { return ssl->verify_result; }
 
-int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused,
                          CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) {
   int index;
   if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl, &index, argl, argp,
-                               new_func, dup_func, free_func)) {
+                               dup_func, free_func)) {
     return -1;
   }
   return index;
@@ -2083,12 +2019,12 @@
   return CRYPTO_get_ex_data(&ssl->ex_data, idx);
 }
 
-int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused,
                              CRYPTO_EX_dup *dup_func,
                              CRYPTO_EX_free *free_func) {
   int index;
   if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl_ctx, &index, argl, argp,
-                               new_func, dup_func, free_func)) {
+                               dup_func, free_func)) {
     return -1;
   }
   return index;
@@ -2133,18 +2069,6 @@
   ssl->cert->dh_tmp_cb = callback;
 }
 
-void SSL_CTX_set_tmp_ecdh_callback(SSL_CTX *ctx,
-                                   EC_KEY *(*callback)(SSL *ssl, int is_export,
-                                                       int keylength)) {
-  ctx->cert->ecdh_tmp_cb = callback;
-}
-
-void SSL_set_tmp_ecdh_callback(SSL *ssl,
-                               EC_KEY *(*callback)(SSL *ssl, int is_export,
-                                                   int keylength)) {
-  ssl->cert->ecdh_tmp_cb = callback;
-}
-
 int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) {
   if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
@@ -2252,9 +2176,9 @@
   ssl->msg_callback_arg = arg;
 }
 
-void SSL_CTX_set_keylog_bio(SSL_CTX *ctx, BIO *keylog_bio) {
-  BIO_free(ctx->keylog_bio);
-  ctx->keylog_bio = keylog_bio;
+void SSL_CTX_set_keylog_callback(SSL_CTX *ctx,
+                                 void (*cb)(const SSL *ssl, const char *line)) {
+  ctx->keylog_callback = cb;
 }
 
 static int cbb_add_hex(CBB *cbb, const uint8_t *in, size_t in_len) {
@@ -2274,18 +2198,12 @@
   return 1;
 }
 
-int ssl_ctx_log_rsa_client_key_exchange(SSL_CTX *ctx,
-                                        const uint8_t *encrypted_premaster,
-                                        size_t encrypted_premaster_len,
-                                        const uint8_t *premaster,
-                                        size_t premaster_len) {
-  BIO *bio = ctx->keylog_bio;
-  CBB cbb;
-  uint8_t *out;
-  size_t out_len;
-  int ret;
-
-  if (bio == NULL) {
+int ssl_log_rsa_client_key_exchange(const SSL *ssl,
+                                    const uint8_t *encrypted_premaster,
+                                    size_t encrypted_premaster_len,
+                                    const uint8_t *premaster,
+                                    size_t premaster_len) {
+  if (ssl->ctx->keylog_callback == NULL) {
     return 1;
   }
 
@@ -2294,7 +2212,9 @@
     return 0;
   }
 
-  CBB_zero(&cbb);
+  CBB cbb;
+  uint8_t *out;
+  size_t out_len;
   if (!CBB_init(&cbb, 4 + 16 + 1 + premaster_len * 2 + 1) ||
       !CBB_add_bytes(&cbb, (const uint8_t *)"RSA ", 4) ||
       /* Only the first 8 bytes of the encrypted premaster secret are
@@ -2302,30 +2222,21 @@
       !cbb_add_hex(&cbb, encrypted_premaster, 8) ||
       !CBB_add_bytes(&cbb, (const uint8_t *)" ", 1) ||
       !cbb_add_hex(&cbb, premaster, premaster_len) ||
-      !CBB_add_bytes(&cbb, (const uint8_t *)"\n", 1) ||
+      !CBB_add_u8(&cbb, 0 /* NUL */) ||
       !CBB_finish(&cbb, &out, &out_len)) {
     CBB_cleanup(&cbb);
     return 0;
   }
 
-  CRYPTO_MUTEX_lock_write(&ctx->lock);
-  ret = BIO_write(bio, out, out_len) >= 0 && BIO_flush(bio);
-  CRYPTO_MUTEX_unlock(&ctx->lock);
-
+  ssl->ctx->keylog_callback(ssl, (const char *)out);
   OPENSSL_free(out);
-  return ret;
+  return 1;
 }
 
-int ssl_ctx_log_master_secret(SSL_CTX *ctx, const uint8_t *client_random,
-                              size_t client_random_len, const uint8_t *master,
-                              size_t master_len) {
-  BIO *bio = ctx->keylog_bio;
-  CBB cbb;
-  uint8_t *out;
-  size_t out_len;
-  int ret;
-
-  if (bio == NULL) {
+int ssl_log_master_secret(const SSL *ssl, const uint8_t *client_random,
+                          size_t client_random_len, const uint8_t *master,
+                          size_t master_len) {
+  if (ssl->ctx->keylog_callback == NULL) {
     return 1;
   }
 
@@ -2334,24 +2245,23 @@
     return 0;
   }
 
-  CBB_zero(&cbb);
+  CBB cbb;
+  uint8_t *out;
+  size_t out_len;
   if (!CBB_init(&cbb, 14 + 64 + 1 + master_len * 2 + 1) ||
       !CBB_add_bytes(&cbb, (const uint8_t *)"CLIENT_RANDOM ", 14) ||
       !cbb_add_hex(&cbb, client_random, 32) ||
       !CBB_add_bytes(&cbb, (const uint8_t *)" ", 1) ||
       !cbb_add_hex(&cbb, master, master_len) ||
-      !CBB_add_bytes(&cbb, (const uint8_t *)"\n", 1) ||
+      !CBB_add_u8(&cbb, 0 /* NUL */) ||
       !CBB_finish(&cbb, &out, &out_len)) {
     CBB_cleanup(&cbb);
     return 0;
   }
 
-  CRYPTO_MUTEX_lock_write(&ctx->lock);
-  ret = BIO_write(bio, out, out_len) >= 0 && BIO_flush(bio);
-  CRYPTO_MUTEX_unlock(&ctx->lock);
-
+  ssl->ctx->keylog_callback(ssl, (const char *)out);
   OPENSSL_free(out);
-  return ret;
+  return 1;
 }
 
 int SSL_is_init_finished(const SSL *ssl) {
@@ -2366,8 +2276,8 @@
   return ssl->s3->tmp.in_false_start;
 }
 
-int SSL_cutthrough_complete(const SSL *s) {
-  return SSL_in_false_start(s);
+int SSL_cutthrough_complete(const SSL *ssl) {
+  return SSL_in_false_start(ssl);
 }
 
 void SSL_get_structure_sizes(size_t *ssl_size, size_t *ssl_ctx_size,
@@ -2377,18 +2287,16 @@
   *ssl_session_size = sizeof(SSL_SESSION);
 }
 
-int ssl3_can_false_start(const SSL *s) {
-  const SSL_CIPHER *const cipher = SSL_get_current_cipher(s);
+int ssl3_can_false_start(const SSL *ssl) {
+  const SSL_CIPHER *const cipher = SSL_get_current_cipher(ssl);
 
   /* False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN. */
-  return !SSL_IS_DTLS(s) &&
-      SSL_version(s) >= TLS1_2_VERSION &&
-      (s->s3->alpn_selected || s->s3->next_proto_neg_seen) &&
+  return !SSL_IS_DTLS(ssl) &&
+      SSL_version(ssl) >= TLS1_2_VERSION &&
+      (ssl->s3->alpn_selected || ssl->s3->next_proto_neg_seen) &&
       cipher != NULL &&
       cipher->algorithm_mkey == SSL_kECDHE &&
-      (cipher->algorithm_enc == SSL_AES128GCM ||
-       cipher->algorithm_enc == SSL_AES256GCM ||
-       cipher->algorithm_enc == SSL_CHACHA20POLY1305_OLD);
+      cipher->algorithm_mac == SSL_AEAD;
 }
 
 const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version) {
@@ -2412,84 +2320,89 @@
   }
 }
 
-uint16_t ssl3_get_max_server_version(const SSL *s) {
+uint16_t ssl3_get_max_server_version(const SSL *ssl) {
   uint16_t max_version;
 
-  if (SSL_IS_DTLS(s)) {
-    max_version = (s->max_version != 0) ? s->max_version : DTLS1_2_VERSION;
-    if (!(s->options & SSL_OP_NO_DTLSv1_2) && DTLS1_2_VERSION >= max_version) {
+  if (SSL_IS_DTLS(ssl)) {
+    max_version = (ssl->max_version != 0) ? ssl->max_version : DTLS1_2_VERSION;
+    if (!(ssl->options & SSL_OP_NO_DTLSv1_2) &&
+        DTLS1_2_VERSION >= max_version) {
       return DTLS1_2_VERSION;
     }
-    if (!(s->options & SSL_OP_NO_DTLSv1) && DTLS1_VERSION >= max_version) {
+    if (!(ssl->options & SSL_OP_NO_DTLSv1) && DTLS1_VERSION >= max_version) {
       return DTLS1_VERSION;
     }
     return 0;
   }
 
-  max_version = (s->max_version != 0) ? s->max_version : TLS1_2_VERSION;
-  if (!(s->options & SSL_OP_NO_TLSv1_2) && TLS1_2_VERSION <= max_version) {
+  max_version = (ssl->max_version != 0) ? ssl->max_version : TLS1_2_VERSION;
+  if (!(ssl->options & SSL_OP_NO_TLSv1_2) && TLS1_2_VERSION <= max_version) {
     return TLS1_2_VERSION;
   }
-  if (!(s->options & SSL_OP_NO_TLSv1_1) && TLS1_1_VERSION <= max_version) {
+  if (!(ssl->options & SSL_OP_NO_TLSv1_1) && TLS1_1_VERSION <= max_version) {
     return TLS1_1_VERSION;
   }
-  if (!(s->options & SSL_OP_NO_TLSv1) && TLS1_VERSION <= max_version) {
+  if (!(ssl->options & SSL_OP_NO_TLSv1) && TLS1_VERSION <= max_version) {
     return TLS1_VERSION;
   }
-  if (!(s->options & SSL_OP_NO_SSLv3) && SSL3_VERSION <= max_version) {
+  if (!(ssl->options & SSL_OP_NO_SSLv3) && SSL3_VERSION <= max_version) {
     return SSL3_VERSION;
   }
   return 0;
 }
 
-uint16_t ssl3_get_mutual_version(SSL *s, uint16_t client_version) {
+uint16_t ssl3_get_mutual_version(SSL *ssl, uint16_t client_version) {
   uint16_t version = 0;
 
-  if (SSL_IS_DTLS(s)) {
+  if (SSL_IS_DTLS(ssl)) {
     /* Clamp client_version to max_version. */
-    if (s->max_version != 0 && client_version < s->max_version) {
-      client_version = s->max_version;
+    if (ssl->max_version != 0 && client_version < ssl->max_version) {
+      client_version = ssl->max_version;
     }
 
-    if (client_version <= DTLS1_2_VERSION && !(s->options & SSL_OP_NO_DTLSv1_2)) {
+    if (client_version <= DTLS1_2_VERSION &&
+        !(ssl->options & SSL_OP_NO_DTLSv1_2)) {
       version = DTLS1_2_VERSION;
     } else if (client_version <= DTLS1_VERSION &&
-               !(s->options & SSL_OP_NO_DTLSv1)) {
+               !(ssl->options & SSL_OP_NO_DTLSv1)) {
       version = DTLS1_VERSION;
     }
 
     /* Check against min_version. */
-    if (version != 0 && s->min_version != 0 && version > s->min_version) {
+    if (version != 0 && ssl->min_version != 0 && version > ssl->min_version) {
       return 0;
     }
     return version;
   } else {
     /* Clamp client_version to max_version. */
-    if (s->max_version != 0 && client_version > s->max_version) {
-      client_version = s->max_version;
+    if (ssl->max_version != 0 && client_version > ssl->max_version) {
+      client_version = ssl->max_version;
     }
 
-    if (client_version >= TLS1_2_VERSION && !(s->options & SSL_OP_NO_TLSv1_2)) {
+    if (client_version >= TLS1_2_VERSION &&
+        !(ssl->options & SSL_OP_NO_TLSv1_2)) {
       version = TLS1_2_VERSION;
     } else if (client_version >= TLS1_1_VERSION &&
-             !(s->options & SSL_OP_NO_TLSv1_1)) {
+               !(ssl->options & SSL_OP_NO_TLSv1_1)) {
       version = TLS1_1_VERSION;
-    } else if (client_version >= TLS1_VERSION && !(s->options & SSL_OP_NO_TLSv1)) {
+    } else if (client_version >= TLS1_VERSION &&
+               !(ssl->options & SSL_OP_NO_TLSv1)) {
       version = TLS1_VERSION;
-    } else if (client_version >= SSL3_VERSION && !(s->options & SSL_OP_NO_SSLv3)) {
+    } else if (client_version >= SSL3_VERSION &&
+               !(ssl->options & SSL_OP_NO_SSLv3)) {
       version = SSL3_VERSION;
     }
 
     /* Check against min_version. */
-    if (version != 0 && s->min_version != 0 && version < s->min_version) {
+    if (version != 0 && ssl->min_version != 0 && version < ssl->min_version) {
       return 0;
     }
     return version;
   }
 }
 
-uint16_t ssl3_get_max_client_version(SSL *s) {
-  uint32_t options = s->options;
+uint16_t ssl3_get_max_client_version(SSL *ssl) {
+  uint32_t options = ssl->options;
   uint16_t version = 0;
 
   /* OpenSSL's API for controlling versions entails blacklisting individual
@@ -2505,15 +2418,15 @@
    *
    * By this scheme, the maximum version is the lowest version V such that V is
    * enabled and V+1 is disabled or unimplemented. */
-  if (SSL_IS_DTLS(s)) {
+  if (SSL_IS_DTLS(ssl)) {
     if (!(options & SSL_OP_NO_DTLSv1_2)) {
       version = DTLS1_2_VERSION;
     }
     if (!(options & SSL_OP_NO_DTLSv1) && (options & SSL_OP_NO_DTLSv1_2)) {
       version = DTLS1_VERSION;
     }
-    if (s->max_version != 0 && version < s->max_version) {
-      version = s->max_version;
+    if (ssl->max_version != 0 && version < ssl->max_version) {
+      version = ssl->max_version;
     }
   } else {
     if (!(options & SSL_OP_NO_TLSv1_2)) {
@@ -2528,53 +2441,53 @@
     if (!(options & SSL_OP_NO_SSLv3) && (options & SSL_OP_NO_TLSv1)) {
       version = SSL3_VERSION;
     }
-    if (s->max_version != 0 && version > s->max_version) {
-      version = s->max_version;
+    if (ssl->max_version != 0 && version > ssl->max_version) {
+      version = ssl->max_version;
     }
   }
 
   return version;
 }
 
-int ssl3_is_version_enabled(SSL *s, uint16_t version) {
-  if (SSL_IS_DTLS(s)) {
-    if (s->max_version != 0 && version < s->max_version) {
+int ssl3_is_version_enabled(SSL *ssl, uint16_t version) {
+  if (SSL_IS_DTLS(ssl)) {
+    if (ssl->max_version != 0 && version < ssl->max_version) {
       return 0;
     }
-    if (s->min_version != 0 && version > s->min_version) {
+    if (ssl->min_version != 0 && version > ssl->min_version) {
       return 0;
     }
 
     switch (version) {
       case DTLS1_VERSION:
-        return !(s->options & SSL_OP_NO_DTLSv1);
+        return !(ssl->options & SSL_OP_NO_DTLSv1);
 
       case DTLS1_2_VERSION:
-        return !(s->options & SSL_OP_NO_DTLSv1_2);
+        return !(ssl->options & SSL_OP_NO_DTLSv1_2);
 
       default:
         return 0;
     }
   } else {
-    if (s->max_version != 0 && version > s->max_version) {
+    if (ssl->max_version != 0 && version > ssl->max_version) {
       return 0;
     }
-    if (s->min_version != 0 && version < s->min_version) {
+    if (ssl->min_version != 0 && version < ssl->min_version) {
       return 0;
     }
 
     switch (version) {
       case SSL3_VERSION:
-        return !(s->options & SSL_OP_NO_SSLv3);
+        return !(ssl->options & SSL_OP_NO_SSLv3);
 
       case TLS1_VERSION:
-        return !(s->options & SSL_OP_NO_TLSv1);
+        return !(ssl->options & SSL_OP_NO_TLSv1);
 
       case TLS1_1_VERSION:
-        return !(s->options & SSL_OP_NO_TLSv1_1);
+        return !(ssl->options & SSL_OP_NO_TLSv1_1);
 
       case TLS1_2_VERSION:
-        return !(s->options & SSL_OP_NO_TLSv1_2);
+        return !(ssl->options & SSL_OP_NO_TLSv1_2);
 
       default:
         return 0;
@@ -2582,8 +2495,8 @@
   }
 }
 
-uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version) {
-  if (!SSL_IS_DTLS(s)) {
+uint16_t ssl3_version_from_wire(SSL *ssl, uint16_t wire_version) {
+  if (!SSL_IS_DTLS(ssl)) {
     return wire_version;
   }
 
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c
index 512a41f..990979b 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_rsa.c
@@ -56,6 +56,8 @@
 
 #include <openssl/ssl.h>
 
+#include <limits.h>
+
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
@@ -79,18 +81,22 @@
   return ssl_set_cert(ssl->cert, x);
 }
 
-int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *d, int len) {
-  X509 *x;
-  int ret;
-
-  x = d2i_X509(NULL, &d, (long)len);
-  if (x == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
+  if (der_len > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return 0;
   }
 
-  ret = SSL_use_certificate(ssl, x);
-  X509_free(x);
+  const uint8_t *p = der;
+  X509 *x509 = d2i_X509(NULL, &p, (long)der_len);
+  if (x509 == NULL || p != der + der_len) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+    X509_free(x509);
+    return 0;
+  }
+
+  int ret = SSL_use_certificate(ssl, x509);
+  X509_free(x509);
   return ret;
 }
 
@@ -165,19 +171,22 @@
   return ret;
 }
 
-int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *d, long len) {
-  int ret;
-  const uint8_t *p;
-  EVP_PKEY *pkey;
-
-  p = d;
-  pkey = d2i_PrivateKey(type, NULL, &p, (long)len);
-  if (pkey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *der,
+                            size_t der_len) {
+  if (der_len > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return 0;
   }
 
-  ret = SSL_use_PrivateKey(ssl, pkey);
+  const uint8_t *p = der;
+  EVP_PKEY *pkey = d2i_PrivateKey(type, NULL, &p, (long)der_len);
+  if (pkey == NULL || p != der + der_len) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+    EVP_PKEY_free(pkey);
+    return 0;
+  }
+
+  int ret = SSL_use_PrivateKey(ssl, pkey);
   EVP_PKEY_free(pkey);
   return ret;
 }
@@ -227,18 +236,23 @@
   return 1;
 }
 
-int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const uint8_t *d) {
-  X509 *x;
-  int ret;
-
-  x = d2i_X509(NULL, &d, (long)len);
-  if (x == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, size_t der_len,
+                                 const uint8_t *der) {
+  if (der_len > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return 0;
   }
 
-  ret = SSL_CTX_use_certificate(ctx, x);
-  X509_free(x);
+  const uint8_t *p = der;
+  X509 *x509 = d2i_X509(NULL, &p, (long)der_len);
+  if (x509 == NULL || p != der + der_len) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+    X509_free(x509);
+    return 0;
+  }
+
+  int ret = SSL_CTX_use_certificate(ctx, x509);
+  X509_free(x509);
   return ret;
 }
 
@@ -287,20 +301,22 @@
   return ssl_set_pkey(ctx->cert, pkey);
 }
 
-int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *d,
-                                long len) {
-  int ret;
-  const uint8_t *p;
-  EVP_PKEY *pkey;
-
-  p = d;
-  pkey = d2i_PrivateKey(type, NULL, &p, (long)len);
-  if (pkey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *der,
+                                size_t der_len) {
+  if (der_len > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
     return 0;
   }
 
-  ret = SSL_CTX_use_PrivateKey(ctx, pkey);
+  const uint8_t *p = der;
+  EVP_PKEY *pkey = d2i_PrivateKey(type, NULL, &p, (long)der_len);
+  if (pkey == NULL || p != der + der_len) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+    EVP_PKEY_free(pkey);
+    return 0;
+  }
+
+  int ret = SSL_CTX_use_PrivateKey(ctx, pkey);
   EVP_PKEY_free(pkey);
   return ret;
 }
@@ -385,20 +401,19 @@
                                           in_len);
   }
 
-  if (ssl_private_key_type(ssl) != EVP_PKEY_RSA) {
+  RSA *rsa = EVP_PKEY_get0_RSA(ssl->cert->privatekey);
+  if (rsa == NULL) {
     /* Decrypt operations are only supported for RSA keys. */
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return ssl_private_key_failure;
   }
 
-  enum ssl_private_key_result_t ret = ssl_private_key_failure;
-  RSA *rsa = ssl->cert->privatekey->pkey.rsa;
   /* Decrypt with no padding. PKCS#1 padding will be removed as part
    * of the timing-sensitive code by the caller. */
-  if (RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) {
-    ret = ssl_private_key_success;
+  if (!RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) {
+    return ssl_private_key_failure;
   }
-  return ret;
+  return ssl_private_key_success;
 }
 
 enum ssl_private_key_result_t ssl_private_key_decrypt_complete(
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index ead0b75..3d59bc3 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -172,7 +172,7 @@
   session->references = 1;
   session->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
   session->time = (unsigned long)time(NULL);
-  CRYPTO_new_ex_data(&g_ex_data_class, session, &session->ex_data);
+  CRYPTO_new_ex_data(&session->ex_data);
   return session;
 }
 
@@ -278,12 +278,13 @@
   return SSL_SESSION_up_ref(ssl->session);
 }
 
-int SSL_SESSION_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
+int SSL_SESSION_get_ex_new_index(long argl, void *argp,
+                                 CRYPTO_EX_unused *unused,
                                  CRYPTO_EX_dup *dup_func,
                                  CRYPTO_EX_free *free_func) {
   int index;
-  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, new_func,
-                               dup_func, free_func)) {
+  if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, dup_func,
+                               free_func)) {
     return -1;
   }
   return index;
@@ -644,10 +645,10 @@
   CRYPTO_MUTEX_unlock(&ctx->lock);
 }
 
-int ssl_clear_bad_session(SSL *s) {
-  if (s->session != NULL && !(s->shutdown & SSL_SENT_SHUTDOWN) &&
-      !SSL_in_init(s)) {
-    SSL_CTX_remove_session(s->ctx, s->session);
+int ssl_clear_bad_session(SSL *ssl) {
+  if (ssl->session != NULL && !(ssl->shutdown & SSL_SENT_SHUTDOWN) &&
+      !SSL_in_init(ssl)) {
+    SSL_CTX_remove_session(ssl->ctx, ssl->session);
     return 1;
   }
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 0cd42a2..9558f1c 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -50,7 +50,9 @@
     "ECDHE-RSA-AES128-GCM-SHA256";
 
 static const ExpectedCipher kExpected1[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
@@ -67,8 +69,10 @@
     "+aRSA";
 
 static const ExpectedCipher kExpected2[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
   { 0, 0 },
@@ -83,6 +87,7 @@
     "ECDHE-RSA-AES128-GCM-SHA256";
 
 static const ExpectedCipher kExpected3[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
   { 0, 0 },
@@ -119,7 +124,9 @@
     "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4";
 
 static const ExpectedCipher kExpected6[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
@@ -133,8 +140,10 @@
     "ECDHE-RSA-AES128-GCM-SHA256";
 
 static const ExpectedCipher kExpected7[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 1 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 1 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 1 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
   { 0, 0 },
@@ -157,6 +166,7 @@
 
 static const ExpectedCipher kExpected8[] = {
   { TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0 },
@@ -169,13 +179,55 @@
 // Exact ciphers may not be used in multi-part rules; they are treated
 // as unknown aliases.
 static const char kRule9[] =
+    "ECDHE-ECDSA-AES128-GCM-SHA256:"
+    "ECDHE-RSA-AES128-GCM-SHA256:"
+    "!ECDHE-RSA-AES128-GCM-SHA256+RSA:"
+    "!ECDSA+ECDHE-ECDSA-AES128-GCM-SHA256";
+
+static const ExpectedCipher kExpected9[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
+  { 0, 0 },
+};
+
+// SSLv3 matches everything that existed before TLS 1.2.
+static const char kRule10[] = "AES128-SHA:AES128-SHA256:!SSLv3";
+
+static const ExpectedCipher kExpected10[] = {
+  { TLS1_CK_RSA_WITH_AES_128_SHA256, 0 },
+  { 0, 0 },
+};
+
+// TLSv1.2 matches everything added in TLS 1.2.
+static const char kRule11[] = "AES128-SHA:AES128-SHA256:!TLSv1.2";
+
+static const ExpectedCipher kExpected11[] = {
+  { TLS1_CK_RSA_WITH_AES_128_SHA, 0 },
+  { 0, 0 },
+};
+
+// The two directives have no intersection.
+static const char kRule12[] = "AES128-SHA:AES128-SHA256:!TLSv1.2+SSLv3";
+
+static const ExpectedCipher kExpected12[] = {
+  { TLS1_CK_RSA_WITH_AES_128_SHA, 0 },
+  { TLS1_CK_RSA_WITH_AES_128_SHA256, 0 },
+  { 0, 0 },
+};
+
+// The shared name of the CHACHA20_POLY1305 variants behaves like a cipher name
+// and not an alias. It may not be used in a multipart rule. (That the shared
+// name works is covered by the standard tests.)
+static const char kRule13[] =
     "ECDHE-ECDSA-CHACHA20-POLY1305:"
     "ECDHE-RSA-CHACHA20-POLY1305:"
     "!ECDHE-RSA-CHACHA20-POLY1305+RSA:"
     "!ECDSA+ECDHE-ECDSA-CHACHA20-POLY1305";
 
-static const ExpectedCipher kExpected9[] = {
+static const ExpectedCipher kExpected13[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { 0, 0 },
 };
@@ -190,6 +242,10 @@
   { kRule7, kExpected7 },
   { kRule8, kExpected8 },
   { kRule9, kExpected9 },
+  { kRule10, kExpected10 },
+  { kRule11, kExpected11 },
+  { kRule12, kExpected12 },
+  { kRule13, kExpected13 },
   { NULL, NULL },
 };
 
@@ -222,6 +278,8 @@
   "DEFAULT",
   "ALL:!eNULL",
   "ALL:!NULL",
+  "MEDIUM",
+  "HIGH",
   "FIPS",
   "SHA",
   "SHA1",
@@ -521,7 +579,7 @@
   }
 
   out->resize(len);
-  if (!EVP_DecodeBase64(bssl::vector_data(out), &len, len, (const uint8_t *)in,
+  if (!EVP_DecodeBase64(out->data(), &len, len, (const uint8_t *)in,
                         strlen(in))) {
     fprintf(stderr, "EVP_DecodeBase64 failed\n");
     return false;
@@ -541,8 +599,7 @@
   }
 
   // Verify the SSL_SESSION decodes.
-  ScopedSSL_SESSION session(SSL_SESSION_from_bytes(bssl::vector_data(&input),
-                                                   input.size()));
+  ScopedSSL_SESSION session(SSL_SESSION_from_bytes(input.data(), input.size()));
   if (!session) {
     fprintf(stderr, "SSL_SESSION_from_bytes failed\n");
     return false;
@@ -558,7 +615,7 @@
   }
   encoded.reset(encoded_raw);
   if (encoded_len != input.size() ||
-      memcmp(bssl::vector_data(&input), encoded.get(), input.size()) != 0) {
+      memcmp(input.data(), encoded.get(), input.size()) != 0) {
     fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n");
     hexdump(stderr, "Before: ", input.data(), input.size());
     hexdump(stderr, "After:  ", encoded_raw, encoded_len);
@@ -566,9 +623,9 @@
   }
 
   // Verify the SSL_SESSION also decodes with the legacy API.
-  cptr = bssl::vector_data(&input);
+  cptr = input.data();
   session.reset(d2i_SSL_SESSION(NULL, &cptr, input.size()));
-  if (!session || cptr != bssl::vector_data(&input) + input.size()) {
+  if (!session || cptr != input.data() + input.size()) {
     fprintf(stderr, "d2i_SSL_SESSION failed\n");
     return false;
   }
@@ -596,7 +653,7 @@
     fprintf(stderr, "i2d_SSL_SESSION did not advance ptr correctly\n");
     return false;
   }
-  if (memcmp(bssl::vector_data(&input), encoded.get(), input.size()) != 0) {
+  if (memcmp(input.data(), encoded.get(), input.size()) != 0) {
     fprintf(stderr, "i2d_SSL_SESSION did not round-trip\n");
     return false;
   }
@@ -611,8 +668,7 @@
   }
 
   // Verify that the SSL_SESSION fails to decode.
-  ScopedSSL_SESSION session(SSL_SESSION_from_bytes(bssl::vector_data(&input),
-                                                   input.size()));
+  ScopedSSL_SESSION session(SSL_SESSION_from_bytes(input.data(), input.size()));
   if (session) {
     fprintf(stderr, "SSL_SESSION_from_bytes unexpectedly succeeded\n");
     return false;
@@ -668,6 +724,8 @@
   { TLS1_CK_PSK_WITH_RC4_128_SHA, "TLS_PSK_WITH_RC4_SHA" },
   { 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" },
   // These names are non-standard:
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD,
     "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" },
@@ -700,8 +758,7 @@
   if (!DecodeBase64(&der, kOpenSSLSession)) {
     return nullptr;
   }
-  ScopedSSL_SESSION session(SSL_SESSION_from_bytes(bssl::vector_data(&der),
-                                                   der.size()));
+  ScopedSSL_SESSION session(SSL_SESSION_from_bytes(der.data(), der.size()));
   if (!session) {
     return nullptr;
   }
diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c
index 076f8bd..0f1d683 100644
--- a/src/ssl/t1_enc.c
+++ b/src/ssl/t1_enc.c
@@ -159,40 +159,36 @@
                        const uint8_t *seed1, size_t seed1_len,
                        const uint8_t *seed2, size_t seed2_len,
                        const uint8_t *seed3, size_t seed3_len) {
-  size_t chunk;
   HMAC_CTX ctx, ctx_tmp, ctx_init;
   uint8_t A1[EVP_MAX_MD_SIZE];
   unsigned A1_len;
   int ret = 0;
 
-  chunk = EVP_MD_size(md);
+  size_t chunk = EVP_MD_size(md);
 
   HMAC_CTX_init(&ctx);
   HMAC_CTX_init(&ctx_tmp);
   HMAC_CTX_init(&ctx_init);
   if (!HMAC_Init_ex(&ctx_init, secret, secret_len, md, NULL) ||
       !HMAC_CTX_copy_ex(&ctx, &ctx_init) ||
-      (seed1_len && !HMAC_Update(&ctx, seed1, seed1_len)) ||
-      (seed2_len && !HMAC_Update(&ctx, seed2, seed2_len)) ||
-      (seed3_len && !HMAC_Update(&ctx, seed3, seed3_len)) ||
+      !HMAC_Update(&ctx, seed1, seed1_len) ||
+      !HMAC_Update(&ctx, seed2, seed2_len) ||
+      !HMAC_Update(&ctx, seed3, seed3_len) ||
       !HMAC_Final(&ctx, A1, &A1_len)) {
     goto err;
   }
 
   for (;;) {
-    /* Reinit mac contexts. */
-    if (!HMAC_CTX_copy_ex(&ctx, &ctx_init) ||
-        !HMAC_Update(&ctx, A1, A1_len) ||
-        (out_len > chunk && !HMAC_CTX_copy_ex(&ctx_tmp, &ctx)) ||
-        (seed1_len && !HMAC_Update(&ctx, seed1, seed1_len)) ||
-        (seed2_len && !HMAC_Update(&ctx, seed2, seed2_len)) ||
-        (seed3_len && !HMAC_Update(&ctx, seed3, seed3_len))) {
-      goto err;
-    }
-
     unsigned len;
     uint8_t hmac[EVP_MAX_MD_SIZE];
-    if (!HMAC_Final(&ctx, hmac, &len)) {
+    if (!HMAC_CTX_copy_ex(&ctx, &ctx_init) ||
+        !HMAC_Update(&ctx, A1, A1_len) ||
+        /* Save a copy of |ctx| to compute the next A1 value below. */
+        (out_len > chunk && !HMAC_CTX_copy_ex(&ctx_tmp, &ctx)) ||
+        !HMAC_Update(&ctx, seed1, seed1_len) ||
+        !HMAC_Update(&ctx, seed2, seed2_len) ||
+        !HMAC_Update(&ctx, seed3, seed3_len) ||
+        !HMAC_Final(&ctx, hmac, &len)) {
       goto err;
     }
     assert(len == chunk);
@@ -228,7 +224,7 @@
   return ret;
 }
 
-int tls1_prf(SSL *s, uint8_t *out, size_t out_len, const uint8_t *secret,
+int tls1_prf(SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret,
              size_t secret_len, const char *label, size_t label_len,
              const uint8_t *seed1, size_t seed1_len,
              const uint8_t *seed2, size_t seed2_len) {
@@ -239,7 +235,7 @@
 
   memset(out, 0, out_len);
 
-  uint32_t algorithm_prf = ssl_get_algorithm_prf(s);
+  uint32_t algorithm_prf = ssl_get_algorithm_prf(ssl);
   if (algorithm_prf == SSL_HANDSHAKE_MAC_DEFAULT) {
     /* If using the MD5/SHA1 PRF, |secret| is partitioned between SHA-1 and
      * MD5, MD5 first. */
@@ -264,17 +260,15 @@
   return 1;
 }
 
-static int tls1_generate_key_block(SSL *s, uint8_t *out, size_t out_len) {
-  return s->enc_method->prf(s, out, out_len, s->session->master_key,
-                            s->session->master_key_length,
-                            TLS_MD_KEY_EXPANSION_CONST,
-                            TLS_MD_KEY_EXPANSION_CONST_SIZE,
-                            s->s3->server_random, SSL3_RANDOM_SIZE,
-                            s->s3->client_random,
-                            SSL3_RANDOM_SIZE);
+static int tls1_generate_key_block(SSL *ssl, uint8_t *out, size_t out_len) {
+  return ssl->enc_method->prf(
+      ssl, out, out_len, ssl->session->master_key,
+      ssl->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 tls1_change_cipher_state(SSL *s, int which) {
+int tls1_change_cipher_state(SSL *ssl, int which) {
   /* is_read is true if we have just read a ChangeCipherSpec message - i.e. we
    * need to update the read cipherspec. Otherwise we have just written one. */
   const char is_read = (which & SSL3_CC_READ) != 0;
@@ -286,17 +280,28 @@
   const uint8_t *client_write_mac_secret, *server_write_mac_secret, *mac_secret;
   const uint8_t *client_write_key, *server_write_key, *key;
   const uint8_t *client_write_iv, *server_write_iv, *iv;
-  const EVP_AEAD *aead = s->s3->tmp.new_aead;
+  const EVP_AEAD *aead = ssl->s3->tmp.new_aead;
   size_t key_len, iv_len, mac_secret_len;
   const uint8_t *key_data;
 
   /* Reset sequence number to zero. */
-  if (!SSL_IS_DTLS(s)) {
-    memset(is_read ? s->s3->read_sequence : s->s3->write_sequence, 0, 8);
+  if (is_read) {
+    if (SSL_IS_DTLS(ssl)) {
+      ssl->d1->r_epoch++;
+      memset(&ssl->d1->bitmap, 0, sizeof(ssl->d1->bitmap));
+    }
+    memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence));
+  } else {
+    if (SSL_IS_DTLS(ssl)) {
+      ssl->d1->w_epoch++;
+      memcpy(ssl->d1->last_write_sequence, ssl->s3->write_sequence,
+             sizeof(ssl->s3->write_sequence));
+    }
+    memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence));
   }
 
-  mac_secret_len = s->s3->tmp.new_mac_secret_len;
-  iv_len = s->s3->tmp.new_fixed_iv_len;
+  mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
+  iv_len = ssl->s3->tmp.new_fixed_iv_len;
 
   if (aead == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -315,7 +320,7 @@
     key_len -= mac_secret_len + iv_len;
   }
 
-  key_data = s->s3->tmp.key_block;
+  key_data = ssl->s3->tmp.key_block;
   client_write_mac_secret = key_data;
   key_data += mac_secret_len;
   server_write_mac_secret = key_data;
@@ -339,58 +344,46 @@
     iv = server_write_iv;
   }
 
-  if (key_data - s->s3->tmp.key_block != s->s3->tmp.key_block_length) {
+  if (key_data - ssl->s3->tmp.key_block != ssl->s3->tmp.key_block_length) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
 
   if (is_read) {
-    SSL_AEAD_CTX_free(s->aead_read_ctx);
-    s->aead_read_ctx = SSL_AEAD_CTX_new(
-        evp_aead_open, ssl3_version_from_wire(s, s->version),
-        s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
+    SSL_AEAD_CTX_free(ssl->aead_read_ctx);
+    ssl->aead_read_ctx = SSL_AEAD_CTX_new(
+        evp_aead_open, ssl3_version_from_wire(ssl, ssl->version),
+        ssl->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
         iv_len);
-    return s->aead_read_ctx != NULL;
+    return ssl->aead_read_ctx != NULL;
   }
 
-  SSL_AEAD_CTX_free(s->aead_write_ctx);
-  s->aead_write_ctx = SSL_AEAD_CTX_new(
-      evp_aead_seal, ssl3_version_from_wire(s, s->version),
-      s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
+  SSL_AEAD_CTX_free(ssl->aead_write_ctx);
+  ssl->aead_write_ctx = SSL_AEAD_CTX_new(
+      evp_aead_seal, ssl3_version_from_wire(ssl, ssl->version),
+      ssl->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
       iv_len);
-  if (s->aead_write_ctx == NULL) {
-    return 0;
-  }
-
-  s->s3->need_record_splitting = 0;
-  if (!SSL_USE_EXPLICIT_IV(s) &&
-      (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
-      SSL_CIPHER_is_block_cipher(s->s3->tmp.new_cipher)) {
-    /* Enable 1/n-1 record-splitting to randomize the IV. See
-     * https://www.openssl.org/~bodo/tls-cbc.txt and the BEAST attack. */
-    s->s3->need_record_splitting = 1;
-  }
-  return 1;
+  return ssl->aead_write_ctx != NULL;
 }
 
-int tls1_setup_key_block(SSL *s) {
+int tls1_setup_key_block(SSL *ssl) {
   uint8_t *p;
   const EVP_AEAD *aead = NULL;
   int ret = 0;
   size_t mac_secret_len, fixed_iv_len, variable_iv_len, key_len;
   size_t key_block_len;
 
-  if (s->s3->tmp.key_block_length != 0) {
+  if (ssl->s3->tmp.key_block_length != 0) {
     return 1;
   }
 
-  if (s->session->cipher == NULL) {
+  if (ssl->session->cipher == NULL) {
     goto cipher_unavailable_err;
   }
 
   if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
-                               s->session->cipher,
-                               ssl3_version_from_wire(s, s->version))) {
+                               ssl->session->cipher,
+                               ssl3_version_from_wire(ssl, ssl->version))) {
     goto cipher_unavailable_err;
   }
   key_len = EVP_AEAD_key_length(aead);
@@ -417,15 +410,15 @@
   assert(fixed_iv_len < 256);
   assert(variable_iv_len < 256);
 
-  s->s3->tmp.new_aead = aead;
-  s->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
-  s->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
-  s->s3->tmp.new_variable_iv_len = (uint8_t)variable_iv_len;
+  ssl->s3->tmp.new_aead = aead;
+  ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len;
+  ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len;
+  ssl->s3->tmp.new_variable_iv_len = (uint8_t)variable_iv_len;
 
   key_block_len = key_len + mac_secret_len + fixed_iv_len;
   key_block_len *= 2;
 
-  ssl3_cleanup_key_block(s);
+  ssl3_cleanup_key_block(ssl);
 
   p = (uint8_t *)OPENSSL_malloc(key_block_len);
   if (p == NULL) {
@@ -433,10 +426,10 @@
     goto err;
   }
 
-  s->s3->tmp.key_block_length = key_block_len;
-  s->s3->tmp.key_block = p;
+  ssl->s3->tmp.key_block_length = key_block_len;
+  ssl->s3->tmp.key_block = p;
 
-  if (!tls1_generate_key_block(s, p, key_block_len)) {
+  if (!tls1_generate_key_block(ssl, p, key_block_len)) {
     goto err;
   }
 
@@ -450,12 +443,12 @@
   return 0;
 }
 
-int tls1_cert_verify_mac(SSL *s, int md_nid, uint8_t *out) {
+int tls1_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *out) {
   const EVP_MD_CTX *ctx_template;
   if (md_nid == NID_md5) {
-    ctx_template = &s->s3->handshake_md5;
-  } else if (md_nid == EVP_MD_CTX_type(&s->s3->handshake_hash)) {
-    ctx_template = &s->s3->handshake_hash;
+    ctx_template = &ssl->s3->handshake_md5;
+  } else if (md_nid == EVP_MD_CTX_type(&ssl->s3->handshake_hash)) {
+    ctx_template = &ssl->s3->handshake_hash;
   } else {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_REQUIRED_DIGEST);
     return 0;
@@ -503,15 +496,15 @@
  * written or -1 in the event of an error. This function works on a copy of the
  * underlying digests so can be called multiple times and prior to the final
  * update etc. */
-int tls1_handshake_digest(SSL *s, uint8_t *out, size_t out_len) {
+int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len) {
   size_t md5_len = 0;
-  if (EVP_MD_CTX_md(&s->s3->handshake_md5) != NULL &&
-      !append_digest(&s->s3->handshake_md5, out, &md5_len, out_len)) {
+  if (EVP_MD_CTX_md(&ssl->s3->handshake_md5) != NULL &&
+      !append_digest(&ssl->s3->handshake_md5, out, &md5_len, out_len)) {
     return -1;
   }
 
   size_t len;
-  if (!append_digest(&s->s3->handshake_hash, out + md5_len, &len,
+  if (!append_digest(&ssl->s3->handshake_hash, out + md5_len, &len,
                      out_len - md5_len)) {
     return -1;
   }
@@ -519,24 +512,24 @@
   return (int)(md5_len + len);
 }
 
-int tls1_final_finish_mac(SSL *s, const char *str, int slen, uint8_t *out) {
+int tls1_final_finish_mac(SSL *ssl, const char *str, int slen, uint8_t *out) {
   uint8_t buf[2 * EVP_MAX_MD_SIZE];
   int err = 0;
   int digests_len;
 
   /* At this point, the handshake should have released the handshake buffer on
    * its own. */
-  assert(s->s3->handshake_buffer == NULL);
+  assert(ssl->s3->handshake_buffer == NULL);
 
-  digests_len = tls1_handshake_digest(s, buf, sizeof(buf));
+  digests_len = tls1_handshake_digest(ssl, buf, sizeof(buf));
   if (digests_len < 0) {
     err = 1;
     digests_len = 0;
   }
 
-  if (!s->enc_method->prf(s, out, 12, s->session->master_key,
-                          s->session->master_key_length, str, slen, buf,
-                          digests_len, NULL, 0)) {
+  if (!ssl->enc_method->prf(ssl, out, 12, ssl->session->master_key,
+                            ssl->session->master_key_length, str, slen, buf,
+                            digests_len, NULL, 0)) {
     err = 1;
   }
 
@@ -547,27 +540,29 @@
   }
 }
 
-int tls1_generate_master_secret(SSL *s, uint8_t *out, const uint8_t *premaster,
+int tls1_generate_master_secret(SSL *ssl, uint8_t *out,
+                                const uint8_t *premaster,
                                 size_t premaster_len) {
-  if (s->s3->tmp.extended_master_secret) {
+  if (ssl->s3->tmp.extended_master_secret) {
     uint8_t digests[2 * EVP_MAX_MD_SIZE];
-    int digests_len = tls1_handshake_digest(s, digests, sizeof(digests));
+    int digests_len = tls1_handshake_digest(ssl, digests, sizeof(digests));
     if (digests_len == -1) {
       return 0;
     }
 
-    if (!s->enc_method->prf(s, out, SSL3_MASTER_SECRET_SIZE, premaster,
-                            premaster_len, TLS_MD_EXTENDED_MASTER_SECRET_CONST,
-                            TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE, digests,
-                            digests_len, NULL, 0)) {
+    if (!ssl->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
+                              premaster_len,
+                              TLS_MD_EXTENDED_MASTER_SECRET_CONST,
+                              TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE, digests,
+                              digests_len, NULL, 0)) {
       return 0;
     }
   } else {
-    if (!s->enc_method->prf(s, out, SSL3_MASTER_SECRET_SIZE, premaster,
-                            premaster_len, TLS_MD_MASTER_SECRET_CONST,
-                            TLS_MD_MASTER_SECRET_CONST_SIZE,
-                            s->s3->client_random, SSL3_RANDOM_SIZE,
-                            s->s3->server_random, SSL3_RANDOM_SIZE)) {
+    if (!ssl->enc_method->prf(ssl, out, SSL3_MASTER_SECRET_SIZE, premaster,
+                              premaster_len, TLS_MD_MASTER_SECRET_CONST,
+                              TLS_MD_MASTER_SECRET_CONST_SIZE,
+                              ssl->s3->client_random, SSL3_RANDOM_SIZE,
+                              ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
       return 0;
     }
   }
@@ -575,11 +570,11 @@
   return SSL3_MASTER_SECRET_SIZE;
 }
 
-int tls1_export_keying_material(SSL *s, uint8_t *out, size_t out_len,
+int tls1_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
                                 const char *label, size_t label_len,
                                 const uint8_t *context, size_t context_len,
                                 int use_context) {
-  if (!s->s3->have_version || s->version == SSL3_VERSION) {
+  if (!ssl->s3->have_version || ssl->version == SSL3_VERSION) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
@@ -598,17 +593,17 @@
     return 0;
   }
 
-  memcpy(seed, s->s3->client_random, SSL3_RANDOM_SIZE);
-  memcpy(seed + SSL3_RANDOM_SIZE, s->s3->server_random, SSL3_RANDOM_SIZE);
+  memcpy(seed, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+  memcpy(seed + SSL3_RANDOM_SIZE, ssl->s3->server_random, SSL3_RANDOM_SIZE);
   if (use_context) {
     seed[2 * SSL3_RANDOM_SIZE] = (uint8_t)(context_len >> 8);
     seed[2 * SSL3_RANDOM_SIZE + 1] = (uint8_t)context_len;
     memcpy(seed + 2 * SSL3_RANDOM_SIZE + 2, context, context_len);
   }
 
-  int ret = s->enc_method->prf(s, out, out_len, s->session->master_key,
-                               s->session->master_key_length, label, label_len,
-                               seed, seed_len, NULL, 0);
+  int ret = ssl->enc_method->prf(ssl, out, out_len, ssl->session->master_key,
+                                 ssl->session->master_key_length, label,
+                                 label_len, seed, seed_len, NULL, 0);
   OPENSSL_free(seed);
   return ret;
 }
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 9a29028..5aea08b 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -127,8 +127,8 @@
 #include "internal.h"
 
 
-static int ssl_check_clienthello_tlsext(SSL *s);
-static int ssl_check_serverhello_tlsext(SSL *s);
+static int ssl_check_clienthello_tlsext(SSL *ssl);
+static int ssl_check_serverhello_tlsext(SSL *ssl);
 
 const SSL3_ENC_METHOD TLSv1_enc_data = {
     tls1_prf,
@@ -335,126 +335,61 @@
   return 0;
 }
 
-struct tls_curve {
-  uint16_t curve_id;
-  int nid;
-  const char curve_name[8];
-};
-
-/* ECC curves from RFC4492. */
-static const struct tls_curve tls_curves[] = {
-    {21, NID_secp224r1, "P-224"},
-    {23, NID_X9_62_prime256v1, "P-256"},
-    {24, NID_secp384r1, "P-384"},
-    {25, NID_secp521r1, "P-521"},
-};
-
 static const uint16_t eccurves_default[] = {
-    23, /* X9_62_prime256v1 */
-    24, /* secp384r1 */
+    SSL_CURVE_SECP256R1,
+    SSL_CURVE_SECP384R1,
 #if defined(BORINGSSL_ANDROID_SYSTEM)
-    25, /* secp521r1 */
+    SSL_CURVE_SECP521R1,
 #endif
 };
 
-int tls1_ec_curve_id2nid(uint16_t curve_id) {
-  size_t i;
-  for (i = 0; i < sizeof(tls_curves) / sizeof(tls_curves[0]); i++) {
-    if (curve_id == tls_curves[i].curve_id) {
-      return tls_curves[i].nid;
-    }
-  }
-  return NID_undef;
-}
-
-int tls1_ec_nid2curve_id(uint16_t *out_curve_id, int nid) {
-  size_t i;
-  for (i = 0; i < sizeof(tls_curves) / sizeof(tls_curves[0]); i++) {
-    if (nid == tls_curves[i].nid) {
-      *out_curve_id = tls_curves[i].curve_id;
-      return 1;
-    }
-  }
-  return 0;
-}
-
-const char* tls1_ec_curve_id2name(uint16_t curve_id) {
-  size_t i;
-  for (i = 0; i < sizeof(tls_curves) / sizeof(tls_curves[0]); i++) {
-    if (curve_id == tls_curves[i].curve_id) {
-      return tls_curves[i].curve_name;
-    }
-  }
-  return NULL;
-}
-
 /* tls1_get_curvelist sets |*out_curve_ids| and |*out_curve_ids_len| to the
  * list of allowed curve IDs. If |get_peer_curves| is non-zero, return the
  * peer's curve list. Otherwise, return the preferred list. */
-static void tls1_get_curvelist(SSL *s, int get_peer_curves,
+static void tls1_get_curvelist(SSL *ssl, int get_peer_curves,
                                const uint16_t **out_curve_ids,
                                size_t *out_curve_ids_len) {
   if (get_peer_curves) {
     /* Only clients send a curve list, so this function is only called
      * on the server. */
-    assert(s->server);
-    *out_curve_ids = s->s3->tmp.peer_ellipticcurvelist;
-    *out_curve_ids_len = s->s3->tmp.peer_ellipticcurvelist_length;
+    assert(ssl->server);
+    *out_curve_ids = ssl->s3->tmp.peer_ellipticcurvelist;
+    *out_curve_ids_len = ssl->s3->tmp.peer_ellipticcurvelist_length;
     return;
   }
 
-  *out_curve_ids = s->tlsext_ellipticcurvelist;
-  *out_curve_ids_len = s->tlsext_ellipticcurvelist_length;
+  *out_curve_ids = ssl->tlsext_ellipticcurvelist;
+  *out_curve_ids_len = ssl->tlsext_ellipticcurvelist_length;
   if (!*out_curve_ids) {
     *out_curve_ids = eccurves_default;
     *out_curve_ids_len = sizeof(eccurves_default) / sizeof(eccurves_default[0]);
   }
 }
 
-int tls1_check_curve(SSL *s, CBS *cbs, uint16_t *out_curve_id) {
-  uint8_t curve_type;
-  uint16_t curve_id;
-  const uint16_t *curves;
-  size_t curves_len, i;
-
-  /* Only support named curves. */
-  if (!CBS_get_u8(cbs, &curve_type) ||
-      curve_type != NAMED_CURVE_TYPE ||
-      !CBS_get_u16(cbs, &curve_id)) {
-    return 0;
-  }
-
-  tls1_get_curvelist(s, 0, &curves, &curves_len);
-  for (i = 0; i < curves_len; i++) {
-    if (curve_id == curves[i]) {
-      *out_curve_id = curve_id;
-      return 1;
-    }
-  }
-
-  return 0;
-}
-
-int tls1_get_shared_curve(SSL *s) {
+int tls1_get_shared_curve(SSL *ssl, uint16_t *out_curve_id) {
   const uint16_t *curves, *peer_curves, *pref, *supp;
   size_t curves_len, peer_curves_len, pref_len, supp_len, i, j;
 
   /* Can't do anything on client side */
-  if (s->server == 0) {
-    return NID_undef;
+  if (ssl->server == 0) {
+    return 0;
   }
 
-  tls1_get_curvelist(s, 0 /* local curves */, &curves, &curves_len);
-  tls1_get_curvelist(s, 1 /* peer curves */, &peer_curves, &peer_curves_len);
+  tls1_get_curvelist(ssl, 0 /* local curves */, &curves, &curves_len);
+  tls1_get_curvelist(ssl, 1 /* peer curves */, &peer_curves, &peer_curves_len);
 
   if (peer_curves_len == 0) {
     /* Clients are not required to send a supported_curves extension. In this
      * case, the server is free to pick any curve it likes. See RFC 4492,
-     * section 4, paragraph 3. */
-    return (curves_len == 0) ? NID_undef : tls1_ec_curve_id2nid(curves[0]);
+     * section 4, paragraph 3.
+     *
+     * However, in the interests of compatibility, we will skip ECDH if the
+     * client didn't send an extension because we can't be sure that they'll
+     * support our favoured curve. */
+    return 0;
   }
 
-  if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
+  if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
     pref = curves;
     pref_len = curves_len;
     supp = peer_curves;
@@ -469,12 +404,13 @@
   for (i = 0; i < pref_len; i++) {
     for (j = 0; j < supp_len; j++) {
       if (pref[i] == supp[j]) {
-        return tls1_ec_curve_id2nid(pref[i]);
+        *out_curve_id = pref[i];
+        return 1;
       }
     }
   }
 
-  return NID_undef;
+  return 0;
 }
 
 int tls1_set_curves(uint16_t **out_curve_ids, size_t *out_curve_ids_len,
@@ -488,7 +424,7 @@
   }
 
   for (i = 0; i < ncurves; i++) {
-    if (!tls1_ec_nid2curve_id(&curve_ids[i], curves[i])) {
+    if (!ssl_nid_to_curve_id(&curve_ids[i], curves[i])) {
       OPENSSL_free(curve_ids);
       return 0;
     }
@@ -521,7 +457,7 @@
 
   /* Determine curve ID */
   nid = EC_GROUP_get_curve_name(grp);
-  if (!tls1_ec_nid2curve_id(&id, nid)) {
+  if (!ssl_nid_to_curve_id(&id, nid)) {
     return 0;
   }
 
@@ -545,19 +481,19 @@
 /* tls1_check_curve_id returns one if |curve_id| is consistent with both our
  * and the peer's curve preferences. Note: if called as the client, only our
  * preferences are checked; the peer (the server) does not send preferences. */
-static int tls1_check_curve_id(SSL *s, uint16_t curve_id) {
+int tls1_check_curve_id(SSL *ssl, uint16_t curve_id) {
   const uint16_t *curves;
   size_t curves_len, i, get_peer_curves;
 
   /* Check against our list, then the peer's list. */
   for (get_peer_curves = 0; get_peer_curves <= 1; get_peer_curves++) {
-    if (get_peer_curves && !s->server) {
+    if (get_peer_curves && !ssl->server) {
       /* Servers do not present a preference list so, if we are a client, only
        * check our list. */
       continue;
     }
 
-    tls1_get_curvelist(s, get_peer_curves, &curves, &curves_len);
+    tls1_get_curvelist(ssl, get_peer_curves, &curves, &curves_len);
     if (get_peer_curves && curves_len == 0) {
       /* Clients are not required to send a supported_curves extension. In this
        * case, the server is free to pick any curve it likes. See RFC 4492,
@@ -578,16 +514,19 @@
   return 1;
 }
 
-int tls1_check_ec_cert(SSL *s, X509 *x) {
+int tls1_check_ec_cert(SSL *ssl, X509 *x) {
   int ret = 0;
   EVP_PKEY *pkey = X509_get_pubkey(x);
   uint16_t curve_id;
   uint8_t comp_id;
 
-  if (!pkey ||
-      pkey->type != EVP_PKEY_EC ||
-      !tls1_curve_params_from_ec_key(&curve_id, &comp_id, pkey->pkey.ec) ||
-      !tls1_check_curve_id(s, curve_id) ||
+  if (!pkey) {
+    goto done;
+  }
+  EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
+  if (ec_key == NULL ||
+      !tls1_curve_params_from_ec_key(&curve_id, &comp_id, ec_key) ||
+      !tls1_check_curve_id(ssl, curve_id) ||
       comp_id != TLSEXT_ECPOINTFORMAT_uncompressed) {
     goto done;
   }
@@ -599,25 +538,6 @@
   return ret;
 }
 
-int tls1_check_ec_tmp_key(SSL *s) {
-  if (s->cert->ecdh_nid != NID_undef) {
-    /* If the curve is preconfigured, ECDH is acceptable iff the peer supports
-     * the curve. */
-    uint16_t curve_id;
-    return tls1_ec_nid2curve_id(&curve_id, s->cert->ecdh_nid) &&
-           tls1_check_curve_id(s, curve_id);
-  }
-
-  if (s->cert->ecdh_tmp_cb != NULL) {
-    /* Assume the callback will provide an acceptable curve. */
-    return 1;
-  }
-
-  /* Otherwise, the curve gets selected automatically. ECDH is acceptable iff
-   * there is a shared curve. */
-  return tls1_get_shared_curve(s) != NID_undef;
-}
-
 /* List of supported signature algorithms and hashes. Should make this
  * customisable at some point, for now include everything we support. */
 
@@ -635,7 +555,7 @@
     tlsext_sigalg(TLSEXT_hash_sha1)
 };
 
-size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs) {
+size_t tls12_get_psigalgs(SSL *ssl, const uint8_t **psigs) {
   *psigs = tls12_sigalgs;
   return sizeof(tls12_sigalgs);
 }
@@ -660,23 +580,6 @@
     return 0;
   }
 
-  if (pkey->type == EVP_PKEY_EC) {
-    uint16_t curve_id;
-    uint8_t comp_id;
-    /* Check compression and curve matches extensions */
-    if (!tls1_curve_params_from_ec_key(&curve_id, &comp_id, pkey->pkey.ec)) {
-      *out_alert = SSL_AD_INTERNAL_ERROR;
-      return 0;
-    }
-
-    if (ssl->server && (!tls1_check_curve_id(ssl, curve_id) ||
-                        comp_id != TLSEXT_ECPOINTFORMAT_uncompressed)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-      *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-      return 0;
-    }
-  }
-
   /* Check signature matches a type we sent */
   sent_sigslen = tls12_get_psigalgs(ssl, &sent_sigs);
   for (i = 0; i < sent_sigslen; i += 2, sent_sigs += 2) {
@@ -705,8 +608,8 @@
  * supported or doesn't appear in supported signature algorithms. Unlike
  * ssl_cipher_get_disabled this applies to a specific session and not global
  * settings. */
-void ssl_set_client_disabled(SSL *s) {
-  CERT *c = s->cert;
+void ssl_set_client_disabled(SSL *ssl) {
+  CERT *c = ssl->cert;
   const uint8_t *sigalgs;
   size_t i, sigalgslen;
   int have_rsa = 0, have_ecdsa = 0;
@@ -715,7 +618,7 @@
 
   /* Now go through all signature algorithms seeing if we support any for RSA,
    * DSA, ECDSA. Do this for all versions not just TLS 1.2. */
-  sigalgslen = tls12_get_psigalgs(s, &sigalgs);
+  sigalgslen = tls12_get_psigalgs(ssl, &sigalgs);
   for (i = 0; i < sigalgslen; i += 2, sigalgs += 2) {
     switch (sigalgs[1]) {
       case TLSEXT_signature_rsa:
@@ -737,7 +640,7 @@
   }
 
   /* with PSK there must be client callback set */
-  if (!s->psk_client_callback) {
+  if (!ssl->psk_client_callback) {
     c->mask_a |= SSL_aPSK;
     c->mask_k |= SSL_kPSK;
   }
@@ -798,7 +701,8 @@
   return 1;
 }
 
-static int ext_sni_parse_serverhello(SSL *ssl, uint8_t *out_alert, CBS *contents) {
+static int ext_sni_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+                                     CBS *contents) {
   if (contents == NULL) {
     return 1;
   }
@@ -821,7 +725,8 @@
   return 1;
 }
 
-static int ext_sni_parse_clienthello(SSL *ssl, uint8_t *out_alert, CBS *contents) {
+static int ext_sni_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                     CBS *contents) {
   if (contents == NULL) {
     return 1;
   }
@@ -943,23 +848,24 @@
 
 static int ext_ri_parse_serverhello(SSL *ssl, uint8_t *out_alert,
                                     CBS *contents) {
+  /* Servers may not switch between omitting the extension and supporting it.
+   * See RFC 5746, sections 3.5 and 4.2. */
+  if (ssl->s3->initial_handshake_complete &&
+      (contents != NULL) != ssl->s3->send_connection_binding) {
+    *out_alert = SSL_AD_HANDSHAKE_FAILURE;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
+    return 0;
+  }
+
   if (contents == NULL) {
-    /* No renegotiation extension received.
-     *
-     * Strictly speaking if we want to avoid an attack we should *always* see
+    /* Strictly speaking, if we want to avoid an attack we should *always* see
      * RI even on initial ServerHello because the client doesn't see any
      * renegotiation during an attack. However this would mean we could not
      * connect to any server which doesn't support RI.
      *
-     * A lack of the extension is allowed if SSL_OP_LEGACY_SERVER_CONNECT is
-     * defined. */
-    if (ssl->options & SSL_OP_LEGACY_SERVER_CONNECT) {
-      return 1;
-    }
-
-    *out_alert = SSL_AD_HANDSHAKE_FAILURE;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
-    return 0;
+     * OpenSSL has |SSL_OP_LEGACY_SERVER_CONNECT| to control this, but in
+     * practical terms every client sets it so it's just assumed here. */
+    return 1;
   }
 
   const size_t expected_len = ssl->s3->previous_client_finished_len +
@@ -1037,7 +943,8 @@
   }
 
   /* Check that the extension matches */
-  if (!CBS_mem_equal(&renegotiated_connection, ssl->s3->previous_client_finished,
+  if (!CBS_mem_equal(&renegotiated_connection,
+                     ssl->s3->previous_client_finished,
                      ssl->s3->previous_client_finished_len)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
     *out_alert = SSL_AD_HANDSHAKE_FAILURE;
@@ -1101,7 +1008,8 @@
   return 1;
 }
 
-static int ext_ems_parse_clienthello(SSL *ssl, uint8_t *out_alert, CBS *contents) {
+static int ext_ems_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                     CBS *contents) {
   if (ssl->version == SSL3_VERSION || contents == NULL) {
     return 1;
   }
@@ -1183,7 +1091,8 @@
   return 1;
 }
 
-static int ext_ticket_parse_clienthello(SSL *ssl, uint8_t *out_alert, CBS *contents) {
+static int ext_ticket_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                        CBS *contents) {
   /* This function isn't used because the ticket extension from the client is
    * handled in ssl_session.c. */
   return 1;
@@ -1661,7 +1570,8 @@
       !CBB_add_u16_length_prefixed(out, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &proto_list) ||
       !CBB_add_u8_length_prefixed(&proto_list, &proto) ||
-      !CBB_add_bytes(&proto, ssl->s3->alpn_selected, ssl->s3->alpn_selected_len) ||
+      !CBB_add_bytes(&proto, ssl->s3->alpn_selected,
+                     ssl->s3->alpn_selected_len) ||
       !CBB_flush(out)) {
     return 0;
   }
@@ -2217,7 +2127,6 @@
     return 1;
   }
 
-  size_t orig_len = CBB_len(out);
   CBB extensions;
   if (!CBB_add_u16_length_prefixed(out, &extensions)) {
     goto err;
@@ -2251,7 +2160,7 @@
   }
 
   if (!SSL_IS_DTLS(ssl)) {
-    header_len += CBB_len(&extensions) - orig_len;
+    header_len += 2 + CBB_len(&extensions);
     if (header_len > 0xff && header_len < 0x200) {
       /* Add padding to workaround bugs in F5 terminators. See RFC 7685.
        *
@@ -2278,10 +2187,8 @@
     }
   }
 
-  /* If only two bytes have been written then the extensions are actually empty
-   * and those two bytes are the zero length. In that case, we don't bother
-   * sending the extensions length. */
-  if (CBB_len(&extensions) - orig_len == 2) {
+  /* Discard empty extensions blocks. */
+  if (CBB_len(&extensions) == 0) {
     CBB_discard_child(out);
   }
 
@@ -2293,8 +2200,6 @@
 }
 
 int ssl_add_serverhello_tlsext(SSL *ssl, CBB *out) {
-  const size_t orig_len = CBB_len(out);
-
   CBB extensions;
   if (!CBB_add_u16_length_prefixed(out, &extensions)) {
     goto err;
@@ -2318,10 +2223,8 @@
     goto err;
   }
 
-  /* If only two bytes have been written then the extensions are actually empty
-   * and those two bytes are the zero length. In that case, we don't bother
-   * sending the extensions length. */
-  if (CBB_len(&extensions) - orig_len == 2) {
+  /* Discard empty extensions blocks. */
+  if (CBB_len(&extensions) == 0) {
     CBB_discard_child(out);
   }
 
@@ -2332,16 +2235,16 @@
   return 0;
 }
 
-static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) {
+static int ssl_scan_clienthello_tlsext(SSL *ssl, CBS *cbs, int *out_alert) {
   size_t i;
   for (i = 0; i < kNumExtensions; i++) {
     if (kExtensions[i].init != NULL) {
-      kExtensions[i].init(s);
+      kExtensions[i].init(ssl);
     }
   }
 
-  s->s3->tmp.extensions.received = 0;
-  s->s3->tmp.custom_extensions.received = 0;
+  ssl->s3->tmp.extensions.received = 0;
+  ssl->s3->tmp.custom_extensions.received = 0;
   /* The renegotiation extension must always be at index zero because the
    * |received| and |sent| bitsets need to be tweaked when the "extension" is
    * sent as an SCSV. */
@@ -2370,7 +2273,7 @@
 
       /* RFC 5746 made the existence of extensions in SSL 3.0 somewhat
        * ambiguous. Ignore all but the renegotiation_info extension. */
-      if (s->version == SSL3_VERSION && type != TLSEXT_TYPE_renegotiate) {
+      if (ssl->version == SSL3_VERSION && type != TLSEXT_TYPE_renegotiate) {
         continue;
       }
 
@@ -2379,16 +2282,16 @@
           tls_extension_find(&ext_index, type);
 
       if (ext == NULL) {
-        if (!custom_ext_parse_clienthello(s, out_alert, type, &extension)) {
+        if (!custom_ext_parse_clienthello(ssl, out_alert, type, &extension)) {
           OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
           return 0;
         }
         continue;
       }
 
-      s->s3->tmp.extensions.received |= (1u << ext_index);
+      ssl->s3->tmp.extensions.received |= (1u << ext_index);
       uint8_t alert = SSL_AD_DECODE_ERROR;
-      if (!ext->parse_clienthello(s, &alert, &extension)) {
+      if (!ext->parse_clienthello(ssl, &alert, &extension)) {
         *out_alert = alert;
         OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
         ERR_add_error_dataf("extension: %u", (unsigned)type);
@@ -2398,11 +2301,11 @@
   }
 
   for (i = 0; i < kNumExtensions; i++) {
-    if (!(s->s3->tmp.extensions.received & (1u << i))) {
+    if (!(ssl->s3->tmp.extensions.received & (1u << i))) {
       /* Extension wasn't observed so call the callback with a NULL
        * parameter. */
       uint8_t alert = SSL_AD_DECODE_ERROR;
-      if (!kExtensions[i].parse_clienthello(s, &alert, NULL)) {
+      if (!kExtensions[i].parse_clienthello(ssl, &alert, NULL)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
         ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
         *out_alert = alert;
@@ -2414,14 +2317,14 @@
   return 1;
 }
 
-int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs) {
+int ssl_parse_clienthello_tlsext(SSL *ssl, CBS *cbs) {
   int alert = -1;
-  if (ssl_scan_clienthello_tlsext(s, cbs, &alert) <= 0) {
-    ssl3_send_alert(s, SSL3_AL_FATAL, alert);
+  if (ssl_scan_clienthello_tlsext(ssl, cbs, &alert) <= 0) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
-  if (ssl_check_clienthello_tlsext(s) <= 0) {
+  if (ssl_check_clienthello_tlsext(ssl) <= 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_TLSEXT);
     return 0;
   }
@@ -2431,7 +2334,7 @@
 
 OPENSSL_COMPILE_ASSERT(kNumExtensions <= sizeof(uint32_t) * 8, too_many_bits);
 
-static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) {
+static int ssl_scan_serverhello_tlsext(SSL *ssl, CBS *cbs, int *out_alert) {
   uint32_t received = 0;
 
   if (CBS_len(cbs) != 0) {
@@ -2460,13 +2363,13 @@
           tls_extension_find(&ext_index, type);
 
       if (ext == NULL) {
-        if (!custom_ext_parse_serverhello(s, out_alert, type, &extension)) {
+        if (!custom_ext_parse_serverhello(ssl, out_alert, type, &extension)) {
           return 0;
         }
         continue;
       }
 
-      if (!(s->s3->tmp.extensions.sent & (1u << ext_index))) {
+      if (!(ssl->s3->tmp.extensions.sent & (1u << ext_index))) {
         /* If the extension was never sent then it is illegal. */
         OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
         ERR_add_error_dataf("extension :%u", (unsigned)type);
@@ -2477,7 +2380,7 @@
       received |= (1u << ext_index);
 
       uint8_t alert = SSL_AD_DECODE_ERROR;
-      if (!ext->parse_serverhello(s, &alert, &extension)) {
+      if (!ext->parse_serverhello(ssl, &alert, &extension)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
         ERR_add_error_dataf("extension: %u", (unsigned)type);
         *out_alert = alert;
@@ -2492,7 +2395,7 @@
       /* Extension wasn't observed so call the callback with a NULL
        * parameter. */
       uint8_t alert = SSL_AD_DECODE_ERROR;
-      if (!kExtensions[i].parse_serverhello(s, &alert, NULL)) {
+      if (!kExtensions[i].parse_serverhello(ssl, &alert, NULL)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
         ERR_add_error_dataf("extension: %u", (unsigned)kExtensions[i].value);
         *out_alert = alert;
@@ -2504,33 +2407,33 @@
   return 1;
 }
 
-static int ssl_check_clienthello_tlsext(SSL *s) {
+static int ssl_check_clienthello_tlsext(SSL *ssl) {
   int ret = SSL_TLSEXT_ERR_NOACK;
   int al = SSL_AD_UNRECOGNIZED_NAME;
 
   /* The handling of the ECPointFormats extension is done elsewhere, namely in
    * ssl3_choose_cipher in s3_lib.c. */
 
-  if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0) {
-    ret = s->ctx->tlsext_servername_callback(s, &al,
-                                             s->ctx->tlsext_servername_arg);
-  } else if (s->initial_ctx != NULL &&
-             s->initial_ctx->tlsext_servername_callback != 0) {
-    ret = s->initial_ctx->tlsext_servername_callback(
-        s, &al, s->initial_ctx->tlsext_servername_arg);
+  if (ssl->ctx != NULL && ssl->ctx->tlsext_servername_callback != 0) {
+    ret = ssl->ctx->tlsext_servername_callback(ssl, &al,
+                                             ssl->ctx->tlsext_servername_arg);
+  } else if (ssl->initial_ctx != NULL &&
+             ssl->initial_ctx->tlsext_servername_callback != 0) {
+    ret = ssl->initial_ctx->tlsext_servername_callback(
+        ssl, &al, ssl->initial_ctx->tlsext_servername_arg);
   }
 
   switch (ret) {
     case SSL_TLSEXT_ERR_ALERT_FATAL:
-      ssl3_send_alert(s, SSL3_AL_FATAL, al);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
       return -1;
 
     case SSL_TLSEXT_ERR_ALERT_WARNING:
-      ssl3_send_alert(s, SSL3_AL_WARNING, al);
+      ssl3_send_alert(ssl, SSL3_AL_WARNING, al);
       return 1;
 
     case SSL_TLSEXT_ERR_NOACK:
-      s->s3->tmp.should_ack_sni = 0;
+      ssl->s3->tmp.should_ack_sni = 0;
       return 1;
 
     default:
@@ -2538,26 +2441,26 @@
   }
 }
 
-static int ssl_check_serverhello_tlsext(SSL *s) {
+static int ssl_check_serverhello_tlsext(SSL *ssl) {
   int ret = SSL_TLSEXT_ERR_OK;
   int al = SSL_AD_UNRECOGNIZED_NAME;
 
-  if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0) {
-    ret = s->ctx->tlsext_servername_callback(s, &al,
-                                             s->ctx->tlsext_servername_arg);
-  } else if (s->initial_ctx != NULL &&
-             s->initial_ctx->tlsext_servername_callback != 0) {
-    ret = s->initial_ctx->tlsext_servername_callback(
-        s, &al, s->initial_ctx->tlsext_servername_arg);
+  if (ssl->ctx != NULL && ssl->ctx->tlsext_servername_callback != 0) {
+    ret = ssl->ctx->tlsext_servername_callback(ssl, &al,
+                                             ssl->ctx->tlsext_servername_arg);
+  } else if (ssl->initial_ctx != NULL &&
+             ssl->initial_ctx->tlsext_servername_callback != 0) {
+    ret = ssl->initial_ctx->tlsext_servername_callback(
+        ssl, &al, ssl->initial_ctx->tlsext_servername_arg);
   }
 
   switch (ret) {
     case SSL_TLSEXT_ERR_ALERT_FATAL:
-      ssl3_send_alert(s, SSL3_AL_FATAL, al);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
       return -1;
 
     case SSL_TLSEXT_ERR_ALERT_WARNING:
-      ssl3_send_alert(s, SSL3_AL_WARNING, al);
+      ssl3_send_alert(ssl, SSL3_AL_WARNING, al);
       return 1;
 
     default:
@@ -2565,14 +2468,14 @@
   }
 }
 
-int ssl_parse_serverhello_tlsext(SSL *s, CBS *cbs) {
+int ssl_parse_serverhello_tlsext(SSL *ssl, CBS *cbs) {
   int alert = -1;
-  if (ssl_scan_serverhello_tlsext(s, cbs, &alert) <= 0) {
-    ssl3_send_alert(s, SSL3_AL_FATAL, alert);
+  if (ssl_scan_serverhello_tlsext(ssl, cbs, &alert) <= 0) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
     return 0;
   }
 
-  if (ssl_check_serverhello_tlsext(s) <= 0) {
+  if (ssl_check_serverhello_tlsext(ssl) <= 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SERVERHELLO_TLSEXT);
     return 0;
   }
@@ -2616,9 +2519,9 @@
   const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN;
 
   if (ssl_ctx->tlsext_ticket_key_cb != NULL) {
-    int cb_ret = ssl_ctx->tlsext_ticket_key_cb(ssl, (uint8_t*)ticket /* name */,
-                                               (uint8_t*)iv, &cipher_ctx, &hmac_ctx,
-                                               0 /* decrypt */);
+    int cb_ret = ssl_ctx->tlsext_ticket_key_cb(
+        ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, &cipher_ctx,
+        &hmac_ctx, 0 /* decrypt */);
     if (cb_ret < 0) {
       ret = 0;
       goto done;
@@ -2732,27 +2635,15 @@
                        sizeof(tls12_sig) / sizeof(tls12_lookup));
 }
 
-int tls12_get_sigandhash(SSL *ssl, uint8_t *p, const EVP_MD *md) {
-  int sig_id, md_id;
+int tls12_add_sigandhash(SSL *ssl, CBB *out, const EVP_MD *md) {
+  int md_id = tls12_find_id(EVP_MD_type(md), tls12_md,
+                            sizeof(tls12_md) / sizeof(tls12_lookup));
+  int sig_id = tls12_get_sigid(ssl_private_key_type(ssl));
 
-  if (!md) {
-    return 0;
-  }
-
-  md_id = tls12_find_id(EVP_MD_type(md), tls12_md,
-                        sizeof(tls12_md) / sizeof(tls12_lookup));
-  if (md_id == -1) {
-    return 0;
-  }
-
-  sig_id = tls12_get_sigid(ssl_private_key_type(ssl));
-  if (sig_id == -1) {
-    return 0;
-  }
-
-  p[0] = (uint8_t)md_id;
-  p[1] = (uint8_t)sig_id;
-  return 1;
+  return md_id != -1 &&
+         sig_id != -1 &&
+         CBB_add_u8(out, (uint8_t)md_id) &&
+         CBB_add_u8(out, (uint8_t)sig_id);
 }
 
 const EVP_MD *tls12_get_hash(uint8_t hash_alg) {
@@ -2921,24 +2812,25 @@
 }
 
 /* tls1_record_handshake_hashes_for_channel_id records the current handshake
- * hashes in |s->session| so that Channel ID resumptions can sign that data. */
-int tls1_record_handshake_hashes_for_channel_id(SSL *s) {
+ * hashes in |ssl->session| so that Channel ID resumptions can sign that
+ * data. */
+int tls1_record_handshake_hashes_for_channel_id(SSL *ssl) {
   int digest_len;
   /* This function should never be called for a resumed session because the
    * handshake hashes that we wish to record are for the original, full
    * handshake. */
-  if (s->hit) {
+  if (ssl->hit) {
     return -1;
   }
 
   digest_len =
-      tls1_handshake_digest(s, s->session->original_handshake_hash,
-                            sizeof(s->session->original_handshake_hash));
+      tls1_handshake_digest(ssl, ssl->session->original_handshake_hash,
+                            sizeof(ssl->session->original_handshake_hash));
   if (digest_len < 0) {
     return -1;
   }
 
-  s->session->original_handshake_hash_len = digest_len;
+  ssl->session->original_handshake_hash_len = digest_len;
 
   return 1;
 }
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 07ba9f5..74674a4 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -12,6 +12,10 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
 #include <openssl/base.h>
 
 #if !defined(OPENSSL_WINDOWS)
@@ -20,7 +24,7 @@
 #include <netinet/tcp.h>
 #include <signal.h>
 #include <sys/socket.h>
-#include <sys/types.h>
+#include <sys/time.h>
 #include <unistd.h>
 #else
 #include <io.h>
@@ -32,8 +36,8 @@
 #pragma comment(lib, "Ws2_32.lib")
 #endif
 
+#include <inttypes.h>
 #include <string.h>
-#include <sys/types.h>
 
 #include <openssl/bio.h>
 #include <openssl/buf.h>
@@ -42,6 +46,7 @@
 #include <openssl/crypto.h>
 #include <openssl/err.h>
 #include <openssl/hmac.h>
+#include <openssl/obj.h>
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
 
@@ -120,9 +125,10 @@
   return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index);
 }
 
-static bool SetTestState(SSL *ssl, std::unique_ptr<TestState> async) {
-  if (SSL_set_ex_data(ssl, g_state_index, (void *)async.get()) == 1) {
-    async.release();
+static bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state) {
+  // |SSL_set_ex_data| takes ownership of |state| only on success.
+  if (SSL_set_ex_data(ssl, g_state_index, state.get()) == 1) {
+    state.release();
     return true;
   }
   return false;
@@ -172,8 +178,8 @@
     return ssl_private_key_failure;
   }
   test_state->private_key_result.resize(len);
-  if (!EVP_PKEY_sign(ctx.get(), bssl::vector_data(
-          &test_state->private_key_result), &len, in, in_len)) {
+  if (!EVP_PKEY_sign(ctx.get(), test_state->private_key_result.data(), &len, in,
+                     in_len)) {
     return ssl_private_key_failure;
   }
   test_state->private_key_result.resize(len);
@@ -202,7 +208,7 @@
     fprintf(stderr, "Output buffer too small.\n");
     return ssl_private_key_failure;
   }
-  memcpy(out, bssl::vector_data(&test_state->private_key_result),
+  memcpy(out, test_state->private_key_result.data(),
          test_state->private_key_result.size());
   *out_len = test_state->private_key_result.size();
 
@@ -221,16 +227,14 @@
     abort();
   }
 
-  EVP_PKEY *pkey = test_state->private_key.get();
-  if (pkey->type != EVP_PKEY_RSA || pkey->pkey.rsa == NULL) {
+  RSA *rsa = EVP_PKEY_get0_RSA(test_state->private_key.get());
+  if (rsa == NULL) {
     fprintf(stderr,
             "AsyncPrivateKeyDecrypt called with incorrect key type.\n");
     abort();
   }
-  RSA *rsa = pkey->pkey.rsa;
   test_state->private_key_result.resize(RSA_size(rsa));
-  if (!RSA_decrypt(rsa, out_len,
-                   bssl::vector_data(&test_state->private_key_result),
+  if (!RSA_decrypt(rsa, out_len, test_state->private_key_result.data(),
                    RSA_size(rsa), in, in_len, RSA_NO_PADDING)) {
     return ssl_private_key_failure;
   }
@@ -262,7 +266,7 @@
     fprintf(stderr, "Output buffer too small.\n");
     return ssl_private_key_failure;
   }
-  memcpy(out, bssl::vector_data(&test_state->private_key_result),
+  memcpy(out, test_state->private_key_result.data(),
          test_state->private_key_result.size());
   *out_len = test_state->private_key_result.size();
 
@@ -728,6 +732,24 @@
   }
 
   ScopedDH dh(DH_get_2048_256(NULL));
+
+  if (config->use_sparse_dh_prime) {
+    // This prime number is 2^1024 + 643 – a value just above a power of two.
+    // Because of its form, values modulo it are essentially certain to be one
+    // byte shorter. This is used to test padding of these values.
+    if (BN_hex2bn(
+            &dh->p,
+            "1000000000000000000000000000000000000000000000000000000000000000"
+            "0000000000000000000000000000000000000000000000000000000000000000"
+            "0000000000000000000000000000000000000000000000000000000000000000"
+            "0000000000000000000000000000000000000000000000000000000000000028"
+            "3") == 0 ||
+        !BN_set_word(dh->g, 2)) {
+      return nullptr;
+    }
+    dh->priv_length = 0;
+  }
+
   if (!dh || !SSL_CTX_set_tmp_dh(ssl_ctx.get(), dh.get())) {
     return nullptr;
   }
@@ -1070,6 +1092,15 @@
     return false;
   }
 
+  if (config->expect_key_exchange_info != 0) {
+    uint32_t info = SSL_SESSION_get_key_exchange_info(SSL_get_session(ssl));
+    if (static_cast<uint32_t>(config->expect_key_exchange_info) != info) {
+      fprintf(stderr, "key_exchange_info was %" PRIu32 ", wanted %" PRIu32 "\n",
+              info, static_cast<uint32_t>(config->expect_key_exchange_info));
+      return false;
+    }
+  }
+
   if (!config->is_server) {
     /* Clients should expect a peer certificate chain iff this was not a PSK
      * cipher suite. */
@@ -1143,15 +1174,6 @@
   if (config->no_ssl3) {
     SSL_set_options(ssl.get(), SSL_OP_NO_SSLv3);
   }
-  if (config->tls_d5_bug) {
-    SSL_set_options(ssl.get(), SSL_OP_TLS_D5_BUG);
-  }
-  if (config->microsoft_big_sslv3_buffer) {
-    SSL_set_options(ssl.get(), SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
-  }
-  if (config->no_legacy_server_connect) {
-    SSL_clear_options(ssl.get(), SSL_OP_LEGACY_SERVER_CONNECT);
-  }
   if (!config->expected_channel_id.empty()) {
     SSL_enable_tls_channel_id(ssl.get());
   }
@@ -1223,6 +1245,21 @@
   if (config->disable_npn) {
     SSL_set_options(ssl.get(), SSL_OP_DISABLE_NPN);
   }
+  if (config->p384_only) {
+    int nid = NID_secp384r1;
+    if (!SSL_set1_curves(ssl.get(), &nid, 1)) {
+      return false;
+    }
+  }
+  if (config->enable_all_curves) {
+    static const int kAllCurves[] = {
+        NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_x25519,
+    };
+    if (!SSL_set1_curves(ssl.get(), kAllCurves,
+                         sizeof(kAllCurves) / sizeof(kAllCurves[0]))) {
+      return false;
+    }
+  }
 
   int sock = Connect(config->port);
   if (sock == -1) {
diff --git a/src/ssl/test/packeted_bio.h b/src/ssl/test/packeted_bio.h
index 30697a5..75cfa13 100644
--- a/src/ssl/test/packeted_bio.h
+++ b/src/ssl/test/packeted_bio.h
@@ -25,7 +25,7 @@
 #include <winsock2.h>
 #pragma warning(pop)
 #else
-#include <sys/types.h>
+#include <sys/time.h>
 #endif
 
 
diff --git a/src/ssl/test/runner/chacha20_poly1305.go b/src/ssl/test/runner/chacha20_poly1305.go
index f2a1bbf..3c6ad82 100644
--- a/src/ssl/test/runner/chacha20_poly1305.go
+++ b/src/ssl/test/runner/chacha20_poly1305.go
@@ -5,11 +5,11 @@
 	"crypto/subtle"
 	"encoding/binary"
 	"errors"
+
+	"./poly1305"
 )
 
-// See draft-agl-tls-chacha20poly1305-04 and
-// draft-irtf-cfrg-chacha20-poly1305-10. Where the two differ, the
-// draft-agl-tls-chacha20poly1305-04 variant is implemented.
+// See RFC 7539.
 
 func leftRotate(a uint32, n uint) uint32 {
 	return (a << n) | (a >> (32 - n))
@@ -62,37 +62,30 @@
 	return
 }
 
-type chaCha20Poly1305 struct {
-	key [32]byte
-}
-
-func newChaCha20Poly1305(key []byte) (cipher.AEAD, error) {
-	if len(key) != 32 {
-		return nil, errors.New("bad key length")
-	}
-	aead := new(chaCha20Poly1305)
-	copy(aead.key[:], key)
-	return aead, nil
-}
-
-func (c *chaCha20Poly1305) NonceSize() int { return 8 }
-func (c *chaCha20Poly1305) Overhead() int  { return 16 }
-
-func (c *chaCha20Poly1305) chaCha20(out, in, nonce []byte, counter uint64) {
+func chaCha20(out, in, key, nonce []byte, counter uint64) {
 	var state [16]uint32
 	state[0] = 0x61707865
 	state[1] = 0x3320646e
 	state[2] = 0x79622d32
 	state[3] = 0x6b206574
 	for i := 0; i < 8; i++ {
-		state[4+i] = binary.LittleEndian.Uint32(c.key[i*4 : i*4+4])
+		state[4+i] = binary.LittleEndian.Uint32(key[i*4 : i*4+4])
 	}
-	state[14] = binary.LittleEndian.Uint32(nonce[0:4])
-	state[15] = binary.LittleEndian.Uint32(nonce[4:8])
+
+	switch len(nonce) {
+	case 8:
+		state[14] = binary.LittleEndian.Uint32(nonce[0:4])
+		state[15] = binary.LittleEndian.Uint32(nonce[4:8])
+	case 12:
+		state[13] = binary.LittleEndian.Uint32(nonce[0:4])
+		state[14] = binary.LittleEndian.Uint32(nonce[4:8])
+		state[15] = binary.LittleEndian.Uint32(nonce[8:12])
+	default:
+		panic("bad nonce length")
+	}
 
 	for i := 0; i < len(in); i += 64 {
-		state[12] = uint32(counter & 0xffffffff)
-		state[13] = uint32(counter >> 32)
+		state[12] = uint32(counter)
 
 		var tmp [64]byte
 		chaCha20Block(&state, tmp[:])
@@ -108,7 +101,68 @@
 	}
 }
 
+// chaCha20Poly1305 implements the AEAD from
+// RFC 7539 and draft-agl-tls-chacha20poly1305-04.
+type chaCha20Poly1305 struct {
+	key [32]byte
+	// oldMode, if true, indicates that the draft spec should be
+	// implemented rather than the final, RFC version.
+	oldMode bool
+}
+
+func newChaCha20Poly1305(key []byte) (cipher.AEAD, error) {
+	if len(key) != 32 {
+		return nil, errors.New("bad key length")
+	}
+	aead := new(chaCha20Poly1305)
+	copy(aead.key[:], key)
+	return aead, nil
+}
+
+func newChaCha20Poly1305Old(key []byte) (cipher.AEAD, error) {
+	if len(key) != 32 {
+		return nil, errors.New("bad key length")
+	}
+	aead := &chaCha20Poly1305{
+		oldMode: true,
+	}
+	copy(aead.key[:], key)
+	return aead, nil
+}
+
+func (c *chaCha20Poly1305) NonceSize() int {
+	if c.oldMode {
+		return 8
+	} else {
+		return 12
+	}
+}
+
+func (c *chaCha20Poly1305) Overhead() int { return 16 }
+
 func (c *chaCha20Poly1305) poly1305(tag *[16]byte, nonce, ciphertext, additionalData []byte) {
+	input := make([]byte, 0, len(additionalData)+15+len(ciphertext)+15+8+8)
+	input = append(input, additionalData...)
+	var zeros [15]byte
+	if pad := len(input) % 16; pad != 0 {
+		input = append(input, zeros[:16-pad]...)
+	}
+	input = append(input, ciphertext...)
+	if pad := len(input) % 16; pad != 0 {
+		input = append(input, zeros[:16-pad]...)
+	}
+	input, out := sliceForAppend(input, 8)
+	binary.LittleEndian.PutUint64(out, uint64(len(additionalData)))
+	input, out = sliceForAppend(input, 8)
+	binary.LittleEndian.PutUint64(out, uint64(len(ciphertext)))
+
+	var poly1305Key [32]byte
+	chaCha20(poly1305Key[:], poly1305Key[:], c.key[:], nonce, 0)
+
+	poly1305.Sum(tag, input, &poly1305Key)
+}
+
+func (c *chaCha20Poly1305) poly1305Old(tag *[16]byte, nonce, ciphertext, additionalData []byte) {
 	input := make([]byte, 0, len(additionalData)+8+len(ciphertext)+8)
 	input = append(input, additionalData...)
 	input, out := sliceForAppend(input, 8)
@@ -118,28 +172,32 @@
 	binary.LittleEndian.PutUint64(out, uint64(len(ciphertext)))
 
 	var poly1305Key [32]byte
-	c.chaCha20(poly1305Key[:], poly1305Key[:], nonce, 0)
+	chaCha20(poly1305Key[:], poly1305Key[:], c.key[:], nonce, 0)
 
-	poly1305Sum(tag, input, &poly1305Key)
+	poly1305.Sum(tag, input, &poly1305Key)
 }
 
 func (c *chaCha20Poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
-	if len(nonce) != 8 {
+	if len(nonce) != c.NonceSize() {
 		panic("Bad nonce length")
 	}
 
 	ret, out := sliceForAppend(dst, len(plaintext)+16)
-	c.chaCha20(out[:len(plaintext)], plaintext, nonce, 1)
+	chaCha20(out[:len(plaintext)], plaintext, c.key[:], nonce, 1)
 
 	var tag [16]byte
-	c.poly1305(&tag, nonce, out[:len(plaintext)], additionalData)
+	if c.oldMode {
+		c.poly1305Old(&tag, nonce, out[:len(plaintext)], additionalData)
+	} else {
+		c.poly1305(&tag, nonce, out[:len(plaintext)], additionalData)
+	}
 	copy(out[len(plaintext):], tag[:])
 
 	return ret
 }
 
 func (c *chaCha20Poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
-	if len(nonce) != 8 {
+	if len(nonce) != c.NonceSize() {
 		panic("Bad nonce length")
 	}
 	if len(ciphertext) < 16 {
@@ -148,12 +206,16 @@
 	plaintextLen := len(ciphertext) - 16
 
 	var tag [16]byte
-	c.poly1305(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+	if c.oldMode {
+		c.poly1305Old(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+	} else {
+		c.poly1305(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+	}
 	if subtle.ConstantTimeCompare(tag[:], ciphertext[plaintextLen:]) != 1 {
 		return nil, errors.New("chacha20: message authentication failed")
 	}
 
 	ret, out := sliceForAppend(dst, plaintextLen)
-	c.chaCha20(out, ciphertext[:plaintextLen], nonce, 1)
+	chaCha20(out, ciphertext[:plaintextLen], c.key[:], nonce, 1)
 	return ret, nil
 }
diff --git a/src/ssl/test/runner/chacha20_poly1305_test.go b/src/ssl/test/runner/chacha20_poly1305_test.go
index be49b11..4d19b8c 100644
--- a/src/ssl/test/runner/chacha20_poly1305_test.go
+++ b/src/ssl/test/runner/chacha20_poly1305_test.go
@@ -6,7 +6,7 @@
 	"testing"
 )
 
-// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.1.1.
+// See RFC 7539, section 2.1.1.
 func TestChaChaQuarterRound(t *testing.T) {
 	state := [16]uint32{0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567}
 	chaChaQuarterRound(&state, 0, 1, 2, 3)
@@ -17,7 +17,7 @@
 	}
 }
 
-// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.2.1.
+// See RFC 7539, section 2.2.1.
 func TestChaChaQuarterRoundState(t *testing.T) {
 	state := [16]uint32{
 		0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a,
@@ -40,7 +40,7 @@
 	}
 }
 
-// See draft-irtf-cfrg-chacha20-poly1305-10, section 2.3.2.
+// See RFC 7539, section 2.3.2.
 func TestChaCha20Block(t *testing.T) {
 	state := [16]uint32{
 		0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
@@ -66,15 +66,23 @@
 	}
 }
 
-// See draft-agl-tls-chacha20poly1305-04, section 7.
-func TestChaCha20Poly1305(t *testing.T) {
-	key, _ := hex.DecodeString("4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007")
-	input, _ := hex.DecodeString("86d09974840bded2a5ca")
-	nonce, _ := hex.DecodeString("cd7cf67be39c794a")
-	ad, _ := hex.DecodeString("87e229d4500845a079c0")
-	output, _ := hex.DecodeString("e3e446f7ede9a19b62a4677dabf4e3d24b876bb284753896e1d6")
+func decodeHexOrPanic(in string) []byte {
+	out, err := hex.DecodeString(in)
+	if err != nil {
+		panic(err)
+	}
+	return out
+}
 
-	aead, err := newChaCha20Poly1305(key)
+// See draft-agl-tls-chacha20poly1305-04, section 7.
+func TestChaCha20Poly1305Old(t *testing.T) {
+	key := decodeHexOrPanic("4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007")
+	input := decodeHexOrPanic("86d09974840bded2a5ca")
+	nonce := decodeHexOrPanic("cd7cf67be39c794a")
+	ad := decodeHexOrPanic("87e229d4500845a079c0")
+	output := decodeHexOrPanic("e3e446f7ede9a19b62a4677dabf4e3d24b876bb284753896e1d6")
+
+	aead, err := newChaCha20Poly1305Old(key)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -97,3 +105,58 @@
 		t.Errorf("Open on malformed data unexpectedly succeeded")
 	}
 }
+
+var chaCha20Poly1305TestVectors = []struct {
+	key, input, nonce, ad, output string
+}{
+	{
+		// See RFC 7539, section 2.8.2.
+		key:    "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
+		input:  "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e",
+		nonce:  "070000004041424344454647",
+		ad:     "50515253c0c1c2c3c4c5c6c7",
+		output: "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691",
+	},
+	{
+		// See RFC 7539, section A.5.
+		key:    "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0",
+		input:  "496e7465726e65742d4472616674732061726520647261667420646f63756d656e74732076616c696420666f722061206d6178696d756d206f6620736978206d6f6e74687320616e64206d617920626520757064617465642c207265706c616365642c206f72206f62736f6c65746564206279206f7468657220646f63756d656e747320617420616e792074696d652e20497420697320696e617070726f70726961746520746f2075736520496e7465726e65742d447261667473206173207265666572656e6365206d6174657269616c206f7220746f2063697465207468656d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67726573732e2fe2809d",
+		nonce:  "000000000102030405060708",
+		ad:     "f33388860000000000004e91",
+		output: "64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb24c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c8559797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523eaf4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a1049e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29a6ad5cb4022b02709beead9d67890cbb22392336fea1851f38",
+	},
+}
+
+// See draft-agl-tls-chacha20poly1305-04, section 7.
+func TestChaCha20Poly1305(t *testing.T) {
+	for i, tt := range chaCha20Poly1305TestVectors {
+		key := decodeHexOrPanic(tt.key)
+		input := decodeHexOrPanic(tt.input)
+		nonce := decodeHexOrPanic(tt.nonce)
+		ad := decodeHexOrPanic(tt.ad)
+		output := decodeHexOrPanic(tt.output)
+
+		aead, err := newChaCha20Poly1305(key)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		out, err := aead.Open(nil, nonce, output, ad)
+		if err != nil {
+			t.Errorf("%d. Open failed: %s", i, err)
+		} else if !bytes.Equal(out, input) {
+			t.Errorf("%d. Open gave %x, wanted %x", i, out, input)
+		}
+
+		out = aead.Seal(nil, nonce, input, ad)
+		if !bytes.Equal(out, output) {
+			t.Errorf("%d. Open gave %x, wanted %x", i, out, output)
+		}
+
+		out[0]++
+		_, err = aead.Open(nil, nonce, out, ad)
+		if err == nil {
+			t.Errorf("%d. Open on malformed data unexpectedly succeeded", i)
+		}
+	}
+}
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index c406000..bfd31a5 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -86,8 +86,10 @@
 var cipherSuites = []*cipherSuite{
 	// Ciphersuite order is chosen so that ECDHE comes before plain RSA
 	// and RC4 comes before AES (because of the Lucky13 attack).
-	{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
-	{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 0, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD, 32, 0, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305Old},
+	{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD, 32, 0, 0, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305Old},
 	{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
@@ -119,11 +121,12 @@
 	{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
 	{TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, dheRSAKA, 0, cipher3DES, macSHA1, nil},
 	{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
+	{TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdhePSKKA, suiteECDHE | suitePSK | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
+	{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
 	{TLS_PSK_WITH_RC4_128_SHA, 16, 20, 0, pskKA, suiteNoDTLS | suitePSK, cipherRC4, macSHA1, nil},
 	{TLS_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil},
 	{TLS_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil},
-	{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
-	{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
 	{TLS_RSA_WITH_NULL_SHA, 0, 20, 0, rsaKA, suiteNoDTLS, cipherNull, macSHA1, nil},
 }
 
@@ -248,12 +251,58 @@
 	return &tlsAead{&fixedNonceAEAD{nonce1, nonce2, aead}, true}
 }
 
+func aeadCHACHA20POLY1305Old(key, fixedNonce []byte) *tlsAead {
+	aead, err := newChaCha20Poly1305Old(key)
+	if err != nil {
+		panic(err)
+	}
+	return &tlsAead{aead, false}
+}
+
+func xorSlice(out, in []byte) {
+	for i := range out {
+		out[i] ^= in[i]
+	}
+}
+
+// xorNonceAEAD wraps an AEAD and XORs a fixed portion of the nonce, left-padded
+// if necessary, each call.
+type xorNonceAEAD struct {
+	// sealNonce and openNonce are buffers where the larger nonce will be
+	// constructed. Since a seal and open operation may be running
+	// concurrently, there is a separate buffer for each.
+	sealNonce, openNonce []byte
+	aead                 cipher.AEAD
+}
+
+func (x *xorNonceAEAD) NonceSize() int { return 8 }
+func (x *xorNonceAEAD) Overhead() int  { return x.aead.Overhead() }
+
+func (x *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+	xorSlice(x.sealNonce[len(x.sealNonce)-len(nonce):], nonce)
+	ret := x.aead.Seal(out, x.sealNonce, plaintext, additionalData)
+	xorSlice(x.sealNonce[len(x.sealNonce)-len(nonce):], nonce)
+	return ret
+}
+
+func (x *xorNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
+	xorSlice(x.openNonce[len(x.openNonce)-len(nonce):], nonce)
+	ret, err := x.aead.Open(out, x.openNonce, plaintext, additionalData)
+	xorSlice(x.openNonce[len(x.openNonce)-len(nonce):], nonce)
+	return ret, err
+}
+
 func aeadCHACHA20POLY1305(key, fixedNonce []byte) *tlsAead {
 	aead, err := newChaCha20Poly1305(key)
 	if err != nil {
 		panic(err)
 	}
-	return &tlsAead{aead, false}
+
+	nonce1, nonce2 := make([]byte, len(fixedNonce)), make([]byte, len(fixedNonce))
+	copy(nonce1, fixedNonce)
+	copy(nonce2, fixedNonce)
+
+	return &tlsAead{&xorNonceAEAD{nonce1, nonce2, aead}, false}
 }
 
 // ssl30MAC implements the SSLv3 MAC function, as defined in
@@ -375,49 +424,52 @@
 // A list of the possible cipher suite ids. Taken from
 // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
 const (
-	TLS_RSA_WITH_NULL_SHA                   uint16 = 0x0002
-	TLS_RSA_WITH_RC4_128_MD5                uint16 = 0x0004
-	TLS_RSA_WITH_RC4_128_SHA                uint16 = 0x0005
-	TLS_RSA_WITH_3DES_EDE_CBC_SHA           uint16 = 0x000a
-	TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA       uint16 = 0x0016
-	TLS_RSA_WITH_AES_128_CBC_SHA            uint16 = 0x002f
-	TLS_DHE_RSA_WITH_AES_128_CBC_SHA        uint16 = 0x0033
-	TLS_RSA_WITH_AES_256_CBC_SHA            uint16 = 0x0035
-	TLS_DHE_RSA_WITH_AES_256_CBC_SHA        uint16 = 0x0039
-	TLS_RSA_WITH_AES_128_CBC_SHA256         uint16 = 0x003c
-	TLS_RSA_WITH_AES_256_CBC_SHA256         uint16 = 0x003d
-	TLS_DHE_RSA_WITH_AES_128_CBC_SHA256     uint16 = 0x0067
-	TLS_DHE_RSA_WITH_AES_256_CBC_SHA256     uint16 = 0x006b
-	TLS_PSK_WITH_RC4_128_SHA                uint16 = 0x008a
-	TLS_PSK_WITH_AES_128_CBC_SHA            uint16 = 0x008c
-	TLS_PSK_WITH_AES_256_CBC_SHA            uint16 = 0x008d
-	TLS_RSA_WITH_AES_128_GCM_SHA256         uint16 = 0x009c
-	TLS_RSA_WITH_AES_256_GCM_SHA384         uint16 = 0x009d
-	TLS_DHE_RSA_WITH_AES_128_GCM_SHA256     uint16 = 0x009e
-	TLS_DHE_RSA_WITH_AES_256_GCM_SHA384     uint16 = 0x009f
-	TLS_ECDHE_ECDSA_WITH_RC4_128_SHA        uint16 = 0xc007
-	TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA    uint16 = 0xc009
-	TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA    uint16 = 0xc00a
-	TLS_ECDHE_RSA_WITH_RC4_128_SHA          uint16 = 0xc011
-	TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA     uint16 = 0xc012
-	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA      uint16 = 0xc013
-	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA      uint16 = 0xc014
-	TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
-	TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xc024
-	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256   uint16 = 0xc027
-	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384   uint16 = 0xc028
-	TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
-	TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
-	TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256   uint16 = 0xc02f
-	TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384   uint16 = 0xc030
-	TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA      uint16 = 0xc035
-	TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA      uint16 = 0xc036
-	renegotiationSCSV                       uint16 = 0x00ff
-	fallbackSCSV                            uint16 = 0x5600
+	TLS_RSA_WITH_NULL_SHA                         uint16 = 0x0002
+	TLS_RSA_WITH_RC4_128_MD5                      uint16 = 0x0004
+	TLS_RSA_WITH_RC4_128_SHA                      uint16 = 0x0005
+	TLS_RSA_WITH_3DES_EDE_CBC_SHA                 uint16 = 0x000a
+	TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA             uint16 = 0x0016
+	TLS_RSA_WITH_AES_128_CBC_SHA                  uint16 = 0x002f
+	TLS_DHE_RSA_WITH_AES_128_CBC_SHA              uint16 = 0x0033
+	TLS_RSA_WITH_AES_256_CBC_SHA                  uint16 = 0x0035
+	TLS_DHE_RSA_WITH_AES_256_CBC_SHA              uint16 = 0x0039
+	TLS_RSA_WITH_AES_128_CBC_SHA256               uint16 = 0x003c
+	TLS_RSA_WITH_AES_256_CBC_SHA256               uint16 = 0x003d
+	TLS_DHE_RSA_WITH_AES_128_CBC_SHA256           uint16 = 0x0067
+	TLS_DHE_RSA_WITH_AES_256_CBC_SHA256           uint16 = 0x006b
+	TLS_PSK_WITH_RC4_128_SHA                      uint16 = 0x008a
+	TLS_PSK_WITH_AES_128_CBC_SHA                  uint16 = 0x008c
+	TLS_PSK_WITH_AES_256_CBC_SHA                  uint16 = 0x008d
+	TLS_RSA_WITH_AES_128_GCM_SHA256               uint16 = 0x009c
+	TLS_RSA_WITH_AES_256_GCM_SHA384               uint16 = 0x009d
+	TLS_DHE_RSA_WITH_AES_128_GCM_SHA256           uint16 = 0x009e
+	TLS_DHE_RSA_WITH_AES_256_GCM_SHA384           uint16 = 0x009f
+	TLS_ECDHE_ECDSA_WITH_RC4_128_SHA              uint16 = 0xc007
+	TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA          uint16 = 0xc009
+	TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA          uint16 = 0xc00a
+	TLS_ECDHE_RSA_WITH_RC4_128_SHA                uint16 = 0xc011
+	TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA           uint16 = 0xc012
+	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA            uint16 = 0xc013
+	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA            uint16 = 0xc014
+	TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256       uint16 = 0xc023
+	TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384       uint16 = 0xc024
+	TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256         uint16 = 0xc027
+	TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384         uint16 = 0xc028
+	TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256       uint16 = 0xc02b
+	TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384       uint16 = 0xc02c
+	TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256         uint16 = 0xc02f
+	TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384         uint16 = 0xc030
+	TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA            uint16 = 0xc035
+	TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA            uint16 = 0xc036
+	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256   uint16 = 0xcca8
+	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
+	TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256   uint16 = 0xccac
+	renegotiationSCSV                             uint16 = 0x00ff
+	fallbackSCSV                                  uint16 = 0x5600
 )
 
 // Additional cipher suite IDs, not IANA-assigned.
 const (
-	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256   uint16 = 0xcc13
-	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcc14
+	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD   uint16 = 0xcc13
+	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD uint16 = 0xcc14
 )
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 078c227..db3c675 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -98,10 +98,11 @@
 type CurveID uint16
 
 const (
-	CurveP224 CurveID = 21
-	CurveP256 CurveID = 23
-	CurveP384 CurveID = 24
-	CurveP521 CurveID = 25
+	CurveP224   CurveID = 21
+	CurveP256   CurveID = 23
+	CurveP384   CurveID = 24
+	CurveP521   CurveID = 25
+	CurveX25519 CurveID = 29
 )
 
 // TLS Elliptic Curve Point Formats
@@ -399,6 +400,17 @@
 	NumBadValues
 )
 
+type RSABadValue int
+
+const (
+	RSABadValueNone RSABadValue = iota
+	RSABadValueCorrupt
+	RSABadValueTooLong
+	RSABadValueTooShort
+	RSABadValueWrongVersion
+	NumRSABadValues
+)
+
 type ProtocolBugs struct {
 	// InvalidSKXSignature specifies that the signature in a
 	// ServerKeyExchange message should be invalid.
@@ -509,10 +521,9 @@
 	// alert to be sent.
 	SendSpuriousAlert alert
 
-	// RsaClientKeyExchangeVersion, if non-zero, causes the client to send a
-	// ClientKeyExchange with the specified version rather than the
-	// client_version when performing the RSA key exchange.
-	RsaClientKeyExchangeVersion uint16
+	// BadRSAClientKeyExchange causes the client to send a corrupted RSA
+	// ClientKeyExchange which would not pass padding checks.
+	BadRSAClientKeyExchange RSABadValue
 
 	// RenewTicketOnResume causes the server to renew the session ticket and
 	// send a NewSessionTicket message during an abbreviated handshake.
@@ -535,11 +546,6 @@
 	// closed the connection) before or after sending app data.
 	AlertBeforeFalseStartTest alert
 
-	// SSL3RSAKeyExchange causes the client to always send an RSA
-	// ClientKeyExchange message without the two-byte length
-	// prefix, as if it were SSL3.
-	SSL3RSAKeyExchange bool
-
 	// SkipCipherVersionCheck causes the server to negotiate
 	// TLS 1.2 ciphers in earlier versions of TLS.
 	SkipCipherVersionCheck bool
@@ -584,10 +590,18 @@
 	// renegotiation handshake to be incorrect.
 	BadRenegotiationInfo bool
 
-	// NoRenegotiationInfo causes the client to behave as if it
-	// didn't support the renegotiation info extension.
+	// NoRenegotiationInfo disables renegotiation info support in all
+	// handshakes.
 	NoRenegotiationInfo bool
 
+	// NoRenegotiationInfoInInitial disables renegotiation info support in
+	// the initial handshake.
+	NoRenegotiationInfoInInitial bool
+
+	// NoRenegotiationInfoAfterInitial disables renegotiation info support
+	// in renegotiation handshakes.
+	NoRenegotiationInfoAfterInitial bool
+
 	// RequireRenegotiationInfo, if true, causes the client to return an
 	// error if the server doesn't reply with the renegotiation extension.
 	RequireRenegotiationInfo bool
@@ -787,6 +801,19 @@
 	// HelloRequest handshake message to be sent before each application
 	// data record. This only makes sense for a server.
 	SendHelloRequestBeforeEveryAppDataRecord bool
+
+	// RequireDHPublicValueLen causes a fatal error if the length (in
+	// bytes) of the server's Diffie-Hellman public value is not equal to
+	// this.
+	RequireDHPublicValueLen int
+
+	// BadChangeCipherSpec, if not nil, is the body to be sent in
+	// ChangeCipherSpec records instead of {1}.
+	BadChangeCipherSpec []byte
+
+	// BadHelloRequest, if not nil, is what to send instead of a
+	// HelloRequest.
+	BadHelloRequest []byte
 }
 
 func (c *Config) serverInit() {
@@ -844,7 +871,7 @@
 	return c.MaxVersion
 }
 
-var defaultCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
+var defaultCurvePreferences = []CurveID{CurveX25519, CurveP256, CurveP384, CurveP521}
 
 func (c *Config) curvePreferences() []CurveID {
 	if c == nil || len(c.CurvePreferences) == 0 {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index ab9e233..cb60a92 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -1201,8 +1201,11 @@
 
 func (c *Conn) Renegotiate() error {
 	if !c.isClient {
-		helloReq := new(helloRequestMsg)
-		c.writeRecord(recordTypeHandshake, helloReq.marshal())
+		helloReq := new(helloRequestMsg).marshal()
+		if c.config.Bugs.BadHelloRequest != nil {
+			helloReq = c.config.Bugs.BadHelloRequest
+		}
+		c.writeRecord(recordTypeHandshake, helloReq)
 	}
 
 	c.handshakeComplete = false
@@ -1414,3 +1417,18 @@
 	prfForVersion(c.vers, c.cipherSuite)(result, c.masterSecret[:], label, seed)
 	return result, nil
 }
+
+// noRenegotiationInfo returns true if the renegotiation info extension
+// should be supported in the current handshake.
+func (c *Conn) noRenegotiationInfo() bool {
+	if c.config.Bugs.NoRenegotiationInfo {
+		return true
+	}
+	if c.cipherSuite == nil && c.config.Bugs.NoRenegotiationInfoInInitial {
+		return true
+	}
+	if c.cipherSuite != nil && c.config.Bugs.NoRenegotiationInfoAfterInitial {
+		return true
+	}
+	return false
+}
diff --git a/src/ssl/test/runner/curve25519/const_amd64.s b/src/ssl/test/runner/curve25519/const_amd64.s
new file mode 100644
index 0000000..797f9b0
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/const_amd64.s
@@ -0,0 +1,20 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+DATA ·REDMASK51(SB)/8, $0x0007FFFFFFFFFFFF
+GLOBL ·REDMASK51(SB), 8, $8
+
+DATA ·_121666_213(SB)/8, $996687872
+GLOBL ·_121666_213(SB), 8, $8
+
+DATA ·_2P0(SB)/8, $0xFFFFFFFFFFFDA
+GLOBL ·_2P0(SB), 8, $8
+
+DATA ·_2P1234(SB)/8, $0xFFFFFFFFFFFFE
+GLOBL ·_2P1234(SB), 8, $8
diff --git a/src/ssl/test/runner/curve25519/cswap_amd64.s b/src/ssl/test/runner/curve25519/cswap_amd64.s
new file mode 100644
index 0000000..45484d1
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/cswap_amd64.s
@@ -0,0 +1,88 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func cswap(inout *[5]uint64, v uint64)
+TEXT ·cswap(SB),7,$0
+	MOVQ inout+0(FP),DI
+	MOVQ v+8(FP),SI
+
+	CMPQ SI,$1
+	MOVQ 0(DI),SI
+	MOVQ 80(DI),DX
+	MOVQ 8(DI),CX
+	MOVQ 88(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,0(DI)
+	MOVQ DX,80(DI)
+	MOVQ CX,8(DI)
+	MOVQ R8,88(DI)
+	MOVQ 16(DI),SI
+	MOVQ 96(DI),DX
+	MOVQ 24(DI),CX
+	MOVQ 104(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,16(DI)
+	MOVQ DX,96(DI)
+	MOVQ CX,24(DI)
+	MOVQ R8,104(DI)
+	MOVQ 32(DI),SI
+	MOVQ 112(DI),DX
+	MOVQ 40(DI),CX
+	MOVQ 120(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,32(DI)
+	MOVQ DX,112(DI)
+	MOVQ CX,40(DI)
+	MOVQ R8,120(DI)
+	MOVQ 48(DI),SI
+	MOVQ 128(DI),DX
+	MOVQ 56(DI),CX
+	MOVQ 136(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,48(DI)
+	MOVQ DX,128(DI)
+	MOVQ CX,56(DI)
+	MOVQ R8,136(DI)
+	MOVQ 64(DI),SI
+	MOVQ 144(DI),DX
+	MOVQ 72(DI),CX
+	MOVQ 152(DI),R8
+	MOVQ SI,R9
+	CMOVQEQ DX,SI
+	CMOVQEQ R9,DX
+	MOVQ CX,R9
+	CMOVQEQ R8,CX
+	CMOVQEQ R9,R8
+	MOVQ SI,64(DI)
+	MOVQ DX,144(DI)
+	MOVQ CX,72(DI)
+	MOVQ R8,152(DI)
+	MOVQ DI,AX
+	MOVQ SI,DX
+	RET
diff --git a/src/ssl/test/runner/curve25519/curve25519.go b/src/ssl/test/runner/curve25519/curve25519.go
new file mode 100644
index 0000000..6918c47
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/curve25519.go
@@ -0,0 +1,841 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// We have a implementation in amd64 assembly so this code is only run on
+// non-amd64 platforms. The amd64 assembly does not support gccgo.
+// +build !amd64 gccgo appengine
+
+package curve25519
+
+// This code is a port of the public domain, "ref10" implementation of
+// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
+
+// fieldElement represents an element of the field GF(2^255 - 19). An element
+// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
+// context.
+type fieldElement [10]int32
+
+func feZero(fe *fieldElement) {
+	for i := range fe {
+		fe[i] = 0
+	}
+}
+
+func feOne(fe *fieldElement) {
+	feZero(fe)
+	fe[0] = 1
+}
+
+func feAdd(dst, a, b *fieldElement) {
+	for i := range dst {
+		dst[i] = a[i] + b[i]
+	}
+}
+
+func feSub(dst, a, b *fieldElement) {
+	for i := range dst {
+		dst[i] = a[i] - b[i]
+	}
+}
+
+func feCopy(dst, src *fieldElement) {
+	for i := range dst {
+		dst[i] = src[i]
+	}
+}
+
+// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0.
+//
+// Preconditions: b in {0,1}.
+func feCSwap(f, g *fieldElement, b int32) {
+	var x fieldElement
+	b = -b
+	for i := range x {
+		x[i] = b & (f[i] ^ g[i])
+	}
+
+	for i := range f {
+		f[i] ^= x[i]
+	}
+	for i := range g {
+		g[i] ^= x[i]
+	}
+}
+
+// load3 reads a 24-bit, little-endian value from in.
+func load3(in []byte) int64 {
+	var r int64
+	r = int64(in[0])
+	r |= int64(in[1]) << 8
+	r |= int64(in[2]) << 16
+	return r
+}
+
+// load4 reads a 32-bit, little-endian value from in.
+func load4(in []byte) int64 {
+	var r int64
+	r = int64(in[0])
+	r |= int64(in[1]) << 8
+	r |= int64(in[2]) << 16
+	r |= int64(in[3]) << 24
+	return r
+}
+
+func feFromBytes(dst *fieldElement, src *[32]byte) {
+	h0 := load4(src[:])
+	h1 := load3(src[4:]) << 6
+	h2 := load3(src[7:]) << 5
+	h3 := load3(src[10:]) << 3
+	h4 := load3(src[13:]) << 2
+	h5 := load4(src[16:])
+	h6 := load3(src[20:]) << 7
+	h7 := load3(src[23:]) << 5
+	h8 := load3(src[26:]) << 4
+	h9 := load3(src[29:]) << 2
+
+	var carry [10]int64
+	carry[9] = (h9 + 1<<24) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+	carry[1] = (h1 + 1<<24) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[3] = (h3 + 1<<24) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[5] = (h5 + 1<<24) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+	carry[7] = (h7 + 1<<24) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+
+	carry[0] = (h0 + 1<<25) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[2] = (h2 + 1<<25) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[4] = (h4 + 1<<25) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[6] = (h6 + 1<<25) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+	carry[8] = (h8 + 1<<25) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+
+	dst[0] = int32(h0)
+	dst[1] = int32(h1)
+	dst[2] = int32(h2)
+	dst[3] = int32(h3)
+	dst[4] = int32(h4)
+	dst[5] = int32(h5)
+	dst[6] = int32(h6)
+	dst[7] = int32(h7)
+	dst[8] = int32(h8)
+	dst[9] = int32(h9)
+}
+
+// feToBytes marshals h to s.
+// Preconditions:
+//   |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Write p=2^255-19; q=floor(h/p).
+// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+//
+// Proof:
+//   Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+//   Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
+//
+//   Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+//   Then 0<y<1.
+//
+//   Write r=h-pq.
+//   Have 0<=r<=p-1=2^255-20.
+//   Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+//
+//   Write x=r+19(2^-255)r+y.
+//   Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+//
+//   Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+//   so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+func feToBytes(s *[32]byte, h *fieldElement) {
+	var carry [10]int32
+
+	q := (19*h[9] + (1 << 24)) >> 25
+	q = (h[0] + q) >> 26
+	q = (h[1] + q) >> 25
+	q = (h[2] + q) >> 26
+	q = (h[3] + q) >> 25
+	q = (h[4] + q) >> 26
+	q = (h[5] + q) >> 25
+	q = (h[6] + q) >> 26
+	q = (h[7] + q) >> 25
+	q = (h[8] + q) >> 26
+	q = (h[9] + q) >> 25
+
+	// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
+	h[0] += 19 * q
+	// Goal: Output h-2^255 q, which is between 0 and 2^255-20.
+
+	carry[0] = h[0] >> 26
+	h[1] += carry[0]
+	h[0] -= carry[0] << 26
+	carry[1] = h[1] >> 25
+	h[2] += carry[1]
+	h[1] -= carry[1] << 25
+	carry[2] = h[2] >> 26
+	h[3] += carry[2]
+	h[2] -= carry[2] << 26
+	carry[3] = h[3] >> 25
+	h[4] += carry[3]
+	h[3] -= carry[3] << 25
+	carry[4] = h[4] >> 26
+	h[5] += carry[4]
+	h[4] -= carry[4] << 26
+	carry[5] = h[5] >> 25
+	h[6] += carry[5]
+	h[5] -= carry[5] << 25
+	carry[6] = h[6] >> 26
+	h[7] += carry[6]
+	h[6] -= carry[6] << 26
+	carry[7] = h[7] >> 25
+	h[8] += carry[7]
+	h[7] -= carry[7] << 25
+	carry[8] = h[8] >> 26
+	h[9] += carry[8]
+	h[8] -= carry[8] << 26
+	carry[9] = h[9] >> 25
+	h[9] -= carry[9] << 25
+	// h10 = carry9
+
+	// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+	// Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
+	// evidently 2^255 h10-2^255 q = 0.
+	// Goal: Output h[0]+...+2^230 h[9].
+
+	s[0] = byte(h[0] >> 0)
+	s[1] = byte(h[0] >> 8)
+	s[2] = byte(h[0] >> 16)
+	s[3] = byte((h[0] >> 24) | (h[1] << 2))
+	s[4] = byte(h[1] >> 6)
+	s[5] = byte(h[1] >> 14)
+	s[6] = byte((h[1] >> 22) | (h[2] << 3))
+	s[7] = byte(h[2] >> 5)
+	s[8] = byte(h[2] >> 13)
+	s[9] = byte((h[2] >> 21) | (h[3] << 5))
+	s[10] = byte(h[3] >> 3)
+	s[11] = byte(h[3] >> 11)
+	s[12] = byte((h[3] >> 19) | (h[4] << 6))
+	s[13] = byte(h[4] >> 2)
+	s[14] = byte(h[4] >> 10)
+	s[15] = byte(h[4] >> 18)
+	s[16] = byte(h[5] >> 0)
+	s[17] = byte(h[5] >> 8)
+	s[18] = byte(h[5] >> 16)
+	s[19] = byte((h[5] >> 24) | (h[6] << 1))
+	s[20] = byte(h[6] >> 7)
+	s[21] = byte(h[6] >> 15)
+	s[22] = byte((h[6] >> 23) | (h[7] << 3))
+	s[23] = byte(h[7] >> 5)
+	s[24] = byte(h[7] >> 13)
+	s[25] = byte((h[7] >> 21) | (h[8] << 4))
+	s[26] = byte(h[8] >> 4)
+	s[27] = byte(h[8] >> 12)
+	s[28] = byte((h[8] >> 20) | (h[9] << 6))
+	s[29] = byte(h[9] >> 2)
+	s[30] = byte(h[9] >> 10)
+	s[31] = byte(h[9] >> 18)
+}
+
+// feMul calculates h = f * g
+// Can overlap h with f or g.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//    |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Notes on implementation strategy:
+//
+// Using schoolbook multiplication.
+// Karatsuba would save a little in some cost models.
+//
+// Most multiplications by 2 and 19 are 32-bit precomputations;
+// cheaper than 64-bit postcomputations.
+//
+// There is one remaining multiplication by 19 in the carry chain;
+// one *19 precomputation can be merged into this,
+// but the resulting data flow is considerably less clean.
+//
+// There are 12 carries below.
+// 10 of them are 2-way parallelizable and vectorizable.
+// Can get away with 11 carries, but then data flow is much deeper.
+//
+// With tighter constraints on inputs can squeeze carries into int32.
+func feMul(h, f, g *fieldElement) {
+	f0 := f[0]
+	f1 := f[1]
+	f2 := f[2]
+	f3 := f[3]
+	f4 := f[4]
+	f5 := f[5]
+	f6 := f[6]
+	f7 := f[7]
+	f8 := f[8]
+	f9 := f[9]
+	g0 := g[0]
+	g1 := g[1]
+	g2 := g[2]
+	g3 := g[3]
+	g4 := g[4]
+	g5 := g[5]
+	g6 := g[6]
+	g7 := g[7]
+	g8 := g[8]
+	g9 := g[9]
+	g1_19 := 19 * g1 // 1.4*2^29
+	g2_19 := 19 * g2 // 1.4*2^30; still ok
+	g3_19 := 19 * g3
+	g4_19 := 19 * g4
+	g5_19 := 19 * g5
+	g6_19 := 19 * g6
+	g7_19 := 19 * g7
+	g8_19 := 19 * g8
+	g9_19 := 19 * g9
+	f1_2 := 2 * f1
+	f3_2 := 2 * f3
+	f5_2 := 2 * f5
+	f7_2 := 2 * f7
+	f9_2 := 2 * f9
+	f0g0 := int64(f0) * int64(g0)
+	f0g1 := int64(f0) * int64(g1)
+	f0g2 := int64(f0) * int64(g2)
+	f0g3 := int64(f0) * int64(g3)
+	f0g4 := int64(f0) * int64(g4)
+	f0g5 := int64(f0) * int64(g5)
+	f0g6 := int64(f0) * int64(g6)
+	f0g7 := int64(f0) * int64(g7)
+	f0g8 := int64(f0) * int64(g8)
+	f0g9 := int64(f0) * int64(g9)
+	f1g0 := int64(f1) * int64(g0)
+	f1g1_2 := int64(f1_2) * int64(g1)
+	f1g2 := int64(f1) * int64(g2)
+	f1g3_2 := int64(f1_2) * int64(g3)
+	f1g4 := int64(f1) * int64(g4)
+	f1g5_2 := int64(f1_2) * int64(g5)
+	f1g6 := int64(f1) * int64(g6)
+	f1g7_2 := int64(f1_2) * int64(g7)
+	f1g8 := int64(f1) * int64(g8)
+	f1g9_38 := int64(f1_2) * int64(g9_19)
+	f2g0 := int64(f2) * int64(g0)
+	f2g1 := int64(f2) * int64(g1)
+	f2g2 := int64(f2) * int64(g2)
+	f2g3 := int64(f2) * int64(g3)
+	f2g4 := int64(f2) * int64(g4)
+	f2g5 := int64(f2) * int64(g5)
+	f2g6 := int64(f2) * int64(g6)
+	f2g7 := int64(f2) * int64(g7)
+	f2g8_19 := int64(f2) * int64(g8_19)
+	f2g9_19 := int64(f2) * int64(g9_19)
+	f3g0 := int64(f3) * int64(g0)
+	f3g1_2 := int64(f3_2) * int64(g1)
+	f3g2 := int64(f3) * int64(g2)
+	f3g3_2 := int64(f3_2) * int64(g3)
+	f3g4 := int64(f3) * int64(g4)
+	f3g5_2 := int64(f3_2) * int64(g5)
+	f3g6 := int64(f3) * int64(g6)
+	f3g7_38 := int64(f3_2) * int64(g7_19)
+	f3g8_19 := int64(f3) * int64(g8_19)
+	f3g9_38 := int64(f3_2) * int64(g9_19)
+	f4g0 := int64(f4) * int64(g0)
+	f4g1 := int64(f4) * int64(g1)
+	f4g2 := int64(f4) * int64(g2)
+	f4g3 := int64(f4) * int64(g3)
+	f4g4 := int64(f4) * int64(g4)
+	f4g5 := int64(f4) * int64(g5)
+	f4g6_19 := int64(f4) * int64(g6_19)
+	f4g7_19 := int64(f4) * int64(g7_19)
+	f4g8_19 := int64(f4) * int64(g8_19)
+	f4g9_19 := int64(f4) * int64(g9_19)
+	f5g0 := int64(f5) * int64(g0)
+	f5g1_2 := int64(f5_2) * int64(g1)
+	f5g2 := int64(f5) * int64(g2)
+	f5g3_2 := int64(f5_2) * int64(g3)
+	f5g4 := int64(f5) * int64(g4)
+	f5g5_38 := int64(f5_2) * int64(g5_19)
+	f5g6_19 := int64(f5) * int64(g6_19)
+	f5g7_38 := int64(f5_2) * int64(g7_19)
+	f5g8_19 := int64(f5) * int64(g8_19)
+	f5g9_38 := int64(f5_2) * int64(g9_19)
+	f6g0 := int64(f6) * int64(g0)
+	f6g1 := int64(f6) * int64(g1)
+	f6g2 := int64(f6) * int64(g2)
+	f6g3 := int64(f6) * int64(g3)
+	f6g4_19 := int64(f6) * int64(g4_19)
+	f6g5_19 := int64(f6) * int64(g5_19)
+	f6g6_19 := int64(f6) * int64(g6_19)
+	f6g7_19 := int64(f6) * int64(g7_19)
+	f6g8_19 := int64(f6) * int64(g8_19)
+	f6g9_19 := int64(f6) * int64(g9_19)
+	f7g0 := int64(f7) * int64(g0)
+	f7g1_2 := int64(f7_2) * int64(g1)
+	f7g2 := int64(f7) * int64(g2)
+	f7g3_38 := int64(f7_2) * int64(g3_19)
+	f7g4_19 := int64(f7) * int64(g4_19)
+	f7g5_38 := int64(f7_2) * int64(g5_19)
+	f7g6_19 := int64(f7) * int64(g6_19)
+	f7g7_38 := int64(f7_2) * int64(g7_19)
+	f7g8_19 := int64(f7) * int64(g8_19)
+	f7g9_38 := int64(f7_2) * int64(g9_19)
+	f8g0 := int64(f8) * int64(g0)
+	f8g1 := int64(f8) * int64(g1)
+	f8g2_19 := int64(f8) * int64(g2_19)
+	f8g3_19 := int64(f8) * int64(g3_19)
+	f8g4_19 := int64(f8) * int64(g4_19)
+	f8g5_19 := int64(f8) * int64(g5_19)
+	f8g6_19 := int64(f8) * int64(g6_19)
+	f8g7_19 := int64(f8) * int64(g7_19)
+	f8g8_19 := int64(f8) * int64(g8_19)
+	f8g9_19 := int64(f8) * int64(g9_19)
+	f9g0 := int64(f9) * int64(g0)
+	f9g1_38 := int64(f9_2) * int64(g1_19)
+	f9g2_19 := int64(f9) * int64(g2_19)
+	f9g3_38 := int64(f9_2) * int64(g3_19)
+	f9g4_19 := int64(f9) * int64(g4_19)
+	f9g5_38 := int64(f9_2) * int64(g5_19)
+	f9g6_19 := int64(f9) * int64(g6_19)
+	f9g7_38 := int64(f9_2) * int64(g7_19)
+	f9g8_19 := int64(f9) * int64(g8_19)
+	f9g9_38 := int64(f9_2) * int64(g9_19)
+	h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38
+	h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19
+	h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38
+	h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19
+	h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38
+	h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19
+	h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38
+	h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19
+	h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38
+	h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0
+	var carry [10]int64
+
+	// |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
+	//   i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
+	// |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
+	//   i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	// |h0| <= 2^25
+	// |h4| <= 2^25
+	// |h1| <= 1.51*2^58
+	// |h5| <= 1.51*2^58
+
+	carry[1] = (h1 + (1 << 24)) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[5] = (h5 + (1 << 24)) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+	// |h1| <= 2^24; from now on fits into int32
+	// |h5| <= 2^24; from now on fits into int32
+	// |h2| <= 1.21*2^59
+	// |h6| <= 1.21*2^59
+
+	carry[2] = (h2 + (1 << 25)) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[6] = (h6 + (1 << 25)) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+	// |h2| <= 2^25; from now on fits into int32 unchanged
+	// |h6| <= 2^25; from now on fits into int32 unchanged
+	// |h3| <= 1.51*2^58
+	// |h7| <= 1.51*2^58
+
+	carry[3] = (h3 + (1 << 24)) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[7] = (h7 + (1 << 24)) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+	// |h3| <= 2^24; from now on fits into int32 unchanged
+	// |h7| <= 2^24; from now on fits into int32 unchanged
+	// |h4| <= 1.52*2^33
+	// |h8| <= 1.52*2^33
+
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[8] = (h8 + (1 << 25)) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+	// |h4| <= 2^25; from now on fits into int32 unchanged
+	// |h8| <= 2^25; from now on fits into int32 unchanged
+	// |h5| <= 1.01*2^24
+	// |h9| <= 1.51*2^58
+
+	carry[9] = (h9 + (1 << 24)) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+	// |h9| <= 2^24; from now on fits into int32 unchanged
+	// |h0| <= 1.8*2^37
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	// |h0| <= 2^25; from now on fits into int32 unchanged
+	// |h1| <= 1.01*2^24
+
+	h[0] = int32(h0)
+	h[1] = int32(h1)
+	h[2] = int32(h2)
+	h[3] = int32(h3)
+	h[4] = int32(h4)
+	h[5] = int32(h5)
+	h[6] = int32(h6)
+	h[7] = int32(h7)
+	h[8] = int32(h8)
+	h[9] = int32(h9)
+}
+
+// feSquare calculates h = f*f. Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func feSquare(h, f *fieldElement) {
+	f0 := f[0]
+	f1 := f[1]
+	f2 := f[2]
+	f3 := f[3]
+	f4 := f[4]
+	f5 := f[5]
+	f6 := f[6]
+	f7 := f[7]
+	f8 := f[8]
+	f9 := f[9]
+	f0_2 := 2 * f0
+	f1_2 := 2 * f1
+	f2_2 := 2 * f2
+	f3_2 := 2 * f3
+	f4_2 := 2 * f4
+	f5_2 := 2 * f5
+	f6_2 := 2 * f6
+	f7_2 := 2 * f7
+	f5_38 := 38 * f5 // 1.31*2^30
+	f6_19 := 19 * f6 // 1.31*2^30
+	f7_38 := 38 * f7 // 1.31*2^30
+	f8_19 := 19 * f8 // 1.31*2^30
+	f9_38 := 38 * f9 // 1.31*2^30
+	f0f0 := int64(f0) * int64(f0)
+	f0f1_2 := int64(f0_2) * int64(f1)
+	f0f2_2 := int64(f0_2) * int64(f2)
+	f0f3_2 := int64(f0_2) * int64(f3)
+	f0f4_2 := int64(f0_2) * int64(f4)
+	f0f5_2 := int64(f0_2) * int64(f5)
+	f0f6_2 := int64(f0_2) * int64(f6)
+	f0f7_2 := int64(f0_2) * int64(f7)
+	f0f8_2 := int64(f0_2) * int64(f8)
+	f0f9_2 := int64(f0_2) * int64(f9)
+	f1f1_2 := int64(f1_2) * int64(f1)
+	f1f2_2 := int64(f1_2) * int64(f2)
+	f1f3_4 := int64(f1_2) * int64(f3_2)
+	f1f4_2 := int64(f1_2) * int64(f4)
+	f1f5_4 := int64(f1_2) * int64(f5_2)
+	f1f6_2 := int64(f1_2) * int64(f6)
+	f1f7_4 := int64(f1_2) * int64(f7_2)
+	f1f8_2 := int64(f1_2) * int64(f8)
+	f1f9_76 := int64(f1_2) * int64(f9_38)
+	f2f2 := int64(f2) * int64(f2)
+	f2f3_2 := int64(f2_2) * int64(f3)
+	f2f4_2 := int64(f2_2) * int64(f4)
+	f2f5_2 := int64(f2_2) * int64(f5)
+	f2f6_2 := int64(f2_2) * int64(f6)
+	f2f7_2 := int64(f2_2) * int64(f7)
+	f2f8_38 := int64(f2_2) * int64(f8_19)
+	f2f9_38 := int64(f2) * int64(f9_38)
+	f3f3_2 := int64(f3_2) * int64(f3)
+	f3f4_2 := int64(f3_2) * int64(f4)
+	f3f5_4 := int64(f3_2) * int64(f5_2)
+	f3f6_2 := int64(f3_2) * int64(f6)
+	f3f7_76 := int64(f3_2) * int64(f7_38)
+	f3f8_38 := int64(f3_2) * int64(f8_19)
+	f3f9_76 := int64(f3_2) * int64(f9_38)
+	f4f4 := int64(f4) * int64(f4)
+	f4f5_2 := int64(f4_2) * int64(f5)
+	f4f6_38 := int64(f4_2) * int64(f6_19)
+	f4f7_38 := int64(f4) * int64(f7_38)
+	f4f8_38 := int64(f4_2) * int64(f8_19)
+	f4f9_38 := int64(f4) * int64(f9_38)
+	f5f5_38 := int64(f5) * int64(f5_38)
+	f5f6_38 := int64(f5_2) * int64(f6_19)
+	f5f7_76 := int64(f5_2) * int64(f7_38)
+	f5f8_38 := int64(f5_2) * int64(f8_19)
+	f5f9_76 := int64(f5_2) * int64(f9_38)
+	f6f6_19 := int64(f6) * int64(f6_19)
+	f6f7_38 := int64(f6) * int64(f7_38)
+	f6f8_38 := int64(f6_2) * int64(f8_19)
+	f6f9_38 := int64(f6) * int64(f9_38)
+	f7f7_38 := int64(f7) * int64(f7_38)
+	f7f8_38 := int64(f7_2) * int64(f8_19)
+	f7f9_76 := int64(f7_2) * int64(f9_38)
+	f8f8_19 := int64(f8) * int64(f8_19)
+	f8f9_38 := int64(f8) * int64(f9_38)
+	f9f9_38 := int64(f9) * int64(f9_38)
+	h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38
+	h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38
+	h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19
+	h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38
+	h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38
+	h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38
+	h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19
+	h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38
+	h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38
+	h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2
+	var carry [10]int64
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+
+	carry[1] = (h1 + (1 << 24)) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[5] = (h5 + (1 << 24)) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+
+	carry[2] = (h2 + (1 << 25)) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[6] = (h6 + (1 << 25)) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+
+	carry[3] = (h3 + (1 << 24)) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[7] = (h7 + (1 << 24)) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[8] = (h8 + (1 << 25)) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+
+	carry[9] = (h9 + (1 << 24)) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+
+	h[0] = int32(h0)
+	h[1] = int32(h1)
+	h[2] = int32(h2)
+	h[3] = int32(h3)
+	h[4] = int32(h4)
+	h[5] = int32(h5)
+	h[6] = int32(h6)
+	h[7] = int32(h7)
+	h[8] = int32(h8)
+	h[9] = int32(h9)
+}
+
+// feMul121666 calculates h = f * 121666. Can overlap h with f.
+//
+// Preconditions:
+//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func feMul121666(h, f *fieldElement) {
+	h0 := int64(f[0]) * 121666
+	h1 := int64(f[1]) * 121666
+	h2 := int64(f[2]) * 121666
+	h3 := int64(f[3]) * 121666
+	h4 := int64(f[4]) * 121666
+	h5 := int64(f[5]) * 121666
+	h6 := int64(f[6]) * 121666
+	h7 := int64(f[7]) * 121666
+	h8 := int64(f[8]) * 121666
+	h9 := int64(f[9]) * 121666
+	var carry [10]int64
+
+	carry[9] = (h9 + (1 << 24)) >> 25
+	h0 += carry[9] * 19
+	h9 -= carry[9] << 25
+	carry[1] = (h1 + (1 << 24)) >> 25
+	h2 += carry[1]
+	h1 -= carry[1] << 25
+	carry[3] = (h3 + (1 << 24)) >> 25
+	h4 += carry[3]
+	h3 -= carry[3] << 25
+	carry[5] = (h5 + (1 << 24)) >> 25
+	h6 += carry[5]
+	h5 -= carry[5] << 25
+	carry[7] = (h7 + (1 << 24)) >> 25
+	h8 += carry[7]
+	h7 -= carry[7] << 25
+
+	carry[0] = (h0 + (1 << 25)) >> 26
+	h1 += carry[0]
+	h0 -= carry[0] << 26
+	carry[2] = (h2 + (1 << 25)) >> 26
+	h3 += carry[2]
+	h2 -= carry[2] << 26
+	carry[4] = (h4 + (1 << 25)) >> 26
+	h5 += carry[4]
+	h4 -= carry[4] << 26
+	carry[6] = (h6 + (1 << 25)) >> 26
+	h7 += carry[6]
+	h6 -= carry[6] << 26
+	carry[8] = (h8 + (1 << 25)) >> 26
+	h9 += carry[8]
+	h8 -= carry[8] << 26
+
+	h[0] = int32(h0)
+	h[1] = int32(h1)
+	h[2] = int32(h2)
+	h[3] = int32(h3)
+	h[4] = int32(h4)
+	h[5] = int32(h5)
+	h[6] = int32(h6)
+	h[7] = int32(h7)
+	h[8] = int32(h8)
+	h[9] = int32(h9)
+}
+
+// feInvert sets out = z^-1.
+func feInvert(out, z *fieldElement) {
+	var t0, t1, t2, t3 fieldElement
+	var i int
+
+	feSquare(&t0, z)
+	for i = 1; i < 1; i++ {
+		feSquare(&t0, &t0)
+	}
+	feSquare(&t1, &t0)
+	for i = 1; i < 2; i++ {
+		feSquare(&t1, &t1)
+	}
+	feMul(&t1, z, &t1)
+	feMul(&t0, &t0, &t1)
+	feSquare(&t2, &t0)
+	for i = 1; i < 1; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t1, &t2)
+	feSquare(&t2, &t1)
+	for i = 1; i < 5; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t2, &t1)
+	feSquare(&t2, &t1)
+	for i = 1; i < 10; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t2, &t2, &t1)
+	feSquare(&t3, &t2)
+	for i = 1; i < 20; i++ {
+		feSquare(&t3, &t3)
+	}
+	feMul(&t2, &t3, &t2)
+	feSquare(&t2, &t2)
+	for i = 1; i < 10; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t2, &t1)
+	feSquare(&t2, &t1)
+	for i = 1; i < 50; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t2, &t2, &t1)
+	feSquare(&t3, &t2)
+	for i = 1; i < 100; i++ {
+		feSquare(&t3, &t3)
+	}
+	feMul(&t2, &t3, &t2)
+	feSquare(&t2, &t2)
+	for i = 1; i < 50; i++ {
+		feSquare(&t2, &t2)
+	}
+	feMul(&t1, &t2, &t1)
+	feSquare(&t1, &t1)
+	for i = 1; i < 5; i++ {
+		feSquare(&t1, &t1)
+	}
+	feMul(out, &t1, &t0)
+}
+
+func scalarMult(out, in, base *[32]byte) {
+	var e [32]byte
+
+	copy(e[:], in[:])
+	e[0] &= 248
+	e[31] &= 127
+	e[31] |= 64
+
+	var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement
+	feFromBytes(&x1, base)
+	feOne(&x2)
+	feCopy(&x3, &x1)
+	feOne(&z3)
+
+	swap := int32(0)
+	for pos := 254; pos >= 0; pos-- {
+		b := e[pos/8] >> uint(pos&7)
+		b &= 1
+		swap ^= int32(b)
+		feCSwap(&x2, &x3, swap)
+		feCSwap(&z2, &z3, swap)
+		swap = int32(b)
+
+		feSub(&tmp0, &x3, &z3)
+		feSub(&tmp1, &x2, &z2)
+		feAdd(&x2, &x2, &z2)
+		feAdd(&z2, &x3, &z3)
+		feMul(&z3, &tmp0, &x2)
+		feMul(&z2, &z2, &tmp1)
+		feSquare(&tmp0, &tmp1)
+		feSquare(&tmp1, &x2)
+		feAdd(&x3, &z3, &z2)
+		feSub(&z2, &z3, &z2)
+		feMul(&x2, &tmp1, &tmp0)
+		feSub(&tmp1, &tmp1, &tmp0)
+		feSquare(&z2, &z2)
+		feMul121666(&z3, &tmp1)
+		feSquare(&x3, &x3)
+		feAdd(&tmp0, &tmp0, &z3)
+		feMul(&z3, &x1, &z2)
+		feMul(&z2, &tmp1, &tmp0)
+	}
+
+	feCSwap(&x2, &x3, swap)
+	feCSwap(&z2, &z3, swap)
+
+	feInvert(&z2, &z2)
+	feMul(&x2, &x2, &z2)
+	feToBytes(out, &x2)
+}
diff --git a/src/ssl/test/runner/curve25519/curve25519_test.go b/src/ssl/test/runner/curve25519/curve25519_test.go
new file mode 100644
index 0000000..14b0ee8
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/curve25519_test.go
@@ -0,0 +1,29 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package curve25519
+
+import (
+	"fmt"
+	"testing"
+)
+
+const expectedHex = "89161fde887b2b53de549af483940106ecc114d6982daa98256de23bdf77661a"
+
+func TestBaseScalarMult(t *testing.T) {
+	var a, b [32]byte
+	in := &a
+	out := &b
+	a[0] = 1
+
+	for i := 0; i < 200; i++ {
+		ScalarBaseMult(out, in)
+		in, out = out, in
+	}
+
+	result := fmt.Sprintf("%x", in[:])
+	if result != expectedHex {
+		t.Errorf("incorrect result: got %s, want %s", result, expectedHex)
+	}
+}
diff --git a/src/ssl/test/runner/curve25519/doc.go b/src/ssl/test/runner/curve25519/doc.go
new file mode 100644
index 0000000..ebeea3c
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/doc.go
@@ -0,0 +1,23 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package curve25519 provides an implementation of scalar multiplication on
+// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html
+package curve25519 // import "golang.org/x/crypto/curve25519"
+
+// basePoint is the x coordinate of the generator of the curve.
+var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+// ScalarMult sets dst to the product in*base where dst and base are the x
+// coordinates of group points and all values are in little-endian form.
+func ScalarMult(dst, in, base *[32]byte) {
+	scalarMult(dst, in, base)
+}
+
+// ScalarBaseMult sets dst to the product in*base where dst and base are the x
+// coordinates of group points, base is the standard generator and all values
+// are in little-endian form.
+func ScalarBaseMult(dst, in *[32]byte) {
+	ScalarMult(dst, in, &basePoint)
+}
diff --git a/src/ssl/test/runner/curve25519/freeze_amd64.s b/src/ssl/test/runner/curve25519/freeze_amd64.s
new file mode 100644
index 0000000..37599fa
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/freeze_amd64.s
@@ -0,0 +1,94 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func freeze(inout *[5]uint64)
+TEXT ·freeze(SB),7,$96-8
+	MOVQ inout+0(FP), DI
+
+	MOVQ SP,R11
+	MOVQ $31,CX
+	NOTQ CX
+	ANDQ CX,SP
+	ADDQ $32,SP
+
+	MOVQ R11,0(SP)
+	MOVQ R12,8(SP)
+	MOVQ R13,16(SP)
+	MOVQ R14,24(SP)
+	MOVQ R15,32(SP)
+	MOVQ BX,40(SP)
+	MOVQ BP,48(SP)
+	MOVQ 0(DI),SI
+	MOVQ 8(DI),DX
+	MOVQ 16(DI),CX
+	MOVQ 24(DI),R8
+	MOVQ 32(DI),R9
+	MOVQ ·REDMASK51(SB),AX
+	MOVQ AX,R10
+	SUBQ $18,R10
+	MOVQ $3,R11
+REDUCELOOP:
+	MOVQ SI,R12
+	SHRQ $51,R12
+	ANDQ AX,SI
+	ADDQ R12,DX
+	MOVQ DX,R12
+	SHRQ $51,R12
+	ANDQ AX,DX
+	ADDQ R12,CX
+	MOVQ CX,R12
+	SHRQ $51,R12
+	ANDQ AX,CX
+	ADDQ R12,R8
+	MOVQ R8,R12
+	SHRQ $51,R12
+	ANDQ AX,R8
+	ADDQ R12,R9
+	MOVQ R9,R12
+	SHRQ $51,R12
+	ANDQ AX,R9
+	IMUL3Q $19,R12,R12
+	ADDQ R12,SI
+	SUBQ $1,R11
+	JA REDUCELOOP
+	MOVQ $1,R12
+	CMPQ R10,SI
+	CMOVQLT R11,R12
+	CMPQ AX,DX
+	CMOVQNE R11,R12
+	CMPQ AX,CX
+	CMOVQNE R11,R12
+	CMPQ AX,R8
+	CMOVQNE R11,R12
+	CMPQ AX,R9
+	CMOVQNE R11,R12
+	NEGQ R12
+	ANDQ R12,AX
+	ANDQ R12,R10
+	SUBQ R10,SI
+	SUBQ AX,DX
+	SUBQ AX,CX
+	SUBQ AX,R8
+	SUBQ AX,R9
+	MOVQ SI,0(DI)
+	MOVQ DX,8(DI)
+	MOVQ CX,16(DI)
+	MOVQ R8,24(DI)
+	MOVQ R9,32(DI)
+	MOVQ 0(SP),R11
+	MOVQ 8(SP),R12
+	MOVQ 16(SP),R13
+	MOVQ 24(SP),R14
+	MOVQ 32(SP),R15
+	MOVQ 40(SP),BX
+	MOVQ 48(SP),BP
+	MOVQ R11,SP
+	MOVQ DI,AX
+	MOVQ SI,DX
+	RET
diff --git a/src/ssl/test/runner/curve25519/ladderstep_amd64.s b/src/ssl/test/runner/curve25519/ladderstep_amd64.s
new file mode 100644
index 0000000..3949f9c
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/ladderstep_amd64.s
@@ -0,0 +1,1398 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func ladderstep(inout *[5][5]uint64)
+TEXT ·ladderstep(SB),0,$384-8
+	MOVQ inout+0(FP),DI
+
+	MOVQ SP,R11
+	MOVQ $31,CX
+	NOTQ CX
+	ANDQ CX,SP
+	ADDQ $32,SP
+
+	MOVQ R11,0(SP)
+	MOVQ R12,8(SP)
+	MOVQ R13,16(SP)
+	MOVQ R14,24(SP)
+	MOVQ R15,32(SP)
+	MOVQ BX,40(SP)
+	MOVQ BP,48(SP)
+	MOVQ 40(DI),SI
+	MOVQ 48(DI),DX
+	MOVQ 56(DI),CX
+	MOVQ 64(DI),R8
+	MOVQ 72(DI),R9
+	MOVQ SI,AX
+	MOVQ DX,R10
+	MOVQ CX,R11
+	MOVQ R8,R12
+	MOVQ R9,R13
+	ADDQ ·_2P0(SB),AX
+	ADDQ ·_2P1234(SB),R10
+	ADDQ ·_2P1234(SB),R11
+	ADDQ ·_2P1234(SB),R12
+	ADDQ ·_2P1234(SB),R13
+	ADDQ 80(DI),SI
+	ADDQ 88(DI),DX
+	ADDQ 96(DI),CX
+	ADDQ 104(DI),R8
+	ADDQ 112(DI),R9
+	SUBQ 80(DI),AX
+	SUBQ 88(DI),R10
+	SUBQ 96(DI),R11
+	SUBQ 104(DI),R12
+	SUBQ 112(DI),R13
+	MOVQ SI,56(SP)
+	MOVQ DX,64(SP)
+	MOVQ CX,72(SP)
+	MOVQ R8,80(SP)
+	MOVQ R9,88(SP)
+	MOVQ AX,96(SP)
+	MOVQ R10,104(SP)
+	MOVQ R11,112(SP)
+	MOVQ R12,120(SP)
+	MOVQ R13,128(SP)
+	MOVQ 96(SP),AX
+	MULQ 96(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 96(SP),AX
+	SHLQ $1,AX
+	MULQ 104(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 96(SP),AX
+	SHLQ $1,AX
+	MULQ 112(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 96(SP),AX
+	SHLQ $1,AX
+	MULQ 120(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 96(SP),AX
+	SHLQ $1,AX
+	MULQ 128(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 104(SP),AX
+	MULQ 104(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 104(SP),AX
+	SHLQ $1,AX
+	MULQ 112(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 104(SP),AX
+	SHLQ $1,AX
+	MULQ 120(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 104(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 128(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 112(SP),AX
+	MULQ 112(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 112(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 120(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 112(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 128(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 120(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 120(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 120(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 128(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 128(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 128(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,136(SP)
+	MOVQ R8,144(SP)
+	MOVQ R9,152(SP)
+	MOVQ AX,160(SP)
+	MOVQ R10,168(SP)
+	MOVQ 56(SP),AX
+	MULQ 56(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 56(SP),AX
+	SHLQ $1,AX
+	MULQ 64(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 56(SP),AX
+	SHLQ $1,AX
+	MULQ 72(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 56(SP),AX
+	SHLQ $1,AX
+	MULQ 80(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 56(SP),AX
+	SHLQ $1,AX
+	MULQ 88(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 64(SP),AX
+	MULQ 64(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 64(SP),AX
+	SHLQ $1,AX
+	MULQ 72(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 64(SP),AX
+	SHLQ $1,AX
+	MULQ 80(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 64(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 88(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 72(SP),AX
+	MULQ 72(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 72(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 80(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 72(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 88(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 80(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 80(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 80(SP),DX
+	IMUL3Q $38,DX,AX
+	MULQ 88(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 88(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 88(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,176(SP)
+	MOVQ R8,184(SP)
+	MOVQ R9,192(SP)
+	MOVQ AX,200(SP)
+	MOVQ R10,208(SP)
+	MOVQ SI,SI
+	MOVQ R8,DX
+	MOVQ R9,CX
+	MOVQ AX,R8
+	MOVQ R10,R9
+	ADDQ ·_2P0(SB),SI
+	ADDQ ·_2P1234(SB),DX
+	ADDQ ·_2P1234(SB),CX
+	ADDQ ·_2P1234(SB),R8
+	ADDQ ·_2P1234(SB),R9
+	SUBQ 136(SP),SI
+	SUBQ 144(SP),DX
+	SUBQ 152(SP),CX
+	SUBQ 160(SP),R8
+	SUBQ 168(SP),R9
+	MOVQ SI,216(SP)
+	MOVQ DX,224(SP)
+	MOVQ CX,232(SP)
+	MOVQ R8,240(SP)
+	MOVQ R9,248(SP)
+	MOVQ 120(DI),SI
+	MOVQ 128(DI),DX
+	MOVQ 136(DI),CX
+	MOVQ 144(DI),R8
+	MOVQ 152(DI),R9
+	MOVQ SI,AX
+	MOVQ DX,R10
+	MOVQ CX,R11
+	MOVQ R8,R12
+	MOVQ R9,R13
+	ADDQ ·_2P0(SB),AX
+	ADDQ ·_2P1234(SB),R10
+	ADDQ ·_2P1234(SB),R11
+	ADDQ ·_2P1234(SB),R12
+	ADDQ ·_2P1234(SB),R13
+	ADDQ 160(DI),SI
+	ADDQ 168(DI),DX
+	ADDQ 176(DI),CX
+	ADDQ 184(DI),R8
+	ADDQ 192(DI),R9
+	SUBQ 160(DI),AX
+	SUBQ 168(DI),R10
+	SUBQ 176(DI),R11
+	SUBQ 184(DI),R12
+	SUBQ 192(DI),R13
+	MOVQ SI,256(SP)
+	MOVQ DX,264(SP)
+	MOVQ CX,272(SP)
+	MOVQ R8,280(SP)
+	MOVQ R9,288(SP)
+	MOVQ AX,296(SP)
+	MOVQ R10,304(SP)
+	MOVQ R11,312(SP)
+	MOVQ R12,320(SP)
+	MOVQ R13,328(SP)
+	MOVQ 280(SP),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,336(SP)
+	MULQ 112(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 288(SP),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,344(SP)
+	MULQ 104(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 256(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 256(SP),AX
+	MULQ 104(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 256(SP),AX
+	MULQ 112(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 256(SP),AX
+	MULQ 120(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 256(SP),AX
+	MULQ 128(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 264(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 264(SP),AX
+	MULQ 104(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 264(SP),AX
+	MULQ 112(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 264(SP),AX
+	MULQ 120(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 264(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 128(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 272(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 272(SP),AX
+	MULQ 104(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 272(SP),AX
+	MULQ 112(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 272(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 120(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 272(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 128(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 280(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 280(SP),AX
+	MULQ 104(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 336(SP),AX
+	MULQ 120(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 336(SP),AX
+	MULQ 128(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 288(SP),AX
+	MULQ 96(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 344(SP),AX
+	MULQ 112(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 344(SP),AX
+	MULQ 120(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 344(SP),AX
+	MULQ 128(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,96(SP)
+	MOVQ R8,104(SP)
+	MOVQ R9,112(SP)
+	MOVQ AX,120(SP)
+	MOVQ R10,128(SP)
+	MOVQ 320(SP),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,256(SP)
+	MULQ 72(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 328(SP),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,264(SP)
+	MULQ 64(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 296(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 296(SP),AX
+	MULQ 64(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 296(SP),AX
+	MULQ 72(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 296(SP),AX
+	MULQ 80(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 296(SP),AX
+	MULQ 88(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 304(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 304(SP),AX
+	MULQ 64(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 304(SP),AX
+	MULQ 72(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 304(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 304(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 88(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 312(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 312(SP),AX
+	MULQ 64(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 312(SP),AX
+	MULQ 72(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 312(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 80(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 312(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 88(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 320(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 320(SP),AX
+	MULQ 64(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 256(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 256(SP),AX
+	MULQ 88(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 328(SP),AX
+	MULQ 56(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 264(SP),AX
+	MULQ 72(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 264(SP),AX
+	MULQ 80(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 264(SP),AX
+	MULQ 88(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,DX
+	MOVQ R8,CX
+	MOVQ R9,R11
+	MOVQ AX,R12
+	MOVQ R10,R13
+	ADDQ ·_2P0(SB),DX
+	ADDQ ·_2P1234(SB),CX
+	ADDQ ·_2P1234(SB),R11
+	ADDQ ·_2P1234(SB),R12
+	ADDQ ·_2P1234(SB),R13
+	ADDQ 96(SP),SI
+	ADDQ 104(SP),R8
+	ADDQ 112(SP),R9
+	ADDQ 120(SP),AX
+	ADDQ 128(SP),R10
+	SUBQ 96(SP),DX
+	SUBQ 104(SP),CX
+	SUBQ 112(SP),R11
+	SUBQ 120(SP),R12
+	SUBQ 128(SP),R13
+	MOVQ SI,120(DI)
+	MOVQ R8,128(DI)
+	MOVQ R9,136(DI)
+	MOVQ AX,144(DI)
+	MOVQ R10,152(DI)
+	MOVQ DX,160(DI)
+	MOVQ CX,168(DI)
+	MOVQ R11,176(DI)
+	MOVQ R12,184(DI)
+	MOVQ R13,192(DI)
+	MOVQ 120(DI),AX
+	MULQ 120(DI)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 128(DI)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 136(DI)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 144(DI)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 120(DI),AX
+	SHLQ $1,AX
+	MULQ 152(DI)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 128(DI),AX
+	MULQ 128(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 128(DI),AX
+	SHLQ $1,AX
+	MULQ 136(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 128(DI),AX
+	SHLQ $1,AX
+	MULQ 144(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 128(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 136(DI),AX
+	MULQ 136(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 136(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 144(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 136(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 144(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 144(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 144(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 152(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 152(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,120(DI)
+	MOVQ R8,128(DI)
+	MOVQ R9,136(DI)
+	MOVQ AX,144(DI)
+	MOVQ R10,152(DI)
+	MOVQ 160(DI),AX
+	MULQ 160(DI)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 168(DI)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 176(DI)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 184(DI)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 160(DI),AX
+	SHLQ $1,AX
+	MULQ 192(DI)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 168(DI),AX
+	MULQ 168(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 168(DI),AX
+	SHLQ $1,AX
+	MULQ 176(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 168(DI),AX
+	SHLQ $1,AX
+	MULQ 184(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 168(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),AX
+	MULQ 176(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 176(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 184(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 184(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 184(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 184(DI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 192(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 192(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	ANDQ DX,SI
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ADDQ R10,CX
+	ANDQ DX,R8
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ADDQ R12,CX
+	ANDQ DX,R9
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ADDQ R14,CX
+	ANDQ DX,AX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,160(DI)
+	MOVQ R8,168(DI)
+	MOVQ R9,176(DI)
+	MOVQ AX,184(DI)
+	MOVQ R10,192(DI)
+	MOVQ 184(DI),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,56(SP)
+	MULQ 16(DI)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 192(DI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,64(SP)
+	MULQ 8(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 160(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 160(DI),AX
+	MULQ 8(DI)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 160(DI),AX
+	MULQ 16(DI)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 160(DI),AX
+	MULQ 24(DI)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 160(DI),AX
+	MULQ 32(DI)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 168(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 168(DI),AX
+	MULQ 8(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 168(DI),AX
+	MULQ 16(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 168(DI),AX
+	MULQ 24(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 168(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 176(DI),AX
+	MULQ 8(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 176(DI),AX
+	MULQ 16(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 176(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(DI)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 184(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 184(DI),AX
+	MULQ 8(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 56(SP),AX
+	MULQ 24(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 56(SP),AX
+	MULQ 32(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 192(DI),AX
+	MULQ 0(DI)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 64(SP),AX
+	MULQ 16(DI)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 64(SP),AX
+	MULQ 24(DI)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 64(SP),AX
+	MULQ 32(DI)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,160(DI)
+	MOVQ R8,168(DI)
+	MOVQ R9,176(DI)
+	MOVQ AX,184(DI)
+	MOVQ R10,192(DI)
+	MOVQ 200(SP),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,56(SP)
+	MULQ 152(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 208(SP),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,64(SP)
+	MULQ 144(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(SP),AX
+	MULQ 136(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 176(SP),AX
+	MULQ 144(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 176(SP),AX
+	MULQ 152(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 176(SP),AX
+	MULQ 160(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 176(SP),AX
+	MULQ 168(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 184(SP),AX
+	MULQ 136(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 184(SP),AX
+	MULQ 144(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 184(SP),AX
+	MULQ 152(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 184(SP),AX
+	MULQ 160(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 184(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 168(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 192(SP),AX
+	MULQ 136(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 192(SP),AX
+	MULQ 144(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 192(SP),AX
+	MULQ 152(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 192(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 160(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 192(SP),DX
+	IMUL3Q $19,DX,AX
+	MULQ 168(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 200(SP),AX
+	MULQ 136(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 200(SP),AX
+	MULQ 144(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 56(SP),AX
+	MULQ 160(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 56(SP),AX
+	MULQ 168(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 208(SP),AX
+	MULQ 136(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 64(SP),AX
+	MULQ 152(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 64(SP),AX
+	MULQ 160(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 64(SP),AX
+	MULQ 168(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,40(DI)
+	MOVQ R8,48(DI)
+	MOVQ R9,56(DI)
+	MOVQ AX,64(DI)
+	MOVQ R10,72(DI)
+	MOVQ 216(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 224(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,CX
+	MOVQ DX,R8
+	MOVQ 232(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,R8
+	MOVQ DX,R9
+	MOVQ 240(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,R9
+	MOVQ DX,R10
+	MOVQ 248(SP),AX
+	MULQ ·_121666_213(SB)
+	SHRQ $13,AX
+	ADDQ AX,R10
+	IMUL3Q $19,DX,DX
+	ADDQ DX,SI
+	ADDQ 136(SP),SI
+	ADDQ 144(SP),CX
+	ADDQ 152(SP),R8
+	ADDQ 160(SP),R9
+	ADDQ 168(SP),R10
+	MOVQ SI,80(DI)
+	MOVQ CX,88(DI)
+	MOVQ R8,96(DI)
+	MOVQ R9,104(DI)
+	MOVQ R10,112(DI)
+	MOVQ 104(DI),SI
+	IMUL3Q $19,SI,AX
+	MOVQ AX,56(SP)
+	MULQ 232(SP)
+	MOVQ AX,SI
+	MOVQ DX,CX
+	MOVQ 112(DI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,64(SP)
+	MULQ 224(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 80(DI),AX
+	MULQ 216(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 80(DI),AX
+	MULQ 224(SP)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 80(DI),AX
+	MULQ 232(SP)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 80(DI),AX
+	MULQ 240(SP)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 80(DI),AX
+	MULQ 248(SP)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 88(DI),AX
+	MULQ 216(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 88(DI),AX
+	MULQ 224(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 88(DI),AX
+	MULQ 232(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 88(DI),AX
+	MULQ 240(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 88(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 248(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 96(DI),AX
+	MULQ 216(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 96(DI),AX
+	MULQ 224(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 96(DI),AX
+	MULQ 232(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 96(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 240(SP)
+	ADDQ AX,SI
+	ADCQ DX,CX
+	MOVQ 96(DI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 248(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 104(DI),AX
+	MULQ 216(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 104(DI),AX
+	MULQ 224(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 56(SP),AX
+	MULQ 240(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 56(SP),AX
+	MULQ 248(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 112(DI),AX
+	MULQ 216(SP)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 64(SP),AX
+	MULQ 232(SP)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 64(SP),AX
+	MULQ 240(SP)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 64(SP),AX
+	MULQ 248(SP)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ ·REDMASK51(SB),DX
+	SHLQ $13,CX:SI
+	ANDQ DX,SI
+	SHLQ $13,R9:R8
+	ANDQ DX,R8
+	ADDQ CX,R8
+	SHLQ $13,R11:R10
+	ANDQ DX,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ DX,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ DX,R14
+	ADDQ R13,R14
+	IMUL3Q $19,R15,CX
+	ADDQ CX,SI
+	MOVQ SI,CX
+	SHRQ $51,CX
+	ADDQ R8,CX
+	MOVQ CX,R8
+	SHRQ $51,CX
+	ANDQ DX,SI
+	ADDQ R10,CX
+	MOVQ CX,R9
+	SHRQ $51,CX
+	ANDQ DX,R8
+	ADDQ R12,CX
+	MOVQ CX,AX
+	SHRQ $51,CX
+	ANDQ DX,R9
+	ADDQ R14,CX
+	MOVQ CX,R10
+	SHRQ $51,CX
+	ANDQ DX,AX
+	IMUL3Q $19,CX,CX
+	ADDQ CX,SI
+	ANDQ DX,R10
+	MOVQ SI,80(DI)
+	MOVQ R8,88(DI)
+	MOVQ R9,96(DI)
+	MOVQ AX,104(DI)
+	MOVQ R10,112(DI)
+	MOVQ 0(SP),R11
+	MOVQ 8(SP),R12
+	MOVQ 16(SP),R13
+	MOVQ 24(SP),R14
+	MOVQ 32(SP),R15
+	MOVQ 40(SP),BX
+	MOVQ 48(SP),BP
+	MOVQ R11,SP
+	MOVQ DI,AX
+	MOVQ SI,DX
+	RET
diff --git a/src/ssl/test/runner/curve25519/mont25519_amd64.go b/src/ssl/test/runner/curve25519/mont25519_amd64.go
new file mode 100644
index 0000000..5822bd5
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/mont25519_amd64.go
@@ -0,0 +1,240 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+package curve25519
+
+// These functions are implemented in the .s files. The names of the functions
+// in the rest of the file are also taken from the SUPERCOP sources to help
+// people following along.
+
+//go:noescape
+
+func cswap(inout *[5]uint64, v uint64)
+
+//go:noescape
+
+func ladderstep(inout *[5][5]uint64)
+
+//go:noescape
+
+func freeze(inout *[5]uint64)
+
+//go:noescape
+
+func mul(dest, a, b *[5]uint64)
+
+//go:noescape
+
+func square(out, in *[5]uint64)
+
+// mladder uses a Montgomery ladder to calculate (xr/zr) *= s.
+func mladder(xr, zr *[5]uint64, s *[32]byte) {
+	var work [5][5]uint64
+
+	work[0] = *xr
+	setint(&work[1], 1)
+	setint(&work[2], 0)
+	work[3] = *xr
+	setint(&work[4], 1)
+
+	j := uint(6)
+	var prevbit byte
+
+	for i := 31; i >= 0; i-- {
+		for j < 8 {
+			bit := ((*s)[i] >> j) & 1
+			swap := bit ^ prevbit
+			prevbit = bit
+			cswap(&work[1], uint64(swap))
+			ladderstep(&work)
+			j--
+		}
+		j = 7
+	}
+
+	*xr = work[1]
+	*zr = work[2]
+}
+
+func scalarMult(out, in, base *[32]byte) {
+	var e [32]byte
+	copy(e[:], (*in)[:])
+	e[0] &= 248
+	e[31] &= 127
+	e[31] |= 64
+
+	var t, z [5]uint64
+	unpack(&t, base)
+	mladder(&t, &z, &e)
+	invert(&z, &z)
+	mul(&t, &t, &z)
+	pack(out, &t)
+}
+
+func setint(r *[5]uint64, v uint64) {
+	r[0] = v
+	r[1] = 0
+	r[2] = 0
+	r[3] = 0
+	r[4] = 0
+}
+
+// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian
+// order.
+func unpack(r *[5]uint64, x *[32]byte) {
+	r[0] = uint64(x[0]) |
+		uint64(x[1])<<8 |
+		uint64(x[2])<<16 |
+		uint64(x[3])<<24 |
+		uint64(x[4])<<32 |
+		uint64(x[5])<<40 |
+		uint64(x[6]&7)<<48
+
+	r[1] = uint64(x[6])>>3 |
+		uint64(x[7])<<5 |
+		uint64(x[8])<<13 |
+		uint64(x[9])<<21 |
+		uint64(x[10])<<29 |
+		uint64(x[11])<<37 |
+		uint64(x[12]&63)<<45
+
+	r[2] = uint64(x[12])>>6 |
+		uint64(x[13])<<2 |
+		uint64(x[14])<<10 |
+		uint64(x[15])<<18 |
+		uint64(x[16])<<26 |
+		uint64(x[17])<<34 |
+		uint64(x[18])<<42 |
+		uint64(x[19]&1)<<50
+
+	r[3] = uint64(x[19])>>1 |
+		uint64(x[20])<<7 |
+		uint64(x[21])<<15 |
+		uint64(x[22])<<23 |
+		uint64(x[23])<<31 |
+		uint64(x[24])<<39 |
+		uint64(x[25]&15)<<47
+
+	r[4] = uint64(x[25])>>4 |
+		uint64(x[26])<<4 |
+		uint64(x[27])<<12 |
+		uint64(x[28])<<20 |
+		uint64(x[29])<<28 |
+		uint64(x[30])<<36 |
+		uint64(x[31]&127)<<44
+}
+
+// pack sets out = x where out is the usual, little-endian form of the 5,
+// 51-bit limbs in x.
+func pack(out *[32]byte, x *[5]uint64) {
+	t := *x
+	freeze(&t)
+
+	out[0] = byte(t[0])
+	out[1] = byte(t[0] >> 8)
+	out[2] = byte(t[0] >> 16)
+	out[3] = byte(t[0] >> 24)
+	out[4] = byte(t[0] >> 32)
+	out[5] = byte(t[0] >> 40)
+	out[6] = byte(t[0] >> 48)
+
+	out[6] ^= byte(t[1]<<3) & 0xf8
+	out[7] = byte(t[1] >> 5)
+	out[8] = byte(t[1] >> 13)
+	out[9] = byte(t[1] >> 21)
+	out[10] = byte(t[1] >> 29)
+	out[11] = byte(t[1] >> 37)
+	out[12] = byte(t[1] >> 45)
+
+	out[12] ^= byte(t[2]<<6) & 0xc0
+	out[13] = byte(t[2] >> 2)
+	out[14] = byte(t[2] >> 10)
+	out[15] = byte(t[2] >> 18)
+	out[16] = byte(t[2] >> 26)
+	out[17] = byte(t[2] >> 34)
+	out[18] = byte(t[2] >> 42)
+	out[19] = byte(t[2] >> 50)
+
+	out[19] ^= byte(t[3]<<1) & 0xfe
+	out[20] = byte(t[3] >> 7)
+	out[21] = byte(t[3] >> 15)
+	out[22] = byte(t[3] >> 23)
+	out[23] = byte(t[3] >> 31)
+	out[24] = byte(t[3] >> 39)
+	out[25] = byte(t[3] >> 47)
+
+	out[25] ^= byte(t[4]<<4) & 0xf0
+	out[26] = byte(t[4] >> 4)
+	out[27] = byte(t[4] >> 12)
+	out[28] = byte(t[4] >> 20)
+	out[29] = byte(t[4] >> 28)
+	out[30] = byte(t[4] >> 36)
+	out[31] = byte(t[4] >> 44)
+}
+
+// invert calculates r = x^-1 mod p using Fermat's little theorem.
+func invert(r *[5]uint64, x *[5]uint64) {
+	var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64
+
+	square(&z2, x)        /* 2 */
+	square(&t, &z2)       /* 4 */
+	square(&t, &t)        /* 8 */
+	mul(&z9, &t, x)       /* 9 */
+	mul(&z11, &z9, &z2)   /* 11 */
+	square(&t, &z11)      /* 22 */
+	mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */
+
+	square(&t, &z2_5_0)      /* 2^6 - 2^1 */
+	for i := 1; i < 5; i++ { /* 2^20 - 2^10 */
+		square(&t, &t)
+	}
+	mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */
+
+	square(&t, &z2_10_0)      /* 2^11 - 2^1 */
+	for i := 1; i < 10; i++ { /* 2^20 - 2^10 */
+		square(&t, &t)
+	}
+	mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */
+
+	square(&t, &z2_20_0)      /* 2^21 - 2^1 */
+	for i := 1; i < 20; i++ { /* 2^40 - 2^20 */
+		square(&t, &t)
+	}
+	mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */
+
+	square(&t, &t)            /* 2^41 - 2^1 */
+	for i := 1; i < 10; i++ { /* 2^50 - 2^10 */
+		square(&t, &t)
+	}
+	mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */
+
+	square(&t, &z2_50_0)      /* 2^51 - 2^1 */
+	for i := 1; i < 50; i++ { /* 2^100 - 2^50 */
+		square(&t, &t)
+	}
+	mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */
+
+	square(&t, &z2_100_0)      /* 2^101 - 2^1 */
+	for i := 1; i < 100; i++ { /* 2^200 - 2^100 */
+		square(&t, &t)
+	}
+	mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */
+
+	square(&t, &t)            /* 2^201 - 2^1 */
+	for i := 1; i < 50; i++ { /* 2^250 - 2^50 */
+		square(&t, &t)
+	}
+	mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */
+
+	square(&t, &t) /* 2^251 - 2^1 */
+	square(&t, &t) /* 2^252 - 2^2 */
+	square(&t, &t) /* 2^253 - 2^3 */
+
+	square(&t, &t) /* 2^254 - 2^4 */
+
+	square(&t, &t)   /* 2^255 - 2^5 */
+	mul(r, &t, &z11) /* 2^255 - 21 */
+}
diff --git a/src/ssl/test/runner/curve25519/mul_amd64.s b/src/ssl/test/runner/curve25519/mul_amd64.s
new file mode 100644
index 0000000..e48d183
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/mul_amd64.s
@@ -0,0 +1,191 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func mul(dest, a, b *[5]uint64)
+TEXT ·mul(SB),0,$128-24
+	MOVQ dest+0(FP), DI
+	MOVQ a+8(FP), SI
+	MOVQ b+16(FP), DX
+
+	MOVQ SP,R11
+	MOVQ $31,CX
+	NOTQ CX
+	ANDQ CX,SP
+	ADDQ $32,SP
+
+	MOVQ R11,0(SP)
+	MOVQ R12,8(SP)
+	MOVQ R13,16(SP)
+	MOVQ R14,24(SP)
+	MOVQ R15,32(SP)
+	MOVQ BX,40(SP)
+	MOVQ BP,48(SP)
+	MOVQ DI,56(SP)
+	MOVQ DX,CX
+	MOVQ 24(SI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,64(SP)
+	MULQ 16(CX)
+	MOVQ AX,R8
+	MOVQ DX,R9
+	MOVQ 32(SI),DX
+	IMUL3Q $19,DX,AX
+	MOVQ AX,72(SP)
+	MULQ 8(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 0(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 0(SI),AX
+	MULQ 8(CX)
+	MOVQ AX,R10
+	MOVQ DX,R11
+	MOVQ 0(SI),AX
+	MULQ 16(CX)
+	MOVQ AX,R12
+	MOVQ DX,R13
+	MOVQ 0(SI),AX
+	MULQ 24(CX)
+	MOVQ AX,R14
+	MOVQ DX,R15
+	MOVQ 0(SI),AX
+	MULQ 32(CX)
+	MOVQ AX,BX
+	MOVQ DX,BP
+	MOVQ 8(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 8(SI),AX
+	MULQ 8(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 8(SI),AX
+	MULQ 16(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 8(SI),AX
+	MULQ 24(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 8(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 16(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 16(SI),AX
+	MULQ 8(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 16(SI),AX
+	MULQ 16(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 16(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(CX)
+	ADDQ AX,R8
+	ADCQ DX,R9
+	MOVQ 16(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 24(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ 24(SI),AX
+	MULQ 8(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 64(SP),AX
+	MULQ 24(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 64(SP),AX
+	MULQ 32(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 32(SI),AX
+	MULQ 0(CX)
+	ADDQ AX,BX
+	ADCQ DX,BP
+	MOVQ 72(SP),AX
+	MULQ 16(CX)
+	ADDQ AX,R10
+	ADCQ DX,R11
+	MOVQ 72(SP),AX
+	MULQ 24(CX)
+	ADDQ AX,R12
+	ADCQ DX,R13
+	MOVQ 72(SP),AX
+	MULQ 32(CX)
+	ADDQ AX,R14
+	ADCQ DX,R15
+	MOVQ ·REDMASK51(SB),SI
+	SHLQ $13,R9:R8
+	ANDQ SI,R8
+	SHLQ $13,R11:R10
+	ANDQ SI,R10
+	ADDQ R9,R10
+	SHLQ $13,R13:R12
+	ANDQ SI,R12
+	ADDQ R11,R12
+	SHLQ $13,R15:R14
+	ANDQ SI,R14
+	ADDQ R13,R14
+	SHLQ $13,BP:BX
+	ANDQ SI,BX
+	ADDQ R15,BX
+	IMUL3Q $19,BP,DX
+	ADDQ DX,R8
+	MOVQ R8,DX
+	SHRQ $51,DX
+	ADDQ R10,DX
+	MOVQ DX,CX
+	SHRQ $51,DX
+	ANDQ SI,R8
+	ADDQ R12,DX
+	MOVQ DX,R9
+	SHRQ $51,DX
+	ANDQ SI,CX
+	ADDQ R14,DX
+	MOVQ DX,AX
+	SHRQ $51,DX
+	ANDQ SI,R9
+	ADDQ BX,DX
+	MOVQ DX,R10
+	SHRQ $51,DX
+	ANDQ SI,AX
+	IMUL3Q $19,DX,DX
+	ADDQ DX,R8
+	ANDQ SI,R10
+	MOVQ R8,0(DI)
+	MOVQ CX,8(DI)
+	MOVQ R9,16(DI)
+	MOVQ AX,24(DI)
+	MOVQ R10,32(DI)
+	MOVQ 0(SP),R11
+	MOVQ 8(SP),R12
+	MOVQ 16(SP),R13
+	MOVQ 24(SP),R14
+	MOVQ 32(SP),R15
+	MOVQ 40(SP),BX
+	MOVQ 48(SP),BP
+	MOVQ R11,SP
+	MOVQ DI,AX
+	MOVQ SI,DX
+	RET
diff --git a/src/ssl/test/runner/curve25519/square_amd64.s b/src/ssl/test/runner/curve25519/square_amd64.s
new file mode 100644
index 0000000..78d1a50
--- /dev/null
+++ b/src/ssl/test/runner/curve25519/square_amd64.s
@@ -0,0 +1,153 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func square(out, in *[5]uint64)
+TEXT ·square(SB),7,$96-16
+	MOVQ out+0(FP), DI
+	MOVQ in+8(FP), SI
+
+	MOVQ SP,R11
+	MOVQ $31,CX
+	NOTQ CX
+	ANDQ CX,SP
+	ADDQ $32, SP
+
+	MOVQ R11,0(SP)
+	MOVQ R12,8(SP)
+	MOVQ R13,16(SP)
+	MOVQ R14,24(SP)
+	MOVQ R15,32(SP)
+	MOVQ BX,40(SP)
+	MOVQ BP,48(SP)
+	MOVQ 0(SI),AX
+	MULQ 0(SI)
+	MOVQ AX,CX
+	MOVQ DX,R8
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 8(SI)
+	MOVQ AX,R9
+	MOVQ DX,R10
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 16(SI)
+	MOVQ AX,R11
+	MOVQ DX,R12
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 24(SI)
+	MOVQ AX,R13
+	MOVQ DX,R14
+	MOVQ 0(SI),AX
+	SHLQ $1,AX
+	MULQ 32(SI)
+	MOVQ AX,R15
+	MOVQ DX,BX
+	MOVQ 8(SI),AX
+	MULQ 8(SI)
+	ADDQ AX,R11
+	ADCQ DX,R12
+	MOVQ 8(SI),AX
+	SHLQ $1,AX
+	MULQ 16(SI)
+	ADDQ AX,R13
+	ADCQ DX,R14
+	MOVQ 8(SI),AX
+	SHLQ $1,AX
+	MULQ 24(SI)
+	ADDQ AX,R15
+	ADCQ DX,BX
+	MOVQ 8(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,CX
+	ADCQ DX,R8
+	MOVQ 16(SI),AX
+	MULQ 16(SI)
+	ADDQ AX,R15
+	ADCQ DX,BX
+	MOVQ 16(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 24(SI)
+	ADDQ AX,CX
+	ADCQ DX,R8
+	MOVQ 16(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,R9
+	ADCQ DX,R10
+	MOVQ 24(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 24(SI)
+	ADDQ AX,R9
+	ADCQ DX,R10
+	MOVQ 24(SI),DX
+	IMUL3Q $38,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,R11
+	ADCQ DX,R12
+	MOVQ 32(SI),DX
+	IMUL3Q $19,DX,AX
+	MULQ 32(SI)
+	ADDQ AX,R13
+	ADCQ DX,R14
+	MOVQ ·REDMASK51(SB),SI
+	SHLQ $13,R8:CX
+	ANDQ SI,CX
+	SHLQ $13,R10:R9
+	ANDQ SI,R9
+	ADDQ R8,R9
+	SHLQ $13,R12:R11
+	ANDQ SI,R11
+	ADDQ R10,R11
+	SHLQ $13,R14:R13
+	ANDQ SI,R13
+	ADDQ R12,R13
+	SHLQ $13,BX:R15
+	ANDQ SI,R15
+	ADDQ R14,R15
+	IMUL3Q $19,BX,DX
+	ADDQ DX,CX
+	MOVQ CX,DX
+	SHRQ $51,DX
+	ADDQ R9,DX
+	ANDQ SI,CX
+	MOVQ DX,R8
+	SHRQ $51,DX
+	ADDQ R11,DX
+	ANDQ SI,R8
+	MOVQ DX,R9
+	SHRQ $51,DX
+	ADDQ R13,DX
+	ANDQ SI,R9
+	MOVQ DX,AX
+	SHRQ $51,DX
+	ADDQ R15,DX
+	ANDQ SI,AX
+	MOVQ DX,R10
+	SHRQ $51,DX
+	IMUL3Q $19,DX,DX
+	ADDQ DX,CX
+	ANDQ SI,R10
+	MOVQ CX,0(DI)
+	MOVQ R8,8(DI)
+	MOVQ R9,16(DI)
+	MOVQ AX,24(DI)
+	MOVQ R10,32(DI)
+	MOVQ 0(SP),R11
+	MOVQ 8(SP),R12
+	MOVQ 16(SP),R13
+	MOVQ 24(SP),R14
+	MOVQ 32(SP),R15
+	MOVQ 40(SP),BX
+	MOVQ 48(SP),BP
+	MOVQ R11,SP
+	MOVQ DI,AX
+	MOVQ SI,DX
+	RET
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 9d8ffee..64630ba 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -98,7 +98,7 @@
 		}
 	}
 
-	if c.config.Bugs.NoRenegotiationInfo {
+	if c.noRenegotiationInfo() {
 		hello.secureRenegotiation = nil
 	}
 
@@ -282,7 +282,7 @@
 		return errors.New("tls: renegotiation extension missing")
 	}
 
-	if len(c.clientVerify) > 0 && !c.config.Bugs.NoRenegotiationInfo {
+	if len(c.clientVerify) > 0 && !c.noRenegotiationInfo() {
 		var expectedRenegInfo []byte
 		expectedRenegInfo = append(expectedRenegInfo, c.clientVerify...)
 		expectedRenegInfo = append(expectedRenegInfo, c.serverVerify...)
@@ -924,7 +924,11 @@
 
 	if !c.config.Bugs.SkipChangeCipherSpec &&
 		c.config.Bugs.EarlyChangeCipherSpec == 0 {
-		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+		ccs := []byte{1}
+		if c.config.Bugs.BadChangeCipherSpec != nil {
+			ccs = c.config.Bugs.BadChangeCipherSpec
+		}
+		c.writeRecord(recordTypeChangeCipherSpec, ccs)
 	}
 
 	if c.config.Bugs.AppDataAfterChangeCipherSpec != nil {
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 568f836..0232772 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -286,7 +286,7 @@
 		hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation
 	}
 
-	if c.config.Bugs.NoRenegotiationInfo {
+	if c.noRenegotiationInfo() {
 		hs.hello.secureRenegotiation = nil
 	}
 
@@ -914,7 +914,11 @@
 	c.dtlsFlushHandshake()
 
 	if !c.config.Bugs.SkipChangeCipherSpec {
-		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
+		ccs := []byte{1}
+		if c.config.Bugs.BadChangeCipherSpec != nil {
+			ccs = c.config.Bugs.BadChangeCipherSpec
+		}
+		c.writeRecord(recordTypeChangeCipherSpec, ccs)
 	}
 
 	if c.config.Bugs.AppDataAfterChangeCipherSpec != nil {
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 3a9b899..4f399d9 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -12,11 +12,15 @@
 	"crypto/rand"
 	"crypto/rsa"
 	"crypto/sha1"
+	"crypto/subtle"
 	"crypto/x509"
 	"encoding/asn1"
 	"errors"
+	"fmt"
 	"io"
 	"math/big"
+
+	"./curve25519"
 )
 
 var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
@@ -137,10 +141,11 @@
 }
 
 func (ka *rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+	bad := config.Bugs.BadRSAClientKeyExchange
 	preMasterSecret := make([]byte, 48)
 	vers := clientHello.vers
-	if config.Bugs.RsaClientKeyExchangeVersion != 0 {
-		vers = config.Bugs.RsaClientKeyExchangeVersion
+	if bad == RSABadValueWrongVersion {
+		vers ^= 1
 	}
 	vers = versionToWire(vers, clientHello.isDTLS)
 	preMasterSecret[0] = byte(vers >> 8)
@@ -150,12 +155,25 @@
 		return nil, nil, err
 	}
 
-	encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret)
+	sentPreMasterSecret := preMasterSecret
+	if bad == RSABadValueTooLong {
+		sentPreMasterSecret = make([]byte, len(sentPreMasterSecret)+1)
+		copy(sentPreMasterSecret, preMasterSecret)
+	} else if bad == RSABadValueTooShort {
+		sentPreMasterSecret = sentPreMasterSecret[:len(sentPreMasterSecret)-1]
+	}
+
+	encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), sentPreMasterSecret)
 	if err != nil {
 		return nil, nil, err
 	}
+	if bad == RSABadValueCorrupt {
+		encrypted[len(encrypted)-1] ^= 1
+		// Clear the high byte to ensure |encrypted| is still below the RSA modulus.
+		encrypted[0] = 0
+	}
 	ckx := new(clientKeyExchangeMsg)
-	if clientHello.vers != VersionSSL30 && !config.Bugs.SSL3RSAKeyExchange {
+	if clientHello.vers != VersionSSL30 {
 		ckx.ciphertext = make([]byte, len(encrypted)+2)
 		ckx.ciphertext[0] = byte(len(encrypted) >> 8)
 		ckx.ciphertext[1] = byte(len(encrypted))
@@ -232,16 +250,90 @@
 	return 0, errors.New("tls: client doesn't support any common hash functions")
 }
 
-func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
+// A ecdhCurve is an instance of ECDH-style key agreement for TLS.
+type ecdhCurve interface {
+	// generateKeypair generates a keypair using rand. It returns the
+	// encoded public key.
+	generateKeypair(rand io.Reader) (publicKey []byte, err error)
+
+	// computeSecret performs a key exchange against peerKey and returns
+	// the resulting shared secret.
+	computeSecret(peerKey []byte) (preMasterSecret []byte, err error)
+}
+
+// ellipticECDHCurve implements ecdhCurve with an elliptic.Curve.
+type ellipticECDHCurve struct {
+	curve      elliptic.Curve
+	privateKey []byte
+}
+
+func (e *ellipticECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) {
+	var x, y *big.Int
+	e.privateKey, x, y, err = elliptic.GenerateKey(e.curve, rand)
+	if err != nil {
+		return nil, err
+	}
+	return elliptic.Marshal(e.curve, x, y), nil
+}
+
+func (e *ellipticECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) {
+	x, y := elliptic.Unmarshal(e.curve, peerKey)
+	if x == nil {
+		return nil, errors.New("tls: invalid peer key")
+	}
+	x, _ = e.curve.ScalarMult(x, y, e.privateKey)
+	preMasterSecret = make([]byte, (e.curve.Params().BitSize+7)>>3)
+	xBytes := x.Bytes()
+	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
+
+	return preMasterSecret, nil
+}
+
+// x25519ECDHCurve implements ecdhCurve with X25519.
+type x25519ECDHCurve struct {
+	privateKey [32]byte
+}
+
+func (e *x25519ECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) {
+	_, err = io.ReadFull(rand, e.privateKey[:])
+	if err != nil {
+		return
+	}
+	var out [32]byte
+	curve25519.ScalarBaseMult(&out, &e.privateKey)
+	return out[:], nil
+}
+
+func (e *x25519ECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) {
+	if len(peerKey) != 32 {
+		return nil, errors.New("tls: invalid peer key")
+	}
+	var out, peerKeyCopy [32]byte
+	copy(peerKeyCopy[:], peerKey)
+	curve25519.ScalarMult(&out, &e.privateKey, &peerKeyCopy)
+
+	// Per draft-irtf-cfrg-curves-11, reject the all-zero value in constant
+	// time.
+	var zeros [32]byte
+	if subtle.ConstantTimeCompare(zeros[:], out[:]) == 1 {
+		return nil, errors.New("tls: X25519 value with wrong order")
+	}
+
+	return out[:], nil
+}
+
+func curveForCurveID(id CurveID) (ecdhCurve, bool) {
 	switch id {
 	case CurveP224:
-		return elliptic.P224(), true
+		return &ellipticECDHCurve{curve: elliptic.P224()}, true
 	case CurveP256:
-		return elliptic.P256(), true
+		return &ellipticECDHCurve{curve: elliptic.P256()}, true
 	case CurveP384:
-		return elliptic.P384(), true
+		return &ellipticECDHCurve{curve: elliptic.P384()}, true
 	case CurveP521:
-		return elliptic.P521(), true
+		return &ellipticECDHCurve{curve: elliptic.P521()}, true
+	case CurveX25519:
+		return &x25519ECDHCurve{}, true
 	default:
 		return nil, false
 	}
@@ -269,6 +361,24 @@
 	return nil
 }
 
+func maybeCorruptECDSAValue(n *big.Int, typeOfCorruption BadValue, limit *big.Int) *big.Int {
+	switch typeOfCorruption {
+	case BadValueNone:
+		return n
+	case BadValueNegative:
+		return new(big.Int).Neg(n)
+	case BadValueZero:
+		return big.NewInt(0)
+	case BadValueLimit:
+		return limit
+	case BadValueLarge:
+		bad := new(big.Int).Set(limit)
+		return bad.Lsh(bad, 20)
+	default:
+		panic("unknown BadValue type")
+	}
+}
+
 // signedKeyAgreement signs the ServerKeyExchange parameters with the
 // server's private key.
 type signedKeyAgreement struct {
@@ -414,28 +524,9 @@
 // pre-master secret is then calculated using ECDH. The signature may
 // either be ECDSA or RSA.
 type ecdheKeyAgreement struct {
-	auth       keyAgreementAuthentication
-	privateKey []byte
-	curve      elliptic.Curve
-	x, y       *big.Int
-}
-
-func maybeCorruptECDSAValue(n *big.Int, typeOfCorruption BadValue, limit *big.Int) *big.Int {
-	switch typeOfCorruption {
-	case BadValueNone:
-		return n
-	case BadValueNegative:
-		return new(big.Int).Neg(n)
-	case BadValueZero:
-		return big.NewInt(0)
-	case BadValueLimit:
-		return limit
-	case BadValueLarge:
-		bad := new(big.Int).Set(limit)
-		return bad.Lsh(bad, 20)
-	default:
-		panic("unknown BadValue type")
-	}
+	auth    keyAgreementAuthentication
+	curve   ecdhCurve
+	peerKey []byte
 }
 
 func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
@@ -461,24 +552,21 @@
 		return nil, errors.New("tls: preferredCurves includes unsupported curve")
 	}
 
-	var x, y *big.Int
-	var err error
-	ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand())
+	publicKey, err := ka.curve.generateKeypair(config.rand())
 	if err != nil {
 		return nil, err
 	}
-	ecdhePublic := elliptic.Marshal(ka.curve, x, y)
 
 	// http://tools.ietf.org/html/rfc4492#section-5.4
-	serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
+	serverECDHParams := make([]byte, 1+2+1+len(publicKey))
 	serverECDHParams[0] = 3 // named curve
 	serverECDHParams[1] = byte(curveid >> 8)
 	serverECDHParams[2] = byte(curveid)
 	if config.Bugs.InvalidSKXCurve {
 		serverECDHParams[2] ^= 0xff
 	}
-	serverECDHParams[3] = byte(len(ecdhePublic))
-	copy(serverECDHParams[4:], ecdhePublic)
+	serverECDHParams[3] = byte(len(publicKey))
+	copy(serverECDHParams[4:], publicKey)
 
 	return ka.auth.signParameters(config, cert, clientHello, hello, serverECDHParams)
 }
@@ -487,16 +575,7 @@
 	if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
 		return nil, errClientKeyExchange
 	}
-	x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:])
-	if x == nil {
-		return nil, errClientKeyExchange
-	}
-	x, _ = ka.curve.ScalarMult(x, y, ka.privateKey)
-	preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
-	xBytes := x.Bytes()
-	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
-
-	return preMasterSecret, nil
+	return ka.curve.computeSecret(ckx.ciphertext[1:])
 }
 
 func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
@@ -517,13 +596,12 @@
 	if publicLen+4 > len(skx.key) {
 		return errServerKeyExchange
 	}
-	ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen])
-	if ka.x == nil {
-		return errServerKeyExchange
-	}
+	// Save the peer key for later.
+	ka.peerKey = skx.key[4 : 4+publicLen]
+
+	// Check the signature.
 	serverECDHParams := skx.key[:4+publicLen]
 	sig := skx.key[4+publicLen:]
-
 	return ka.auth.verifyParameters(config, clientHello, serverHello, cert, serverECDHParams, sig)
 }
 
@@ -531,21 +609,20 @@
 	if ka.curve == nil {
 		return nil, nil, errors.New("missing ServerKeyExchange message")
 	}
-	priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand())
+
+	publicKey, err := ka.curve.generateKeypair(config.rand())
 	if err != nil {
 		return nil, nil, err
 	}
-	x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv)
-	preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3)
-	xBytes := x.Bytes()
-	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
-
-	serialized := elliptic.Marshal(ka.curve, mx, my)
+	preMasterSecret, err := ka.curve.computeSecret(ka.peerKey)
+	if err != nil {
+		return nil, nil, err
+	}
 
 	ckx := new(clientKeyExchangeMsg)
-	ckx.ciphertext = make([]byte, 1+len(serialized))
-	ckx.ciphertext[0] = byte(len(serialized))
-	copy(ckx.ciphertext[1:], serialized)
+	ckx.ciphertext = make([]byte, 1+len(publicKey))
+	ckx.ciphertext[0] = byte(len(publicKey))
+	copy(ckx.ciphertext[1:], publicKey)
 
 	return preMasterSecret, ckx, nil
 }
@@ -652,6 +729,10 @@
 		return errServerKeyExchange
 	}
 
+	if l := config.Bugs.RequireDHPublicValueLen; l != 0 && l != yLen {
+		return fmt.Errorf("RequireDHPublicValueLen set to %d, but server's public value was %d bytes on the wire and %d bytes if minimal", l, yLen, (ka.yTheirs.BitLen()+7)/8)
+	}
+
 	sig := k
 	serverDHParams := skx.key[:len(skx.key)-len(sig)]
 
diff --git a/src/ssl/test/runner/packet_adapter.go b/src/ssl/test/runner/packet_adapter.go
index 2351eb0..a8da311 100644
--- a/src/ssl/test/runner/packet_adapter.go
+++ b/src/ssl/test/runner/packet_adapter.go
@@ -96,7 +96,7 @@
 // for acknowledgement of the timeout, buffering any packets received since
 // then. The packets are then returned.
 func (p *packetAdaptor) SendReadTimeout(d time.Duration) ([][]byte, error) {
-	p.log("Simulating read timeout: " + d.String(), nil)
+	p.log("Simulating read timeout: "+d.String(), nil)
 
 	payload := make([]byte, 1+8)
 	payload[0] = opcodeTimeout
diff --git a/src/ssl/test/runner/poly1305/const_amd64.s b/src/ssl/test/runner/poly1305/const_amd64.s
new file mode 100644
index 0000000..8e861f3
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/const_amd64.s
@@ -0,0 +1,45 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+DATA ·SCALE(SB)/8, $0x37F4000000000000
+GLOBL ·SCALE(SB), 8, $8
+DATA ·TWO32(SB)/8, $0x41F0000000000000
+GLOBL ·TWO32(SB), 8, $8
+DATA ·TWO64(SB)/8, $0x43F0000000000000
+GLOBL ·TWO64(SB), 8, $8
+DATA ·TWO96(SB)/8, $0x45F0000000000000
+GLOBL ·TWO96(SB), 8, $8
+DATA ·ALPHA32(SB)/8, $0x45E8000000000000
+GLOBL ·ALPHA32(SB), 8, $8
+DATA ·ALPHA64(SB)/8, $0x47E8000000000000
+GLOBL ·ALPHA64(SB), 8, $8
+DATA ·ALPHA96(SB)/8, $0x49E8000000000000
+GLOBL ·ALPHA96(SB), 8, $8
+DATA ·ALPHA130(SB)/8, $0x4C08000000000000
+GLOBL ·ALPHA130(SB), 8, $8
+DATA ·DOFFSET0(SB)/8, $0x4330000000000000
+GLOBL ·DOFFSET0(SB), 8, $8
+DATA ·DOFFSET1(SB)/8, $0x4530000000000000
+GLOBL ·DOFFSET1(SB), 8, $8
+DATA ·DOFFSET2(SB)/8, $0x4730000000000000
+GLOBL ·DOFFSET2(SB), 8, $8
+DATA ·DOFFSET3(SB)/8, $0x4930000000000000
+GLOBL ·DOFFSET3(SB), 8, $8
+DATA ·DOFFSET3MINUSTWO128(SB)/8, $0x492FFFFE00000000
+GLOBL ·DOFFSET3MINUSTWO128(SB), 8, $8
+DATA ·HOFFSET0(SB)/8, $0x43300001FFFFFFFB
+GLOBL ·HOFFSET0(SB), 8, $8
+DATA ·HOFFSET1(SB)/8, $0x45300001FFFFFFFE
+GLOBL ·HOFFSET1(SB), 8, $8
+DATA ·HOFFSET2(SB)/8, $0x47300001FFFFFFFE
+GLOBL ·HOFFSET2(SB), 8, $8
+DATA ·HOFFSET3(SB)/8, $0x49300003FFFFFFFE
+GLOBL ·HOFFSET3(SB), 8, $8
+DATA ·ROUNDING(SB)/2, $0x137f
+GLOBL ·ROUNDING(SB), 8, $2
diff --git a/src/ssl/test/runner/poly1305/poly1305.go b/src/ssl/test/runner/poly1305/poly1305.go
new file mode 100644
index 0000000..4a5f826
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/poly1305.go
@@ -0,0 +1,32 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Package poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf.
+
+Poly1305 is a fast, one-time authentication function. It is infeasible for an
+attacker to generate an authenticator for a message without the key. However, a
+key must only be used for a single message. Authenticating two different
+messages with the same key allows an attacker to forge authenticators for other
+messages with the same key.
+
+Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
+used with a fixed key in order to generate one-time keys from an nonce.
+However, in this package AES isn't used and the one-time key is specified
+directly.
+*/
+package poly1305 // import "golang.org/x/crypto/poly1305"
+
+import "crypto/subtle"
+
+// TagSize is the size, in bytes, of a poly1305 authenticator.
+const TagSize = 16
+
+// Verify returns true if mac is a valid authenticator for m with the given
+// key.
+func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
+	var tmp [16]byte
+	Sum(&tmp, m, key)
+	return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
+}
diff --git a/src/ssl/test/runner/poly1305/poly1305_amd64.s b/src/ssl/test/runner/poly1305/poly1305_amd64.s
new file mode 100644
index 0000000..f8d4ee9
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/poly1305_amd64.s
@@ -0,0 +1,497 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 6a from the public
+// domain sources in SUPERCOP: http://bench.cr.yp.to/supercop.html
+
+// +build amd64,!gccgo,!appengine
+
+// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
+TEXT ·poly1305(SB),0,$224-32
+	MOVQ out+0(FP),DI
+	MOVQ m+8(FP),SI
+	MOVQ mlen+16(FP),DX
+	MOVQ key+24(FP),CX
+
+	MOVQ SP,R11
+	MOVQ $31,R9
+	NOTQ R9
+	ANDQ R9,SP
+	ADDQ $32,SP
+
+	MOVQ R11,32(SP)
+	MOVQ R12,40(SP)
+	MOVQ R13,48(SP)
+	MOVQ R14,56(SP)
+	MOVQ R15,64(SP)
+	MOVQ BX,72(SP)
+	MOVQ BP,80(SP)
+	FLDCW ·ROUNDING(SB)
+	MOVL 0(CX),R8
+	MOVL 4(CX),R9
+	MOVL 8(CX),AX
+	MOVL 12(CX),R10
+	MOVQ DI,88(SP)
+	MOVQ CX,96(SP)
+	MOVL $0X43300000,108(SP)
+	MOVL $0X45300000,116(SP)
+	MOVL $0X47300000,124(SP)
+	MOVL $0X49300000,132(SP)
+	ANDL $0X0FFFFFFF,R8
+	ANDL $0X0FFFFFFC,R9
+	ANDL $0X0FFFFFFC,AX
+	ANDL $0X0FFFFFFC,R10
+	MOVL R8,104(SP)
+	MOVL R9,112(SP)
+	MOVL AX,120(SP)
+	MOVL R10,128(SP)
+	FMOVD 104(SP), F0
+	FSUBD ·DOFFSET0(SB), F0
+	FMOVD 112(SP), F0
+	FSUBD ·DOFFSET1(SB), F0
+	FMOVD 120(SP), F0
+	FSUBD ·DOFFSET2(SB), F0
+	FMOVD 128(SP), F0
+	FSUBD ·DOFFSET3(SB), F0
+	FXCHD F0, F3
+	FMOVDP F0, 136(SP)
+	FXCHD F0, F1
+	FMOVD F0, 144(SP)
+	FMULD ·SCALE(SB), F0
+	FMOVDP F0, 152(SP)
+	FMOVD F0, 160(SP)
+	FMULD ·SCALE(SB), F0
+	FMOVDP F0, 168(SP)
+	FMOVD F0, 176(SP)
+	FMULD ·SCALE(SB), F0
+	FMOVDP F0, 184(SP)
+	FLDZ
+	FLDZ
+	FLDZ
+	FLDZ
+	CMPQ DX,$16
+	JB ADDATMOST15BYTES
+	INITIALATLEAST16BYTES:
+	MOVL 12(SI),DI
+	MOVL 8(SI),CX
+	MOVL 4(SI),R8
+	MOVL 0(SI),R9
+	MOVL DI,128(SP)
+	MOVL CX,120(SP)
+	MOVL R8,112(SP)
+	MOVL R9,104(SP)
+	ADDQ $16,SI
+	SUBQ $16,DX
+	FXCHD F0, F3
+	FADDD 128(SP), F0
+	FSUBD ·DOFFSET3MINUSTWO128(SB), F0
+	FXCHD F0, F1
+	FADDD 112(SP), F0
+	FSUBD ·DOFFSET1(SB), F0
+	FXCHD F0, F2
+	FADDD 120(SP), F0
+	FSUBD ·DOFFSET2(SB), F0
+	FXCHD F0, F3
+	FADDD 104(SP), F0
+	FSUBD ·DOFFSET0(SB), F0
+	CMPQ DX,$16
+	JB MULTIPLYADDATMOST15BYTES
+	MULTIPLYADDATLEAST16BYTES:
+	MOVL 12(SI),DI
+	MOVL 8(SI),CX
+	MOVL 4(SI),R8
+	MOVL 0(SI),R9
+	MOVL DI,128(SP)
+	MOVL CX,120(SP)
+	MOVL R8,112(SP)
+	MOVL R9,104(SP)
+	ADDQ $16,SI
+	SUBQ $16,DX
+	FMOVD ·ALPHA130(SB), F0
+	FADDD F2,F0
+	FSUBD ·ALPHA130(SB), F0
+	FSUBD F0,F2
+	FMULD ·SCALE(SB), F0
+	FMOVD ·ALPHA32(SB), F0
+	FADDD F2,F0
+	FSUBD ·ALPHA32(SB), F0
+	FSUBD F0,F2
+	FXCHD F0, F2
+	FADDDP F0,F1
+	FMOVD ·ALPHA64(SB), F0
+	FADDD F4,F0
+	FSUBD ·ALPHA64(SB), F0
+	FSUBD F0,F4
+	FMOVD ·ALPHA96(SB), F0
+	FADDD F6,F0
+	FSUBD ·ALPHA96(SB), F0
+	FSUBD F0,F6
+	FXCHD F0, F6
+	FADDDP F0,F1
+	FXCHD F0, F3
+	FADDDP F0,F5
+	FXCHD F0, F3
+	FADDDP F0,F1
+	FMOVD 176(SP), F0
+	FMULD F3,F0
+	FMOVD 160(SP), F0
+	FMULD F4,F0
+	FMOVD 144(SP), F0
+	FMULD F5,F0
+	FMOVD 136(SP), F0
+	FMULDP F0,F6
+	FMOVD 160(SP), F0
+	FMULD F4,F0
+	FADDDP F0,F3
+	FMOVD 144(SP), F0
+	FMULD F4,F0
+	FADDDP F0,F2
+	FMOVD 136(SP), F0
+	FMULD F4,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULDP F0,F4
+	FXCHD F0, F3
+	FADDDP F0,F5
+	FMOVD 144(SP), F0
+	FMULD F4,F0
+	FADDDP F0,F2
+	FMOVD 136(SP), F0
+	FMULD F4,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULD F4,F0
+	FADDDP F0,F3
+	FMOVD 168(SP), F0
+	FMULDP F0,F4
+	FXCHD F0, F3
+	FADDDP F0,F4
+	FMOVD 136(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F1
+	FXCHD F0, F3
+	FMOVD 184(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F3
+	FXCHD F0, F1
+	FMOVD 168(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F1
+	FMOVD 152(SP), F0
+	FMULDP F0,F5
+	FXCHD F0, F4
+	FADDDP F0,F1
+	CMPQ DX,$16
+	FXCHD F0, F2
+	FMOVD 128(SP), F0
+	FSUBD ·DOFFSET3MINUSTWO128(SB), F0
+	FADDDP F0,F1
+	FXCHD F0, F1
+	FMOVD 120(SP), F0
+	FSUBD ·DOFFSET2(SB), F0
+	FADDDP F0,F1
+	FXCHD F0, F3
+	FMOVD 112(SP), F0
+	FSUBD ·DOFFSET1(SB), F0
+	FADDDP F0,F1
+	FXCHD F0, F2
+	FMOVD 104(SP), F0
+	FSUBD ·DOFFSET0(SB), F0
+	FADDDP F0,F1
+	JAE MULTIPLYADDATLEAST16BYTES
+	MULTIPLYADDATMOST15BYTES:
+	FMOVD ·ALPHA130(SB), F0
+	FADDD F2,F0
+	FSUBD ·ALPHA130(SB), F0
+	FSUBD F0,F2
+	FMULD ·SCALE(SB), F0
+	FMOVD ·ALPHA32(SB), F0
+	FADDD F2,F0
+	FSUBD ·ALPHA32(SB), F0
+	FSUBD F0,F2
+	FMOVD ·ALPHA64(SB), F0
+	FADDD F5,F0
+	FSUBD ·ALPHA64(SB), F0
+	FSUBD F0,F5
+	FMOVD ·ALPHA96(SB), F0
+	FADDD F7,F0
+	FSUBD ·ALPHA96(SB), F0
+	FSUBD F0,F7
+	FXCHD F0, F7
+	FADDDP F0,F1
+	FXCHD F0, F5
+	FADDDP F0,F1
+	FXCHD F0, F3
+	FADDDP F0,F5
+	FADDDP F0,F1
+	FMOVD 176(SP), F0
+	FMULD F1,F0
+	FMOVD 160(SP), F0
+	FMULD F2,F0
+	FMOVD 144(SP), F0
+	FMULD F3,F0
+	FMOVD 136(SP), F0
+	FMULDP F0,F4
+	FMOVD 160(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F3
+	FMOVD 144(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F2
+	FMOVD 136(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULDP F0,F5
+	FXCHD F0, F4
+	FADDDP F0,F3
+	FMOVD 144(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F2
+	FMOVD 136(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F4
+	FMOVD 168(SP), F0
+	FMULDP F0,F5
+	FXCHD F0, F4
+	FADDDP F0,F2
+	FMOVD 136(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F4
+	FMOVD 168(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F3
+	FMOVD 152(SP), F0
+	FMULDP F0,F5
+	FXCHD F0, F4
+	FADDDP F0,F1
+	ADDATMOST15BYTES:
+	CMPQ DX,$0
+	JE NOMOREBYTES
+	MOVL $0,0(SP)
+	MOVL $0, 4 (SP)
+	MOVL $0, 8 (SP)
+	MOVL $0, 12 (SP)
+	LEAQ 0(SP),DI
+	MOVQ DX,CX
+	REP; MOVSB
+	MOVB $1,0(DI)
+	MOVL  12 (SP),DI
+	MOVL  8 (SP),SI
+	MOVL  4 (SP),DX
+	MOVL 0(SP),CX
+	MOVL DI,128(SP)
+	MOVL SI,120(SP)
+	MOVL DX,112(SP)
+	MOVL CX,104(SP)
+	FXCHD F0, F3
+	FADDD 128(SP), F0
+	FSUBD ·DOFFSET3(SB), F0
+	FXCHD F0, F2
+	FADDD 120(SP), F0
+	FSUBD ·DOFFSET2(SB), F0
+	FXCHD F0, F1
+	FADDD 112(SP), F0
+	FSUBD ·DOFFSET1(SB), F0
+	FXCHD F0, F3
+	FADDD 104(SP), F0
+	FSUBD ·DOFFSET0(SB), F0
+	FMOVD ·ALPHA130(SB), F0
+	FADDD F3,F0
+	FSUBD ·ALPHA130(SB), F0
+	FSUBD F0,F3
+	FMULD ·SCALE(SB), F0
+	FMOVD ·ALPHA32(SB), F0
+	FADDD F2,F0
+	FSUBD ·ALPHA32(SB), F0
+	FSUBD F0,F2
+	FMOVD ·ALPHA64(SB), F0
+	FADDD F6,F0
+	FSUBD ·ALPHA64(SB), F0
+	FSUBD F0,F6
+	FMOVD ·ALPHA96(SB), F0
+	FADDD F5,F0
+	FSUBD ·ALPHA96(SB), F0
+	FSUBD F0,F5
+	FXCHD F0, F4
+	FADDDP F0,F3
+	FXCHD F0, F6
+	FADDDP F0,F1
+	FXCHD F0, F3
+	FADDDP F0,F5
+	FXCHD F0, F3
+	FADDDP F0,F1
+	FMOVD 176(SP), F0
+	FMULD F3,F0
+	FMOVD 160(SP), F0
+	FMULD F4,F0
+	FMOVD 144(SP), F0
+	FMULD F5,F0
+	FMOVD 136(SP), F0
+	FMULDP F0,F6
+	FMOVD 160(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F3
+	FMOVD 144(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F2
+	FMOVD 136(SP), F0
+	FMULD F5,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULDP F0,F5
+	FXCHD F0, F4
+	FADDDP F0,F5
+	FMOVD 144(SP), F0
+	FMULD F6,F0
+	FADDDP F0,F2
+	FMOVD 136(SP), F0
+	FMULD F6,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULD F6,F0
+	FADDDP F0,F4
+	FMOVD 168(SP), F0
+	FMULDP F0,F6
+	FXCHD F0, F5
+	FADDDP F0,F4
+	FMOVD 136(SP), F0
+	FMULD F2,F0
+	FADDDP F0,F1
+	FMOVD 184(SP), F0
+	FMULD F2,F0
+	FADDDP F0,F5
+	FMOVD 168(SP), F0
+	FMULD F2,F0
+	FADDDP F0,F3
+	FMOVD 152(SP), F0
+	FMULDP F0,F2
+	FXCHD F0, F1
+	FADDDP F0,F3
+	FXCHD F0, F3
+	FXCHD F0, F2
+	NOMOREBYTES:
+	MOVL $0,R10
+	FMOVD ·ALPHA130(SB), F0
+	FADDD F4,F0
+	FSUBD ·ALPHA130(SB), F0
+	FSUBD F0,F4
+	FMULD ·SCALE(SB), F0
+	FMOVD ·ALPHA32(SB), F0
+	FADDD F2,F0
+	FSUBD ·ALPHA32(SB), F0
+	FSUBD F0,F2
+	FMOVD ·ALPHA64(SB), F0
+	FADDD F4,F0
+	FSUBD ·ALPHA64(SB), F0
+	FSUBD F0,F4
+	FMOVD ·ALPHA96(SB), F0
+	FADDD F6,F0
+	FSUBD ·ALPHA96(SB), F0
+	FXCHD F0, F6
+	FSUBD F6,F0
+	FXCHD F0, F4
+	FADDDP F0,F3
+	FXCHD F0, F4
+	FADDDP F0,F1
+	FXCHD F0, F2
+	FADDDP F0,F3
+	FXCHD F0, F4
+	FADDDP F0,F3
+	FXCHD F0, F3
+	FADDD ·HOFFSET0(SB), F0
+	FXCHD F0, F3
+	FADDD ·HOFFSET1(SB), F0
+	FXCHD F0, F1
+	FADDD ·HOFFSET2(SB), F0
+	FXCHD F0, F2
+	FADDD ·HOFFSET3(SB), F0
+	FXCHD F0, F3
+	FMOVDP F0, 104(SP)
+	FMOVDP F0, 112(SP)
+	FMOVDP F0, 120(SP)
+	FMOVDP F0, 128(SP)
+	MOVL 108(SP),DI
+	ANDL $63,DI
+	MOVL 116(SP),SI
+	ANDL $63,SI
+	MOVL 124(SP),DX
+	ANDL $63,DX
+	MOVL 132(SP),CX
+	ANDL $63,CX
+	MOVL 112(SP),R8
+	ADDL DI,R8
+	MOVQ R8,112(SP)
+	MOVL 120(SP),DI
+	ADCL SI,DI
+	MOVQ DI,120(SP)
+	MOVL 128(SP),DI
+	ADCL DX,DI
+	MOVQ DI,128(SP)
+	MOVL R10,DI
+	ADCL CX,DI
+	MOVQ DI,136(SP)
+	MOVQ $5,DI
+	MOVL 104(SP),SI
+	ADDL SI,DI
+	MOVQ DI,104(SP)
+	MOVL R10,DI
+	MOVQ 112(SP),DX
+	ADCL DX,DI
+	MOVQ DI,112(SP)
+	MOVL R10,DI
+	MOVQ 120(SP),CX
+	ADCL CX,DI
+	MOVQ DI,120(SP)
+	MOVL R10,DI
+	MOVQ 128(SP),R8
+	ADCL R8,DI
+	MOVQ DI,128(SP)
+	MOVQ $0XFFFFFFFC,DI
+	MOVQ 136(SP),R9
+	ADCL R9,DI
+	SARL $16,DI
+	MOVQ DI,R9
+	XORL $0XFFFFFFFF,R9
+	ANDQ DI,SI
+	MOVQ 104(SP),AX
+	ANDQ R9,AX
+	ORQ AX,SI
+	ANDQ DI,DX
+	MOVQ 112(SP),AX
+	ANDQ R9,AX
+	ORQ AX,DX
+	ANDQ DI,CX
+	MOVQ 120(SP),AX
+	ANDQ R9,AX
+	ORQ AX,CX
+	ANDQ DI,R8
+	MOVQ 128(SP),DI
+	ANDQ R9,DI
+	ORQ DI,R8
+	MOVQ 88(SP),DI
+	MOVQ 96(SP),R9
+	ADDL 16(R9),SI
+	ADCL 20(R9),DX
+	ADCL 24(R9),CX
+	ADCL 28(R9),R8
+	MOVL SI,0(DI)
+	MOVL DX,4(DI)
+	MOVL CX,8(DI)
+	MOVL R8,12(DI)
+	MOVQ 32(SP),R11
+	MOVQ 40(SP),R12
+	MOVQ 48(SP),R13
+	MOVQ 56(SP),R14
+	MOVQ 64(SP),R15
+	MOVQ 72(SP),BX
+	MOVQ 80(SP),BP
+	MOVQ R11,SP
+	RET
diff --git a/src/ssl/test/runner/poly1305/poly1305_arm.s b/src/ssl/test/runner/poly1305/poly1305_arm.s
new file mode 100644
index 0000000..c153867
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/poly1305_arm.s
@@ -0,0 +1,379 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This code was translated into a form compatible with 5a from the public
+// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
+
+// +build arm,!gccgo,!appengine
+
+DATA poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
+DATA poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
+DATA poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
+DATA poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
+DATA poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
+GLOBL poly1305_init_constants_armv6<>(SB), 8, $20
+
+// Warning: the linker may use R11 to synthesize certain instructions. Please
+// take care and verify that no synthetic instructions use it.
+
+TEXT poly1305_init_ext_armv6<>(SB),4,$-4
+  MOVM.DB.W [R4-R11], (R13)
+  MOVM.IA.W (R1), [R2-R5]
+  MOVW $poly1305_init_constants_armv6<>(SB), R7
+  MOVW R2, R8
+  MOVW R2>>26, R9
+  MOVW R3>>20, g
+  MOVW R4>>14, R11
+  MOVW R5>>8, R12
+  ORR R3<<6, R9, R9
+  ORR R4<<12, g, g
+  ORR R5<<18, R11, R11
+  MOVM.IA (R7), [R2-R6]
+  AND R8, R2, R2
+  AND R9, R3, R3
+  AND g, R4, R4
+  AND R11, R5, R5
+  AND R12, R6, R6
+  MOVM.IA.W [R2-R6], (R0)
+  EOR R2, R2, R2
+  EOR R3, R3, R3
+  EOR R4, R4, R4
+  EOR R5, R5, R5
+  EOR R6, R6, R6
+  MOVM.IA.W [R2-R6], (R0)
+  MOVM.IA.W (R1), [R2-R5]
+  MOVM.IA [R2-R6], (R0)
+  MOVM.IA.W (R13), [R4-R11]
+  RET
+
+#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
+  MOVBU (offset+0)(Rsrc), Rtmp; \
+  MOVBU Rtmp, (offset+0)(Rdst); \
+  MOVBU (offset+1)(Rsrc), Rtmp; \
+  MOVBU Rtmp, (offset+1)(Rdst); \
+  MOVBU (offset+2)(Rsrc), Rtmp; \
+  MOVBU Rtmp, (offset+2)(Rdst); \
+  MOVBU (offset+3)(Rsrc), Rtmp; \
+  MOVBU Rtmp, (offset+3)(Rdst)
+
+TEXT poly1305_blocks_armv6<>(SB),4,$-4
+  MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13)
+  SUB $128, R13
+  MOVW R0, 36(R13)
+  MOVW R1, 40(R13)
+  MOVW R2, 44(R13)
+  MOVW R1, R14
+  MOVW R2, R12
+  MOVW 56(R0), R8
+  WORD $0xe1180008 // TST R8, R8 not working see issue 5921
+  EOR R6, R6, R6
+  MOVW.EQ $(1<<24), R6
+  MOVW R6, 32(R13)
+  ADD $64, R13, g
+  MOVM.IA (R0), [R0-R9]
+  MOVM.IA [R0-R4], (g)
+  CMP $16, R12
+  BLO poly1305_blocks_armv6_done
+poly1305_blocks_armv6_mainloop:
+  WORD $0xe31e0003 // TST R14, #3 not working see issue 5921
+  BEQ poly1305_blocks_armv6_mainloop_aligned
+  ADD $48, R13, g
+  MOVW_UNALIGNED(R14, g, R0, 0)
+  MOVW_UNALIGNED(R14, g, R0, 4)
+  MOVW_UNALIGNED(R14, g, R0, 8)
+  MOVW_UNALIGNED(R14, g, R0, 12)
+  MOVM.IA (g), [R0-R3]
+  ADD $16, R14
+  B poly1305_blocks_armv6_mainloop_loaded
+poly1305_blocks_armv6_mainloop_aligned:
+  MOVM.IA.W (R14), [R0-R3]
+poly1305_blocks_armv6_mainloop_loaded:
+  MOVW R0>>26, g
+  MOVW R1>>20, R11
+  MOVW R2>>14, R12
+  MOVW R14, 40(R13)
+  MOVW R3>>8, R4
+  ORR R1<<6, g, g
+  ORR R2<<12, R11, R11
+  ORR R3<<18, R12, R12
+  BIC $0xfc000000, R0, R0
+  BIC $0xfc000000, g, g
+  MOVW 32(R13), R3
+  BIC $0xfc000000, R11, R11
+  BIC $0xfc000000, R12, R12
+  ADD R0, R5, R5
+  ADD g, R6, R6
+  ORR R3, R4, R4
+  ADD R11, R7, R7
+  ADD $64, R13, R14
+  ADD R12, R8, R8
+  ADD R4, R9, R9
+  MOVM.IA (R14), [R0-R4]
+  MULLU R4, R5, (R11, g)
+  MULLU R3, R5, (R14, R12)
+  MULALU R3, R6, (R11, g)
+  MULALU R2, R6, (R14, R12)
+  MULALU R2, R7, (R11, g)
+  MULALU R1, R7, (R14, R12)
+  ADD R4<<2, R4, R4
+  ADD R3<<2, R3, R3
+  MULALU R1, R8, (R11, g)
+  MULALU R0, R8, (R14, R12)
+  MULALU R0, R9, (R11, g)
+  MULALU R4, R9, (R14, R12)
+  MOVW g, 24(R13)
+  MOVW R11, 28(R13)
+  MOVW R12, 16(R13)
+  MOVW R14, 20(R13)
+  MULLU R2, R5, (R11, g)
+  MULLU R1, R5, (R14, R12)
+  MULALU R1, R6, (R11, g)
+  MULALU R0, R6, (R14, R12)
+  MULALU R0, R7, (R11, g)
+  MULALU R4, R7, (R14, R12)
+  ADD R2<<2, R2, R2
+  ADD R1<<2, R1, R1
+  MULALU R4, R8, (R11, g)
+  MULALU R3, R8, (R14, R12)
+  MULALU R3, R9, (R11, g)
+  MULALU R2, R9, (R14, R12)
+  MOVW g, 8(R13)
+  MOVW R11, 12(R13)
+  MOVW R12, 0(R13)
+  MOVW R14, w+4(SP)
+  MULLU R0, R5, (R11, g)
+  MULALU R4, R6, (R11, g)
+  MULALU R3, R7, (R11, g)
+  MULALU R2, R8, (R11, g)
+  MULALU R1, R9, (R11, g)
+  MOVM.IA (R13), [R0-R7]
+  MOVW g>>26, R12
+  MOVW R4>>26, R14
+  ORR R11<<6, R12, R12
+  ORR R5<<6, R14, R14
+  BIC $0xfc000000, g, g
+  BIC $0xfc000000, R4, R4
+  ADD.S R12, R0, R0
+  ADC $0, R1, R1
+  ADD.S R14, R6, R6
+  ADC $0, R7, R7
+  MOVW R0>>26, R12
+  MOVW R6>>26, R14
+  ORR R1<<6, R12, R12
+  ORR R7<<6, R14, R14
+  BIC $0xfc000000, R0, R0
+  BIC $0xfc000000, R6, R6
+  ADD R14<<2, R14, R14
+  ADD.S R12, R2, R2
+  ADC $0, R3, R3
+  ADD R14, g, g
+  MOVW R2>>26, R12
+  MOVW g>>26, R14
+  ORR R3<<6, R12, R12
+  BIC $0xfc000000, g, R5
+  BIC $0xfc000000, R2, R7
+  ADD R12, R4, R4
+  ADD R14, R0, R0
+  MOVW R4>>26, R12
+  BIC $0xfc000000, R4, R8
+  ADD R12, R6, R9
+  MOVW w+44(SP), R12
+  MOVW w+40(SP), R14
+  MOVW R0, R6
+  CMP $32, R12
+  SUB $16, R12, R12
+  MOVW R12, 44(R13)
+  BHS poly1305_blocks_armv6_mainloop
+poly1305_blocks_armv6_done:
+  MOVW 36(R13), R12
+  MOVW R5, 20(R12)
+  MOVW R6, 24(R12)
+  MOVW R7, 28(R12)
+  MOVW R8, 32(R12)
+  MOVW R9, 36(R12)
+  ADD $128, R13, R13
+  MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, g, R11, R14]
+  RET
+
+#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+  MOVBU.P 1(Rsrc), Rtmp; \
+  MOVBU.P Rtmp, 1(Rdst); \
+  MOVBU.P 1(Rsrc), Rtmp; \
+  MOVBU.P Rtmp, 1(Rdst)
+
+#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+  MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
+  MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
+
+TEXT poly1305_finish_ext_armv6<>(SB),4,$-4
+  MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13)
+  SUB $16, R13, R13
+  MOVW R0, R5
+  MOVW R1, R6
+  MOVW R2, R7
+  MOVW R3, R8
+  AND.S R2, R2, R2
+  BEQ poly1305_finish_ext_armv6_noremaining
+  EOR R0, R0
+  MOVW R13, R9
+  MOVW R0, 0(R13)
+  MOVW R0, 4(R13)
+  MOVW R0, 8(R13)
+  MOVW R0, 12(R13)
+  WORD $0xe3110003 // TST R1, #3 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_aligned
+  WORD $0xe3120008 // TST R2, #8 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_skip8
+  MOVWP_UNALIGNED(R1, R9, g)
+  MOVWP_UNALIGNED(R1, R9, g)
+poly1305_finish_ext_armv6_skip8:
+  WORD $0xe3120004 // TST $4, R2 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_skip4
+  MOVWP_UNALIGNED(R1, R9, g)
+poly1305_finish_ext_armv6_skip4:
+  WORD $0xe3120002 // TST $2, R2 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_skip2
+  MOVHUP_UNALIGNED(R1, R9, g)
+  B poly1305_finish_ext_armv6_skip2
+poly1305_finish_ext_armv6_aligned:
+  WORD $0xe3120008 // TST R2, #8 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_skip8_aligned
+  MOVM.IA.W (R1), [g-R11]
+  MOVM.IA.W [g-R11], (R9)
+poly1305_finish_ext_armv6_skip8_aligned:
+  WORD $0xe3120004 // TST $4, R2 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_skip4_aligned
+  MOVW.P 4(R1), g
+  MOVW.P g, 4(R9)
+poly1305_finish_ext_armv6_skip4_aligned:
+  WORD $0xe3120002 // TST $2, R2 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_skip2
+  MOVHU.P 2(R1), g
+  MOVH.P g, 2(R9)
+poly1305_finish_ext_armv6_skip2:
+  WORD $0xe3120001 // TST $1, R2 not working see issue 5921
+  BEQ poly1305_finish_ext_armv6_skip1
+  MOVBU.P 1(R1), g
+  MOVBU.P g, 1(R9)
+poly1305_finish_ext_armv6_skip1:
+  MOVW $1, R11
+  MOVBU R11, 0(R9)
+  MOVW R11, 56(R5)
+  MOVW R5, R0
+  MOVW R13, R1
+  MOVW $16, R2
+  BL poly1305_blocks_armv6<>(SB)
+poly1305_finish_ext_armv6_noremaining:
+  MOVW 20(R5), R0
+  MOVW 24(R5), R1
+  MOVW 28(R5), R2
+  MOVW 32(R5), R3
+  MOVW 36(R5), R4
+  MOVW R4>>26, R12
+  BIC $0xfc000000, R4, R4
+  ADD R12<<2, R12, R12
+  ADD R12, R0, R0
+  MOVW R0>>26, R12
+  BIC $0xfc000000, R0, R0
+  ADD R12, R1, R1
+  MOVW R1>>26, R12
+  BIC $0xfc000000, R1, R1
+  ADD R12, R2, R2
+  MOVW R2>>26, R12
+  BIC $0xfc000000, R2, R2
+  ADD R12, R3, R3
+  MOVW R3>>26, R12
+  BIC $0xfc000000, R3, R3
+  ADD R12, R4, R4
+  ADD $5, R0, R6
+  MOVW R6>>26, R12
+  BIC $0xfc000000, R6, R6
+  ADD R12, R1, R7
+  MOVW R7>>26, R12
+  BIC $0xfc000000, R7, R7
+  ADD R12, R2, g
+  MOVW g>>26, R12
+  BIC $0xfc000000, g, g
+  ADD R12, R3, R11
+  MOVW $-(1<<26), R12
+  ADD R11>>26, R12, R12
+  BIC $0xfc000000, R11, R11
+  ADD R12, R4, R14
+  MOVW R14>>31, R12
+  SUB $1, R12
+  AND R12, R6, R6
+  AND R12, R7, R7
+  AND R12, g, g
+  AND R12, R11, R11
+  AND R12, R14, R14
+  MVN R12, R12
+  AND R12, R0, R0
+  AND R12, R1, R1
+  AND R12, R2, R2
+  AND R12, R3, R3
+  AND R12, R4, R4
+  ORR R6, R0, R0
+  ORR R7, R1, R1
+  ORR g, R2, R2
+  ORR R11, R3, R3
+  ORR R14, R4, R4
+  ORR R1<<26, R0, R0
+  MOVW R1>>6, R1
+  ORR R2<<20, R1, R1
+  MOVW R2>>12, R2
+  ORR R3<<14, R2, R2
+  MOVW R3>>18, R3
+  ORR R4<<8, R3, R3
+  MOVW 40(R5), R6
+  MOVW 44(R5), R7
+  MOVW 48(R5), g
+  MOVW 52(R5), R11
+  ADD.S R6, R0, R0
+  ADC.S R7, R1, R1
+  ADC.S g, R2, R2
+  ADC.S R11, R3, R3
+  MOVM.IA [R0-R3], (R8)
+  MOVW R5, R12
+  EOR R0, R0, R0
+  EOR R1, R1, R1
+  EOR R2, R2, R2
+  EOR R3, R3, R3
+  EOR R4, R4, R4
+  EOR R5, R5, R5
+  EOR R6, R6, R6
+  EOR R7, R7, R7
+  MOVM.IA.W [R0-R7], (R12)
+  MOVM.IA [R0-R7], (R12)
+  ADD $16, R13, R13
+  MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, g, R11, R14]
+  RET
+
+// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key)
+TEXT ·poly1305_auth_armv6(SB),0,$280-16
+  MOVW  out+0(FP), R4
+  MOVW  m+4(FP), R5
+  MOVW  mlen+8(FP), R6
+  MOVW  key+12(FP), R7
+
+  MOVW R13, R8
+  BIC $63, R13
+  SUB $64, R13, R13
+  MOVW  R13, R0
+  MOVW  R7, R1
+  BL poly1305_init_ext_armv6<>(SB)
+  BIC.S $15, R6, R2
+  BEQ poly1305_auth_armv6_noblocks
+  MOVW R13, R0
+  MOVW R5, R1
+  ADD R2, R5, R5
+  SUB R2, R6, R6
+  BL poly1305_blocks_armv6<>(SB)
+poly1305_auth_armv6_noblocks:
+  MOVW R13, R0
+  MOVW R5, R1
+  MOVW R6, R2
+  MOVW R4, R3
+  BL poly1305_finish_ext_armv6<>(SB)
+  MOVW R8, R13
+  RET
diff --git a/src/ssl/test/runner/poly1305/poly1305_test.go b/src/ssl/test/runner/poly1305/poly1305_test.go
new file mode 100644
index 0000000..b3e9231
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/poly1305_test.go
@@ -0,0 +1,86 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package poly1305
+
+import (
+	"bytes"
+	"testing"
+	"unsafe"
+)
+
+var testData = []struct {
+	in, k, correct []byte
+}{
+	{
+		[]byte("Hello world!"),
+		[]byte("this is 32-byte key for Poly1305"),
+		[]byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0},
+	},
+	{
+		make([]byte, 32),
+		[]byte("this is 32-byte key for Poly1305"),
+		[]byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07},
+	},
+	{
+		make([]byte, 2007),
+		[]byte("this is 32-byte key for Poly1305"),
+		[]byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa},
+	},
+	{
+		make([]byte, 2007),
+		make([]byte, 32),
+		make([]byte, 16),
+	},
+}
+
+func testSum(t *testing.T, unaligned bool) {
+	var out [16]byte
+	var key [32]byte
+
+	for i, v := range testData {
+		in := v.in
+		if unaligned {
+			in = unalignBytes(in)
+		}
+		copy(key[:], v.k)
+		Sum(&out, in, &key)
+		if !bytes.Equal(out[:], v.correct) {
+			t.Errorf("%d: expected %x, got %x", i, v.correct, out[:])
+		}
+	}
+}
+
+func TestSum(t *testing.T)          { testSum(t, false) }
+func TestSumUnaligned(t *testing.T) { testSum(t, true) }
+
+func benchmark(b *testing.B, size int, unaligned bool) {
+	var out [16]byte
+	var key [32]byte
+	in := make([]byte, size)
+	if unaligned {
+		in = unalignBytes(in)
+	}
+	b.SetBytes(int64(len(in)))
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		Sum(&out, in, &key)
+	}
+}
+
+func Benchmark64(b *testing.B)          { benchmark(b, 64, false) }
+func Benchmark1K(b *testing.B)          { benchmark(b, 1024, false) }
+func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) }
+func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) }
+
+func unalignBytes(in []byte) []byte {
+	out := make([]byte, len(in)+1)
+	if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
+		out = out[1:]
+	} else {
+		out = out[:len(in)]
+	}
+	copy(out, in)
+	return out
+}
diff --git a/src/ssl/test/runner/poly1305/sum_amd64.go b/src/ssl/test/runner/poly1305/sum_amd64.go
new file mode 100644
index 0000000..6775c70
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/sum_amd64.go
@@ -0,0 +1,24 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build amd64,!gccgo,!appengine
+
+package poly1305
+
+// This function is implemented in poly1305_amd64.s
+
+//go:noescape
+
+func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+	var mPtr *byte
+	if len(m) > 0 {
+		mPtr = &m[0]
+	}
+	poly1305(out, mPtr, uint64(len(m)), key)
+}
diff --git a/src/ssl/test/runner/poly1305/sum_arm.go b/src/ssl/test/runner/poly1305/sum_arm.go
new file mode 100644
index 0000000..50b979c
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/sum_arm.go
@@ -0,0 +1,24 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build arm,!gccgo,!appengine
+
+package poly1305
+
+// This function is implemented in poly1305_arm.s
+
+//go:noescape
+
+func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+	var mPtr *byte
+	if len(m) > 0 {
+		mPtr = &m[0]
+	}
+	poly1305_auth_armv6(out, mPtr, uint32(len(m)), key)
+}
diff --git a/src/ssl/test/runner/poly1305.go b/src/ssl/test/runner/poly1305/sum_ref.go
similarity index 96%
rename from src/ssl/test/runner/poly1305.go
rename to src/ssl/test/runner/poly1305/sum_ref.go
index edef338..0b24fc7 100644
--- a/src/ssl/test/runner/poly1305.go
+++ b/src/ssl/test/runner/poly1305/sum_ref.go
@@ -2,15 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package runner
+// +build !amd64,!arm gccgo appengine
+
+package poly1305
 
 // Based on original, public domain implementation from NaCl by D. J.
 // Bernstein.
 
-import (
-	"crypto/subtle"
-	"math"
-)
+import "math"
 
 const (
 	alpham80 = 0.00000000558793544769287109375
@@ -32,18 +31,10 @@
 	offset3  = 535219245894202480694386063513315216128475136.0
 )
 
-// poly1305Verify returns true if mac is a valid authenticator for m with the
-// given key.
-func poly1305Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
-	var tmp [16]byte
-	poly1305Sum(&tmp, m, key)
-	return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
-}
-
-// poly1305Sum generates an authenticator for m using a one-time key and puts
-// the 16-byte result into out. Authenticating two different messages with the
-// same key allows an attacker to forge messages at will.
-func poly1305Sum(out *[16]byte, m []byte, key *[32]byte) {
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
 	r := key
 	s := key[16:]
 	var (
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 6ab71cf..45bb0b7 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -27,6 +27,7 @@
 var (
 	useValgrind     = flag.Bool("valgrind", false, "If true, run code under valgrind")
 	useGDB          = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
+	useLLDB         = flag.Bool("lldb", false, "If true, run BoringSSL code under lldb")
 	flagDebug       = flag.Bool("debug", false, "Hexdump the contents of the connection")
 	mallocTest      = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.")
 	mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
@@ -521,6 +522,14 @@
 	return exec.Command("xterm", xtermArgs...)
 }
 
+func lldbOf(path string, args ...string) *exec.Cmd {
+	xtermArgs := []string{"-e", "lldb", "--"}
+	xtermArgs = append(xtermArgs, path)
+	xtermArgs = append(xtermArgs, args...)
+
+	return exec.Command("xterm", xtermArgs...)
+}
+
 type moreMallocsError struct{}
 
 func (moreMallocsError) Error() string {
@@ -637,6 +646,8 @@
 		shim = valgrindOf(false, shimPath, flags...)
 	} else if *useGDB {
 		shim = gdbOf(shimPath, flags...)
+	} else if *useLLDB {
+		shim = lldbOf(shimPath, flags...)
 	} else {
 		shim = exec.Command(shimPath, flags...)
 	}
@@ -801,6 +812,7 @@
 	{"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
 	{"ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
+	{"ECDHE-ECDSA-CHACHA20-POLY1305-OLD", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD},
 	{"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
 	{"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 	{"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
@@ -809,11 +821,13 @@
 	{"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
 	{"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+	{"ECDHE-RSA-CHACHA20-POLY1305-OLD", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD},
 	{"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
 	{"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
 	{"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
 	{"ECDHE-PSK-AES128-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA},
 	{"ECDHE-PSK-AES256-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA},
+	{"ECDHE-PSK-CHACHA20-POLY1305", TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256},
 	{"PSK-RC4-SHA", TLS_PSK_WITH_RC4_128_SHA},
 	{"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
 	{"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
@@ -824,6 +838,12 @@
 	return strings.Contains("-"+suiteName+"-", "-"+component+"-")
 }
 
+func isTLSOnly(suiteName string) bool {
+	// BoringSSL doesn't support ECDHE without a curves extension, and
+	// SSLv3 doesn't contain extensions.
+	return hasComponent(suiteName, "ECDHE") || isTLS12Only(suiteName)
+}
+
 func isTLS12Only(suiteName string) bool {
 	return hasComponent(suiteName, "GCM") ||
 		hasComponent(suiteName, "SHA256") ||
@@ -907,18 +927,6 @@
 			expectedError: ":WRONG_CURVE:",
 		},
 		{
-			testType: serverTest,
-			name:     "BadRSAVersion",
-			config: Config{
-				CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
-				Bugs: ProtocolBugs{
-					RsaClientKeyExchangeVersion: VersionTLS11,
-				},
-			},
-			shouldFail:    true,
-			expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
-		},
-		{
 			name: "NoFallbackSCSV",
 			config: Config{
 				Bugs: ProtocolBugs{
@@ -1006,7 +1014,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			testType: serverTest,
@@ -1017,7 +1025,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			testType: serverTest,
@@ -1032,7 +1040,7 @@
 				"-advertise-npn", "\x03foo\x03bar\x03baz",
 			},
 			shouldFail:    true,
-			expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			name: "FragmentAcrossChangeCipherSpec-Client",
@@ -1042,7 +1050,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			testType: serverTest,
@@ -1053,7 +1061,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			testType: serverTest,
@@ -1068,7 +1076,7 @@
 				"-advertise-npn", "\x03foo\x03bar\x03baz",
 			},
 			shouldFail:    true,
-			expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			testType: serverTest,
@@ -1127,7 +1135,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":CCS_RECEIVED_EARLY:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			testType: serverTest,
@@ -1138,7 +1146,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":CCS_RECEIVED_EARLY:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			name: "SkipNewSessionTicket",
@@ -1148,7 +1156,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":CCS_RECEIVED_EARLY:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			testType: serverTest,
@@ -1425,7 +1433,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			name: "AppDataAfterChangeCipherSpec-Empty",
@@ -1435,7 +1443,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:",
+			expectedError: ":UNEXPECTED_RECORD:",
 		},
 		{
 			protocol: dtls,
@@ -1689,14 +1697,13 @@
 		{
 			name: "UnsupportedCurve",
 			config: Config{
-				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-				// BoringSSL implements P-224 but doesn't enable it by
-				// default.
-				CurvePreferences: []CurveID{CurveP224},
+				CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				CurvePreferences: []CurveID{CurveP256},
 				Bugs: ProtocolBugs{
 					IgnorePeerCurvePreferences: true,
 				},
 			},
+			flags:         []string{"-p384-only"},
 			shouldFail:    true,
 			expectedError: ":WRONG_CURVE:",
 		},
@@ -1812,6 +1819,8 @@
 					NoSupportedCurves: true,
 				},
 			},
+			shouldFail:    true,
+			expectedError: ":NO_SHARED_CIPHER:",
 		},
 		{
 			testType: serverTest,
@@ -1997,6 +2006,88 @@
 			resumeSession:        true,
 			expectResumeRejected: true,
 		},
+		{
+			name: "CheckLeafCurve",
+			config: Config{
+				CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+				Certificates: []Certificate{getECDSACertificate()},
+			},
+			flags:         []string{"-p384-only"},
+			shouldFail:    true,
+			expectedError: ":BAD_ECC_CERT:",
+		},
+		{
+			name: "BadChangeCipherSpec-1",
+			config: Config{
+				Bugs: ProtocolBugs{
+					BadChangeCipherSpec: []byte{2},
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_CHANGE_CIPHER_SPEC:",
+		},
+		{
+			name: "BadChangeCipherSpec-2",
+			config: Config{
+				Bugs: ProtocolBugs{
+					BadChangeCipherSpec: []byte{1, 1},
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_CHANGE_CIPHER_SPEC:",
+		},
+		{
+			protocol: dtls,
+			name:     "BadChangeCipherSpec-DTLS-1",
+			config: Config{
+				Bugs: ProtocolBugs{
+					BadChangeCipherSpec: []byte{2},
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_CHANGE_CIPHER_SPEC:",
+		},
+		{
+			protocol: dtls,
+			name:     "BadChangeCipherSpec-DTLS-2",
+			config: Config{
+				Bugs: ProtocolBugs{
+					BadChangeCipherSpec: []byte{1, 1},
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_CHANGE_CIPHER_SPEC:",
+		},
+		{
+			name:        "BadHelloRequest-1",
+			renegotiate: 1,
+			config: Config{
+				Bugs: ProtocolBugs{
+					BadHelloRequest: []byte{typeHelloRequest, 0, 0, 1, 1},
+				},
+			},
+			flags: []string{
+				"-renegotiate-freely",
+				"-expect-total-renegotiations", "1",
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_HELLO_REQUEST:",
+		},
+		{
+			name:        "BadHelloRequest-2",
+			renegotiate: 1,
+			config: Config{
+				Bugs: ProtocolBugs{
+					BadHelloRequest: []byte{typeServerKeyExchange, 0, 0, 0},
+				},
+			},
+			flags: []string{
+				"-renegotiate-freely",
+				"-expect-total-renegotiations", "1",
+			},
+			shouldFail:    true,
+			expectedError: ":BAD_HELLO_REQUEST:",
+		},
 	}
 	testCases = append(testCases, basicTests...)
 }
@@ -2035,20 +2126,12 @@
 				continue
 			}
 
-			testCases = append(testCases, testCase{
-				testType: clientTest,
-				name:     ver.name + "-" + suite.name + "-client",
-				config: Config{
-					MinVersion:           ver.version,
-					MaxVersion:           ver.version,
-					CipherSuites:         []uint16{suite.id},
-					Certificates:         []Certificate{cert},
-					PreSharedKey:         []byte(psk),
-					PreSharedKeyIdentity: pskIdentity,
-				},
-				flags:         flags,
-				resumeSession: true,
-			})
+			shouldFail := isTLSOnly(suite.name) && ver.version == VersionSSL30
+
+			expectedError := ""
+			if shouldFail {
+				expectedError = ":NO_SHARED_CIPHER:"
+			}
 
 			testCases = append(testCases, testCase{
 				testType: serverTest,
@@ -2065,6 +2148,27 @@
 				keyFile:       keyFile,
 				flags:         flags,
 				resumeSession: true,
+				shouldFail:    shouldFail,
+				expectedError: expectedError,
+			})
+
+			if shouldFail {
+				continue
+			}
+
+			testCases = append(testCases, testCase{
+				testType: clientTest,
+				name:     ver.name + "-" + suite.name + "-client",
+				config: Config{
+					MinVersion:           ver.version,
+					MaxVersion:           ver.version,
+					CipherSuites:         []uint16{suite.id},
+					Certificates:         []Certificate{cert},
+					PreSharedKey:         []byte(psk),
+					PreSharedKeyIdentity: pskIdentity,
+				},
+				flags:         flags,
+				resumeSession: true,
 			})
 
 			if ver.hasDTLS && isDTLSCipher(suite.name) {
@@ -2115,20 +2219,6 @@
 			flags:      flags,
 			messageLen: maxPlaintext,
 		})
-		testCases = append(testCases, testCase{
-			name: suite.name + "-LargeRecord-Extra",
-			config: Config{
-				CipherSuites:         []uint16{suite.id},
-				Certificates:         []Certificate{cert},
-				PreSharedKey:         []byte(psk),
-				PreSharedKeyIdentity: pskIdentity,
-				Bugs: ProtocolBugs{
-					SendLargeRecords: true,
-				},
-			},
-			flags:      append(flags, "-microsoft-big-sslv3-buffer"),
-			messageLen: maxPlaintext + 16384,
-		})
 		if isDTLSCipher(suite.name) {
 			testCases = append(testCases, testCase{
 				protocol: dtls,
@@ -2157,7 +2247,37 @@
 			},
 		},
 		shouldFail:    true,
-		expectedError: "BAD_DH_P_LENGTH",
+		expectedError: ":BAD_DH_P_LENGTH:",
+	})
+
+	testCases = append(testCases, testCase{
+		name: "SillyDH",
+		config: Config{
+			CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				// This is a 4097-bit prime number, generated
+				// with:
+				// openssl gendh 4097 | openssl asn1parse -i
+				DHGroupPrime: bigFromHex("01D366FA64A47419B0CD4A45918E8D8C8430F674621956A9F52B0CA592BC104C6E38D60C58F2CA66792A2B7EBDC6F8FFE75AB7D6862C261F34E96A2AEEF53AB7C21365C2E8FB0582F71EB57B1C227C0E55AE859E9904A25EFECD7B435C4D4357BD840B03649D4A1F8037D89EA4E1967DBEEF1CC17A6111C48F12E9615FFF336D3F07064CB17C0B765A012C850B9E3AA7A6984B96D8C867DDC6D0F4AB52042572244796B7ECFF681CD3B3E2E29AAECA391A775BEE94E502FB15881B0F4AC60314EA947C0C82541C3D16FD8C0E09BB7F8F786582032859D9C13187CE6C0CB6F2D3EE6C3C9727C15F14B21D3CD2E02BDB9D119959B0E03DC9E5A91E2578762300B1517D2352FC1D0BB934A4C3E1B20CE9327DB102E89A6C64A8C3148EDFC5A94913933853442FA84451B31FD21E492F92DD5488E0D871AEBFE335A4B92431DEC69591548010E76A5B365D346786E9A2D3E589867D796AA5E25211201D757560D318A87DFB27F3E625BC373DB48BF94A63161C674C3D4265CB737418441B7650EABC209CF675A439BEB3E9D1AA1B79F67198A40CEFD1C89144F7D8BAF61D6AD36F466DA546B4174A0E0CAF5BD788C8243C7C2DDDCC3DB6FC89F12F17D19FBD9B0BC76FE92891CD6BA07BEA3B66EF12D0D85E788FD58675C1B0FBD16029DCC4D34E7A1A41471BDEDF78BF591A8B4E96D88BEC8EDC093E616292BFC096E69A916E8D624B"),
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":DH_P_TOO_LONG:",
+	})
+
+	// This test ensures that Diffie-Hellman public values are padded with
+	// zeros so that they're the same length as the prime. This is to avoid
+	// hitting a bug in yaSSL.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "DHPublicValuePadded",
+		config: Config{
+			CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				RequireDHPublicValueLen: (1025 + 7) / 8,
+			},
+		},
+		flags: []string{"-use-sparse-dh-prime"},
 	})
 
 	// versionSpecificCiphersTest specifies a test for the TLS 1.0 and TLS
@@ -3168,40 +3288,6 @@
 	}
 }
 
-func addD5BugTests() {
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "D5Bug-NoQuirk-Reject",
-		config: Config{
-			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
-			Bugs: ProtocolBugs{
-				SSL3RSAKeyExchange: true,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG:",
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "D5Bug-Quirk-Normal",
-		config: Config{
-			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
-		},
-		flags: []string{"-tls-d5-bug"},
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "D5Bug-Quirk-Bug",
-		config: Config{
-			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
-			Bugs: ProtocolBugs{
-				SSL3RSAKeyExchange: true,
-			},
-		},
-		flags: []string{"-tls-d5-bug"},
-	})
-}
-
 func addExtensionTests() {
 	testCases = append(testCases, testCase{
 		testType: clientTest,
@@ -3801,15 +3887,28 @@
 		expectedError: ":RENEGOTIATION_MISMATCH:",
 	})
 	testCases = append(testCases, testCase{
-		name: "Renegotiate-Client-NoExt",
+		name:        "Renegotiate-Client-Downgrade",
+		renegotiate: 1,
 		config: Config{
 			Bugs: ProtocolBugs{
-				NoRenegotiationInfo: true,
+				NoRenegotiationInfoAfterInitial: true,
 			},
 		},
+		flags:         []string{"-renegotiate-freely"},
 		shouldFail:    true,
-		expectedError: ":UNSAFE_LEGACY_RENEGOTIATION_DISABLED:",
-		flags:         []string{"-no-legacy-server-connect"},
+		expectedError: ":RENEGOTIATION_MISMATCH:",
+	})
+	testCases = append(testCases, testCase{
+		name:        "Renegotiate-Client-Upgrade",
+		renegotiate: 1,
+		config: Config{
+			Bugs: ProtocolBugs{
+				NoRenegotiationInfoInInitial: true,
+			},
+		},
+		flags:         []string{"-renegotiate-freely"},
+		shouldFail:    true,
+		expectedError: ":RENEGOTIATION_MISMATCH:",
 	})
 	testCases = append(testCases, testCase{
 		name:        "Renegotiate-Client-NoExt-Allowed",
@@ -4518,6 +4617,114 @@
 	})
 }
 
+func addRSAClientKeyExchangeTests() {
+	for bad := RSABadValue(1); bad < NumRSABadValues; bad++ {
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     fmt.Sprintf("BadRSAClientKeyExchange-%d", bad),
+			config: Config{
+				// Ensure the ClientHello version and final
+				// version are different, to detect if the
+				// server uses the wrong one.
+				MaxVersion:   VersionTLS11,
+				CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
+				Bugs: ProtocolBugs{
+					BadRSAClientKeyExchange: bad,
+				},
+			},
+			shouldFail:    true,
+			expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
+		})
+	}
+}
+
+var testCurves = []struct {
+	name string
+	id   CurveID
+}{
+	{"P-256", CurveP256},
+	{"P-384", CurveP384},
+	{"P-521", CurveP521},
+	{"X25519", CurveX25519},
+}
+
+func addCurveTests() {
+	for _, curve := range testCurves {
+		testCases = append(testCases, testCase{
+			name: "CurveTest-Client-" + curve.name,
+			config: Config{
+				CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				CurvePreferences: []CurveID{curve.id},
+			},
+			flags: []string{"-enable-all-curves"},
+		})
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "CurveTest-Server-" + curve.name,
+			config: Config{
+				CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				CurvePreferences: []CurveID{curve.id},
+			},
+			flags: []string{"-enable-all-curves"},
+		})
+	}
+}
+
+func addKeyExchangeInfoTests() {
+	testCases = append(testCases, testCase{
+		name: "KeyExchangeInfo-RSA-Client",
+		config: Config{
+			CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256},
+		},
+		// key.pem is a 1024-bit RSA key.
+		flags: []string{"-expect-key-exchange-info", "1024"},
+	})
+	// TODO(davidben): key_exchange_info doesn't work for plain RSA on the
+	// server. Either fix this or change the API as it's not very useful in
+	// this case.
+
+	testCases = append(testCases, testCase{
+		name: "KeyExchangeInfo-DHE-Client",
+		config: Config{
+			CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+			Bugs: ProtocolBugs{
+				// This is a 1234-bit prime number, generated
+				// with:
+				// openssl gendh 1234 | openssl asn1parse -i
+				DHGroupPrime: bigFromHex("0215C589A86BE450D1255A86D7A08877A70E124C11F0C75E476BA6A2186B1C830D4A132555973F2D5881D5F737BB800B7F417C01EC5960AEBF79478F8E0BBB6A021269BD10590C64C57F50AD8169D5488B56EE38DC5E02DA1A16ED3B5F41FEB2AD184B78A31F3A5B2BEC8441928343DA35DE3D4F89F0D4CEDE0034045084A0D1E6182E5EF7FCA325DD33CE81BE7FA87D43613E8FA7A1457099AB53"),
+			},
+		},
+		flags: []string{"-expect-key-exchange-info", "1234"},
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "KeyExchangeInfo-DHE-Server",
+		config: Config{
+			CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+		},
+		// bssl_shim as a server configures a 2048-bit DHE group.
+		flags: []string{"-expect-key-exchange-info", "2048"},
+	})
+
+	testCases = append(testCases, testCase{
+		name: "KeyExchangeInfo-ECDHE-Client",
+		config: Config{
+			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			CurvePreferences: []CurveID{CurveX25519},
+		},
+		flags: []string{"-expect-key-exchange-info", "29", "-enable-all-curves"},
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "KeyExchangeInfo-ECDHE-Server",
+		config: Config{
+			CipherSuites:     []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+			CurvePreferences: []CurveID{CurveX25519},
+		},
+		flags: []string{"-expect-key-exchange-info", "29", "-enable-all-curves"},
+	})
+}
+
 func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
 	defer wg.Done()
 
@@ -4604,7 +4811,6 @@
 	addDDoSCallbackTests()
 	addVersionNegotiationTests()
 	addMinimumVersionTests()
-	addD5BugTests()
 	addExtensionTests()
 	addResumptionVersionTests()
 	addExtendedMasterSecretTests()
@@ -4615,6 +4821,9 @@
 	addExportKeyingMaterialTests()
 	addTLSUniqueTests()
 	addCustomExtensionTests()
+	addRSAClientKeyExchangeTests()
+	addCurveTests()
+	addKeyExchangeInfoTests()
 	for _, async := range []bool{false, true} {
 		for _, splitHandshake := range []bool{false, true} {
 			for _, protocol := range []protocol{tls, dtls} {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 50e6b23..1cf316d 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -61,7 +61,6 @@
   { "-no-tls1", &TestConfig::no_tls1 },
   { "-no-ssl3", &TestConfig::no_ssl3 },
   { "-shim-writes-first", &TestConfig::shim_writes_first },
-  { "-tls-d5-bug", &TestConfig::tls_d5_bug },
   { "-expect-session-miss", &TestConfig::expect_session_miss },
   { "-expect-extended-master-secret",
     &TestConfig::expect_extended_master_secret },
@@ -76,7 +75,6 @@
   { "-fail-second-ddos-callback", &TestConfig::fail_second_ddos_callback },
   { "-handshake-never-done", &TestConfig::handshake_never_done },
   { "-use-export-context", &TestConfig::use_export_context },
-  { "-no-legacy-server-connect", &TestConfig::no_legacy_server_connect },
   { "-tls-unique", &TestConfig::tls_unique },
   { "-expect-ticket-renewal", &TestConfig::expect_ticket_renewal },
   { "-expect-no-session", &TestConfig::expect_no_session },
@@ -90,7 +88,6 @@
   { "-custom-extension-fail-add", &TestConfig::custom_extension_fail_add },
   { "-check-close-notify", &TestConfig::check_close_notify },
   { "-shim-shuts-down", &TestConfig::shim_shuts_down },
-  { "-microsoft-big-sslv3-buffer", &TestConfig::microsoft_big_sslv3_buffer },
   { "-verify-fail", &TestConfig::verify_fail },
   { "-verify-peer", &TestConfig::verify_peer },
   { "-expect-verify-result", &TestConfig::expect_verify_result },
@@ -98,6 +95,9 @@
   { "-renegotiate-freely", &TestConfig::renegotiate_freely },
   { "-renegotiate-ignore", &TestConfig::renegotiate_ignore },
   { "-disable-npn", &TestConfig::disable_npn },
+  { "-p384-only", &TestConfig::p384_only },
+  { "-enable-all-curves", &TestConfig::enable_all_curves },
+  { "-use-sparse-dh-prime", &TestConfig::use_sparse_dh_prime },
 };
 
 const Flag<std::string> kStringFlags[] = {
@@ -143,6 +143,8 @@
   { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
   { "-expect-server-key-exchange-hash",
     &TestConfig::expect_server_key_exchange_hash },
+  { "-expect-key-exchange-info",
+    &TestConfig::expect_key_exchange_info },
 };
 
 }  // namespace
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 9f295ae..4e0a46a 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -45,7 +45,6 @@
   std::string expected_channel_id;
   std::string send_channel_id;
   bool shim_writes_first = false;
-  bool tls_d5_bug = false;
   std::string host_name;
   std::string advertise_alpn;
   std::string expected_alpn;
@@ -77,7 +76,6 @@
   std::string export_label;
   std::string export_context;
   bool use_export_context = false;
-  bool no_legacy_server_connect = false;
   bool tls_unique = false;
   bool expect_ticket_renewal = false;
   bool expect_no_session = false;
@@ -90,7 +88,6 @@
   std::string ocsp_response;
   bool check_close_notify = false;
   bool shim_shuts_down = false;
-  bool microsoft_big_sslv3_buffer = false;
   bool verify_fail = false;
   bool verify_peer = false;
   bool expect_verify_result = false;
@@ -101,6 +98,10 @@
   bool renegotiate_ignore = false;
   bool disable_npn = false;
   int expect_server_key_exchange_hash = 0;
+  bool p384_only = false;
+  bool enable_all_curves = false;
+  bool use_sparse_dh_prime = false;
+  int expect_key_exchange_info = 0;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index bdc5c01..3381eae 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -114,7 +114,6 @@
 #include <openssl/err.h>
 
 #include "internal.h"
-#include "../crypto/internal.h"
 
 
 /* kMaxEmptyRecords is the number of consecutive, empty records that will be
@@ -123,14 +122,12 @@
  * forever. */
 static const uint8_t kMaxEmptyRecords = 32;
 
-static struct CRYPTO_STATIC_MUTEX g_big_buffer_lock = CRYPTO_STATIC_MUTEX_INIT;
-static uint64_t g_big_buffer_use_count = 0;
-
-uint64_t OPENSSL_get_big_buffer_use_count(void) {
-  CRYPTO_STATIC_MUTEX_lock_read(&g_big_buffer_lock);
-  uint64_t ret = g_big_buffer_use_count;
-  CRYPTO_STATIC_MUTEX_unlock(&g_big_buffer_lock);
-  return ret;
+/* ssl_needs_record_splitting returns one if |ssl|'s current outgoing cipher
+ * state needs record-splitting and zero otherwise. */
+static int ssl_needs_record_splitting(const SSL *ssl) {
+  return !SSL_USE_EXPLICIT_IV(ssl) && ssl->aead_write_ctx != NULL &&
+         (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
+         SSL_CIPHER_is_block_cipher(ssl->aead_write_ctx->cipher);
 }
 
 size_t ssl_record_prefix_len(const SSL *ssl) {
@@ -150,7 +147,7 @@
   } else {
     size_t ret = SSL3_RT_HEADER_LENGTH +
                  SSL_AEAD_CTX_explicit_nonce_len(ssl->aead_write_ctx);
-    if (ssl->s3->need_record_splitting) {
+    if (ssl_needs_record_splitting(ssl)) {
       ret += SSL3_RT_HEADER_LENGTH;
       ret += ssl_cipher_get_record_split_len(ssl->aead_write_ctx->cipher);
     }
@@ -165,7 +162,7 @@
   } else {
     size_t ret = SSL3_RT_HEADER_LENGTH +
                  SSL_AEAD_CTX_max_overhead(ssl->aead_write_ctx);
-    if (ssl->s3->need_record_splitting) {
+    if (ssl_needs_record_splitting(ssl)) {
       ret *= 2;
     }
     return ret;
@@ -198,11 +195,7 @@
   }
 
   /* Check the ciphertext length. */
-  size_t extra = 0;
-  if (ssl->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) {
-    extra = SSL3_RT_MAX_EXTRA;
-  }
-  if (ciphertext_len > SSL3_RT_MAX_ENCRYPTED_LENGTH + extra) {
+  if (ciphertext_len > SSL3_RT_MAX_ENCRYPTED_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_ENCRYPTED_LENGTH_TOO_LONG);
     *out_alert = SSL_AD_RECORD_OVERFLOW;
     return ssl_open_record_error;
@@ -235,20 +228,12 @@
   }
 
   /* Check the plaintext length. */
-  if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH + extra) {
+  if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
     *out_alert = SSL_AD_RECORD_OVERFLOW;
     return ssl_open_record_error;
   }
 
-  if (extra > 0 &&
-      (ciphertext_len > SSL3_RT_MAX_ENCRYPTED_LENGTH ||
-       plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH)) {
-    CRYPTO_STATIC_MUTEX_lock_write(&g_big_buffer_lock);
-    g_big_buffer_use_count++;
-    CRYPTO_STATIC_MUTEX_unlock(&g_big_buffer_lock);
-  }
-
   /* Limit the number of consecutive empty records. */
   if (plaintext_len == 0) {
     ssl->s3->empty_record_count++;
@@ -323,8 +308,8 @@
 int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
                     uint8_t type, const uint8_t *in, size_t in_len) {
   size_t frag_len = 0;
-  if (ssl->s3->need_record_splitting && type == SSL3_RT_APPLICATION_DATA &&
-      in_len > 1) {
+  if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
+      ssl_needs_record_splitting(ssl)) {
     /* |do_seal_record| will notice if it clobbers |in[0]|, but not if it
      * aliases the rest of |in|. */
     if (in + 1 <= out && out < in + in_len) {