external/boringssl: Sync to 915c121bb5d424e09bf05c3aabf172a44e958e28.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/ea52ec98a56a40879b37493f3d1da1a1679e1fba..915c121bb5d424e09bf05c3aabf172a44e958e28

Test: BoringSSL CTS Presubmits
Change-Id: I3f5eba69372b484e19f4ca250c81f208aa5d3dc5
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index 49d8788..ed9a183 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-ea52ec98a56a40879b37493f3d1da1a1679e1fba
+915c121bb5d424e09bf05c3aabf172a44e958e28
diff --git a/src/crypto/asn1/a_int.c b/src/crypto/asn1/a_int.c
index 8a4edd6..dd74550 100644
--- a/src/crypto/asn1/a_int.c
+++ b/src/crypto/asn1/a_int.c
@@ -347,40 +347,45 @@
 
 int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)
 {
-    int j, k;
-    unsigned int i;
-    unsigned char buf[sizeof(long) + 1];
-    long d;
-
-    a->type = V_ASN1_INTEGER;
-    if (a->length < (int)(sizeof(long) + 1)) {
-        if (a->data != NULL)
-            OPENSSL_free(a->data);
-        if ((a->data =
-             (unsigned char *)OPENSSL_malloc(sizeof(long) + 1)) != NULL)
-            OPENSSL_memset((char *)a->data, 0, sizeof(long) + 1);
+    if (v >= 0) {
+        return ASN1_INTEGER_set_uint64(a, (uint64_t) v);
     }
-    if (a->data == NULL) {
+
+    if (!ASN1_INTEGER_set_uint64(a, 0 - (uint64_t) v)) {
+        return 0;
+    }
+
+    a->type = V_ASN1_NEG_INTEGER;
+    return 1;
+}
+
+int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v)
+{
+    uint8_t *const newdata = OPENSSL_malloc(sizeof(uint64_t));
+    if (newdata == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
-    }
-    d = v;
-    if (d < 0) {
-        d = -d;
-        a->type = V_ASN1_NEG_INTEGER;
+        return 0;
     }
 
-    for (i = 0; i < sizeof(long); i++) {
-        if (d == 0)
+    OPENSSL_free(out->data);
+    out->data = newdata;
+    v = CRYPTO_bswap8(v);
+    memcpy(out->data, &v, sizeof(v));
+
+    out->type = V_ASN1_INTEGER;
+
+    size_t leading_zeros;
+    for (leading_zeros = 0; leading_zeros < sizeof(uint64_t) - 1;
+         leading_zeros++) {
+        if (out->data[leading_zeros] != 0) {
             break;
-        buf[i] = (int)d & 0xff;
-        d >>= 8;
+        }
     }
-    j = 0;
-    for (k = i - 1; k >= 0; k--)
-        a->data[j++] = buf[k];
-    a->length = j;
-    return (1);
+
+    out->length = sizeof(uint64_t) - leading_zeros;
+    OPENSSL_memmove(out->data, out->data + leading_zeros, out->length);
+
+    return 1;
 }
 
 long ASN1_INTEGER_get(const ASN1_INTEGER *a)
diff --git a/src/crypto/asn1/asn1_test.cc b/src/crypto/asn1/asn1_test.cc
index accf3ba..7c114dd 100644
--- a/src/crypto/asn1/asn1_test.cc
+++ b/src/crypto/asn1/asn1_test.cc
@@ -15,6 +15,7 @@
 #include <stdio.h>
 
 #include <gtest/gtest.h>
+#include <limits.h>
 
 #include <openssl/asn1.h>
 #include <openssl/err.h>
@@ -60,3 +61,30 @@
   EXPECT_EQ(Bytes(&kZero, 1), Bytes(obj->value.asn1_string->data,
                                     obj->value.asn1_string->length));
 }
+
+TEST(ASN1Test, IntegerSetting) {
+  bssl::UniquePtr<ASN1_INTEGER> by_bn(M_ASN1_INTEGER_new());
+  bssl::UniquePtr<ASN1_INTEGER> by_long(M_ASN1_INTEGER_new());
+  bssl::UniquePtr<ASN1_INTEGER> by_uint64(M_ASN1_INTEGER_new());
+  bssl::UniquePtr<BIGNUM> bn(BN_new());
+
+  const std::vector<int64_t> kValues = {
+      LONG_MIN, -2, -1, 0, 1, 2, 0xff, 0x100, 0xffff, 0x10000, LONG_MAX,
+  };
+  for (const auto &i : kValues) {
+    SCOPED_TRACE(i);
+
+    ASSERT_EQ(1, ASN1_INTEGER_set(by_long.get(), i));
+    const uint64_t abs = i < 0 ? (0 - (uint64_t) i) : i;
+    ASSERT_TRUE(BN_set_u64(bn.get(), abs));
+    BN_set_negative(bn.get(), i < 0);
+    ASSERT_TRUE(BN_to_ASN1_INTEGER(bn.get(), by_bn.get()));
+
+    EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_long.get()));
+
+    if (i >= 0) {
+      ASSERT_EQ(1, ASN1_INTEGER_set_uint64(by_uint64.get(), i));
+      EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_uint64.get()));
+    }
+  }
+}
diff --git a/src/crypto/bytestring/ber.c b/src/crypto/bytestring/ber.c
index 4dc94f6..bb5e17c 100644
--- a/src/crypto/bytestring/ber.c
+++ b/src/crypto/bytestring/ber.c
@@ -29,10 +29,7 @@
 // is_string_type returns one if |tag| is a string type and zero otherwise. It
 // ignores the constructed bit.
 static int is_string_type(unsigned tag) {
-  if ((tag & 0xc0) != 0) {
-    return 0;
-  }
-  switch (tag & 0x1f) {
+  switch (tag & ~CBS_ASN1_CONSTRUCTED) {
     case CBS_ASN1_BITSTRING:
     case CBS_ASN1_OCTETSTRING:
     case CBS_ASN1_UTF8STRING:
diff --git a/src/crypto/bytestring/bytestring_test.cc b/src/crypto/bytestring/bytestring_test.cc
index 5a6a5c1..1969e73 100644
--- a/src/crypto/bytestring/bytestring_test.cc
+++ b/src/crypto/bytestring/bytestring_test.cc
@@ -123,27 +123,27 @@
   uint64_t value;
 
   CBS_init(&data, kData1, sizeof(kData1));
-  EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x1));
-  EXPECT_TRUE(CBS_peek_asn1_tag(&data, 0x30));
+  EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_BOOLEAN));
+  EXPECT_TRUE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
 
-  ASSERT_TRUE(CBS_get_asn1(&data, &contents, 0x30));
+  ASSERT_TRUE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
   EXPECT_EQ(Bytes("\x01\x02"), Bytes(CBS_data(&contents), CBS_len(&contents)));
 
   CBS_init(&data, kData2, sizeof(kData2));
   // data is truncated
-  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
 
   CBS_init(&data, kData3, sizeof(kData3));
   // zero byte length of length
-  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
 
   CBS_init(&data, kData4, sizeof(kData4));
   // long form mistakenly used.
-  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
 
   CBS_init(&data, kData5, sizeof(kData5));
   // length takes too many bytes.
-  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
 
   CBS_init(&data, kData1, sizeof(kData1));
   // wrong tag.
@@ -151,56 +151,72 @@
 
   CBS_init(&data, NULL, 0);
   // peek at empty data.
-  EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x30));
+  EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
 
   CBS_init(&data, NULL, 0);
   // optional elements at empty data.
-  ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
+  ASSERT_TRUE(CBS_get_optional_asn1(
+      &data, &contents, &present,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
   EXPECT_FALSE(present);
-  ASSERT_TRUE(
-      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
+  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+      &data, &contents, &present,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
   EXPECT_FALSE(present);
   EXPECT_EQ(0u, CBS_len(&contents));
-  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(&data, &contents, NULL, 0xa0));
+  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+      &data, &contents, NULL,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
   EXPECT_EQ(0u, CBS_len(&contents));
-  ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
+  ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
   EXPECT_EQ(42u, value);
 
   CBS_init(&data, kData6, sizeof(kData6));
   // optional element.
-  ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
+  ASSERT_TRUE(CBS_get_optional_asn1(
+      &data, &contents, &present,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
   EXPECT_FALSE(present);
-  ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa1));
+  ASSERT_TRUE(CBS_get_optional_asn1(
+      &data, &contents, &present,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
   EXPECT_TRUE(present);
   EXPECT_EQ(Bytes("\x04\x01\x01"),
             Bytes(CBS_data(&contents), CBS_len(&contents)));
 
   CBS_init(&data, kData6, sizeof(kData6));
   // optional octet string.
-  ASSERT_TRUE(
-      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
+  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+      &data, &contents, &present,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
   EXPECT_FALSE(present);
   EXPECT_EQ(0u, CBS_len(&contents));
-  ASSERT_TRUE(
-      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
+  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
+      &data, &contents, &present,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
   EXPECT_TRUE(present);
   EXPECT_EQ(Bytes("\x01"), Bytes(CBS_data(&contents), CBS_len(&contents)));
 
   CBS_init(&data, kData7, sizeof(kData7));
   // invalid optional octet string.
-  EXPECT_FALSE(
-      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
+  EXPECT_FALSE(CBS_get_optional_asn1_octet_string(
+      &data, &contents, &present,
+      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
 
   CBS_init(&data, kData8, sizeof(kData8));
   // optional integer.
-  ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
+  ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
   EXPECT_EQ(42u, value);
-  ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
+  ASSERT_TRUE(CBS_get_optional_asn1_uint64(
+      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
   EXPECT_EQ(1u, value);
 
   CBS_init(&data, kData9, sizeof(kData9));
   // invalid optional integer.
-  EXPECT_FALSE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
+  EXPECT_FALSE(CBS_get_optional_asn1_uint64(
+      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
 
   unsigned tag;
   CBS_init(&data, kData1, sizeof(kData1));
@@ -217,6 +233,54 @@
             Bytes(CBS_data(&contents), CBS_len(&contents)));
 }
 
+TEST(CBSTest, ParseASN1Tag) {
+  const struct {
+    bool ok;
+    unsigned tag;
+    std::vector<uint8_t> in;
+  } kTests[] = {
+      {true, CBS_ASN1_SEQUENCE, {0x30, 0}},
+      {true, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 4, {0xa4, 0}},
+      {true, CBS_ASN1_APPLICATION | 30, {0x5e, 0}},
+      {true, CBS_ASN1_APPLICATION | 31, {0x5f, 0x1f, 0}},
+      {true, CBS_ASN1_APPLICATION | 32, {0x5f, 0x20, 0}},
+      {true,
+       CBS_ASN1_PRIVATE | CBS_ASN1_CONSTRUCTED | 0x1fffffff,
+       {0xff, 0x81, 0xff, 0xff, 0xff, 0x7f, 0}},
+      // Tag number fits in unsigned but not |CBS_ASN1_TAG_NUMBER_MASK|.
+      {false, 0, {0xff, 0x82, 0xff, 0xff, 0xff, 0x7f, 0}},
+      // Tag number does not fit in unsigned.
+      {false, 0, {0xff, 0x90, 0x80, 0x80, 0x80, 0, 0}},
+      // Tag number is not minimally-encoded
+      {false, 0, {0x5f, 0x80, 0x1f, 0}},
+      // Tag number should have used short form.
+      {false, 0, {0x5f, 0x80, 0x1e, 0}},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(Bytes(t.in));
+    unsigned tag;
+    CBS cbs, child;
+    CBS_init(&cbs, t.in.data(), t.in.size());
+    ASSERT_EQ(t.ok, !!CBS_get_any_asn1(&cbs, &child, &tag));
+    if (t.ok) {
+      EXPECT_EQ(t.tag, tag);
+      EXPECT_EQ(0u, CBS_len(&child));
+      EXPECT_EQ(0u, CBS_len(&cbs));
+
+      CBS_init(&cbs, t.in.data(), t.in.size());
+      EXPECT_TRUE(CBS_peek_asn1_tag(&cbs, t.tag));
+      EXPECT_FALSE(CBS_peek_asn1_tag(&cbs, t.tag + 1));
+
+      EXPECT_TRUE(CBS_get_asn1(&cbs, &child, t.tag));
+      EXPECT_EQ(0u, CBS_len(&child));
+      EXPECT_EQ(0u, CBS_len(&cbs));
+
+      CBS_init(&cbs, t.in.data(), t.in.size());
+      EXPECT_FALSE(CBS_get_asn1(&cbs, &child, t.tag + 1));
+    }
+  }
+}
+
 TEST(CBSTest, GetOptionalASN1Bool) {
   static const uint8_t kTrue[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0xff};
   static const uint8_t kFalse[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x00};
@@ -416,15 +480,42 @@
 }
 
 TEST(CBBTest, ASN1) {
-  static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3};
+  static const uint8_t kExpected[] = {
+      // SEQUENCE { 1 2 3 }
+      0x30, 3, 1, 2, 3,
+      // [4 CONSTRUCTED] { 4 5 6 }
+      0xa4, 3, 4, 5, 6,
+      // [APPLICATION 30 PRIMITIVE] { 7 8 9 }
+      0x5e, 3, 7, 8, 9,
+      // [APPLICATION 31 PRIMITIVE] { 10 11 12 }
+      0x5f, 0x1f, 3, 10, 11, 12,
+      // [PRIVATE 2^29-1 CONSTRUCTED] { 13 14 15 }
+      0xff, 0x81, 0xff, 0xff, 0xff, 0x7f, 3, 13, 14, 15,
+  };
   uint8_t *buf;
   size_t buf_len;
   bssl::ScopedCBB cbb;
   CBB contents, inner_contents;
 
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
-  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
   ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x01\x02\x03", 3));
+  ASSERT_TRUE(
+      CBB_add_asn1(cbb.get(), &contents,
+                   CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 4));
+  ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x04\x05\x06", 3));
+  ASSERT_TRUE(
+      CBB_add_asn1(cbb.get(), &contents,
+                   CBS_ASN1_APPLICATION | 30));
+  ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x07\x08\x09", 3));
+  ASSERT_TRUE(
+      CBB_add_asn1(cbb.get(), &contents,
+                   CBS_ASN1_APPLICATION | 31));
+  ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x0a\x0b\x0c", 3));
+  ASSERT_TRUE(
+      CBB_add_asn1(cbb.get(), &contents,
+                   CBS_ASN1_PRIVATE | CBS_ASN1_CONSTRUCTED | 0x1fffffff));
+  ASSERT_TRUE(CBB_add_bytes(&contents, (const uint8_t *)"\x0d\x0e\x0f", 3));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
   bssl::UniquePtr<uint8_t> scoper(buf);
 
@@ -432,7 +523,7 @@
 
   std::vector<uint8_t> test_data(100000, 0x42);
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
-  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
   ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 130));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
   scoper.reset(buf);
@@ -442,7 +533,7 @@
   EXPECT_EQ(Bytes(test_data.data(), 130), Bytes(buf + 3, 130));
 
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
-  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
   ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 1000));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
   scoper.reset(buf);
@@ -452,8 +543,8 @@
   EXPECT_EQ(Bytes(test_data.data(), 1000), Bytes(buf + 4, 1000));
 
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
-  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
-  ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, 0x30));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, CBS_ASN1_SEQUENCE));
   ASSERT_TRUE(CBB_add_bytes(&inner_contents, test_data.data(), 100000));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
   scoper.reset(buf);
@@ -490,6 +581,12 @@
   static const uint8_t kIndefBER[] = {0x30, 0x80, 0x01, 0x01, 0x02, 0x00, 0x00};
   static const uint8_t kIndefDER[] = {0x30, 0x03, 0x01, 0x01, 0x02};
 
+  // kIndefBER2 contains a constructed [APPLICATION 31] with an indefinite
+  // length.
+  static const uint8_t kIndefBER2[] = {0x7f, 0x1f, 0x80, 0x01,
+                                       0x01, 0x02, 0x00, 0x00};
+  static const uint8_t kIndefDER2[] = {0x7f, 0x1f, 0x03, 0x01, 0x01, 0x02};
+
   // kOctetStringBER contains an indefinite length OCTET STRING with two parts.
   // These parts need to be concatenated in DER form.
   static const uint8_t kOctetStringBER[] = {0x24, 0x80, 0x04, 0x02, 0,    1,
@@ -534,6 +631,8 @@
                    sizeof(kSimpleBER));
   ExpectBerConvert("kIndefBER", kIndefDER, sizeof(kIndefDER), kIndefBER,
                    sizeof(kIndefBER));
+  ExpectBerConvert("kIndefBER2", kIndefDER2, sizeof(kIndefDER2), kIndefBER2,
+                   sizeof(kIndefBER2));
   ExpectBerConvert("kOctetStringBER", kOctetStringDER, sizeof(kOctetStringDER),
                    kOctetStringBER, sizeof(kOctetStringBER));
   ExpectBerConvert("kNSSBER", kNSSDER, sizeof(kNSSDER), kNSSBER,
diff --git a/src/crypto/bytestring/cbb.c b/src/crypto/bytestring/cbb.c
index f8f5e0f..b12a66c 100644
--- a/src/crypto/bytestring/cbb.c
+++ b/src/crypto/bytestring/cbb.c
@@ -356,17 +356,20 @@
 }
 
 int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
-  if (tag > 0xff ||
-      (tag & 0x1f) == 0x1f) {
-    // Long form identifier octets are not supported. Further, all current valid
-    // tag serializations are 8 bits.
-    cbb->base->error = 1;
+  if (!CBB_flush(cbb)) {
     return 0;
   }
 
-  if (!CBB_flush(cbb) ||
-      // |tag|'s representation matches the DER encoding.
-      !CBB_add_u8(cbb, (uint8_t)tag)) {
+  // Split the tag into leading bits and tag number.
+  uint8_t tag_bits = (tag >> CBS_ASN1_TAG_SHIFT) & 0xe0;
+  unsigned tag_number = tag & CBS_ASN1_TAG_NUMBER_MASK;
+  if (tag_number >= 0x1f) {
+    // Set all the bits in the tag number to signal high tag number form.
+    if (!CBB_add_u8(cbb, tag_bits | 0x1f) ||
+        !add_base128_integer(cbb, tag_number)) {
+      return 0;
+    }
+  } else if (!CBB_add_u8(cbb, tag_bits | tag_number)) {
     return 0;
   }
 
diff --git a/src/crypto/bytestring/cbs.c b/src/crypto/bytestring/cbs.c
index f3fc863..e456330 100644
--- a/src/crypto/bytestring/cbs.c
+++ b/src/crypto/bytestring/cbs.c
@@ -207,18 +207,9 @@
   return 1;
 }
 
-static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
-                                    size_t *out_header_len, int ber_ok) {
-  uint8_t tag, length_byte;
-  CBS header = *cbs;
-  CBS throwaway;
-
-  if (out == NULL) {
-    out = &throwaway;
-  }
-
-  if (!CBS_get_u8(&header, &tag) ||
-      !CBS_get_u8(&header, &length_byte)) {
+static int parse_asn1_tag(CBS *cbs, unsigned *out) {
+  uint8_t tag_byte;
+  if (!CBS_get_u8(cbs, &tag_byte)) {
     return 0;
   }
 
@@ -229,22 +220,58 @@
   // allotted bits), then the tag is more than one byte long and the
   // continuation bytes contain the tag number. This parser only supports tag
   // numbers less than 31 (and thus single-byte tags).
-  if ((tag & 0x1f) == 0x1f) {
-    return 0;
+  unsigned tag = ((unsigned)tag_byte & 0xe0) << CBS_ASN1_TAG_SHIFT;
+  unsigned tag_number = tag_byte & 0x1f;
+  if (tag_number == 0x1f) {
+    uint64_t v;
+    if (!parse_base128_integer(cbs, &v) ||
+        // Check the tag number is within our supported bounds.
+        v > CBS_ASN1_TAG_NUMBER_MASK ||
+        // Small tag numbers should have used low tag number form.
+        v < 0x1f) {
+      return 0;
+    }
+    tag_number = (unsigned)v;
   }
 
+  tag |= tag_number;
+
+  *out = tag;
+  return 1;
+}
+
+static int cbs_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
+                                    size_t *out_header_len, int ber_ok) {
+  CBS header = *cbs;
+  CBS throwaway;
+
+  if (out == NULL) {
+    out = &throwaway;
+  }
+
+  unsigned tag;
+  if (!parse_asn1_tag(&header, &tag)) {
+    return 0;
+  }
   if (out_tag != NULL) {
     *out_tag = tag;
   }
 
+  uint8_t length_byte;
+  if (!CBS_get_u8(&header, &length_byte)) {
+    return 0;
+  }
+
+  size_t header_len = CBS_len(cbs) - CBS_len(&header);
+
   size_t len;
   // The format for the length encoding is specified in ITU-T X.690 section
   // 8.1.3.
   if ((length_byte & 0x80) == 0) {
     // Short form length.
-    len = ((size_t) length_byte) + 2;
+    len = ((size_t) length_byte) + header_len;
     if (out_header_len != NULL) {
-      *out_header_len = 2;
+      *out_header_len = header_len;
     }
   } else {
     // The high bit indicate that this is the long form, while the next 7 bits
@@ -256,9 +283,9 @@
     if (ber_ok && (tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
       // indefinite length
       if (out_header_len != NULL) {
-        *out_header_len = 2;
+        *out_header_len = header_len;
       }
-      return CBS_get_bytes(cbs, out, 2);
+      return CBS_get_bytes(cbs, out, header_len);
     }
 
     // ITU-T X.690 clause 8.1.3.5.c specifies that the value 0xff shall not be
@@ -281,13 +308,13 @@
       return 0;
     }
     len = len32;
-    if (len + 2 + num_bytes < len) {
+    if (len + header_len + num_bytes < len) {
       // Overflow.
       return 0;
     }
-    len += 2 + num_bytes;
+    len += header_len + num_bytes;
     if (out_header_len != NULL) {
-      *out_header_len = 2 + num_bytes;
+      *out_header_len = header_len + num_bytes;
     }
   }
 
@@ -355,7 +382,10 @@
   if (CBS_len(cbs) < 1) {
     return 0;
   }
-  return CBS_data(cbs)[0] == tag_value;
+
+  CBS copy = *cbs;
+  unsigned actual_tag;
+  return parse_asn1_tag(&copy, &actual_tag) && tag_value == actual_tag;
 }
 
 int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
diff --git a/src/crypto/cipher_extra/e_tls.c b/src/crypto/cipher_extra/e_tls.c
index 72754c0..bba22be 100644
--- a/src/crypto/cipher_extra/e_tls.c
+++ b/src/crypto/cipher_extra/e_tls.c
@@ -191,8 +191,7 @@
   // block from encrypting the input and split the result between |out| and
   // |out_tag|. Then feed the rest.
 
-  const size_t early_mac_len =
-      (block_size - (in_len % block_size) % block_size);
+  const size_t early_mac_len = (block_size - (in_len % block_size)) % block_size;
   if (early_mac_len != 0) {
     assert(len + block_size - early_mac_len == in_len);
     uint8_t buf[EVP_MAX_BLOCK_LENGTH];
diff --git a/src/crypto/fipsmodule/ec/ec_key.c b/src/crypto/fipsmodule/ec/ec_key.c
index f64cb21..4e0bcb2 100644
--- a/src/crypto/fipsmodule/ec/ec_key.c
+++ b/src/crypto/fipsmodule/ec/ec_key.c
@@ -233,20 +233,21 @@
 const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key) { return key->group; }
 
 int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group) {
+  // If |key| already has a group, it is an error to switch to another one.
+  if (key->group != NULL) {
+    if (EC_GROUP_cmp(key->group, group, NULL) != 0) {
+      OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
+      return 0;
+    }
+    return 1;
+  }
+
+  assert(key->priv_key == NULL);
+  assert(key->pub_key == NULL);
+
   EC_GROUP_free(key->group);
-  // TODO(fork): duplicating the group seems wasteful but see
-  // |EC_KEY_set_conv_form|.
   key->group = EC_GROUP_dup(group);
-  if (key->group == NULL) {
-    return 0;
-  }
-  // XXX: |BN_cmp| is not constant time.
-  if (key->priv_key != NULL &&
-      (BN_is_negative(key->priv_key) ||
-       BN_cmp(key->priv_key, EC_GROUP_get0_order(group)) >= 0)) {
-    return 0;
-  }
-  return 1;
+  return key->group != NULL;
 }
 
 const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key) {
@@ -254,10 +255,14 @@
 }
 
 int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key) {
+  if (key->group == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
   // XXX: |BN_cmp| is not constant time.
-  if (key->group != NULL &&
-      (BN_is_negative(priv_key) ||
-       BN_cmp(priv_key, EC_GROUP_get0_order(key->group)) >= 0)) {
+  if (BN_is_negative(priv_key) ||
+      BN_cmp(priv_key, EC_GROUP_get0_order(key->group)) >= 0) {
     OPENSSL_PUT_ERROR(EC, EC_R_WRONG_ORDER);
     return 0;
   }
@@ -271,6 +276,16 @@
 }
 
 int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key) {
+  if (key->group == NULL) {
+    OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
+  if (EC_GROUP_cmp(key->group, pub_key->group, NULL) != 0) {
+    OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
+    return 0;
+  }
+
   EC_POINT_free(key->pub_key);
   key->pub_key = EC_POINT_dup(pub_key, key->group);
   return (key->pub_key == NULL) ? 0 : 1;
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 8e7a81d..d2cd2af 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -305,6 +305,36 @@
   EXPECT_NE(0, EC_GROUP_cmp(group.get(), group3.get(), NULL));
 }
 
+TEST(ECTest, SetKeyWithoutGroup) {
+  bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+  ASSERT_TRUE(key);
+
+  // Private keys may not be configured without a group.
+  EXPECT_FALSE(EC_KEY_set_private_key(key.get(), BN_value_one()));
+
+  // Public keys may not be configured without a group.
+  bssl::UniquePtr<EC_GROUP> group(
+      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+  ASSERT_TRUE(group);
+  EXPECT_FALSE(
+      EC_KEY_set_public_key(key.get(), EC_GROUP_get0_generator(group.get())));
+}
+
+TEST(ECTest, GroupMismatch) {
+  bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_secp384r1));
+  ASSERT_TRUE(key);
+  bssl::UniquePtr<EC_GROUP> p256(
+      EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+  ASSERT_TRUE(p256);
+
+  // Changing a key's group is invalid.
+  EXPECT_FALSE(EC_KEY_set_group(key.get(), p256.get()));
+
+  // Configuring a public key with the wrong group is invalid.
+  EXPECT_FALSE(
+      EC_KEY_set_public_key(key.get(), EC_GROUP_get0_generator(p256.get())));
+}
+
 class ECCurveTest : public testing::TestWithParam<EC_builtin_curve> {};
 
 TEST_P(ECCurveTest, SetAffine) {
diff --git a/src/crypto/fipsmodule/modes/internal.h b/src/crypto/fipsmodule/modes/internal.h
index f6ee8f4..68ef4dc 100644
--- a/src/crypto/fipsmodule/modes/internal.h
+++ b/src/crypto/fipsmodule/modes/internal.h
@@ -66,38 +66,6 @@
 #define STRICT_ALIGNMENT 0
 #endif
 
-#if defined(__GNUC__) && __GNUC__ >= 2
-static inline uint32_t CRYPTO_bswap4(uint32_t x) {
-  return __builtin_bswap32(x);
-}
-
-static inline uint64_t CRYPTO_bswap8(uint64_t x) {
-  return __builtin_bswap64(x);
-}
-#elif defined(_MSC_VER)
-OPENSSL_MSVC_PRAGMA(warning(push, 3))
-#include <intrin.h>
-OPENSSL_MSVC_PRAGMA(warning(pop))
-#pragma intrinsic(_byteswap_uint64, _byteswap_ulong)
-static inline uint32_t CRYPTO_bswap4(uint32_t x) {
-  return _byteswap_ulong(x);
-}
-
-static inline uint64_t CRYPTO_bswap8(uint64_t x) {
-  return _byteswap_uint64(x);
-}
-#else
-static inline uint32_t CRYPTO_bswap4(uint32_t x) {
-  x = (x >> 16) | (x << 16);
-  x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
-  return x;
-}
-
-static inline uint64_t CRYPTO_bswap8(uint64_t x) {
-  return CRYPTO_bswap4(x >> 32) | (((uint64_t)CRYPTO_bswap4(x)) << 32);
-}
-#endif
-
 static inline uint32_t GETU32(const void *in) {
   uint32_t v;
   OPENSSL_memcpy(&v, in, sizeof(v));
diff --git a/src/crypto/fipsmodule/rsa/rsa.c b/src/crypto/fipsmodule/rsa/rsa.c
index 4a84314..ffe2cd0 100644
--- a/src/crypto/fipsmodule/rsa/rsa.c
+++ b/src/crypto/fipsmodule/rsa/rsa.c
@@ -852,6 +852,8 @@
   return rsa_default_private_transform(rsa, out, in, len);
 }
 
+int RSA_flags(const RSA *rsa) { return rsa->flags; }
+
 int RSA_blinding_on(RSA *rsa, BN_CTX *ctx) {
   return 1;
 }
diff --git a/src/crypto/fipsmodule/rsa/rsa_impl.c b/src/crypto/fipsmodule/rsa/rsa_impl.c
index c391228..b5a4e51 100644
--- a/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -1097,5 +1097,4 @@
   // |rsa_default_*| implementation.
   OPENSSL_memset(out, 0, sizeof(RSA_METHOD));
   out->common.is_static = 1;
-  out->flags = RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE;
 }
diff --git a/src/crypto/internal.h b/src/crypto/internal.h
index 5706414..e6bab02 100644
--- a/src/crypto/internal.h
+++ b/src/crypto/internal.h
@@ -591,6 +591,41 @@
                                         void *obj, CRYPTO_EX_DATA *ad);
 
 
+// Endianness conversions.
+
+#if defined(__GNUC__) && __GNUC__ >= 2
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  return __builtin_bswap32(x);
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return __builtin_bswap64(x);
+}
+#elif defined(_MSC_VER)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
+#include <intrin.h>
+OPENSSL_MSVC_PRAGMA(warning(pop))
+#pragma intrinsic(_byteswap_uint64, _byteswap_ulong)
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  return _byteswap_ulong(x);
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return _byteswap_uint64(x);
+}
+#else
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  x = (x >> 16) | (x << 16);
+  x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
+  return x;
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return CRYPTO_bswap4(x >> 32) | (((uint64_t)CRYPTO_bswap4(x)) << 32);
+}
+#endif
+
+
 // Language bug workarounds.
 //
 // Most C standard library functions are undefined if passed NULL, even when the
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index 158fd45..b4cecca 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -835,7 +835,7 @@
 
   /* This ensures the X509 took a reference to |buf|, otherwise this will be a
    * reference to free memory and ASAN should notice. */
-  ASSERT_EQ(CBS_ASN1_SEQUENCE, enc_pointer[0]);
+  ASSERT_EQ(0x30, enc_pointer[0]);
 }
 
 TEST(X509Test, TestFromBufferWithTrailingData) {
diff --git a/src/include/openssl/asn1.h b/src/include/openssl/asn1.h
index 6572995..c7ead03 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -737,6 +737,7 @@
 OPENSSL_EXPORT ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data,int len, const char *sn, const char *ln);
 
 OPENSSL_EXPORT int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
+OPENSSL_EXPORT int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v);
 OPENSSL_EXPORT long ASN1_INTEGER_get(const ASN1_INTEGER *a);
 OPENSSL_EXPORT ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai);
 OPENSSL_EXPORT BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai,BIGNUM *bn);
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 25411c7..43349e9 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -164,34 +164,36 @@
 #define CBS_ASN1_UNIVERSALSTRING 0x1cu
 #define CBS_ASN1_BMPSTRING 0x1eu
 
+// CBS_ASN1_TAG_SHIFT is how much the in-memory representation shifts the class
+// and constructed bits from the DER serialization. This allows representing tag
+// numbers beyond 31.
+//
+// Consumers must use the following constants to decompose or assemble tags.
+#define CBS_ASN1_TAG_SHIFT 24
+
 // CBS_ASN1_CONSTRUCTED may be ORed into a tag to toggle the constructed
 // bit. |CBS| and |CBB| APIs consider the constructed bit to be part of the
 // tag.
-#define CBS_ASN1_CONSTRUCTED 0x20u
+#define CBS_ASN1_CONSTRUCTED (0x20u << CBS_ASN1_TAG_SHIFT)
 
-// The following values specify the constructed bit or tag class and may be ORed
-// into a tag number to produce the final tag. If none is used, the tag will be
-// UNIVERSAL.
-//
-// Note that although they currently match the DER serialization, consumers must
-// use these bits rather than make assumptions about the representation. This is
-// to allow for tag numbers beyond 31 in the future.
-#define CBS_ASN1_APPLICATION 0x40u
-#define CBS_ASN1_CONTEXT_SPECIFIC 0x80u
-#define CBS_ASN1_PRIVATE 0xc0u
+// The following values specify the tag class and may be ORed into a tag number
+// to produce the final tag. If none is used, the tag will be UNIVERSAL.
+#define CBS_ASN1_UNIVERSAL (0u << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_APPLICATION (0x40u << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_CONTEXT_SPECIFIC (0x80u << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_PRIVATE (0xc0u << CBS_ASN1_TAG_SHIFT)
 
-// CBS_ASN1_CLASS_MASK may be ANDed with a tag to query its class.
-#define CBS_ASN1_CLASS_MASK 0xc0u
+// CBS_ASN1_CLASS_MASK may be ANDed with a tag to query its class. This will
+// give one of the four values above.
+#define CBS_ASN1_CLASS_MASK (0xc0u << CBS_ASN1_TAG_SHIFT)
 
 // CBS_ASN1_TAG_NUMBER_MASK may be ANDed with a tag to query its number.
-#define CBS_ASN1_TAG_NUMBER_MASK 0x1fu
+#define CBS_ASN1_TAG_NUMBER_MASK ((1u << (5 + CBS_ASN1_TAG_SHIFT)) - 1)
 
 // CBS_get_asn1 sets |*out| to the contents of DER-encoded, ASN.1 element (not
 // including tag and length bytes) and advances |cbs| over it. The ASN.1
 // element must match |tag_value|. It returns one on success and zero
 // on error.
-//
-// Tag numbers greater than 30 are not supported (i.e. short form only).
 OPENSSL_EXPORT int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value);
 
 // CBS_get_asn1_element acts like |CBS_get_asn1| but |out| will include the
@@ -209,16 +211,12 @@
 // (not including tag and length bytes), sets |*out_tag| to the tag number, and
 // advances |*cbs|. It returns one on success and zero on error. Either of |out|
 // and |out_tag| may be NULL to ignore the value.
-//
-// Tag numbers greater than 30 are not supported (i.e. short form only).
 OPENSSL_EXPORT int CBS_get_any_asn1(CBS *cbs, CBS *out, unsigned *out_tag);
 
 // CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from
 // |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to
 // the tag number and |*out_header_len| to the length of the ASN.1 header. Each
 // of |out|, |out_tag|, and |out_header_len| may be NULL to ignore the value.
-//
-// Tag numbers greater than 30 are not supported (i.e. short form only).
 OPENSSL_EXPORT int CBS_get_any_asn1_element(CBS *cbs, CBS *out,
                                             unsigned *out_tag,
                                             size_t *out_header_len);
@@ -403,9 +401,7 @@
 
 // CBB_add_asn1 sets |*out_contents| to a |CBB| into which the contents of an
 // ASN.1 object can be written. The |tag| argument will be used as the tag for
-// the object. Passing in |tag| number 31 will return in an error since only
-// single octet identifiers are supported. It returns one on success or zero
-// on error.
+// the object. It returns one on success or zero on error.
 OPENSSL_EXPORT int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag);
 
 // CBB_add_bytes appends |len| bytes from |data| to |cbb|. It returns one on
diff --git a/src/include/openssl/ec_key.h b/src/include/openssl/ec_key.h
index 7ef1b14..5fe0318 100644
--- a/src/include/openssl/ec_key.h
+++ b/src/include/openssl/ec_key.h
@@ -116,14 +116,16 @@
 OPENSSL_EXPORT const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);
 
 // EC_KEY_set_group sets the |EC_GROUP| object that |key| will use to |group|.
-// It returns one on success and zero otherwise.
+// It returns one on success and zero otherwise. If |key| already has a group,
+// it is an error to change to a different one.
 OPENSSL_EXPORT int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group);
 
 // EC_KEY_get0_private_key returns a pointer to the private key inside |key|.
 OPENSSL_EXPORT const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key);
 
 // EC_KEY_set_private_key sets the private key of |key| to |priv|. It returns
-// one on success and zero otherwise.
+// one on success and zero otherwise. |key| must already have had a group
+// configured (see |EC_KEY_set_group| and |EC_KEY_new_by_curve_name|).
 OPENSSL_EXPORT int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *prv);
 
 // EC_KEY_get0_public_key returns a pointer to the public key point inside
@@ -131,7 +133,9 @@
 OPENSSL_EXPORT const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key);
 
 // EC_KEY_set_public_key sets the public key of |key| to |pub|, by copying it.
-// It returns one on success and zero otherwise.
+// It returns one on success and zero otherwise. |key| must already have had a
+// group configured (see |EC_KEY_set_group| and |EC_KEY_new_by_curve_name|), and
+// |pub| must also belong to that group.
 OPENSSL_EXPORT int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub);
 
 #define EC_PKEY_NO_PARAMETERS 0x001
diff --git a/src/include/openssl/rsa.h b/src/include/openssl/rsa.h
index 11aa8e4..1731f14 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -489,12 +489,6 @@
 // API, like a platform key store.
 #define RSA_FLAG_OPAQUE 1
 
-// Deprecated and ignored.
-#define RSA_FLAG_CACHE_PUBLIC 2
-
-// Deprecated and ignored.
-#define RSA_FLAG_CACHE_PRIVATE 4
-
 // RSA_FLAG_NO_BLINDING disables blinding of private operations, which is a
 // dangerous thing to do. It is deprecated and should not be used. It will
 // be ignored whenever possible.
@@ -506,10 +500,6 @@
 // RSA_FLAG_EXT_PKEY is deprecated and ignored.
 #define RSA_FLAG_EXT_PKEY 0x20
 
-// RSA_FLAG_SIGN_VER causes the |sign| and |verify| functions of |rsa_meth_st|
-// to be called when set.
-#define RSA_FLAG_SIGN_VER 0x40
-
 
 // RSA public exponent values.
 
@@ -519,6 +509,12 @@
 
 // Deprecated functions.
 
+#define RSA_METHOD_FLAG_NO_CHECK RSA_FLAG_OPAQUE
+
+// RSA_flags returns the flags for |rsa|. These are a bitwise OR of |RSA_FLAG_*|
+// constants.
+OPENSSL_EXPORT int RSA_flags(const RSA *rsa);
+
 // RSA_blinding_on returns one.
 OPENSSL_EXPORT int RSA_blinding_on(RSA *rsa, BN_CTX *ctx);
 
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 066390b..6ab1682 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -591,12 +591,8 @@
 #define DTLS1_VERSION 0xfeff
 #define DTLS1_2_VERSION 0xfefd
 
-#define TLS1_3_DRAFT_VERSION 0x7f12
-#define TLS1_3_DRAFT21_VERSION 0x7f15
 #define TLS1_3_DRAFT22_VERSION 0x7f16
-#define TLS1_3_EXPERIMENT_VERSION 0x7e01
 #define TLS1_3_EXPERIMENT2_VERSION 0x7e02
-#define TLS1_3_EXPERIMENT3_VERSION 0x7e03
 
 // SSL_CTX_set_min_proto_version sets the minimum protocol version for |ctx| to
 // |version|. If |version| is zero, the default minimum version is used. It
@@ -2384,6 +2380,11 @@
 // either |X509_V_OK| or a |X509_V_ERR_*| value.
 OPENSSL_EXPORT long SSL_get_verify_result(const SSL *ssl);
 
+// SSL_alert_from_verify_result returns the SSL alert code, such as
+// |SSL_AD_CERTIFICATE_EXPIRED|, that corresponds to an |X509_V_ERR_*| value.
+// The return value is always an alert, even when |result| is |X509_V_OK|.
+OPENSSL_EXPORT int SSL_alert_from_verify_result(long result);
+
 // SSL_get_ex_data_X509_STORE_CTX_idx returns the ex_data index used to look up
 // the |SSL| associated with an |X509_STORE_CTX| in the verify callback.
 OPENSSL_EXPORT int SSL_get_ex_data_X509_STORE_CTX_idx(void);
@@ -3226,11 +3227,7 @@
 
 enum tls13_variant_t {
   tls13_default = 0,
-  tls13_experiment = 1,
-  tls13_experiment2 = 2,
-  tls13_experiment3 = 3,
-  tls13_draft21 = 4,
-  tls13_draft22 = 5,
+  tls13_experiment2 = 1,
 };
 
 // SSL_CTX_set_tls13_variant sets which variant of TLS 1.3 we negotiate. On the
@@ -3490,6 +3487,10 @@
 OPENSSL_EXPORT void SSL_CTX_set_false_start_allowed_without_alpn(SSL_CTX *ctx,
                                                                  int allowed);
 
+// SSL_is_draft_downgrade returns one if the TLS 1.3 anti-downgrade mechanism
+// would have aborted |ssl|'s handshake and zero otherwise.
+OPENSSL_EXPORT int SSL_is_draft_downgrade(const SSL *ssl);
+
 
 // Deprecated functions.
 
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index cdda459..4c7012b 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -464,7 +464,7 @@
     hs->session_id_len = ssl->session->session_id_length;
     OPENSSL_memcpy(hs->session_id, ssl->session->session_id,
                    hs->session_id_len);
-  } else if (ssl_is_resumption_variant(hs->max_version, ssl->tls13_variant)) {
+  } else if (hs->max_version >= TLS1_3_VERSION) {
     hs->session_id_len = sizeof(hs->session_id);
     if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
       return ssl_hs_error;
@@ -632,8 +632,20 @@
   OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
                  SSL3_RANDOM_SIZE);
 
-  // TODO(davidben): Implement the TLS 1.1 and 1.2 downgrade sentinels once TLS
-  // 1.3 is finalized and we are not implementing a draft version.
+  // Measure, but do not enforce, the TLS 1.3 anti-downgrade feature, with a
+  // different value.
+  //
+  // For draft TLS 1.3 versions, it is not safe to deploy this feature. However,
+  // some TLS terminators are non-compliant and copy the origin server's value,
+  // so we wish to measure eventual compatibility impact.
+  if (!ssl->s3->initial_handshake_complete &&
+      hs->max_version >= TLS1_3_VERSION &&
+      OPENSSL_memcmp(ssl->s3->server_random + SSL3_RANDOM_SIZE -
+                         sizeof(kDraftDowngradeRandom),
+                     kDraftDowngradeRandom,
+                     sizeof(kDraftDowngradeRandom)) == 0) {
+    ssl->s3->draft_downgrade = true;
+  }
 
   if (!ssl->s3->initial_handshake_complete && ssl->session != NULL &&
       ssl->session->session_id_length != 0 &&
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index bb565e9..bcbd7e2 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -707,8 +707,16 @@
     return ssl_hs_error;
   }
 
-  // TODO(davidben): Implement the TLS 1.1 and 1.2 downgrade sentinels once TLS
-  // 1.3 is finalized and we are not implementing a draft version.
+  // Implement the TLS 1.3 anti-downgrade feature, but with a different value.
+  //
+  // For draft TLS 1.3 versions, it is not safe to deploy this feature. However,
+  // some TLS terminators are non-compliant and copy the origin server's value,
+  // so we wish to measure eventual compatibility impact.
+  if (hs->max_version >= TLS1_3_VERSION) {
+    OPENSSL_memcpy(ssl->s3->server_random + SSL3_RANDOM_SIZE -
+                       sizeof(kDraftDowngradeRandom),
+                   kDraftDowngradeRandom, sizeof(kDraftDowngradeRandom));
+  }
 
   const SSL_SESSION *session = hs->new_session.get();
   if (ssl->session != NULL) {
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 4151d2b..78d7aa6 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -1010,6 +1010,7 @@
 #define SSL_MAX_HANDSHAKE_FLIGHT 7
 
 extern const uint8_t kHelloRetryRequest[SSL3_RANDOM_SIZE];
+extern const uint8_t kDraftDowngradeRandom[8];
 
 // ssl_max_handshake_message_len returns the maximum number of bytes permitted
 // in a handshake message for |ssl|.
@@ -2297,6 +2298,13 @@
   // wpend_pending is true if we have a pending write outstanding.
   bool wpend_pending:1;
 
+  // early_data_accepted is true if early data was accepted by the server.
+  bool early_data_accepted:1;
+
+  // draft_downgrade is whether the TLS 1.3 anti-downgrade logic would have
+  // fired, were it not a draft.
+  bool draft_downgrade:1;
+
   uint8_t send_alert[2] = {0};
 
   // hs_buf is the buffer of handshake data to process.
@@ -2643,9 +2651,6 @@
   // hash of the peer's certificate and then discard it to save memory and
   // session space. Only effective on the server side.
   bool retain_only_sha256_of_client_certs:1;
-
-  // early_data_accepted is true if early data was accepted by the server.
-  bool early_data_accepted:1;
 };
 
 // From draft-ietf-tls-tls13-18, used in determining PSK modes.
diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc
index b925cd7..a3fc8d7 100644
--- a/src/ssl/s3_lib.cc
+++ b/src/ssl/s3_lib.cc
@@ -175,7 +175,9 @@
       send_connection_binding(false),
       tlsext_channel_id_valid(false),
       key_update_pending(false),
-      wpend_pending(false) {}
+      wpend_pending(false),
+      early_data_accepted(false),
+      draft_downgrade(false) {}
 
 SSL3_STATE::~SSL3_STATE() {}
 
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index e6518ba..e14d551 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -306,7 +306,7 @@
   if (type == SSL3_RT_HANDSHAKE) {
     // If reading 0-RTT data, reject handshake data. 0-RTT data is terminated
     // by an alert.
-    if (!ssl_is_draft21(ssl->version) && is_early_data_read) {
+    if (!ssl_is_draft22(ssl->version) && is_early_data_read) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
       *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
       return ssl_open_record_error;
@@ -335,7 +335,7 @@
   // Handle the end_of_early_data alert.
   static const uint8_t kEndOfEarlyData[2] = {SSL3_AL_WARNING,
                                              TLS1_AD_END_OF_EARLY_DATA};
-  if (!ssl_is_draft21(ssl->version) && is_early_data_read &&
+  if (!ssl_is_draft22(ssl->version) && is_early_data_read &&
       type == SSL3_RT_ALERT && body == kEndOfEarlyData) {
     // Stop accepting early data.
     ssl->s3->hs->can_early_read = false;
diff --git a/src/ssl/ssl_aead_ctx.cc b/src/ssl/ssl_aead_ctx.cc
index 775827c..247e889 100644
--- a/src/ssl/ssl_aead_ctx.cc
+++ b/src/ssl/ssl_aead_ctx.cc
@@ -173,10 +173,7 @@
     return version_;
   }
 
-  if (ssl_is_resumption_record_version_experiment(version_)) {
-    return TLS1_2_VERSION;
-  }
-  return TLS1_VERSION;
+  return TLS1_2_VERSION;
 }
 
 size_t SSLAEADContext::ExplicitNonceLen() const {
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index 6da081e..5e420e8 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -1187,7 +1187,7 @@
 }
 
 int SSL_early_data_accepted(const SSL *ssl) {
-  return ssl->early_data_accepted;
+  return ssl->s3->early_data_accepted;
 }
 
 void SSL_reset_early_data_reject(SSL *ssl) {
@@ -2571,6 +2571,8 @@
   ctx->false_start_allowed_without_alpn = !!allowed;
 }
 
+int SSL_is_draft_downgrade(const SSL *ssl) { return ssl->s3->draft_downgrade; }
+
 int SSL_clear(SSL *ssl) {
   // In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
   // established session to be offered the next time around. wpa_supplicant
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 8288878..5d37448 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -2617,7 +2617,8 @@
   EXPECT_EQ(TLS1_3_VERSION, ctx->conf_max_version);
 
   // TLS1_3_DRAFT_VERSION is not an API-level version.
-  EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_DRAFT_VERSION));
+  EXPECT_FALSE(
+      SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_DRAFT22_VERSION));
   ERR_clear_error();
 
   ctx.reset(SSL_CTX_new(DTLS_method()));
@@ -2960,9 +2961,7 @@
       uint16_t record_version, length;
       ASSERT_TRUE(CBS_get_u8(&cbs, &type));
       ASSERT_TRUE(CBS_get_u16(&cbs, &record_version));
-      EXPECT_TRUE(record_version == version() ||
-                  record_version == (is_dtls() ? DTLS1_VERSION : TLS1_VERSION))
-          << "Invalid record version: " << record_version;
+      EXPECT_EQ(record_version & 0xff00, version() & 0xff00);
       if (is_dtls()) {
         uint16_t epoch;
         ASSERT_TRUE(CBS_get_u16(&cbs, &epoch));
@@ -3862,7 +3861,7 @@
       !TestPaddingExtension(TLS1_3_VERSION, TLS1_2_VERSION) ||
       // Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there
       // will be a PSK binder after the padding extension.
-      !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_DRAFT_VERSION)) {
+      !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_DRAFT22_VERSION)) {
     ADD_FAILURE() << "Tests failed";
   }
 }
diff --git a/src/ssl/ssl_versions.cc b/src/ssl/ssl_versions.cc
index 2406bd8..4ef54da 100644
--- a/src/ssl/ssl_versions.cc
+++ b/src/ssl/ssl_versions.cc
@@ -34,12 +34,8 @@
       *out = version;
       return true;
 
-    case TLS1_3_DRAFT_VERSION:
-    case TLS1_3_DRAFT21_VERSION:
     case TLS1_3_DRAFT22_VERSION:
-    case TLS1_3_EXPERIMENT_VERSION:
     case TLS1_3_EXPERIMENT2_VERSION:
-    case TLS1_3_EXPERIMENT3_VERSION:
       *out = TLS1_3_VERSION;
       return true;
 
@@ -62,11 +58,7 @@
 
 static const uint16_t kTLSVersions[] = {
     TLS1_3_DRAFT22_VERSION,
-    TLS1_3_EXPERIMENT3_VERSION,
     TLS1_3_EXPERIMENT2_VERSION,
-    TLS1_3_EXPERIMENT_VERSION,
-    TLS1_3_DRAFT_VERSION,
-    TLS1_3_DRAFT21_VERSION,
     TLS1_2_VERSION,
     TLS1_1_VERSION,
     TLS1_VERSION,
@@ -109,12 +101,8 @@
 
 static const char *ssl_version_to_string(uint16_t version) {
   switch (version) {
-    case TLS1_3_DRAFT_VERSION:
-    case TLS1_3_DRAFT21_VERSION:
     case TLS1_3_DRAFT22_VERSION:
-    case TLS1_3_EXPERIMENT_VERSION:
     case TLS1_3_EXPERIMENT2_VERSION:
-    case TLS1_3_EXPERIMENT3_VERSION:
       return "TLSv1.3";
 
     case TLS1_2_VERSION:
@@ -143,12 +131,8 @@
 static uint16_t wire_version_to_api(uint16_t version) {
   switch (version) {
     // Report TLS 1.3 draft versions as TLS 1.3 in the public API.
-    case TLS1_3_DRAFT_VERSION:
-    case TLS1_3_DRAFT21_VERSION:
     case TLS1_3_DRAFT22_VERSION:
-    case TLS1_3_EXPERIMENT_VERSION:
     case TLS1_3_EXPERIMENT2_VERSION:
-    case TLS1_3_EXPERIMENT3_VERSION:
       return TLS1_3_VERSION;
     default:
       return version;
@@ -159,16 +143,12 @@
 // particular, it picks an arbitrary TLS 1.3 representative. This should only be
 // used in context where that does not matter.
 static bool api_version_to_wire(uint16_t *out, uint16_t version) {
-  if (version == TLS1_3_DRAFT_VERSION ||
-      version == TLS1_3_DRAFT21_VERSION ||
-      version == TLS1_3_DRAFT22_VERSION ||
-      version == TLS1_3_EXPERIMENT_VERSION ||
-      version == TLS1_3_EXPERIMENT2_VERSION ||
-      version == TLS1_3_EXPERIMENT3_VERSION) {
+  if (version == TLS1_3_DRAFT22_VERSION ||
+      version == TLS1_3_EXPERIMENT2_VERSION) {
     return false;
   }
   if (version == TLS1_3_VERSION) {
-    version = TLS1_3_DRAFT_VERSION;
+    version = TLS1_3_DRAFT22_VERSION;
   }
 
   // Check it is a real protocol version.
@@ -321,32 +301,16 @@
 
   // TLS 1.3 variants must additionally match |tls13_variant|.
   if (protocol_version != TLS1_3_VERSION ||
-      (ssl->tls13_variant == tls13_experiment &&
-       version == TLS1_3_EXPERIMENT_VERSION) ||
       (ssl->tls13_variant == tls13_experiment2 &&
        version == TLS1_3_EXPERIMENT2_VERSION) ||
-      (ssl->tls13_variant == tls13_experiment3 &&
-       version == TLS1_3_EXPERIMENT3_VERSION) ||
-      (ssl->tls13_variant == tls13_draft21 &&
-       version == TLS1_3_DRAFT21_VERSION) ||
-      (ssl->tls13_variant == tls13_draft22 &&
-       version == TLS1_3_DRAFT22_VERSION) ||
       (ssl->tls13_variant == tls13_default &&
-       version == TLS1_3_DRAFT_VERSION)) {
+       version == TLS1_3_DRAFT22_VERSION)) {
     return true;
   }
 
   // The server, when not configured at |tls13_default|, should additionally
-  // enable all variants, except draft-21 which is implemented solely for QUIC
-  // interop testing and will not be deployed, and draft-22 which will be
-  // enabled once the draft is finalized and ready to be deployed in Chrome.
-  // Currently, this is to implement the draft-18 vs. experiments field trials.
-  // In the future, this will be to transition cleanly to a final draft-22
-  // which hopefully includes the deployability fixes.
-  if (ssl->server &&
-      ssl->tls13_variant != tls13_default &&
-      version != TLS1_3_DRAFT21_VERSION &&
-      version != TLS1_3_DRAFT22_VERSION) {
+  // enable all variants.
+  if (ssl->server && ssl->tls13_variant != tls13_default) {
     return true;
   }
 
@@ -397,42 +361,10 @@
   return false;
 }
 
-bool ssl_is_draft21(uint16_t version) {
-  return version == TLS1_3_DRAFT21_VERSION || version == TLS1_3_DRAFT22_VERSION;
-}
-
 bool ssl_is_draft22(uint16_t version) {
   return version == TLS1_3_DRAFT22_VERSION;
 }
 
-bool ssl_is_resumption_experiment(uint16_t version) {
-  return version == TLS1_3_EXPERIMENT_VERSION ||
-         version == TLS1_3_EXPERIMENT2_VERSION ||
-         version == TLS1_3_EXPERIMENT3_VERSION ||
-         version == TLS1_3_DRAFT22_VERSION;
-}
-
-bool ssl_is_resumption_variant(uint16_t max_version,
-                               enum tls13_variant_t variant) {
-  if (max_version < TLS1_3_VERSION) {
-    return false;
-  }
-  return variant == tls13_experiment || variant == tls13_experiment2 ||
-         variant == tls13_experiment3 || variant == tls13_draft22;
-}
-
-bool ssl_is_resumption_client_ccs_experiment(uint16_t version) {
-  return version == TLS1_3_EXPERIMENT_VERSION ||
-         version == TLS1_3_EXPERIMENT2_VERSION ||
-         version == TLS1_3_DRAFT22_VERSION;
-}
-
-bool ssl_is_resumption_record_version_experiment(uint16_t version) {
-  return version == TLS1_3_EXPERIMENT2_VERSION ||
-         version == TLS1_3_EXPERIMENT3_VERSION ||
-         version == TLS1_3_DRAFT22_VERSION;
-}
-
 }  // namespace bssl
 
 using namespace bssl;
diff --git a/src/ssl/ssl_x509.cc b/src/ssl/ssl_x509.cc
index 2b7ba83..5c0365f 100644
--- a/src/ssl/ssl_x509.cc
+++ b/src/ssl/ssl_x509.cc
@@ -354,66 +354,6 @@
   session->x509_chain_without_leaf = NULL;
 }
 
-static int ssl_verify_alarm_type(long type) {
-  switch (type) {
-    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
-    case X509_V_ERR_UNABLE_TO_GET_CRL:
-    case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
-      return SSL_AD_UNKNOWN_CA;
-
-    case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
-    case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
-    case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
-    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
-    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
-    case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
-    case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
-    case X509_V_ERR_CERT_NOT_YET_VALID:
-    case X509_V_ERR_CRL_NOT_YET_VALID:
-    case X509_V_ERR_CERT_UNTRUSTED:
-    case X509_V_ERR_CERT_REJECTED:
-    case X509_V_ERR_HOSTNAME_MISMATCH:
-    case X509_V_ERR_EMAIL_MISMATCH:
-    case X509_V_ERR_IP_ADDRESS_MISMATCH:
-      return SSL_AD_BAD_CERTIFICATE;
-
-    case X509_V_ERR_CERT_SIGNATURE_FAILURE:
-    case X509_V_ERR_CRL_SIGNATURE_FAILURE:
-      return SSL_AD_DECRYPT_ERROR;
-
-    case X509_V_ERR_CERT_HAS_EXPIRED:
-    case X509_V_ERR_CRL_HAS_EXPIRED:
-      return SSL_AD_CERTIFICATE_EXPIRED;
-
-    case X509_V_ERR_CERT_REVOKED:
-      return SSL_AD_CERTIFICATE_REVOKED;
-
-    case X509_V_ERR_UNSPECIFIED:
-    case X509_V_ERR_OUT_OF_MEM:
-    case X509_V_ERR_INVALID_CALL:
-    case X509_V_ERR_STORE_LOOKUP:
-      return SSL_AD_INTERNAL_ERROR;
-
-    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
-    case X509_V_ERR_CERT_CHAIN_TOO_LONG:
-    case X509_V_ERR_PATH_LENGTH_EXCEEDED:
-    case X509_V_ERR_INVALID_CA:
-      return SSL_AD_UNKNOWN_CA;
-
-    case X509_V_ERR_APPLICATION_VERIFICATION:
-      return SSL_AD_HANDSHAKE_FAILURE;
-
-    case X509_V_ERR_INVALID_PURPOSE:
-      return SSL_AD_UNSUPPORTED_CERTIFICATE;
-
-    default:
-      return SSL_AD_CERTIFICATE_UNKNOWN;
-  }
-}
-
 static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session,
                                                      SSL *ssl,
                                                      uint8_t *out_alert) {
@@ -464,7 +404,7 @@
 
   // If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
   if (verify_ret <= 0 && ssl->verify_mode != SSL_VERIFY_NONE) {
-    *out_alert = ssl_verify_alarm_type(ctx->error);
+    *out_alert = SSL_alert_from_verify_result(ctx->error);
     return 0;
   }
 
@@ -1297,3 +1237,63 @@
   check_ssl_x509_method(ssl);
   return set_cert_store(&ssl->cert->verify_store, store, 1);
 }
+
+int SSL_alert_from_verify_result(long result) {
+  switch (result) {
+    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+    case X509_V_ERR_UNABLE_TO_GET_CRL:
+    case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+      return SSL_AD_UNKNOWN_CA;
+
+    case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+    case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+    case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+    case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+    case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+    case X509_V_ERR_CERT_NOT_YET_VALID:
+    case X509_V_ERR_CRL_NOT_YET_VALID:
+    case X509_V_ERR_CERT_UNTRUSTED:
+    case X509_V_ERR_CERT_REJECTED:
+    case X509_V_ERR_HOSTNAME_MISMATCH:
+    case X509_V_ERR_EMAIL_MISMATCH:
+    case X509_V_ERR_IP_ADDRESS_MISMATCH:
+      return SSL_AD_BAD_CERTIFICATE;
+
+    case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+    case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+      return SSL_AD_DECRYPT_ERROR;
+
+    case X509_V_ERR_CERT_HAS_EXPIRED:
+    case X509_V_ERR_CRL_HAS_EXPIRED:
+      return SSL_AD_CERTIFICATE_EXPIRED;
+
+    case X509_V_ERR_CERT_REVOKED:
+      return SSL_AD_CERTIFICATE_REVOKED;
+
+    case X509_V_ERR_UNSPECIFIED:
+    case X509_V_ERR_OUT_OF_MEM:
+    case X509_V_ERR_INVALID_CALL:
+    case X509_V_ERR_STORE_LOOKUP:
+      return SSL_AD_INTERNAL_ERROR;
+
+    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+    case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+    case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+    case X509_V_ERR_INVALID_CA:
+      return SSL_AD_UNKNOWN_CA;
+
+    case X509_V_ERR_APPLICATION_VERIFICATION:
+      return SSL_AD_HANDSHAKE_FAILURE;
+
+    case X509_V_ERR_INVALID_PURPOSE:
+      return SSL_AD_UNSUPPORTED_CERTIFICATE;
+
+    default:
+      return SSL_AD_CERTIFICATE_UNKNOWN;
+  }
+}
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 8d03623..814cce1 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -1810,7 +1810,7 @@
   // selected cipher in HelloRetryRequest does not match. This avoids performing
   // the transcript hash transformation for multiple hashes.
   if (hs->received_hello_retry_request &&
-      ssl_is_draft21(ssl->version) &&
+      ssl_is_draft22(ssl->version) &&
       ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) {
     return true;
   }
@@ -2033,7 +2033,7 @@
     return false;
   }
 
-  ssl->early_data_accepted = true;
+  ssl->s3->early_data_accepted = true;
   return true;
 }
 
@@ -2055,7 +2055,7 @@
 }
 
 static bool ext_early_data_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
-  if (!hs->ssl->early_data_accepted) {
+  if (!hs->ssl->s3->early_data_accepted) {
     return true;
   }
 
@@ -3264,6 +3264,7 @@
   int sig_ok = ECDSA_do_verify(digest, digest_len, sig.get(), key.get());
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   sig_ok = 1;
+  ERR_clear_error();
 #endif
   if (!sig_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 0b4c760..38e4077 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1808,6 +1808,11 @@
     return false;
   }
 
+  if (config->expect_draft_downgrade != !!SSL_is_draft_downgrade(ssl)) {
+    fprintf(stderr, "Got %sdraft downgrade signal, but wanted the opposite.\n",
+            SSL_is_draft_downgrade(ssl) ? "" : "no ");
+  }
+
   return true;
 }
 
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index c6d5c65..51effed 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -33,30 +33,18 @@
 
 // A draft version of TLS 1.3 that is sent over the wire for the current draft.
 const (
-	tls13DraftVersion       = 0x7f12
-	tls13Draft21Version     = 0x7f15
-	tls13ExperimentVersion  = 0x7e01
 	tls13Experiment2Version = 0x7e02
-	tls13Experiment3Version = 0x7e03
 	tls13Draft22Version     = 0x7f16
 )
 
 const (
-	TLS13Default     = 0
-	TLS13Experiment  = 1
-	TLS13Experiment2 = 2
-	TLS13Experiment3 = 3
-	TLS13Draft21     = 4
-	TLS13Draft22     = 5
+	TLS13Draft22     = 0
+	TLS13Experiment2 = 1
 )
 
 var allTLSWireVersions = []uint16{
-	tls13DraftVersion,
 	tls13Draft22Version,
-	tls13Draft21Version,
-	tls13Experiment3Version,
 	tls13Experiment2Version,
-	tls13ExperimentVersion,
 	VersionTLS12,
 	VersionTLS11,
 	VersionTLS10,
@@ -1525,6 +1513,14 @@
 	// PadClientHello, if non-zero, pads the ClientHello to a multiple of
 	// that many bytes.
 	PadClientHello int
+
+	// SendDraftTLS13DowngradeRandom, if true, causes the server to send the
+	// draft TLS 1.3 anti-downgrade signal.
+	SendDraftTLS13DowngradeRandom bool
+
+	// ExpectDraftTLS13DowngradeRandom, if true, causes the client to
+	// require the server send the draft TLS 1.3 anti-downgrade signal.
+	ExpectDraftTLS13DowngradeRandom bool
 }
 
 func (c *Config) serverInit() {
@@ -1637,7 +1633,7 @@
 		switch vers {
 		case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
 			return vers, true
-		case tls13DraftVersion, tls13Draft22Version, tls13Draft21Version, tls13ExperimentVersion, tls13Experiment2Version, tls13Experiment3Version:
+		case tls13Draft22Version, tls13Experiment2Version:
 			return VersionTLS13, true
 		}
 	}
@@ -1645,40 +1641,16 @@
 	return 0, false
 }
 
-func isDraft21(vers uint16) bool {
-	return vers == tls13Draft21Version || vers == tls13Draft22Version
-}
-
 func isDraft22(vers uint16) bool {
 	return vers == tls13Draft22Version
 }
 
-func isResumptionExperiment(vers uint16) bool {
-	return vers == tls13ExperimentVersion || vers == tls13Experiment2Version || vers == tls13Experiment3Version || vers == tls13Draft22Version
-}
-
-func isResumptionClientCCSExperiment(vers uint16) bool {
-	return vers == tls13ExperimentVersion || vers == tls13Experiment2Version || vers == tls13Draft22Version
-}
-
-func isResumptionRecordVersionExperiment(vers uint16) bool {
-	return vers == tls13Experiment2Version || vers == tls13Experiment3Version || vers == tls13Draft22Version
-}
-
-func isResumptionRecordVersionVariant(variant int) bool {
-	return variant == TLS13Experiment2 || variant == TLS13Experiment3 || variant == TLS13Draft22
-}
-
 // isSupportedVersion checks if the specified wire version is acceptable. If so,
 // it returns true and the corresponding protocol version. Otherwise, it returns
 // false.
 func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) {
-	if (c.TLS13Variant != TLS13Experiment && wireVers == tls13ExperimentVersion) ||
-		(c.TLS13Variant != TLS13Experiment2 && wireVers == tls13Experiment2Version) ||
-		(c.TLS13Variant != TLS13Experiment3 && wireVers == tls13Experiment3Version) ||
-		(c.TLS13Variant != TLS13Draft22 && wireVers == tls13Draft22Version) ||
-		(c.TLS13Variant != TLS13Draft21 && wireVers == tls13Draft21Version) ||
-		(c.TLS13Variant != TLS13Default && wireVers == tls13DraftVersion) {
+	if (c.TLS13Variant != TLS13Experiment2 && wireVers == tls13Experiment2Version) ||
+		(c.TLS13Variant != TLS13Draft22 && wireVers == tls13Draft22Version) {
 		return 0, false
 	}
 
@@ -1975,6 +1947,8 @@
 	// See draft-ietf-tls-tls13-16, section 6.3.1.2.
 	downgradeTLS13 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01}
 	downgradeTLS12 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00}
+
+	downgradeTLS13Draft = []uint8{0x95, 0xb9, 0x9f, 0x87, 0x22, 0xfe, 0x9b, 0x64}
 )
 
 func containsGREASE(values []uint16) bool {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index c6ee443..6493aa7 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -802,9 +802,6 @@
 		if c.haveVers {
 			expect = c.vers
 			if c.vers >= VersionTLS13 {
-				expect = VersionTLS10
-			}
-			if isResumptionRecordVersionExperiment(c.wireVersion) {
 				expect = VersionTLS12
 			}
 		} else {
@@ -907,7 +904,7 @@
 
 	// Check they match that we expect.
 	expected := [6]byte{byte(recordTypeChangeCipherSpec), 3, 1, 0, 1, 1}
-	if isResumptionRecordVersionExperiment(c.wireVersion) {
+	if c.vers >= VersionTLS13 {
 		expected[2] = 3
 	}
 	if !bytes.Equal(b.data[:6], expected[:]) {
@@ -1197,7 +1194,7 @@
 			}
 		}
 		vers := c.vers
-		if vers == 0 || vers >= VersionTLS13 {
+		if vers == 0 {
 			// Some TLS servers fail if the record version is
 			// greater than TLS 1.0 for the initial ClientHello.
 			//
@@ -1205,7 +1202,7 @@
 			// layer to {3, 1}.
 			vers = VersionTLS10
 		}
-		if isResumptionRecordVersionExperiment(c.wireVersion) || isResumptionRecordVersionExperiment(c.out.wireVersion) {
+		if c.vers >= VersionTLS13 || c.out.version >= VersionTLS13 {
 			vers = VersionTLS12
 		}
 
@@ -1240,7 +1237,7 @@
 	}
 	c.out.freeBlock(b)
 
-	if typ == recordTypeChangeCipherSpec && !isResumptionExperiment(c.wireVersion) {
+	if typ == recordTypeChangeCipherSpec && c.vers < VersionTLS13 {
 		err = c.out.changeCipherSpec(c.config)
 		if err != nil {
 			return n, c.sendAlertLocked(alertLevelError, err.(alert))
@@ -1563,7 +1560,7 @@
 		earlyALPN:          c.clientProtocol,
 	}
 
-	if isDraft21(c.wireVersion) {
+	if isDraft22(c.wireVersion) {
 		session.masterSecret = deriveSessionPSK(cipherSuite, c.wireVersion, c.resumptionSecret, newSessionTicket.ticketNonce)
 	}
 
@@ -1854,7 +1851,7 @@
 	if cipherSuite == nil {
 		cipherSuite = c.earlyCipherSuite
 	}
-	if isDraft21(c.wireVersion) {
+	if isDraft22(c.wireVersion) {
 		hash := cipherSuite.hash()
 		exporterKeyingLabel := []byte("exporter")
 		contextHash := hash.New()
@@ -1951,7 +1948,7 @@
 		maxEarlyDataSize:            c.config.MaxEarlyDataSize,
 	}
 
-	if isDraft21(c.wireVersion) {
+	if isDraft22(c.wireVersion) {
 		m.ticketNonce = nonce
 	}
 
@@ -1970,7 +1967,7 @@
 		earlyALPN:          []byte(c.clientProtocol),
 	}
 
-	if isDraft21(c.wireVersion) {
+	if isDraft22(c.wireVersion) {
 		state.masterSecret = deriveSessionPSK(c.cipherSuite, c.wireVersion, c.resumptionSecret, nonce)
 	}
 
@@ -2017,11 +2014,7 @@
 	payload := make([]byte, 5+len)
 	payload[0] = byte(recordTypeApplicationData)
 	payload[1] = 3
-	payload[2] = 1
-	if isResumptionRecordVersionVariant(c.config.TLS13Variant) {
-		payload[1] = 3
-		payload[2] = 3
-	}
+	payload[2] = 3
 	payload[3] = byte(len >> 8)
 	payload[4] = byte(len)
 	_, err := c.conn.Write(payload)
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 55d21c9..0f4a714 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -377,7 +377,7 @@
 			// set. Fill in an arbitrary TLS 1.3 version to compute
 			// the binder.
 			if session.vers < VersionTLS13 {
-				version = tls13DraftVersion
+				version = tls13Draft22Version
 			}
 			generatePSKBinders(version, hello, pskCipherSuite, session.masterSecret, []byte{}, []byte{}, c.config)
 		}
@@ -416,14 +416,16 @@
 
 		if !c.config.Bugs.SkipChangeCipherSpec && isDraft22(session.wireVersion) {
 			c.wireVersion = session.wireVersion
+			c.vers = VersionTLS13
 			c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 			c.wireVersion = 0
+			c.vers = 0
 		}
 
 		var earlyTrafficSecret []byte
-		if isDraft21(session.wireVersion) {
-			earlyTrafficSecret = finishedHash.deriveSecret(earlyTrafficLabelDraft21)
-			c.earlyExporterSecret = finishedHash.deriveSecret(earlyExporterLabelDraft21)
+		if isDraft22(session.wireVersion) {
+			earlyTrafficSecret = finishedHash.deriveSecret(earlyTrafficLabelDraft22)
+			c.earlyExporterSecret = finishedHash.deriveSecret(earlyExporterLabelDraft22)
 		} else {
 			earlyTrafficSecret = finishedHash.deriveSecret(earlyTrafficLabel)
 			c.earlyExporterSecret = finishedHash.deriveSecret(earlyExporterLabel)
@@ -598,6 +600,9 @@
 			return errors.New("tls: downgrade from TLS 1.2 detected")
 		}
 	}
+	if c.config.Bugs.ExpectDraftTLS13DowngradeRandom && !bytes.Equal(serverHello.random[len(serverHello.random)-8:], downgradeTLS13Draft) {
+		return errors.New("tls: server did not send draft TLS 1.3 anti-downgrade signal")
+	}
 
 	suite := mutualCipherSuite(hello.cipherSuites, serverHello.cipherSuite)
 	if suite == nil {
@@ -626,7 +631,7 @@
 
 	hs.writeHash(helloBytes, hs.c.sendHandshakeSeq-1)
 	if haveHelloRetryRequest {
-		if isDraft21(c.wireVersion) {
+		if isDraft22(c.wireVersion) {
 			err = hs.finishedHash.UpdateForHelloRetryRequest()
 			if err != nil {
 				return err
@@ -727,13 +732,13 @@
 func (hs *clientHandshakeState) doTLS13Handshake() error {
 	c := hs.c
 
-	if isResumptionExperiment(c.wireVersion) && !isDraft22(c.wireVersion) {
+	if !isDraft22(c.wireVersion) {
 		// Early versions of the middlebox hacks inserted
 		// ChangeCipherSpec differently on 0-RTT and 2-RTT handshakes.
 		c.expectTLS13ChangeCipherSpec = true
 	}
 
-	if isResumptionExperiment(c.wireVersion) && !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
+	if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
 		return errors.New("tls: session IDs did not match.")
 	}
 
@@ -791,9 +796,9 @@
 
 	clientLabel := clientHandshakeTrafficLabel
 	serverLabel := serverHandshakeTrafficLabel
-	if isDraft21(c.wireVersion) {
-		clientLabel = clientHandshakeTrafficLabelDraft21
-		serverLabel = serverHandshakeTrafficLabelDraft21
+	if isDraft22(c.wireVersion) {
+		clientLabel = clientHandshakeTrafficLabelDraft22
+		serverLabel = serverHandshakeTrafficLabelDraft22
 	}
 
 	// Derive handshake traffic keys and switch read key to handshake
@@ -939,10 +944,10 @@
 	clientLabel = clientApplicationTrafficLabel
 	serverLabel = serverApplicationTrafficLabel
 	exportLabel := exporterLabel
-	if isDraft21(c.wireVersion) {
-		clientLabel = clientApplicationTrafficLabelDraft21
-		serverLabel = serverApplicationTrafficLabelDraft21
-		exportLabel = exporterLabelDraft21
+	if isDraft22(c.wireVersion) {
+		clientLabel = clientApplicationTrafficLabelDraft22
+		serverLabel = serverApplicationTrafficLabelDraft22
+		exportLabel = exporterLabelDraft22
 	}
 
 	clientTrafficSecret := hs.finishedHash.deriveSecret(clientLabel)
@@ -991,7 +996,7 @@
 			helloRequest := new(helloRequestMsg)
 			c.writeRecord(recordTypeHandshake, helloRequest.marshal())
 		}
-		if isDraft21(c.wireVersion) {
+		if isDraft22(c.wireVersion) {
 			endOfEarlyData := new(endOfEarlyDataMsg)
 			endOfEarlyData.nonEmpty = c.config.Bugs.NonEmptyEndOfEarlyData
 			c.writeRecord(recordTypeHandshake, endOfEarlyData.marshal())
@@ -1001,7 +1006,7 @@
 		}
 	}
 
-	if !c.config.Bugs.SkipChangeCipherSpec && isResumptionClientCCSExperiment(c.wireVersion) && !hs.hello.hasEarlyData {
+	if !c.config.Bugs.SkipChangeCipherSpec && !hs.hello.hasEarlyData {
 		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 	}
 
@@ -1098,8 +1103,8 @@
 	c.useOutTrafficSecret(c.wireVersion, hs.suite, clientTrafficSecret)
 
 	resumeLabel := resumptionLabel
-	if isDraft21(c.wireVersion) {
-		resumeLabel = resumptionLabelDraft21
+	if isDraft22(c.wireVersion) {
+		resumeLabel = resumptionLabelDraft22
 	}
 
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumeLabel)
@@ -1845,8 +1850,8 @@
 	binderSize := len(hello.pskBinders)*(binderLen+1) + 2
 	truncatedHello := helloBytes[:len(helloBytes)-binderSize]
 	binderLabel := resumptionPSKBinderLabel
-	if isDraft21(version) {
-		binderLabel = resumptionPSKBinderLabelDraft21
+	if isDraft22(version) {
+		binderLabel = resumptionPSKBinderLabelDraft22
 	}
 	binder := computePSKBinder(psk, version, binderLabel, pskCipherSuite, firstClientHello, helloRetryRequest, truncatedHello)
 	if config.Bugs.SendShortPSKBinder {
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 93d02e1..c4a6e16 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -896,21 +896,17 @@
 	}
 	if m.versOverride != 0 {
 		hello.addU16(m.versOverride)
-	} else if isResumptionExperiment(m.vers) {
+	} else if vers >= VersionTLS13 {
 		hello.addU16(VersionTLS12)
 	} else {
 		hello.addU16(m.vers)
 	}
 
 	hello.addBytes(m.random)
-	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
-		sessionId := hello.addU8LengthPrefixed()
-		sessionId.addBytes(m.sessionId)
-	}
+	sessionId := hello.addU8LengthPrefixed()
+	sessionId.addBytes(m.sessionId)
 	hello.addU16(m.cipherSuite)
-	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
-		hello.addU8(m.compressionMethod)
-	}
+	hello.addU8(m.compressionMethod)
 
 	extensions := hello.addU16LengthPrefixed()
 
@@ -927,14 +923,12 @@
 			extensions.addU16(2) // Length
 			extensions.addU16(m.pskIdentity)
 		}
-		if isResumptionExperiment(m.vers) || m.supportedVersOverride != 0 {
-			extensions.addU16(extensionSupportedVersions)
-			extensions.addU16(2) // Length
-			if m.supportedVersOverride != 0 {
-				extensions.addU16(m.supportedVersOverride)
-			} else {
-				extensions.addU16(m.vers)
-			}
+		extensions.addU16(extensionSupportedVersions)
+		extensions.addU16(2) // Length
+		if m.supportedVersOverride != 0 {
+			extensions.addU16(m.supportedVersOverride)
+		} else {
+			extensions.addU16(m.vers)
 		}
 		if len(m.customExtension) > 0 {
 			extensions.addU16(extensionCustom)
@@ -980,19 +974,11 @@
 	if !ok {
 		return false
 	}
-	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
-		if !reader.readU8LengthPrefixedBytes(&m.sessionId) {
-			return false
-		}
-	}
-	if !reader.readU16(&m.cipherSuite) {
+	if !reader.readU8LengthPrefixedBytes(&m.sessionId) ||
+		!reader.readU16(&m.cipherSuite) ||
+		!reader.readU8(&m.compressionMethod) {
 		return false
 	}
-	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
-		if !reader.readU8(&m.compressionMethod) {
-			return false
-		}
-	}
 
 	if len(reader) == 0 && m.vers < VersionTLS13 {
 		// Extension data is optional before TLS 1.3.
@@ -1052,9 +1038,7 @@
 				}
 				m.hasPSKIdentity = true
 			case extensionSupportedVersions:
-				if !isResumptionExperiment(m.vers) {
-					return false
-				}
+				// Parsed above.
 			default:
 				// Only allow the 3 extensions that are sent in
 				// the clear in TLS 1.3.
@@ -1386,7 +1370,7 @@
 		retryRequest.addU8(m.compressionMethod)
 	} else {
 		retryRequest.addU16(m.vers)
-		if isDraft21(m.vers) {
+		if isDraft22(m.vers) {
 			retryRequest.addU16(m.cipherSuite)
 		}
 	}
@@ -1440,7 +1424,7 @@
 			compressionMethod != 0 {
 			return false
 		}
-	} else if isDraft21(m.vers) && !reader.readU16(&m.cipherSuite) {
+	} else if isDraft22(m.vers) && !reader.readU16(&m.cipherSuite) {
 		return false
 	}
 	var extensions byteReader
@@ -1806,7 +1790,7 @@
 		requestContext := body.addU8LengthPrefixed()
 		requestContext.addBytes(m.requestContext)
 		extensions := newByteBuilder()
-		if isDraft21(m.vers) {
+		if isDraft22(m.vers) {
 			extensions = body.addU16LengthPrefixed()
 			if m.hasSignatureAlgorithm {
 				extensions.addU16(extensionSignatureAlgorithms)
@@ -1884,7 +1868,7 @@
 	m.raw = data
 	reader := byteReader(data[4:])
 
-	if isDraft21(m.vers) {
+	if isDraft22(m.vers) {
 		var extensions byteReader
 		if !reader.readU8LengthPrefixedBytes(&m.requestContext) ||
 			!reader.readU16LengthPrefixed(&extensions) ||
@@ -2037,7 +2021,7 @@
 	body.addU32(m.ticketLifetime)
 	if version >= VersionTLS13 {
 		body.addU32(m.ticketAgeAdd)
-		if isDraft21(m.vers) {
+		if isDraft22(m.vers) {
 			body.addU8LengthPrefixed().addBytes(m.ticketNonce)
 		}
 	}
@@ -2049,7 +2033,7 @@
 		extensions := body.addU16LengthPrefixed()
 		if m.maxEarlyDataSize > 0 {
 			extID := extensionTicketEarlyDataInfo
-			if isDraft21(m.vers) {
+			if isDraft22(m.vers) {
 				extID = extensionEarlyData
 			}
 			extensions.addU16(extID)
@@ -2089,7 +2073,7 @@
 		}
 		m.ticketAgeAdd = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
 		data = data[4:]
-		if isDraft21(m.vers) {
+		if isDraft22(m.vers) {
 			nonceLen := int(data[0])
 			data = data[1:]
 			if len(data) < nonceLen {
@@ -2128,7 +2112,7 @@
 		}
 
 		extID := extensionTicketEarlyDataInfo
-		if isDraft21(m.vers) {
+		if isDraft22(m.vers) {
 			extID = extensionEarlyData
 		}
 
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 9ba6c2c..6b7daee 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -281,7 +281,7 @@
 	}
 
 	if config.Bugs.ExpectNoTLS12Session {
-		if len(hs.clientHello.sessionId) > 0 && !isResumptionExperiment(c.wireVersion) {
+		if len(hs.clientHello.sessionId) > 0 && c.vers >= VersionTLS13 {
 			return fmt.Errorf("tls: client offered an unexpected session ID")
 		}
 		if len(hs.clientHello.sessionTicket) > 0 {
@@ -585,7 +585,7 @@
 	}
 
 	if sendHelloRetryRequest {
-		if isDraft21(c.wireVersion) {
+		if isDraft22(c.wireVersion) {
 			if err := hs.finishedHash.UpdateForHelloRetryRequest(); err != nil {
 				return err
 			}
@@ -654,7 +654,7 @@
 
 		// PSK binders and obfuscated ticket age are both updated in the
 		// second ClientHello.
-		if isDraft21(c.wireVersion) && len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) {
+		if isDraft22(c.wireVersion) && len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) {
 			newClientHelloCopy.pskIdentities = oldClientHelloCopy.pskIdentities
 		} else {
 			if len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) {
@@ -695,9 +695,9 @@
 		}
 		if encryptedExtensions.extensions.hasEarlyData {
 			var earlyTrafficSecret []byte
-			if isDraft21(c.wireVersion) {
-				earlyTrafficSecret = hs.finishedHash.deriveSecret(earlyTrafficLabelDraft21)
-				c.earlyExporterSecret = hs.finishedHash.deriveSecret(earlyExporterLabelDraft21)
+			if isDraft22(c.wireVersion) {
+				earlyTrafficSecret = hs.finishedHash.deriveSecret(earlyTrafficLabelDraft22)
+				c.earlyExporterSecret = hs.finishedHash.deriveSecret(earlyExporterLabelDraft22)
 			} else {
 				earlyTrafficSecret = hs.finishedHash.deriveSecret(earlyTrafficLabel)
 				c.earlyExporterSecret = hs.finishedHash.deriveSecret(earlyExporterLabel)
@@ -809,7 +809,7 @@
 	}
 	c.flushHandshake()
 
-	if !c.config.Bugs.SkipChangeCipherSpec && isResumptionExperiment(c.wireVersion) && !sendHelloRetryRequest {
+	if !c.config.Bugs.SkipChangeCipherSpec && !sendHelloRetryRequest {
 		c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
 	}
 
@@ -819,9 +819,9 @@
 
 	clientLabel := clientHandshakeTrafficLabel
 	serverLabel := serverHandshakeTrafficLabel
-	if isDraft21(c.wireVersion) {
-		clientLabel = clientHandshakeTrafficLabelDraft21
-		serverLabel = serverHandshakeTrafficLabelDraft21
+	if isDraft22(c.wireVersion) {
+		clientLabel = clientHandshakeTrafficLabelDraft22
+		serverLabel = serverHandshakeTrafficLabelDraft22
 	}
 
 	// Switch to handshake traffic keys.
@@ -968,10 +968,10 @@
 	clientLabel = clientApplicationTrafficLabel
 	serverLabel = serverApplicationTrafficLabel
 	exportLabel := exporterLabel
-	if isDraft21(c.wireVersion) {
-		clientLabel = clientApplicationTrafficLabelDraft21
-		serverLabel = serverApplicationTrafficLabelDraft21
-		exportLabel = exporterLabelDraft21
+	if isDraft22(c.wireVersion) {
+		clientLabel = clientApplicationTrafficLabelDraft22
+		serverLabel = serverApplicationTrafficLabelDraft22
+		exportLabel = exporterLabelDraft22
 	}
 
 	clientTrafficSecret := hs.finishedHash.deriveSecret(clientLabel)
@@ -991,7 +991,7 @@
 
 	// Read end_of_early_data.
 	if encryptedExtensions.extensions.hasEarlyData {
-		if isDraft21(c.wireVersion) {
+		if isDraft22(c.wireVersion) {
 			msg, err := c.readHandshake()
 			if err != nil {
 				return err
@@ -1012,7 +1012,7 @@
 			}
 		}
 	}
-	if isResumptionClientCCSExperiment(c.wireVersion) && !isDraft22(c.wireVersion) && !hs.clientHello.hasEarlyData {
+	if !isDraft22(c.wireVersion) && !hs.clientHello.hasEarlyData {
 		// Early versions of the middlebox hacks inserted
 		// ChangeCipherSpec differently on 0-RTT and 2-RTT handshakes.
 		c.expectTLS13ChangeCipherSpec = true
@@ -1132,8 +1132,8 @@
 	c.cipherSuite = hs.suite
 
 	resumeLabel := resumptionLabel
-	if isDraft21(c.wireVersion) {
-		resumeLabel = resumptionLabelDraft21
+	if isDraft22(c.wireVersion) {
+		resumeLabel = resumptionLabelDraft22
 	}
 
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumeLabel)
@@ -1181,6 +1181,9 @@
 	if c.vers <= VersionTLS11 && config.maxVersion(c.isDTLS) == VersionTLS12 {
 		copy(hs.hello.random[len(hs.hello.random)-8:], downgradeTLS12)
 	}
+	if config.Bugs.SendDraftTLS13DowngradeRandom {
+		copy(hs.hello.random[len(hs.hello.random)-8:], downgradeTLS13Draft)
+	}
 
 	if len(hs.clientHello.sessionId) == 0 && c.config.Bugs.ExpectClientHelloSessionID {
 		return false, errors.New("tls: expected non-empty session ID from client")
@@ -2135,8 +2138,8 @@
 	}
 
 	binderLabel := resumptionPSKBinderLabel
-	if isDraft21(version) {
-		binderLabel = resumptionPSKBinderLabelDraft21
+	if isDraft22(version) {
+		binderLabel = resumptionPSKBinderLabelDraft22
 	}
 	binder := computePSKBinder(sessionState.masterSecret, version, binderLabel, pskCipherSuite, firstClientHello, helloRetryRequest, truncatedHello)
 	if !bytes.Equal(binder, binderToVerify) {
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index 54e18cb..62c98b7 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -396,7 +396,7 @@
 }
 
 func (h *finishedHash) nextSecret() {
-	if isDraft21(h.wireVersion) {
+	if isDraft22(h.wireVersion) {
 		derivedLabel := []byte("derived")
 		h.secret = hkdfExpandLabel(h.hash, h.wireVersion, h.secret, derivedLabel, h.hash.New().Sum(nil), h.hash.Size())
 	}
@@ -410,7 +410,7 @@
 	}
 
 	versionLabel := []byte("TLS 1.3, ")
-	if isDraft21(version) {
+	if isDraft22(version) {
 		versionLabel = []byte("tls13 ")
 	}
 
@@ -450,17 +450,17 @@
 	exporterLabel                 = []byte("exporter master secret")
 	resumptionLabel               = []byte("resumption master secret")
 
-	externalPSKBinderLabelDraft21        = []byte("ext binder")
-	resumptionPSKBinderLabelDraft21      = []byte("res binder")
-	earlyTrafficLabelDraft21             = []byte("c e traffic")
-	clientHandshakeTrafficLabelDraft21   = []byte("c hs traffic")
-	serverHandshakeTrafficLabelDraft21   = []byte("s hs traffic")
-	clientApplicationTrafficLabelDraft21 = []byte("c ap traffic")
-	serverApplicationTrafficLabelDraft21 = []byte("s ap traffic")
-	applicationTrafficLabelDraft21       = []byte("traffic upd")
-	earlyExporterLabelDraft21            = []byte("e exp master")
-	exporterLabelDraft21                 = []byte("exp master")
-	resumptionLabelDraft21               = []byte("res master")
+	externalPSKBinderLabelDraft22        = []byte("ext binder")
+	resumptionPSKBinderLabelDraft22      = []byte("res binder")
+	earlyTrafficLabelDraft22             = []byte("c e traffic")
+	clientHandshakeTrafficLabelDraft22   = []byte("c hs traffic")
+	serverHandshakeTrafficLabelDraft22   = []byte("s hs traffic")
+	clientApplicationTrafficLabelDraft22 = []byte("c ap traffic")
+	serverApplicationTrafficLabelDraft22 = []byte("s ap traffic")
+	applicationTrafficLabelDraft22       = []byte("traffic upd")
+	earlyExporterLabelDraft22            = []byte("e exp master")
+	exporterLabelDraft22                 = []byte("exp master")
+	resumptionLabelDraft22               = []byte("res master")
 
 	resumptionPSKLabel = []byte("resumption")
 )
@@ -515,8 +515,8 @@
 
 func updateTrafficSecret(hash crypto.Hash, version uint16, secret []byte) []byte {
 	trafficLabel := applicationTrafficLabel
-	if isDraft21(version) {
-		trafficLabel = applicationTrafficLabelDraft21
+	if isDraft22(version) {
+		trafficLabel = applicationTrafficLabelDraft22
 	}
 	return hkdfExpandLabel(hash, version, secret, trafficLabel, nil, hash.Size())
 }
@@ -526,7 +526,7 @@
 	finishedHash.addEntropy(psk)
 	binderKey := finishedHash.deriveSecret(label)
 	finishedHash.Write(clientHello)
-	if isDraft21(version) && len(helloRetryRequest) != 0 {
+	if isDraft22(version) && len(helloRetryRequest) != 0 {
 		finishedHash.UpdateForHelloRetryRequest()
 	}
 	finishedHash.Write(helloRetryRequest)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 4cfce26..7506280 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -1321,20 +1321,6 @@
 		versionDTLS: VersionDTLS12,
 	},
 	{
-		name:         "TLS13",
-		version:      VersionTLS13,
-		excludeFlag:  "-no-tls13",
-		versionWire:  tls13DraftVersion,
-		tls13Variant: TLS13Default,
-	},
-	{
-		name:         "TLS13Draft21",
-		version:      VersionTLS13,
-		excludeFlag:  "-no-tls13",
-		versionWire:  tls13Draft21Version,
-		tls13Variant: TLS13Draft21,
-	},
-	{
 		name:         "TLS13Draft22",
 		version:      VersionTLS13,
 		excludeFlag:  "-no-tls13",
@@ -1342,26 +1328,12 @@
 		tls13Variant: TLS13Draft22,
 	},
 	{
-		name:         "TLS13Experiment",
-		version:      VersionTLS13,
-		excludeFlag:  "-no-tls13",
-		versionWire:  tls13ExperimentVersion,
-		tls13Variant: TLS13Experiment,
-	},
-	{
 		name:         "TLS13Experiment2",
 		version:      VersionTLS13,
 		excludeFlag:  "-no-tls13",
 		versionWire:  tls13Experiment2Version,
 		tls13Variant: TLS13Experiment2,
 	},
-	{
-		name:         "TLS13Experiment3",
-		version:      VersionTLS13,
-		excludeFlag:  "-no-tls13",
-		versionWire:  tls13Experiment3Version,
-		tls13Variant: TLS13Experiment3,
-	},
 }
 
 func allVersions(protocol protocol) []tlsVersion {
@@ -3923,7 +3895,7 @@
 	// Test that an empty client CA list doesn't send a CA extension.
 	testCases = append(testCases, testCase{
 		testType: serverTest,
-		name:     "TLS13Draft21-Empty-Client-CA-List",
+		name:     "TLS13Draft22-Empty-Client-CA-List",
 		config: Config{
 			MaxVersion:   VersionTLS13,
 			Certificates: []Certificate{rsaCertificate},
@@ -3931,7 +3903,7 @@
 				ExpectNoCertificateAuthoritiesExtension: true,
 			},
 		},
-		tls13Variant: TLS13Draft21,
+		tls13Variant: TLS13Draft22,
 		flags: []string{
 			"-require-any-client-certificate",
 			"-use-client-ca-list", "<EMPTY>",
@@ -5334,9 +5306,8 @@
 				expectedClientVersion := expectedVersion
 				if expectedVersion == VersionTLS13 && runnerVers.tls13Variant != shimVers.tls13Variant {
 					expectedClientVersion = VersionTLS12
-					expectedServerVersion = VersionTLS12
-					if shimVers.tls13Variant != TLS13Default && runnerVers.tls13Variant != TLS13Draft21 && runnerVers.tls13Variant != TLS13Draft22 {
-						expectedServerVersion = VersionTLS13
+					if shimVers.tls13Variant == TLS13Draft22 {
+						expectedServerVersion = VersionTLS12
 					}
 				}
 
@@ -5353,10 +5324,7 @@
 				clientVers = recordVersionToWire(clientVers, protocol)
 				serverVers := expectedServerVersion
 				if expectedServerVersion >= VersionTLS13 {
-					serverVers = VersionTLS10
-					if runnerVers.tls13Variant == TLS13Experiment2 || runnerVers.tls13Variant == TLS13Experiment3 || runnerVers.tls13Variant == TLS13Draft22 {
-						serverVers = VersionTLS12
-					}
+					serverVers = VersionTLS12
 				}
 				serverVers = recordVersionToWire(serverVers, protocol)
 
@@ -5541,21 +5509,6 @@
 		expectedError: ":UNEXPECTED_EXTENSION:",
 	})
 
-	// Test that the non-experimental TLS 1.3 isn't negotiated by the
-	// supported_versions extension in the ServerHello.
-	testCases = append(testCases, testCase{
-		testType: clientTest,
-		name:     "SupportedVersionSelection-TLS13",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendServerSupportedExtensionVersion: tls13DraftVersion,
-			},
-		},
-		shouldFail:    true,
-		expectedError: ":UNEXPECTED_EXTENSION:",
-	})
-
 	// Test that the maximum version is selected regardless of the
 	// client-sent order.
 	testCases = append(testCases, testCase{
@@ -5563,7 +5516,7 @@
 		name:     "IgnoreClientVersionOrder",
 		config: Config{
 			Bugs: ProtocolBugs{
-				SendSupportedVersions: []uint16{VersionTLS12, tls13DraftVersion},
+				SendSupportedVersions: []uint16{VersionTLS12, tls13Draft22Version},
 			},
 		},
 		expectedVersion: VersionTLS13,
@@ -5694,6 +5647,27 @@
 		// TODO(davidben): This test should fail once TLS 1.3 is final
 		// and the fallback signal restored.
 	})
+
+	testCases = append(testCases, testCase{
+		name: "Draft-Downgrade-Client",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				SendDraftTLS13DowngradeRandom: true,
+			},
+		},
+		flags: []string{"-expect-draft-downgrade"},
+	})
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "Draft-Downgrade-Server",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				ExpectDraftTLS13DowngradeRandom: true,
+			},
+		},
+	})
 }
 
 func addMinimumVersionTests() {
@@ -6814,8 +6788,7 @@
 							MaxVersion:   sessionVers.version,
 							TLS13Variant: sessionVers.tls13Variant,
 							Bugs: ProtocolBugs{
-								ExpectNoTLS12Session: sessionVers.version >= VersionTLS13,
-								ExpectNoTLS13PSK:     sessionVers.version < VersionTLS13,
+								ExpectNoTLS13PSK: sessionVers.version < VersionTLS13,
 							},
 						},
 						expectedVersion:       sessionVers.version,
@@ -11380,19 +11353,14 @@
 			tls13Variant: variant,
 		})
 
-		hasSessionID := false
-		if variant != TLS13Default {
-			hasSessionID = true
-		}
-
-		// Test that the client sends a fake session ID in the correct experiments.
+		// Test that the client sends a fake session ID in TLS 1.3.
 		testCases = append(testCases, testCase{
 			testType: clientTest,
 			name:     "TLS13SessionID-" + name,
 			config: Config{
 				MaxVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
-					ExpectClientHelloSessionID: hasSessionID,
+					ExpectClientHelloSessionID: true,
 				},
 			},
 			tls13Variant: variant,
@@ -11709,7 +11677,7 @@
 			expectedError: ":WRONG_CURVE:",
 		})
 
-		if isDraft21(version.versionWire) {
+		if isDraft22(version.versionWire) {
 			testCases = append(testCases, testCase{
 				name: "HelloRetryRequest-CipherChange-" + name,
 				config: Config{
@@ -11996,7 +11964,7 @@
 			expectedError: ":DECODE_ERROR:",
 		})
 
-		if isDraft21(version.versionWire) {
+		if isDraft22(version.versionWire) {
 			testCases = append(testCases, testCase{
 				name: "UnknownExtensionInCertificateRequest-" + name,
 				config: Config{
@@ -12678,7 +12646,7 @@
 		})
 
 		expectedError := ":UNEXPECTED_RECORD:"
-		if isDraft21(version.versionWire) {
+		if isDraft22(version.versionWire) {
 			// In draft-21 and up, early data is expected to be
 			// terminated by a handshake message, though we test
 			// with the wrong one.
@@ -12780,7 +12748,7 @@
 			expectedLocalError: "remote error: error decrypting message",
 		})
 
-		if isDraft21(version.versionWire) {
+		if isDraft22(version.versionWire) {
 			testCases = append(testCases, testCase{
 				testType: serverTest,
 				name:     "Server-NonEmptyEndOfEarlyData-" + name,
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 579cf89..6744a00 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -130,6 +130,7 @@
   { "-use-custom-verify-callback", &TestConfig::use_custom_verify_callback },
   { "-allow-false-start-without-alpn",
     &TestConfig::allow_false_start_without_alpn },
+  { "-expect-draft-downgrade", &TestConfig::expect_draft_downgrade },
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 49b86ed..a459ae5 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -146,6 +146,7 @@
   bool use_custom_verify_callback = false;
   std::string expect_msg_callback;
   bool allow_false_start_without_alpn = false;
+  bool expect_draft_downgrade = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_initial,
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index 57acbcb..2a5a935 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -43,6 +43,14 @@
     0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c,
 };
 
+// This value was selected by truncating the SHA-256 hash of "Draft TLS 1.3
+// Downgrade" to 8 bytes:
+//
+//   echo -n 'Draft TLS 1.3 Downgrade' | sha256sum | head -c 16
+const uint8_t kDraftDowngradeRandom[8] = {0x95, 0xb9, 0x9f, 0x87,
+                                          0x22, 0xfe, 0x9b, 0x64};
+
+
 bool tls13_get_cert_verify_signature_input(
     SSL_HANDSHAKE *hs, Array<uint8_t> *out,
     enum ssl_cert_verify_context_t cert_verify_context) {
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index f471a4e..8d46d9f 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -104,7 +104,7 @@
     CBS body = msg.body;
     uint16_t server_version;
     if (!CBS_get_u16(&body, &server_version) ||
-        (ssl_is_draft21(ssl->version) &&
+        (ssl_is_draft22(ssl->version) &&
          !CBS_get_u16(&body, &cipher_suite)) ||
         !CBS_get_u16_length_prefixed(&body, &extensions) ||
         CBS_len(&body) != 0) {
@@ -114,7 +114,7 @@
     }
   }
 
-  if (ssl_is_draft21(ssl->version)) {
+  if (ssl_is_draft22(ssl->version)) {
     const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
     // Check if the cipher is a TLS 1.3 cipher.
     if (cipher == NULL ||
@@ -253,12 +253,11 @@
   uint8_t compression_method;
   if (!CBS_get_u16(&body, &server_version) ||
       !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
-      (ssl_is_resumption_experiment(ssl->version) &&
-       (!CBS_get_u8_length_prefixed(&body, &session_id) ||
-        !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len))) ||
+      !CBS_get_u8_length_prefixed(&body, &session_id) ||
+      !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
       !CBS_get_u16(&body, &cipher_suite) ||
-      (ssl_is_resumption_experiment(ssl->version) &&
-       (!CBS_get_u8(&body, &compression_method) || compression_method != 0)) ||
+      !CBS_get_u8(&body, &compression_method) ||
+      compression_method != 0 ||
       !CBS_get_u16_length_prefixed(&body, &extensions) ||
       CBS_len(&body) != 0) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -266,10 +265,7 @@
     return ssl_hs_error;
   }
 
-  uint16_t expected_version = ssl_is_resumption_experiment(ssl->version)
-                                  ? TLS1_2_VERSION
-                                  : ssl->version;
-  if (server_version != expected_version) {
+  if (server_version != TLS1_2_VERSION) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
     return ssl_hs_error;
@@ -297,7 +293,7 @@
   }
 
   // Check that the cipher matches the one in the HelloRetryRequest.
-  if (ssl_is_draft21(ssl->version) &&
+  if (ssl_is_draft22(ssl->version) &&
       hs->received_hello_retry_request &&
       hs->new_cipher != cipher) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
@@ -324,14 +320,6 @@
     return ssl_hs_error;
   }
 
-  // supported_versions is parsed in handshake_client to select the experimental
-  // TLS 1.3 version.
-  if (have_supported_versions && !ssl_is_resumption_experiment(ssl->version)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-    return ssl_hs_error;
-  }
-
   alert = SSL_AD_DECODE_ERROR;
   if (have_pre_shared_key) {
     if (ssl->session == NULL) {
@@ -426,8 +414,7 @@
   if (!hs->early_data_offered) {
     // Earlier versions of the resumption experiment added ChangeCipherSpec just
     // before the Finished flight.
-    if (ssl_is_resumption_client_ccs_experiment(ssl->version) &&
-        !ssl_is_draft22(ssl->version) &&
+    if (!ssl_is_draft22(ssl->version) &&
         !ssl->method->add_change_cipher_spec(ssl)) {
       return ssl_hs_error;
     }
@@ -477,7 +464,7 @@
     hs->new_session->early_alpn_len = ssl->s3->alpn_selected.size();
   }
 
-  if (ssl->early_data_accepted) {
+  if (ssl->s3->early_data_accepted) {
     if (hs->early_session->cipher != hs->new_session->cipher ||
         MakeConstSpan(hs->early_session->early_alpn,
                       hs->early_session->early_alpn_len) !=
@@ -497,7 +484,7 @@
 
   ssl->method->next_message(ssl);
   hs->tls13_state = state_read_certificate_request;
-  if (hs->in_early_data && !ssl->early_data_accepted) {
+  if (hs->in_early_data && !ssl->s3->early_data_accepted) {
     return ssl_hs_early_data_rejected;
   }
   return ssl_hs_ok;
@@ -523,7 +510,7 @@
   }
 
 
-  if (ssl_is_draft21(ssl->version)) {
+  if (ssl_is_draft22(ssl->version)) {
     bool have_sigalgs = false, have_ca = false;
     CBS sigalgs, ca;
     const SSL_EXTENSION_TYPE ext_types[] = {
@@ -676,9 +663,9 @@
 static enum ssl_hs_wait_t do_send_end_of_early_data(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
-  if (ssl->early_data_accepted) {
+  if (ssl->s3->early_data_accepted) {
     hs->can_early_write = false;
-    if (ssl_is_draft21(ssl->version)) {
+    if (ssl_is_draft22(ssl->version)) {
       ScopedCBB cbb;
       CBB body;
       if (!ssl->method->init_message(ssl, cbb.get(), &body,
@@ -917,7 +904,7 @@
   CBS body = msg.body, ticket_nonce, ticket, extensions;
   if (!CBS_get_u32(&body, &server_timeout) ||
       !CBS_get_u32(&body, &session->ticket_age_add) ||
-      (ssl_is_draft21(ssl->version) &&
+      (ssl_is_draft22(ssl->version) &&
        !CBS_get_u8_length_prefixed(&body, &ticket_nonce)) ||
       !CBS_get_u16_length_prefixed(&body, &ticket) ||
       !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
@@ -941,7 +928,7 @@
   // Parse out the extensions.
   bool have_early_data_info = false;
   CBS early_data_info;
-  uint16_t ext_id = ssl_is_draft21(ssl->version)
+  uint16_t ext_id = ssl_is_draft22(ssl->version)
                         ? TLSEXT_TYPE_early_data
                         : TLSEXT_TYPE_ticket_early_data_info;
   const SSL_EXTENSION_TYPE ext_types[] = {
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index 9dcd071..1bf820e 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -72,7 +72,7 @@
                              size_t label_len, const uint8_t *hash,
                              size_t hash_len, size_t len) {
   const char *kTLS13LabelVersion =
-      ssl_is_draft21(version) ? "tls13 " : "TLS 1.3, ";
+      ssl_is_draft22(version) ? "tls13 " : "TLS 1.3, ";
 
   ScopedCBB cbb;
   CBB child;
@@ -104,7 +104,7 @@
   SSL *const ssl = hs->ssl;
 
   // Draft 18 does not include the extra Derive-Secret step.
-  if (ssl_is_draft21(ssl->version)) {
+  if (ssl_is_draft22(ssl->version)) {
     uint8_t derive_context[EVP_MAX_MD_SIZE];
     unsigned derive_context_len;
     if (!EVP_Digest(nullptr, 0, derive_context, &derive_context_len,
@@ -224,24 +224,24 @@
 static const char kTLS13LabelServerApplicationTraffic[] =
     "server application traffic secret";
 
-static const char kTLS13Draft21LabelExporter[] = "exp master";
-static const char kTLS13Draft21LabelEarlyExporter[] = "e exp master";
+static const char kTLS13Draft22LabelExporter[] = "exp master";
+static const char kTLS13Draft22LabelEarlyExporter[] = "e exp master";
 
-static const char kTLS13Draft21LabelClientEarlyTraffic[] = "c e traffic";
-static const char kTLS13Draft21LabelClientHandshakeTraffic[] = "c hs traffic";
-static const char kTLS13Draft21LabelServerHandshakeTraffic[] = "s hs traffic";
-static const char kTLS13Draft21LabelClientApplicationTraffic[] = "c ap traffic";
-static const char kTLS13Draft21LabelServerApplicationTraffic[] = "s ap traffic";
+static const char kTLS13Draft22LabelClientEarlyTraffic[] = "c e traffic";
+static const char kTLS13Draft22LabelClientHandshakeTraffic[] = "c hs traffic";
+static const char kTLS13Draft22LabelServerHandshakeTraffic[] = "s hs traffic";
+static const char kTLS13Draft22LabelClientApplicationTraffic[] = "c ap traffic";
+static const char kTLS13Draft22LabelServerApplicationTraffic[] = "s ap traffic";
 
 int tls13_derive_early_secrets(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   uint16_t version = SSL_get_session(ssl)->ssl_version;
 
-  const char *early_traffic_label = ssl_is_draft21(version)
-                                        ? kTLS13Draft21LabelClientEarlyTraffic
+  const char *early_traffic_label = ssl_is_draft22(version)
+                                        ? kTLS13Draft22LabelClientEarlyTraffic
                                         : kTLS13LabelClientEarlyTraffic;
-  const char *early_exporter_label = ssl_is_draft21(version)
-                                         ? kTLS13Draft21LabelEarlyExporter
+  const char *early_exporter_label = ssl_is_draft22(version)
+                                         ? kTLS13Draft22LabelEarlyExporter
                                          : kTLS13LabelEarlyExporter;
   if (!derive_secret(hs, hs->early_traffic_secret, hs->hash_len,
                      early_traffic_label, strlen(early_traffic_label)) ||
@@ -257,11 +257,11 @@
 
 int tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  const char *client_label = ssl_is_draft21(ssl->version)
-                                 ? kTLS13Draft21LabelClientHandshakeTraffic
+  const char *client_label = ssl_is_draft22(ssl->version)
+                                 ? kTLS13Draft22LabelClientHandshakeTraffic
                                  : kTLS13LabelClientHandshakeTraffic;
-  const char *server_label = ssl_is_draft21(ssl->version)
-                                 ? kTLS13Draft21LabelServerHandshakeTraffic
+  const char *server_label = ssl_is_draft22(ssl->version)
+                                 ? kTLS13Draft22LabelServerHandshakeTraffic
                                  : kTLS13LabelServerHandshakeTraffic;
   return derive_secret(hs, hs->client_handshake_secret, hs->hash_len,
                        client_label, strlen(client_label)) &&
@@ -276,14 +276,14 @@
 int tls13_derive_application_secrets(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   ssl->s3->exporter_secret_len = hs->hash_len;
-  const char *client_label = ssl_is_draft21(ssl->version)
-                                 ? kTLS13Draft21LabelClientApplicationTraffic
+  const char *client_label = ssl_is_draft22(ssl->version)
+                                 ? kTLS13Draft22LabelClientApplicationTraffic
                                  : kTLS13LabelClientApplicationTraffic;
-  const char *server_label = ssl_is_draft21(ssl->version)
-                                 ? kTLS13Draft21LabelServerApplicationTraffic
+  const char *server_label = ssl_is_draft22(ssl->version)
+                                 ? kTLS13Draft22LabelServerApplicationTraffic
                                  : kTLS13LabelServerApplicationTraffic;
-  const char *exporter_label = ssl_is_draft21(ssl->version)
-                                   ? kTLS13Draft21LabelExporter
+  const char *exporter_label = ssl_is_draft22(ssl->version)
+                                   ? kTLS13Draft22LabelExporter
                                    : kTLS13LabelExporter;
   return derive_secret(hs, hs->client_traffic_secret_0, hs->hash_len,
                        client_label, strlen(client_label)) &&
@@ -301,7 +301,7 @@
 
 static const char kTLS13LabelApplicationTraffic[] =
     "application traffic secret";
-static const char kTLS13Draft21LabelApplicationTraffic[] = "traffic upd";
+static const char kTLS13Draft22LabelApplicationTraffic[] = "traffic upd";
 
 int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
   uint8_t *secret;
@@ -314,8 +314,8 @@
     secret_len = ssl->s3->write_traffic_secret_len;
   }
 
-  const char *traffic_label = ssl_is_draft21(ssl->version)
-                                  ? kTLS13Draft21LabelApplicationTraffic
+  const char *traffic_label = ssl_is_draft22(ssl->version)
+                                  ? kTLS13Draft22LabelApplicationTraffic
                                   : kTLS13LabelApplicationTraffic;
 
   const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl));
@@ -329,15 +329,15 @@
 }
 
 static const char kTLS13LabelResumption[] = "resumption master secret";
-static const char kTLS13Draft21LabelResumption[] = "res master";
+static const char kTLS13Draft22LabelResumption[] = "res master";
 
 int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs) {
   if (hs->hash_len > SSL_MAX_MASTER_KEY_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return 0;
   }
-  const char *resumption_label = ssl_is_draft21(hs->ssl->version)
-                                     ? kTLS13Draft21LabelResumption
+  const char *resumption_label = ssl_is_draft22(hs->ssl->version)
+                                     ? kTLS13Draft22LabelResumption
                                      : kTLS13LabelResumption;
   hs->new_session->master_key_length = hs->hash_len;
   return derive_secret(hs, hs->new_session->master_key,
@@ -388,7 +388,7 @@
 static const char kTLS13LabelResumptionPSK[] = "resumption";
 
 bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce) {
-  if (!ssl_is_draft21(session->ssl_version)) {
+  if (!ssl_is_draft22(session->ssl_version)) {
     return true;
   }
 
@@ -413,7 +413,7 @@
   }
 
   uint16_t version = SSL_get_session(ssl)->ssl_version;
-  if (!ssl_is_draft21(version)) {
+  if (!ssl_is_draft22(version)) {
     const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl));
     return hkdf_expand_label(out.data(), version, digest, secret.data(),
                              secret.size(), label.data(), label.size(),
@@ -443,7 +443,7 @@
 }
 
 static const char kTLS13LabelPSKBinder[] = "resumption psk binder key";
-static const char kTLS13Draft21LabelPSKBinder[] = "res binder";
+static const char kTLS13Draft22LabelPSKBinder[] = "res binder";
 
 static int tls13_psk_binder(uint8_t *out, uint16_t version,
                             const EVP_MD *digest, uint8_t *psk, size_t psk_len,
@@ -461,8 +461,8 @@
                     NULL, 0)) {
     return 0;
   }
-  const char *binder_label = ssl_is_draft21(version)
-                                 ? kTLS13Draft21LabelPSKBinder
+  const char *binder_label = ssl_is_draft22(version)
+                                 ? kTLS13Draft22LabelPSKBinder
                                  : kTLS13LabelPSKBinder;
 
   uint8_t binder_key[EVP_MAX_MD_SIZE] = {0};
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index 1040ace..4651459 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -182,7 +182,7 @@
                                    SSL3_MT_NEW_SESSION_TICKET) ||
         !CBB_add_u32(&body, session->timeout) ||
         !CBB_add_u32(&body, session->ticket_age_add) ||
-        (ssl_is_draft21(ssl->version) &&
+        (ssl_is_draft22(ssl->version) &&
          (!CBB_add_u8_length_prefixed(&body, &nonce_cbb) ||
           !CBB_add_bytes(&nonce_cbb, nonce, sizeof(nonce)))) ||
         !CBB_add_u16_length_prefixed(&body, &ticket) ||
@@ -194,7 +194,7 @@
 
     if (ssl->cert->enable_early_data) {
       CBB early_data_info;
-      if (!CBB_add_u16(&extensions, ssl_is_draft21(ssl->version)
+      if (!CBB_add_u16(&extensions, ssl_is_draft22(ssl->version)
                                         ? TLSEXT_TYPE_early_data
                                         : TLSEXT_TYPE_ticket_early_data_info) ||
           !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
@@ -398,7 +398,7 @@
           // The negotiated ALPN must match the one in the ticket.
           ssl->s3->alpn_selected ==
               MakeConstSpan(session->early_alpn, session->early_alpn_len)) {
-        ssl->early_data_accepted = true;
+        ssl->s3->early_data_accepted = true;
       }
 
       if (hs->new_session == NULL) {
@@ -457,7 +457,7 @@
     return ssl_hs_error;
   }
 
-  if (ssl->early_data_accepted) {
+  if (ssl->s3->early_data_accepted) {
     if (!tls13_derive_early_secrets(hs)) {
       return ssl_hs_error;
     }
@@ -469,10 +469,10 @@
   bool need_retry;
   if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
     if (need_retry) {
-      ssl->early_data_accepted = false;
+      ssl->s3->early_data_accepted = false;
       ssl->s3->skip_early_data = true;
       ssl->method->next_message(ssl);
-      if (ssl_is_draft21(ssl->version) &&
+      if (ssl_is_draft22(ssl->version) &&
           !hs->transcript.UpdateForHelloRetryRequest()) {
         return ssl_hs_error;
       }
@@ -525,7 +525,7 @@
     if (!ssl->method->init_message(ssl, cbb.get(), &body,
                                    SSL3_MT_HELLO_RETRY_REQUEST) ||
         !CBB_add_u16(&body, ssl->version) ||
-        (ssl_is_draft21(ssl->version) &&
+        (ssl_is_draft22(ssl->version) &&
          !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher))) ||
         !tls1_get_shared_group(hs, &group_id) ||
         !CBB_add_u16_length_prefixed(&body, &extensions) ||
@@ -580,34 +580,26 @@
 static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
-  uint16_t version = ssl->version;
-  if (ssl_is_resumption_experiment(ssl->version)) {
-    version = TLS1_2_VERSION;
-  }
-
   // Send a ServerHello.
   ScopedCBB cbb;
   CBB body, extensions, session_id;
   if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_SERVER_HELLO) ||
-      !CBB_add_u16(&body, version) ||
+      !CBB_add_u16(&body, TLS1_2_VERSION) ||
       !RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) ||
       !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
-      (ssl_is_resumption_experiment(ssl->version) &&
-       (!CBB_add_u8_length_prefixed(&body, &session_id) ||
-        !CBB_add_bytes(&session_id, hs->session_id, hs->session_id_len))) ||
+      !CBB_add_u8_length_prefixed(&body, &session_id) ||
+      !CBB_add_bytes(&session_id, hs->session_id, hs->session_id_len) ||
       !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher)) ||
-      (ssl_is_resumption_experiment(ssl->version) && !CBB_add_u8(&body, 0)) ||
+      !CBB_add_u8(&body, 0) ||
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
       !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) ||
       !ssl_ext_key_share_add_serverhello(hs, &extensions) ||
-      (ssl_is_resumption_experiment(ssl->version) &&
-       !ssl_ext_supported_versions_add_serverhello(hs, &extensions)) ||
+      !ssl_ext_supported_versions_add_serverhello(hs, &extensions) ||
       !ssl_add_message_cbb(ssl, cbb.get())) {
     return ssl_hs_error;
   }
 
-  if (ssl_is_resumption_experiment(ssl->version) &&
-      (!ssl_is_draft22(ssl->version) || !hs->sent_hello_retry_request) &&
+  if ((!ssl_is_draft22(ssl->version) || !hs->sent_hello_retry_request) &&
       !ssl->method->add_change_cipher_spec(ssl)) {
     return ssl_hs_error;
   }
@@ -639,7 +631,7 @@
 
   // Send a CertificateRequest, if necessary.
   if (hs->cert_request) {
-    if (ssl_is_draft21(ssl->version)) {
+    if (ssl_is_draft22(ssl->version)) {
       CBB cert_request_extensions, sigalg_contents, sigalgs_cbb;
       if (!ssl->method->init_message(ssl, cbb.get(), &body,
                                      SSL3_MT_CERTIFICATE_REQUEST) ||
@@ -732,12 +724,12 @@
     return ssl_hs_error;
   }
 
-  if (ssl->early_data_accepted) {
+  if (ssl->s3->early_data_accepted) {
     // If accepting 0-RTT, we send tickets half-RTT. This gets the tickets on
     // the wire sooner and also avoids triggering a write on |SSL_read| when
     // processing the client Finished. This requires computing the client
     // Finished early. See draft-ietf-tls-tls13-18, section 4.5.1.
-    if (ssl_is_draft21(ssl->version)) {
+    if (ssl_is_draft22(ssl->version)) {
       static const uint8_t kEndOfEarlyData[4] = {SSL3_MT_END_OF_EARLY_DATA, 0,
                                                  0, 0};
       if (!hs->transcript.Update(kEndOfEarlyData)) {
@@ -780,7 +772,7 @@
 
 static enum ssl_hs_wait_t do_read_second_client_flight(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (ssl->early_data_accepted) {
+  if (ssl->s3->early_data_accepted) {
     if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->early_traffic_secret,
                                hs->hash_len)) {
       return ssl_hs_error;
@@ -790,7 +782,8 @@
     hs->in_early_data = true;
   }
   hs->tls13_state = state_process_end_of_early_data;
-  return ssl->early_data_accepted ? ssl_hs_read_end_of_early_data : ssl_hs_ok;
+  return ssl->s3->early_data_accepted ? ssl_hs_read_end_of_early_data
+                                      : ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_process_end_of_early_data(SSL_HANDSHAKE *hs) {
@@ -798,8 +791,8 @@
   if (hs->early_data_offered) {
     // If early data was not accepted, the EndOfEarlyData and ChangeCipherSpec
     // message will be in the discarded early data.
-    if (hs->ssl->early_data_accepted) {
-      if (ssl_is_draft21(ssl->version)) {
+    if (hs->ssl->s3->early_data_accepted) {
+      if (ssl_is_draft22(ssl->version)) {
         SSLMessage msg;
         if (!ssl->method->get_message(ssl, &msg)) {
           return ssl_hs_read_message;
@@ -821,8 +814,9 @@
                              hs->hash_len)) {
     return ssl_hs_error;
   }
-  hs->tls13_state = ssl->early_data_accepted ? state_read_client_finished
-                                             : state_read_client_certificate;
+  hs->tls13_state = ssl->s3->early_data_accepted
+                        ? state_read_client_finished
+                        : state_read_client_certificate;
   return ssl_hs_ok;
 }
 
@@ -921,14 +915,14 @@
   if (!ssl_check_message_type(ssl, msg, SSL3_MT_FINISHED) ||
       // If early data was accepted, we've already computed the client Finished
       // and derived the resumption secret.
-      !tls13_process_finished(hs, msg, ssl->early_data_accepted) ||
+      !tls13_process_finished(hs, msg, ssl->s3->early_data_accepted) ||
       // evp_aead_seal keys have already been switched.
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0,
                              hs->hash_len)) {
     return ssl_hs_error;
   }
 
-  if (!ssl->early_data_accepted) {
+  if (!ssl->s3->early_data_accepted) {
     if (!ssl_hash_message(hs, msg) ||
         !tls13_derive_resumption_secret(hs)) {
       return ssl_hs_error;
diff --git a/src/ssl/tls_record.cc b/src/ssl/tls_record.cc
index a1363fa..3d34951 100644
--- a/src/ssl/tls_record.cc
+++ b/src/ssl/tls_record.cc
@@ -264,7 +264,7 @@
   *out_consumed = in.size() - CBS_len(&cbs);
 
   if (ssl->s3->have_version &&
-      ssl_is_resumption_experiment(ssl->version) &&
+      ssl_protocol_version(ssl) >= TLS1_3_VERSION &&
       SSL_in_init(ssl) &&
       type == SSL3_RT_CHANGE_CIPHER_SPEC &&
       ciphertext_len == 1 &&
@@ -357,7 +357,7 @@
 
   if (type == SSL3_RT_ALERT) {
     // Return end_of_early_data alerts as-is for the caller to process.
-    if (!ssl_is_draft21(ssl->version) &&
+    if (!ssl_is_draft22(ssl->version) &&
         out->size() == 2 &&
         (*out)[0] == SSL3_AL_WARNING &&
         (*out)[1] == TLS1_AD_END_OF_EARLY_DATA) {
diff --git a/src/third_party/fiat/README.md b/src/third_party/fiat/README.md
index 5512715..eaf0a60 100644
--- a/src/third_party/fiat/README.md
+++ b/src/third_party/fiat/README.md
@@ -3,3 +3,41 @@
 Some of the code in this directory is generated by
 [Fiat](https://github.com/mit-plv/fiat-crypto) and thus these files are
 licensed under the MIT license. (See LICENSE file.)
+
+## Curve25519
+
+To generate the field arithmetic procedures in `curve25519.c` from a fiat-crypto
+checkout (as of `c47f48268f15e202a28b556845f231b2038cb426`), run
+`make src/Specific/solinas32_2e255m19_10limbs/femul.c` (replacing `femul` with
+the desired field operation). The "source" file specifying the finite field and
+referencing the desired implementation strategy is
+`src/Specific/solinas32_2e255m19_10limbs/CurveParameters.v`, specifying roughly
+"unsaturated arithmetic modulo 2^255-19 using 10 limbs of radix 2^25.5 in 32-bit
+unsigned integers with a single carry chain and two wraparound carries" where
+only the prime is considered normative and everything else is treated as
+"compiler hints".
+
+## P256
+
+To generate the field arithmetic procedures in `p256.c` from a fiat-crypto
+checkout, run
+`make src/Specific/montgomery64_2e256m2e224p2e192p2e96m1_4limbs/femul.c`.
+The corresponding "source" file is
+`src/Specific/montgomery64_2e256m2e224p2e192p2e96m1_4limbs/CurveParameters.v`,
+specifying roughly "64-bit saturated word-by-word Montgomery reduction modulo
+2^256 - 2^224 + 2^192 + 2^96 - 1". Again, everything except for the prime is
+untrusted. There is currently a known issue where `fesub.c` for p256 does not
+manage to complete the build (specialization) within a week on Coq 8.7.0.
+<https://github.com/JasonGross/fiat-crypto/tree/3e6851ddecaac70d0feb484a75360d57f6e41244/src/Specific/montgomery64_2e256m2e224p2e192p2e96m1_4limbs>
+does manage to build that file, but the work on that branch was never finished
+(the correctness proofs of implementation templates still apply, but the
+now abandoned prototype specialization facilities there are unverified).
+
+## Working With Fiat Crypto Field Arithmetic
+
+The fiat-crypto readme <https://github.com/mit-plv/fiat-crypto#arithmetic-core>
+contains an overview of the implementation templates followed by a tour of the
+specialization machinery. It may be helpful to first read about the less messy
+parts of the system from chapter 3 of <http://adam.chlipala.net/theses/andreser.pdf>.
+There is work ongoing to replace the entire specialization mechanism with
+something much more principled <https://github.com/mit-plv/fiat-crypto/projects/4>.
diff --git a/src/third_party/fiat/curve25519.c b/src/third_party/fiat/curve25519.c
index d54aa83..2e56450 100644
--- a/src/third_party/fiat/curve25519.c
+++ b/src/third_party/fiat/curve25519.c
@@ -122,7 +122,7 @@
 //
 //   Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
 //   so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
-static void fe_tobytes_impl(uint8_t *s, const uint32_t h[10]) {
+static void fe_tobytes_impl(uint8_t s[32], const uint32_t h[10]) {
   assert_fe_loose(h);
   int32_t h0 = h[0];
   int32_t h1 = h[1];
@@ -203,11 +203,11 @@
   s[31] = h9 >> 18;
 }
 
-static void fe_tobytes(uint8_t *s, const fe *h) {
+static void fe_tobytes(uint8_t s[32], const fe *h) {
   fe_tobytes_impl(s, h->v);
 }
 
-static void fe_loose_tobytes(uint8_t *s, const fe_loose *h) {
+static void fe_loose_tobytes(uint8_t s[32], const fe_loose *h) {
   fe_tobytes_impl(s, h->v);
 }
 
@@ -281,13 +281,6 @@
 
 // h = f + g
 // Can overlap h with f or g.
-//
-// Preconditions:
-//    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
-//    |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
-//
-// Postconditions:
-//    |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 static void fe_add(fe_loose *h, const fe *f, const fe *g) {
   assert_fe(f->v);
   assert_fe(g->v);
@@ -331,13 +324,6 @@
 
 // h = f - g
 // Can overlap h with f or g.
-//
-// Preconditions:
-//    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
-//    |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
-//
-// Postconditions:
-//    |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 static void fe_sub(fe_loose *h, const fe *f, const fe *g) {
   assert_fe(f->v);
   assert_fe(g->v);
@@ -766,12 +752,6 @@
 }
 
 // h = -f
-//
-// Preconditions:
-//    |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
-//
-// Postconditions:
-//    |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
 static void fe_neg(fe_loose *h, const fe *f) {
   assert_fe(f->v);
   fe_neg_impl(h->v, f->v);
@@ -794,9 +774,6 @@
 
 // return 0 if f == 0
 // return 1 if f != 0
-//
-// Preconditions:
-//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 static int fe_isnonzero(const fe_loose *f) {
   uint8_t s[32];
   fe_loose_tobytes(s, f);
@@ -807,9 +784,6 @@
 
 // return 1 if f is in {1,3,5,...,q-2}
 // return 0 if f is in {0,2,4,...,q-1}
-//
-// Preconditions:
-//    |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
 static int fe_isnegative(const fe *f) {
   uint8_t s[32];
   fe_tobytes(s, f);
@@ -995,7 +969,7 @@
   fe_mul_ttt(out, &t0, z);
 }
 
-void x25519_ge_tobytes(uint8_t *s, const ge_p2 *h) {
+void x25519_ge_tobytes(uint8_t s[32], const ge_p2 *h) {
   fe recip;
   fe x;
   fe y;
@@ -1007,7 +981,7 @@
   s[31] ^= fe_isnegative(&x) << 7;
 }
 
-static void ge_p3_tobytes(uint8_t *s, const ge_p3 *h) {
+static void ge_p3_tobytes(uint8_t s[32], const ge_p3 *h) {
   fe recip;
   fe x;
   fe y;
@@ -3848,7 +3822,7 @@
 //   s[0]+256*s[1]+...+256^31*s[31] = s mod l
 //   where l = 2^252 + 27742317777372353535851937790883648493.
 //   Overwrites s in place.
-void x25519_sc_reduce(uint8_t *s) {
+void x25519_sc_reduce(uint8_t s[64]) {
   int64_t s0 = 2097151 & load_3(s);
   int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
   int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
@@ -4676,8 +4650,8 @@
   ED25519_keypair_from_seed(out_public_key, out_private_key, seed);
 }
 
-int ED25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
-                 const uint8_t private_key[64]) {
+int ED25519_sign(uint8_t out_sig[64], const uint8_t *message,
+                 size_t message_len, const uint8_t private_key[64]) {
   uint8_t az[SHA512_DIGEST_LENGTH];
   SHA512(private_key, 32, az);
 
@@ -4928,6 +4902,24 @@
   e[0] &= 248;
   e[31] &= 127;
   e[31] |= 64;
+
+  // The following implementation was transcribed to Coq and proven to
+  // correspond to unary scalar multiplication in affine coordinates given that
+  // x1 != 0 is the x coordinate of some point on the curve. It was also checked
+  // in Coq that doing a ladderstep with x1 = x3 = 0 gives z2' = z3' = 0, and z2
+  // = z3 = 0 gives z2' = z3' = 0. The statement was quantified over the
+  // underlying field, so it applies to Curve25519 itself and the quadratic
+  // twist of Curve25519. It was not proven in Coq that prime-field arithmetic
+  // correctly simulates extension-field arithmetic on prime-field values.
+  // The decoding of the byte array representation of e was not considered.
+  // Specification of Montgomery curves in affine coordinates:
+  // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Spec/MontgomeryCurve.v#L27>
+  // Proof that these form a group that is isomorphic to a Weierstrass curve:
+  // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/AffineProofs.v#L35>
+  // Coq transcription and correctness proof of the loop (where scalarbits=255):
+  // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L118>
+  // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L278>
+  // preconditions: 0 <= e < 2^255 (not necessarily e < order), fe_invert(0) = 0
   fe_frombytes(&x1, point);
   fe_1(&x2);
   fe_0(&z2);
@@ -4937,11 +4929,22 @@
   unsigned swap = 0;
   int pos;
   for (pos = 254; pos >= 0; --pos) {
+    // loop invariant as of right before the test, for the case where x1 != 0:
+    //   pos >= -1; if z2 = 0 then x2 is nonzero; if z3 = 0 then x3 is nonzero
+    //   let r := e >> (pos+1) in the following equalities of projective points:
+    //   to_xz (r*P)     === if swap then (x3, z3) else (x2, z2)
+    //   to_xz ((r+1)*P) === if swap then (x2, z2) else (x3, z3)
+    //   x1 is the nonzero x coordinate of the nonzero point (r*P-(r+1)*P)
     unsigned b = 1 & (e[pos / 8] >> (pos & 7));
     swap ^= b;
     fe_cswap(&x2, &x3, swap);
     fe_cswap(&z2, &z3, swap);
     swap = b;
+    // Coq transcription of ladderstep formula (called from transcribed loop):
+    // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L89>
+    // <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L131>
+    // x1 != 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L217>
+    // x1  = 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L147>
     fe_sub(&tmp0l, &x3, &z3);
     fe_sub(&tmp1l, &x2, &z2);
     fe_add(&x2l, &x2, &z2);
@@ -4961,6 +4964,7 @@
     fe_mul_ttt(&z3, &x1, &z2);
     fe_mul_tll(&z2, &tmp1l, &tmp0l);
   }
+  // here pos=-1, so r=e, so to_xz (e*P) === if swap then (x3, z3) else (x2, z2)
   fe_cswap(&x2, &x3, swap);
   fe_cswap(&z2, &z3, swap);
 
diff --git a/src/third_party/fiat/internal.h b/src/third_party/fiat/internal.h
index 10218e0..0533d27 100644
--- a/src/third_party/fiat/internal.h
+++ b/src/third_party/fiat/internal.h
@@ -56,17 +56,17 @@
 // Addition and subtraction produce fe_loose from (fe, fe).
 typedef struct fe_loose { uint32_t v[10]; } fe_loose;
 
-/* ge means group element.
-
- * Here the group is the set of pairs (x,y) of field elements (see fe.h)
- * satisfying -x^2 + y^2 = 1 + d x^2y^2
- * where d = -121665/121666.
- *
- * Representations:
- *   ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
- *   ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
- *   ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
- *   ge_precomp (Duif): (y+x,y-x,2dxy) */
+// ge means group element.
+//
+// Here the group is the set of pairs (x,y) of field elements (see fe.h)
+// satisfying -x^2 + y^2 = 1 + d x^2y^2
+// where d = -121665/121666.
+//
+// Representations:
+//   ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+//   ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+//   ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+//   ge_precomp (Duif): (y+x,y-x,2dxy)
 
 typedef struct {
   fe X;
@@ -101,7 +101,7 @@
   fe_loose T2d;
 } ge_cached;
 
-void x25519_ge_tobytes(uint8_t *s, const ge_p2 *h);
+void x25519_ge_tobytes(uint8_t s[32], const ge_p2 *h);
 int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t *s);
 void x25519_ge_p3_to_cached(ge_cached *r, const ge_p3 *p);
 void x25519_ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p);
@@ -112,7 +112,7 @@
     ge_p3 *h, const uint8_t a[32], const uint8_t precomp_table[15 * 2 * 32]);
 void x25519_ge_scalarmult_base(ge_p3 *h, const uint8_t a[32]);
 void x25519_ge_scalarmult(ge_p2 *r, const uint8_t *scalar, const ge_p3 *A);
-void x25519_sc_reduce(uint8_t *s);
+void x25519_sc_reduce(uint8_t s[64]);
 
 enum spake2_state_t {
   spake2_state_init = 0,
diff --git a/src/third_party/fiat/p256.c b/src/third_party/fiat/p256.c
index 25ef383..9c7de35 100644
--- a/src/third_party/fiat/p256.c
+++ b/src/third_party/fiat/p256.c
@@ -983,12 +983,24 @@
 // Building on top of the field operations we have the operations on the
 // elliptic curve group itself. Points on the curve are represented in Jacobian
 // coordinates.
+//
+// Both operations were transcribed to Coq and proven to correspond to naive
+// implementations using Affine coordinates, for all suitable fields.  In the
+// Coq proofs, issues of constant-time execution and memory layout (aliasing)
+// conventions were not considered. Specification of affine coordinates:
+// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Spec/WeierstrassCurve.v#L28>
+// As a sanity check, a proof that these points form a commutative group:
+// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/AffineProofs.v#L33>
 
 // point_double calculates 2*(x_in, y_in, z_in)
 //
 // The method is taken from:
 //   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
 //
+// Coq transcription and correctness proof:
+// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L93>
+// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L201>
+//
 // Outputs can equal corresponding inputs, i.e., x_out == x_in is allowed.
 // while x_out == y_in is not (maybe this works, but it's not tested).
 static void point_double(fe x_out, fe y_out, fe z_out,
@@ -1037,6 +1049,10 @@
 //   http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl,
 // adapted for mixed addition (z2 = 1, or z2 = 0 for the point at infinity).
 //
+// Coq transcription and correctness proof:
+// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L135>
+// <https://github.com/mit-plv/fiat-crypto/blob/79f8b5f39ed609339f0233098dee1a3c4e6b3080/src/Curves/Weierstrass/Jacobian.v#L205>
+//
 // This function includes a branch for checking whether the two input points
 // are equal, (while not equal to the point at infinity). This case never
 // happens during single point multiplication, so there is no timing leak for
diff --git a/src/tool/client.cc b/src/tool/client.cc
index fa279ae..fc8f5e0 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -332,30 +332,14 @@
 }
 
 static bool GetTLS13Variant(tls13_variant_t *out, const std::string &in) {
-  if (in == "draft") {
+  if (in == "draft22") {
     *out = tls13_default;
     return true;
   }
-  if (in == "draft21") {
-    *out = tls13_draft21;
-    return true;
-  }
-  if (in == "experiment") {
-    *out = tls13_experiment;
-    return true;
-  }
   if (in == "experiment2") {
     *out = tls13_experiment2;
     return true;
   }
-  if (in == "experiment3") {
-    *out = tls13_experiment3;
-    return true;
-  }
-  if (in == "draft22") {
-    *out = tls13_draft22;
-    return true;
-  }
   return false;
 }
 
diff --git a/src/tool/server.cc b/src/tool/server.cc
index 9963885..37235a7 100644
--- a/src/tool/server.cc
+++ b/src/tool/server.cc
@@ -68,10 +68,7 @@
         "-early-data", kBooleanArgument, "Allow early data",
     },
     {
-        "-tls13-variant", kBooleanArgument, "Enable TLS 1.3 variants",
-    },
-    {
-        "-tls13-draft22-variant", kBooleanArgument, "Enable TLS 1.3 Draft 22.",
+        "-tls13-variant", kBooleanArgument, "Enables all TLS 1.3 variants",
     },
     {
         "-www", kBooleanArgument,
@@ -310,11 +307,8 @@
     SSL_CTX_set_early_data_enabled(ctx.get(), 1);
   }
 
-  // Draft 22 variants need to be explicitly enabled.
-  if (args_map.count("-tls13-draft22-variant") != 0) {
-    SSL_CTX_set_tls13_variant(ctx.get(), tls13_draft22);
-  } else if (args_map.count("-tls13-variant") != 0) {
-    SSL_CTX_set_tls13_variant(ctx.get(), tls13_experiment);
+  if (args_map.count("-tls13-variant") != 0) {
+    SSL_CTX_set_tls13_variant(ctx.get(), tls13_experiment2);
   }
 
   if (args_map.count("-debug") != 0) {
diff --git a/src/util/fipstools/delocate.go b/src/util/fipstools/delocate.go
index 6d56f92..2c1ba49 100644
--- a/src/util/fipstools/delocate.go
+++ b/src/util/fipstools/delocate.go
@@ -792,6 +792,9 @@
 const (
 	instrPush instructionType = iota
 	instrMove
+	// instrTransformingMove is essentially a move, but it performs some
+	// transformation of the data during the process.
+	instrTransformingMove
 	instrJump
 	instrConditionalMove
 	instrOther
@@ -818,6 +821,11 @@
 		if len(args) == 1 {
 			return instrJump
 		}
+
+	case "vpbroadcastq":
+		if len(args) == 2 {
+			return instrTransformingMove
+		}
 	}
 
 	return instrOther
@@ -870,6 +878,13 @@
 	}
 }
 
+func finalTransform(w stringWriter, transformInstruction, reg string) wrapperFunc {
+	return func(k func()) {
+		k()
+		w.WriteString("\t" + transformInstruction + " " + reg + ", " + reg + "\n")
+	}
+}
+
 func isValidLEATarget(reg string) bool {
 	return !strings.HasPrefix(reg, "%xmm") && !strings.HasPrefix(reg, "%ymm") && !strings.HasPrefix(reg, "%zmm")
 }
@@ -1018,6 +1033,13 @@
 				case instrMove:
 					assertNodeType(argNodes[1], ruleRegisterOrConstant)
 					targetReg = d.contents(argNodes[1])
+				case instrTransformingMove:
+					assertNodeType(argNodes[1], ruleRegisterOrConstant)
+					targetReg = d.contents(argNodes[1])
+					wrappers = append(wrappers, finalTransform(d.output, instructionName, targetReg))
+					if isValidLEATarget(targetReg) {
+						return nil, fmt.Errorf("Currently transforming moves are assumed to target XMM registers. Otherwise we'll pop %rax before reading it to do the transform.")
+					}
 				default:
 					return nil, fmt.Errorf("Cannot rewrite GOTPCREL reference for instruction %q", instructionName)
 				}
diff --git a/src/util/fipstools/testdata/x86_64-GOTRewrite/in.s b/src/util/fipstools/testdata/x86_64-GOTRewrite/in.s
index 9b4b201..0f9c70e 100644
--- a/src/util/fipstools/testdata/x86_64-GOTRewrite/in.s
+++ b/src/util/fipstools/testdata/x86_64-GOTRewrite/in.s
@@ -34,4 +34,8 @@
 	movq foobar_bss_get@GOTPCREL(%rip), %r11
 	movq OPENSSL_ia32cap_get@GOTPCREL(%rip), %r11
 
+	# Transforming moves run the transform in-place after the load.
+	vpbroadcastq stderr@GOTPCREL(%rip), %xmm0
+	vpbroadcastq foo@GOTPCREL(%rip), %xmm0
+
 .comm foobar,64,32
diff --git a/src/util/fipstools/testdata/x86_64-GOTRewrite/out.s b/src/util/fipstools/testdata/x86_64-GOTRewrite/out.s
index 0e07721..0420af6 100644
--- a/src/util/fipstools/testdata/x86_64-GOTRewrite/out.s
+++ b/src/util/fipstools/testdata/x86_64-GOTRewrite/out.s
@@ -124,6 +124,28 @@
 # WAS movq OPENSSL_ia32cap_get@GOTPCREL(%rip), %r11
 	leaq	OPENSSL_ia32cap_get(%rip), %r11
 
+	# Transforming moves run the transform in-place after the load.
+# WAS vpbroadcastq stderr@GOTPCREL(%rip), %xmm0
+	leaq -128(%rsp), %rsp
+	pushq %rax
+	pushf
+	leaq stderr_GOTPCREL_external(%rip), %rax
+	addq (%rax), %rax
+	movq (%rax), %rax
+	popf
+	vmovq %rax, %xmm0
+	popq %rax
+	leaq 128(%rsp), %rsp
+	vpbroadcastq %xmm0, %xmm0
+# WAS vpbroadcastq foo@GOTPCREL(%rip), %xmm0
+	leaq -128(%rsp), %rsp
+	pushq %rax
+	leaq	.Lfoo_local_target(%rip), %rax
+	vmovq %rax, %xmm0
+	popq %rax
+	leaq 128(%rsp), %rsp
+	vpbroadcastq %xmm0, %xmm0
+
 .comm foobar,64,32
 .text
 BORINGSSL_bcm_text_end: