external/boringssl: Sync to 8ca0b41.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/9d908ba519f2cfe5e21561bdee3e224b94d14a89..8ca0b4127da11d766067ea6ec4122017ba0edb0e

Change-Id: I732653bc8fcba70707c615f8731ca75397a08736
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index cc95a70..8d8784b 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -249,12 +249,12 @@
   /* TODO(davidben): What is this code doing and do we need it? */
   if (ssl->d1->mtu < dtls1_min_mtu() &&
       !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
-    long mtu = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+    long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
     if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
       ssl->d1->mtu = (unsigned)mtu;
     } else {
       ssl->d1->mtu = kDefaultMTU;
-      BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
+      BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
     }
   }
 
@@ -274,7 +274,7 @@
   }
   ret -= overhead;
 
-  size_t pending = BIO_wpending(SSL_get_wbio(ssl));
+  size_t pending = BIO_wpending(ssl->wbio);
   if (ret <= pending) {
     return 0;
   }
@@ -290,7 +290,7 @@
   /* During the handshake, wbio is buffered to pack messages together. Flush the
    * buffer if the ChangeCipherSpec would not fit in a packet. */
   if (dtls1_max_record_size(ssl) == 0) {
-    int ret = BIO_flush(SSL_get_wbio(ssl));
+    int ret = BIO_flush(ssl->wbio);
     if (ret <= 0) {
       ssl->rwstate = SSL_WRITING;
       return ret;
@@ -339,13 +339,13 @@
     /* During the handshake, wbio is buffered to pack messages together. Flush
      * the buffer if there isn't enough room to make progress. */
     if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) {
-      int flush_ret = BIO_flush(SSL_get_wbio(ssl));
+      int flush_ret = BIO_flush(ssl->wbio);
       if (flush_ret <= 0) {
         ssl->rwstate = SSL_WRITING;
         ret = flush_ret;
         goto err;
       }
-      assert(BIO_wpending(SSL_get_wbio(ssl)) == 0);
+      assert(BIO_wpending(ssl->wbio) == 0);
     }
 
     size_t todo = dtls1_max_record_size(ssl);
@@ -483,17 +483,6 @@
   return frag;
 }
 
-/* dtls1_max_handshake_message_len returns the maximum number of bytes
- * 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 *ssl) {
-  size_t max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
-  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 *ssl) {
@@ -521,7 +510,7 @@
   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(ssl) ||
+      msg_len > ssl_max_handshake_message_len(ssl) ||
       frag_len > ssl->s3->rrec.length) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -567,9 +556,9 @@
 }
 
 /* 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 *ssl, int st1, int stn, int msg_type, long max,
+ * if |msg_type| == -1). Read an entire handshake message. Handshake messages
+ * arrive in fragments. */
+long dtls1_get_message(SSL *ssl, int msg_type,
                        enum ssl_hash_message_t hash_message, int *ok) {
   pitem *item = NULL;
   hm_fragment *frag = NULL;
@@ -589,8 +578,9 @@
       goto f_err;
     }
     *ok = 1;
+    assert(ssl->init_buf->length >= DTLS1_HM_HEADER_LENGTH);
     ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
-    ssl->init_num = (int)ssl->s3->tmp.message_size;
+    ssl->init_num = (int)ssl->init_buf->length - DTLS1_HM_HEADER_LENGTH;
     return ssl->init_num;
   }
 
@@ -610,17 +600,11 @@
   assert(ssl->d1->handshake_read_seq == frag->msg_header.seq);
   assert(frag->reassembly == NULL);
 
-  if (frag->msg_header.msg_len > (size_t)max) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
-    goto err;
-  }
-
   /* Reconstruct the assembled message. */
-  size_t len;
   CBB cbb;
   CBB_zero(&cbb);
-  if (!BUF_MEM_grow(ssl->init_buf, (size_t)frag->msg_header.msg_len +
-                                       DTLS1_HM_HEADER_LENGTH) ||
+  if (!BUF_MEM_reserve(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) ||
@@ -629,19 +613,19 @@
       !CBB_add_u24(&cbb, 0 /* frag_off */) ||
       !CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
       !CBB_add_bytes(&cbb, frag->fragment, frag->msg_header.msg_len) ||
-      !CBB_finish(&cbb, NULL, &len)) {
+      !CBB_finish(&cbb, NULL, &ssl->init_buf->length)) {
     CBB_cleanup(&cbb);
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
   }
-  assert(len == (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
+  assert(ssl->init_buf->length ==
+         (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
 
   ssl->d1->handshake_read_seq++;
 
   /* TODO(davidben): This function has a lot of implicit outputs. Simplify the
    * |ssl_get_message| API. */
   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;
 
@@ -662,7 +646,6 @@
   pitem_free(item);
   dtls1_hm_fragment_free(frag);
 
-  ssl->state = stn;
   *ok = 1;
   return ssl->init_num;
 
@@ -689,7 +672,7 @@
 
   if (!SSL_in_init(ssl)) {
     /* done, no need to send a retransmit */
-    BIO_set_flags(SSL_get_rbio(ssl), BIO_FLAGS_READ);
+    BIO_set_flags(ssl->rbio, BIO_FLAGS_READ);
     return code;
   }
 
@@ -737,25 +720,39 @@
     ret = dtls1_do_handshake_write(ssl, use_epoch);
   }
 
-  /* TODO(davidben): Check return value? */
-  (void)BIO_flush(SSL_get_wbio(ssl));
   return ret;
 }
 
-
 int dtls1_retransmit_buffered_messages(SSL *ssl) {
-  pqueue sent = ssl->d1->sent_messages;
-  piterator iter = pqueue_iterator(sent);
-  pitem *item;
+  /* Ensure we are packing handshake messages. */
+  const int was_buffered = ssl_is_wbio_buffered(ssl);
+  assert(was_buffered == SSL_in_init(ssl));
+  if (!was_buffered && !ssl_init_wbio_buffer(ssl)) {
+    return -1;
+  }
+  assert(ssl_is_wbio_buffered(ssl));
 
+  int ret = -1;
+  piterator iter = pqueue_iterator(ssl->d1->sent_messages);
+  pitem *item;
   for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) {
     hm_fragment *frag = (hm_fragment *)item->data;
     if (dtls1_retransmit_message(ssl, frag) <= 0) {
-      return -1;
+      goto err;
     }
   }
 
-  return 1;
+  ret = BIO_flush(ssl->wbio);
+  if (ret <= 0) {
+    ssl->rwstate = SSL_WRITING;
+    goto err;
+  }
+
+err:
+  if (!was_buffered) {
+    ssl_free_wbio_buffer(ssl);
+  }
+  return ret;
 }
 
 /* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current
diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c
index 2fc9094..19ad1f8 100644
--- a/src/ssl/d1_clnt.c
+++ b/src/ssl/d1_clnt.c
@@ -161,30 +161,27 @@
         if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
           if (buf == NULL ||
-              !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+              !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
             ret = -1;
             goto end;
           }
           ssl->init_buf = buf;
           buf = NULL;
         }
+        ssl->init_num = 0;
 
-        if (!ssl_init_wbio_buffer(ssl, 0)) {
+        if (!ssl_init_wbio_buffer(ssl)) {
           ret = -1;
           goto end;
         }
 
-        /* don't push the buffering BIO quite yet */
-
         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:
-        ssl->shutdown = 0;
         dtls1_start_timer(ssl);
         ret = ssl3_send_client_hello(ssl);
         if (ret <= 0) {
@@ -192,22 +189,14 @@
         }
 
         if (ssl->d1->send_cookie) {
-          ssl->state = SSL3_ST_CW_FLUSH;
           ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
         } else {
-          ssl->state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
+          ssl->s3->tmp.next_state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
         }
-
-        ssl->init_num = 0;
-        /* turn on buffering for the next lot of output */
-        if (ssl->bbio != ssl->wbio) {
-          ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
-        }
-
+        ssl->state = SSL3_ST_CW_FLUSH;
         break;
 
       case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
-      case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B:
         ret = dtls1_get_hello_verify(ssl);
         if (ret <= 0) {
           goto end;
@@ -219,11 +208,9 @@
         } else {
           ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_SRVR_HELLO_A:
-      case SSL3_ST_CR_SRVR_HELLO_B:
         ret = ssl3_get_server_hello(ssl);
         if (ret <= 0) {
           goto end;
@@ -238,12 +225,10 @@
         } else {
           ssl->state = SSL3_ST_CR_CERT_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_A:
-      case SSL3_ST_CR_CERT_B:
-        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
           ret = ssl3_get_server_certificate(ssl);
           if (ret <= 0) {
             goto end;
@@ -257,7 +242,6 @@
           skip = 1;
           ssl->state = SSL3_ST_CR_KEY_EXCH_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_VERIFY_SERVER_CERT:
@@ -267,31 +251,29 @@
         }
 
         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(ssl);
         if (ret <= 0) {
           goto end;
         }
-        ssl->state = SSL3_ST_CR_CERT_REQ_A;
-        ssl->init_num = 0;
+        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+          ssl->state = SSL3_ST_CR_CERT_REQ_A;
+        } else {
+          ssl->state = SSL3_ST_CR_SRVR_DONE_A;
+        }
         break;
 
       case SSL3_ST_CR_CERT_REQ_A:
-      case SSL3_ST_CR_CERT_REQ_B:
         ret = ssl3_get_certificate_request(ssl);
         if (ret <= 0) {
           goto end;
         }
         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(ssl);
         if (ret <= 0) {
           goto end;
@@ -302,7 +284,6 @@
         } else {
           ssl->s3->tmp.next_state = SSL3_ST_CW_KEY_EXCH_A;
         }
-        ssl->init_num = 0;
         ssl->state = ssl->s3->tmp.next_state;
         break;
 
@@ -316,7 +297,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_CW_KEY_EXCH_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_KEY_EXCH_A:
@@ -333,8 +313,6 @@
         } else {
           ssl->state = SSL3_ST_CW_CHANGE_A;
         }
-
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CERT_VRFY_A:
@@ -346,7 +324,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_CW_CHANGE_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CHANGE_A:
@@ -361,7 +338,6 @@
         }
 
         ssl->state = SSL3_ST_CW_FINISHED_A;
-        ssl->init_num = 0;
 
         if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
@@ -392,27 +368,22 @@
             ssl->s3->tmp.next_state = SSL3_ST_CR_CHANGE;
           }
         }
-        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(ssl);
         if (ret <= 0) {
           goto end;
         }
         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(ssl);
         if (ret <= 0) {
           goto end;
         }
         ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CHANGE:
@@ -429,9 +400,7 @@
         break;
 
       case SSL3_ST_CR_FINISHED_A:
-      case SSL3_ST_CR_FINISHED_B:
-        ret =
-            ssl3_get_finished(ssl, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B);
+        ret = ssl3_get_finished(ssl);
         if (ret <= 0) {
           goto end;
         }
@@ -443,7 +412,6 @@
           ssl->state = SSL_ST_OK;
         }
 
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_FLUSH:
@@ -462,6 +430,10 @@
         /* Remove write buffering now. */
         ssl_free_wbio_buffer(ssl);
 
+	/* |init_buf| cannot be released because post-handshake retransmit
+         * relies on that buffer being available as scratch space.
+         *
+         * TODO(davidben): Fix this. */
         ssl->init_num = 0;
         ssl->s3->initial_handshake_complete = 1;
 
@@ -510,11 +482,7 @@
   CBS hello_verify_request, cookie;
   uint16_t server_version;
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index e48fbf1..c1c9f91 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -157,18 +157,27 @@
   return cipher->algorithm_enc != SSL_RC4 && cipher->algorithm_enc != SSL_eNULL;
 }
 
+void DTLSv1_set_initial_timeout_duration(SSL *ssl, unsigned int duration_ms) {
+  ssl->initial_timeout_duration_ms = duration_ms;
+}
+
 void dtls1_start_timer(SSL *ssl) {
-  /* If timer is not set, initialize duration with 1 second */
+  /* If timer is not set, initialize duration (by default, 1 second) */
   if (ssl->d1->next_timeout.tv_sec == 0 && ssl->d1->next_timeout.tv_usec == 0) {
-    ssl->d1->timeout_duration = 1;
+    ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms;
   }
 
   /* Set timeout to current time */
   get_current_time(ssl, &ssl->d1->next_timeout);
 
   /* Add duration to current time */
-  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.tv_sec += ssl->d1->timeout_duration_ms / 1000;
+  ssl->d1->next_timeout.tv_usec += (ssl->d1->timeout_duration_ms % 1000) * 1000;
+  if (ssl->d1->next_timeout.tv_usec >= 1000000) {
+    ssl->d1->next_timeout.tv_sec++;
+    ssl->d1->next_timeout.tv_usec -= 1000000;
+  }
+  BIO_ctrl(ssl->rbio, BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
            &ssl->d1->next_timeout);
 }
 
@@ -230,9 +239,9 @@
 }
 
 void dtls1_double_timeout(SSL *ssl) {
-  ssl->d1->timeout_duration *= 2;
-  if (ssl->d1->timeout_duration > 60) {
-    ssl->d1->timeout_duration = 60;
+  ssl->d1->timeout_duration_ms *= 2;
+  if (ssl->d1->timeout_duration_ms > 60000) {
+    ssl->d1->timeout_duration_ms = 60000;
   }
   dtls1_start_timer(ssl);
 }
@@ -241,8 +250,8 @@
   /* Reset everything */
   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->timeout_duration_ms = ssl->initial_timeout_duration_ms;
+  BIO_ctrl(ssl->rbio, BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
            &ssl->d1->next_timeout);
   /* Clear retransmission buffer */
   dtls1_clear_record_buffer(ssl);
@@ -254,8 +263,7 @@
   /* Reduce MTU after 2 unsuccessful retransmissions */
   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);
+    long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL);
     if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
       ssl->d1->mtu = (unsigned)mtu;
     }
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index 4690486..34eeddb 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -131,6 +131,16 @@
  * more data is needed. */
 static int dtls1_get_record(SSL *ssl) {
 again:
+  switch (ssl->s3->recv_shutdown) {
+    case ssl_shutdown_none:
+      break;
+    case ssl_shutdown_fatal_alert:
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+      return -1;
+    case ssl_shutdown_close_notify:
+      return 0;
+  }
+
   /* Read a new packet if there is no unconsumed one. */
   if (ssl_read_buffer_len(ssl) == 0) {
     int ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */);
@@ -217,7 +227,9 @@
    * alerts also aren't delivered reliably, so we may even time out because the
    * peer never received our close_notify. Report to the caller that the channel
    * has fully shut down. */
-  ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+  if (ssl->s3->recv_shutdown == ssl_shutdown_none) {
+    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+  }
 }
 
 /* Return up to 'len' payload bytes received in 'type' records.
@@ -273,26 +285,7 @@
 
   /* we now have a packet which can be read and processed */
 
-  /* If the other end has shut down, throw anything we read away (even in
-   * 'peek' mode) */
-  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
-    rr->length = 0;
-    return 0;
-  }
-
-
   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->s3->aead_read_ctx == NULL)) {
-      /* TODO(davidben): Is this check redundant with the handshake_func
-       * check? */
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_APP_DATA_IN_HANDSHAKE);
-      goto f_err;
-    }
-
     /* Discard empty records. */
     if (rr->length == 0) {
       goto start;
@@ -354,8 +347,7 @@
 
     if (alert_level == SSL3_AL_WARNING) {
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
-        ssl->s3->clean_shutdown = 1;
-        ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+        ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
         return 0;
       }
     } else if (alert_level == SSL3_AL_FATAL) {
@@ -364,7 +356,7 @@
       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);
-      ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+      ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
       SSL_CTX_remove_session(ssl->ctx, ssl->session);
       return 0;
     } else {
diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c
index e7b1607..324bff7 100644
--- a/src/ssl/d1_srtp.c
+++ b/src/ssl/d1_srtp.c
@@ -124,7 +124,7 @@
 #include "internal.h"
 
 
-const SRTP_PROTECTION_PROFILE kSRTPProfiles[] = {
+static const SRTP_PROTECTION_PROFILE kSRTPProfiles[] = {
     {
         "SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80,
     },
@@ -188,6 +188,7 @@
     }
   } while (col);
 
+  sk_SRTP_PROTECTION_PROFILE_free(*out);
   *out = profiles;
 
   return 1;
diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c
index d353281..9913e73 100644
--- a/src/ssl/d1_srvr.c
+++ b/src/ssl/d1_srvr.c
@@ -158,7 +158,7 @@
 
         if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
-          if (buf == NULL || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+          if (buf == NULL || !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
             ret = -1;
             goto end;
           }
@@ -168,7 +168,7 @@
 
         ssl->init_num = 0;
 
-        if (!ssl_init_wbio_buffer(ssl, 1)) {
+        if (!ssl_init_wbio_buffer(ssl)) {
           ret = -1;
           goto end;
         }
@@ -185,15 +185,12 @@
       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:
-        ssl->shutdown = 0;
         ret = ssl3_get_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
         dtls1_stop_timer(ssl);
         ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_HELLO_A:
@@ -213,12 +210,11 @@
         } else {
           ssl->state = SSL3_ST_SW_CERT_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_A:
       case SSL3_ST_SW_CERT_B:
-        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
           dtls1_start_timer(ssl);
           ret = ssl3_send_server_certificate(ssl);
           if (ret <= 0) {
@@ -233,7 +229,6 @@
           skip = 1;
           ssl->state = SSL3_ST_SW_KEY_EXCH_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_STATUS_A:
@@ -243,7 +238,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_SW_KEY_EXCH_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_KEY_EXCH_A:
@@ -271,7 +265,6 @@
         }
 
         ssl->state = SSL3_ST_SW_CERT_REQ_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_REQ_A:
@@ -286,7 +279,6 @@
           skip = 1;
         }
         ssl->state = SSL3_ST_SW_SRVR_DONE_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_DONE_A:
@@ -298,7 +290,6 @@
         }
         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:
@@ -311,36 +302,30 @@
         break;
 
       case SSL3_ST_SR_CERT_A:
-      case SSL3_ST_SR_CERT_B:
         if (ssl->s3->tmp.cert_request) {
           ret = ssl3_get_client_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
         }
-        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(ssl);
         if (ret <= 0) {
           goto end;
         }
         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(ssl);
         if (ret <= 0) {
           goto end;
         }
         ssl->state = SSL3_ST_SR_CHANGE;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SR_CHANGE:
@@ -358,9 +343,7 @@
         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);
+        ret = ssl3_get_finished(ssl);
         if (ret <= 0) {
           goto end;
         }
@@ -372,7 +355,6 @@
         } else {
           ssl->state = SSL3_ST_SW_CHANGE_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SESSION_TICKET_A:
@@ -382,7 +364,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_SW_CHANGE_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CHANGE_A:
@@ -395,7 +376,6 @@
         }
 
         ssl->state = SSL3_ST_SW_FINISHED_A;
-        ssl->init_num = 0;
 
         if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
@@ -416,7 +396,6 @@
         } else {
           ssl->s3->tmp.next_state = SSL_ST_OK;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL_ST_OK:
@@ -425,6 +404,10 @@
         /* remove buffering on output */
         ssl_free_wbio_buffer(ssl);
 
+	/* |init_buf| cannot be released because post-handshake retransmit
+         * relies on that buffer being available as scratch space.
+         *
+         * TODO(davidben): Fix this. */
         ssl->init_num = 0;
         ssl->s3->initial_handshake_complete = 1;
 
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 982f304..3b56e78 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -167,6 +167,7 @@
 #define SSL_kECDHE 0x00000004L
 /* SSL_kPSK is only set for plain PSK, not ECDHE_PSK. */
 #define SSL_kPSK 0x00000008L
+#define SSL_kCECPQ1 0x00000010L
 
 /* Bits for |algorithm_auth| (server authentication). */
 #define SSL_aRSA 0x00000001L
@@ -174,6 +175,8 @@
 /* SSL_aPSK is set for both PSK and ECDHE_PSK. */
 #define SSL_aPSK 0x00000004L
 
+#define SSL_aCERT (SSL_aRSA | SSL_aECDSA)
+
 /* Bits for |algorithm_enc| (symmetric encryption). */
 #define SSL_3DES 0x00000001L
 #define SSL_RC4 0x00000002L
@@ -239,17 +242,16 @@
  * server key used in |cipher| or |EVP_PKEY_NONE| if there is none. */
 int ssl_cipher_get_key_type(const SSL_CIPHER *cipher);
 
-/* ssl_cipher_has_server_public_key returns 1 if |cipher| involves a server
- * public key in the key exchange, sent in a server Certificate message.
- * Otherwise it returns 0. */
-int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher);
+/* ssl_cipher_uses_certificate_auth returns one if |cipher| authenticates the
+ * server and, optionally, the client with a certificate. Otherwise it returns
+ * zero. */
+int ssl_cipher_uses_certificate_auth(const SSL_CIPHER *cipher);
 
 /* ssl_cipher_requires_server_key_exchange returns 1 if |cipher| requires a
  * ServerKeyExchange message. Otherwise it returns 0.
  *
- * Unlike |ssl_cipher_has_server_public_key|, this function may return zero
- * while still allowing |cipher| an optional ServerKeyExchange. This is the
- * case for plain PSK ciphers. */
+ * This function may return zero while still allowing |cipher| an optional
+ * ServerKeyExchange. This is the case for plain PSK ciphers. */
 int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher);
 
 /* ssl_cipher_get_record_split_len, for TLS 1.0 CBC mode ciphers, returns the
@@ -282,6 +284,8 @@
   /* omit_version_in_ad is non-zero if the version should be omitted
    * in the AEAD's ad parameter. */
   char omit_version_in_ad;
+  /* omit_ad is non-zero if the AEAD's ad parameter should be omitted. */
+  char omit_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;
@@ -526,45 +530,66 @@
 int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len);
 
 
-/* ECDH curves. */
+/* ECDH groups. */
 
-#define SSL_CURVE_SECP256R1 23
-#define SSL_CURVE_SECP384R1 24
-#define SSL_CURVE_SECP521R1 25
-#define SSL_CURVE_X25519 29
+#define SSL_GROUP_SECP256R1 23
+#define SSL_GROUP_SECP384R1 24
+#define SSL_GROUP_SECP521R1 25
+#define SSL_GROUP_X25519 29
+#define SSL_GROUP_CECPQ1 65165
 
 /* 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;
+  uint16_t group_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
+  /* offer 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);
+  int (*offer)(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);
+  /* accept performs a key exchange against the |peer_key| generated by |offer|.
+   * On success, it returns one, writes the public value to |out_public_key|,
+   * 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|. On failure, it returns zero and sets |*out_alert| to an
+   * alert to send to the peer. */
+  int (*accept)(SSL_ECDH_CTX *ctx, CBB *out_public_key, uint8_t **out_secret,
+                size_t *out_secret_len, uint8_t *out_alert,
+                const uint8_t *peer_key, size_t peer_key_len);
+
+  /* finish performs a key exchange against the |peer_key| generated by
+   * |accept|. On success, it 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|. On
+   * failure, it returns zero and sets |*out_alert| to an alert to send to the
+   * peer. */
+  int (*finish)(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);
+
+  /* get_key initializes |out| with a length-prefixed key from |cbs|. It returns
+   * one on success and zero on error. */
+  int (*get_key)(CBS *cbs, CBS *out);
+
+  /* add_key initializes |out_contents| to receive a key. Typically it will then
+   * be passed to |offer| or |accept|. It returns one on success and zero on
+   * error. */
+  int (*add_key)(CBB *cbb, CBB *out_contents);
 } /* 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
+/* ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it
+ * sets |*out_group_id| to the group ID and returns one. Otherwise, it returns
  * zero. */
-int ssl_nid_to_curve_id(uint16_t *out_curve_id, int nid);
+int ssl_nid_to_group_id(uint16_t *out_group_id, int nid);
 
-/* SSL_ECDH_CTX_init sets up |ctx| for use with curve |curve_id|. It returns one
+/* SSL_ECDH_CTX_init sets up |ctx| for use with curve |group_id|. It returns one
  * on success and zero on error. */
-int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t curve_id);
+int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t group_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|. */
@@ -574,12 +599,31 @@
  * 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);
+/* SSL_ECDH_CTX_get_key calls the |get_key| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out);
+
+/* SSL_ECDH_CTX_add_key calls the |add_key| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents);
+
+/* SSL_ECDH_CTX_offer calls the |offer| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key);
+
+/* SSL_ECDH_CTX_accept calls the |accept| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+                        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_CTX_finish the |finish| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_finish(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);
+
+/* Handshake messages. */
+
+/* ssl_max_handshake_message_len returns the maximum number of bytes permitted
+ * in a handshake message for |ssl|. */
+size_t ssl_max_handshake_message_len(const SSL *ssl);
 
 
 /* Transport buffers. */
@@ -828,8 +872,7 @@
   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,
+  long (*ssl_get_message)(SSL *ssl, int msg_type,
                           enum ssl_hash_message_t hash_message, int *ok);
   int (*ssl_read_app_data)(SSL *ssl, uint8_t *buf, int len, int peek);
   int (*ssl_read_change_cipher_spec)(SSL *ssl);
@@ -956,13 +999,12 @@
    * timeout. */
   struct timeval next_timeout;
 
-  /* Timeout duration */
-  unsigned short timeout_duration;
+  /* timeout_duration_ms is the timeout duration in milliseconds. */
+  unsigned timeout_duration_ms;
 } DTLS1_STATE;
 
 extern const SSL3_ENC_METHOD TLSv1_enc_data;
 extern const SSL3_ENC_METHOD SSLv3_enc_data;
-extern const SRTP_PROTECTION_PROFILE kSRTPProfiles[];
 
 int ssl_clear_bad_session(SSL *ssl);
 CERT *ssl_cert_new(void);
@@ -1020,14 +1062,14 @@
 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_get_finished(SSL *ssl);
 int ssl3_send_change_cipher_spec(SSL *ssl, int state_a, int state_b);
 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);
+long ssl3_get_message(SSL *ssl, int msg_type,
+                      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. */
@@ -1132,11 +1174,15 @@
 int dtls1_connect(SSL *ssl);
 void dtls1_free(SSL *ssl);
 
-long dtls1_get_message(SSL *ssl, int st1, int stn, int mt, long max,
-                       enum ssl_hash_message_t hash_message, int *ok);
+long dtls1_get_message(SSL *ssl, int mt, enum ssl_hash_message_t hash_message,
+                       int *ok);
 int dtls1_dispatch_alert(SSL *ssl);
 
-int ssl_init_wbio_buffer(SSL *ssl, int push);
+/* ssl_is_wbio_buffered returns one if |ssl|'s write BIO is buffered and zero
+ * otherwise. */
+int ssl_is_wbio_buffered(const SSL *ssl);
+
+int ssl_init_wbio_buffer(SSL *ssl);
 void ssl_free_wbio_buffer(SSL *ssl);
 
 int tls1_change_cipher_state(SSL *ssl, int which);
@@ -1147,21 +1193,21 @@
 
 char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx);
 
-/* 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
+/* tls1_check_group_id returns one if |group_id| is consistent with both our
+ * and the peer's group 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);
+int tls1_check_group_id(SSL *ssl, uint16_t group_id);
 
-/* 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
+/* tls1_get_shared_group sets |*out_group_id| to the first preferred shared
+ * group between client and server preferences and returns one. If none may be
  * found, it returns zero. */
-int tls1_get_shared_curve(SSL *ssl, uint16_t *out_curve_id);
+int tls1_get_shared_group(SSL *ssl, uint16_t *out_group_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
- * returns one and writes the array to |*out_curve_ids| and its size to
- * |*out_curve_ids_len|. Otherwise, it returns zero. */
-int tls1_set_curves(uint16_t **out_curve_ids, size_t *out_curve_ids_len,
+ * into a newly allocated array of TLS group IDs. On success, the function
+ * returns one and writes the array to |*out_group_ids| and its size to
+ * |*out_group_ids_len|. Otherwise, it returns zero. */
+int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len,
                     const int *curves, size_t ncurves);
 
 /* tls1_check_ec_cert returns one if |x| is an ECC certificate with curve and
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 5d364ab..d5e304d 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -211,13 +211,13 @@
       ssl, !ssl->server, ssl->s3->tmp.peer_finish_md);
 }
 
-int ssl3_get_finished(SSL *ssl, int a, int b) {
+int ssl3_get_finished(SSL *ssl) {
   int al, finished_len, ok;
   long message_len;
   uint8_t *p;
 
-  message_len = ssl->method->ssl_get_message(
-      ssl, a, b, SSL3_MT_FINISHED, EVP_MAX_MD_SIZE, ssl_dont_hash_message, &ok);
+  message_len = ssl->method->ssl_get_message(ssl, SSL3_MT_FINISHED,
+                                             ssl_dont_hash_message, &ok);
 
   if (!ok) {
     return message_len;
@@ -298,125 +298,122 @@
   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 *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;
+size_t ssl_max_handshake_message_len(const SSL *ssl) {
+  /* kMaxMessageLen is the default maximum message size for handshakes which do
+   * not accept peer certificate chains. */
+  static const size_t kMaxMessageLen = 16384;
+
+  if ((!ssl->server || (ssl->verify_mode & SSL_VERIFY_PEER)) &&
+      kMaxMessageLen < ssl->max_cert_list) {
+    return ssl->max_cert_list;
+  }
+  return kMaxMessageLen;
+}
+
+static int extend_handshake_buffer(SSL *ssl, size_t length) {
+  if (!BUF_MEM_reserve(ssl->init_buf, length)) {
+    return -1;
+  }
+  while (ssl->init_buf->length < length) {
+    int ret =
+        ssl3_read_bytes(ssl, SSL3_RT_HANDSHAKE,
+                        (uint8_t *)ssl->init_buf->data + ssl->init_buf->length,
+                        length - ssl->init_buf->length, 0);
+    if (ret <= 0) {
+      return ret;
+    }
+    ssl->init_buf->length += (size_t)ret;
+  }
+  return 1;
+}
+
+/* Obtain handshake message of message type |msg_type| (any if |msg_type| ==
+ * -1). */
+long ssl3_get_message(SSL *ssl, int msg_type,
+                      enum ssl_hash_message_t hash_message, int *ok) {
+  *ok = 0;
 
   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);
+    assert(ssl->s3->tmp.message_complete);
     ssl->s3->tmp.reuse_message = 0;
     if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
-      al = SSL_AD_UNEXPECTED_MESSAGE;
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-      goto f_err;
+      return -1;
     }
     *ok = 1;
-    ssl->state = body_state;
+    assert(ssl->init_buf->length >= 4);
     ssl->init_msg = (uint8_t *)ssl->init_buf->data + 4;
-    ssl->init_num = (int)ssl->s3->tmp.message_size;
+    ssl->init_num = (int)ssl->init_buf->length - 4;
     return ssl->init_num;
   }
 
-  p = (uint8_t *)ssl->init_buf->data;
-
-  if (ssl->state == header_state) {
-    assert(ssl->init_num < 4);
-
-    for (;;) {
-      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;
-        }
-        ssl->init_num += bytes_read;
-      }
-
-      static const uint8_t kHelloRequest[4] = {SSL3_MT_HELLO_REQUEST, 0, 0, 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. */
-      ssl->init_num = 0;
-
-      if (ssl->msg_callback) {
-        ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, p, 4, ssl,
-                        ssl->msg_callback_arg);
-      }
-    }
-
-    /* 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;
-    }
-    ssl->s3->tmp.message_type = *(p++);
-
-    n2l3(p, l);
-    if (l > (unsigned long)max) {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
-      goto f_err;
-    }
-
-    if (l && !BUF_MEM_grow_clean(ssl->init_buf, l + 4)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
-      goto err;
-    }
-    ssl->s3->tmp.message_size = l;
-    ssl->state = body_state;
-
-    ssl->init_msg = (uint8_t *)ssl->init_buf->data + 4;
-    ssl->init_num = 0;
+again:
+  if (ssl->s3->tmp.message_complete) {
+    ssl->s3->tmp.message_complete = 0;
+    ssl->init_buf->length = 0;
   }
 
-  /* next state (body_state) */
-  p = ssl->init_msg;
-  n = ssl->s3->tmp.message_size - ssl->init_num;
-  while (n > 0) {
-    int bytes_read =
-        ssl3_read_bytes(ssl, SSL3_RT_HANDSHAKE, &p[ssl->init_num], n, 0);
-    if (bytes_read <= 0) {
-      *ok = 0;
-      return bytes_read;
-    }
-    ssl->init_num += bytes_read;
-    n -= bytes_read;
+  /* Read the message header, if we haven't yet. */
+  int ret = extend_handshake_buffer(ssl, 4);
+  if (ret <= 0) {
+    return ret;
   }
 
+  /* Parse out the length. Cap it so the peer cannot force us to buffer up to
+   * 2^24 bytes. */
+  const uint8_t *p = (uint8_t *)ssl->init_buf->data;
+  size_t msg_len = (((uint32_t)p[1]) << 16) | (((uint32_t)p[2]) << 8) | p[3];
+  if (msg_len > ssl_max_handshake_message_len(ssl)) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
+    return -1;
+  }
+
+  /* Read the message body, if we haven't yet. */
+  ret = extend_handshake_buffer(ssl, 4 + msg_len);
+  if (ret <= 0) {
+    return ret;
+  }
+
+  /* We have now received a complete message. */
+  ssl->s3->tmp.message_complete = 1;
+  if (ssl->msg_callback) {
+    ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
+                      ssl->init_buf->length, ssl, ssl->msg_callback_arg);
+  }
+
+  static const uint8_t kHelloRequest[4] = {SSL3_MT_HELLO_REQUEST, 0, 0, 0};
+  if (!ssl->server && ssl->init_buf->length == sizeof(kHelloRequest) &&
+      memcmp(kHelloRequest, ssl->init_buf->data, sizeof(kHelloRequest)) == 0) {
+    /* 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. */
+    goto again;
+  }
+
+  uint8_t actual_type = ((const uint8_t *)ssl->init_buf->data)[0];
+  if (msg_type >= 0 && actual_type != msg_type) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    return -1;
+  }
+  ssl->s3->tmp.message_type = actual_type;
+
+  ssl->init_msg = (uint8_t*)ssl->init_buf->data + 4;
+  ssl->init_num = ssl->init_buf->length - 4;
+
   /* Feed this message into MAC computation. */
   if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
-    goto err;
+    return -1;
   }
-  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 ssl->init_num;
-
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-
-err:
-  *ok = 0;
-  return -1;
 }
 
 int ssl3_hash_current_message(SSL *ssl) {
diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c
index 6f381cf..2665f15 100644
--- a/src/ssl/s3_clnt.c
+++ b/src/ssl/s3_clnt.c
@@ -200,7 +200,7 @@
         if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
           if (buf == NULL ||
-              !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+              !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
             ret = -1;
             goto end;
           }
@@ -208,14 +208,13 @@
           ssl->init_buf = buf;
           buf = NULL;
         }
+        ssl->init_num = 0;
 
-        if (!ssl_init_wbio_buffer(ssl, 0)) {
+        if (!ssl_init_wbio_buffer(ssl)) {
           ret = -1;
           goto end;
         }
 
-        /* don't push the buffering BIO quite yet */
-
         if (!ssl3_init_handshake_buffer(ssl)) {
           OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
           ret = -1;
@@ -223,28 +222,19 @@
         }
 
         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:
-        ssl->shutdown = 0;
         ret = ssl3_send_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
-        ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
-        ssl->init_num = 0;
-
-        /* turn on buffering for the next lot of output */
-        if (ssl->bbio != ssl->wbio) {
-          ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
-        }
-
+        ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
+        ssl->state = SSL3_ST_CW_FLUSH;
         break;
 
       case SSL3_ST_CR_SRVR_HELLO_A:
-      case SSL3_ST_CR_SRVR_HELLO_B:
         ret = ssl3_get_server_hello(ssl);
         if (ret <= 0) {
           goto end;
@@ -259,12 +249,10 @@
         } else {
           ssl->state = SSL3_ST_CR_CERT_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CERT_A:
-      case SSL3_ST_CR_CERT_B:
-        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
           ret = ssl3_get_server_certificate(ssl);
           if (ret <= 0) {
             goto end;
@@ -278,7 +266,6 @@
           skip = 1;
           ssl->state = SSL3_ST_CR_KEY_EXCH_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_VERIFY_SERVER_CERT:
@@ -288,31 +275,29 @@
         }
 
         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(ssl);
         if (ret <= 0) {
           goto end;
         }
-        ssl->state = SSL3_ST_CR_CERT_REQ_A;
-        ssl->init_num = 0;
+        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+          ssl->state = SSL3_ST_CR_CERT_REQ_A;
+        } else {
+          ssl->state = SSL3_ST_CR_SRVR_DONE_A;
+        }
         break;
 
       case SSL3_ST_CR_CERT_REQ_A:
-      case SSL3_ST_CR_CERT_REQ_B:
         ret = ssl3_get_certificate_request(ssl);
         if (ret <= 0) {
           goto end;
         }
         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(ssl);
         if (ret <= 0) {
           goto end;
@@ -322,7 +307,6 @@
         } else {
           ssl->state = SSL3_ST_CW_KEY_EXCH_A;
         }
-        ssl->init_num = 0;
 
         break;
 
@@ -335,7 +319,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_CW_KEY_EXCH_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_KEY_EXCH_A:
@@ -351,8 +334,6 @@
         } else {
           ssl->state = SSL3_ST_CW_CHANGE_A;
         }
-
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CERT_VRFY_A:
@@ -363,7 +344,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_CW_CHANGE_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_CHANGE_A:
@@ -381,7 +361,6 @@
         if (ssl->s3->next_proto_neg_seen) {
           ssl->state = SSL3_ST_CW_NEXT_PROTO_A;
         }
-        ssl->init_num = 0;
 
         if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
@@ -448,27 +427,22 @@
             }
           }
         }
-        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(ssl);
         if (ret <= 0) {
           goto end;
         }
         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(ssl);
         if (ret <= 0) {
           goto end;
         }
         ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CR_CHANGE:
@@ -485,9 +459,7 @@
         break;
 
       case SSL3_ST_CR_FINISHED_A:
-      case SSL3_ST_CR_FINISHED_B:
-        ret = ssl3_get_finished(ssl, SSL3_ST_CR_FINISHED_A,
-                                SSL3_ST_CR_FINISHED_B);
+        ret = ssl3_get_finished(ssl);
         if (ret <= 0) {
           goto end;
         }
@@ -497,7 +469,6 @@
         } else {
           ssl->state = SSL_ST_OK;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_CW_FLUSH:
@@ -528,13 +499,13 @@
 
         BUF_MEM_free(ssl->init_buf);
         ssl->init_buf = NULL;
+        ssl->init_num = 0;
 
         /* Remove write buffering now. */
         ssl_free_wbio_buffer(ssl);
 
         const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
 
-        ssl->init_num = 0;
         ssl->s3->tmp.in_false_start = 0;
         ssl->s3->initial_handshake_complete = 1;
 
@@ -733,10 +704,8 @@
   uint16_t server_version, cipher_suite;
   uint8_t compression_method;
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_HELLO, ssl_hash_message,
+                                   &ok);
 
   if (!ok) {
     uint32_t err = ERR_peek_error();
@@ -866,7 +835,8 @@
   /* 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->hit || ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
+  if (ssl->hit || ssl3_protocol_version(ssl) < TLS1_2_VERSION ||
+      !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
     ssl3_free_handshake_buffer(ssl);
   }
 
@@ -963,9 +933,8 @@
   CBS cbs, certificate_list;
   const uint8_t *data;
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE, ssl_hash_message,
+                                   &ok);
 
   if (!ok) {
     return n;
@@ -1053,11 +1022,7 @@
   EC_KEY *ecdh = NULL;
   EC_POINT *srvr_ecpoint = NULL;
 
-  /* use same message size as in ssl3_get_certificate_request() as
-   * ServerKeyExchange message may be skipped */
-  long 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);
+  long n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
   if (!ok) {
     return n;
   }
@@ -1167,26 +1132,27 @@
     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|. */
+    /* |dh_Ys| was initialized with CBS_get_u16_length_prefixed, so peer_key_len
+     * 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;
+    uint8_t group_type;
+    uint16_t group_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) ||
+    if (!CBS_get_u8(&server_key_exchange, &group_type) ||
+        group_type != NAMED_CURVE_TYPE ||
+        !CBS_get_u16(&server_key_exchange, &group_id) ||
         !CBS_get_u8_length_prefixed(&server_key_exchange, &point)) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto f_err;
     }
-    ssl->session->key_exchange_info = curve_id;
+    ssl->session->key_exchange_info = group_id;
 
-    /* Ensure the curve is consistent with preferences. */
-    if (!tls1_check_curve_id(ssl, curve_id)) {
+    /* Ensure the group is consistent with preferences. */
+    if (!tls1_check_group_id(ssl, group_id)) {
       al = SSL_AD_ILLEGAL_PARAMETER;
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       goto f_err;
@@ -1194,11 +1160,31 @@
 
     /* 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) ||
+    if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, group_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|. */
+    /* |point| was initialized with CBS_get_u8_length_prefixed, so peer_key_len
+     * 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_kCECPQ1) {
+    if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, SSL_GROUP_CECPQ1)) {
+      goto err;
+    }
+    CBS key;
+    if (!CBS_get_u16_length_prefixed(&server_key_exchange, &key)) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      goto f_err;
+    }
+
+    size_t peer_key_len;
+    if (!CBS_stow(&key, &ssl->s3->tmp.peer_key, &peer_key_len)) {
+      goto err;
+    }
+    /* |key| was initialized with CBS_get_u16_length_prefixed, so peer_key_len
+     * 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)) {
@@ -1215,7 +1201,7 @@
            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(ssl->s3->tmp.new_cipher)) {
+  if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
     pkey = X509_get_pubkey(ssl->session->peer);
     if (pkey == NULL) {
       goto err;
@@ -1302,9 +1288,7 @@
   X509_NAME *xn = NULL;
   STACK_OF(X509_NAME) *ca_sk = NULL;
 
-  long 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);
+  long n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1409,9 +1393,8 @@
 
 int ssl3_get_new_session_ticket(SSL *ssl) {
   int ok, al;
-  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);
+  long n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET,
+                                        ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1487,9 +1470,7 @@
   CBS certificate_status, ocsp_response;
   uint8_t status_type;
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1530,10 +1511,8 @@
   int ok;
   long n;
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_DONE, ssl_hash_message,
+                                   &ok);
 
   if (!ok) {
     return n;
@@ -1657,31 +1636,24 @@
         !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. */
+  } else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) {
+    /* Generate a keypair and serialize the public half. */
     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)) {
+    if (!SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &cbb, &child)) {
       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)) {
+    if (!SSL_ECDH_CTX_accept(&ssl->s3->tmp.ecdh_ctx, &child, &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;
     }
+    if (!CBB_flush(&cbb)) {
+      goto err;
+    }
 
     /* The key exchange state may now be discarded. */
     SSL_ECDH_CTX_cleanup(&ssl->s3->tmp.ecdh_ctx);
@@ -2020,7 +1992,8 @@
       !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)) {
+      !ssl_set_handshake_header(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS,
+                                length)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     goto err;
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 7df046f..4cf5aa3 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -220,7 +220,7 @@
 
   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_supported_group_list);
   OPENSSL_free(ssl->s3->tmp.peer_psk_identity_hint);
   ssl3_free_handshake_buffer(ssl);
   ssl3_free_handshake_hash(ssl);
@@ -382,14 +382,14 @@
 }
 
 int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len) {
-  return tls1_set_curves(&ctx->tlsext_ellipticcurvelist,
-                         &ctx->tlsext_ellipticcurvelist_length, curves,
+  return tls1_set_curves(&ctx->supported_group_list,
+                         &ctx->supported_group_list_len, curves,
                          curves_len);
 }
 
 int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) {
-  return tls1_set_curves(&ssl->tlsext_ellipticcurvelist,
-                         &ssl->tlsext_ellipticcurvelist_length, curves,
+  return tls1_set_curves(&ssl->supported_group_list,
+                         &ssl->supported_group_list_len, curves,
                          curves_len);
 }
 
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index d9c21d4..c54c10b 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -133,6 +133,16 @@
 static int ssl3_get_record(SSL *ssl) {
   int ret;
 again:
+  switch (ssl->s3->recv_shutdown) {
+    case ssl_shutdown_none:
+      break;
+    case ssl_shutdown_fatal_alert:
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+      return -1;
+    case ssl_shutdown_close_notify:
+      return 0;
+  }
+
   /* Ensure the buffer is large enough to decrypt in-place. */
   ret = ssl_read_buffer_extend_to(ssl, ssl_record_prefix_len(ssl));
   if (ret <= 0) {
@@ -393,27 +403,9 @@
 
   /* we now have a packet which can be read and processed */
 
-  /* If the other end has shut down, throw anything we read away (even in
-   * 'peek' mode) */
-  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
-    rr->length = 0;
-    return 0;
-  }
-
   if (type != 0 && type == rr->type) {
     ssl->s3->warning_alert_count = 0;
 
-    /* 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->s3->aead_read_ctx == NULL) {
-      /* TODO(davidben): Is this check redundant with the handshake_func
-       * check? */
-      al = SSL_AD_UNEXPECTED_MESSAGE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_APP_DATA_IN_HANDSHAKE);
-      goto f_err;
-    }
-
     /* Discard empty records. */
     if (rr->length == 0) {
       goto start;
@@ -546,8 +538,7 @@
 
     if (alert_level == SSL3_AL_WARNING) {
       if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
-        ssl->s3->clean_shutdown = 1;
-        ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+        ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
         return 0;
       }
 
@@ -563,7 +554,7 @@
       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);
-      ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+      ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
       SSL_CTX_remove_session(ssl->ctx, ssl->session);
       return 0;
     } else {
@@ -575,7 +566,9 @@
     goto start;
   }
 
-  if (ssl->shutdown & SSL_SENT_SHUTDOWN) {
+  if (type == 0) {
+    /* This may only occur from read_close_notify. */
+    assert(ssl->s3->send_shutdown == ssl_shutdown_close_notify);
     /* close_notify has been sent, so discard all records other than alerts. */
     rr->length = 0;
     goto start;
@@ -591,9 +584,19 @@
 }
 
 int ssl3_send_alert(SSL *ssl, int level, int desc) {
-  /* If a fatal one, remove from cache */
-  if (level == 2 && ssl->session != NULL) {
-    SSL_CTX_remove_session(ssl->ctx, ssl->session);
+  /* It is illegal to send an alert when we've already sent a closing one. */
+  if (ssl->s3->send_shutdown != ssl_shutdown_none) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+    return -1;
+  }
+
+  if (level == SSL3_AL_FATAL) {
+    if (ssl->session != NULL) {
+      SSL_CTX_remove_session(ssl->ctx, ssl->session);
+    }
+    ssl->s3->send_shutdown = ssl_shutdown_fatal_alert;
+  } else if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) {
+    ssl->s3->send_shutdown = ssl_shutdown_close_notify;
   }
 
   ssl->s3->alert_dispatch = 1;
@@ -605,8 +608,7 @@
     return ssl->method->ssl_dispatch_alert(ssl);
   }
 
-  /* else data is still being written out, we will get written some time in the
-   * future */
+  /* The alert will be dispatched later. */
   return -1;
 }
 
diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c
index f06ee56..50007eb 100644
--- a/src/ssl/s3_srvr.c
+++ b/src/ssl/s3_srvr.c
@@ -208,7 +208,7 @@
 
         if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
-          if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+          if (!buf || !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
             ret = -1;
             goto end;
           }
@@ -219,7 +219,7 @@
 
         /* Enable a write buffer. This groups handshake messages within a flight
          * into a single write. */
-        if (!ssl_init_wbio_buffer(ssl, 1)) {
+        if (!ssl_init_wbio_buffer(ssl)) {
           ret = -1;
           goto end;
         }
@@ -257,14 +257,11 @@
       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:
-        ssl->shutdown = 0;
         ret = ssl3_get_client_hello(ssl);
         if (ret <= 0) {
           goto end;
         }
         ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_HELLO_A:
@@ -282,12 +279,11 @@
         } else {
           ssl->state = SSL3_ST_SW_CERT_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_A:
       case SSL3_ST_SW_CERT_B:
-        if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
           ret = ssl3_send_server_certificate(ssl);
           if (ret <= 0) {
             goto end;
@@ -301,7 +297,6 @@
           skip = 1;
           ssl->state = SSL3_ST_SW_KEY_EXCH_A;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_STATUS_A:
@@ -311,7 +306,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_SW_KEY_EXCH_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_KEY_EXCH_A:
@@ -337,7 +331,6 @@
         }
 
         ssl->state = SSL3_ST_SW_CERT_REQ_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CERT_REQ_A:
@@ -351,7 +344,6 @@
           skip = 1;
         }
         ssl->state = SSL3_ST_SW_SRVR_DONE_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SRVR_DONE_A:
@@ -362,7 +354,6 @@
         }
         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:
@@ -381,37 +372,31 @@
         break;
 
       case SSL3_ST_SR_CERT_A:
-      case SSL3_ST_SR_CERT_B:
         if (ssl->s3->tmp.cert_request) {
           ret = ssl3_get_client_certificate(ssl);
           if (ret <= 0) {
             goto end;
           }
         }
-        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(ssl);
         if (ret <= 0) {
           goto end;
         }
         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(ssl);
         if (ret <= 0) {
           goto end;
         }
 
         ssl->state = SSL3_ST_SR_CHANGE;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SR_CHANGE:
@@ -435,12 +420,10 @@
         break;
 
       case SSL3_ST_SR_NEXT_PROTO_A:
-      case SSL3_ST_SR_NEXT_PROTO_B:
         ret = ssl3_get_next_proto(ssl);
         if (ret <= 0) {
           goto end;
         }
-        ssl->init_num = 0;
         if (ssl->s3->tlsext_channel_id_valid) {
           ssl->state = SSL3_ST_SR_CHANNEL_ID_A;
         } else {
@@ -449,19 +432,15 @@
         break;
 
       case SSL3_ST_SR_CHANNEL_ID_A:
-      case SSL3_ST_SR_CHANNEL_ID_B:
         ret = ssl3_get_channel_id(ssl);
         if (ret <= 0) {
           goto end;
         }
-        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(ssl, SSL3_ST_SR_FINISHED_A,
-                                SSL3_ST_SR_FINISHED_B);
+        ret = ssl3_get_finished(ssl);
         if (ret <= 0) {
           goto end;
         }
@@ -482,7 +461,6 @@
             goto end;
           }
         }
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_SESSION_TICKET_A:
@@ -492,7 +470,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_SW_CHANGE_A;
-        ssl->init_num = 0;
         break;
 
       case SSL3_ST_SW_CHANGE_A:
@@ -503,7 +480,6 @@
           goto end;
         }
         ssl->state = SSL3_ST_SW_FINISHED_A;
-        ssl->init_num = 0;
 
         if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
@@ -524,7 +500,6 @@
         } else {
           ssl->s3->tmp.next_state = SSL_ST_OK;
         }
-        ssl->init_num = 0;
         break;
 
       case SSL_ST_OK:
@@ -533,11 +508,11 @@
 
         BUF_MEM_free(ssl->init_buf);
         ssl->init_buf = NULL;
+        ssl->init_num = 0;
 
         /* remove buffering on output */
         ssl_free_wbio_buffer(ssl);
 
-        ssl->init_num = 0;
 
         /* If we aren't retaining peer certificates then we can discard it
          * now. */
@@ -626,7 +601,7 @@
   const uint8_t *p;
   int ret;
   CBS v2_client_hello, cipher_specs, session_id, challenge;
-  size_t msg_length, rand_len, len;
+  size_t msg_length, rand_len;
   uint8_t msg_type;
   uint16_t version, cipher_spec_length, session_id_length, challenge_length;
   CBB client_hello, hello_body, cipher_suites;
@@ -731,7 +706,7 @@
 
   /* Add the null compression scheme and finish. */
   if (!CBB_add_u8(&hello_body, 1) || !CBB_add_u8(&hello_body, 0) ||
-      !CBB_finish(&client_hello, NULL, &len)) {
+      !CBB_finish(&client_hello, NULL, &ssl->init_buf->length)) {
     CBB_cleanup(&client_hello);
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
@@ -740,8 +715,7 @@
   /* Mark the message for "re"-use by the version-specific method. */
   ssl->s3->tmp.reuse_message = 1;
   ssl->s3->tmp.message_type = SSL3_MT_CLIENT_HELLO;
-  /* The handshake message header is 4 bytes. */
-  ssl->s3->tmp.message_size = len - 4;
+  ssl->s3->tmp.message_complete = 1;
 
   /* Consume and discard the V2ClientHello. */
   ssl_read_buffer_consume(ssl, 2 + msg_length);
@@ -767,20 +741,17 @@
    * SSLv3, even if prompted with TLSv1. */
   switch (ssl->state) {
     case SSL3_ST_SR_CLNT_HELLO_A:
-    case 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);
+      n = ssl->method->ssl_get_message(ssl, SSL3_MT_CLIENT_HELLO,
+                                       ssl_hash_message, &ok);
 
       if (!ok) {
         return n;
       }
 
-      ssl->state = SSL3_ST_SR_CLNT_HELLO_C;
+      ssl->state = SSL3_ST_SR_CLNT_HELLO_B;
       /* fallthrough */
+    case SSL3_ST_SR_CLNT_HELLO_B:
     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. */
@@ -796,9 +767,9 @@
         goto f_err;
       }
 
-      if (ssl->state == SSL3_ST_SR_CLNT_HELLO_C &&
+      if (ssl->state == SSL3_ST_SR_CLNT_HELLO_B &&
           ssl->ctx->select_certificate_cb != NULL) {
-        ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
+        ssl->state = SSL3_ST_SR_CLNT_HELLO_C;
         switch (ssl->ctx->select_certificate_cb(&early_ctx)) {
           case 0:
             ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
@@ -814,7 +785,7 @@
             /* fallthrough */;
         }
       }
-      ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
+      ssl->state = SSL3_ST_SR_CLNT_HELLO_C;
       break;
 
     default:
@@ -1053,8 +1024,8 @@
         ssl->s3->tlsext_channel_id_valid) {
       ssl->s3->tmp.cert_request = 0;
     }
-    /* Plain PSK forbids Certificate and CertificateRequest. */
-    if (ssl->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK) {
+    /* CertificateRequest may only be sent in certificate-based ciphers. */
+    if (!ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
       ssl->s3->tmp.cert_request = 0;
     }
   } else {
@@ -1239,25 +1210,31 @@
           !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)) {
+          !SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
         goto err;
       }
     } else if (alg_k & SSL_kECDHE) {
-      /* Determine the curve to use. */
-      uint16_t curve_id;
-      if (!tls1_get_shared_curve(ssl, &curve_id)) {
+      /* Determine the group to use. */
+      uint16_t group_id;
+      if (!tls1_get_shared_group(ssl, &group_id)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY);
         ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
         goto err;
       }
-      ssl->session->key_exchange_info = curve_id;
+      ssl->session->key_exchange_info = group_id;
 
       /* Set up ECDH, generate a key, and emit the public half. */
-      if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id) ||
+      if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, group_id) ||
           !CBB_add_u8(&cbb, NAMED_CURVE_TYPE) ||
-          !CBB_add_u16(&cbb, curve_id) ||
+          !CBB_add_u16(&cbb, group_id) ||
           !CBB_add_u8_length_prefixed(&cbb, &child) ||
-          !SSL_ECDH_CTX_generate_keypair(&ssl->s3->tmp.ecdh_ctx, &child)) {
+          !SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
+        goto err;
+      }
+    } else if (alg_k & SSL_kCECPQ1) {
+      if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, SSL_GROUP_CECPQ1) ||
+          !CBB_add_u16_length_prefixed(&cbb, &child) ||
+          !SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
         goto err;
       }
     } else {
@@ -1272,7 +1249,7 @@
   }
 
   /* Add a signature. */
-  if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+  if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
     if (!ssl_has_private_key(ssl)) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       goto err;
@@ -1449,12 +1426,10 @@
   unsigned psk_len = 0;
   uint8_t psk[PSK_MAX_PSK_LEN];
 
-  if (ssl->state == SSL3_ST_SR_KEY_EXCH_A ||
-      ssl->state == SSL3_ST_SR_KEY_EXCH_B) {
+  if (ssl->state == SSL3_ST_SR_KEY_EXCH_A) {
     int ok;
     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);
+        ssl, SSL3_MT_CLIENT_KEY_EXCHANGE, ssl_hash_message, &ok);
     if (!ok) {
       return n;
     }
@@ -1524,7 +1499,7 @@
 
     enum ssl_private_key_result_t decrypt_result;
     size_t decrypt_len;
-    if (ssl->state == SSL3_ST_SR_KEY_EXCH_B) {
+    if (ssl->state == SSL3_ST_SR_KEY_EXCH_A) {
       if (!ssl_has_private_key(ssl) ||
           ssl_private_key_type(ssl) != EVP_PKEY_RSA) {
         al = SSL_AD_HANDSHAKE_FAILURE;
@@ -1552,7 +1527,7 @@
           CBS_data(&encrypted_premaster_secret),
           CBS_len(&encrypted_premaster_secret));
     } else {
-      assert(ssl->state == SSL3_ST_SR_KEY_EXCH_C);
+      assert(ssl->state == SSL3_ST_SR_KEY_EXCH_B);
       /* Complete async decrypt. */
       decrypt_result = ssl_private_key_decrypt_complete(
           ssl, decrypt_buf, &decrypt_len, rsa_size);
@@ -1565,7 +1540,7 @@
         goto err;
       case ssl_private_key_retry:
         ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
-        ssl->state = SSL3_ST_SR_KEY_EXCH_C;
+        ssl->state = SSL3_ST_SR_KEY_EXCH_B;
         goto err;
     }
 
@@ -1621,19 +1596,12 @@
 
     OPENSSL_free(decrypt_buf);
     decrypt_buf = NULL;
-  } else if (alg_k & (SSL_kECDHE|SSL_kDHE)) {
-    /* Parse the ClientKeyExchange. ECDHE uses a u8 length prefix while DHE uses
-     * u16. */
+  } else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) {
+    /* Parse the ClientKeyExchange. */
     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);
-    }
-
-    if (!peer_key_ok || CBS_len(&client_key_exchange) != 0) {
+    if (!SSL_ECDH_CTX_get_key(&ssl->s3->tmp.ecdh_ctx, &client_key_exchange,
+                              &peer_key) ||
+        CBS_len(&client_key_exchange) != 0) {
       al = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       goto f_err;
@@ -1641,9 +1609,9 @@
 
     /* 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))) {
+    if (!SSL_ECDH_CTX_finish(&ssl->s3->tmp.ecdh_ctx, &premaster_secret,
+                             &premaster_secret_len, &alert, CBS_data(&peer_key),
+                             CBS_len(&peer_key))) {
       al = alert;
       goto f_err;
     }
@@ -1734,10 +1702,8 @@
     return 1;
   }
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE_VERIFY,
+                                   ssl_dont_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1833,9 +1799,7 @@
   int is_first_certificate = 1;
 
   assert(ssl->s3->tmp.cert_request);
-  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);
+  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -2023,7 +1987,7 @@
       p += placeholder_len;
 
       len = p - ssl_handshake_start(ssl);
-      if (!ssl_set_handshake_header(ssl, SSL3_MT_NEWSESSION_TICKET, len)) {
+      if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) {
         goto err;
       }
       ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
@@ -2092,7 +2056,7 @@
     /* Skip ticket lifetime hint */
     p = ssl_handshake_start(ssl) + 4;
     s2n(len - 6, p);
-    if (!ssl_set_handshake_header(ssl, SSL3_MT_NEWSESSION_TICKET, len)) {
+    if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) {
       goto err;
     }
     ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
@@ -2122,10 +2086,8 @@
     return -1;
   }
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message,
+                                   &ok);
 
   if (!ok) {
     return n;
@@ -2164,10 +2126,8 @@
   BIGNUM x, y;
   CBS encrypted_extensions, extension;
 
-  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);
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS,
+                                   ssl_dont_hash_message, &ok);
 
   if (!ok) {
     return n;
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index 4de9d45..1e549ea 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -92,6 +92,15 @@
     if (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) {
       aead_ctx->variable_nonce_included_in_record = 1;
     }
+
+    /* The TLS 1.3 construction XORs the fixed nonce into the sequence number
+     * and omits the additional data. */
+    if (version >= TLS1_3_VERSION) {
+      aead_ctx->xor_fixed_nonce = 1;
+      aead_ctx->variable_nonce_len = 8;
+      aead_ctx->variable_nonce_included_in_record = 0;
+      aead_ctx->omit_ad = 1;
+    }
   } else {
     aead_ctx->variable_nonce_included_in_record = 1;
     aead_ctx->random_variable_nonce = 1;
@@ -139,6 +148,10 @@
                                   uint8_t type, uint16_t wire_version,
                                   const uint8_t seqnum[8],
                                   size_t plaintext_len) {
+  if (aead->omit_ad) {
+    return 0;
+  }
+
   memcpy(out, seqnum, 8);
   size_t len = 8;
   out[len++] = type;
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 58ce582..dcee293 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -375,6 +375,52 @@
      SSL_HANDSHAKE_MAC_SHA384,
     },
 
+    /* CECPQ1 (combined elliptic curve + post-quantum) suites. */
+
+    /* Cipher 16B7 */
+    {
+     TLS1_TXT_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kCECPQ1,
+     SSL_aRSA,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    /* Cipher 16B8 */
+    {
+     TLS1_TXT_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kCECPQ1,
+     SSL_aECDSA,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    /* Cipher 16B9 */
+    {
+     TLS1_TXT_CECPQ1_RSA_WITH_AES_256_GCM_SHA384,
+     TLS1_CK_CECPQ1_RSA_WITH_AES_256_GCM_SHA384,
+     SSL_kCECPQ1,
+     SSL_aRSA,
+     SSL_AES256GCM,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA384,
+    },
+
+    /* Cipher 16BA */
+    {
+     TLS1_TXT_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384,
+     TLS1_CK_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384,
+     SSL_kCECPQ1,
+     SSL_aECDSA,
+     SSL_AES256GCM,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA384,
+    },
+
     /* Cipher C007 */
     {
      TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA,
@@ -615,6 +661,7 @@
      SSL_AEAD,
      SSL_HANDSHAKE_MAC_SHA256,
     },
+
 };
 
 static const size_t kCiphersLen = sizeof(kCiphers) / sizeof(kCiphers[0]);
@@ -651,8 +698,9 @@
 } CIPHER_ALIAS;
 
 static const CIPHER_ALIAS kCipherAliases[] = {
-    /* "ALL" doesn't include eNULL (must be specifically enabled) */
-    {"ALL", ~0u, ~0u, ~SSL_eNULL, ~0u, 0},
+    /* "ALL" doesn't include eNULL nor kCECPQ1. These must be explicitly
+     * enabled. */
+    {"ALL", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, 0},
 
     /* The "COMPLEMENTOFDEFAULT" rule is omitted. It matches nothing. */
 
@@ -667,15 +715,16 @@
     {"DH", SSL_kDHE, ~0u, ~0u, ~0u, 0},
 
     {"kECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
+    {"kCECPQ1", SSL_kCECPQ1, ~0u, ~0u, ~0u, 0},
     {"kEECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
     {"ECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
 
     {"kPSK", SSL_kPSK, ~0u, ~0u, ~0u, 0},
 
     /* server authentication aliases */
-    {"aRSA", ~0u, SSL_aRSA, ~SSL_eNULL, ~0u, 0},
-    {"aECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0},
-    {"ECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0},
+    {"aRSA", ~SSL_kCECPQ1, SSL_aRSA, ~SSL_eNULL, ~0u, 0},
+    {"aECDSA", ~SSL_kCECPQ1, SSL_aECDSA, ~0u, ~0u, 0},
+    {"ECDSA", ~SSL_kCECPQ1, SSL_aECDSA, ~0u, ~0u, 0},
     {"aPSK", ~0u, SSL_aPSK, ~0u, ~0u, 0},
 
     /* aliases combining key exchange and server authentication */
@@ -690,29 +739,29 @@
     {"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,
+    {"AES256", ~SSL_kCECPQ1, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, 0},
+    {"AES", ~SSL_kCECPQ1, ~0u, SSL_AES, ~0u, 0},
+    {"AESGCM", ~SSL_kCECPQ1, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, 0},
+    {"CHACHA20", ~SSL_kCECPQ1, ~0u, SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD, ~0u,
      0},
 
     /* MAC aliases */
     {"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},
+    {"SHA256", ~SSL_kCECPQ1, ~0u, ~0u, SSL_SHA256, 0},
+    {"SHA384", ~SSL_kCECPQ1, ~0u, ~0u, SSL_SHA384, 0},
 
     /* 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},
+    {"SSLv3", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
+    {"TLSv1", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
+    {"TLSv1.2", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, TLS1_2_VERSION},
 
     /* 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},
+    {"HIGH", ~SSL_kCECPQ1, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
+    {"FIPS", ~SSL_kCECPQ1, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
 };
 
 static const size_t kCipherAliasesLen =
@@ -1404,6 +1453,7 @@
 
   /* 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, 0, CIPHER_ADD, -1,
                         0, &head, &tail);
   ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0,
@@ -1623,6 +1673,10 @@
   return (cipher->algorithm_mkey & SSL_kECDHE) != 0;
 }
 
+int SSL_CIPHER_is_CECPQ1(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mkey & SSL_kCECPQ1) != 0;
+}
+
 uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher) {
   if (cipher->algorithm_prf != SSL_HANDSHAKE_MAC_DEFAULT) {
     /* Cipher suites before TLS 1.2 use the default PRF, while all those added
@@ -1672,6 +1726,17 @@
           return "UNKNOWN";
       }
 
+    case SSL_kCECPQ1:
+      switch (cipher->algorithm_auth) {
+        case SSL_aECDSA:
+          return "CECPQ1_ECDSA";
+        case SSL_aRSA:
+          return "CECPQ1_RSA";
+        default:
+          assert(0);
+          return "UNKNOWN";
+      }
+
     case SSL_kPSK:
       assert(cipher->algorithm_auth == SSL_aPSK);
       return "PSK";
@@ -1826,6 +1891,10 @@
       kx = "ECDH";
       break;
 
+    case SSL_kCECPQ1:
+      kx = "CECPQ1";
+      break;
+
     case SSL_kPSK:
       kx = "PSK";
       break;
@@ -1957,20 +2026,15 @@
   return EVP_PKEY_NONE;
 }
 
-int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher) {
-  /* PSK-authenticated ciphers do not use a certificate. (RSA_PSK is not
-   * supported.) */
-  if (cipher->algorithm_auth & SSL_aPSK) {
-    return 0;
-  }
-
-  /* All other ciphers include it. */
-  return 1;
+int ssl_cipher_uses_certificate_auth(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_auth & SSL_aCERT) != 0;
 }
 
 int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher) {
   /* Ephemeral Diffie-Hellman key exchanges require a ServerKeyExchange. */
-  if (cipher->algorithm_mkey & SSL_kDHE || cipher->algorithm_mkey & SSL_kECDHE) {
+  if (cipher->algorithm_mkey & SSL_kDHE ||
+      cipher->algorithm_mkey & SSL_kECDHE ||
+      cipher->algorithm_mkey & SSL_kCECPQ1) {
     return 1;
   }
 
diff --git a/src/ssl/ssl_ecdh.c b/src/ssl/ssl_ecdh.c
index d48c93f..1236cd3 100644
--- a/src/ssl/ssl_ecdh.c
+++ b/src/ssl/ssl_ecdh.c
@@ -23,6 +23,7 @@
 #include <openssl/ec.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
+#include <openssl/newhope.h>
 #include <openssl/nid.h>
 
 #include "internal.h"
@@ -35,7 +36,7 @@
   BN_clear_free(private_key);
 }
 
-static int ssl_ec_point_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+static int ssl_ec_point_offer(SSL_ECDH_CTX *ctx, CBB *out) {
   assert(ctx->data == NULL);
   BIGNUM *private_key = BN_new();
   if (private_key == NULL) {
@@ -84,12 +85,9 @@
   return ret;
 }
 
-static 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) {
+static int ssl_ec_point_finish(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;
@@ -150,6 +148,18 @@
   return ret;
 }
 
+static int ssl_ec_point_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+                               uint8_t **out_secret, size_t *out_secret_len,
+                               uint8_t *out_alert, const uint8_t *peer_key,
+                               size_t peer_key_len) {
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+  if (!ssl_ec_point_offer(ctx, out_public_key) ||
+      !ssl_ec_point_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
+                           peer_key_len)) {
+    return 0;
+  }
+  return 1;
+}
 
 /* X25119 implementation. */
 
@@ -161,7 +171,7 @@
   OPENSSL_free(ctx->data);
 }
 
-static int ssl_x25519_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+static int ssl_x25519_offer(SSL_ECDH_CTX *ctx, CBB *out) {
   assert(ctx->data == NULL);
 
   ctx->data = OPENSSL_malloc(32);
@@ -174,10 +184,9 @@
   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) {
+static int ssl_x25519_finish(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;
 
@@ -199,6 +208,166 @@
   return 1;
 }
 
+static int ssl_x25519_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+                             uint8_t **out_secret, size_t *out_secret_len,
+                             uint8_t *out_alert, const uint8_t *peer_key,
+                             size_t peer_key_len) {
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+  if (!ssl_x25519_offer(ctx, out_public_key) ||
+      !ssl_x25519_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
+                         peer_key_len)) {
+    return 0;
+  }
+  return 1;
+}
+
+
+/* Combined X25119 + New Hope (post-quantum) implementation. */
+
+typedef struct {
+  uint8_t x25519_key[32];
+  NEWHOPE_POLY *newhope_sk;
+} cecpq1_data;
+
+#define CECPQ1_OFFERMSG_LENGTH (32 + NEWHOPE_OFFERMSG_LENGTH)
+#define CECPQ1_ACCEPTMSG_LENGTH (32 + NEWHOPE_ACCEPTMSG_LENGTH)
+#define CECPQ1_SECRET_LENGTH (32 + SHA256_DIGEST_LENGTH)
+
+static void ssl_cecpq1_cleanup(SSL_ECDH_CTX *ctx) {
+  if (ctx->data == NULL) {
+    return;
+  }
+  cecpq1_data *data = ctx->data;
+  NEWHOPE_POLY_free(data->newhope_sk);
+  OPENSSL_cleanse(data, sizeof(cecpq1_data));
+  OPENSSL_free(data);
+}
+
+static int ssl_cecpq1_offer(SSL_ECDH_CTX *ctx, CBB *out) {
+  assert(ctx->data == NULL);
+  cecpq1_data *data = OPENSSL_malloc(sizeof(cecpq1_data));
+  if (data == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  ctx->data = data;
+  data->newhope_sk = NEWHOPE_POLY_new();
+  if (data->newhope_sk == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  uint8_t x25519_public_key[32];
+  X25519_keypair(x25519_public_key, data->x25519_key);
+
+  uint8_t newhope_offermsg[NEWHOPE_OFFERMSG_LENGTH];
+  NEWHOPE_offer(newhope_offermsg, data->newhope_sk);
+
+  if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) ||
+      !CBB_add_bytes(out, newhope_offermsg, sizeof(newhope_offermsg))) {
+    return 0;
+  }
+  return 1;
+}
+
+static int ssl_cecpq1_accept(SSL_ECDH_CTX *ctx, CBB *cbb, uint8_t **out_secret,
+                             size_t *out_secret_len, uint8_t *out_alert,
+                             const uint8_t *peer_key, size_t peer_key_len) {
+  if (peer_key_len != CECPQ1_OFFERMSG_LENGTH) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
+  }
+
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+
+  assert(ctx->data == NULL);
+  cecpq1_data *data = OPENSSL_malloc(sizeof(cecpq1_data));
+  if (data == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  data->newhope_sk = NULL;
+  ctx->data = data;
+
+  uint8_t *secret = OPENSSL_malloc(CECPQ1_SECRET_LENGTH);
+  if (secret == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  /* Generate message to server, and secret key, at once. */
+
+  uint8_t x25519_public_key[32];
+  X25519_keypair(x25519_public_key, data->x25519_key);
+  if (!X25519(secret, data->x25519_key, peer_key)) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+    goto err;
+  }
+
+  uint8_t newhope_acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH];
+  if (!NEWHOPE_accept(secret + 32, newhope_acceptmsg, peer_key + 32,
+                      NEWHOPE_OFFERMSG_LENGTH)) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    goto err;
+  }
+
+  if (!CBB_add_bytes(cbb, x25519_public_key, sizeof(x25519_public_key)) ||
+      !CBB_add_bytes(cbb, newhope_acceptmsg, sizeof(newhope_acceptmsg))) {
+    goto err;
+  }
+
+  *out_secret = secret;
+  *out_secret_len = CECPQ1_SECRET_LENGTH;
+  return 1;
+
+ err:
+  OPENSSL_cleanse(secret, CECPQ1_SECRET_LENGTH);
+  OPENSSL_free(secret);
+  return 0;
+}
+
+static int ssl_cecpq1_finish(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) {
+  if (peer_key_len != CECPQ1_ACCEPTMSG_LENGTH) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return 0;
+  }
+
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+
+  assert(ctx->data != NULL);
+  cecpq1_data *data = ctx->data;
+
+  uint8_t *secret = OPENSSL_malloc(CECPQ1_SECRET_LENGTH);
+  if (secret == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  if (!X25519(secret, data->x25519_key, peer_key)) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+    goto err;
+  }
+
+  if (!NEWHOPE_finish(secret + 32, data->newhope_sk, peer_key + 32,
+                      NEWHOPE_ACCEPTMSG_LENGTH)) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    goto err;
+  }
+
+  *out_secret = secret;
+  *out_secret_len = CECPQ1_SECRET_LENGTH;
+  return 1;
+
+ err:
+  OPENSSL_cleanse(secret, CECPQ1_SECRET_LENGTH);
+  OPENSSL_free(secret);
+  return 0;
+}
+
 
 /* Legacy DHE-based implementation. */
 
@@ -206,7 +375,7 @@
   DH_free((DH *)ctx->data);
 }
 
-static int ssl_dhe_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+static int ssl_dhe_offer(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);
@@ -218,10 +387,9 @@
          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) {
+static int ssl_dhe_finish(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);
@@ -257,53 +425,91 @@
   return 0;
 }
 
+static int ssl_dhe_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+                          uint8_t **out_secret, size_t *out_secret_len,
+                          uint8_t *out_alert, const uint8_t *peer_key,
+                          size_t peer_key_len) {
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+  if (!ssl_dhe_offer(ctx, out_public_key) ||
+      !ssl_dhe_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
+                      peer_key_len)) {
+    return 0;
+  }
+  return 1;
+}
+
 static const SSL_ECDH_METHOD kDHEMethod = {
     NID_undef, 0, "",
     ssl_dhe_cleanup,
-    ssl_dhe_generate_keypair,
-    ssl_dhe_compute_secret,
+    ssl_dhe_offer,
+    ssl_dhe_accept,
+    ssl_dhe_finish,
+    CBS_get_u16_length_prefixed,
+    CBB_add_u16_length_prefixed,
 };
 
-
 static const SSL_ECDH_METHOD kMethods[] = {
     {
         NID_X9_62_prime256v1,
-        SSL_CURVE_SECP256R1,
+        SSL_GROUP_SECP256R1,
         "P-256",
         ssl_ec_point_cleanup,
-        ssl_ec_point_generate_keypair,
-        ssl_ec_point_compute_secret,
+        ssl_ec_point_offer,
+        ssl_ec_point_accept,
+        ssl_ec_point_finish,
+        CBS_get_u8_length_prefixed,
+        CBB_add_u8_length_prefixed,
     },
     {
         NID_secp384r1,
-        SSL_CURVE_SECP384R1,
+        SSL_GROUP_SECP384R1,
         "P-384",
         ssl_ec_point_cleanup,
-        ssl_ec_point_generate_keypair,
-        ssl_ec_point_compute_secret,
+        ssl_ec_point_offer,
+        ssl_ec_point_accept,
+        ssl_ec_point_finish,
+        CBS_get_u8_length_prefixed,
+        CBB_add_u8_length_prefixed,
     },
     {
         NID_secp521r1,
-        SSL_CURVE_SECP521R1,
+        SSL_GROUP_SECP521R1,
         "P-521",
         ssl_ec_point_cleanup,
-        ssl_ec_point_generate_keypair,
-        ssl_ec_point_compute_secret,
+        ssl_ec_point_offer,
+        ssl_ec_point_accept,
+        ssl_ec_point_finish,
+        CBS_get_u8_length_prefixed,
+        CBB_add_u8_length_prefixed,
     },
     {
         NID_X25519,
-        SSL_CURVE_X25519,
+        SSL_GROUP_X25519,
         "X25519",
         ssl_x25519_cleanup,
-        ssl_x25519_generate_keypair,
-        ssl_x25519_compute_secret,
+        ssl_x25519_offer,
+        ssl_x25519_accept,
+        ssl_x25519_finish,
+        CBS_get_u8_length_prefixed,
+        CBB_add_u8_length_prefixed,
+    },
+    {
+        NID_cecpq1,
+        SSL_GROUP_CECPQ1,
+        "CECPQ1",
+        ssl_cecpq1_cleanup,
+        ssl_cecpq1_offer,
+        ssl_cecpq1_accept,
+        ssl_cecpq1_finish,
+        CBS_get_u16_length_prefixed,
+        CBB_add_u16_length_prefixed,
     },
 };
 
-static const SSL_ECDH_METHOD *method_from_curve_id(uint16_t curve_id) {
+static const SSL_ECDH_METHOD *method_from_group_id(uint16_t group_id) {
   size_t i;
   for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); i++) {
-    if (kMethods[i].curve_id == curve_id) {
+    if (kMethods[i].group_id == group_id) {
       return &kMethods[i];
     }
   }
@@ -320,27 +526,27 @@
   return NULL;
 }
 
-const char* SSL_get_curve_name(uint16_t curve_id) {
-  const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
+const char* SSL_get_curve_name(uint16_t group_id) {
+  const SSL_ECDH_METHOD *method = method_from_group_id(group_id);
   if (method == NULL) {
     return NULL;
   }
   return method->name;
 }
 
-int ssl_nid_to_curve_id(uint16_t *out_curve_id, int nid) {
+int ssl_nid_to_group_id(uint16_t *out_group_id, int nid) {
   const SSL_ECDH_METHOD *method = method_from_nid(nid);
   if (method == NULL) {
     return 0;
   }
-  *out_curve_id = method->curve_id;
+  *out_group_id = method->group_id;
   return 1;
 }
 
-int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t curve_id) {
+int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t group_id) {
   SSL_ECDH_CTX_cleanup(ctx);
 
-  const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
+  const SSL_ECDH_METHOD *method = method_from_group_id(group_id);
   if (method == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
     return 0;
@@ -356,6 +562,20 @@
   ctx->data = params;
 }
 
+int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out) {
+  if (ctx->method == NULL) {
+    return 0;
+  }
+  return ctx->method->get_key(cbs, out);
+}
+
+int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents) {
+  if (ctx->method == NULL) {
+    return 0;
+  }
+  return ctx->method->add_key(cbb, out_contents);
+}
+
 void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) {
   if (ctx->method == NULL) {
     return;
@@ -365,13 +585,21 @@
   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_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key) {
+  return ctx->method->offer(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);
+int SSL_ECDH_CTX_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+                        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->accept(ctx, out_public_key, out_secret, out_secret_len,
+                             out_alert, peer_key, peer_key_len);
+}
+
+int SSL_ECDH_CTX_finish(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->finish(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 84047b2..8e9b196 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -297,6 +297,9 @@
   if (method->version != 0) {
     SSL_CTX_set_max_version(ret, method->version);
     SSL_CTX_set_min_version(ret, method->version);
+  } else if (!method->method->is_dtls) {
+    /* TODO(svaldez): Enable TLS 1.3 once implemented. */
+    SSL_CTX_set_max_version(ret, TLS1_2_VERSION);
   }
 
   return ret;
@@ -341,7 +344,7 @@
   sk_X509_NAME_pop_free(ctx->client_CA, X509_NAME_free);
   sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles);
   OPENSSL_free(ctx->psk_identity_hint);
-  OPENSSL_free(ctx->tlsext_ellipticcurvelist);
+  OPENSSL_free(ctx->supported_group_list);
   OPENSSL_free(ctx->alpn_client_proto_list);
   OPENSSL_free(ctx->ocsp_response);
   OPENSSL_free(ctx->signed_cert_timestamp_list);
@@ -369,6 +372,10 @@
   ssl->min_version = ctx->min_version;
   ssl->max_version = ctx->max_version;
 
+  /* RFC 6347 states that implementations SHOULD use an initial timer value of
+   * 1 second. */
+  ssl->initial_timeout_duration_ms = 1000;
+
   ssl->options = ctx->options;
   ssl->mode = ctx->mode;
   ssl->max_cert_list = ctx->max_cert_list;
@@ -399,14 +406,14 @@
   CRYPTO_refcount_inc(&ctx->references);
   ssl->initial_ctx = ctx;
 
-  if (ctx->tlsext_ellipticcurvelist) {
-    ssl->tlsext_ellipticcurvelist =
-        BUF_memdup(ctx->tlsext_ellipticcurvelist,
-                   ctx->tlsext_ellipticcurvelist_length * 2);
-    if (!ssl->tlsext_ellipticcurvelist) {
+  if (ctx->supported_group_list) {
+    ssl->supported_group_list =
+        BUF_memdup(ctx->supported_group_list,
+                   ctx->supported_group_list_len * 2);
+    if (!ssl->supported_group_list) {
       goto err;
     }
-    ssl->tlsext_ellipticcurvelist_length = ctx->tlsext_ellipticcurvelist_length;
+    ssl->supported_group_list_len = ctx->supported_group_list_len;
   }
 
   if (ssl->ctx->alpn_client_proto_list) {
@@ -467,14 +474,8 @@
 
   CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data);
 
-  if (ssl->bbio != NULL) {
-    /* If the buffering BIO is in place, pop it off */
-    if (ssl->bbio == ssl->wbio) {
-      ssl->wbio = BIO_pop(ssl->wbio);
-    }
-    BIO_free(ssl->bbio);
-    ssl->bbio = NULL;
-  }
+  ssl_free_wbio_buffer(ssl);
+  assert(ssl->bbio == NULL);
 
   int free_wbio = ssl->wbio != ssl->rbio;
   BIO_free_all(ssl->rbio);
@@ -495,7 +496,7 @@
 
   OPENSSL_free(ssl->tlsext_hostname);
   SSL_CTX_free(ssl->initial_ctx);
-  OPENSSL_free(ssl->tlsext_ellipticcurvelist);
+  OPENSSL_free(ssl->supported_group_list);
   OPENSSL_free(ssl->alpn_client_proto_list);
   EVP_PKEY_free(ssl->tlsext_channel_id_private);
   OPENSSL_free(ssl->psk_identity_hint);
@@ -512,14 +513,12 @@
 
 void SSL_set_connect_state(SSL *ssl) {
   ssl->server = 0;
-  ssl->shutdown = 0;
   ssl->state = SSL_ST_CONNECT;
   ssl->handshake_func = ssl->method->ssl_connect;
 }
 
 void SSL_set_accept_state(SSL *ssl) {
   ssl->server = 1;
-  ssl->shutdown = 0;
   ssl->state = SSL_ST_ACCEPT;
   ssl->handshake_func = ssl->method->ssl_accept;
 }
@@ -527,10 +526,7 @@
 void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) {
   /* If the output buffering BIO is still in place, remove it. */
   if (ssl->bbio != NULL) {
-    if (ssl->wbio == ssl->bbio) {
-      ssl->wbio = ssl->wbio->next_bio;
-      ssl->bbio->next_bio = NULL;
-    }
+    ssl->wbio = BIO_pop(ssl->wbio);
   }
 
   if (ssl->rbio != rbio) {
@@ -541,11 +537,23 @@
   }
   ssl->rbio = rbio;
   ssl->wbio = wbio;
+
+  /* Re-attach |bbio| to the new |wbio|. */
+  if (ssl->bbio != NULL) {
+    ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
+  }
 }
 
 BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio; }
 
-BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; }
+BIO *SSL_get_wbio(const SSL *ssl) {
+  if (ssl->bbio != NULL) {
+    /* If |bbio| is active, the true caller-configured BIO is its |next_bio|. */
+    assert(ssl->bbio == ssl->wbio);
+    return ssl->bbio->next_bio;
+  }
+  return ssl->wbio;
+}
 
 int SSL_do_handshake(SSL *ssl) {
   ssl->rwstate = SSL_NOTHING;
@@ -597,10 +605,6 @@
     return -1;
   }
 
-  if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
-    return 0;
-  }
-
   /* This may require multiple iterations. False Start will cause
    * |ssl->handshake_func| to signal success one step early, but the handshake
    * must be completely finished before other modes are accepted. */
@@ -637,7 +641,7 @@
     return -1;
   }
 
-  if (ssl->shutdown & SSL_SENT_SHUTDOWN) {
+  if (ssl->s3->send_shutdown != ssl_shutdown_none) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
     return -1;
   }
@@ -662,11 +666,6 @@
   /* Functions which use SSL_get_error must clear the error queue on entry. */
   ERR_clear_error();
 
-  /* Note that this function behaves differently from what one might expect.
-   * Return values are 0 for no success (yet), 1 for success; but calling it
-   * once is usually not enough, even if blocking I/O is used (see
-   * ssl3_shutdown). */
-
   if (ssl->handshake_func == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
     return -1;
@@ -678,44 +677,37 @@
     return -1;
   }
 
-  /* Do nothing if configured not to send a close_notify. */
   if (ssl->quiet_shutdown) {
-    ssl->shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN;
+    /* Do nothing if configured not to send a close_notify. */
+    ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
     return 1;
   }
 
-  if (!(ssl->shutdown & SSL_SENT_SHUTDOWN)) {
-    ssl->shutdown |= SSL_SENT_SHUTDOWN;
-    ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY);
+  /* This function completes in two stages. It sends a close_notify and then it
+   * waits for a close_notify to come in. Perform exactly one action and return
+   * whether or not it succeeds. */
 
-    /* our shutdown alert has been sent now, and if it still needs to be
-     * written, ssl->s3->alert_dispatch will be true */
-    if (ssl->s3->alert_dispatch) {
-      return -1; /* return WANT_WRITE */
+  if (ssl->s3->send_shutdown != ssl_shutdown_close_notify) {
+    /* Send a close_notify. */
+    if (ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY) <= 0) {
+      return -1;
     }
   } else if (ssl->s3->alert_dispatch) {
-    /* resend it if not sent */
-    int ret = ssl->method->ssl_dispatch_alert(ssl);
-    if (ret == -1) {
-      /* we only get to return -1 here the 2nd/Nth invocation, we must  have
-       * already signalled return 0 upon a previous invoation, return
-       * WANT_WRITE */
-      return ret;
+    /* Finish sending the close_notify. */
+    if (ssl->method->ssl_dispatch_alert(ssl) <= 0) {
+      return -1;
     }
-  } else if (!(ssl->shutdown & SSL_RECEIVED_SHUTDOWN)) {
-    /* If we are waiting for a close from our peer, we are closed */
+  } else if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) {
+    /* Wait for the peer's close_notify. */
     ssl->method->ssl_read_close_notify(ssl);
-    if (!(ssl->shutdown & SSL_RECEIVED_SHUTDOWN)) {
-      return -1; /* return WANT_READ */
+    if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) {
+      return -1;
     }
   }
 
-  if (ssl->shutdown == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN) &&
-      !ssl->s3->alert_dispatch) {
-    return 1;
-  } else {
-    return 0;
-  }
+  /* Return 0 for unidirectional shutdown and 1 for bidirectional shutdown. */
+  return ssl->s3->recv_shutdown == ssl_shutdown_close_notify;
 }
 
 int SSL_get_error(const SSL *ssl, int ret_code) {
@@ -738,8 +730,7 @@
   }
 
   if (ret_code == 0) {
-    if ((ssl->shutdown & SSL_RECEIVED_SHUTDOWN) && ssl->s3->clean_shutdown) {
-      /* The socket was cleanly shut down with a close_notify. */
+    if (ssl->s3->recv_shutdown == ssl_shutdown_close_notify) {
       return SSL_ERROR_ZERO_RETURN;
     }
     /* An EOF was observed which violates the protocol, and the underlying
@@ -1035,35 +1026,36 @@
 }
 
 int SSL_set_wfd(SSL *ssl, int fd) {
-  if (ssl->rbio == NULL ||
-      BIO_method_type(ssl->rbio) != BIO_TYPE_SOCKET ||
-      BIO_get_fd(ssl->rbio, NULL) != fd) {
+  BIO *rbio = SSL_get_rbio(ssl);
+  if (rbio == NULL || BIO_method_type(rbio) != BIO_TYPE_SOCKET ||
+      BIO_get_fd(rbio, NULL) != fd) {
     BIO *bio = BIO_new(BIO_s_socket());
     if (bio == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
       return 0;
     }
     BIO_set_fd(bio, fd, BIO_NOCLOSE);
-    SSL_set_bio(ssl, SSL_get_rbio(ssl), bio);
+    SSL_set_bio(ssl, rbio, bio);
   } else {
-    SSL_set_bio(ssl, SSL_get_rbio(ssl), SSL_get_rbio(ssl));
+    SSL_set_bio(ssl, rbio, rbio);
   }
 
   return 1;
 }
 
 int SSL_set_rfd(SSL *ssl, int fd) {
-  if (ssl->wbio == NULL || BIO_method_type(ssl->wbio) != BIO_TYPE_SOCKET ||
-      BIO_get_fd(ssl->wbio, NULL) != fd) {
+  BIO *wbio = SSL_get_wbio(ssl);
+  if (wbio == NULL || BIO_method_type(wbio) != BIO_TYPE_SOCKET ||
+      BIO_get_fd(wbio, NULL) != fd) {
     BIO *bio = BIO_new(BIO_s_socket());
     if (bio == NULL) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
       return 0;
     }
     BIO_set_fd(bio, fd, BIO_NOCLOSE);
-    SSL_set_bio(ssl, bio, SSL_get_wbio(ssl));
+    SSL_set_bio(ssl, bio, wbio);
   } else {
-    SSL_set_bio(ssl, SSL_get_wbio(ssl), SSL_get_wbio(ssl));
+    SSL_set_bio(ssl, wbio, wbio);
   }
   return 1;
 }
@@ -1710,7 +1702,7 @@
       mask_a |= SSL_aRSA;
     } else if (ssl_private_key_type(ssl) == EVP_PKEY_EC) {
       /* An ECC certificate may be usable for ECDSA cipher suites depending on
-       * the key usage extension and on the client's curve preferences. */
+       * the key usage extension and on the client's group preferences. */
       X509 *x = ssl->cert->x509;
       /* This call populates extension flags (ex_flags). */
       X509_check_purpose(x, -1, 0);
@@ -1727,12 +1719,15 @@
     mask_k |= SSL_kDHE;
   }
 
-  /* Check for a shared curve to consider ECDHE ciphers. */
+  /* Check for a shared group to consider ECDHE ciphers. */
   uint16_t unused;
-  if (tls1_get_shared_curve(ssl, &unused)) {
+  if (tls1_get_shared_group(ssl, &unused)) {
     mask_k |= SSL_kECDHE;
   }
 
+  /* CECPQ1 ciphers are always acceptable if supported by both sides. */
+  mask_k |= SSL_kCECPQ1;
+
   /* PSK requires a server callback. */
   if (ssl->psk_server_callback != NULL) {
     mask_k |= SSL_kPSK;
@@ -1779,7 +1774,7 @@
       flush_cache = 1;
       ctx->handshakes_since_cache_flush = 0;
     }
-    CRYPTO_MUTEX_unlock(&ctx->lock);
+    CRYPTO_MUTEX_unlock_write(&ctx->lock);
 
     if (flush_cache) {
       SSL_CTX_flush_sessions(ctx, (unsigned long)time(NULL));
@@ -1789,6 +1784,9 @@
 
 static const char *ssl_get_version(int version) {
   switch (version) {
+    case TLS1_3_VERSION:
+      return "TLSv1.3";
+
     case TLS1_2_VERSION:
       return "TLSv1.2";
 
@@ -1865,38 +1863,26 @@
 
 int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
 
-int ssl_init_wbio_buffer(SSL *ssl, int push) {
-  BIO *bbio;
+int ssl_is_wbio_buffered(const SSL *ssl) {
+  return ssl->bbio != NULL;
+}
 
-  if (ssl->bbio == NULL) {
-    bbio = BIO_new(BIO_f_buffer());
-    if (bbio == NULL) {
-      return 0;
-    }
-    ssl->bbio = bbio;
-  } else {
-    bbio = ssl->bbio;
-    if (ssl->bbio == ssl->wbio) {
-      ssl->wbio = BIO_pop(ssl->wbio);
-    }
+int ssl_init_wbio_buffer(SSL *ssl) {
+  if (ssl->bbio != NULL) {
+    /* Already buffered. */
+    assert(ssl->bbio == ssl->wbio);
+    return 1;
   }
 
-  BIO_reset(bbio);
-  if (!BIO_set_read_buffer_size(bbio, 1)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+  BIO *bbio = BIO_new(BIO_f_buffer());
+  if (bbio == NULL ||
+      !BIO_set_read_buffer_size(bbio, 1)) {
+    BIO_free(bbio);
     return 0;
   }
 
-  if (push) {
-    if (ssl->wbio != bbio) {
-      ssl->wbio = BIO_push(bbio, ssl->wbio);
-    }
-  } else {
-    if (ssl->wbio == bbio) {
-      ssl->wbio = BIO_pop(bbio);
-    }
-  }
-
+  ssl->bbio = bbio;
+  ssl->wbio = BIO_push(bbio, ssl->wbio);
   return 1;
 }
 
@@ -1905,11 +1891,9 @@
     return;
   }
 
-  if (ssl->bbio == ssl->wbio) {
-    /* remove buffering */
-    ssl->wbio = BIO_pop(ssl->wbio);
-  }
+  assert(ssl->bbio == ssl->wbio);
 
+  ssl->wbio = BIO_pop(ssl->wbio);
   BIO_free(ssl->bbio);
   ssl->bbio = NULL;
 }
@@ -1931,12 +1915,32 @@
 void SSL_set_shutdown(SSL *ssl, int mode) {
   /* It is an error to clear any bits that have already been set. (We can't try
    * to get a second close_notify or send two.) */
-  assert((ssl->shutdown & mode) == ssl->shutdown);
+  assert((SSL_get_shutdown(ssl) & mode) == SSL_get_shutdown(ssl));
 
-  ssl->shutdown |= mode;
+  if (mode & SSL_RECEIVED_SHUTDOWN &&
+      ssl->s3->recv_shutdown == ssl_shutdown_none) {
+    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+  }
+
+  if (mode & SSL_SENT_SHUTDOWN &&
+      ssl->s3->send_shutdown == ssl_shutdown_none) {
+    ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+  }
 }
 
-int SSL_get_shutdown(const SSL *ssl) { return ssl->shutdown; }
+int SSL_get_shutdown(const SSL *ssl) {
+  int ret = 0;
+  if (ssl->s3->recv_shutdown != ssl_shutdown_none) {
+    /* Historically, OpenSSL set |SSL_RECEIVED_SHUTDOWN| on both close_notify
+     * and fatal alert. */
+    ret |= SSL_RECEIVED_SHUTDOWN;
+  }
+  if (ssl->s3->send_shutdown == ssl_shutdown_close_notify) {
+    /* Historically, OpenSSL set |SSL_SENT_SHUTDOWN| on only close_notify. */
+    ret |= SSL_SENT_SHUTDOWN;
+  }
+  return ret;
+}
 
 int SSL_version(const SSL *ssl) { return ssl->version; }
 
@@ -2293,7 +2297,7 @@
 
   /* False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN. */
   return !SSL_IS_DTLS(ssl) &&
-      SSL_version(ssl) >= TLS1_2_VERSION &&
+      SSL_version(ssl) == TLS1_2_VERSION &&
       (ssl->s3->alpn_selected || ssl->s3->next_proto_neg_seen) &&
       cipher != NULL &&
       cipher->algorithm_mkey == SSL_kECDHE &&
@@ -2308,6 +2312,7 @@
     case TLS1_VERSION:
     case TLS1_1_VERSION:
     case TLS1_2_VERSION:
+    case TLS1_3_VERSION:
     case DTLS1_VERSION:
     case DTLS1_2_VERSION:
       return &TLSv1_enc_data;
@@ -2332,7 +2337,10 @@
     return 0;
   }
 
-  max_version = (ssl->max_version != 0) ? ssl->max_version : TLS1_2_VERSION;
+  max_version = (ssl->max_version != 0) ? ssl->max_version : TLS1_3_VERSION;
+  if (!(ssl->options & SSL_OP_NO_TLSv1_3) && TLS1_3_VERSION <= max_version) {
+    return TLS1_3_VERSION;
+  }
   if (!(ssl->options & SSL_OP_NO_TLSv1_2) && TLS1_2_VERSION <= max_version) {
     return TLS1_2_VERSION;
   }
@@ -2376,8 +2384,11 @@
       client_version = ssl->max_version;
     }
 
-    if (client_version >= TLS1_2_VERSION &&
-        !(ssl->options & SSL_OP_NO_TLSv1_2)) {
+    if (client_version >= TLS1_3_VERSION &&
+        !(ssl->options & SSL_OP_NO_TLSv1_3)) {
+      version = TLS1_3_VERSION;
+    } else if (client_version >= TLS1_2_VERSION &&
+               !(ssl->options & SSL_OP_NO_TLSv1_2)) {
       version = TLS1_2_VERSION;
     } else if (client_version >= TLS1_1_VERSION &&
                !(ssl->options & SSL_OP_NO_TLSv1_1)) {
@@ -2426,7 +2437,10 @@
       version = ssl->max_version;
     }
   } else {
-    if (!(options & SSL_OP_NO_TLSv1_2)) {
+    if (!(options & SSL_OP_NO_TLSv1_3)) {
+      version = TLS1_3_VERSION;
+    }
+    if (!(options & SSL_OP_NO_TLSv1_2) && (options & SSL_OP_NO_TLSv1_3)) {
       version = TLS1_2_VERSION;
     }
     if (!(options & SSL_OP_NO_TLSv1_1) && (options & SSL_OP_NO_TLSv1_2)) {
@@ -2486,6 +2500,9 @@
       case TLS1_2_VERSION:
         return !(ssl->options & SSL_OP_NO_TLSv1_2);
 
+      case TLS1_3_VERSION:
+        return !(ssl->options & SSL_OP_NO_TLSv1_3);
+
       default:
         return 0;
     }
@@ -2644,7 +2661,6 @@
   }
 
   ssl->hit = 0;
-  ssl->shutdown = 0;
 
   /* SSL_clear may be called before or after the |ssl| is initialized in either
    * accept or connect state. In the latter case, SSL_clear should preserve the
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index 12d065e..8e51a6a 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -396,7 +396,7 @@
       SSL_SESSION_up_ref(session);
     }
     /* TODO(davidben): This should probably move it to the front of the list. */
-    CRYPTO_MUTEX_unlock(&ssl->initial_ctx->lock);
+    CRYPTO_MUTEX_unlock_read(&ssl->initial_ctx->lock);
 
     if (session != NULL) {
       *out_session = session;
@@ -517,7 +517,7 @@
   SSL_SESSION *old_session;
   CRYPTO_MUTEX_lock_write(&ctx->lock);
   if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, session)) {
-    CRYPTO_MUTEX_unlock(&ctx->lock);
+    CRYPTO_MUTEX_unlock_write(&ctx->lock);
     SSL_SESSION_free(session);
     return 0;
   }
@@ -525,7 +525,7 @@
   if (old_session != NULL) {
     if (old_session == session) {
       /* |session| was already in the cache. */
-      CRYPTO_MUTEX_unlock(&ctx->lock);
+      CRYPTO_MUTEX_unlock_write(&ctx->lock);
       SSL_SESSION_free(old_session);
       return 0;
     }
@@ -547,7 +547,7 @@
     }
   }
 
-  CRYPTO_MUTEX_unlock(&ctx->lock);
+  CRYPTO_MUTEX_unlock_write(&ctx->lock);
   return 1;
 }
 
@@ -571,7 +571,7 @@
     }
 
     if (lock) {
-      CRYPTO_MUTEX_unlock(&ctx->lock);
+      CRYPTO_MUTEX_unlock_write(&ctx->lock);
     }
 
     if (ret) {
@@ -654,11 +654,12 @@
   tp.time = time;
   CRYPTO_MUTEX_lock_write(&ctx->lock);
   lh_SSL_SESSION_doall_arg(tp.cache, timeout_doall_arg, &tp);
-  CRYPTO_MUTEX_unlock(&ctx->lock);
+  CRYPTO_MUTEX_unlock_write(&ctx->lock);
 }
 
 int ssl_clear_bad_session(SSL *ssl) {
-  if (ssl->session != NULL && !(ssl->shutdown & SSL_SENT_SHUTDOWN) &&
+  if (ssl->session != NULL &&
+      ssl->s3->send_shutdown != ssl_shutdown_close_notify &&
       !SSL_in_init(ssl)) {
     SSL_CTX_remove_session(ssl->ctx, ssl->session);
     return 1;
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c
index 8fa197d..15d1270 100644
--- a/src/ssl/ssl_stat.c
+++ b/src/ssl/ssl_stat.c
@@ -110,39 +110,21 @@
     case SSL3_ST_CR_SRVR_HELLO_A:
       return "SSLv3 read server hello A";
 
-    case SSL3_ST_CR_SRVR_HELLO_B:
-      return "SSLv3 read server hello B";
-
     case SSL3_ST_CR_CERT_A:
       return "SSLv3 read server certificate A";
 
-    case SSL3_ST_CR_CERT_B:
-      return "SSLv3 read server certificate B";
-
     case SSL3_ST_CR_KEY_EXCH_A:
       return "SSLv3 read server key exchange A";
 
-    case SSL3_ST_CR_KEY_EXCH_B:
-      return "SSLv3 read server key exchange B";
-
     case SSL3_ST_CR_CERT_REQ_A:
       return "SSLv3 read server certificate request A";
 
-    case SSL3_ST_CR_CERT_REQ_B:
-      return "SSLv3 read server certificate request B";
-
     case SSL3_ST_CR_SESSION_TICKET_A:
       return "SSLv3 read server session ticket A";
 
-    case SSL3_ST_CR_SESSION_TICKET_B:
-      return "SSLv3 read server session ticket B";
-
     case SSL3_ST_CR_SRVR_DONE_A:
       return "SSLv3 read server done A";
 
-    case SSL3_ST_CR_SRVR_DONE_B:
-      return "SSLv3 read server done B";
-
     case SSL3_ST_CW_CERT_A:
       return "SSLv3 write client certificate A";
 
@@ -191,10 +173,6 @@
     case SSL3_ST_SR_FINISHED_A:
       return "SSLv3 read finished A";
 
-    case SSL3_ST_CR_FINISHED_B:
-    case SSL3_ST_SR_FINISHED_B:
-      return "SSLv3 read finished B";
-
     case SSL3_ST_CW_FLUSH:
     case SSL3_ST_SW_FLUSH:
       return "SSLv3 flush data";
@@ -208,9 +186,6 @@
     case SSL3_ST_SR_CLNT_HELLO_C:
       return "SSLv3 read client hello C";
 
-    case SSL3_ST_SR_CLNT_HELLO_D:
-      return "SSLv3 read client hello D";
-
     case SSL3_ST_SW_HELLO_REQ_A:
       return "SSLv3 write hello request A";
 
@@ -259,9 +234,6 @@
     case SSL3_ST_SR_CERT_A:
       return "SSLv3 read client certificate A";
 
-    case SSL3_ST_SR_CERT_B:
-      return "SSLv3 read client certificate B";
-
     case SSL3_ST_SR_KEY_EXCH_A:
       return "SSLv3 read client key exchange A";
 
@@ -271,16 +243,10 @@
     case SSL3_ST_SR_CERT_VRFY_A:
       return "SSLv3 read certificate verify A";
 
-    case SSL3_ST_SR_CERT_VRFY_B:
-      return "SSLv3 read certificate verify B";
-
     /* DTLS */
     case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
       return "DTLS1 read hello verify request A";
 
-    case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B:
-      return "DTLS1 read hello verify request B";
-
     default:
       return "unknown state";
   }
@@ -311,33 +277,18 @@
     case SSL3_ST_CR_SRVR_HELLO_A:
       return "3RSH_A";
 
-    case SSL3_ST_CR_SRVR_HELLO_B:
-      return "3RSH_B";
-
     case SSL3_ST_CR_CERT_A:
       return "3RSC_A";
 
-    case SSL3_ST_CR_CERT_B:
-      return "3RSC_B";
-
     case SSL3_ST_CR_KEY_EXCH_A:
       return "3RSKEA";
 
-    case SSL3_ST_CR_KEY_EXCH_B:
-      return "3RSKEB";
-
     case SSL3_ST_CR_CERT_REQ_A:
       return "3RCR_A";
 
-    case SSL3_ST_CR_CERT_REQ_B:
-      return "3RCR_B";
-
     case SSL3_ST_CR_SRVR_DONE_A:
       return "3RSD_A";
 
-    case SSL3_ST_CR_SRVR_DONE_B:
-      return "3RSD_B";
-
     case SSL3_ST_CW_CERT_A:
       return "3WCC_A";
 
@@ -386,10 +337,6 @@
     case SSL3_ST_CR_FINISHED_A:
       return "3RFINA";
 
-    case SSL3_ST_SR_FINISHED_B:
-    case SSL3_ST_CR_FINISHED_B:
-      return "3RFINB";
-
     case SSL3_ST_SW_HELLO_REQ_A:
       return "3WHR_A";
 
@@ -408,9 +355,6 @@
     case SSL3_ST_SR_CLNT_HELLO_C:
       return "3RCH_C";
 
-    case SSL3_ST_SR_CLNT_HELLO_D:
-      return "3RCH_D";
-
     case SSL3_ST_SW_SRVR_HELLO_A:
       return "3WSH_A";
 
@@ -444,28 +388,16 @@
     case SSL3_ST_SR_CERT_A:
       return "3RCC_A";
 
-    case SSL3_ST_SR_CERT_B:
-      return "3RCC_B";
-
     case SSL3_ST_SR_KEY_EXCH_A:
       return "3RCKEA";
 
-    case SSL3_ST_SR_KEY_EXCH_B:
-      return "3RCKEB";
-
     case SSL3_ST_SR_CERT_VRFY_A:
       return "3RCV_A";
 
-    case SSL3_ST_SR_CERT_VRFY_B:
-      return "3RCV_B";
-
     /* DTLS */
     case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
       return "DRCHVA";
 
-    case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B:
-      return "DRCHVB";
-
     default:
       return "UNKWN ";
   }
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 590a2c1..ef38902 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -254,6 +254,31 @@
   "TLSv1.2",
 };
 
+static const char *kMustNotIncludeCECPQ1[] = {
+  "ALL",
+  "DEFAULT",
+  "MEDIUM",
+  "HIGH",
+  "FIPS",
+  "SHA",
+  "SHA1",
+  "SHA256",
+  "SHA384",
+  "RSA",
+  "SSLv3",
+  "TLSv1",
+  "TLSv1.2",
+  "aRSA",
+  "RSA",
+  "aECDSA",
+  "ECDSA",
+  "AES",
+  "AES128",
+  "AES256",
+  "AESGCM",
+  "CHACHA20",
+};
+
 static void PrintCipherPreferenceList(ssl_cipher_preference_list_st *list) {
   bool in_group = false;
   for (size_t i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) {
@@ -324,6 +349,24 @@
   return true;
 }
 
+static bool TestRuleDoesNotIncludeCECPQ1(const char *rule) {
+  ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method()));
+  if (!ctx) {
+    return false;
+  }
+  if (!SSL_CTX_set_cipher_list(ctx.get(), rule)) {
+    fprintf(stderr, "Error: cipher rule '%s' failed\n", rule);
+    return false;
+  }
+  for (size_t i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) {
+    if (SSL_CIPHER_is_CECPQ1(sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i))) {
+      fprintf(stderr, "Error: cipher rule '%s' includes CECPQ1\n",rule);
+      return false;
+    }
+  }
+  return true;
+}
+
 static bool TestCipherRules() {
   for (const CipherTest &test : kCipherTests) {
     if (!TestCipherRule(test)) {
@@ -349,6 +392,12 @@
     }
   }
 
+  for (const char *rule : kMustNotIncludeCECPQ1) {
+    if (!TestRuleDoesNotIncludeCECPQ1(rule)) {
+      return false;
+    }
+  }
+
   return true;
 }
 
@@ -646,7 +695,10 @@
   if (!ctx) {
     return false;
   }
-  return ctx->min_version == version && ctx->max_version == version;
+  // TODO(svaldez): Remove TLS1_2_VERSION fallback upon implementing TLS 1.3.
+  return ctx->min_version == version &&
+         (ctx->max_version == version ||
+          (version == 0 && ctx->max_version == TLS1_2_VERSION));
 }
 
 static bool CipherGetRFCName(std::string *out, uint16_t value) {
@@ -1027,23 +1079,9 @@
       PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
 }
 
-static bool TestSequenceNumber(bool dtls) {
-  ScopedSSL_CTX client_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
-  ScopedSSL_CTX server_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
-  if (!client_ctx || !server_ctx) {
-    return false;
-  }
-
-  ScopedX509 cert = GetTestCertificate();
-  ScopedEVP_PKEY key = GetTestKey();
-  if (!cert || !key ||
-      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
-      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
-    return false;
-  }
-
-  // Create a client and server connected to each other.
-  ScopedSSL client(SSL_new(client_ctx.get())), server(SSL_new(server_ctx.get()));
+static bool ConnectClientAndServer(ScopedSSL *out_client, ScopedSSL *out_server,
+                                   SSL_CTX *client_ctx, SSL_CTX *server_ctx) {
+  ScopedSSL client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
   if (!client || !server) {
     return false;
   }
@@ -1083,6 +1121,32 @@
     }
   }
 
+  *out_client = std::move(client);
+  *out_server = std::move(server);
+  return true;
+}
+
+static bool TestSequenceNumber(bool dtls) {
+  ScopedSSL_CTX client_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+  ScopedSSL_CTX server_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+  if (!client_ctx || !server_ctx) {
+    return false;
+  }
+
+  ScopedX509 cert = GetTestCertificate();
+  ScopedEVP_PKEY key = GetTestKey();
+  if (!cert || !key ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+    return false;
+  }
+
+  ScopedSSL client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get())) {
+    return false;
+  }
+
   uint64_t client_read_seq = SSL_get_read_sequence(client.get());
   uint64_t client_write_seq = SSL_get_write_sequence(client.get());
   uint64_t server_read_seq = SSL_get_read_sequence(server.get());
@@ -1131,6 +1195,62 @@
   return true;
 }
 
+static bool TestOneSidedShutdown() {
+  ScopedSSL_CTX client_ctx(SSL_CTX_new(TLS_method()));
+  ScopedSSL_CTX server_ctx(SSL_CTX_new(TLS_method()));
+  if (!client_ctx || !server_ctx) {
+    return false;
+  }
+
+  ScopedX509 cert = GetTestCertificate();
+  ScopedEVP_PKEY key = GetTestKey();
+  if (!cert || !key ||
+      !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+    return false;
+  }
+
+  ScopedSSL client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+                              server_ctx.get())) {
+    return false;
+  }
+
+  // Shut down half the connection. SSL_shutdown will return 0 to signal only
+  // one side has shut down.
+  if (SSL_shutdown(client.get()) != 0) {
+    fprintf(stderr, "Could not shutdown.\n");
+    return false;
+  }
+
+  // Reading from the server should consume the EOF.
+  uint8_t byte;
+  if (SSL_read(server.get(), &byte, 1) != 0 ||
+      SSL_get_error(server.get(), 0) != SSL_ERROR_ZERO_RETURN) {
+    fprintf(stderr, "Connection was not shut down cleanly.\n");
+    return false;
+  }
+
+  // However, the server may continue to write data and then shut down the
+  // connection.
+  byte = 42;
+  if (SSL_write(server.get(), &byte, 1) != 1 ||
+      SSL_read(client.get(), &byte, 1) != 1 ||
+      byte != 42) {
+    fprintf(stderr, "Could not send byte.\n");
+    return false;
+  }
+
+  // The server may then shutdown the connection.
+  if (SSL_shutdown(server.get()) != 1 ||
+      SSL_shutdown(client.get()) != 1) {
+    fprintf(stderr, "Could not complete shutdown.\n");
+    return false;
+  }
+
+  return true;
+}
+
 int main() {
   CRYPTO_library_init();
 
@@ -1154,7 +1274,8 @@
       !TestClientCAList() ||
       !TestInternalSessionCache() ||
       !TestSequenceNumber(false /* TLS */) ||
-      !TestSequenceNumber(true /* DTLS */)) {
+      !TestSequenceNumber(true /* DTLS */) ||
+      !TestOneSidedShutdown()) {
     ERR_print_errors_fp(stderr);
     return 1;
   }
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index eac9579..16cac15 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -291,77 +291,77 @@
   return 0;
 }
 
-static const uint16_t eccurves_default[] = {
-    SSL_CURVE_X25519,
-    SSL_CURVE_SECP256R1,
-    SSL_CURVE_SECP384R1,
+static const uint16_t kDefaultGroups[] = {
+    SSL_GROUP_X25519,
+    SSL_GROUP_SECP256R1,
+    SSL_GROUP_SECP384R1,
 #if defined(BORINGSSL_ANDROID_SYSTEM)
-    SSL_CURVE_SECP521R1,
+    SSL_GROUP_SECP521R1,
 #endif
 };
 
-/* 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 *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. */
+/* tls1_get_grouplist sets |*out_group_ids| and |*out_group_ids_len| to the
+ * list of allowed group IDs. If |get_peer_groups| is non-zero, return the
+ * peer's group list. Otherwise, return the preferred list. */
+static void tls1_get_grouplist(SSL *ssl, int get_peer_groups,
+                               const uint16_t **out_group_ids,
+                               size_t *out_group_ids_len) {
+  if (get_peer_groups) {
+    /* Only clients send a supported group list, so this function is only
+     * called on the server. */
     assert(ssl->server);
-    *out_curve_ids = ssl->s3->tmp.peer_ellipticcurvelist;
-    *out_curve_ids_len = ssl->s3->tmp.peer_ellipticcurvelist_length;
+    *out_group_ids = ssl->s3->tmp.peer_supported_group_list;
+    *out_group_ids_len = ssl->s3->tmp.peer_supported_group_list_len;
     return;
   }
 
-  *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]);
+  *out_group_ids = ssl->supported_group_list;
+  *out_group_ids_len = ssl->supported_group_list_len;
+  if (!*out_group_ids) {
+    *out_group_ids = kDefaultGroups;
+    *out_group_ids_len = sizeof(kDefaultGroups) / sizeof(kDefaultGroups[0]);
   }
 }
 
-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;
+int tls1_get_shared_group(SSL *ssl, uint16_t *out_group_id) {
+  const uint16_t *groups, *peer_groups, *pref, *supp;
+  size_t groups_len, peer_groups_len, pref_len, supp_len, i, j;
 
   /* Can't do anything on client side */
   if (ssl->server == 0) {
     return 0;
   }
 
-  tls1_get_curvelist(ssl, 0 /* local curves */, &curves, &curves_len);
-  tls1_get_curvelist(ssl, 1 /* peer curves */, &peer_curves, &peer_curves_len);
+  tls1_get_grouplist(ssl, 0 /* local groups */, &groups, &groups_len);
+  tls1_get_grouplist(ssl, 1 /* peer groups */, &peer_groups, &peer_groups_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,
+  if (peer_groups_len == 0) {
+    /* Clients are not required to send a supported_groups extension. In this
+     * case, the server is free to pick any group it likes. See RFC 4492,
      * 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. */
+     * support our favoured group. */
     return 0;
   }
 
   if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
-    pref = curves;
-    pref_len = curves_len;
-    supp = peer_curves;
-    supp_len = peer_curves_len;
+    pref = groups;
+    pref_len = groups_len;
+    supp = peer_groups;
+    supp_len = peer_groups_len;
   } else {
-    pref = peer_curves;
-    pref_len = peer_curves_len;
-    supp = curves;
-    supp_len = curves_len;
+    pref = peer_groups;
+    pref_len = peer_groups_len;
+    supp = groups;
+    supp_len = groups_len;
   }
 
   for (i = 0; i < pref_len; i++) {
     for (j = 0; j < supp_len; j++) {
       if (pref[i] == supp[j]) {
-        *out_curve_id = pref[i];
+        *out_group_id = pref[i];
         return 1;
       }
     }
@@ -370,34 +370,34 @@
   return 0;
 }
 
-int tls1_set_curves(uint16_t **out_curve_ids, size_t *out_curve_ids_len,
+int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len,
                     const int *curves, size_t ncurves) {
-  uint16_t *curve_ids;
+  uint16_t *group_ids;
   size_t i;
 
-  curve_ids = OPENSSL_malloc(ncurves * sizeof(uint16_t));
-  if (curve_ids == NULL) {
+  group_ids = OPENSSL_malloc(ncurves * sizeof(uint16_t));
+  if (group_ids == NULL) {
     return 0;
   }
 
   for (i = 0; i < ncurves; i++) {
-    if (!ssl_nid_to_curve_id(&curve_ids[i], curves[i])) {
-      OPENSSL_free(curve_ids);
+    if (!ssl_nid_to_group_id(&group_ids[i], curves[i])) {
+      OPENSSL_free(group_ids);
       return 0;
     }
   }
 
-  OPENSSL_free(*out_curve_ids);
-  *out_curve_ids = curve_ids;
-  *out_curve_ids_len = ncurves;
+  OPENSSL_free(*out_group_ids);
+  *out_group_ids = group_ids;
+  *out_group_ids_len = ncurves;
 
   return 1;
 }
 
-/* tls1_curve_params_from_ec_key sets |*out_curve_id| and |*out_comp_id| to the
- * TLS curve ID and point format, respectively, for |ec|. It returns one on
+/* tls1_curve_params_from_ec_key sets |*out_group_id| and |*out_comp_id| to the
+ * TLS group ID and point format, respectively, for |ec|. It returns one on
  * success and zero on failure. */
-static int tls1_curve_params_from_ec_key(uint16_t *out_curve_id,
+static int tls1_curve_params_from_ec_key(uint16_t *out_group_id,
                                          uint8_t *out_comp_id, EC_KEY *ec) {
   int nid;
   uint16_t id;
@@ -412,14 +412,14 @@
     return 0;
   }
 
-  /* Determine curve ID */
+  /* Determine group ID */
   nid = EC_GROUP_get_curve_name(grp);
-  if (!ssl_nid_to_curve_id(&id, nid)) {
+  if (!ssl_nid_to_group_id(&id, nid)) {
     return 0;
   }
 
-  /* Set the named curve ID. Arbitrary explicit curves are not supported. */
-  *out_curve_id = id;
+  /* Set the named group ID. Arbitrary explicit groups are not supported. */
+  *out_group_id = id;
 
   if (out_comp_id) {
     if (EC_KEY_get0_public_key(ec) == NULL) {
@@ -435,35 +435,35 @@
   return 1;
 }
 
-/* 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
+/* tls1_check_group_id returns one if |group_id| is consistent with both our
+ * and the peer's group 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) {
-  const uint16_t *curves;
-  size_t curves_len, i, get_peer_curves;
+int tls1_check_group_id(SSL *ssl, uint16_t group_id) {
+  const uint16_t *groups;
+  size_t groups_len, i, get_peer_groups;
 
   /* 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 && !ssl->server) {
+  for (get_peer_groups = 0; get_peer_groups <= 1; get_peer_groups++) {
+    if (get_peer_groups && !ssl->server) {
       /* Servers do not present a preference list so, if we are a client, only
        * check our list. */
       continue;
     }
 
-    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,
+    tls1_get_grouplist(ssl, get_peer_groups, &groups, &groups_len);
+    if (get_peer_groups && groups_len == 0) {
+      /* Clients are not required to send a supported_groups extension. In this
+       * case, the server is free to pick any group it likes. See RFC 4492,
        * section 4, paragraph 3. */
       continue;
     }
-    for (i = 0; i < curves_len; i++) {
-      if (curves[i] == curve_id) {
+    for (i = 0; i < groups_len; i++) {
+      if (groups[i] == group_id) {
         break;
       }
     }
 
-    if (i == curves_len) {
+    if (i == groups_len) {
       return 0;
     }
   }
@@ -474,7 +474,7 @@
 int tls1_check_ec_cert(SSL *ssl, X509 *x) {
   int ret = 0;
   EVP_PKEY *pkey = X509_get_pubkey(x);
-  uint16_t curve_id;
+  uint16_t group_id;
   uint8_t comp_id;
 
   if (!pkey) {
@@ -482,8 +482,8 @@
   }
   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) ||
+      !tls1_curve_params_from_ec_key(&group_id, &comp_id, ec_key) ||
+      !tls1_check_group_id(ssl, group_id) ||
       comp_id != TLSEXT_ECPOINTFORMAT_uncompressed) {
     goto done;
   }
@@ -1809,35 +1809,36 @@
 }
 
 
-/* EC supported curves.
+/* Negotiated Groups
  *
- * https://tools.ietf.org/html/rfc4492#section-5.1.2 */
+ * https://tools.ietf.org/html/rfc4492#section-5.1.2
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-12#section-6.3.2.2 */
 
-static void ext_ec_curves_init(SSL *ssl) {
-  OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
-  ssl->s3->tmp.peer_ellipticcurvelist = NULL;
-  ssl->s3->tmp.peer_ellipticcurvelist_length = 0;
+static void ext_supported_groups_init(SSL *ssl) {
+  OPENSSL_free(ssl->s3->tmp.peer_supported_group_list);
+  ssl->s3->tmp.peer_supported_group_list = NULL;
+  ssl->s3->tmp.peer_supported_group_list_len = 0;
 }
 
-static int ext_ec_curves_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_supported_groups_add_clienthello(SSL *ssl, CBB *out) {
   if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
     return 1;
   }
 
-  CBB contents, curves_bytes;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_elliptic_curves) ||
+  CBB contents, groups_bytes;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
-      !CBB_add_u16_length_prefixed(&contents, &curves_bytes)) {
+      !CBB_add_u16_length_prefixed(&contents, &groups_bytes)) {
     return 0;
   }
 
-  const uint16_t *curves;
-  size_t curves_len;
-  tls1_get_curvelist(ssl, 0, &curves, &curves_len);
+  const uint16_t *groups;
+  size_t groups_len;
+  tls1_get_grouplist(ssl, 0, &groups, &groups_len);
 
   size_t i;
-  for (i = 0; i < curves_len; i++) {
-    if (!CBB_add_u16(&curves_bytes, curves[i])) {
+  for (i = 0; i < groups_len; i++) {
+    if (!CBB_add_u16(&groups_bytes, groups[i])) {
       return 0;
     }
   }
@@ -1845,54 +1846,55 @@
   return CBB_flush(out);
 }
 
-static int ext_ec_curves_parse_serverhello(SSL *ssl, uint8_t *out_alert,
-                                           CBS *contents) {
+static int ext_supported_groups_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+                                                  CBS *contents) {
   /* This extension is not expected to be echoed by servers and is ignored. */
   return 1;
 }
 
-static int ext_ec_curves_parse_clienthello(SSL *ssl, uint8_t *out_alert,
-                                           CBS *contents) {
+static int ext_supported_groups_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                                  CBS *contents) {
   if (contents == NULL) {
     return 1;
   }
 
-  CBS elliptic_curve_list;
-  if (!CBS_get_u16_length_prefixed(contents, &elliptic_curve_list) ||
-      CBS_len(&elliptic_curve_list) == 0 ||
-      (CBS_len(&elliptic_curve_list) & 1) != 0 ||
+  CBS supported_group_list;
+  if (!CBS_get_u16_length_prefixed(contents, &supported_group_list) ||
+      CBS_len(&supported_group_list) == 0 ||
+      (CBS_len(&supported_group_list) & 1) != 0 ||
       CBS_len(contents) != 0) {
     return 0;
   }
 
-  ssl->s3->tmp.peer_ellipticcurvelist = OPENSSL_malloc(CBS_len(&elliptic_curve_list));
-  if (ssl->s3->tmp.peer_ellipticcurvelist == NULL) {
+  ssl->s3->tmp.peer_supported_group_list = OPENSSL_malloc(
+      CBS_len(&supported_group_list));
+  if (ssl->s3->tmp.peer_supported_group_list == NULL) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return 0;
   }
 
-  const size_t num_curves = CBS_len(&elliptic_curve_list) / 2;
+  const size_t num_groups = CBS_len(&supported_group_list) / 2;
   size_t i;
-  for (i = 0; i < num_curves; i++) {
-    if (!CBS_get_u16(&elliptic_curve_list,
-                     &ssl->s3->tmp.peer_ellipticcurvelist[i])) {
+  for (i = 0; i < num_groups; i++) {
+    if (!CBS_get_u16(&supported_group_list,
+                     &ssl->s3->tmp.peer_supported_group_list[i])) {
       goto err;
     }
   }
 
-  assert(CBS_len(&elliptic_curve_list) == 0);
-  ssl->s3->tmp.peer_ellipticcurvelist_length = num_curves;
+  assert(CBS_len(&supported_group_list) == 0);
+  ssl->s3->tmp.peer_supported_group_list_len = num_groups;
 
   return 1;
 
 err:
-  OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
-  ssl->s3->tmp.peer_ellipticcurvelist = NULL;
+  OPENSSL_free(ssl->s3->tmp.peer_supported_group_list);
+  ssl->s3->tmp.peer_supported_group_list = NULL;
   *out_alert = SSL_AD_INTERNAL_ERROR;
   return 0;
 }
 
-static int ext_ec_curves_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_supported_groups_add_serverhello(SSL *ssl, CBB *out) {
   /* Servers don't echo this extension. */
   return 1;
 }
@@ -2003,12 +2005,12 @@
    * intolerant to the last extension being zero-length. See
    * https://crbug.com/363583. */
   {
-    TLSEXT_TYPE_elliptic_curves,
-    ext_ec_curves_init,
-    ext_ec_curves_add_clienthello,
-    ext_ec_curves_parse_serverhello,
-    ext_ec_curves_parse_clienthello,
-    ext_ec_curves_add_serverhello,
+    TLSEXT_TYPE_supported_groups,
+    ext_supported_groups_init,
+    ext_supported_groups_add_clienthello,
+    ext_supported_groups_parse_serverhello,
+    ext_supported_groups_parse_clienthello,
+    ext_supported_groups_add_serverhello,
   },
 };
 
diff --git a/src/ssl/test/README.md b/src/ssl/test/README.md
new file mode 100644
index 0000000..7a46c32
--- /dev/null
+++ b/src/ssl/test/README.md
@@ -0,0 +1,35 @@
+# BoringSSL SSL Tests
+
+This directory contains BoringSSL's protocol-level test suite.
+
+Testing a TLS implementation can be difficult. We need to produce invalid but
+sufficiently correct handshakes to get our implementation close to its edge
+cases. TLS's cryptographic steps mean we cannot use a transcript and effectively
+need a TLS implementation on the other end. But we do not wish to litter
+BoringSSL with options for bugs to test against.
+
+Instead, we use a fork of the Go `crypto/tls` package, heavily patched with
+configurable bugs. This code, along with a test suite and harness written in Go,
+lives in the `runner` directory. The harness runs BoringSSL via a C/C++ shim
+binary which lives in this directory. All communication with the shim binary
+occurs with command-line flags, sockets, and standard I/O.
+
+This strategy also ensures we always test against a second implementation. All
+features should be implemented twice, once in C for BoringSSL and once in Go for
+testing. If possible, the Go code should be suitable for potentially
+upstreaming. However, sometimes test code has different needs. For example, our
+test DTLS code enforces strict ordering on sequence numbers and has controlled
+packet drop simulation.
+
+To run the tests manually, run `go test` from the `runner` directory. It takes
+command-line flags found at the top of `runner/runner.go`. The `-help` option
+also works after using `go test -c` to make a `runner.test` binary first.
+
+If adding a new test, these files may be a good starting point:
+
+ * `runner/runner.go`: the test harness and all the individual tests.
+ * `runner/common.go`: contains the `Config` and `ProtocolBugs` struct which
+   control the Go TLS implementation's behavior.
+ * `test_config.h`, `test_config.cc`: the command-line flags which control the
+   shim's behavior.
+ * `bssl_shim.cc`: the shim binary itself.
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 5effa58..519736d 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -117,11 +117,11 @@
 static int g_config_index = 0;
 static int g_state_index = 0;
 
-static bool SetConfigPtr(SSL *ssl, const TestConfig *config) {
+static bool SetTestConfig(SSL *ssl, const TestConfig *config) {
   return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1;
 }
 
-static const TestConfig *GetConfigPtr(const SSL *ssl) {
+static const TestConfig *GetTestConfig(const SSL *ssl) {
   return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index);
 }
 
@@ -300,7 +300,7 @@
 
 static bool GetCertificate(SSL *ssl, ScopedX509 *out_x509,
                            ScopedEVP_PKEY *out_pkey) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
 
   if (!config->digest_prefs.empty()) {
     std::unique_ptr<char, Free<char>> digest_prefs(
@@ -353,7 +353,7 @@
 
   if (pkey) {
     TestState *test_state = GetTestState(ssl);
-    const TestConfig *config = GetConfigPtr(ssl);
+    const TestConfig *config = GetTestConfig(ssl);
     if (config->async) {
       test_state->private_key = std::move(pkey);
       SSL_set_private_key_method(ssl, &g_async_private_key_method);
@@ -370,7 +370,7 @@
 }
 
 static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) {
-  const TestConfig *config = GetConfigPtr(ctx->ssl);
+  const TestConfig *config = GetTestConfig(ctx->ssl);
   GetTestState(ctx->ssl)->early_callback_called = true;
 
   if (!config->expected_server_name.empty()) {
@@ -422,7 +422,7 @@
 }
 
 static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) {
-  if (GetConfigPtr(ssl)->async && !GetTestState(ssl)->cert_ready) {
+  if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) {
     return -1;
   }
 
@@ -446,7 +446,7 @@
 static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) {
   SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx,
       SSL_get_ex_data_X509_STORE_CTX_idx());
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
 
   if (!config->expected_ocsp_response.empty()) {
     const uint8_t *data;
@@ -468,7 +468,7 @@
 
 static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
                                         unsigned int *out_len, void *arg) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
   if (config->advertise_npn.empty()) {
     return SSL_TLSEXT_ERR_NOACK;
   }
@@ -480,7 +480,7 @@
 
 static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
                                    const uint8_t* in, unsigned inlen, void* arg) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
   if (config->select_next_proto.empty()) {
     return SSL_TLSEXT_ERR_NOACK;
   }
@@ -492,7 +492,7 @@
 
 static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen,
                               const uint8_t* in, unsigned inlen, void* arg) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
   if (config->decline_alpn) {
     return SSL_TLSEXT_ERR_NOACK;
   }
@@ -514,7 +514,7 @@
                                   char *out_identity,
                                   unsigned max_identity_len,
                                   uint8_t *out_psk, unsigned max_psk_len) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
 
   if (strcmp(hint ? hint : "", config->psk_identity.c_str()) != 0) {
     fprintf(stderr, "Server PSK hint did not match.\n");
@@ -536,7 +536,7 @@
 
 static unsigned PskServerCallback(SSL *ssl, const char *identity,
                                   uint8_t *out_psk, unsigned max_psk_len) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
 
   if (strcmp(identity, config->psk_identity.c_str()) != 0) {
     fprintf(stderr, "Client PSK identity did not match.\n");
@@ -584,7 +584,7 @@
 }
 
 static int DDoSCallback(const struct ssl_early_callback_ctx *early_context) {
-  const TestConfig *config = GetConfigPtr(early_context->ssl);
+  const TestConfig *config = GetTestConfig(early_context->ssl);
   static int callback_num = 0;
 
   callback_num++;
@@ -597,7 +597,7 @@
 
 static void InfoCallback(const SSL *ssl, int type, int val) {
   if (type == SSL_CB_HANDSHAKE_DONE) {
-    if (GetConfigPtr(ssl)->handshake_never_done) {
+    if (GetTestConfig(ssl)->handshake_never_done) {
       fprintf(stderr, "handshake completed\n");
       // Abort before any expected error code is printed, to ensure the overall
       // test fails.
@@ -633,7 +633,7 @@
   }
 
   if (!encrypt) {
-    return GetConfigPtr(ssl)->renew_ticket ? 2 : 1;
+    return GetTestConfig(ssl)->renew_ticket ? 2 : 1;
   }
   return 1;
 }
@@ -655,10 +655,10 @@
     abort();
   }
 
-  if (GetConfigPtr(ssl)->custom_extension_skip) {
+  if (GetTestConfig(ssl)->custom_extension_skip) {
     return 0;
   }
-  if (GetConfigPtr(ssl)->custom_extension_fail_add) {
+  if (GetTestConfig(ssl)->custom_extension_fail_add) {
     return -1;
   }
 
@@ -888,7 +888,7 @@
     return false;
   }
 
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
   TestState *test_state = GetTestState(ssl);
   if (test_state->clock_delta.tv_usec != 0 ||
       test_state->clock_delta.tv_sec != 0) {
@@ -926,7 +926,7 @@
       AsyncBioAllowWrite(test_state->async_bio, 1);
       return true;
     case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
-      ScopedEVP_PKEY pkey = LoadPrivateKey(GetConfigPtr(ssl)->send_channel_id);
+      ScopedEVP_PKEY pkey = LoadPrivateKey(GetTestConfig(ssl)->send_channel_id);
       if (!pkey) {
         return false;
       }
@@ -953,7 +953,7 @@
 // DoRead reads from |ssl|, resolving any asynchronous operations. It returns
 // the result value of the final |SSL_read| call.
 static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
   TestState *test_state = GetTestState(ssl);
   int ret;
   do {
@@ -974,7 +974,7 @@
 // WriteAll writes |in_len| bytes from |in| to |ssl|, resolving any asynchronous
 // operations. It returns the result of the final |SSL_write| call.
 static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
   int ret;
   do {
     ret = SSL_write(ssl, in, in_len);
@@ -989,7 +989,7 @@
 // DoShutdown calls |SSL_shutdown|, resolving any asynchronous operations. It
 // returns the result of the final |SSL_shutdown| call.
 static int DoShutdown(SSL *ssl) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
   int ret;
   do {
     ret = SSL_shutdown(ssl);
@@ -1001,7 +1001,7 @@
 // initial handshake (or False Starts), whether all the properties are
 // consistent with the test configuration and invariants.
 static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
-  const TestConfig *config = GetConfigPtr(ssl);
+  const TestConfig *config = GetTestConfig(ssl);
 
   if (SSL_get_current_cipher(ssl) == nullptr) {
     fprintf(stderr, "null cipher after handshake\n");
@@ -1187,7 +1187,7 @@
     return false;
   }
 
-  if (!SetConfigPtr(ssl.get(), config) ||
+  if (!SetTestConfig(ssl.get(), config) ||
       !SetTestState(ssl.get(), std::unique_ptr<TestState>(new TestState))) {
     return false;
   }
@@ -1219,6 +1219,9 @@
   if (config->partial_write) {
     SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE);
   }
+  if (config->no_tls13) {
+    SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3);
+  }
   if (config->no_tls12) {
     SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2);
   }
@@ -1310,13 +1313,17 @@
   }
   if (config->enable_all_curves) {
     static const int kAllCurves[] = {
-        NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519,
+      NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519,
     };
     if (!SSL_set1_curves(ssl.get(), kAllCurves,
                          sizeof(kAllCurves) / sizeof(kAllCurves[0]))) {
       return false;
     }
   }
+  if (config->initial_timeout_duration_ms > 0) {
+    DTLSv1_set_initial_timeout_duration(ssl.get(),
+                                        config->initial_timeout_duration_ms);
+  }
 
   int sock = Connect(config->port);
   if (sock == -1) {
diff --git a/src/ssl/test/runner/chacha20_poly1305.go b/src/ssl/test/runner/chacha20_poly1305.go
index 3c6ad82..8b97545 100644
--- a/src/ssl/test/runner/chacha20_poly1305.go
+++ b/src/ssl/test/runner/chacha20_poly1305.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
 package runner
 
 import (
diff --git a/src/ssl/test/runner/chacha20_poly1305_test.go b/src/ssl/test/runner/chacha20_poly1305_test.go
index 4d19b8c..8cecb5c 100644
--- a/src/ssl/test/runner/chacha20_poly1305_test.go
+++ b/src/ssl/test/runner/chacha20_poly1305_test.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
 package runner
 
 import (
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index bfd31a5..799f2d5 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -43,6 +43,9 @@
 	// client indicates that it supports ECC with a curve and point format
 	// that we're happy with.
 	suiteECDHE = 1 << iota
+	// suiteCECPQ1 indicates that the cipher suite uses the
+	// experimental, temporary, and non-standard CECPQ1 key agreement.
+	suiteCECPQ1
 	// suiteECDSA indicates that the cipher suite involves an ECDSA
 	// signature and therefore may only be selected when the server's
 	// certificate is ECDSA. If this is not set then the cipher suite is
@@ -104,6 +107,10 @@
 	{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil},
 	{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
 	{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+	{TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, cecpq1RSAKA, suiteCECPQ1 | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, cecpq1ECDSAKA, suiteCECPQ1 | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, cecpq1RSAKA, suiteCECPQ1 | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+	{TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, cecpq1ECDSAKA, suiteCECPQ1 | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
 	{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, dheRSAKA, suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, dheRSAKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
 	{TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil},
@@ -373,6 +380,15 @@
 	}
 }
 
+func cecpq1ECDSAKA(version uint16) keyAgreement {
+	return &cecpq1KeyAgreement{
+		auth: &signedKeyAgreement{
+			sigType: signatureECDSA,
+			version: version,
+		},
+	}
+}
+
 func ecdheRSAKA(version uint16) keyAgreement {
 	return &ecdheKeyAgreement{
 		auth: &signedKeyAgreement{
@@ -382,6 +398,15 @@
 	}
 }
 
+func cecpq1RSAKA(version uint16) keyAgreement {
+	return &cecpq1KeyAgreement{
+		auth: &signedKeyAgreement{
+			sigType: signatureRSA,
+			version: version,
+		},
+	}
+}
+
 func dheRSAKA(version uint16) keyAgreement {
 	return &dheKeyAgreement{
 		auth: &signedKeyAgreement{
@@ -472,4 +497,8 @@
 const (
 	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD   uint16 = 0xcc13
 	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD uint16 = 0xcc14
+	TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256      uint16 = 0x16b7
+	TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256    uint16 = 0x16b8
+	TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384            uint16 = 0x16b9
+	TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384          uint16 = 0x16ba
 )
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 2e9ce04..57b7b29 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -787,6 +787,10 @@
 	// on connection shutdown.
 	NoCloseNotify bool
 
+	// SendAlertOnShutdown, if non-zero, is the alert to send instead of
+	// close_notify on shutdown.
+	SendAlertOnShutdown alert
+
 	// ExpectCloseNotify, if true, requires a close_notify from the peer on
 	// shutdown. Records from the peer received after close_notify is sent
 	// are not discard.
@@ -834,6 +838,10 @@
 	// NullAllCiphers, if true, causes every cipher to behave like the null
 	// cipher.
 	NullAllCiphers bool
+
+	// SendSCTListOnResume, if not nil, causes the server to send the
+	// supplied SCT list in resumption handshakes.
+	SendSCTListOnResume []byte
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 43548e8..3913995 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -1292,7 +1292,16 @@
 	c.handshakeMutex.Lock()
 	defer c.handshakeMutex.Unlock()
 	if c.handshakeComplete && !c.config.Bugs.NoCloseNotify {
-		alertErr = c.sendAlert(alertCloseNotify)
+		alert := alertCloseNotify
+		if c.config.Bugs.SendAlertOnShutdown != 0 {
+			alert = c.config.Bugs.SendAlertOnShutdown
+		}
+		alertErr = c.sendAlert(alert)
+		// Clear local alerts when sending alerts so we continue to wait
+		// for the peer rather than closing the socket early.
+		if opErr, ok := alertErr.(*net.OpError); ok && opErr.Op == "local error" {
+			alertErr = nil
+		}
 	}
 
 	// Consume a close_notify from the peer if one hasn't been received
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index c3ee521..a31dfc0 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -46,6 +46,7 @@
 	b := c.rawInput
 
 	// Read a new packet only if the current one is empty.
+	var newPacket bool
 	if len(b.data) == 0 {
 		// Pick some absurdly large buffer size.
 		b.resize(maxCiphertext + recordHeaderLen)
@@ -57,6 +58,7 @@
 			return 0, nil, fmt.Errorf("dtls: exceeded maximum packet length")
 		}
 		c.rawInput.resize(n)
+		newPacket = true
 	}
 
 	// Read out one record.
@@ -108,6 +110,13 @@
 		c.in.setErrorLocked(c.sendAlert(err))
 	}
 	b.off = off
+
+	// Require that ChangeCipherSpec always share a packet with either the
+	// previous or next handshake message.
+	if newPacket && typ == recordTypeChangeCipherSpec && c.rawInput == nil {
+		return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: ChangeCipherSpec not packed together with Finished"))
+	}
+
 	return typ, b, nil
 }
 
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index d2cac98..72d1eb9 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -488,6 +488,10 @@
 	hs.hello.sessionId = hs.clientHello.sessionId
 	hs.hello.ticketSupported = c.config.Bugs.RenewTicketOnResume
 
+	if c.config.Bugs.SendSCTListOnResume != nil {
+		hs.hello.sctList = c.config.Bugs.SendSCTListOnResume
+	}
+
 	hs.finishedHash = newFinishedHash(c.vers, hs.suite)
 	hs.finishedHash.discardHandshakeBuffer()
 	hs.writeClientHash(hs.clientHello.marshal())
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 54aa3d3..9a9962b 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -21,6 +21,7 @@
 	"math/big"
 
 	"./curve25519"
+	"./newhope"
 )
 
 var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
@@ -252,13 +253,16 @@
 
 // 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)
+	// offer generates a keypair using rand. It returns the encoded |publicKey|.
+	offer(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)
+	// accept responds to the |peerKey| generated by |offer| with the acceptor's
+	// |publicKey|, and returns agreed-upon |preMasterSecret| to the acceptor.
+	accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error)
+
+	// finish returns the computed |preMasterSecret|, given the |peerKey|
+	// generated by |accept|.
+	finish(peerKey []byte) (preMasterSecret []byte, err error)
 }
 
 // ellipticECDHCurve implements ecdhCurve with an elliptic.Curve.
@@ -267,7 +271,7 @@
 	privateKey []byte
 }
 
-func (e *ellipticECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) {
+func (e *ellipticECDHCurve) offer(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 {
@@ -276,7 +280,19 @@
 	return elliptic.Marshal(e.curve, x, y), nil
 }
 
-func (e *ellipticECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) {
+func (e *ellipticECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+	publicKey, err = e.offer(rand)
+	if err != nil {
+		return nil, nil, err
+	}
+	preMasterSecret, err = e.finish(peerKey)
+	if err != nil {
+		return nil, nil, err
+	}
+	return
+}
+
+func (e *ellipticECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
 	x, y := elliptic.Unmarshal(e.curve, peerKey)
 	if x == nil {
 		return nil, errors.New("tls: invalid peer key")
@@ -294,7 +310,7 @@
 	privateKey [32]byte
 }
 
-func (e *x25519ECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) {
+func (e *x25519ECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) {
 	_, err = io.ReadFull(rand, e.privateKey[:])
 	if err != nil {
 		return
@@ -304,7 +320,19 @@
 	return out[:], nil
 }
 
-func (e *x25519ECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) {
+func (e *x25519ECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+	publicKey, err = e.offer(rand)
+	if err != nil {
+		return nil, nil, err
+	}
+	preMasterSecret, err = e.finish(peerKey)
+	if err != nil {
+		return nil, nil, err
+	}
+	return
+}
+
+func (e *x25519ECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
 	if len(peerKey) != 32 {
 		return nil, errors.New("tls: invalid peer key")
 	}
@@ -321,6 +349,66 @@
 	return out[:], nil
 }
 
+// cecpq1Curve is combined elliptic curve (X25519) and post-quantum (new hope) key
+// agreement.
+type cecpq1Curve struct {
+	x25519  *x25519ECDHCurve
+	newhope *newhope.Poly
+}
+
+func (e *cecpq1Curve) offer(rand io.Reader) (publicKey []byte, err error) {
+	var x25519OfferMsg, newhopeOfferMsg []byte
+
+	e.x25519 = new(x25519ECDHCurve)
+	if x25519OfferMsg, err = e.x25519.offer(rand); err != nil {
+		return nil, err
+	}
+
+	newhopeOfferMsg, e.newhope = newhope.Offer(rand)
+
+	return append(x25519OfferMsg, newhopeOfferMsg[:]...), nil
+}
+
+func (e *cecpq1Curve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+	if len(peerKey) != 32+newhope.OfferMsgLen {
+		return nil, nil, errors.New("cecpq1: invalid offer message")
+	}
+
+	var x25519AcceptMsg, newhopeAcceptMsg []byte
+	var x25519Secret []byte
+	var newhopeSecret newhope.Key
+
+	x25519 := new(x25519ECDHCurve)
+	if x25519AcceptMsg, x25519Secret, err = x25519.accept(rand, peerKey[:32]); err != nil {
+		return nil, nil, err
+	}
+
+	if newhopeSecret, newhopeAcceptMsg, err = newhope.Accept(rand, peerKey[32:]); err != nil {
+		return nil, nil, err
+	}
+
+	return append(x25519AcceptMsg, newhopeAcceptMsg[:]...), append(x25519Secret, newhopeSecret[:]...), nil
+}
+
+func (e *cecpq1Curve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
+	if len(peerKey) != 32+newhope.AcceptMsgLen {
+		return nil, errors.New("cecpq1: invalid accept message")
+	}
+
+	var x25519Secret []byte
+	var newhopeSecret newhope.Key
+
+	if x25519Secret, err = e.x25519.finish(peerKey[:32]); err != nil {
+		return nil, err
+	}
+
+	if newhopeSecret, err = e.newhope.Finish(peerKey[32:]); err != nil {
+		return nil, err
+	}
+
+	return append(x25519Secret, newhopeSecret[:]...), nil
+}
+
 func curveForCurveID(id CurveID) (ecdhCurve, bool) {
 	switch id {
 	case CurveP224:
@@ -551,7 +639,7 @@
 		return nil, errors.New("tls: preferredCurves includes unsupported curve")
 	}
 
-	publicKey, err := ka.curve.generateKeypair(config.rand())
+	publicKey, err := ka.curve.offer(config.rand())
 	if err != nil {
 		return nil, err
 	}
@@ -577,7 +665,7 @@
 	if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
 		return nil, errClientKeyExchange
 	}
-	return ka.curve.computeSecret(ckx.ciphertext[1:])
+	return ka.curve.finish(ckx.ciphertext[1:])
 }
 
 func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
@@ -612,11 +700,7 @@
 		return nil, nil, errors.New("missing ServerKeyExchange message")
 	}
 
-	publicKey, err := ka.curve.generateKeypair(config.rand())
-	if err != nil {
-		return nil, nil, err
-	}
-	preMasterSecret, err := ka.curve.computeSecret(ka.peerKey)
+	publicKey, preMasterSecret, err := ka.curve.accept(config.rand(), ka.peerKey)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -632,6 +716,77 @@
 	return preMasterSecret, ckx, nil
 }
 
+// cecpq1RSAKeyAgreement is like an ecdheKeyAgreement, but using the cecpq1Curve
+// pseudo-curve, and without any parameters (e.g. curve name) other than the
+// keys being exchanged. The signature may either be ECDSA or RSA.
+type cecpq1KeyAgreement struct {
+	auth    keyAgreementAuthentication
+	curve   ecdhCurve
+	peerKey []byte
+}
+
+func (ka *cecpq1KeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+	ka.curve = &cecpq1Curve{}
+	publicKey, err := ka.curve.offer(config.rand())
+	if err != nil {
+		return nil, err
+	}
+
+	var params []byte
+	params = append(params, byte(len(publicKey)>>8))
+	params = append(params, byte(len(publicKey)&0xff))
+	params = append(params, publicKey[:]...)
+
+	return ka.auth.signParameters(config, cert, clientHello, hello, params)
+}
+
+func (ka *cecpq1KeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+	if len(ckx.ciphertext) < 2 {
+		return nil, errClientKeyExchange
+	}
+	peerKeyLen := int(ckx.ciphertext[0])<<8 + int(ckx.ciphertext[1])
+	peerKey := ckx.ciphertext[2:]
+	if peerKeyLen != len(peerKey) {
+		return nil, errClientKeyExchange
+	}
+	return ka.curve.finish(peerKey)
+}
+
+func (ka *cecpq1KeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+	if len(skx.key) < 2 {
+		return errServerKeyExchange
+	}
+	peerKeyLen := int(skx.key[0])<<8 + int(skx.key[1])
+	// Save the peer key for later.
+	if len(skx.key) < 2+peerKeyLen {
+		return errServerKeyExchange
+	}
+	ka.peerKey = skx.key[2 : 2+peerKeyLen]
+	if peerKeyLen != len(ka.peerKey) {
+		return errServerKeyExchange
+	}
+
+	// Check the signature.
+	params := skx.key[:2+peerKeyLen]
+	sig := skx.key[2+peerKeyLen:]
+	return ka.auth.verifyParameters(config, clientHello, serverHello, cert, params, sig)
+}
+
+func (ka *cecpq1KeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+	curve := &cecpq1Curve{}
+	publicKey, preMasterSecret, err := curve.accept(config.rand(), ka.peerKey)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ckx := new(clientKeyExchangeMsg)
+	ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)>>8))
+	ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)&0xff))
+	ckx.ciphertext = append(ckx.ciphertext, publicKey[:]...)
+
+	return preMasterSecret, ckx, nil
+}
+
 // dheRSAKeyAgreement implements a TLS key agreement where the server generates
 // an ephemeral Diffie-Hellman public/private key pair and signs it. The
 // pre-master secret is then calculated using Diffie-Hellman.
diff --git a/src/ssl/test/runner/newhope/newhope.go b/src/ssl/test/runner/newhope/newhope.go
new file mode 100644
index 0000000..36c2c85
--- /dev/null
+++ b/src/ssl/test/runner/newhope/newhope.go
@@ -0,0 +1,319 @@
+// Copyright (c) 2016, 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. */
+
+// package newhope contains a post-quantum key agreement algorithm,
+// reimplemented from the reference implementation at
+// https://github.com/tpoeppelmann/newhope.
+//
+// Note that this package does not interoperate with the reference
+// implementation.
+package newhope
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"errors"
+	"io"
+)
+
+const (
+	// q is the prime that defines the field.
+	q = 12289
+	// n is the number of coefficients in polynomials.
+	n = 1024
+	// k is the width of the noise distribution.
+	k = 16
+
+	// These values are used in the NTT calculation. See the paper for
+	// details about their origins.
+	omega        = 49
+	invOmega     = 1254
+	sqrtOmega    = 7
+	invSqrtOmega = 8778
+	invN         = 12277
+
+	// encodedPolyLen is the length, in bytes, of an encoded polynomial.  The
+	// encoding uses 14 bits per coefficient.
+	encodedPolyLen = (n * 14) / 8
+
+	// offerMsgLen is the length, in bytes, of the offering (first) message of
+	// the key exchange.
+	OfferMsgLen = encodedPolyLen + 32
+
+	// acceptMsgLen is the length, in bytes, of the accepting (second) message
+	// of the key exchange.
+	AcceptMsgLen = encodedPolyLen + 256
+)
+
+// count16Bits returns the number of '1' bits in v.
+func count16Bits(v uint16) (sum uint16) {
+	for i := 0; i < 16; i++ {
+		sum += v & 1
+		v >>= 1
+	}
+
+	return sum
+}
+
+// Poly is a polynomial of n coefficients.
+type Poly [n]uint16
+
+// Key is the result of a key agreement.
+type Key [32]uint8
+
+// sampleNoise returns a random polynomial where the coefficients are
+// drawn from the noise distribution.
+func sampleNoise(rand io.Reader) *Poly {
+	poly := new(Poly)
+	buf := make([]byte, 4)
+
+	for i := range poly {
+		if _, err := io.ReadFull(rand, buf); err != nil {
+			panic(err)
+		}
+		a := count16Bits(uint16(buf[0])<<8 | uint16(buf[1]))
+		b := count16Bits(uint16(buf[2])<<8 | uint16(buf[3]))
+		poly[i] = (q + a - b) % q
+	}
+
+	return poly
+}
+
+// randomPolynomial returns a random polynomial where the coefficients are
+// drawn uniformly at random from the underlying field.
+func randomPolynomial(rand io.Reader) *Poly {
+	poly := new(Poly)
+
+	buf := make([]byte, 2)
+	for i := range poly {
+		for {
+			if _, err := io.ReadFull(rand, buf); err != nil {
+				panic(err)
+			}
+
+			v := uint16(buf[1])<<8 | uint16(buf[0])
+			v &= 0x3fff
+
+			if v < q {
+				poly[i] = v
+				break
+			}
+		}
+	}
+
+	return poly
+}
+
+type zeroReader struct {
+	io.Reader
+}
+
+func (z *zeroReader) Read(dst []byte) (n int, err error) {
+	for i := range dst {
+		dst[i] = 0
+	}
+	return len(dst), nil
+}
+
+// seedToPolynomial uses AES-CTR to generate a pseudo-random polynomial given a
+// 32-byte seed.
+func seedToPolynomial(seed []byte) *Poly {
+	aes, err := aes.NewCipher(seed[0:16])
+	if err != nil {
+		panic(err)
+	}
+	stream := cipher.NewCTR(aes, seed[16:32])
+	reader := &cipher.StreamReader{S: stream, R: &zeroReader{}}
+	return randomPolynomial(reader)
+}
+
+// forwardNTT converts |in| into the frequency domain.
+func forwardNTT(in *Poly) *Poly {
+	return ntt(in, omega, sqrtOmega, 1, 1)
+}
+
+// inverseNTT converts |in| into the time domain.
+func inverseNTT(in *Poly) *Poly {
+	return ntt(in, invOmega, 1, invSqrtOmega, invN)
+}
+
+// ntt performs the number-theoretic transform (a discrete Fourier transform in
+// a field) on in. Significant magic is in effect here. See the paper for the
+// details of how this works.
+func ntt(in *Poly, omega, preScaleBase, postScaleBase, postScale uint16) *Poly {
+	out := new(Poly)
+	omega_to_the_i := 1
+
+	for i := range out {
+		omegaToTheIJ := 1
+		preScale := int(1)
+		sum := 0
+
+		for j := range in {
+			t := (int(in[j]) * preScale) % q
+			sum += (t * omegaToTheIJ) % q
+			omegaToTheIJ = (omegaToTheIJ * omega_to_the_i) % q
+			preScale = (int(preScaleBase) * preScale) % q
+		}
+
+		out[i] = uint16((sum * int(postScale)) % q)
+
+		omega_to_the_i = (omega_to_the_i * int(omega)) % q
+		postScale = uint16((int(postScale) * int(postScaleBase)) % q)
+	}
+
+	return out
+}
+
+// encodeRec encodes the reconciliation data compactly, for use in the accept
+// message.
+func encodeRec(rec *reconciliationData) []byte {
+	var ret [n / 4]byte
+
+	for i := 0; i < n/4; i++ {
+		ret[i] = rec[4*i] | rec[4*i+1]<<2 | rec[4*i+2]<<4 | rec[4*i+3]<<6
+	}
+
+	return ret[:]
+}
+
+// decodeRec decodes reconciliation data from the accept message.
+func decodeRec(message []byte) (rec *reconciliationData) {
+	rec = new(reconciliationData)
+
+	for i, b := range message {
+		rec[4*i] = b & 0x03
+		rec[4*i+1] = (b >> 2) & 0x3
+		rec[4*i+2] = (b >> 4) & 0x3
+		rec[4*i+3] = b >> 6
+	}
+
+	return rec
+}
+
+// encodePoly returns a byte array that encodes a polynomial compactly, with 14
+// bits per coefficient.
+func encodePoly(poly *Poly) []byte {
+	ret := make([]byte, encodedPolyLen)
+
+	for i := 0; i < n/4; i++ {
+		t0 := poly[4*i]
+		t1 := poly[4*i+1]
+		t2 := poly[4*i+2]
+		t3 := poly[4*i+3]
+
+		ret[7*i] = byte(t0)
+		ret[7*i+1] = byte(t0>>8) | byte(t1<<6)
+		ret[7*i+2] = byte(t1 >> 2)
+		ret[7*i+3] = byte(t1>>10) | byte(t2<<4)
+		ret[7*i+4] = byte(t2 >> 4)
+		ret[7*i+5] = byte(t2>>12) | byte(t3<<2)
+		ret[7*i+6] = byte(t3 >> 6)
+	}
+
+	return ret
+}
+
+// decodePoly inverts encodePoly.
+func decodePoly(encoded []byte) *Poly {
+	ret := new(Poly)
+
+	for i := 0; i < n/4; i++ {
+		ret[4*i] = uint16(encoded[7*i]) | uint16(encoded[7*i+1]&0x3f)<<8
+		ret[4*i+1] = uint16(encoded[7*i+1])>>6 | uint16(encoded[7*i+2])<<2 | uint16(encoded[7*i+3]&0x0f)<<10
+		ret[4*i+2] = uint16(encoded[7*i+3])>>4 | uint16(encoded[7*i+4])<<4 | uint16(encoded[7*i+5]&0x03)<<12
+		ret[4*i+3] = uint16(encoded[7*i+5])>>2 | uint16(encoded[7*i+6])<<6
+	}
+
+	return ret
+}
+
+// Offer starts a new key exchange. It returns a message that should be
+// transmitted to the peer, and a polynomial that must be retained in order to
+// complete the exchange.
+func Offer(rand io.Reader) (offerMsg []byte, sFreq *Poly) {
+	seed := make([]byte, 32)
+
+	if _, err := io.ReadFull(rand, seed); err != nil {
+		panic(err)
+	}
+
+	aFreq := seedToPolynomial(seed)
+	sFreq = forwardNTT(sampleNoise(rand))
+	eFreq := forwardNTT(sampleNoise(rand))
+
+	bFreq := new(Poly)
+	for i := range bFreq {
+		bFreq[i] = uint16((int(sFreq[i])*int(aFreq[i]) + int(eFreq[i])) % q)
+	}
+
+	offerMsg = encodePoly(bFreq)
+	offerMsg = append(offerMsg, seed[:]...)
+	return offerMsg, sFreq
+}
+
+// Accept processes a message generated by |Offer| and returns a reply message
+// and the shared key.
+func Accept(rand io.Reader, offerMsg []byte) (sharedKey Key, acceptMsg []byte, err error) {
+	if len(offerMsg) != OfferMsgLen {
+		return sharedKey, nil, errors.New("newhope: offer message has incorrect length")
+	}
+
+	bFreq := decodePoly(offerMsg)
+	seed := offerMsg[encodedPolyLen:]
+
+	aFreq := seedToPolynomial(seed)
+	sPrimeFreq := forwardNTT(sampleNoise(rand))
+	ePrimeFreq := forwardNTT(sampleNoise(rand))
+
+	uFreq := new(Poly)
+	for i := range uFreq {
+		uFreq[i] = uint16((int(sPrimeFreq[i])*int(aFreq[i]) + int(ePrimeFreq[i])) % q)
+	}
+
+	vFreq := new(Poly)
+	for i := range vFreq {
+		vFreq[i] = uint16((int(sPrimeFreq[i]) * int(bFreq[i])) % q)
+	}
+
+	v := inverseNTT(vFreq)
+	ePrimePrime := sampleNoise(rand)
+	for i := range v {
+		v[i] = uint16((int(v[i]) + int(ePrimePrime[i])) % q)
+	}
+
+	rec := helprec(rand, v)
+
+	sharedKey = reconcile(v, rec)
+	acceptMsg = encodePoly(uFreq)
+	acceptMsg = append(acceptMsg, encodeRec(rec)[:]...)
+	return sharedKey, acceptMsg, nil
+}
+
+// Finish processes the reply from the peer and returns the shared key.
+func (sk *Poly) Finish(acceptMsg []byte) (sharedKey Key, err error) {
+	if len(acceptMsg) != AcceptMsgLen {
+		return sharedKey, errors.New("newhope: accept message has incorrect length")
+	}
+
+	uFreq := decodePoly(acceptMsg[:encodedPolyLen])
+	rec := decodeRec(acceptMsg[encodedPolyLen:])
+
+	for i, u := range uFreq {
+		uFreq[i] = uint16((int(u) * int(sk[i])) % q)
+	}
+	u := inverseNTT(uFreq)
+
+	return reconcile(u, rec), nil
+}
diff --git a/src/ssl/test/runner/newhope/newhope_test.go b/src/ssl/test/runner/newhope/newhope_test.go
new file mode 100644
index 0000000..31e95c7
--- /dev/null
+++ b/src/ssl/test/runner/newhope/newhope_test.go
@@ -0,0 +1,154 @@
+// Copyright (c) 2016, 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. */
+
+package newhope
+
+import (
+	"bytes"
+	"crypto/rand"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"testing"
+)
+
+func TestNTTRoundTrip(t *testing.T) {
+	var a Poly
+	for i := range a {
+		a[i] = uint16(i)
+	}
+
+	frequency := forwardNTT(&a)
+	original := inverseNTT(frequency)
+
+	for i, v := range a {
+		if v != original[i] {
+			t.Errorf("NTT didn't invert correctly: original[%d] = %d", i, original[i])
+			break
+		}
+	}
+}
+
+func TestNTTInv(t *testing.T) {
+	var a Poly
+	for i := range a {
+		a[i] = uint16(i)
+	}
+
+	result := ntt(&a, invOmega, 1, invSqrtOmega, invN)
+	if result[0] != 6656 || result[1] != 1792 || result[2] != 1234 {
+		t.Errorf("NTT^-1 gave bad result: %v", result[:8])
+	}
+}
+
+func disabledTestNoise(t *testing.T) {
+	var buckets [1 + 2*k]int
+	numSamples := 100
+
+	for i := 0; i < numSamples; i++ {
+		noise := sampleNoise(rand.Reader)
+		for _, v := range noise {
+			value := (int(v) + k) % q
+			buckets[value]++
+		}
+	}
+
+	sum := 0
+	squareSum := 0
+
+	for i, count := range buckets {
+		sum += (i - k) * count
+		squareSum += (i - k) * (i - k) * count
+	}
+
+	mean := float64(sum) / float64(n*numSamples)
+	if mean < -0.5 || 0.5 < mean {
+		t.Errorf("mean out of range: %f", mean)
+	}
+
+	expectedVariance := 0.5 * 0.5 * float64(k*2) // I think?
+	variance := float64(squareSum)/float64(n*numSamples) - mean*mean
+
+	if variance < expectedVariance-1.0 || expectedVariance+1.0 < variance {
+		t.Errorf("variance out of range: got %f, want %f", variance, expectedVariance)
+	}
+
+	file, err := ioutil.TempFile("", "noise")
+	fmt.Printf("writing noise to %s\n", file.Name())
+	if err != nil {
+		t.Fatal(err)
+	}
+	for i, count := range buckets {
+		dots := ""
+		for i := 0; i < count/(3*numSamples); i++ {
+			dots += "++"
+		}
+		fmt.Fprintf(file, "%+d\t%d\t%s\n", i-k, count, dots)
+	}
+	file.Close()
+}
+
+func TestSeedToPolynomial(t *testing.T) {
+	seed := make([]byte, 32)
+	seed[0] = 1
+	seed[31] = 2
+
+	poly := seedToPolynomial(seed)
+	if poly[0] != 3313 || poly[1] != 9277 || poly[2] != 11020 {
+		t.Errorf("bad result: %v", poly[:3])
+	}
+}
+
+func TestEncodeDecodePoly(t *testing.T) {
+	poly := randomPolynomial(rand.Reader)
+	poly2 := decodePoly(encodePoly(poly))
+	if *poly != *poly2 {
+		t.Errorf("decodePoly(encodePoly) isn't the identity function")
+	}
+}
+
+func TestEncodeDecodeRec(t *testing.T) {
+	var r reconciliationData
+	if _, err := io.ReadFull(rand.Reader, r[:]); err != nil {
+		panic(err)
+	}
+	for i := range r {
+		r[i] &= 3
+	}
+
+	encoded := encodeRec(&r)
+	decoded := decodeRec(encoded)
+
+	if *decoded != r {
+		t.Errorf("bad decode of rec")
+	}
+}
+
+func TestExchange(t *testing.T) {
+	for count := 0; count < 64; count++ {
+		offerMsg, state := Offer(rand.Reader)
+		sharedKey1, acceptMsg, err := Accept(rand.Reader, offerMsg)
+		if err != nil {
+			t.Errorf("Accept: %v", err)
+		}
+		sharedKey2, err := state.Finish(acceptMsg)
+		if err != nil {
+			t.Fatal("Finish: %v", err)
+		}
+
+		if !bytes.Equal(sharedKey1[:], sharedKey2[:]) {
+			t.Fatalf("keys mismatched on iteration %d: %x vs %x", count, sharedKey1, sharedKey2)
+		}
+	}
+}
diff --git a/src/ssl/test/runner/newhope/reconciliation.go b/src/ssl/test/runner/newhope/reconciliation.go
new file mode 100644
index 0000000..07479fe
--- /dev/null
+++ b/src/ssl/test/runner/newhope/reconciliation.go
@@ -0,0 +1,132 @@
+// Copyright (c) 2016, 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. */
+
+package newhope
+
+// This file contains the reconciliation algorithm for NewHope. This is simply a
+// monkey-see-monkey-do version of the reference code, with the exception that
+// the key resulting from reconciliation is whitened with SHA2 rather than SHA3.
+//
+// Thanks to the authors of the reference code for allowing us to release this
+// under the BoringSSL license.
+
+import (
+	"crypto/sha256"
+	"io"
+)
+
+func abs(v int32) int32 {
+	mask := v >> 31
+	return (v ^ mask) - mask
+}
+
+func f(x int32) (v0, v1, k int32) {
+	// Next 6 lines compute t = x/q;
+	b := x * 2730
+	t := b >> 25
+	b = x - t*12289
+	b = 12288 - b
+	b >>= 31
+	t -= b
+
+	r := t & 1
+	xit := (t >> 1)
+	v0 = xit + r // v0 = round(x/(2*q))
+
+	t -= 1
+	r = t & 1
+	v1 = (t >> 1) + r
+
+	k = abs(x - (v0 * 2 * q))
+	return
+}
+
+// reconciliationData is the data needed for reconciliation. There are 2 bits
+// per coefficient; this is the unpacked form.
+type reconciliationData [n]uint8
+
+func helprec(rand io.Reader, v *Poly) *reconciliationData {
+	var randBits [n / (4 * 8)]byte
+	if _, err := io.ReadFull(rand, randBits[:]); err != nil {
+		panic(err)
+	}
+
+	ret := new(reconciliationData)
+
+	for i := uint(0); i < n/4; i++ {
+		rbit := int32((randBits[i>>3] >> (i & 7)) & 1)
+
+		a0, b0, k0 := f(8*int32(v[i]) + 4*rbit)
+		a1, b1, k1 := f(8*int32(v[256+i]) + 4*rbit)
+		a2, b2, k2 := f(8*int32(v[512+i]) + 4*rbit)
+		a3, b3, k3 := f(8*int32(v[768+i]) + 4*rbit)
+
+		k := (2*q - 1 - (k0 + k1 + k2 + k3)) >> 31
+
+		v0 := ((^k) & a0) ^ (k & b0)
+		v1 := ((^k) & a1) ^ (k & b1)
+		v2 := ((^k) & a2) ^ (k & b2)
+		v3 := ((^k) & a3) ^ (k & b3)
+
+		ret[i] = uint8((v0 - v3) & 3)
+		ret[i+256] = uint8((v1 - v3) & 3)
+		ret[i+512] = uint8((v2 - v3) & 3)
+		ret[i+768] = uint8((-k + 2*v3) & 3)
+	}
+
+	return ret
+}
+
+func g(x int32) int32 {
+	// Next 6 lines compute t = x/(4*q);
+	b := x * 2730
+	t := b >> 27
+	b = x - t*49156
+	b = 49155 - b
+	b >>= 31
+	t -= b
+
+	c := t & 1
+	t = (t >> 1) + c // t = round(x/(8*q))
+
+	t *= 8 * q
+
+	return abs(t - x)
+}
+
+func ldDecode(xi0, xi1, xi2, xi3 int32) uint8 {
+	t := g(xi0)
+	t += g(xi1)
+	t += g(xi2)
+	t += g(xi3)
+
+	t -= 8 * q
+	t >>= 31
+	return uint8(t & 1)
+}
+
+func reconcile(v *Poly, reconciliation *reconciliationData) Key {
+	key := new(Key)
+
+	for i := uint(0); i < n/4; i++ {
+		t0 := 16*q + 8*int32(v[i]) - q*(2*int32(reconciliation[i])+int32(reconciliation[i+768]))
+		t1 := 16*q + 8*int32(v[i+256]) - q*(2*int32(reconciliation[256+i])+int32(reconciliation[i+768]))
+		t2 := 16*q + 8*int32(v[i+512]) - q*(2*int32(reconciliation[512+i])+int32(reconciliation[i+768]))
+		t3 := 16*q + 8*int32(v[i+768]) - q*int32(reconciliation[i+768])
+
+		key[i>>3] |= ldDecode(t0, t1, t2, t3) << (i & 7)
+	}
+
+	return sha256.Sum256(key[:])
+}
diff --git a/src/ssl/test/runner/recordingconn.go b/src/ssl/test/runner/recordingconn.go
index 752610e..dfc10c7 100644
--- a/src/ssl/test/runner/recordingconn.go
+++ b/src/ssl/test/runner/recordingconn.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
 package runner
 
 import (
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 5b746c6..c10987e 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
 package runner
 
 import (
@@ -901,6 +915,10 @@
 	{"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},
+	{"CECPQ1-RSA-CHACHA20-POLY1305-SHA256", TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256},
+	{"CECPQ1-ECDSA-CHACHA20-POLY1305-SHA256", TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
+	{"CECPQ1-RSA-AES256-GCM-SHA384", TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384},
+	{"CECPQ1-ECDSA-AES256-GCM-SHA384", TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384},
 	{"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},
@@ -1391,7 +1409,7 @@
 		},
 		{
 			name:          "DisableEverything",
-			flags:         []string{"-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"},
+			flags:         []string{"-no-tls13", "-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"},
 			shouldFail:    true,
 			expectedError: ":WRONG_SSL_VERSION:",
 		},
@@ -2061,6 +2079,19 @@
 			shimShutsDown: true,
 		},
 		{
+			name: "Unclean-Shutdown-Alert",
+			config: Config{
+				Bugs: ProtocolBugs{
+					SendAlertOnShutdown: alertDecompressionFailure,
+					ExpectCloseNotify:   true,
+				},
+			},
+			shimShutsDown: true,
+			flags:         []string{"-check-close-notify"},
+			shouldFail:    true,
+			expectedError: ":SSLV3_ALERT_DECOMPRESSION_FAILURE:",
+		},
+		{
 			name: "LargePlaintext",
 			config: Config{
 				Bugs: ProtocolBugs{
@@ -2268,6 +2299,10 @@
 			// NULL ciphers must be explicitly enabled.
 			flags = append(flags, "-cipher", "DEFAULT:NULL-SHA")
 		}
+		if hasComponent(suite.name, "CECPQ1") {
+			// CECPQ1 ciphers must be explicitly enabled.
+			flags = append(flags, "-cipher", "DEFAULT:kCECPQ1")
+		}
 
 		for _, ver := range tlsVersions {
 			if ver.version < VersionTLS12 && isTLS12Only(suite.name) {
@@ -2714,6 +2749,40 @@
 		shouldFail:    true,
 		expectedError: ":UNEXPECTED_MESSAGE:",
 	})
+
+	// Client auth is only legal in certificate-based ciphers.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "ClientAuth-PSK",
+		config: Config{
+			CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA},
+			PreSharedKey: []byte("secret"),
+			ClientAuth:   RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-psk", "secret",
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_MESSAGE:",
+	})
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "ClientAuth-ECDHE_PSK",
+		config: Config{
+			CipherSuites: []uint16{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA},
+			PreSharedKey: []byte("secret"),
+			ClientAuth:   RequireAnyClientCert,
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-psk", "secret",
+		},
+		shouldFail:    true,
+		expectedError: ":UNEXPECTED_MESSAGE:",
+	})
 }
 
 func addExtendedMasterSecretTests() {
@@ -3879,6 +3948,20 @@
 		resumeSession: true,
 	})
 	testCases = append(testCases, testCase{
+		name: "SendSCTListOnResume",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendSCTListOnResume: []byte("bogus"),
+			},
+		},
+		flags: []string{
+			"-enable-signed-cert-timestamps",
+			"-expect-signed-cert-timestamps",
+			base64.StdEncoding.EncodeToString(testSCTList),
+		},
+		resumeSession: true,
+	})
+	testCases = append(testCases, testCase{
 		name:     "SignedCertificateTimestampList-Server",
 		testType: serverTest,
 		flags: []string{
@@ -4583,6 +4666,24 @@
 	60 * time.Second,
 }
 
+// shortTimeouts is an alternate set of timeouts which would occur if the
+// initial timeout duration was set to 250ms.
+var shortTimeouts = []time.Duration{
+	250 * time.Millisecond,
+	500 * time.Millisecond,
+	1 * time.Second,
+	2 * time.Second,
+	4 * time.Second,
+	8 * time.Second,
+	16 * time.Second,
+	32 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+	60 * time.Second,
+}
+
 func addDTLSRetransmitTests() {
 	// Test that this is indeed the timeout schedule. Stress all
 	// four patterns of handshake.
@@ -4659,6 +4760,31 @@
 		},
 		flags: []string{"-async"},
 	})
+
+	// Test the timeout schedule when a shorter initial timeout duration is set.
+	testCases = append(testCases, testCase{
+		protocol: dtls,
+		name:     "DTLS-Retransmit-Short-Client",
+		config: Config{
+			Bugs: ProtocolBugs{
+				TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
+			},
+		},
+		resumeSession: true,
+		flags:         []string{"-async", "-initial-timeout-duration-ms", "250"},
+	})
+	testCases = append(testCases, testCase{
+		protocol: dtls,
+		testType: serverTest,
+		name:     "DTLS-Retransmit-Short-Server",
+		config: Config{
+			Bugs: ProtocolBugs{
+				TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
+			},
+		},
+		resumeSession: true,
+		flags:         []string{"-async", "-initial-timeout-duration-ms", "250"},
+	})
 }
 
 func addExportKeyingMaterialTests() {
diff --git a/src/ssl/test/runner/runner_test.go b/src/ssl/test/runner/runner_test.go
index 320ff52..1ba28e0 100644
--- a/src/ssl/test/runner/runner_test.go
+++ b/src/ssl/test/runner/runner_test.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
 package runner
 
 import "testing"
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 67a017d..536978b 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -56,6 +56,7 @@
     &TestConfig::write_different_record_sizes },
   { "-cbc-record-splitting", &TestConfig::cbc_record_splitting },
   { "-partial-write", &TestConfig::partial_write },
+  { "-no-tls13", &TestConfig::no_tls13 },
   { "-no-tls12", &TestConfig::no_tls12 },
   { "-no-tls11", &TestConfig::no_tls11 },
   { "-no-tls1", &TestConfig::no_tls1 },
@@ -148,6 +149,7 @@
     &TestConfig::expect_server_key_exchange_hash },
   { "-expect-key-exchange-info",
     &TestConfig::expect_key_exchange_info },
+  { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
 };
 
 }  // namespace
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index fe117d8..aff194e 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -38,6 +38,7 @@
   bool write_different_record_sizes = false;
   bool cbc_record_splitting = false;
   bool partial_write = false;
+  bool no_tls13 = false;
   bool no_tls12 = false;
   bool no_tls11 = false;
   bool no_tls1 = false;
@@ -104,6 +105,7 @@
   bool use_sparse_dh_prime = false;
   int expect_key_exchange_info = 0;
   bool use_old_client_cert_callback = false;
+  int initial_timeout_duration_ms = 0;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);