external/boringssl: Sync to a5462d3050ac6a68ab488450bf5856475dbef992.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/27bc0f26c8d132df04f5b0b173aefeb8aaa13c33..a5462d3050ac6a68ab488450bf5856475dbef992

Test: BoringSSL CTS Presubmits
Change-Id: Ieac8258ca12c1fcbdc00196d7d3f3fc0635f94e3
diff --git a/src/crypto/asn1/a_object.c b/src/crypto/asn1/a_object.c
index a710add..005e37d 100644
--- a/src/crypto/asn1/a_object.c
+++ b/src/crypto/asn1/a_object.c
@@ -87,134 +87,6 @@
     return (objsize);
 }
 
-int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)
-{
-    int i, first, len = 0, c, use_bn;
-    char ftmp[24], *tmp = ftmp;
-    int tmpsize = sizeof ftmp;
-    const char *p;
-    unsigned long l;
-    BIGNUM *bl = NULL;
-
-    if (num == 0)
-        return (0);
-    else if (num == -1)
-        num = strlen(buf);
-
-    p = buf;
-    c = *(p++);
-    num--;
-    if ((c >= '0') && (c <= '2')) {
-        first = c - '0';
-    } else {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_FIRST_NUM_TOO_LARGE);
-        goto err;
-    }
-
-    if (num <= 0) {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_SECOND_NUMBER);
-        goto err;
-    }
-    c = *(p++);
-    num--;
-    for (;;) {
-        if (num <= 0)
-            break;
-        if ((c != '.') && (c != ' ')) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_SEPARATOR);
-            goto err;
-        }
-        l = 0;
-        use_bn = 0;
-        for (;;) {
-            if (num <= 0)
-                break;
-            num--;
-            c = *(p++);
-            if ((c == ' ') || (c == '.'))
-                break;
-            if ((c < '0') || (c > '9')) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_DIGIT);
-                goto err;
-            }
-            if (!use_bn && l >= ((ULONG_MAX - 80) / 10L)) {
-                use_bn = 1;
-                if (!bl)
-                    bl = BN_new();
-                if (!bl || !BN_set_word(bl, l))
-                    goto err;
-            }
-            if (use_bn) {
-                if (!BN_mul_word(bl, 10L)
-                    || !BN_add_word(bl, c - '0'))
-                    goto err;
-            } else
-                l = l * 10L + (long)(c - '0');
-        }
-        if (len == 0) {
-            if ((first < 2) && (l >= 40)) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_SECOND_NUMBER_TOO_LARGE);
-                goto err;
-            }
-            if (use_bn) {
-                if (!BN_add_word(bl, first * 40))
-                    goto err;
-            } else
-                l += (long)first *40;
-        }
-        i = 0;
-        if (use_bn) {
-            int blsize;
-            blsize = BN_num_bits(bl);
-            blsize = (blsize + 6) / 7;
-            if (blsize > tmpsize) {
-                if (tmp != ftmp)
-                    OPENSSL_free(tmp);
-                tmpsize = blsize + 32;
-                tmp = OPENSSL_malloc(tmpsize);
-                if (!tmp)
-                    goto err;
-            }
-            while (blsize--) {
-                BN_ULONG t = BN_div_word(bl, 0x80L);
-                if (t == (BN_ULONG)-1)
-                    goto err;
-                tmp[i++] = (unsigned char)t;
-            }
-        } else {
-
-            for (;;) {
-                tmp[i++] = (unsigned char)l & 0x7f;
-                l >>= 7L;
-                if (l == 0L)
-                    break;
-            }
-
-        }
-        if (out != NULL) {
-            if (len + i > olen) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_BUFFER_TOO_SMALL);
-                goto err;
-            }
-            while (--i > 0)
-                out[len++] = tmp[i] | 0x80;
-            out[len++] = tmp[0];
-        } else
-            len += i;
-    }
-    if (tmp != ftmp)
-        OPENSSL_free(tmp);
-    if (bl)
-        BN_free(bl);
-    return (len);
- err:
-    if (tmp != ftmp)
-        OPENSSL_free(tmp);
-    if (bl)
-        BN_free(bl);
-    return (0);
-}
-
 int i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *a)
 {
     return OBJ_obj2txt(buf, buf_len, a, 0);
diff --git a/src/crypto/bio/bio_test.cc b/src/crypto/bio/bio_test.cc
index eb54f7e..8479c8e 100644
--- a/src/crypto/bio/bio_test.cc
+++ b/src/crypto/bio/bio_test.cc
@@ -27,6 +27,7 @@
 
 #if !defined(OPENSSL_WINDOWS)
 #include <arpa/inet.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <netinet/in.h>
 #include <string.h>
diff --git a/src/crypto/bytestring/ber.c b/src/crypto/bytestring/ber.c
index bb5e17c..4dc94f6 100644
--- a/src/crypto/bytestring/ber.c
+++ b/src/crypto/bytestring/ber.c
@@ -29,7 +29,10 @@
 // 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) {
-  switch (tag & ~CBS_ASN1_CONSTRUCTED) {
+  if ((tag & 0xc0) != 0) {
+    return 0;
+  }
+  switch (tag & 0x1f) {
     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 7e3d453..5a6a5c1 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, CBS_ASN1_BOOLEAN));
-  EXPECT_TRUE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
+  EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x1));
+  EXPECT_TRUE(CBS_peek_asn1_tag(&data, 0x30));
 
-  ASSERT_TRUE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBS_get_asn1(&data, &contents, 0x30));
   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, CBS_ASN1_SEQUENCE));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
 
   CBS_init(&data, kData3, sizeof(kData3));
   // zero byte length of length
-  EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
 
   CBS_init(&data, kData4, sizeof(kData4));
   // long form mistakenly used.
-  EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
 
   CBS_init(&data, kData5, sizeof(kData5));
   // length takes too many bytes.
-  EXPECT_FALSE(CBS_get_asn1(&data, &contents, CBS_ASN1_SEQUENCE));
+  EXPECT_FALSE(CBS_get_asn1(&data, &contents, 0x30));
 
   CBS_init(&data, kData1, sizeof(kData1));
   // wrong tag.
@@ -151,72 +151,56 @@
 
   CBS_init(&data, NULL, 0);
   // peek at empty data.
-  EXPECT_FALSE(CBS_peek_asn1_tag(&data, CBS_ASN1_SEQUENCE));
+  EXPECT_FALSE(CBS_peek_asn1_tag(&data, 0x30));
 
   CBS_init(&data, NULL, 0);
   // optional elements at empty data.
-  ASSERT_TRUE(CBS_get_optional_asn1(
-      &data, &contents, &present,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+  ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
   EXPECT_FALSE(present);
-  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
-      &data, &contents, &present,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+  ASSERT_TRUE(
+      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
   EXPECT_FALSE(present);
   EXPECT_EQ(0u, CBS_len(&contents));
-  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
-      &data, &contents, NULL,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(&data, &contents, NULL, 0xa0));
   EXPECT_EQ(0u, CBS_len(&contents));
-  ASSERT_TRUE(CBS_get_optional_asn1_uint64(
-      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
+  ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
   EXPECT_EQ(42u, value);
 
   CBS_init(&data, kData6, sizeof(kData6));
   // optional element.
-  ASSERT_TRUE(CBS_get_optional_asn1(
-      &data, &contents, &present,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+  ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa0));
   EXPECT_FALSE(present);
-  ASSERT_TRUE(CBS_get_optional_asn1(
-      &data, &contents, &present,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
+  ASSERT_TRUE(CBS_get_optional_asn1(&data, &contents, &present, 0xa1));
   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,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
+  ASSERT_TRUE(
+      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0));
   EXPECT_FALSE(present);
   EXPECT_EQ(0u, CBS_len(&contents));
-  ASSERT_TRUE(CBS_get_optional_asn1_octet_string(
-      &data, &contents, &present,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
+  ASSERT_TRUE(
+      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
   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,
-      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
+  EXPECT_FALSE(
+      CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1));
 
   CBS_init(&data, kData8, sizeof(kData8));
   // optional integer.
-  ASSERT_TRUE(CBS_get_optional_asn1_uint64(
-      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0, 42));
+  ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42));
   EXPECT_EQ(42u, value);
-  ASSERT_TRUE(CBS_get_optional_asn1_uint64(
-      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
+  ASSERT_TRUE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
   EXPECT_EQ(1u, value);
 
   CBS_init(&data, kData9, sizeof(kData9));
   // invalid optional integer.
-  EXPECT_FALSE(CBS_get_optional_asn1_uint64(
-      &data, &value, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1, 42));
+  EXPECT_FALSE(CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42));
 
   unsigned tag;
   CBS_init(&data, kData1, sizeof(kData1));
@@ -233,54 +217,6 @@
             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};
@@ -480,42 +416,15 @@
 }
 
 TEST(CBBTest, ASN1) {
-  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,
-  };
+  static const uint8_t kExpected[] = {0x30, 3, 1, 2, 3};
   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, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
   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);
 
@@ -523,7 +432,7 @@
 
   std::vector<uint8_t> test_data(100000, 0x42);
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
-  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
   ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 130));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
   scoper.reset(buf);
@@ -533,7 +442,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, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
   ASSERT_TRUE(CBB_add_bytes(&contents, test_data.data(), 1000));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
   scoper.reset(buf);
@@ -543,8 +452,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, CBS_ASN1_SEQUENCE));
-  ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, CBS_ASN1_SEQUENCE));
+  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &contents, 0x30));
+  ASSERT_TRUE(CBB_add_asn1(&contents, &inner_contents, 0x30));
   ASSERT_TRUE(CBB_add_bytes(&inner_contents, test_data.data(), 100000));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
   scoper.reset(buf);
@@ -581,12 +490,6 @@
   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,
@@ -631,8 +534,6 @@
                    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,
@@ -886,3 +787,98 @@
               CBS_asn1_bitstring_has_bit(&cbs, test.bit));
   }
 }
+
+TEST(CBBTest, AddOIDFromText) {
+  const struct {
+    const char *text;
+    std::vector<uint8_t> der;
+  } kValidOIDs[] = {
+      // Some valid values.
+      {"0.0", {0x00}},
+      {"0.2.3.4", {0x2, 0x3, 0x4}},
+      {"1.2.3.4", {0x2a, 0x3, 0x4}},
+      {"2.2.3.4", {0x52, 0x3, 0x4}},
+      {"1.2.840.113554.4.1.72585",
+       {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09}},
+      // Test edge cases around the first component.
+      {"0.39", {0x27}},
+      {"1.0", {0x28}},
+      {"1.39", {0x4f}},
+      {"2.0", {0x50}},
+      {"2.1", {0x51}},
+      {"2.40", {0x78}},
+      // Edge cases near an overflow.
+      {"1.2.18446744073709551615",
+       {0x2a, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}},
+      {"2.18446744073709551535",
+       {0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}},
+  };
+
+  const char *kInvalidTexts[] = {
+      // Invalid second component.
+      "0.40",
+      "1.40",
+      // Invalid first component.
+      "3.1",
+      // The empty string is not an OID.
+      "",
+      // No empty components.
+      ".1.2.3.4.5",
+      "1..2.3.4.5",
+      "1.2.3.4.5.",
+      // There must be at least two components.
+      "1",
+      // No extra leading zeros.
+      "00.1.2.3.4",
+      "01.1.2.3.4",
+      // Overflow for both components or 40*A + B.
+      "1.2.18446744073709551616",
+      "2.18446744073709551536",
+  };
+
+  const std::vector<uint8_t> kInvalidDER[] = {
+      // The empty string is not an OID.
+      {},
+      // Non-minimal representation.
+      {0x80, 0x01},
+      // Overflow. This is the DER representation of
+      // 1.2.840.113554.4.1.72585.18446744073709551616. (The final value is
+      // 2^64.)
+      {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09,
+       0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00},
+  };
+
+  for (const auto &t : kValidOIDs) {
+    SCOPED_TRACE(t.text);
+
+    bssl::ScopedCBB cbb;
+    ASSERT_TRUE(CBB_init(cbb.get(), 0));
+    ASSERT_TRUE(CBB_add_asn1_oid_from_text(cbb.get(), t.text, strlen(t.text)));
+    uint8_t *out;
+    size_t len;
+    ASSERT_TRUE(CBB_finish(cbb.get(), &out, &len));
+    bssl::UniquePtr<uint8_t> free_out(out);
+    EXPECT_EQ(Bytes(t.der), Bytes(out, len));
+
+    CBS cbs;
+    CBS_init(&cbs, t.der.data(), t.der.size());
+    bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
+    ASSERT_TRUE(text.get());
+    EXPECT_STREQ(t.text, text.get());
+  }
+
+  for (const char *t : kInvalidTexts) {
+    SCOPED_TRACE(t);
+    bssl::ScopedCBB cbb;
+    ASSERT_TRUE(CBB_init(cbb.get(), 0));
+    EXPECT_FALSE(CBB_add_asn1_oid_from_text(cbb.get(), t, strlen(t)));
+  }
+
+  for (const auto &t : kInvalidDER) {
+    SCOPED_TRACE(Bytes(t));
+    CBS cbs;
+    CBS_init(&cbs, t.data(), t.size());
+    bssl::UniquePtr<char> text(CBS_asn1_oid_to_text(&cbs));
+    EXPECT_FALSE(text);
+  }
+}
diff --git a/src/crypto/bytestring/cbb.c b/src/crypto/bytestring/cbb.c
index b1afe7d..f8f5e0f 100644
--- a/src/crypto/bytestring/cbb.c
+++ b/src/crypto/bytestring/cbb.c
@@ -15,6 +15,7 @@
 #include <openssl/bytestring.h>
 
 #include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/mem.h>
@@ -328,37 +329,44 @@
   return cbb_add_length_prefixed(cbb, out_contents, 3);
 }
 
+// add_base128_integer encodes |v| as a big-endian base-128 integer where the
+// high bit of each byte indicates where there is more data. This is the
+// encoding used in DER for both high tag number form and OID components.
+static int add_base128_integer(CBB *cbb, uint64_t v) {
+  unsigned len_len = 0;
+  uint64_t copy = v;
+  while (copy > 0) {
+    len_len++;
+    copy >>= 7;
+  }
+  if (len_len == 0) {
+    len_len = 1;  // Zero is encoded with one byte.
+  }
+  for (unsigned i = len_len - 1; i < len_len; i--) {
+    uint8_t byte = (v >> (7 * i)) & 0x7f;
+    if (i != 0) {
+      // The high bit denotes whether there is more data.
+      byte |= 0x80;
+    }
+    if (!CBB_add_u8(cbb, byte)) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
 int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
-  if (!CBB_flush(cbb)) {
+  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;
     return 0;
   }
 
-  // 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)) {
-      return 0;
-    }
-
-    unsigned len_len = 0;
-    unsigned copy = tag_number;
-    while (copy > 0) {
-      len_len++;
-      copy >>= 7;
-    }
-    for (unsigned i = len_len - 1; i < len_len; i--) {
-      uint8_t byte = (tag_number >> (7 * i)) & 0x7f;
-      if (i != 0) {
-        // The high bit denotes whether there is more data.
-        byte |= 0x80;
-      }
-      if (!CBB_add_u8(cbb, byte)) {
-        return 0;
-      }
-    }
-  } else if (!CBB_add_u8(cbb, tag_bits | tag_number)) {
+  if (!CBB_flush(cbb) ||
+      // |tag|'s representation matches the DER encoding.
+      !CBB_add_u8(cbb, (uint8_t)tag)) {
     return 0;
   }
 
@@ -492,3 +500,69 @@
 
   return CBB_flush(cbb);
 }
+
+// parse_dotted_decimal parses one decimal component from |cbs|, where |cbs| is
+// an OID literal, e.g., "1.2.840.113554.4.1.72585". It consumes both the
+// component and the dot, so |cbs| may be passed into the function again for the
+// next value.
+static int parse_dotted_decimal(CBS *cbs, uint64_t *out) {
+  *out = 0;
+  int seen_digit = 0;
+  for (;;) {
+    // Valid terminators for a component are the end of the string or a
+    // non-terminal dot. If the string ends with a dot, this is not a valid OID
+    // string.
+    uint8_t u;
+    if (!CBS_get_u8(cbs, &u) ||
+        (u == '.' && CBS_len(cbs) > 0)) {
+      break;
+    }
+    if (u < '0' || u > '9' ||
+        // Forbid stray leading zeros.
+        (seen_digit && *out == 0) ||
+        // Check for overflow.
+        *out > UINT64_MAX / 10 ||
+        *out * 10 > UINT64_MAX - (u - '0')) {
+      return 0;
+    }
+    *out = *out * 10 + (u - '0');
+    seen_digit = 1;
+  }
+  // The empty string is not a legal OID component.
+  return seen_digit;
+}
+
+int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text, size_t len) {
+  if (!CBB_flush(cbb)) {
+    return 0;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, (const uint8_t *)text, len);
+
+  // OIDs must have at least two components.
+  uint64_t a, b;
+  if (!parse_dotted_decimal(&cbs, &a) ||
+      !parse_dotted_decimal(&cbs, &b)) {
+    return 0;
+  }
+
+  // The first component is encoded as 40 * |a| + |b|. This assumes that |a| is
+  // 0, 1, or 2 and that, when it is 0 or 1, |b| is at most 39.
+  if (a > 2 ||
+      (a < 2 && b > 39) ||
+      b > UINT64_MAX - 80 ||
+      !add_base128_integer(cbb, 40u * a + b)) {
+    return 0;
+  }
+
+  // The remaining components are encoded unmodified.
+  while (CBS_len(&cbs) > 0) {
+    if (!parse_dotted_decimal(&cbs, &a) ||
+        !add_base128_integer(cbb, a)) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
diff --git a/src/crypto/bytestring/cbs.c b/src/crypto/bytestring/cbs.c
index d96371c..f3fc863 100644
--- a/src/crypto/bytestring/cbs.c
+++ b/src/crypto/bytestring/cbs.c
@@ -12,11 +12,16 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#if !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
 #include <openssl/buf.h>
 #include <openssl/mem.h>
 #include <openssl/bytestring.h>
 
 #include <assert.h>
+#include <inttypes.h>
 #include <string.h>
 
 #include "internal.h"
@@ -175,9 +180,45 @@
   return cbs_get_length_prefixed(cbs, out, 3);
 }
 
-static int parse_asn1_tag(CBS *cbs, unsigned *out) {
-  uint8_t tag_byte;
-  if (!CBS_get_u8(cbs, &tag_byte)) {
+// parse_base128_integer reads a big-endian base-128 integer from |cbs| and sets
+// |*out| to the result. This is the encoding used in DER for both high tag
+// number form and OID components.
+static int parse_base128_integer(CBS *cbs, uint64_t *out) {
+  uint64_t v = 0;
+  uint8_t b;
+  do {
+    if (!CBS_get_u8(cbs, &b)) {
+      return 0;
+    }
+    if ((v >> (64 - 7)) != 0) {
+      // The value is too large.
+      return 0;
+    }
+    if (v == 0 && b == 0x80) {
+      // The value must be minimally encoded.
+      return 0;
+    }
+    v = (v << 7) | (b & 0x7f);
+
+    // Values end at an octet with the high bit cleared.
+  } while (b & 0x80);
+
+  *out = v;
+  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)) {
     return 0;
   }
 
@@ -188,70 +229,22 @@
   // 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).
-  unsigned tag = ((unsigned)tag_byte & 0xe0) << CBS_ASN1_TAG_SHIFT;
-  unsigned tag_number = tag_byte & 0x1f;
-  if (tag_number == 0x1f) {
-    tag_number = 0;
-    for (;;) {
-      if (!CBS_get_u8(cbs, &tag_byte) ||
-          ((tag_number << 7) >> 7) != tag_number) {
-        return 0;
-      }
-      tag_number = (tag_number << 7) | (tag_byte & 0x7f);
-      // The tag must be represented in the minimal number of bytes.
-      if (tag_number == 0) {
-        return 0;
-      }
-      if ((tag_byte & 0x80) == 0) {
-        break;
-      }
-    }
-    if (// Check the tag number is within our supported bounds.
-        tag_number > CBS_ASN1_TAG_NUMBER_MASK ||
-        // Small tag numbers should have used low tag number form.
-        tag_number < 0x1f) {
-      return 0;
-    }
-  }
-
-  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)) {
+  if ((tag & 0x1f) == 0x1f) {
     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) + header_len;
+    len = ((size_t) length_byte) + 2;
     if (out_header_len != NULL) {
-      *out_header_len = header_len;
+      *out_header_len = 2;
     }
   } else {
     // The high bit indicate that this is the long form, while the next 7 bits
@@ -263,9 +256,9 @@
     if (ber_ok && (tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
       // indefinite length
       if (out_header_len != NULL) {
-        *out_header_len = header_len;
+        *out_header_len = 2;
       }
-      return CBS_get_bytes(cbs, out, header_len);
+      return CBS_get_bytes(cbs, out, 2);
     }
 
     // ITU-T X.690 clause 8.1.3.5.c specifies that the value 0xff shall not be
@@ -288,13 +281,13 @@
       return 0;
     }
     len = len32;
-    if (len + header_len + num_bytes < len) {
+    if (len + 2 + num_bytes < len) {
       // Overflow.
       return 0;
     }
-    len += header_len + num_bytes;
+    len += 2 + num_bytes;
     if (out_header_len != NULL) {
-      *out_header_len = header_len + num_bytes;
+      *out_header_len = 2 + num_bytes;
     }
   }
 
@@ -362,10 +355,7 @@
   if (CBS_len(cbs) < 1) {
     return 0;
   }
-
-  CBS copy = *cbs;
-  unsigned actual_tag;
-  return parse_asn1_tag(&copy, &actual_tag) && tag_value == actual_tag;
+  return CBS_data(cbs)[0] == tag_value;
 }
 
 int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
@@ -527,3 +517,55 @@
   return byte_num < CBS_len(cbs) &&
          (CBS_data(cbs)[byte_num] & (1 << bit_num)) != 0;
 }
+
+static int add_decimal(CBB *out, uint64_t v) {
+  char buf[DECIMAL_SIZE(uint64_t) + 1];
+  BIO_snprintf(buf, sizeof(buf), "%" PRIu64, v);
+  return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf));
+}
+
+char *CBS_asn1_oid_to_text(const CBS *cbs) {
+  CBB cbb;
+  if (!CBB_init(&cbb, 32)) {
+    goto err;
+  }
+
+  CBS copy = *cbs;
+  // The first component is 40 * value1 + value2, where value1 is 0, 1, or 2.
+  uint64_t v;
+  if (!parse_base128_integer(&copy, &v)) {
+    goto err;
+  }
+
+  if (v >= 80) {
+    if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) ||
+        !add_decimal(&cbb, v - 80)) {
+      goto err;
+    }
+  } else if (!add_decimal(&cbb, v / 40) ||
+             !CBB_add_u8(&cbb, '.') ||
+             !add_decimal(&cbb, v % 40)) {
+    goto err;
+  }
+
+  while (CBS_len(&copy) != 0) {
+    if (!parse_base128_integer(&copy, &v) ||
+        !CBB_add_u8(&cbb, '.') ||
+        !add_decimal(&cbb, v)) {
+      goto err;
+    }
+  }
+
+  uint8_t *txt;
+  size_t txt_len;
+  if (!CBB_add_u8(&cbb, '\0') ||
+      !CBB_finish(&cbb, &txt, &txt_len)) {
+    goto err;
+  }
+
+  return (char *)txt;
+
+err:
+  CBB_cleanup(&cbb);
+  return NULL;
+}
diff --git a/src/crypto/err/obj.errordata b/src/crypto/err/obj.errordata
index c54435e..be13451 100644
--- a/src/crypto/err/obj.errordata
+++ b/src/crypto/err/obj.errordata
@@ -1 +1,2 @@
+OBJ,101,INVALID_OID_STRING
 OBJ,100,UNKNOWN_NID
diff --git a/src/crypto/fipsmodule/bn/rsaz_exp.c b/src/crypto/fipsmodule/bn/rsaz_exp.c
index d0090a6..cb9a233 100644
--- a/src/crypto/fipsmodule/bn/rsaz_exp.c
+++ b/src/crypto/fipsmodule/bn/rsaz_exp.c
@@ -227,7 +227,9 @@
 
 		rsaz_1024_sqr_avx2(result, result, m, k0, 5);
 
-		wvalue = *((const unsigned short*)&p_str[index / 8]);
+		uint16_t wvalue_16;
+		memcpy(&wvalue_16, &p_str[index / 8], sizeof(wvalue_16));
+		wvalue = wvalue_16;
 		wvalue = (wvalue>> (index%8)) & 31;
 		index-=5;
 
diff --git a/src/crypto/fipsmodule/ec/ec.c b/src/crypto/fipsmodule/ec/ec.c
index 977cd26..266baa2 100644
--- a/src/crypto/fipsmodule/ec/ec.c
+++ b/src/crypto/fipsmodule/ec/ec.c
@@ -817,6 +817,24 @@
   return ec_GFp_simple_invert(group, a, ctx);
 }
 
+static int arbitrary_bignum_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
+                                      const BIGNUM *in, BN_CTX *ctx) {
+  const BIGNUM *order = EC_GROUP_get0_order(group);
+  if (BN_is_negative(in) || BN_num_bits(in) > BN_num_bits(order)) {
+    // This is an unusual input, so we do not guarantee constant-time
+    // processing, even ignoring |bn_correct_top|.
+    BN_CTX_start(ctx);
+    BIGNUM *tmp = BN_CTX_get(ctx);
+    int ok = tmp != NULL &&
+             BN_nnmod(tmp, in, order, ctx) &&
+             ec_bignum_to_scalar(group, out, tmp);
+    BN_CTX_end(ctx);
+    return ok;
+  }
+
+  return ec_bignum_to_scalar(group, out, in);
+}
+
 int EC_POINT_mul(const EC_GROUP *group, EC_POINT *r, const BIGNUM *g_scalar,
                  const EC_POINT *p, const BIGNUM *p_scalar, BN_CTX *ctx) {
   // Previously, this function set |r| to the point at infinity if there was
@@ -828,30 +846,27 @@
     return 0;
   }
 
-  // We cannot easily process arbitrary scalars in constant-time, and there is
-  // no need to do so. Require that scalars be the same size as the order.
-  //
-  // One could require they be fully reduced, but some consumers try to check
-  // that |order| * |pubkey| is the identity. This comes from following NIST SP
-  // 800-56A section 5.6.2.3.2. (Though all our curves have cofactor one, so
-  // this check isn't useful.)
   int ret = 0;
   EC_SCALAR g_scalar_storage, p_scalar_storage;
   EC_SCALAR *g_scalar_arg = NULL, *p_scalar_arg = NULL;
-  unsigned order_bits = BN_num_bits(&group->order);
+  BN_CTX *new_ctx = NULL;
+  if (ctx == NULL) {
+    new_ctx = BN_CTX_new();
+    if (new_ctx == NULL) {
+      goto err;
+    }
+    ctx = new_ctx;
+  }
+
   if (g_scalar != NULL) {
-    if (BN_is_negative(g_scalar) || BN_num_bits(g_scalar) > order_bits ||
-        !ec_bignum_to_scalar(group, &g_scalar_storage, g_scalar)) {
-      OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
+    if (!arbitrary_bignum_to_scalar(group, &g_scalar_storage, g_scalar, ctx)) {
       goto err;
     }
     g_scalar_arg = &g_scalar_storage;
   }
 
   if (p_scalar != NULL) {
-    if (BN_is_negative(p_scalar) || BN_num_bits(p_scalar) > order_bits ||
-        !ec_bignum_to_scalar(group, &p_scalar_storage, p_scalar)) {
-      OPENSSL_PUT_ERROR(EC, EC_R_INVALID_SCALAR);
+    if (!arbitrary_bignum_to_scalar(group, &p_scalar_storage, p_scalar, ctx)) {
       goto err;
     }
     p_scalar_arg = &p_scalar_storage;
@@ -860,6 +875,7 @@
   ret = ec_point_mul_scalar(group, r, g_scalar_arg, p, p_scalar_arg, ctx);
 
 err:
+  BN_CTX_free(new_ctx);
   OPENSSL_cleanse(&g_scalar_storage, sizeof(g_scalar_storage));
   OPENSSL_cleanse(&p_scalar_storage, sizeof(p_scalar_storage));
   return ret;
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 5e5ce94..139840e 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -439,6 +439,52 @@
       << "p * order did not return point at infinity.";
 }
 
+// Test that |EC_POINT_mul| works with out-of-range scalars. Even beyond the
+// usual |bn_correct_top| disclaimer, we completely disclaim all hope here as a
+// reduction is needed, but we'll compute the right answer.
+TEST_P(ECCurveTest, MulOutOfRange) {
+  bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(GetParam().nid));
+  ASSERT_TRUE(group);
+
+  bssl::UniquePtr<BIGNUM> n_minus_one(BN_dup(EC_GROUP_get0_order(group.get())));
+  ASSERT_TRUE(n_minus_one);
+  ASSERT_TRUE(BN_sub_word(n_minus_one.get(), 1));
+
+  bssl::UniquePtr<BIGNUM> minus_one(BN_new());
+  ASSERT_TRUE(minus_one);
+  ASSERT_TRUE(BN_one(minus_one.get()));
+  BN_set_negative(minus_one.get(), 1);
+
+  bssl::UniquePtr<BIGNUM> seven(BN_new());
+  ASSERT_TRUE(seven);
+  ASSERT_TRUE(BN_set_word(seven.get(), 7));
+
+  bssl::UniquePtr<BIGNUM> ten_n_plus_seven(
+      BN_dup(EC_GROUP_get0_order(group.get())));
+  ASSERT_TRUE(ten_n_plus_seven);
+  ASSERT_TRUE(BN_mul_word(ten_n_plus_seven.get(), 10));
+  ASSERT_TRUE(BN_add_word(ten_n_plus_seven.get(), 7));
+
+  bssl::UniquePtr<EC_POINT> point1(EC_POINT_new(group.get())),
+      point2(EC_POINT_new(group.get()));
+  ASSERT_TRUE(point1);
+  ASSERT_TRUE(point2);
+
+  ASSERT_TRUE(EC_POINT_mul(group.get(), point1.get(), n_minus_one.get(),
+                           nullptr, nullptr, nullptr));
+  ASSERT_TRUE(EC_POINT_mul(group.get(), point2.get(), minus_one.get(), nullptr,
+                           nullptr, nullptr));
+  EXPECT_EQ(0, EC_POINT_cmp(group.get(), point1.get(), point2.get(), nullptr))
+      << "-1 * G and (n-1) * G did not give the same result";
+
+  ASSERT_TRUE(EC_POINT_mul(group.get(), point1.get(), seven.get(), nullptr,
+                           nullptr, nullptr));
+  ASSERT_TRUE(EC_POINT_mul(group.get(), point2.get(), ten_n_plus_seven.get(),
+                           nullptr, nullptr, nullptr));
+  EXPECT_EQ(0, EC_POINT_cmp(group.get(), point1.get(), point2.get(), nullptr))
+      << "7 * G and (10n + 7) * G did not give the same result";
+}
+
 // Test that 10×∞ + G = G.
 TEST_P(ECCurveTest, Mul) {
   bssl::UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(GetParam().nid));
diff --git a/src/crypto/obj/obj.c b/src/crypto/obj/obj.c
index 52e265b..a34d6dc 100644
--- a/src/crypto/obj/obj.c
+++ b/src/crypto/obj/obj.c
@@ -389,16 +389,30 @@
   return obj->ln;
 }
 
-ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
-  int nid = NID_undef;
-  ASN1_OBJECT *op = NULL;
-  unsigned char *buf;
-  unsigned char *p;
-  const unsigned char *bufp;
-  int contents_len, total_len;
+static ASN1_OBJECT *create_object_with_text_oid(int (*get_nid)(void),
+                                                const char *oid,
+                                                const char *short_name,
+                                                const char *long_name) {
+  uint8_t *buf;
+  size_t len;
+  CBB cbb;
+  if (!CBB_init(&cbb, 32) ||
+      !CBB_add_asn1_oid_from_text(&cbb, oid, strlen(oid)) ||
+      !CBB_finish(&cbb, &buf, &len)) {
+    OPENSSL_PUT_ERROR(OBJ, OBJ_R_INVALID_OID_STRING);
+    CBB_cleanup(&cbb);
+    return NULL;
+  }
 
+  ASN1_OBJECT *ret = ASN1_OBJECT_create(get_nid ? get_nid() : NID_undef, buf,
+                                        len, short_name, long_name);
+  OPENSSL_free(buf);
+  return ret;
+}
+
+ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
   if (!dont_search_names) {
-    nid = OBJ_sn2nid(s);
+    int nid = OBJ_sn2nid(s);
     if (nid == NID_undef) {
       nid = OBJ_ln2nid(s);
     }
@@ -408,31 +422,7 @@
     }
   }
 
-  // Work out size of content octets
-  contents_len = a2d_ASN1_OBJECT(NULL, 0, s, -1);
-  if (contents_len <= 0) {
-    return NULL;
-  }
-  // Work out total size
-  total_len = ASN1_object_size(0, contents_len, V_ASN1_OBJECT);
-
-  buf = OPENSSL_malloc(total_len);
-  if (buf == NULL) {
-    OPENSSL_PUT_ERROR(OBJ, ERR_R_MALLOC_FAILURE);
-    return NULL;
-  }
-
-  p = buf;
-  // Write out tag+length
-  ASN1_put_object(&p, 0, contents_len, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
-  // Write out contents
-  a2d_ASN1_OBJECT(p, contents_len, s, -1);
-
-  bufp = buf;
-  op = d2i_ASN1_OBJECT(NULL, &bufp, total_len);
-  OPENSSL_free(buf);
-
-  return op;
+  return create_object_with_text_oid(NULL, s, NULL, NULL);
 }
 
 static int strlcpy_int(char *dst, const char *src, int dst_size) {
@@ -444,36 +434,6 @@
   return (int)ret;
 }
 
-static int parse_oid_component(CBS *cbs, uint64_t *out) {
-  uint64_t v = 0;
-  uint8_t b;
-  do {
-    if (!CBS_get_u8(cbs, &b)) {
-      return 0;
-    }
-    if ((v >> (64 - 7)) != 0) {
-      // The component is too large.
-      return 0;
-    }
-    if (v == 0 && b == 0x80) {
-      // The component must be minimally encoded.
-      return 0;
-    }
-    v = (v << 7) | (b & 0x7f);
-
-    // Components end at an octet with the high bit cleared.
-  } while (b & 0x80);
-
-  *out = v;
-  return 1;
-}
-
-static int add_decimal(CBB *out, uint64_t v) {
-  char buf[DECIMAL_SIZE(uint64_t) + 1];
-  BIO_snprintf(buf, sizeof(buf), "%" PRIu64, v);
-  return CBB_add_bytes(out, (const uint8_t *)buf, strlen(buf));
-}
-
 int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj,
                 int always_return_oid) {
   // Python depends on the empty OID successfully encoding as the empty
@@ -495,56 +455,19 @@
     }
   }
 
-  CBB cbb;
-  if (!CBB_init(&cbb, 32)) {
-    goto err;
-  }
-
   CBS cbs;
   CBS_init(&cbs, obj->data, obj->length);
-
-  // The first component is 40 * value1 + value2, where value1 is 0, 1, or 2.
-  uint64_t v;
-  if (!parse_oid_component(&cbs, &v)) {
-    goto err;
-  }
-
-  if (v >= 80) {
-    if (!CBB_add_bytes(&cbb, (const uint8_t *)"2.", 2) ||
-        !add_decimal(&cbb, v - 80)) {
-      goto err;
+  char *txt = CBS_asn1_oid_to_text(&cbs);
+  if (txt == NULL) {
+    if (out_len > 0) {
+      out[0] = '\0';
     }
-  } else if (!add_decimal(&cbb, v / 40) ||
-             !CBB_add_u8(&cbb, '.') ||
-             !add_decimal(&cbb, v % 40)) {
-    goto err;
+    return -1;
   }
 
-  while (CBS_len(&cbs) != 0) {
-    if (!parse_oid_component(&cbs, &v) ||
-        !CBB_add_u8(&cbb, '.') ||
-        !add_decimal(&cbb, v)) {
-      goto err;
-    }
-  }
-
-  uint8_t *txt;
-  size_t txt_len;
-  if (!CBB_add_u8(&cbb, '\0') ||
-      !CBB_finish(&cbb, &txt, &txt_len)) {
-    goto err;
-  }
-
-  int ret = strlcpy_int(out, (const char *)txt, out_len);
+  int ret = strlcpy_int(out, txt, out_len);
   OPENSSL_free(txt);
   return ret;
-
-err:
-  CBB_cleanup(&cbb);
-  if (out_len > 0) {
-    out[0] = '\0';
-  }
-  return -1;
 }
 
 static uint32_t hash_nid(const ASN1_OBJECT *obj) {
@@ -621,41 +544,11 @@
 }
 
 int OBJ_create(const char *oid, const char *short_name, const char *long_name) {
-  int ret = NID_undef;
-  ASN1_OBJECT *op = NULL;
-  unsigned char *buf = NULL;
-  int len;
-
-  len = a2d_ASN1_OBJECT(NULL, 0, oid, -1);
-  if (len <= 0) {
-    goto err;
+  ASN1_OBJECT *op =
+      create_object_with_text_oid(obj_next_nid, oid, short_name, long_name);
+  if (op == NULL ||
+      !obj_add_object(op)) {
+    return NID_undef;
   }
-
-  buf = OPENSSL_malloc(len);
-  if (buf == NULL) {
-    OPENSSL_PUT_ERROR(OBJ, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-
-  len = a2d_ASN1_OBJECT(buf, len, oid, -1);
-  if (len == 0) {
-    goto err;
-  }
-
-  op = (ASN1_OBJECT *)ASN1_OBJECT_create(obj_next_nid(), buf, len, short_name,
-                                         long_name);
-  if (op == NULL) {
-    goto err;
-  }
-
-  if (obj_add_object(op)) {
-    ret = op->nid;
-  }
-  op = NULL;
-
-err:
-  ASN1_OBJECT_free(op);
-  OPENSSL_free(buf);
-
-  return ret;
+  return op->nid;
 }
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index cd4e61d..158fd45 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -26,6 +26,7 @@
 #include <openssl/pem.h>
 #include <openssl/pool.h>
 #include <openssl/x509.h>
+#include <openssl/x509v3.h>
 
 #include "../internal.h"
 
@@ -834,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(0x30, enc_pointer[0]);
+  ASSERT_EQ(CBS_ASN1_SEQUENCE, enc_pointer[0]);
 }
 
 TEST(X509Test, TestFromBufferWithTrailingData) {
@@ -996,3 +997,41 @@
               std::string(reinterpret_cast<const char *>(contents), len));
   }
 }
+
+TEST(X509Test, PrettyPrintIntegers) {
+  static const char *kTests[] = {
+      // Small numbers are pretty-printed in decimal.
+      "0",
+      "-1",
+      "1",
+      "42",
+      "-42",
+      "256",
+      "-256",
+      // Large numbers are pretty-printed in hex to avoid taking quadratic time.
+      "0x0123456789",
+      "-0x0123456789",
+  };
+  for (const char *in : kTests) {
+    SCOPED_TRACE(in);
+    BIGNUM *bn = nullptr;
+    ASSERT_TRUE(BN_asc2bn(&bn, in));
+    bssl::UniquePtr<BIGNUM> free_bn(bn);
+
+    {
+      bssl::UniquePtr<ASN1_INTEGER> asn1(BN_to_ASN1_INTEGER(bn, nullptr));
+      ASSERT_TRUE(asn1);
+      bssl::UniquePtr<char> out(i2s_ASN1_INTEGER(nullptr, asn1.get()));
+      ASSERT_TRUE(out.get());
+      EXPECT_STREQ(in, out.get());
+    }
+
+    {
+      bssl::UniquePtr<ASN1_ENUMERATED> asn1(BN_to_ASN1_ENUMERATED(bn, nullptr));
+      ASSERT_TRUE(asn1);
+      bssl::UniquePtr<char> out(i2s_ASN1_ENUMERATED(nullptr, asn1.get()));
+      ASSERT_TRUE(out.get());
+      EXPECT_STREQ(in, out.get());
+    }
+  }
+}
diff --git a/src/crypto/x509v3/v3_utl.c b/src/crypto/x509v3/v3_utl.c
index feb3dc6..7d109ee 100644
--- a/src/crypto/x509v3/v3_utl.c
+++ b/src/crypto/x509v3/v3_utl.c
@@ -155,6 +155,45 @@
     return 1;
 }
 
+static char *bignum_to_string(const BIGNUM *bn)
+{
+    char *tmp, *ret;
+    size_t len;
+
+    /*
+     * Display large numbers in hex and small numbers in decimal. Converting to
+     * decimal takes quadratic time and is no more useful than hex for large
+     * numbers.
+     */
+    if (BN_num_bits(bn) < 32) {
+        return BN_bn2dec(bn);
+    }
+
+    tmp = BN_bn2hex(bn);
+    if (tmp == NULL) {
+        return NULL;
+    }
+
+    len = strlen(tmp) + 3;
+    ret = OPENSSL_malloc(len);
+    if (ret == NULL) {
+        OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
+        OPENSSL_free(tmp);
+        return NULL;
+    }
+
+    /* Prepend "0x", but place it after the "-" if negative. */
+    if (tmp[0] == '-') {
+        BUF_strlcpy(ret, "-0x", len);
+        BUF_strlcat(ret, tmp + 1, len);
+    } else {
+        BUF_strlcpy(ret, "0x", len);
+        BUF_strlcat(ret, tmp, len);
+    }
+    OPENSSL_free(tmp);
+    return ret;
+}
+
 char *i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *method, ASN1_ENUMERATED *a)
 {
     BIGNUM *bntmp = NULL;
@@ -162,7 +201,7 @@
     if (!a)
         return NULL;
     if (!(bntmp = ASN1_ENUMERATED_to_BN(a, NULL)) ||
-        !(strtmp = BN_bn2dec(bntmp)))
+        !(strtmp = bignum_to_string(bntmp)))
         OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
     BN_free(bntmp);
     return strtmp;
@@ -175,7 +214,7 @@
     if (!a)
         return NULL;
     if (!(bntmp = ASN1_INTEGER_to_BN(a, NULL)) ||
-        !(strtmp = BN_bn2dec(bntmp)))
+        !(strtmp = bignum_to_string(bntmp)))
         OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
     BN_free(bntmp);
     return strtmp;
diff --git a/src/include/openssl/asn1.h b/src/include/openssl/asn1.h
index 2ef4c97..5c8bf4c 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -737,7 +737,6 @@
 OPENSSL_EXPORT int i2a_ASN1_STRING(BIO *bp, ASN1_STRING *a, int type);
 OPENSSL_EXPORT int i2t_ASN1_OBJECT(char *buf,int buf_len,ASN1_OBJECT *a);
 
-OPENSSL_EXPORT int a2d_ASN1_OBJECT(unsigned char *out,int olen, const char *buf, int num);
 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);
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 3906809..25411c7 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -164,36 +164,34 @@
 #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 << CBS_ASN1_TAG_SHIFT)
+#define CBS_ASN1_CONSTRUCTED 0x20u
 
-// 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)
+// 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
 
-// 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_CLASS_MASK may be ANDed with a tag to query its class.
+#define CBS_ASN1_CLASS_MASK 0xc0u
 
 // CBS_ASN1_TAG_NUMBER_MASK may be ANDed with a tag to query its number.
-#define CBS_ASN1_TAG_NUMBER_MASK ((1u << (5 + CBS_ASN1_TAG_SHIFT)) - 1)
+#define CBS_ASN1_TAG_NUMBER_MASK 0x1fu
 
 // 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
@@ -211,12 +209,16 @@
 // (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);
@@ -279,6 +281,13 @@
 // is indexed starting from zero.
 OPENSSL_EXPORT int CBS_asn1_bitstring_has_bit(const CBS *cbs, unsigned bit);
 
+// CBS_asn1_oid_to_text interprets |cbs| as DER-encoded ASN.1 OBJECT IDENTIFIER
+// contents (not including the element framing) and returns the ASCII
+// representation (e.g., "1.2.840.113554.4.1.72585") in a newly-allocated
+// string, or NULL on failure. The caller must release the result with
+// |OPENSSL_free|.
+OPENSSL_EXPORT char *CBS_asn1_oid_to_text(const CBS *cbs);
+
 
 // CRYPTO ByteBuilder.
 //
@@ -394,7 +403,9 @@
 
 // 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. It returns one on success or zero on error.
+// 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.
 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
@@ -443,6 +454,17 @@
 // error.
 OPENSSL_EXPORT int CBB_add_asn1_uint64(CBB *cbb, uint64_t value);
 
+// CBB_add_asn1_oid_from_text decodes |len| bytes from |text| as an ASCII OID
+// representation, e.g. "1.2.840.113554.4.1.72585", and writes the DER-encoded
+// contents to |cbb|. It returns one on success and zero on malloc failure or if
+// |text| was invalid. It does not include the OBJECT IDENTIFER framing, only
+// the element's contents.
+//
+// This function considers OID strings with components which do not fit in a
+// |uint64_t| to be invalid.
+OPENSSL_EXPORT int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text,
+                                              size_t len);
+
 
 #if defined(__cplusplus)
 }  // extern C
diff --git a/src/include/openssl/err.h b/src/include/openssl/err.h
index 9a65ffb..920c306 100644
--- a/src/include/openssl/err.h
+++ b/src/include/openssl/err.h
@@ -262,14 +262,6 @@
 // ERR_clear_error clears the error queue for the current thread.
 OPENSSL_EXPORT void ERR_clear_error(void);
 
-// ERR_remove_thread_state clears the error queue for the current thread if
-// |tid| is NULL. Otherwise it calls |assert(0)|, because it's no longer
-// possible to delete the error queue for other threads.
-//
-// Error queues are thread-local data and are deleted automatically. You do not
-// need to call this function. Use |ERR_clear_error|.
-OPENSSL_EXPORT void ERR_remove_thread_state(const CRYPTO_THREADID *tid);
-
 // ERR_set_mark "marks" the most recent error for use with |ERR_pop_to_mark|.
 // It returns one if an error was marked and zero if there are no errors.
 OPENSSL_EXPORT int ERR_set_mark(void);
@@ -382,6 +374,14 @@
 // ERR_remove_state calls |ERR_clear_error|.
 OPENSSL_EXPORT void ERR_remove_state(unsigned long pid);
 
+// ERR_remove_thread_state clears the error queue for the current thread if
+// |tid| is NULL. Otherwise it calls |assert(0)|, because it's no longer
+// possible to delete the error queue for other threads.
+//
+// Use |ERR_clear_error| instead. Note error queues are deleted automatically on
+// thread exit. You do not need to call this function to release memory.
+OPENSSL_EXPORT void ERR_remove_thread_state(const CRYPTO_THREADID *tid);
+
 // ERR_func_error_string returns the string "OPENSSL_internal".
 OPENSSL_EXPORT const char *ERR_func_error_string(uint32_t packed_error);
 
diff --git a/src/include/openssl/obj.h b/src/include/openssl/obj.h
index 2bdc3b7..374658e 100644
--- a/src/include/openssl/obj.h
+++ b/src/include/openssl/obj.h
@@ -228,5 +228,6 @@
 #endif
 
 #define OBJ_R_UNKNOWN_NID 100
+#define OBJ_R_INVALID_OID_STRING 101
 
 #endif  // OPENSSL_HEADER_OBJ_H
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 53a8eb5..cdf68a1 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -593,7 +593,7 @@
 
 #define TLS1_3_DRAFT_VERSION 0x7f12
 #define TLS1_3_DRAFT21_VERSION 0x7f15
-#define TLS1_3_DRAFT22_VERSION 0x7e04
+#define TLS1_3_DRAFT22_VERSION 0x7f16
 #define TLS1_3_EXPERIMENT_VERSION 0x7e01
 #define TLS1_3_EXPERIMENT2_VERSION 0x7e02
 #define TLS1_3_EXPERIMENT3_VERSION 0x7e03
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 583aceb..53adba6 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -295,11 +295,6 @@
     return 0;
   }
 
-  // Renegotiations do not participate in session resumption.
-  int has_session_id = ssl->session != NULL &&
-                       !ssl->s3->initial_handshake_complete &&
-                       ssl->session->session_id_length > 0;
-
   CBB child;
   if (!CBB_add_u16(&body, hs->client_version) ||
       !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
@@ -307,19 +302,10 @@
     return 0;
   }
 
-  if (has_session_id) {
-    if (!CBB_add_bytes(&child, ssl->session->session_id,
-                       ssl->session->session_id_length)) {
-      return 0;
-    }
-  } else {
-    // In TLS 1.3 experimental encodings, send a fake placeholder session ID
-    // when we do not otherwise have one to send.
-    if (hs->max_version >= TLS1_3_VERSION &&
-        ssl_is_resumption_variant(ssl->tls13_variant) &&
-        !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
-      return 0;
-    }
+  // Do not send a session ID on renegotiation.
+  if (!ssl->s3->initial_handshake_complete &&
+      !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
+    return 0;
   }
 
   if (SSL_is_dtls(ssl)) {
@@ -472,7 +458,13 @@
 
   // Initialize a random session ID for the experimental TLS 1.3 variant
   // requiring a session id.
-  if (ssl_is_resumption_variant(ssl->tls13_variant)) {
+  if (ssl->session != nullptr &&
+      !ssl->s3->initial_handshake_complete &&
+      ssl->session->session_id_length > 0) {
+    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(ssl->tls13_variant)) {
     hs->session_id_len = sizeof(hs->session_id);
     if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
       return ssl_hs_error;
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 0216401..5343346 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -38,7 +38,7 @@
 	tls13ExperimentVersion  = 0x7e01
 	tls13Experiment2Version = 0x7e02
 	tls13Experiment3Version = 0x7e03
-	tls13Draft22Version     = 0x7e04
+	tls13Draft22Version     = 0x7f16
 )
 
 const (
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 21f491a..ec320ae 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -1383,7 +1383,7 @@
 	}
 
 	if !m.unmarshal(data) {
-		return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+		return nil, c.in.setErrorLocked(c.sendAlert(alertDecodeError))
 	}
 	return m, nil
 }
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index 93da121..93d02e1 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -125,6 +125,121 @@
 	bb.child = nil
 }
 
+type byteReader []byte
+
+func (br *byteReader) readInternal(out *byteReader, n int) bool {
+	if len(*br) < n {
+		return false
+	}
+	*out = (*br)[:n]
+	*br = (*br)[n:]
+	return true
+}
+
+func (br *byteReader) readBytes(out *[]byte, n int) bool {
+	var child byteReader
+	if !br.readInternal(&child, n) {
+		return false
+	}
+	*out = []byte(child)
+	return true
+}
+
+func (br *byteReader) readUint(out *uint64, n int) bool {
+	var b []byte
+	if !br.readBytes(&b, n) {
+		return false
+	}
+	*out = 0
+	for _, v := range b {
+		*out <<= 8
+		*out |= uint64(v)
+	}
+	return true
+}
+
+func (br *byteReader) readU8(out *uint8) bool {
+	var b []byte
+	if !br.readBytes(&b, 1) {
+		return false
+	}
+	*out = b[0]
+	return true
+}
+
+func (br *byteReader) readU16(out *uint16) bool {
+	var v uint64
+	if !br.readUint(&v, 2) {
+		return false
+	}
+	*out = uint16(v)
+	return true
+}
+
+func (br *byteReader) readU24(out *uint32) bool {
+	var v uint64
+	if !br.readUint(&v, 3) {
+		return false
+	}
+	*out = uint32(v)
+	return true
+}
+
+func (br *byteReader) readU32(out *uint32) bool {
+	var v uint64
+	if !br.readUint(&v, 4) {
+		return false
+	}
+	*out = uint32(v)
+	return true
+}
+
+func (br *byteReader) readU64(out *uint64) bool {
+	return br.readUint(out, 8)
+}
+
+func (br *byteReader) readLengthPrefixed(out *byteReader, n int) bool {
+	var length uint64
+	return br.readUint(&length, n) &&
+		uint64(len(*br)) >= length &&
+		br.readInternal(out, int(length))
+}
+
+func (br *byteReader) readLengthPrefixedBytes(out *[]byte, n int) bool {
+	var length uint64
+	return br.readUint(&length, n) &&
+		uint64(len(*br)) >= length &&
+		br.readBytes(out, int(length))
+}
+
+func (br *byteReader) readU8LengthPrefixed(out *byteReader) bool {
+	return br.readLengthPrefixed(out, 1)
+}
+func (br *byteReader) readU8LengthPrefixedBytes(out *[]byte) bool {
+	return br.readLengthPrefixedBytes(out, 1)
+}
+
+func (br *byteReader) readU16LengthPrefixed(out *byteReader) bool {
+	return br.readLengthPrefixed(out, 2)
+}
+func (br *byteReader) readU16LengthPrefixedBytes(out *[]byte) bool {
+	return br.readLengthPrefixedBytes(out, 2)
+}
+
+func (br *byteReader) readU24LengthPrefixed(out *byteReader) bool {
+	return br.readLengthPrefixed(out, 3)
+}
+func (br *byteReader) readU24LengthPrefixedBytes(out *[]byte) bool {
+	return br.readLengthPrefixedBytes(out, 3)
+}
+
+func (br *byteReader) readU32LengthPrefixed(out *byteReader) bool {
+	return br.readLengthPrefixed(out, 4)
+}
+func (br *byteReader) readU32LengthPrefixedBytes(out *[]byte) bool {
+	return br.readLengthPrefixedBytes(out, 4)
+}
+
 type keyShareEntry struct {
 	group       CurveID
 	keyExchange []byte
@@ -422,11 +537,7 @@
 
 		srtpProtectionProfiles := useSrtpExt.addU16LengthPrefixed()
 		for _, p := range m.srtpProtectionProfiles {
-			// An SRTPProtectionProfile is defined as uint8[2],
-			// not uint16. For some reason, we're storing it
-			// as a uint16.
-			srtpProtectionProfiles.addU8(byte(p >> 8))
-			srtpProtectionProfiles.addU8(byte(p))
+			srtpProtectionProfiles.addU16(p)
 		}
 		srtpMki := useSrtpExt.addU8LengthPrefixed()
 		srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier))
@@ -482,58 +593,54 @@
 	return m.raw
 }
 
+func parseSignatureAlgorithms(reader *byteReader, out *[]signatureAlgorithm) bool {
+	var sigAlgs byteReader
+	if !reader.readU16LengthPrefixed(&sigAlgs) {
+		return false
+	}
+	*out = make([]signatureAlgorithm, 0, len(sigAlgs)/2)
+	for len(sigAlgs) > 0 {
+		var v uint16
+		if !sigAlgs.readU16(&v) {
+			return false
+		}
+		*out = append(*out, signatureAlgorithm(v))
+	}
+	return true
+}
+
 func (m *clientHelloMsg) unmarshal(data []byte) bool {
-	if len(data) < 42 {
-		return false
-	}
 	m.raw = data
-	m.vers = uint16(data[4])<<8 | uint16(data[5])
-	m.random = data[6:38]
-	sessionIdLen := int(data[38])
-	if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
+	reader := byteReader(data[4:])
+	if !reader.readU16(&m.vers) ||
+		!reader.readBytes(&m.random, 32) ||
+		!reader.readU8LengthPrefixedBytes(&m.sessionId) ||
+		len(m.sessionId) > 32 {
 		return false
 	}
-	m.sessionId = data[39 : 39+sessionIdLen]
-	data = data[39+sessionIdLen:]
 	if m.isDTLS {
-		if len(data) < 1 {
+		if !reader.readU8LengthPrefixedBytes(&m.cookie) ||
+			len(m.cookie) > 32 {
 			return false
 		}
-		cookieLen := int(data[0])
-		if cookieLen > 32 || len(data) < 1+cookieLen {
+	}
+	var cipherSuites byteReader
+	if !reader.readU16LengthPrefixed(&cipherSuites) ||
+		!reader.readU8LengthPrefixedBytes(&m.compressionMethods) {
+		return false
+	}
+
+	m.cipherSuites = make([]uint16, 0, len(cipherSuites)/2)
+	for len(cipherSuites) > 0 {
+		var v uint16
+		if !cipherSuites.readU16(&v) {
 			return false
 		}
-		m.cookie = data[1 : 1+cookieLen]
-		data = data[1+cookieLen:]
-	}
-	if len(data) < 2 {
-		return false
-	}
-	// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
-	// they are uint16s, the number must be even.
-	cipherSuiteLen := int(data[0])<<8 | int(data[1])
-	if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
-		return false
-	}
-	numCipherSuites := cipherSuiteLen / 2
-	m.cipherSuites = make([]uint16, numCipherSuites)
-	for i := 0; i < numCipherSuites; i++ {
-		m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
-		if m.cipherSuites[i] == scsvRenegotiation {
+		m.cipherSuites = append(m.cipherSuites, v)
+		if v == scsvRenegotiation {
 			m.secureRenegotiation = []byte{}
 		}
 	}
-	data = data[2+cipherSuiteLen:]
-	if len(data) < 1 {
-		return false
-	}
-	compressionMethodsLen := int(data[0])
-	if len(data) < 1+compressionMethodsLen {
-		return false
-	}
-	m.compressionMethods = data[1 : 1+compressionMethodsLen]
-
-	data = data[1+compressionMethodsLen:]
 
 	m.nextProtoNeg = false
 	m.serverName = ""
@@ -549,167 +656,108 @@
 	m.extendedMasterSecret = false
 	m.customExtension = ""
 
-	if len(data) == 0 {
+	if len(reader) == 0 {
 		// ClientHello is optionally followed by extension data
 		return true
 	}
-	if len(data) < 2 {
+
+	var extensions byteReader
+	if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
 		return false
 	}
-
-	extensionsLength := int(data[0])<<8 | int(data[1])
-	data = data[2:]
-	if extensionsLength != len(data) {
-		return false
-	}
-
-	for len(data) != 0 {
-		if len(data) < 4 {
+	for len(extensions) > 0 {
+		var extension uint16
+		var body byteReader
+		if !extensions.readU16(&extension) ||
+			!extensions.readU16LengthPrefixed(&body) {
 			return false
 		}
-		extension := uint16(data[0])<<8 | uint16(data[1])
-		length := int(data[2])<<8 | int(data[3])
-		data = data[4:]
-		if len(data) < length {
-			return false
-		}
-
 		switch extension {
 		case extensionServerName:
-			if length < 2 {
+			var names byteReader
+			if !body.readU16LengthPrefixed(&names) || len(body) != 0 {
 				return false
 			}
-			numNames := int(data[0])<<8 | int(data[1])
-			d := data[2:]
-			for i := 0; i < numNames; i++ {
-				if len(d) < 3 {
-					return false
-				}
-				nameType := d[0]
-				nameLen := int(d[1])<<8 | int(d[2])
-				d = d[3:]
-				if len(d) < nameLen {
+			for len(names) > 0 {
+				var nameType byte
+				var name []byte
+				if !names.readU8(&nameType) ||
+					!names.readU16LengthPrefixedBytes(&name) {
 					return false
 				}
 				if nameType == 0 {
-					m.serverName = string(d[0:nameLen])
-					break
+					m.serverName = string(name)
 				}
-				d = d[nameLen:]
 			}
 		case extensionNextProtoNeg:
-			if length > 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.nextProtoNeg = true
 		case extensionStatusRequest:
-			m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
+			m.ocspStapling = len(body) > 0 && body[0] == statusTypeOCSP
 		case extensionSupportedCurves:
 			// http://tools.ietf.org/html/rfc4492#section-5.5.1
-			if length < 2 {
+			var curves byteReader
+			if !body.readU16LengthPrefixed(&curves) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])<<8 | int(data[1])
-			if l%2 == 1 || length != l+2 {
-				return false
-			}
-			numCurves := l / 2
-			m.supportedCurves = make([]CurveID, numCurves)
-			d := data[2:]
-			for i := 0; i < numCurves; i++ {
-				m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
-				d = d[2:]
+			m.supportedCurves = make([]CurveID, 0, len(curves)/2)
+			for len(curves) > 0 {
+				var v uint16
+				if !curves.readU16(&v) {
+					return false
+				}
+				m.supportedCurves = append(m.supportedCurves, CurveID(v))
 			}
 		case extensionSupportedPoints:
 			// http://tools.ietf.org/html/rfc4492#section-5.5.2
-			if length < 1 {
+			if !body.readU8LengthPrefixedBytes(&m.supportedPoints) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])
-			if length != l+1 {
-				return false
-			}
-			m.supportedPoints = data[1 : 1+l]
 		case extensionSessionTicket:
 			// http://tools.ietf.org/html/rfc5077#section-3.2
 			m.ticketSupported = true
-			m.sessionTicket = data[:length]
+			m.sessionTicket = []byte(body)
 		case extensionKeyShare:
 			// draft-ietf-tls-tls13 section 6.3.2.3
-			if length < 2 {
+			var keyShares byteReader
+			if !body.readU16LengthPrefixed(&keyShares) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])<<8 | int(data[1])
-			if l != length-2 {
-				return false
-			}
-			d := data[2:length]
 			m.hasKeyShares = true
-			for len(d) > 0 {
-				// The next KeyShareEntry contains a NamedGroup (2 bytes) and a
-				// key_exchange (2-byte length prefix with at least 1 byte of content).
-				if len(d) < 5 {
+			for len(keyShares) > 0 {
+				var entry keyShareEntry
+				var group uint16
+				if !keyShares.readU16(&group) ||
+					!keyShares.readU16LengthPrefixedBytes(&entry.keyExchange) {
 					return false
 				}
-				entry := keyShareEntry{}
-				entry.group = CurveID(d[0])<<8 | CurveID(d[1])
-				keyExchLen := int(d[2])<<8 | int(d[3])
-				d = d[4:]
-				if len(d) < keyExchLen {
-					return false
-				}
-				entry.keyExchange = d[:keyExchLen]
-				d = d[keyExchLen:]
+				entry.group = CurveID(group)
 				m.keyShares = append(m.keyShares, entry)
 			}
 		case extensionPreSharedKey:
 			// draft-ietf-tls-tls13-18 section 4.2.6
-			if length < 2 {
+			var psks, binders byteReader
+			if !body.readU16LengthPrefixed(&psks) ||
+				!body.readU16LengthPrefixed(&binders) ||
+				len(body) != 0 {
 				return false
 			}
-			l := int(data[0])<<8 | int(data[1])
-			d := data[2 : l+2]
-			// Parse PSK identities.
-			for len(d) > 0 {
-				if len(d) < 2 {
+			for len(psks) > 0 {
+				var psk pskIdentity
+				if !psks.readU16LengthPrefixedBytes(&psk.ticket) ||
+					!psks.readU32(&psk.obfuscatedTicketAge) {
 					return false
 				}
-				pskLen := int(d[0])<<8 | int(d[1])
-				d = d[2:]
-
-				if len(d) < pskLen+4 {
-					return false
-				}
-				ticket := d[:pskLen]
-				obfuscatedTicketAge := uint32(d[pskLen])<<24 | uint32(d[pskLen+1])<<16 | uint32(d[pskLen+2])<<8 | uint32(d[pskLen+3])
-				psk := pskIdentity{
-					ticket:              ticket,
-					obfuscatedTicketAge: obfuscatedTicketAge,
-				}
 				m.pskIdentities = append(m.pskIdentities, psk)
-				d = d[pskLen+4:]
 			}
-			d = data[l+2:]
-			if len(d) < 2 {
-				return false
-			}
-			l = int(d[0])<<8 | int(d[1])
-			d = d[2:]
-			if l != len(d) {
-				return false
-			}
-			// Parse PSK binders.
-			for len(d) > 0 {
-				if len(d) < 1 {
+			for len(binders) > 0 {
+				var binder []byte
+				if !binders.readU8LengthPrefixedBytes(&binder) {
 					return false
 				}
-				binderLen := int(d[0])
-				d = d[1:]
-				if binderLen > len(d) {
-					return false
-				}
-				m.pskBinders = append(m.pskBinders, d[:binderLen])
-				d = d[binderLen:]
+				m.pskBinders = append(m.pskBinders, binder)
 			}
 
 			// There must be the same number of identities as binders.
@@ -718,121 +766,88 @@
 			}
 		case extensionPSKKeyExchangeModes:
 			// draft-ietf-tls-tls13-18 section 4.2.7
-			if length < 1 {
+			if !body.readU8LengthPrefixedBytes(&m.pskKEModes) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])
-			if l != length-1 {
-				return false
-			}
-			m.pskKEModes = data[1:length]
 		case extensionEarlyData:
 			// draft-ietf-tls-tls13 section 6.3.2.5
-			if length != 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.hasEarlyData = true
 		case extensionCookie:
-			if length < 2 {
+			if !body.readU16LengthPrefixedBytes(&m.tls13Cookie) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])<<8 | int(data[1])
-			if l != length-2 || l == 0 {
-				return false
-			}
-			m.tls13Cookie = data[2 : 2+l]
 		case extensionSignatureAlgorithms:
 			// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
-			if length < 2 || length&1 != 0 {
+			if !parseSignatureAlgorithms(&body, &m.signatureAlgorithms) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])<<8 | int(data[1])
-			if l != length-2 {
-				return false
-			}
-			n := l / 2
-			d := data[2:]
-			m.signatureAlgorithms = make([]signatureAlgorithm, n)
-			for i := range m.signatureAlgorithms {
-				m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1])
-				d = d[2:]
-			}
 		case extensionSupportedVersions:
-			if length < 1+2 {
+			var versions byteReader
+			if !body.readU8LengthPrefixed(&versions) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])
-			if l != length-1 || l%2 == 1 || l < 2 {
-				return false
-			}
-			n := l / 2
-			d := data[1:]
-			m.supportedVersions = make([]uint16, n)
-			for i := range m.supportedVersions {
-				m.supportedVersions[i] = uint16(d[0])<<8 | uint16(d[1])
-				d = d[2:]
-			}
-		case extensionRenegotiationInfo:
-			if length < 1 || length != int(data[0])+1 {
-				return false
-			}
-			m.secureRenegotiation = data[1:length]
-		case extensionALPN:
-			if length < 2 {
-				return false
-			}
-			l := int(data[0])<<8 | int(data[1])
-			if l != length-2 {
-				return false
-			}
-			d := data[2:length]
-			for len(d) != 0 {
-				stringLen := int(d[0])
-				d = d[1:]
-				if stringLen == 0 || stringLen > len(d) {
+			m.supportedVersions = make([]uint16, 0, len(versions)/2)
+			for len(versions) > 0 {
+				var v uint16
+				if !versions.readU16(&v) {
 					return false
 				}
-				m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen]))
-				d = d[stringLen:]
+				m.supportedVersions = append(m.supportedVersions, v)
+			}
+		case extensionRenegotiationInfo:
+			if !body.readU8LengthPrefixedBytes(&m.secureRenegotiation) || len(body) != 0 {
+				return false
+			}
+		case extensionALPN:
+			var protocols byteReader
+			if !body.readU16LengthPrefixed(&protocols) || len(body) != 0 {
+				return false
+			}
+			for len(protocols) > 0 {
+				var protocol []byte
+				if !protocols.readU8LengthPrefixedBytes(&protocol) {
+					return false
+				}
+				m.alpnProtocols = append(m.alpnProtocols, string(protocol))
 			}
 		case extensionChannelID:
-			if length > 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.channelIDSupported = true
 		case extensionExtendedMasterSecret:
-			if length != 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.extendedMasterSecret = true
 		case extensionUseSRTP:
-			if length < 2 {
+			var profiles byteReader
+			var mki []byte
+			if !body.readU16LengthPrefixed(&profiles) ||
+				!body.readU8LengthPrefixedBytes(&mki) ||
+				len(body) != 0 {
 				return false
 			}
-			l := int(data[0])<<8 | int(data[1])
-			if l > length-2 || l%2 != 0 {
-				return false
+			m.srtpProtectionProfiles = make([]uint16, 0, len(profiles)/2)
+			for len(profiles) > 0 {
+				var v uint16
+				if !profiles.readU16(&v) {
+					return false
+				}
+				m.srtpProtectionProfiles = append(m.srtpProtectionProfiles, v)
 			}
-			n := l / 2
-			m.srtpProtectionProfiles = make([]uint16, n)
-			d := data[2:length]
-			for i := 0; i < n; i++ {
-				m.srtpProtectionProfiles[i] = uint16(d[0])<<8 | uint16(d[1])
-				d = d[2:]
-			}
-			if len(d) < 1 || int(d[0]) != len(d)-1 {
-				return false
-			}
-			m.srtpMasterKeyIdentifier = string(d[1:])
+			m.srtpMasterKeyIdentifier = string(mki)
 		case extensionSignedCertificateTimestamp:
-			if length != 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.sctListSupported = true
 		case extensionCustom:
-			m.customExtension = string(data[:length])
+			m.customExtension = string(body)
 		}
-		data = data[length:]
 
 		if isGREASEValue(extension) {
 			m.hasGREASEExtension = true
@@ -955,76 +970,56 @@
 }
 
 func (m *serverHelloMsg) unmarshal(data []byte) bool {
-	if len(data) < 42 {
+	m.raw = data
+	reader := byteReader(data[4:])
+	if !reader.readU16(&m.vers) ||
+		!reader.readBytes(&m.random, 32) {
 		return false
 	}
-	m.raw = data
-	m.vers = uint16(data[4])<<8 | uint16(data[5])
 	vers, ok := wireToVersion(m.vers, m.isDTLS)
 	if !ok {
 		return false
 	}
-	m.random = data[6:38]
-	data = data[38:]
 	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
-		sessionIdLen := int(data[0])
-		if sessionIdLen > 32 || len(data) < 1+sessionIdLen {
+		if !reader.readU8LengthPrefixedBytes(&m.sessionId) {
 			return false
 		}
-		m.sessionId = data[1 : 1+sessionIdLen]
-		data = data[1+sessionIdLen:]
 	}
-	if len(data) < 2 {
+	if !reader.readU16(&m.cipherSuite) {
 		return false
 	}
-	m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
-	data = data[2:]
 	if vers < VersionTLS13 || isResumptionExperiment(m.vers) {
-		if len(data) < 1 {
+		if !reader.readU8(&m.compressionMethod) {
 			return false
 		}
-		m.compressionMethod = data[0]
-		data = data[1:]
 	}
 
-	if len(data) == 0 && m.vers < VersionTLS13 {
+	if len(reader) == 0 && m.vers < VersionTLS13 {
 		// Extension data is optional before TLS 1.3.
 		m.extensions = serverExtensions{}
 		m.omitExtensions = true
 		return true
 	}
-	if len(data) < 2 {
-		return false
-	}
 
-	extensionsLength := int(data[0])<<8 | int(data[1])
-	data = data[2:]
-	if len(data) != extensionsLength {
+	var extensions byteReader
+	if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
 		return false
 	}
 
 	// Parse out the version from supported_versions if available.
 	if m.vers == VersionTLS12 {
-		vdata := data
-		for len(vdata) != 0 {
-			if len(vdata) < 4 {
+		extensionsCopy := extensions
+		for len(extensionsCopy) > 0 {
+			var extension uint16
+			var body byteReader
+			if !extensionsCopy.readU16(&extension) ||
+				!extensionsCopy.readU16LengthPrefixed(&body) {
 				return false
 			}
-			extension := uint16(vdata[0])<<8 | uint16(vdata[1])
-			length := int(vdata[2])<<8 | int(vdata[3])
-			vdata = vdata[4:]
-
-			if len(vdata) < length {
-				return false
-			}
-			d := vdata[:length]
-			vdata = vdata[length:]
-
 			if extension == extensionSupportedVersions {
-				if len(d) < 2 {
+				if !body.readU16(&m.vers) || len(body) != 0 {
 					return false
 				}
-				m.vers = uint16(d[0])<<8 | uint16(d[1])
 				vers, ok = wireToVersion(m.vers, m.isDTLS)
 				if !ok {
 					return false
@@ -1034,38 +1029,27 @@
 	}
 
 	if vers >= VersionTLS13 {
-		for len(data) != 0 {
-			if len(data) < 4 {
+		for len(extensions) > 0 {
+			var extension uint16
+			var body byteReader
+			if !extensions.readU16(&extension) ||
+				!extensions.readU16LengthPrefixed(&body) {
 				return false
 			}
-			extension := uint16(data[0])<<8 | uint16(data[1])
-			length := int(data[2])<<8 | int(data[3])
-			data = data[4:]
-
-			if len(data) < length {
-				return false
-			}
-			d := data[:length]
-			data = data[length:]
-
 			switch extension {
 			case extensionKeyShare:
 				m.hasKeyShare = true
-				if len(d) < 4 {
+				var group uint16
+				if !body.readU16(&group) ||
+					!body.readU16LengthPrefixedBytes(&m.keyShare.keyExchange) ||
+					len(body) != 0 {
 					return false
 				}
-				m.keyShare.group = CurveID(uint16(d[0])<<8 | uint16(d[1]))
-				keyExchLen := int(d[2])<<8 | int(d[3])
-				if keyExchLen != len(d)-4 {
-					return false
-				}
-				m.keyShare.keyExchange = make([]byte, keyExchLen)
-				copy(m.keyShare.keyExchange, d[4:])
+				m.keyShare.group = CurveID(group)
 			case extensionPreSharedKey:
-				if len(d) != 2 {
+				if !body.readU16(&m.pskIdentity) || len(body) != 0 {
 					return false
 				}
-				m.pskIdentity = uint16(d[0])<<8 | uint16(d[1])
 				m.hasPSKIdentity = true
 			case extensionSupportedVersions:
 				if !isResumptionExperiment(m.vers) {
@@ -1077,7 +1061,7 @@
 				return false
 			}
 		}
-	} else if !m.extensions.unmarshal(data, vers) {
+	} else if !m.extensions.unmarshal(extensions, vers) {
 		return false
 	}
 
@@ -1109,23 +1093,12 @@
 
 func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool {
 	m.raw = data
-	if len(data) < 6 {
+	reader := byteReader(data[4:])
+	var extensions byteReader
+	if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
 		return false
 	}
-	if data[0] != typeEncryptedExtensions {
-		return false
-	}
-	msgLen := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
-	data = data[4:]
-	if len(data) != msgLen {
-		return false
-	}
-	extLen := int(data[0])<<8 | int(data[1])
-	data = data[2:]
-	if extLen != len(data) {
-		return false
-	}
-	return m.extensions.unmarshal(data, VersionTLS13)
+	return m.extensions.unmarshal(extensions, VersionTLS13)
 }
 
 type serverExtensions struct {
@@ -1211,8 +1184,7 @@
 		extension := extensions.addU16LengthPrefixed()
 
 		srtpProtectionProfiles := extension.addU16LengthPrefixed()
-		srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile >> 8))
-		srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile))
+		srtpProtectionProfiles.addU16(m.srtpProtectionProfile)
 		srtpMki := extension.addU8LengthPrefixed()
 		srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier))
 	}
@@ -1276,96 +1248,77 @@
 	}
 }
 
-func (m *serverExtensions) unmarshal(data []byte, version uint16) bool {
+func (m *serverExtensions) unmarshal(data byteReader, version uint16) bool {
 	// Reset all fields.
 	*m = serverExtensions{}
 
-	for len(data) != 0 {
-		if len(data) < 4 {
+	for len(data) > 0 {
+		var extension uint16
+		var body byteReader
+		if !data.readU16(&extension) ||
+			!data.readU16LengthPrefixed(&body) {
 			return false
 		}
-		extension := uint16(data[0])<<8 | uint16(data[1])
-		length := int(data[2])<<8 | int(data[3])
-		data = data[4:]
-		if len(data) < length {
-			return false
-		}
-
 		switch extension {
 		case extensionNextProtoNeg:
 			m.nextProtoNeg = true
-			d := data[:length]
-			for len(d) > 0 {
-				l := int(d[0])
-				d = d[1:]
-				if l == 0 || l > len(d) {
+			for len(body) > 0 {
+				var protocol []byte
+				if !body.readU8LengthPrefixedBytes(&protocol) {
 					return false
 				}
-				m.nextProtos = append(m.nextProtos, string(d[:l]))
-				d = d[l:]
+				m.nextProtos = append(m.nextProtos, string(protocol))
 			}
 		case extensionStatusRequest:
-			if length > 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.ocspStapling = true
 		case extensionSessionTicket:
-			if length > 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.ticketSupported = true
 		case extensionRenegotiationInfo:
-			if length < 1 || length != int(data[0])+1 {
+			if !body.readU8LengthPrefixedBytes(&m.secureRenegotiation) || len(body) != 0 {
 				return false
 			}
-			m.secureRenegotiation = data[1:length]
 		case extensionALPN:
-			d := data[:length]
-			if len(d) < 3 {
+			var protocols, protocol byteReader
+			if !body.readU16LengthPrefixed(&protocols) ||
+				len(body) != 0 ||
+				!protocols.readU8LengthPrefixed(&protocol) ||
+				len(protocols) != 0 {
 				return false
 			}
-			l := int(d[0])<<8 | int(d[1])
-			if l != len(d)-2 {
-				return false
-			}
-			d = d[2:]
-			l = int(d[0])
-			if l != len(d)-1 {
-				return false
-			}
-			d = d[1:]
-			m.alpnProtocol = string(d)
-			m.alpnProtocolEmpty = len(d) == 0
+			m.alpnProtocol = string(protocol)
+			m.alpnProtocolEmpty = len(protocol) == 0
 		case extensionChannelID:
-			if length > 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.channelIDRequested = true
 		case extensionExtendedMasterSecret:
-			if length != 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.extendedMasterSecret = true
 		case extensionUseSRTP:
-			if length < 2+2+1 {
+			var profiles, mki byteReader
+			if !body.readU16LengthPrefixed(&profiles) ||
+				!profiles.readU16(&m.srtpProtectionProfile) ||
+				len(profiles) != 0 ||
+				!body.readU8LengthPrefixed(&mki) ||
+				len(body) != 0 {
 				return false
 			}
-			if data[0] != 0 || data[1] != 2 {
-				return false
-			}
-			m.srtpProtectionProfile = uint16(data[2])<<8 | uint16(data[3])
-			d := data[4:length]
-			l := int(d[0])
-			if l != len(d)-1 {
-				return false
-			}
-			m.srtpMasterKeyIdentifier = string(d[1:])
+			m.srtpMasterKeyIdentifier = string(mki)
 		case extensionSignedCertificateTimestamp:
-			m.sctList = data[:length]
+			m.sctList = []byte(body)
 		case extensionCustom:
-			m.customExtension = string(data[:length])
+			m.customExtension = string(body)
 		case extensionServerName:
-			if length != 0 {
+			if len(body) != 0 {
 				return false
 			}
 			m.serverNameAck = true
@@ -1375,21 +1328,16 @@
 				return false
 			}
 			// http://tools.ietf.org/html/rfc4492#section-5.5.2
-			if length < 1 {
+			if !body.readU8LengthPrefixedBytes(&m.supportedPoints) || len(body) != 0 {
 				return false
 			}
-			l := int(data[0])
-			if length != l+1 {
-				return false
-			}
-			m.supportedPoints = data[1 : 1+l]
 		case extensionSupportedCurves:
 			// The server can only send supported_curves in TLS 1.3.
 			if version < VersionTLS13 {
 				return false
 			}
 		case extensionEarlyData:
-			if version < VersionTLS13 || length != 0 {
+			if version < VersionTLS13 || len(body) != 0 {
 				return false
 			}
 			m.hasEarlyData = true
@@ -1397,7 +1345,6 @@
 			// Unknown extensions are illegal from the server.
 			return false
 		}
-		data = data[length:]
 	}
 
 	return true
@@ -1409,6 +1356,7 @@
 	isServerHello       bool
 	sessionId           []byte
 	cipherSuite         uint16
+	compressionMethod   uint8
 	hasSelectedGroup    bool
 	selectedGroup       CurveID
 	cookie              []byte
@@ -1435,7 +1383,7 @@
 		sessionId := retryRequest.addU8LengthPrefixed()
 		sessionId.addBytes(m.sessionId)
 		retryRequest.addU16(m.cipherSuite)
-		retryRequest.addU8(0)
+		retryRequest.addU8(m.compressionMethod)
 	} else {
 		retryRequest.addU16(m.vers)
 		if isDraft21(m.vers) {
@@ -1478,73 +1426,56 @@
 
 func (m *helloRetryRequestMsg) unmarshal(data []byte) bool {
 	m.raw = data
-	if len(data) < 8 {
+	reader := byteReader(data[4:])
+	if !reader.readU16(&m.vers) {
 		return false
 	}
-	m.vers = uint16(data[4])<<8 | uint16(data[5])
-	data = data[6:]
 	if m.isServerHello {
-		if len(data) < 33 {
+		var random []byte
+		var compressionMethod byte
+		if !reader.readBytes(&random, 32) ||
+			!reader.readU8LengthPrefixedBytes(&m.sessionId) ||
+			!reader.readU16(&m.cipherSuite) ||
+			!reader.readU8(&compressionMethod) ||
+			compressionMethod != 0 {
 			return false
 		}
-		data = data[32:] // Random
-		sessionIdLen := int(data[0])
-		if sessionIdLen > 32 || len(data) < 1+sessionIdLen+3 {
-			return false
-		}
-		m.sessionId = data[1 : 1+sessionIdLen]
-		data = data[1+sessionIdLen:]
-		m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
-		data = data[2:]
-		data = data[1:] // Compression Method
-	} else {
-		if isDraft21(m.vers) {
-			m.cipherSuite = uint16(data[0])<<8 | uint16(data[1])
-			data = data[2:]
-		}
-	}
-	extLen := int(data[0])<<8 | int(data[1])
-	data = data[2:]
-	if len(data) != extLen || len(data) == 0 {
+	} else if isDraft21(m.vers) && !reader.readU16(&m.cipherSuite) {
 		return false
 	}
-	for len(data) > 0 {
-		if len(data) < 4 {
+	var extensions byteReader
+	if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 {
+		return false
+	}
+	for len(extensions) > 0 {
+		var extension uint16
+		var body byteReader
+		if !extensions.readU16(&extension) ||
+			!extensions.readU16LengthPrefixed(&body) {
 			return false
 		}
-		extension := uint16(data[0])<<8 | uint16(data[1])
-		length := int(data[2])<<8 | int(data[3])
-		data = data[4:]
-		if len(data) < length {
-			return false
-		}
-
 		switch extension {
 		case extensionSupportedVersions:
-			if length != 2 || !m.isServerHello {
+			if !m.isServerHello ||
+				!body.readU16(&m.vers) ||
+				len(body) != 0 {
 				return false
 			}
-			m.vers = uint16(data[0])<<8 | uint16(data[1])
 		case extensionKeyShare:
-			if length != 2 {
+			var v uint16
+			if !body.readU16(&v) || len(body) != 0 {
 				return false
 			}
 			m.hasSelectedGroup = true
-			m.selectedGroup = CurveID(data[0])<<8 | CurveID(data[1])
+			m.selectedGroup = CurveID(v)
 		case extensionCookie:
-			if length < 2 {
+			if !body.readU16LengthPrefixedBytes(&m.cookie) || len(body) != 0 {
 				return false
 			}
-			cookieLen := int(data[0])<<8 | int(data[1])
-			if 2+cookieLen != length {
-				return false
-			}
-			m.cookie = data[2 : 2+cookieLen]
 		default:
 			// Unknown extensions are illegal from the server.
 			return false
 		}
-		data = data[length:]
 	}
 	return true
 }
@@ -1613,85 +1544,46 @@
 }
 
 func (m *certificateMsg) unmarshal(data []byte) bool {
-	if len(data) < 4 {
-		return false
-	}
-
 	m.raw = data
-	data = data[4:]
+	reader := byteReader(data[4:])
 
-	if m.hasRequestContext {
-		if len(data) == 0 {
-			return false
-		}
-		contextLen := int(data[0])
-		if len(data) < 1+contextLen {
-			return false
-		}
-		m.requestContext = make([]byte, contextLen)
-		copy(m.requestContext, data[1:])
-		data = data[1+contextLen:]
-	}
-
-	if len(data) < 3 {
-		return false
-	}
-	certsLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
-	data = data[3:]
-	if len(data) != certsLen {
+	if m.hasRequestContext && !reader.readU8LengthPrefixedBytes(&m.requestContext) {
 		return false
 	}
 
+	var certs byteReader
+	if !reader.readU24LengthPrefixed(&certs) || len(reader) != 0 {
+		return false
+	}
 	m.certificates = nil
-	for len(data) != 0 {
-		if len(data) < 3 {
+	for len(certs) > 0 {
+		var cert certificateEntry
+		if !certs.readU24LengthPrefixedBytes(&cert.data) {
 			return false
 		}
-		certLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2])
-		if len(data) < 3+certLen {
-			return false
-		}
-		cert := certificateEntry{
-			data: data[3 : 3+certLen],
-		}
-		data = data[3+certLen:]
 		if m.hasRequestContext {
-			if len(data) < 2 {
+			var extensions byteReader
+			if !certs.readU16LengthPrefixed(&extensions) {
 				return false
 			}
-			extensionsLen := int(data[0])<<8 | int(data[1])
-			if len(data) < 2+extensionsLen {
-				return false
-			}
-			extensions := data[2 : 2+extensionsLen]
-			data = data[2+extensionsLen:]
-			for len(extensions) != 0 {
-				if len(extensions) < 4 {
+			for len(extensions) > 0 {
+				var extension uint16
+				var body byteReader
+				if !extensions.readU16(&extension) ||
+					!extensions.readU16LengthPrefixed(&body) {
 					return false
 				}
-				extension := uint16(extensions[0])<<8 | uint16(extensions[1])
-				length := int(extensions[2])<<8 | int(extensions[3])
-				if len(extensions) < 4+length {
-					return false
-				}
-				contents := extensions[4 : 4+length]
-				extensions = extensions[4+length:]
-
 				switch extension {
 				case extensionStatusRequest:
-					if length < 4 {
+					var statusType byte
+					if !body.readU8(&statusType) ||
+						statusType != statusTypeOCSP ||
+						!body.readU24LengthPrefixedBytes(&cert.ocspResponse) ||
+						len(body) != 0 {
 						return false
 					}
-					if contents[0] != statusTypeOCSP {
-						return false
-					}
-					respLen := int(contents[1])<<16 | int(contents[2])<<8 | int(contents[3])
-					if respLen+4 != len(contents) || respLen == 0 {
-						return false
-					}
-					cert.ocspResponse = contents[4:]
 				case extensionSignedCertificateTimestamp:
-					cert.sctList = contents
+					cert.sctList = []byte(body)
 				default:
 					return false
 				}
@@ -1712,16 +1604,11 @@
 	if m.raw != nil {
 		return m.raw
 	}
-	length := len(m.key)
-	x := make([]byte, length+4)
-	x[0] = typeServerKeyExchange
-	x[1] = uint8(length >> 16)
-	x[2] = uint8(length >> 8)
-	x[3] = uint8(length)
-	copy(x[4:], m.key)
-
-	m.raw = x
-	return x
+	msg := newByteBuilder()
+	msg.addU8(typeServerKeyExchange)
+	msg.addU24LengthPrefixed().addBytes(m.key)
+	m.raw = msg.finish()
+	return m.raw
 }
 
 func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool {
@@ -1746,19 +1633,12 @@
 
 	var x []byte
 	if m.statusType == statusTypeOCSP {
-		x = make([]byte, 4+4+len(m.response))
-		x[0] = typeCertificateStatus
-		l := len(m.response) + 4
-		x[1] = byte(l >> 16)
-		x[2] = byte(l >> 8)
-		x[3] = byte(l)
-		x[4] = statusTypeOCSP
-
-		l -= 4
-		x[5] = byte(l >> 16)
-		x[6] = byte(l >> 8)
-		x[7] = byte(l)
-		copy(x[8:], m.response)
+		msg := newByteBuilder()
+		msg.addU8(typeCertificateStatus)
+		body := msg.addU24LengthPrefixed()
+		body.addU8(statusTypeOCSP)
+		body.addU24LengthPrefixed().addBytes(m.response)
+		x = msg.finish()
 	} else {
 		x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType}
 	}
@@ -1769,22 +1649,13 @@
 
 func (m *certificateStatusMsg) unmarshal(data []byte) bool {
 	m.raw = data
-	if len(data) < 5 {
+	reader := byteReader(data[4:])
+	if !reader.readU8(&m.statusType) ||
+		m.statusType != statusTypeOCSP ||
+		!reader.readU24LengthPrefixedBytes(&m.response) ||
+		len(reader) != 0 {
 		return false
 	}
-	m.statusType = data[4]
-
-	m.response = nil
-	if m.statusType == statusTypeOCSP {
-		if len(data) < 8 {
-			return false
-		}
-		respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7])
-		if uint32(len(data)) != 4+4+respLen {
-			return false
-		}
-		m.response = data[8:]
-	}
 	return true
 }
 
@@ -1809,16 +1680,11 @@
 	if m.raw != nil {
 		return m.raw
 	}
-	length := len(m.ciphertext)
-	x := make([]byte, length+4)
-	x[0] = typeClientKeyExchange
-	x[1] = uint8(length >> 16)
-	x[2] = uint8(length >> 8)
-	x[3] = uint8(length)
-	copy(x[4:], m.ciphertext)
-
-	m.raw = x
-	return x
+	msg := newByteBuilder()
+	msg.addU8(typeClientKeyExchange)
+	msg.addU24LengthPrefixed().addBytes(m.ciphertext)
+	m.raw = msg.finish()
+	return m.raw
 }
 
 func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool {
@@ -1839,17 +1705,16 @@
 	verifyData []byte
 }
 
-func (m *finishedMsg) marshal() (x []byte) {
+func (m *finishedMsg) marshal() []byte {
 	if m.raw != nil {
 		return m.raw
 	}
 
-	x = make([]byte, 4+len(m.verifyData))
-	x[0] = typeFinished
-	x[3] = byte(len(m.verifyData))
-	copy(x[4:], m.verifyData)
-	m.raw = x
-	return
+	msg := newByteBuilder()
+	msg.addU8(typeFinished)
+	msg.addU24LengthPrefixed().addBytes(m.verifyData)
+	m.raw = msg.finish()
+	return m.raw
 }
 
 func (m *finishedMsg) unmarshal(data []byte) bool {
@@ -1870,52 +1735,38 @@
 	if m.raw != nil {
 		return m.raw
 	}
-	l := len(m.proto)
-	if l > 255 {
-		l = 255
-	}
 
-	padding := 32 - (l+2)%32
-	length := l + padding + 2
-	x := make([]byte, length+4)
-	x[0] = typeNextProtocol
-	x[1] = uint8(length >> 16)
-	x[2] = uint8(length >> 8)
-	x[3] = uint8(length)
+	padding := 32 - (len(m.proto)+2)%32
 
-	y := x[4:]
-	y[0] = byte(l)
-	copy(y[1:], []byte(m.proto[0:l]))
-	y = y[1+l:]
-	y[0] = byte(padding)
-
-	m.raw = x
-
-	return x
+	msg := newByteBuilder()
+	msg.addU8(typeNextProtocol)
+	body := msg.addU24LengthPrefixed()
+	body.addU8LengthPrefixed().addBytes([]byte(m.proto))
+	body.addU8LengthPrefixed().addBytes(make([]byte, padding))
+	m.raw = msg.finish()
+	return m.raw
 }
 
 func (m *nextProtoMsg) unmarshal(data []byte) bool {
 	m.raw = data
+	reader := byteReader(data[4:])
+	var proto, padding []byte
+	if !reader.readU8LengthPrefixedBytes(&proto) ||
+		!reader.readU8LengthPrefixedBytes(&padding) ||
+		len(reader) != 0 {
+		return false
+	}
+	m.proto = string(proto)
 
-	if len(data) < 5 {
+	// Padding is not meant to be checked normally, but as this is a testing
+	// implementation, we check the padding is as expected.
+	if len(padding) != 32-(len(m.proto)+2)%32 {
 		return false
 	}
-	data = data[4:]
-	protoLen := int(data[0])
-	data = data[1:]
-	if len(data) < protoLen {
-		return false
-	}
-	m.proto = string(data[0:protoLen])
-	data = data[protoLen:]
-
-	if len(data) < 1 {
-		return false
-	}
-	paddingLen := int(data[0])
-	data = data[1:]
-	if len(data) != paddingLen {
-		return false
+	for _, v := range padding {
+		if v != 0 {
+			return false
+		}
 	}
 
 	return true
@@ -2014,167 +1865,72 @@
 	return m.raw
 }
 
-func parseSignatureAlgorithms(data []byte) ([]signatureAlgorithm, []byte, bool) {
-	if len(data) < 2 {
-		return nil, nil, false
+func parseCAs(reader *byteReader, out *[][]byte) bool {
+	var cas byteReader
+	if !reader.readU16LengthPrefixed(&cas) {
+		return false
 	}
-	sigAlgsLen := int(data[0])<<8 | int(data[1])
-	data = data[2:]
-	if sigAlgsLen&1 != 0 {
-		return nil, nil, false
-	}
-	if len(data) < int(sigAlgsLen) {
-		return nil, nil, false
-	}
-	numSigAlgs := sigAlgsLen / 2
-	signatureAlgorithms := make([]signatureAlgorithm, numSigAlgs)
-	for i := range signatureAlgorithms {
-		signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1])
-		data = data[2:]
-	}
-
-	return signatureAlgorithms, data, true
-}
-
-func parseCAs(data []byte) ([][]byte, []byte, bool) {
-	if len(data) < 2 {
-		return nil, nil, false
-	}
-	casLength := uint16(data[0])<<8 | uint16(data[1])
-	data = data[2:]
-	if len(data) < int(casLength) {
-		return nil, nil, false
-	}
-
-	cas := data[:casLength]
-	data = data[casLength:]
-
-	var certificateAuthorities [][]byte
 	for len(cas) > 0 {
-		if len(cas) < 2 {
-			return nil, nil, false
+		var ca []byte
+		if !cas.readU16LengthPrefixedBytes(&ca) {
+			return false
 		}
-		caLen := uint16(cas[0])<<8 | uint16(cas[1])
-		cas = cas[2:]
-
-		if len(cas) < int(caLen) {
-			return nil, nil, false
-		}
-
-		certificateAuthorities = append(certificateAuthorities, cas[:caLen])
-		cas = cas[caLen:]
+		*out = append(*out, ca)
 	}
-	return certificateAuthorities, data, true
+	return true
 }
 
 func (m *certificateRequestMsg) unmarshal(data []byte) bool {
 	m.raw = data
+	reader := byteReader(data[4:])
 
-	if len(data) < 5 {
-		return false
-	}
-	data = data[4:]
-
-	if m.hasRequestContext {
-		contextLen := int(data[0])
-		if len(data) < 1+contextLen {
+	if isDraft21(m.vers) {
+		var extensions byteReader
+		if !reader.readU8LengthPrefixedBytes(&m.requestContext) ||
+			!reader.readU16LengthPrefixed(&extensions) ||
+			len(reader) != 0 {
 			return false
 		}
-		m.requestContext = make([]byte, contextLen)
-		copy(m.requestContext, data[1:])
-		data = data[1+contextLen:]
-		if isDraft21(m.vers) {
-			if len(data) < 2 {
+		for len(extensions) > 0 {
+			var extension uint16
+			var body byteReader
+			if !extensions.readU16(&extension) ||
+				!extensions.readU16LengthPrefixed(&body) {
 				return false
 			}
-			extensionsLen := int(data[0])<<8 | int(data[1])
-			if len(data) < 2+extensionsLen {
-				return false
-			}
-			extensions := data[2 : 2+extensionsLen]
-			data = data[2+extensionsLen:]
-			for len(extensions) != 0 {
-				if len(extensions) < 4 {
+			switch extension {
+			case extensionSignatureAlgorithms:
+				if !parseSignatureAlgorithms(&body, &m.signatureAlgorithms) || len(body) != 0 {
 					return false
 				}
-				extension := uint16(extensions[0])<<8 | uint16(extensions[1])
-				length := int(extensions[2])<<8 | int(extensions[3])
-				if len(extensions) < 4+length {
+			case extensionCertificateAuthorities:
+				if !parseCAs(&body, &m.certificateAuthorities) || len(body) != 0 {
 					return false
 				}
-				contents := extensions[4 : 4+length]
-				extensions = extensions[4+length:]
-				switch extension {
-				case extensionSignatureAlgorithms:
-					sigAlgs, rest, ok := parseSignatureAlgorithms(contents)
-					if !ok || len(rest) != 0 {
-						return false
-					}
-					m.signatureAlgorithms = sigAlgs
-				case extensionCertificateAuthorities:
-					cas, rest, ok := parseCAs(contents)
-					if !ok || len(rest) != 0 {
-						return false
-					}
-					m.hasCAExtension = true
-					m.certificateAuthorities = cas
-				}
+				m.hasCAExtension = true
 			}
-		} else {
-			if m.hasSignatureAlgorithm {
-				sigAlgs, rest, ok := parseSignatureAlgorithms(data)
-				if !ok {
-					return false
-				}
-				m.signatureAlgorithms = sigAlgs
-				data = rest
-			}
-
-			cas, rest, ok := parseCAs(data)
-			if !ok {
-				return false
-			}
-			m.certificateAuthorities = cas
-			data = rest
-
-			// Ignore certificate extensions.
-			if len(data) < 2 {
-				return false
-			}
-			extsLength := int(data[0])<<8 | int(data[1])
-			if len(data) < 2+extsLength {
-				return false
-			}
-			data = data[2+extsLength:]
 		}
+	} else if m.hasRequestContext {
+		var extensions byteReader
+		if !reader.readU8LengthPrefixedBytes(&m.requestContext) ||
+			!parseSignatureAlgorithms(&reader, &m.signatureAlgorithms) ||
+			!parseCAs(&reader, &m.certificateAuthorities) ||
+			!reader.readU16LengthPrefixed(&extensions) ||
+			len(reader) != 0 {
+			return false
+		}
+		// Ignore certificate extensions.
 	} else {
-		numCertTypes := int(data[0])
-		if len(data) < 1+numCertTypes {
+		if !reader.readU8LengthPrefixedBytes(&m.certificateTypes) {
 			return false
 		}
-		m.certificateTypes = make([]byte, numCertTypes)
-		copy(m.certificateTypes, data[1:])
-		data = data[1+numCertTypes:]
-
-		if m.hasSignatureAlgorithm {
-			sigAlgs, rest, ok := parseSignatureAlgorithms(data)
-			if !ok {
-				return false
-			}
-			m.signatureAlgorithms = sigAlgs
-			data = rest
-		}
-
-		cas, rest, ok := parseCAs(data)
-		if !ok {
+		if m.hasSignatureAlgorithm && !parseSignatureAlgorithms(&reader, &m.signatureAlgorithms) {
 			return false
 		}
-		m.certificateAuthorities = cas
-		data = rest
-	}
-
-	if len(data) > 0 {
-		return false
+		if !parseCAs(&reader, &m.certificateAuthorities) ||
+			len(reader) != 0 {
+			return false
+		}
 	}
 
 	return true
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 4286911..595f8da 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -368,6 +368,7 @@
 		isDTLS:                c.isDTLS,
 		vers:                  c.wireVersion,
 		sessionId:             hs.clientHello.sessionId,
+		compressionMethod:     config.Bugs.SendCompressionMethod,
 		versOverride:          config.Bugs.SendServerHelloVersion,
 		supportedVersOverride: config.Bugs.SendServerSupportedExtensionVersion,
 		customExtension:       config.Bugs.CustomUnencryptedExtension,
@@ -534,6 +535,7 @@
 		vers:                c.wireVersion,
 		sessionId:           hs.clientHello.sessionId,
 		cipherSuite:         cipherSuite,
+		compressionMethod:   config.Bugs.SendCompressionMethod,
 		duplicateExtensions: config.Bugs.DuplicateHelloRetryRequestExtensions,
 	}
 
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 8700af2..66c13fc 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -2785,6 +2785,34 @@
 			expectedLocalError: "remote error: illegal parameter",
 		},
 		{
+			testType: clientTest,
+			name:     "TLS13Draft22-InvalidCompressionMethod",
+			config: Config{
+				MaxVersion: VersionTLS13,
+				Bugs: ProtocolBugs{
+					SendCompressionMethod: 1,
+				},
+			},
+			tls13Variant:  TLS13Draft22,
+			shouldFail:    true,
+			expectedError: ":DECODE_ERROR:",
+		},
+		{
+			testType: clientTest,
+			name:     "TLS13Draft22-HRR-InvalidCompressionMethod",
+			config: Config{
+				MaxVersion:       VersionTLS13,
+				CurvePreferences: []CurveID{CurveP384},
+				Bugs: ProtocolBugs{
+					SendCompressionMethod: 1,
+				},
+			},
+			tls13Variant:       TLS13Draft22,
+			shouldFail:         true,
+			expectedError:      ":DECODE_ERROR:",
+			expectedLocalError: "remote error: error decoding message",
+		},
+		{
 			name: "GREASE-Client-TLS12",
 			config: Config{
 				MaxVersion: VersionTLS12,
@@ -7268,6 +7296,25 @@
 		},
 	})
 	testCases = append(testCases, testCase{
+		name: "Renegotiate-Client-TLS13Draft22",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			Bugs: ProtocolBugs{
+				FailIfResumeOnRenego: true,
+			},
+		},
+		tls13Variant: TLS13Draft22,
+		renegotiate:  1,
+		// Test renegotiation after both an initial and resumption
+		// handshake.
+		resumeSession: true,
+		flags: []string{
+			"-renegotiate-freely",
+			"-expect-total-renegotiations", "1",
+			"-expect-secure-renegotiation",
+		},
+	})
+	testCases = append(testCases, testCase{
 		name:        "Renegotiate-Client-EmptyExt",
 		renegotiate: 1,
 		config: Config{
@@ -11017,6 +11064,23 @@
 			resumeSession: true,
 		})
 
+		// Test that the client correctly handles a TLS 1.3 ServerHello which echoes
+		// a TLS 1.2 session ID.
+		testCases = append(testCases, testCase{
+			testType: clientTest,
+			name:     "TLS12SessionID-" + name,
+			config: Config{
+				MaxVersion:             VersionTLS12,
+				SessionTicketsDisabled: true,
+			},
+			resumeConfig: &Config{
+				MaxVersion: VersionTLS13,
+			},
+			tls13Variant:         variant,
+			resumeSession:        true,
+			expectResumeRejected: true,
+		})
+
 		// Test that the server correctly echoes back session IDs of
 		// various lengths.
 		testCases = append(testCases, testCase{
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index 688fa06..f471a4e 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -75,11 +75,14 @@
 
     CBS body = msg.body, server_random, session_id;
     uint16_t server_version;
+    uint8_t compression_method;
     if (!CBS_get_u16(&body, &server_version) ||
         !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
         !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) ||
-        !CBS_skip(&body, 1) ||
+        !CBS_get_u8(&body, &compression_method) ||
+        compression_method != 0 ||
         !CBS_get_u16_length_prefixed(&body, &extensions) ||
         CBS_len(&extensions) == 0 ||
         CBS_len(&body) != 0) {
@@ -251,7 +254,8 @@
   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_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)) ||
diff --git a/src/tool/client.cc b/src/tool/client.cc
index 57e1b6e..fa279ae 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -122,7 +122,8 @@
     },
     {
         "-early-data", kOptionalArgument, "Enable early data. The argument to "
-        "this flag is the early data to send.",
+        "this flag is the early data to send or if it starts with '@', the "
+        "file to read from for early data.",
     },
     {
         "-tls13-variant", kOptionalArgument,
@@ -299,8 +300,19 @@
   }
 
   if (args_map.count("-early-data") != 0 && SSL_in_early_data(ssl.get())) {
-    int ed_size = args_map["-early-data"].size();
-    int ssl_ret = SSL_write(ssl.get(), args_map["-early-data"].data(), ed_size);
+    std::string early_data = args_map["-early-data"];
+    if (early_data.size() > 0 && early_data[0] == '@') {
+      const char *filename = early_data.c_str() + 1;
+      std::vector<uint8_t> data;
+      ScopedFILE f(fopen(filename, "rb"));
+      if (f == nullptr || !ReadAll(&data, f.get())) {
+        fprintf(stderr, "Error reading %s.\n", filename);
+        return false;
+      }
+      early_data = std::string(data.begin(), data.end());
+    }
+    int ed_size = early_data.size();
+    int ssl_ret = SSL_write(ssl.get(), early_data.data(), ed_size);
     if (ssl_ret <= 0) {
       int ssl_err = SSL_get_error(ssl.get(), ssl_ret);
       fprintf(stderr, "Error while writing: %d\n", ssl_err);
diff --git a/src/tool/generate_ed25519.cc b/src/tool/generate_ed25519.cc
index 35b57b9..6499dbe 100644
--- a/src/tool/generate_ed25519.cc
+++ b/src/tool/generate_ed25519.cc
@@ -21,14 +21,6 @@
 #include "internal.h"
 
 
-struct FileCloser {
-  void operator()(FILE *file) {
-    fclose(file);
-  }
-};
-
-using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
-
 static const struct argument kArguments[] = {
     {
         "-out-public", kRequiredArgument, "The file to write the public key to",
diff --git a/src/tool/internal.h b/src/tool/internal.h
index a6c8eca..b626270 100644
--- a/src/tool/internal.h
+++ b/src/tool/internal.h
@@ -44,6 +44,14 @@
   #define BORINGSSL_WRITE write
 #endif
 
+struct FileCloser {
+  void operator()(FILE *file) {
+    fclose(file);
+  }
+};
+
+using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
+
 enum ArgumentType {
   kRequiredArgument,
   kOptionalArgument,
diff --git a/src/tool/server.cc b/src/tool/server.cc
index 0061cb3..9963885 100644
--- a/src/tool/server.cc
+++ b/src/tool/server.cc
@@ -71,6 +71,9 @@
         "-tls13-variant", kBooleanArgument, "Enable TLS 1.3 variants",
     },
     {
+        "-tls13-draft22-variant", kBooleanArgument, "Enable TLS 1.3 Draft 22.",
+    },
+    {
         "-www", kBooleanArgument,
         "The server will print connection information in response to a "
         "HTTP GET request.",
@@ -88,14 +91,6 @@
     },
 };
 
-struct FileCloser {
-  void operator()(FILE *file) {
-    fclose(file);
-  }
-};
-
-using ScopedFILE = std::unique_ptr<FILE, FileCloser>;
-
 static bool LoadOCSPResponse(SSL_CTX *ctx, const char *filename) {
   ScopedFILE f(fopen(filename, "rb"));
   std::vector<uint8_t> data;
@@ -315,8 +310,10 @@
     SSL_CTX_set_early_data_enabled(ctx.get(), 1);
   }
 
-  // Enabling any TLS 1.3 variant on the server enables all of them.
-  if (args_map.count("-tls13-variant") != 0) {
+  // 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);
   }