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;