external/boringssl: update from upstream

BUG=24082170

7104cc9 Update and fix fuzzing instructions.
9a4beb8 Add four, basic fuzz tests.
4ab2540 Add AArch64 Montgomery assembly.
ad38dc7 Enable Montgomery optimisations on ARM.
2e64f1b Check PKCS#8 pkey field is valid before cleansing.
f606f98 bssl pkcs12 shouldn't crash on missing key.
e348ff4 Fix build.
6e80765 Add SSL_get_server_key_exchange_hash.
788be4a Remove the hard-coded SHA-1 exception for sigalgs.
5d5e39f Remove non-ASM version of |bn_mul_mont| in bn/generic.c.
59b0fcc Define BORINGSSL_201510.
e6d1e5a Use typedef names, not struct names.
16285ea Rewrite DTLS handshake message sending logic.
c81ee8b Add missing state to DTLS state machine.
2e24b9b Allow SHA-512 unaligned data access in |OPENSSL_NO_ASM| mode.
e82e6f6 Constify more BN_MONT_CTX parameters.
c7817d8 Add SSL_CIPHER_get_min_version and tidy up SSL_TLSV1_2 logic.
9d94d5e Remove untested, unnecessary big-endian SHA-1/SHA-256 optimizations.
38feb99 Require that EC points are on the curve.
ef793f4 Add various functions for SSL_CIPHER.
f93995b Test that the client doesn't offer TLS 1.2 ciphers when it shouldn't.
5f88999 Fix up several comments and detect problems in the future.
e57a192 Add missing newline in aead.h.
c2d3280 Add SSL_get_ivs.
a97b737 Separate CCS and handshake writing in DTLS.
ac9404c Improve crypto/digest/md32_common.h mechanism.
8fb0f52 Free BN_MONT_CTX in generic code.
bb87535 Fix ASan bot.
d93831d Make it possible for a static linker to discard unused RSA functions.
e8f783a Unwind DH_METHOD and DSA_METHOD.
3fc138e Don't bother sampling __func__.
165248c Fix several MSVC warnings.
8f7ecb8 (Hopefully) fix a warning on Windows.
466b989 Initialise variable before jump.
1895493 Add Intel's P-256
27a0d08 Add ssl_renegotiate_ignore.
fa9eb56 Correct the spelling of "primitive".
f1c1cf8 Revert "Improve crypto/digest/md32_common.h mechanism."
00461cf Improve crypto/digest/md32_common.h mechanism.
ecc2591 Update link to Google style guide.
efb42fb Make BN_mod_exp_mont_consttime take a const context.
eb8be01 Add ciphers option to bssl.
09d68c9 Expand a comment.
2e0901b Don't use ssl3_write_pending in DTLS.
13e81fc Fix DTLS asynchronous write handling.
ebda9b3 Make recordingconn emit more useful things for DTLS.
069bedf Fix documentation typo.
ce51469 Fix a missing initializer that only Clang warns about.
d9e8173 Fix several warnings that arise in Android.
bb85f3d Reorganise |SSL_SESSION| and |SSL| to save a little memory.
dff504d Make the instructions for downloading the ARM compiler easier to copy and paste.

Change-Id: I5ef2238f77f2bcab239919c8c50c3705b4577f09
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index 4d550e9..a940af6 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -145,10 +145,6 @@
  * current one to buffer. */
 static const unsigned int kHandshakeBufferSize = 10;
 
-static void dtls1_fix_message_header(SSL *s, unsigned long frag_off,
-                                     unsigned long frag_len);
-static unsigned char *dtls1_write_message_header(SSL *s, unsigned char *p);
-
 static hm_fragment *dtls1_hm_fragment_new(size_t frag_len, int reassembly) {
   hm_fragment *frag = OPENSSL_malloc(sizeof(hm_fragment));
   if (frag == NULL) {
@@ -251,127 +247,165 @@
   frag->reassembly = NULL;
 }
 
-/* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or
- * SSL3_RT_CHANGE_CIPHER_SPEC) */
-int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch) {
-  int ret;
-  int curr_mtu;
-  unsigned int len, frag_off;
-
-  /* AHA!  Figure out the MTU, and stick to the right size */
-  if (s->d1->mtu < dtls1_min_mtu() &&
-      !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) {
-    long mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+static void dtls1_update_mtu(SSL *ssl) {
+  /* 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);
     if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
-      s->d1->mtu = (unsigned)mtu;
+      ssl->d1->mtu = (unsigned)mtu;
     } else {
-      s->d1->mtu = kDefaultMTU;
-      BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SET_MTU, s->d1->mtu, NULL);
+      ssl->d1->mtu = kDefaultMTU;
+      BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
     }
   }
 
-  /* should have something reasonable now */
-  assert(s->d1->mtu >= dtls1_min_mtu());
+  /* The MTU should be above the minimum now. */
+  assert(ssl->d1->mtu >= dtls1_min_mtu());
+}
 
-  if (s->init_off == 0 && type == SSL3_RT_HANDSHAKE) {
-    assert(s->init_num ==
-           (int)s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH);
+/* dtls1_max_record_size returns the maximum record body length that may be
+ * written without exceeding the MTU. It accounts for any buffering installed on
+ * the write BIO. If no record may be written, it returns zero. */
+static size_t dtls1_max_record_size(SSL *ssl) {
+  size_t ret = ssl->d1->mtu;
+
+  size_t overhead = ssl_max_seal_overhead(ssl);
+  if (ret <= overhead) {
+    return 0;
+  }
+  ret -= overhead;
+
+  size_t pending = BIO_wpending(SSL_get_wbio(ssl));
+  if (ret <= pending) {
+    return 0;
+  }
+  ret -= pending;
+
+  return ret;
+}
+
+static int dtls1_write_change_cipher_spec(SSL *ssl,
+                                          enum dtls1_use_epoch_t use_epoch) {
+  dtls1_update_mtu(ssl);
+
+  /* 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) {
+    ssl->rwstate = SSL_WRITING;
+    int ret = BIO_flush(SSL_get_wbio(ssl));
+    if (ret <= 0) {
+      return ret;
+    }
+    ssl->rwstate = SSL_NOTHING;
   }
 
-  /* Determine the maximum overhead of the current cipher. */
-  size_t max_overhead = SSL_AEAD_CTX_max_overhead(s->aead_write_ctx);
-
-  frag_off = 0;
-  while (s->init_num) {
-    /* Account for data in the buffering BIO; multiple records may be packed
-     * into a single packet during the handshake.
-     *
-     * TODO(davidben): This is buggy; if the MTU is larger than the buffer size,
-     * the large record will be split across two packets. Moreover, in that
-     * case, the |dtls1_write_bytes| call may not return synchronously. This
-     * will break on retry as the |s->init_off| and |s->init_num| adjustment
-     * will run a second time. */
-    curr_mtu = s->d1->mtu - BIO_wpending(SSL_get_wbio(s)) -
-        DTLS1_RT_HEADER_LENGTH - max_overhead;
-
-    if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) {
-      /* Flush the buffer and continue with a fresh packet.
-       *
-       * TODO(davidben): If |BIO_flush| is not synchronous and requires multiple
-       * calls to |dtls1_do_write|, |frag_off| will be wrong. */
-      ret = BIO_flush(SSL_get_wbio(s));
-      if (ret <= 0) {
-        return ret;
-      }
-      assert(BIO_wpending(SSL_get_wbio(s)) == 0);
-      curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH - max_overhead;
-    }
-
-    /* XDTLS: this function is too long.  split out the CCS part */
-    if (type == SSL3_RT_HANDSHAKE) {
-      /* If this isn't the first fragment, reserve space to prepend a new
-       * fragment header. This will override the body of a previous fragment. */
-      if (s->init_off != 0) {
-        assert(s->init_off > DTLS1_HM_HEADER_LENGTH);
-        s->init_off -= DTLS1_HM_HEADER_LENGTH;
-        s->init_num += DTLS1_HM_HEADER_LENGTH;
-      }
-
-      if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) {
-        /* To make forward progress, the MTU must, at minimum, fit the handshake
-         * header and one byte of handshake body. */
-        OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
-        return -1;
-      }
-
-      if (s->init_num > curr_mtu) {
-        len = curr_mtu;
-      } else {
-        len = s->init_num;
-      }
-      assert(len >= DTLS1_HM_HEADER_LENGTH);
-
-      dtls1_fix_message_header(s, frag_off, len - DTLS1_HM_HEADER_LENGTH);
-      dtls1_write_message_header(
-          s, (uint8_t *)&s->init_buf->data[s->init_off]);
-    } else {
-      assert(type == SSL3_RT_CHANGE_CIPHER_SPEC);
-      /* ChangeCipherSpec cannot be fragmented. */
-      if (s->init_num > curr_mtu) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
-        return -1;
-      }
-      len = s->init_num;
-    }
-
-    ret = dtls1_write_bytes(s, type, &s->init_buf->data[s->init_off], len,
-                            use_epoch);
-    if (ret < 0) {
-      return -1;
-    }
-
-    /* bad if this assert fails, only part of the handshake message got sent.
-     * But why would this happen? */
-    assert(len == (unsigned int)ret);
-
-    if (ret == s->init_num) {
-      if (s->msg_callback) {
-        s->msg_callback(1, s->version, type, s->init_buf->data,
-                        (size_t)(s->init_off + s->init_num), s,
-                        s->msg_callback_arg);
-      }
-
-      s->init_off = 0; /* done writing this message */
-      s->init_num = 0;
-
-      return 1;
-    }
-    s->init_off += ret;
-    s->init_num -= ret;
-    frag_off += (ret -= DTLS1_HM_HEADER_LENGTH);
+  static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
+  int ret = dtls1_write_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                              sizeof(kChangeCipherSpec), use_epoch);
+  if (ret <= 0) {
+    return ret;
   }
 
-  return 0;
+  if (ssl->msg_callback != NULL) {
+    ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC,
+                      kChangeCipherSpec, sizeof(kChangeCipherSpec), ssl,
+                      ssl->msg_callback_arg);
+  }
+
+  return 1;
+}
+
+int dtls1_do_handshake_write(SSL *ssl, enum dtls1_use_epoch_t use_epoch) {
+  dtls1_update_mtu(ssl);
+
+  int ret = -1;
+  CBB cbb;
+  CBB_zero(&cbb);
+  /* Allocate a temporary buffer to hold the message fragments to avoid
+   * clobbering the message. */
+  uint8_t *buf = OPENSSL_malloc(ssl->d1->mtu);
+  if (buf == NULL) {
+    goto err;
+  }
+
+  /* Consume the message header. Fragments will have different headers
+   * prepended. */
+  if (ssl->init_off == 0) {
+    ssl->init_off += DTLS1_HM_HEADER_LENGTH;
+    ssl->init_num -= DTLS1_HM_HEADER_LENGTH;
+  }
+  assert(ssl->init_off >= DTLS1_HM_HEADER_LENGTH);
+
+  do {
+    /* 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) {
+      ssl->rwstate = SSL_WRITING;
+      int flush_ret = BIO_flush(SSL_get_wbio(ssl));
+      if (flush_ret <= 0) {
+        ret = flush_ret;
+        goto err;
+      }
+      ssl->rwstate = SSL_NOTHING;
+      assert(BIO_wpending(SSL_get_wbio(ssl)) == 0);
+    }
+
+    size_t todo = dtls1_max_record_size(ssl);
+    if (todo < DTLS1_HM_HEADER_LENGTH + 1) {
+      /* To make forward progress, the MTU must, at minimum, fit the handshake
+       * header and one byte of handshake body. */
+      OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
+      goto err;
+    }
+    todo -= DTLS1_HM_HEADER_LENGTH;
+
+    if (todo > (size_t)ssl->init_num) {
+      todo = ssl->init_num;
+    }
+    if (todo >= (1u << 24)) {
+      todo = (1u << 24) - 1;
+    }
+
+    size_t len;
+    if (!CBB_init_fixed(&cbb, buf, ssl->d1->mtu) ||
+        !CBB_add_u8(&cbb, ssl->d1->w_msg_hdr.type) ||
+        !CBB_add_u24(&cbb, ssl->d1->w_msg_hdr.msg_len) ||
+        !CBB_add_u16(&cbb, ssl->d1->w_msg_hdr.seq) ||
+        !CBB_add_u24(&cbb, ssl->init_off - DTLS1_HM_HEADER_LENGTH) ||
+        !CBB_add_u24(&cbb, todo) ||
+        !CBB_add_bytes(
+            &cbb, (const uint8_t *)ssl->init_buf->data + ssl->init_off, todo) ||
+        !CBB_finish(&cbb, NULL, &len)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+
+    int write_ret = dtls1_write_bytes(ssl, SSL3_RT_HANDSHAKE, buf, len,
+                                      use_epoch);
+    if (write_ret <= 0) {
+      ret = write_ret;
+      goto err;
+    }
+    ssl->init_off += todo;
+    ssl->init_num -= todo;
+  } while (ssl->init_num > 0);
+
+  if (ssl->msg_callback != NULL) {
+    ssl->msg_callback(
+        1 /* write */, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
+        (size_t)(ssl->init_off + ssl->init_num), ssl, ssl->msg_callback_arg);
+  }
+
+  ssl->init_off = 0;
+  ssl->init_num = 0;
+
+  ret = 1;
+
+err:
+  CBB_cleanup(&cbb);
+  OPENSSL_free(buf);
+  return ret;
 }
 
 /* dtls1_is_next_message_complete returns one if the next handshake message is
@@ -644,37 +678,6 @@
   return -1;
 }
 
-/* for these 2 messages, we need to
- * ssl->enc_read_ctx			re-init
- * ssl->s3->read_sequence		zero
- * ssl->s3->read_mac_secret		re-init
- * ssl->session->read_sym_enc		assign
- * ssl->session->read_compression	assign
- * ssl->session->read_hash		assign */
-int dtls1_send_change_cipher_spec(SSL *s, int a, int b) {
-  uint8_t *p;
-
-  if (s->state == a) {
-    p = (uint8_t *)s->init_buf->data;
-    *p++ = SSL3_MT_CCS;
-    s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
-    s->init_num = DTLS1_CCS_HEADER_LENGTH;
-
-    s->init_off = 0;
-
-    dtls1_set_message_header(s, SSL3_MT_CCS, 0, s->d1->handshake_write_seq, 0,
-                             0);
-
-    /* buffer the message to handle re-xmits */
-    dtls1_buffer_message(s, 1);
-
-    s->state = b;
-  }
-
-  /* SSL3_ST_CW_CHANGE_B */
-  return dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC, dtls1_use_current_epoch);
-}
-
 int dtls1_read_failed(SSL *s, int code) {
   if (code > 0) {
     assert(0);
@@ -696,7 +699,9 @@
   return DTLSv1_handle_timeout(s);
 }
 
-int dtls1_get_queue_priority(unsigned short seq, int is_ccs) {
+static uint16_t dtls1_get_queue_priority(uint16_t seq, int is_ccs) {
+  assert(seq * 2 >= seq);
+
   /* The index of the retransmission queue actually is the message sequence
    * number, since the queue only contains messages of a single handshake.
    * However, the ChangeCipherSpec has no message sequence number and so using
@@ -709,27 +714,6 @@
 }
 
 static int dtls1_retransmit_message(SSL *s, hm_fragment *frag) {
-  int ret;
-  /* XDTLS: for now assuming that read/writes are blocking */
-  unsigned long header_length;
-
-  /* assert(s->init_num == 0);
-     assert(s->init_off == 0); */
-
-  if (frag->msg_header.is_ccs) {
-    header_length = DTLS1_CCS_HEADER_LENGTH;
-  } else {
-    header_length = DTLS1_HM_HEADER_LENGTH;
-  }
-
-  memcpy(s->init_buf->data, frag->fragment,
-         frag->msg_header.msg_len + header_length);
-  s->init_num = frag->msg_header.msg_len + header_length;
-
-  dtls1_set_message_header(s, frag->msg_header.type,
-                           frag->msg_header.msg_len, frag->msg_header.seq,
-                           0, frag->msg_header.frag_len);
-
   /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
    * (negotiated cipher) exist. */
   assert(s->d1->w_epoch == 0 || s->d1->w_epoch == 1);
@@ -739,10 +723,24 @@
     use_epoch = dtls1_use_previous_epoch;
   }
 
-  ret = dtls1_do_write(s, frag->msg_header.is_ccs ? SSL3_RT_CHANGE_CIPHER_SPEC
-                                                  : SSL3_RT_HANDSHAKE,
-                       use_epoch);
+  /* TODO(davidben): This cannot handle non-blocking writes. */
+  int ret;
+  if (frag->msg_header.is_ccs) {
+    ret = dtls1_write_change_cipher_spec(s, use_epoch);
+  } else {
+    /* Restore the message body.
+     * TODO(davidben): Make this less stateful. */
+    memcpy(s->init_buf->data, frag->fragment,
+           frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
+    s->init_num = frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH;
 
+    dtls1_set_message_header(s, frag->msg_header.type,
+                             frag->msg_header.msg_len, frag->msg_header.seq,
+                             0, frag->msg_header.frag_len);
+    ret = dtls1_do_handshake_write(s, use_epoch);
+  }
+
+  /* TODO(davidben): Check return value? */
   (void)BIO_flush(SSL_get_wbio(s));
   return ret;
 }
@@ -763,46 +761,65 @@
   return 1;
 }
 
-int dtls1_buffer_message(SSL *s, int is_ccs) {
-  pitem *item;
-  hm_fragment *frag;
-  uint8_t seq64be[8];
+/* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current
+ * handshake flight, ordered just before the handshake message numbered
+ * |seq|. */
+static int dtls1_buffer_change_cipher_spec(SSL *ssl, uint16_t seq) {
+  hm_fragment *frag = dtls1_hm_fragment_new(0 /* frag_len */,
+                                            0 /* no reassembly */);
+  if (frag == NULL) {
+    return 0;
+  }
+  frag->msg_header.is_ccs = 1;
+  frag->msg_header.epoch = ssl->d1->w_epoch;
 
+  uint16_t priority = dtls1_get_queue_priority(seq, 1 /* is_ccs */);
+  uint8_t seq64be[8];
+  memset(seq64be, 0, sizeof(seq64be));
+  seq64be[6] = (uint8_t)(priority >> 8);
+  seq64be[7] = (uint8_t)priority;
+
+  pitem *item = pitem_new(seq64be, frag);
+  if (item == NULL) {
+    dtls1_hm_fragment_free(frag);
+    return 0;
+  }
+
+  pqueue_insert(ssl->d1->sent_messages, item);
+  return 1;
+}
+
+int dtls1_buffer_message(SSL *s) {
   /* this function is called immediately after a message has
    * been serialized */
   assert(s->init_off == 0);
 
-  frag = dtls1_hm_fragment_new(s->init_num, 0);
+  hm_fragment *frag = dtls1_hm_fragment_new(s->init_num, 0);
   if (!frag) {
     return 0;
   }
 
   memcpy(frag->fragment, s->init_buf->data, s->init_num);
 
-  if (is_ccs) {
-    assert(s->d1->w_msg_hdr.msg_len + DTLS1_CCS_HEADER_LENGTH ==
-           (unsigned int)s->init_num);
-  } else {
-    assert(s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH ==
-           (unsigned int)s->init_num);
-  }
+  assert(s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH ==
+         (unsigned int)s->init_num);
 
   frag->msg_header.msg_len = s->d1->w_msg_hdr.msg_len;
   frag->msg_header.seq = s->d1->w_msg_hdr.seq;
   frag->msg_header.type = s->d1->w_msg_hdr.type;
   frag->msg_header.frag_off = 0;
   frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len;
-  frag->msg_header.is_ccs = is_ccs;
+  frag->msg_header.is_ccs = 0;
   frag->msg_header.epoch = s->d1->w_epoch;
 
+  uint16_t priority = dtls1_get_queue_priority(frag->msg_header.seq,
+                                               0 /* handshake */);
+  uint8_t seq64be[8];
   memset(seq64be, 0, sizeof(seq64be));
-  seq64be[6] = (uint8_t)(
-      dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs) >>
-      8);
-  seq64be[7] = (uint8_t)(
-      dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs));
+  seq64be[6] = (uint8_t)(priority >> 8);
+  seq64be[7] = (uint8_t)priority;
 
-  item = pitem_new(seq64be, frag);
+  pitem *item = pitem_new(seq64be, frag);
   if (item == NULL) {
     dtls1_hm_fragment_free(frag);
     return 0;
@@ -812,6 +829,17 @@
   return 1;
 }
 
+int dtls1_send_change_cipher_spec(SSL *s, int a, int b) {
+  if (s->state == a) {
+    /* Buffer the message to handle retransmits. */
+    s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
+    dtls1_buffer_change_cipher_spec(s, s->d1->handshake_write_seq);
+    s->state = b;
+  }
+
+  return dtls1_write_change_cipher_spec(s, dtls1_use_current_epoch);
+}
+
 /* call this function when the buffered messages are no longer needed */
 void dtls1_clear_record_buffer(SSL *s) {
   pitem *item;
@@ -836,27 +864,6 @@
   msg_hdr->frag_len = frag_len;
 }
 
-static void dtls1_fix_message_header(SSL *s, unsigned long frag_off,
-                                     unsigned long frag_len) {
-  struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr;
-
-  msg_hdr->frag_off = frag_off;
-  msg_hdr->frag_len = frag_len;
-}
-
-static uint8_t *dtls1_write_message_header(SSL *s, uint8_t *p) {
-  struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr;
-
-  *p++ = msg_hdr->type;
-  l2n3(msg_hdr->msg_len, p);
-
-  s2n(msg_hdr->seq, p);
-  l2n3(msg_hdr->frag_off, p);
-  l2n3(msg_hdr->frag_len, p);
-
-  return p;
-}
-
 unsigned int dtls1_min_mtu(void) {
   return kMinMTU;
 }
diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c
index 3dd5f8c..b86655c 100644
--- a/src/ssl/d1_clnt.c
+++ b/src/ssl/d1_clnt.c
@@ -190,13 +190,6 @@
       case SSL3_ST_CW_CLNT_HELLO_A:
       case SSL3_ST_CW_CLNT_HELLO_B:
         s->shutdown = 0;
-
-        if (!ssl3_init_handshake_buffer(s)) {
-          OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-          ret = -1;
-          goto end;
-        }
-
         dtls1_start_timer(s);
         ret = ssl3_send_client_hello(s);
         if (ret <= 0) {
@@ -352,6 +345,7 @@
 
       case SSL3_ST_CW_CERT_VRFY_A:
       case SSL3_ST_CW_CERT_VRFY_B:
+      case SSL3_ST_CW_CERT_VRFY_C:
         dtls1_start_timer(s);
         ret = ssl3_send_cert_verify(s);
         if (ret <= 0) {
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index cb95585..787ad9a 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -321,7 +321,7 @@
   s->init_off = 0;
 
   /* Buffer the message to handle re-xmits */
-  dtls1_buffer_message(s, 0);
+  dtls1_buffer_message(s);
 
   /* Add the new message to the handshake hash. Serialize the message
    * header as if it were a single fragment. */
@@ -336,5 +336,5 @@
 }
 
 int dtls1_handshake_write(SSL *s) {
-  return dtls1_do_write(s, SSL3_RT_HANDSHAKE, dtls1_use_current_epoch);
+  return dtls1_do_handshake_write(s, dtls1_use_current_epoch);
 }
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index a31dcc3..8cabdd4 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -512,8 +512,9 @@
 
 static int do_dtls1_write(SSL *s, int type, const uint8_t *buf,
                           unsigned int len, enum dtls1_use_epoch_t use_epoch) {
-  /* ssl3_write_pending drops the write if |BIO_write| fails in DTLS, so there
-   * is never pending data. */
+  /* There should never be a pending write buffer in DTLS. One can't write half
+   * a datagram, so the write buffer is always dropped in
+   * |ssl_write_buffer_flush|. */
   assert(!ssl_write_buffer_is_pending(s));
 
   /* If we have an alert to send, lets send it */
@@ -540,19 +541,16 @@
   if (!ssl_write_buffer_init(s, &out, max_out) ||
       !dtls_seal_record(s, out, &ciphertext_len, max_out, type, buf, len,
                         use_epoch)) {
+    ssl_write_buffer_clear(s);
     return -1;
   }
   ssl_write_buffer_set_len(s, ciphertext_len);
 
-  /* memorize arguments so that ssl3_write_pending can detect bad write retries
-   * later */
-  s->s3->wpend_tot = len;
-  s->s3->wpend_buf = buf;
-  s->s3->wpend_type = type;
-  s->s3->wpend_ret = len;
-
-  /* we now just need to write the buffer */
-  return ssl3_write_pending(s, type, buf, len);
+  int ret = ssl_write_buffer_flush(s);
+  if (ret <= 0) {
+    return ret;
+  }
+  return (int)len;
 }
 
 int dtls1_dispatch_alert(SSL *s) {
diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c
index 628bb29..115bd70 100644
--- a/src/ssl/d1_srtp.c
+++ b/src/ssl/d1_srtp.c
@@ -133,7 +133,7 @@
     {
         "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32,
     },
-    {0},
+    {0, 0},
 };
 
 static int find_profile_by_name(const char *profile_name,
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 76a31bf..520131e 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -695,15 +695,6 @@
 /* See if we use signature algorithms extension and signature algorithm before
  * signatures. */
 #define SSL_USE_SIGALGS(s) (s->enc_method->enc_flags & SSL_ENC_FLAG_SIGALGS)
-/* Allow TLS 1.2 ciphersuites: applies to DTLS 1.2 as well as TLS 1.2: may
- * apply to others in future. */
-#define SSL_USE_TLS1_2_CIPHERS(s) \
-  (s->enc_method->enc_flags & SSL_ENC_FLAG_TLS1_2_CIPHERS)
-/* Determine if a client can use TLS 1.2 ciphersuites: can't rely on method
- * flags because it may not be set to correct version yet. */
-#define SSL_CLIENT_USE_TLS1_2_CIPHERS(s)                       \
-  ((SSL_IS_DTLS(s) && s->client_version <= DTLS1_2_VERSION) || \
-   (!SSL_IS_DTLS(s) && s->client_version >= TLS1_2_VERSION))
 
 /* SSL_kRSA <- RSA_ENC | (RSA_TMP & RSA_SIGN) |
  * 	    <- (EXPORT & (RSA_ENC | RSA_TMP) & RSA_SIGN)
@@ -739,17 +730,12 @@
   const SSL_PRIVATE_KEY_METHOD *key_method;
 
   /* For clients the following masks are of *disabled* key and auth algorithms
-   * based on the current session.
+   * based on the current configuration.
    *
    * TODO(davidben): Remove these. They get checked twice: when sending the
-   * ClientHello and when processing the ServerHello. However, mask_ssl is a
-   * different value both times. mask_k and mask_a are not, but is a
-   * round-about way of checking the server's cipher was one of the advertised
-   * ones. (Currently it checks the masks and then the list of ciphers prior to
-   * applying the masks in ClientHello.) */
+   * ClientHello and when processing the ServerHello. */
   uint32_t mask_k;
   uint32_t mask_a;
-  uint32_t mask_ssl;
 
   DH *dh_tmp;
   DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
@@ -857,9 +843,6 @@
 #define SSL_ENC_FLAG_SIGALGS 0x2
 /* Uses SHA256 default PRF */
 #define SSL_ENC_FLAG_SHA256_PRF 0x4
-/* Allow TLS 1.2 ciphersuites: applies to DTLS 1.2 as well as TLS 1.2:
- * may apply to others in future. */
-#define SSL_ENC_FLAG_TLS1_2_CIPHERS 0x8
 
 /* lengths of messages */
 #define DTLS1_COOKIE_LENGTH 256
@@ -912,7 +895,9 @@
   /* records being received in the current epoch */
   DTLS1_BITMAP bitmap;
 
-  /* handshake message numbers */
+  /* handshake message numbers.
+   * TODO(davidben): It doesn't make much sense to store both of these. Only
+   * store one. */
   uint16_t handshake_write_seq;
   uint16_t next_handshake_write_seq;
 
@@ -1075,11 +1060,10 @@
 int ssl3_set_handshake_header(SSL *s, int htype, unsigned long len);
 int ssl3_handshake_write(SSL *s);
 
-int dtls1_do_write(SSL *s, int type, enum dtls1_use_epoch_t use_epoch);
+int dtls1_do_handshake_write(SSL *s, enum dtls1_use_epoch_t use_epoch);
 int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek);
 void dtls1_read_close_notify(SSL *ssl);
 int dtls1_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek);
-int ssl3_write_pending(SSL *s, int type, const uint8_t *buf, unsigned int len);
 void dtls1_set_message_header(SSL *s, uint8_t mt, unsigned long len,
                               unsigned short seq_num, unsigned long frag_off,
                               unsigned long frag_len);
@@ -1091,8 +1075,7 @@
 int dtls1_send_change_cipher_spec(SSL *s, int a, int b);
 int dtls1_send_finished(SSL *s, int a, int b, const char *sender, int slen);
 int dtls1_read_failed(SSL *s, int code);
-int dtls1_buffer_message(SSL *s, int ccs);
-int dtls1_get_queue_priority(unsigned short seq, int is_ccs);
+int dtls1_buffer_message(SSL *s);
 int dtls1_retransmit_buffered_messages(SSL *s);
 void dtls1_clear_record_buffer(SSL *s);
 void dtls1_get_message_header(uint8_t *data, struct hm_header_st *msg_hdr);
@@ -1311,8 +1294,13 @@
 const EVP_MD *tls1_choose_signing_digest(SSL *ssl);
 
 size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs);
-int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s,
-                            CBS *cbs, EVP_PKEY *pkey);
+
+/* tls12_check_peer_sigalg checks that |hash| and |signature| are consistent
+ * with |pkey| and |ssl|'s sent, supported signature algorithms and, if so,
+ * writes the relevant digest into |*out_md| and returns 1. Otherwise it
+ * returns 0 and writes an alert into |*out_alert|. */
+int tls12_check_peer_sigalg(SSL *ssl, const EVP_MD **out_md, int *out_alert,
+                            uint8_t hash, uint8_t signature, EVP_PKEY *pkey);
 void ssl_set_client_disabled(SSL *s);
 
 #endif /* OPENSSL_HEADER_SSL_INTERNAL_H */
diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c
index 13bc0e8..843403b 100644
--- a/src/ssl/s3_clnt.c
+++ b/src/ssl/s3_clnt.c
@@ -604,9 +604,12 @@
   for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
     const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
     /* Skip disabled ciphers */
-    if (cipher->algorithm_ssl & ssl->cert->mask_ssl ||
-        cipher->algorithm_mkey & ssl->cert->mask_k ||
-        cipher->algorithm_auth & ssl->cert->mask_a) {
+    if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
+        (cipher->algorithm_auth & ssl->cert->mask_a)) {
+      continue;
+    }
+    if (SSL_CIPHER_get_min_version(cipher) >
+        ssl3_version_from_wire(ssl, ssl->client_version)) {
       continue;
     }
     any_enabled = 1;
@@ -645,6 +648,13 @@
     return ssl_do_write(ssl);
   }
 
+  /* In DTLS, reset the handshake buffer each time a new ClientHello is
+   * assembled. We may send multiple if we receive HelloVerifyRequest. */
+  if (SSL_IS_DTLS(ssl) && !ssl3_init_handshake_buffer(ssl)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
+  }
+
   CBB cbb;
   CBB_zero(&cbb);
 
@@ -734,7 +744,6 @@
   CBS server_hello, server_random, session_id;
   uint16_t server_version, cipher_suite;
   uint8_t compression_method;
-  uint32_t mask_ssl;
 
   n = s->method->ssl_get_message(s, SSL3_ST_CR_SRVR_HELLO_A,
                                  SSL3_ST_CR_SRVR_HELLO_B, SSL3_MT_SERVER_HELLO,
@@ -827,18 +836,11 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
     goto f_err;
   }
-  /* ct->mask_ssl was computed from client capabilities. Now
-   * that the final version is known, compute a new mask_ssl. */
-  if (!SSL_USE_TLS1_2_CIPHERS(s)) {
-    mask_ssl = SSL_TLSV1_2;
-  } else {
-    mask_ssl = 0;
-  }
   /* If the cipher is disabled then we didn't sent it in the ClientHello, so if
    * the server selected it, it's an error. */
-  if ((c->algorithm_ssl & mask_ssl) ||
-      (c->algorithm_mkey & ct->mask_k) ||
-      (c->algorithm_auth & ct->mask_a)) {
+  if ((c->algorithm_mkey & ct->mask_k) ||
+      (c->algorithm_auth & ct->mask_a) ||
+      SSL_CIPHER_get_min_version(c) > ssl3_version_from_wire(s, s->version)) {
     al = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     goto f_err;
@@ -1242,9 +1244,17 @@
     }
 
     if (SSL_USE_SIGALGS(s)) {
-      if (!tls12_check_peer_sigalg(&md, &al, s, &server_key_exchange, pkey)) {
+      uint8_t hash, signature;
+      if (!CBS_get_u8(&server_key_exchange, &hash) ||
+          !CBS_get_u8(&server_key_exchange, &signature)) {
+        al = SSL_AD_DECODE_ERROR;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
         goto f_err;
       }
+      if (!tls12_check_peer_sigalg(s, &md, &al, hash, signature, pkey)) {
+        goto f_err;
+      }
+      s->s3->tmp.server_key_exchange_hash = hash;
     } else if (pkey->type == EVP_PKEY_RSA) {
       md = EVP_md5_sha1();
     } else {
@@ -1449,7 +1459,9 @@
 
   if (CBS_len(&ticket) == 0) {
     /* RFC 5077 allows a server to change its mind and send no ticket after
-     * negotiating the extension. Behave as if no ticket was sent. */
+     * negotiating the extension. The value of |tlsext_ticket_expected| is
+     * checked in |ssl_update_cache| so is cleared here to avoid an unnecessary
+     * update. */
     s->tlsext_ticket_expected = 0;
     return 1;
   }
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 5209802..7bf223d 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -503,8 +503,8 @@
 
     ok = 1;
 
-    /* Skip TLS v1.2 only ciphersuites if not supported */
-    if ((c->algorithm_ssl & SSL_TLSV1_2) && !SSL_USE_TLS1_2_CIPHERS(s)) {
+    /* Check the TLS version. */
+    if (SSL_CIPHER_get_min_version(c) > ssl3_version_from_wire(s, s->version)) {
       ok = 0;
     }
 
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 1cb2ac6..7416d0e 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -249,6 +249,23 @@
   }
 }
 
+static int ssl3_write_pending(SSL *s, int type, const uint8_t *buf,
+                              unsigned int len) {
+  if (s->s3->wpend_tot > (int)len ||
+      (s->s3->wpend_buf != buf &&
+       !(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) ||
+      s->s3->wpend_type != type) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
+    return -1;
+  }
+
+  int ret = ssl_write_buffer_flush(s);
+  if (ret <= 0) {
+    return ret;
+  }
+  return s->s3->wpend_ret;
+}
+
 /* do_ssl3_write writes an SSL record of the given type. */
 static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned len) {
   /* If there is still data from the previous record, flush it. */
@@ -298,22 +315,6 @@
   return ssl3_write_pending(s, type, buf, len);
 }
 
-int ssl3_write_pending(SSL *s, int type, const uint8_t *buf, unsigned int len) {
-  if (s->s3->wpend_tot > (int)len ||
-      (s->s3->wpend_buf != buf &&
-       !(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) ||
-      s->s3->wpend_type != type) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
-    return -1;
-  }
-
-  int ret = ssl_write_buffer_flush(s);
-  if (ret <= 0) {
-    return ret;
-  }
-  return s->s3->wpend_ret;
-}
-
 /* ssl3_expect_change_cipher_spec informs the record layer that a
  * ChangeCipherSpec record is required at this point. If a Handshake record is
  * received before ChangeCipherSpec, the connection will fail. Moreover, if
@@ -345,6 +346,8 @@
       return ssl->s3->total_renegotiations == 0;
     case ssl_renegotiate_freely:
       return 1;
+    case ssl_renegotiate_ignore:
+      return 1;
   }
 
   assert(0);
@@ -566,6 +569,10 @@
       goto err;
     }
 
+    if (s->renegotiate_mode == ssl_renegotiate_ignore) {
+      goto start;
+    }
+
     /* Renegotiation is only supported at quiescent points in the application
      * protocol, namely in HTTPS, just before reading the HTTP response. Require
      * the record-layer be idle and avoid complexities of sending a handshake
diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c
index fad2d0a..8cfa0e6 100644
--- a/src/ssl/s3_srvr.c
+++ b/src/ssl/s3_srvr.c
@@ -2056,9 +2056,17 @@
   CBS_init(&certificate_verify, s->init_msg, n);
 
   /* Determine the digest type if needbe. */
-  if (SSL_USE_SIGALGS(s) &&
-      !tls12_check_peer_sigalg(&md, &al, s, &certificate_verify, pkey)) {
-    goto f_err;
+  if (SSL_USE_SIGALGS(s)) {
+    uint8_t hash, signature_type;
+    if (!CBS_get_u8(&certificate_verify, &hash) ||
+        !CBS_get_u8(&certificate_verify, &signature_type)) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      goto f_err;
+    }
+    if (!tls12_check_peer_sigalg(s, &md, &al, hash, signature_type, pkey)) {
+      goto f_err;
+    }
   }
 
   /* Compute the digest. */
diff --git a/src/ssl/ssl_buffer.c b/src/ssl/ssl_buffer.c
index 63dcd80..f1abc53 100644
--- a/src/ssl/ssl_buffer.c
+++ b/src/ssl/ssl_buffer.c
@@ -292,11 +292,18 @@
     return 1;
   }
 
+  ssl->rwstate = SSL_WRITING;
   int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
-  /* Drop the write buffer whether or not the write succeeded synchronously.
-   * TODO(davidben): How does this interact with the retry flag? */
+  if (ret <= 0) {
+    /* If the write failed, drop the write buffer anyway. Datagram transports
+     * can't write half a packet, so the caller is expected to retry from the
+     * top. */
+    ssl_write_buffer_clear(ssl);
+    return ret;
+  }
+  ssl->rwstate = SSL_NOTHING;
   ssl_write_buffer_clear(ssl);
-  return (ret <= 0) ? ret : 1;
+  return 1;
 }
 
 int ssl_write_buffer_flush(SSL *ssl) {
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 0ffeb5b..e87835f 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -1384,10 +1384,18 @@
   return (cipher->algorithm_mac & SSL_MD5) != 0;
 }
 
+int SSL_CIPHER_has_SHA1_HMAC(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mac & SSL_SHA1) != 0;
+}
+
 int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher) {
   return (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) != 0;
 }
 
+int SSL_CIPHER_is_AES128GCM(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_enc & SSL_AES128GCM) != 0;
+}
+
 int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher) {
   return (cipher->algorithm_enc & SSL_CHACHA20POLY1305_OLD) != 0;
 }
@@ -1406,6 +1414,17 @@
       cipher->algorithm_mac != SSL_AEAD;
 }
 
+int SSL_CIPHER_is_ECDSA(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_auth & SSL_aECDSA) != 0;
+}
+
+uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher) {
+  if (cipher->algorithm_ssl & SSL_TLSV1_2) {
+    return TLS1_2_VERSION;
+  }
+  return SSL3_VERSION;
+}
+
 /* return the actual cipher being used */
 const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher) {
   if (cipher != NULL) {
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 5fc2f66..c78a91a 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -1986,14 +1986,16 @@
 }
 
 void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) {
-  ctx->quiet_shutdown = mode;
+  ctx->quiet_shutdown = (mode != 0);
 }
 
 int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx) {
   return ctx->quiet_shutdown;
 }
 
-void SSL_set_quiet_shutdown(SSL *ssl, int mode) { ssl->quiet_shutdown = mode; }
+void SSL_set_quiet_shutdown(SSL *ssl, int mode) {
+  ssl->quiet_shutdown = (mode != 0);
+}
 
 int SSL_get_quiet_shutdown(const SSL *ssl) { return ssl->quiet_shutdown; }
 
@@ -2634,6 +2636,27 @@
          EVP_AEAD_CTX_get_rc4_state(&ssl->aead_write_ctx->ctx, write_key);
 }
 
+int SSL_get_ivs(const SSL *ssl, const uint8_t **out_read_iv,
+                const uint8_t **out_write_iv, size_t *out_iv_len) {
+  if (ssl->aead_read_ctx == NULL || ssl->aead_write_ctx == NULL) {
+    return 0;
+  }
+
+  size_t write_iv_len;
+  if (!EVP_AEAD_CTX_get_iv(&ssl->aead_read_ctx->ctx, out_read_iv, out_iv_len) ||
+      !EVP_AEAD_CTX_get_iv(&ssl->aead_write_ctx->ctx, out_write_iv,
+                           &write_iv_len) ||
+      *out_iv_len != write_iv_len) {
+    return 0;
+  }
+
+  return 1;
+}
+
+uint8_t SSL_get_server_key_exchange_hash(const SSL *ssl) {
+  return ssl->s3->tmp.server_key_exchange_hash;
+}
+
 int SSL_clear(SSL *ssl) {
   if (ssl->method == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_METHOD_SPECIFIED);
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index a6c48f1..9a29028 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -169,8 +169,7 @@
     TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
     tls1_alert_code,
     tls1_export_keying_material,
-    SSL_ENC_FLAG_EXPLICIT_IV|SSL_ENC_FLAG_SIGALGS|SSL_ENC_FLAG_SHA256_PRF
-            |SSL_ENC_FLAG_TLS1_2_CIPHERS,
+    SSL_ENC_FLAG_EXPLICIT_IV|SSL_ENC_FLAG_SIGALGS|SSL_ENC_FLAG_SHA256_PRF,
 };
 
 static int compare_uint16_t(const void *p1, const void *p2) {
@@ -641,16 +640,11 @@
   return sizeof(tls12_sigalgs);
 }
 
-/* tls12_check_peer_sigalg parses a SignatureAndHashAlgorithm out of |cbs|. It
- * checks it is consistent with |s|'s sent supported signature algorithms and,
- * if so, writes the relevant digest into |*out_md| and returns 1. Otherwise it
- * returns 0 and writes an alert into |*out_alert|. */
-int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s,
-                            CBS *cbs, EVP_PKEY *pkey) {
+int tls12_check_peer_sigalg(SSL *ssl, const EVP_MD **out_md, int *out_alert,
+                            uint8_t hash, uint8_t signature, EVP_PKEY *pkey) {
   const uint8_t *sent_sigs;
   size_t sent_sigslen, i;
   int sigalg = tls12_get_sigid(pkey->type);
-  uint8_t hash, signature;
 
   /* Should never happen */
   if (sigalg == -1) {
@@ -659,13 +653,6 @@
     return 0;
   }
 
-  if (!CBS_get_u8(cbs, &hash) ||
-      !CBS_get_u8(cbs, &signature)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
-  }
-
   /* Check key type is consistent with signature */
   if (sigalg != signature) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE);
@@ -682,8 +669,8 @@
       return 0;
     }
 
-    if (s->server && (!tls1_check_curve_id(s, curve_id) ||
-                      comp_id != TLSEXT_ECPOINTFORMAT_uncompressed)) {
+    if (ssl->server && (!tls1_check_curve_id(ssl, curve_id) ||
+                        comp_id != TLSEXT_ECPOINTFORMAT_uncompressed)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
       return 0;
@@ -691,15 +678,14 @@
   }
 
   /* Check signature matches a type we sent */
-  sent_sigslen = tls12_get_psigalgs(s, &sent_sigs);
+  sent_sigslen = tls12_get_psigalgs(ssl, &sent_sigs);
   for (i = 0; i < sent_sigslen; i += 2, sent_sigs += 2) {
     if (hash == sent_sigs[0] && signature == sent_sigs[1]) {
       break;
     }
   }
 
-  /* Allow fallback to SHA-1. */
-  if (i == sent_sigslen && hash != TLSEXT_hash_sha1) {
+  if (i == sent_sigslen) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE);
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     return 0;
@@ -727,13 +713,6 @@
   c->mask_a = 0;
   c->mask_k = 0;
 
-  /* Don't allow TLS 1.2 only ciphers if we don't suppport them */
-  if (!SSL_CLIENT_USE_TLS1_2_CIPHERS(s)) {
-    c->mask_ssl = SSL_TLSV1_2;
-  } else {
-    c->mask_ssl = 0;
-  }
-
   /* Now go through all signature algorithms seeing if we support any for RSA,
    * DSA, ECDSA. Do this for all versions not just TLS 1.2. */
   sigalgslen = tls12_get_psigalgs(s, &sigalgs);
diff --git a/src/ssl/test/async_bio.cc b/src/ssl/test/async_bio.cc
index 0534845..7a5737b 100644
--- a/src/ssl/test/async_bio.cc
+++ b/src/ssl/test/async_bio.cc
@@ -26,6 +26,7 @@
 
 struct AsyncBio {
   bool datagram;
+  bool enforce_write_quota;
   size_t read_quota;
   size_t write_quota;
 };
@@ -43,9 +44,7 @@
     return 0;
   }
 
-  if (a->datagram) {
-    // Perform writes synchronously; the DTLS implementation drops any packets
-    // that failed to send.
+  if (!a->enforce_write_quota) {
     return BIO_write(bio->next_bio, in, inl);
   }
 
@@ -111,6 +110,7 @@
     return 0;
   }
   memset(a, 0, sizeof(*a));
+  a->enforce_write_quota = true;
   bio->init = 1;
   bio->ptr = (char *)a;
   return 1;
@@ -178,3 +178,11 @@
   }
   a->write_quota += count;
 }
+
+void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) {
+  AsyncBio *a = GetData(bio);
+  if (a == NULL) {
+    return;
+  }
+  a->enforce_write_quota = enforce;
+}
diff --git a/src/ssl/test/async_bio.h b/src/ssl/test/async_bio.h
index 1ccdf9b..fbc4016 100644
--- a/src/ssl/test/async_bio.h
+++ b/src/ssl/test/async_bio.h
@@ -38,5 +38,8 @@
 // AsyncBioAllowWrite increments |bio|'s write quota by |count|.
 void AsyncBioAllowWrite(BIO *bio, size_t count);
 
+// AsyncBioEnforceWriteQuota configures where |bio| enforces its write quota.
+void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce);
+
 
 #endif  // HEADER_ASYNC_BIO
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 3f4e9cf..07ba9f5 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -808,6 +808,7 @@
     return false;
   }
 
+  const TestConfig *config = GetConfigPtr(ssl);
   TestState *test_state = GetTestState(ssl);
   if (test_state->clock_delta.tv_usec != 0 ||
       test_state->clock_delta.tv_sec != 0) {
@@ -818,7 +819,17 @@
     test_state->clock.tv_sec += test_state->clock_delta.tv_sec;
     memset(&test_state->clock_delta, 0, sizeof(test_state->clock_delta));
 
-    if (DTLSv1_handle_timeout(ssl) < 0) {
+    // The DTLS retransmit logic silently ignores write failures. So the test
+    // may progress, allow writes through synchronously.
+    if (config->async) {
+      AsyncBioEnforceWriteQuota(test_state->async_bio, false);
+    }
+    int timeout_ret = DTLSv1_handle_timeout(ssl);
+    if (config->async) {
+      AsyncBioEnforceWriteQuota(test_state->async_bio, true);
+    }
+
+    if (timeout_ret < 0) {
       fprintf(stderr, "Error retransmitting.\n");
       return false;
     }
@@ -863,9 +874,19 @@
 // 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);
+  TestState *test_state = GetTestState(ssl);
   int ret;
   do {
+    if (config->async) {
+      // The DTLS retransmit logic silently ignores write failures. So the test
+      // may progress, allow writes through synchronously. |SSL_read| may
+      // trigger a retransmit, so disconnect the write quota.
+      AsyncBioEnforceWriteQuota(test_state->async_bio, false);
+    }
     ret = SSL_read(ssl, out, max_out);
+    if (config->async) {
+      AsyncBioEnforceWriteQuota(test_state->async_bio, true);
+    }
   } while (config->async && RetryAsync(ssl, ret));
   return ret;
 }
@@ -1040,6 +1061,15 @@
     }
   }
 
+  if (config->expect_server_key_exchange_hash != 0 &&
+      config->expect_server_key_exchange_hash !=
+          SSL_get_server_key_exchange_hash(ssl)) {
+    fprintf(stderr, "ServerKeyExchange hash was %d, wanted %d.\n",
+            SSL_get_server_key_exchange_hash(ssl),
+            config->expect_server_key_exchange_hash);
+    return false;
+  }
+
   if (!config->is_server) {
     /* Clients should expect a peer certificate chain iff this was not a PSK
      * cipher suite. */
@@ -1184,6 +1214,9 @@
   if (config->renegotiate_freely) {
     SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely);
   }
+  if (config->renegotiate_ignore) {
+    SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore);
+  }
   if (!config->check_close_notify) {
     SSL_set_quiet_shutdown(ssl.get(), 1);
   }
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 7defec1..078c227 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -782,6 +782,11 @@
 	// connections where the client offers a non-empty session ID or session
 	// ticket.
 	FailIfSessionOffered bool
+
+	// SendHelloRequestBeforeEveryAppDataRecord, if true, causes a
+	// HelloRequest handshake message to be sent before each application
+	// data record. This only makes sense for a server.
+	SendHelloRequestBeforeEveryAppDataRecord bool
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 986e2b5..ab9e233 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -1078,14 +1078,16 @@
 		seq := packet[5:11]
 		length := uint16(packet[11])<<8 | uint16(packet[12])
 		if bytes.Equal(c.in.seq[:2], epoch) {
-			if !bytes.Equal(c.in.seq[2:], seq) {
+			if bytes.Compare(seq, c.in.seq[2:]) < 0 {
 				return errors.New("tls: sequence mismatch")
 			}
+			copy(c.in.seq[2:], seq)
 			c.in.incSeq(false)
 		} else {
-			if !bytes.Equal(c.in.nextSeq[:], seq) {
+			if bytes.Compare(seq, c.in.nextSeq[:]) < 0 {
 				return errors.New("tls: sequence mismatch")
 			}
+			copy(c.in.nextSeq[:], seq)
 			c.in.incNextSeq()
 		}
 		if len(packet) < 13+int(length) {
@@ -1150,6 +1152,10 @@
 		c.sendAlertLocked(alertLevelError, c.config.Bugs.SendSpuriousAlert)
 	}
 
+	if c.config.Bugs.SendHelloRequestBeforeEveryAppDataRecord {
+		c.writeRecord(recordTypeHandshake, []byte{typeHelloRequest, 0, 0, 0})
+	}
+
 	// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
 	// attack when using block mode ciphers due to predictable IVs.
 	// This can be prevented by splitting each Application Data
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index fac035e..c3ee521 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -80,15 +80,21 @@
 			return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, expect))
 		}
 	}
-	seq := b.data[3:11]
-	// For test purposes, we assume a reliable channel. Require
-	// that the explicit sequence number matches the incrementing
-	// one we maintain. A real implementation would maintain a
-	// replay window and such.
-	if !bytes.Equal(seq, c.in.seq[:]) {
+	epoch := b.data[3:5]
+	seq := b.data[5:11]
+	// For test purposes, require the sequence number be monotonically
+	// increasing, so c.in includes the minimum next sequence number. Gaps
+	// may occur if packets failed to be sent out. A real implementation
+	// would maintain a replay window and such.
+	if !bytes.Equal(epoch, c.in.seq[:2]) {
+		c.sendAlert(alertIllegalParameter)
+		return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad epoch"))
+	}
+	if bytes.Compare(seq, c.in.seq[2:]) < 0 {
 		c.sendAlert(alertIllegalParameter)
 		return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number"))
 	}
+	copy(c.in.seq[2:], seq)
 	n := int(b.data[11])<<8 | int(b.data[12])
 	if n > maxCiphertext || len(b.data) < recordHeaderLen+n {
 		c.sendAlert(alertRecordOverflow)
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 9647715..568f836 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -203,6 +203,15 @@
 		hs.clientHello.signatureAndHashes = config.signatureAndHashesForServer()
 	}
 
+	// Check the client cipher list is consistent with the version.
+	if hs.clientHello.vers < VersionTLS12 {
+		for _, id := range hs.clientHello.cipherSuites {
+			if isTLS12Cipher(id) {
+				return false, fmt.Errorf("tls: client offered TLS 1.2 cipher before TLS 1.2")
+			}
+		}
+	}
+
 	c.vers, ok = config.mutualVersion(hs.clientHello.vers)
 	if !ok {
 		c.sendAlert(alertProtocolVersion)
@@ -1053,3 +1062,14 @@
 
 	return nil
 }
+
+func isTLS12Cipher(id uint16) bool {
+	for _, cipher := range cipherSuites {
+		if cipher.id != id {
+			continue
+		}
+		return cipher.flags&suiteTLS12 != 0
+	}
+	// Unknown cipher.
+	return false
+}
diff --git a/src/ssl/test/runner/packet_adapter.go b/src/ssl/test/runner/packet_adapter.go
index b432659..2351eb0 100644
--- a/src/ssl/test/runner/packet_adapter.go
+++ b/src/ssl/test/runner/packet_adapter.go
@@ -28,13 +28,22 @@
 
 type packetAdaptor struct {
 	net.Conn
+	debug *recordingConn
 }
 
 // newPacketAdaptor wraps a reliable streaming net.Conn into a reliable
 // packet-based net.Conn. The stream contains packets and control commands,
 // distinguished by a one byte opcode.
 func newPacketAdaptor(conn net.Conn) *packetAdaptor {
-	return &packetAdaptor{conn}
+	return &packetAdaptor{conn, nil}
+}
+
+func (p *packetAdaptor) log(message string, data []byte) {
+	if p.debug == nil {
+		return
+	}
+
+	p.debug.LogSpecial(message, data)
 }
 
 func (p *packetAdaptor) readOpcode() (byte, error) {
@@ -87,6 +96,8 @@
 // for acknowledgement of the timeout, buffering any packets received since
 // then. The packets are then returned.
 func (p *packetAdaptor) SendReadTimeout(d time.Duration) ([][]byte, error) {
+	p.log("Simulating read timeout: " + d.String(), nil)
+
 	payload := make([]byte, 1+8)
 	payload[0] = opcodeTimeout
 	binary.BigEndian.PutUint64(payload[1:], uint64(d.Nanoseconds()))
@@ -102,6 +113,7 @@
 		}
 		switch opcode {
 		case opcodeTimeoutAck:
+			p.log("Received timeout ACK", nil)
 			// Done! Return the packets buffered and continue.
 			return packets, nil
 		case opcodePacket:
@@ -110,6 +122,7 @@
 			if err != nil {
 				return nil, err
 			}
+			p.log("Simulating dropped packet", packet)
 			packets = append(packets, packet)
 		default:
 			return nil, fmt.Errorf("unexpected opcode '%d'", opcode)
diff --git a/src/ssl/test/runner/recordingconn.go b/src/ssl/test/runner/recordingconn.go
index 6537714..39deb19 100644
--- a/src/ssl/test/runner/recordingconn.go
+++ b/src/ssl/test/runner/recordingconn.go
@@ -12,33 +12,49 @@
 	"sync"
 )
 
+type flowType int
+
+const (
+	readFlow flowType = iota
+	writeFlow
+	specialFlow
+)
+
+type flow struct {
+	flowType flowType
+	message  string
+	data     []byte
+}
+
 // recordingConn is a net.Conn that records the traffic that passes through it.
 // WriteTo can be used to produce output that can be later be loaded with
 // ParseTestData.
 type recordingConn struct {
 	net.Conn
 	sync.Mutex
-	flows   [][]byte
-	reading bool
+	flows       []flow
+	isDatagram  bool
+	local, peer string
+}
+
+func (r *recordingConn) appendFlow(flowType flowType, message string, data []byte) {
+	r.Lock()
+	defer r.Unlock()
+
+	if l := len(r.flows); flowType == specialFlow || r.isDatagram || l == 0 || r.flows[l-1].flowType != flowType {
+		buf := make([]byte, len(data))
+		copy(buf, data)
+		r.flows = append(r.flows, flow{flowType, message, buf})
+	} else {
+		r.flows[l-1].data = append(r.flows[l-1].data, data...)
+	}
 }
 
 func (r *recordingConn) Read(b []byte) (n int, err error) {
 	if n, err = r.Conn.Read(b); n == 0 {
 		return
 	}
-	b = b[:n]
-
-	r.Lock()
-	defer r.Unlock()
-
-	if l := len(r.flows); l == 0 || !r.reading {
-		buf := make([]byte, len(b))
-		copy(buf, b)
-		r.flows = append(r.flows, buf)
-	} else {
-		r.flows[l-1] = append(r.flows[l-1], b[:n]...)
-	}
-	r.reading = true
+	r.appendFlow(readFlow, "", b[:n])
 	return
 }
 
@@ -46,37 +62,33 @@
 	if n, err = r.Conn.Write(b); n == 0 {
 		return
 	}
-	b = b[:n]
-
-	r.Lock()
-	defer r.Unlock()
-
-	if l := len(r.flows); l == 0 || r.reading {
-		buf := make([]byte, len(b))
-		copy(buf, b)
-		r.flows = append(r.flows, buf)
-	} else {
-		r.flows[l-1] = append(r.flows[l-1], b[:n]...)
-	}
-	r.reading = false
+	r.appendFlow(writeFlow, "", b[:n])
 	return
 }
 
+// LogSpecial appends an entry to the record of type 'special'.
+func (r *recordingConn) LogSpecial(message string, data []byte) {
+	r.appendFlow(specialFlow, message, data)
+}
+
 // WriteTo writes hex dumps to w that contains the recorded traffic.
 func (r *recordingConn) WriteTo(w io.Writer) {
-	// TLS always starts with a client to server flow.
-	clientToServer := true
-
+	fmt.Fprintf(w, ">>> runner is %s, shim is %s\n", r.local, r.peer)
 	for i, flow := range r.flows {
-		source, dest := "client", "server"
-		if !clientToServer {
-			source, dest = dest, source
+		switch flow.flowType {
+		case readFlow:
+			fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, r.peer, r.local)
+		case writeFlow:
+			fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, r.local, r.peer)
+		case specialFlow:
+			fmt.Fprintf(w, ">>> Flow %d %q\n", i+1, flow.message)
 		}
-		fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
-		dumper := hex.Dumper(w)
-		dumper.Write(flow)
-		dumper.Close()
-		clientToServer = !clientToServer
+
+		if flow.data != nil {
+			dumper := hex.Dumper(w)
+			dumper.Write(flow.data)
+			dumper.Close()
+		}
 	}
 }
 
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 17fdff9..6ab71cf 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -244,22 +244,36 @@
 var testCases []testCase
 
 func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool) error {
-	var connDebug *recordingConn
 	var connDamage *damageAdaptor
-	if *flagDebug {
-		connDebug = &recordingConn{Conn: conn}
-		conn = connDebug
-		defer func() {
-			connDebug.WriteTo(os.Stdout)
-		}()
-	}
 
 	if test.protocol == dtls {
 		config.Bugs.PacketAdaptor = newPacketAdaptor(conn)
 		conn = config.Bugs.PacketAdaptor
-		if test.replayWrites {
-			conn = newReplayAdaptor(conn)
+	}
+
+	if *flagDebug {
+		local, peer := "client", "server"
+		if test.testType == clientTest {
+			local, peer = peer, local
 		}
+		connDebug := &recordingConn{
+			Conn:       conn,
+			isDatagram: test.protocol == dtls,
+			local:      local,
+			peer:       peer,
+		}
+		conn = connDebug
+		defer func() {
+			connDebug.WriteTo(os.Stdout)
+		}()
+
+		if config.Bugs.PacketAdaptor != nil {
+			config.Bugs.PacketAdaptor.debug = connDebug
+		}
+	}
+
+	if test.replayWrites {
+		conn = newReplayAdaptor(conn)
 	}
 
 	if test.damageFirstWrite {
@@ -2918,27 +2932,25 @@
 		})
 	}
 
-	var suffix string
-	var flags []string
-	var maxHandshakeRecordLength int
-	if protocol == dtls {
-		suffix = "-DTLS"
-	}
-	if async {
-		suffix += "-Async"
-		flags = append(flags, "-async")
-	} else {
-		suffix += "-Sync"
-	}
-	if splitHandshake {
-		suffix += "-SplitHandshakeRecords"
-		maxHandshakeRecordLength = 1
-	}
 	for _, test := range tests {
 		test.protocol = protocol
-		test.name += suffix
-		test.config.Bugs.MaxHandshakeRecordLength = maxHandshakeRecordLength
-		test.flags = append(test.flags, flags...)
+		if protocol == dtls {
+			test.name += "-DTLS"
+		}
+		if async {
+			test.name += "-Async"
+			test.flags = append(test.flags, "-async")
+		} else {
+			test.name += "-Sync"
+		}
+		if splitHandshake {
+			test.name += "-SplitHandshakeRecords"
+			test.config.Bugs.MaxHandshakeRecordLength = 1
+			if protocol == dtls {
+				test.config.Bugs.MaxPacketLength = 256
+				test.flags = append(test.flags, "-mtu", "256")
+			}
+		}
 		testCases = append(testCases, test)
 	}
 }
@@ -3906,6 +3918,28 @@
 			"-expect-total-renegotiations", "2",
 		},
 	})
+	testCases = append(testCases, testCase{
+		name: "Renegotiate-Client-NoIgnore",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendHelloRequestBeforeEveryAppDataRecord: true,
+			},
+		},
+		shouldFail:    true,
+		expectedError: ":NO_RENEGOTIATION:",
+	})
+	testCases = append(testCases, testCase{
+		name: "Renegotiate-Client-Ignore",
+		config: Config{
+			Bugs: ProtocolBugs{
+				SendHelloRequestBeforeEveryAppDataRecord: true,
+			},
+		},
+		flags: []string{
+			"-renegotiate-ignore",
+			"-expect-total-renegotiations", "0",
+		},
+	})
 }
 
 func addDTLSReplayTests() {
@@ -3992,6 +4026,19 @@
 				},
 			},
 		})
+
+		testCases = append(testCases, testCase{
+			name: "SigningHash-ServerKeyExchange-Verify-" + hash.name,
+			config: Config{
+				CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+				SignatureAndHashes: []signatureAndHash{
+					{signatureRSA, 42},
+					{signatureRSA, hash.id},
+					{signatureRSA, 255},
+				},
+			},
+			flags: []string{"-expect-server-key-exchange-hash", strconv.Itoa(int(hash.id))},
+		})
 	}
 
 	// Test that hash resolution takes the signature type into account.
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index abec0e1..50e6b23 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -96,6 +96,7 @@
   { "-expect-verify-result", &TestConfig::expect_verify_result },
   { "-renegotiate-once", &TestConfig::renegotiate_once },
   { "-renegotiate-freely", &TestConfig::renegotiate_freely },
+  { "-renegotiate-ignore", &TestConfig::renegotiate_ignore },
   { "-disable-npn", &TestConfig::disable_npn },
 };
 
@@ -140,6 +141,8 @@
   { "-mtu", &TestConfig::mtu },
   { "-export-keying-material", &TestConfig::export_keying_material },
   { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
+  { "-expect-server-key-exchange-hash",
+    &TestConfig::expect_server_key_exchange_hash },
 };
 
 }  // namespace
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 23fa1f1..9f295ae 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -98,7 +98,9 @@
   int expect_total_renegotiations = 0;
   bool renegotiate_once = false;
   bool renegotiate_freely = false;
+  bool renegotiate_ignore = false;
   bool disable_npn = false;
+  int expect_server_key_exchange_hash = 0;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);