Rewrite the X509_NAME parser
The old parser was a mess. It has to deal with X509_NAME using a
slightly different in-memory represtation than tasn_dec expects, and it
has to populate cached fields. On top of that, the handling of the cache
was not thread-safe.
It previously worked by first using the tasn_dec machinery to parse a
STACK_OF(STACK_OF(X509_NAME_ENTRY)), and then flattening it. Then it
makes a *new* STACK_OF(STACK_OF(X509_NAME_ENTRY)) with canonicalized
fields. Then it re-encodes that with tasn_enc to save the canonicalized
form.
Instead of all that, parse with CBS so we don't need an allocated
intermediate representation. We can just write it out into the
X509_NAME representation as we parse it.
For the two cached fields, fix the thread-safety issue by wrapping it in
a struct and managing it with an atomic pointer. I had vaguely hoped we
could avoid this, but that seems too complicated with the API we've got.
When computing the cached encodings, rather than making a new allocated
intermediate representation, we can just write a canonicalized encoder
and write it straight into a single CBB, so we only have one growable
array's worth of buffer.
It's faster too!
Before:
Did 1319499 Parse X.509 certificate operations in 5001015us (263846.2 ops/sec)
After:
Did 1794529 Parse X.509 certificate operations in 5000632us (358860.4 ops/sec)
Subsequent CLs will make the API const-correct, test threading, and
embed the X509_NAMEs into the X509 struct.
One nuisance: it's hard for an infallible x509_name_init to initialize
a STACK_OF(X509_NAME_ENTRY). For now I just made the zero state legal
and the add function materializes the STACK_OF on demand. We don't
expose the underlying object, so it doesn't matter much. Really we
should stop believing in fallible mallocs.
Bug: 42290417, 42290269
Change-Id: I0081c75dc06c76c3863eb9418e61262953c9179f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81891
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/a_type.cc b/crypto/asn1/a_type.cc
index de888f1..b714340 100644
--- a/crypto/asn1/a_type.cc
+++ b/crypto/asn1/a_type.cc
@@ -356,10 +356,7 @@
return CBB_add_asn1_bool(out, in->value.boolean != ASN1_BOOLEAN_FALSE);
case V_ASN1_INTEGER:
case V_ASN1_ENUMERATED:
- return asn1_marshal_integer(out, in->value.integer,
- static_cast<CBS_ASN1_TAG>(in->type));
case V_ASN1_BIT_STRING:
- return asn1_marshal_bit_string(out, in->value.bit_string, /*tag=*/0);
case V_ASN1_OCTET_STRING:
case V_ASN1_NUMERICSTRING:
case V_ASN1_PRINTABLESTRING:
@@ -374,14 +371,49 @@
case V_ASN1_UNIVERSALSTRING:
case V_ASN1_BMPSTRING:
case V_ASN1_UTF8STRING:
- return asn1_marshal_octet_string(out, in->value.asn1_string,
+ case V_ASN1_SEQUENCE:
+ case V_ASN1_SET:
+ case V_ASN1_OTHER:
+ return asn1_marshal_any_string(out, in->value.asn1_string);
+ default:
+ // |ASN1_TYPE|s can have type -1 when default-constructed.
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
+ return 0;
+ }
+}
+
+int asn1_marshal_any_string(CBB *out, const ASN1_STRING *in) {
+ switch (in->type) {
+ case V_ASN1_INTEGER:
+ case V_ASN1_NEG_INTEGER:
+ return asn1_marshal_integer(out, in, CBS_ASN1_INTEGER);
+ case V_ASN1_ENUMERATED:
+ case V_ASN1_NEG_ENUMERATED:
+ return asn1_marshal_integer(out, in, CBS_ASN1_ENUMERATED);
+ case V_ASN1_BIT_STRING:
+ return asn1_marshal_bit_string(out, in, /*tag=*/0);
+ case V_ASN1_OCTET_STRING:
+ case V_ASN1_NUMERICSTRING:
+ case V_ASN1_PRINTABLESTRING:
+ case V_ASN1_T61STRING:
+ case V_ASN1_VIDEOTEXSTRING:
+ case V_ASN1_IA5STRING:
+ case V_ASN1_UTCTIME:
+ case V_ASN1_GENERALIZEDTIME:
+ case V_ASN1_GRAPHICSTRING:
+ case V_ASN1_VISIBLESTRING:
+ case V_ASN1_GENERALSTRING:
+ case V_ASN1_UNIVERSALSTRING:
+ case V_ASN1_BMPSTRING:
+ case V_ASN1_UTF8STRING:
+ return asn1_marshal_octet_string(out, in,
static_cast<CBS_ASN1_TAG>(in->type));
case V_ASN1_SEQUENCE:
case V_ASN1_SET:
case V_ASN1_OTHER:
// These three types store the whole TLV as contents.
- return CBB_add_bytes(out, ASN1_STRING_get0_data(in->value.asn1_string),
- ASN1_STRING_length(in->value.asn1_string));
+ return CBB_add_bytes(out, ASN1_STRING_get0_data(in),
+ ASN1_STRING_length(in));
default:
// |ASN1_TYPE|s can have type -1 when default-constructed.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
diff --git a/crypto/asn1/internal.h b/crypto/asn1/internal.h
index 2ddabc8..1ab7fa5 100644
--- a/crypto/asn1/internal.h
+++ b/crypto/asn1/internal.h
@@ -194,6 +194,10 @@
// result to |out|. It returns one on success and zeron on error.
int asn1_marshal_any(CBB *out, const ASN1_TYPE *in);
+// asn1_marshal_any_string marshals |in| as a DER-encoded ASN.1 value and writes
+// the result to |out|. It returns one on success and zeron on error.
+int asn1_marshal_any_string(CBB *out, const ASN1_STRING *in);
+
// Support structures for the template-based encoder.
diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h
index b4d48ca..3bee7cd 100644
--- a/crypto/x509/internal.h
+++ b/crypto/x509/internal.h
@@ -52,7 +52,7 @@
struct X509_name_entry_st {
ASN1_OBJECT *object;
- ASN1_STRING *value;
+ ASN1_STRING value;
int set;
} /* X509_NAME_ENTRY */;
@@ -60,13 +60,19 @@
// (RFC 5280) and C type is |X509_NAME_ENTRY*|.
DECLARE_ASN1_ITEM(X509_NAME_ENTRY)
-// we always keep X509_NAMEs in 2 forms.
+struct X509_NAME_CACHE {
+ // canon contains the DER-encoded canonicalized X.509 Name, not including the
+ // outermost TLV.
+ uint8_t *canon;
+ size_t canon_len;
+ // der contains the DER-encoded X.509 Name, including the outermost TLV.
+ uint8_t *der;
+ size_t der_len;
+};
+
struct X509_name_st {
STACK_OF(X509_NAME_ENTRY) *entries;
- int modified; // true if 'bytes' needs to be built
- BUF_MEM *bytes;
- unsigned char *canon_enc;
- int canon_enclen;
+ mutable bssl::Atomic<X509_NAME_CACHE *> cache;
} /* X509_NAME */;
struct x509_attributes_st {
@@ -567,13 +573,19 @@
// TODO(https://crbug.com/boringssl/695): Remove this.
int DIST_POINT_set_dpname(DIST_POINT_NAME *dpn, X509_NAME *iname);
+void x509_name_init(X509_NAME *name);
+void x509_name_cleanup(X509_NAME *name);
+
+// x509_parse_name parses a DER-encoded, X.509 Name from |cbs| and writes the
+// result to |*out|. It returns one on success and zero on error.
+int x509_parse_name(CBS *cbs, X509_NAME *out);
+
// x509_marshal_name marshals |in| as a DER-encoded, X.509 Name and writes the
// result to |out|. It returns one on success and zero on error.
-//
-// TODO(https://crbug.com/boringssl/407): This function should be const and
-// thread-safe but is currently neither in some cases, notably if |in| was
-// mutated.
-int x509_marshal_name(CBB *out, X509_NAME *in);
+int x509_marshal_name(CBB *out, const X509_NAME *in);
+
+const X509_NAME_CACHE *x509_name_get_cache(const X509_NAME *name);
+void x509_name_invalidate_cache(X509_NAME *name);
void x509_algor_init(X509_ALGOR *alg);
void x509_algor_cleanup(X509_ALGOR *alg);
diff --git a/crypto/x509/v3_ncons.cc b/crypto/x509/v3_ncons.cc
index 7661ea8..c94bb20 100644
--- a/crypto/x509/v3_ncons.cc
+++ b/crypto/x509/v3_ncons.cc
@@ -340,17 +340,19 @@
// subset of the name.
static int nc_dn(X509_NAME *nm, X509_NAME *base) {
- // Ensure canonical encodings are up to date.
- if (nm->modified && i2d_X509_NAME(nm, NULL) < 0) {
+ const X509_NAME_CACHE *nm_cache = x509_name_get_cache(nm);
+ if (nm_cache == nullptr) {
return X509_V_ERR_OUT_OF_MEM;
}
- if (base->modified && i2d_X509_NAME(base, NULL) < 0) {
+ const X509_NAME_CACHE *base_cache = x509_name_get_cache(base);
+ if (base_cache == nullptr) {
return X509_V_ERR_OUT_OF_MEM;
}
- if (base->canon_enclen > nm->canon_enclen) {
+ if (base_cache->canon_len > nm_cache->canon_len) {
return X509_V_ERR_PERMITTED_VIOLATION;
}
- if (OPENSSL_memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen)) {
+ if (OPENSSL_memcmp(base_cache->canon, nm_cache->canon,
+ base_cache->canon_len)) {
return X509_V_ERR_PERMITTED_VIOLATION;
}
return X509_V_OK;
diff --git a/crypto/x509/x509_cmp.cc b/crypto/x509/x509_cmp.cc
index be08ec8..3e5a784 100644
--- a/crypto/x509/x509_cmp.cc
+++ b/crypto/x509/x509_cmp.cc
@@ -94,41 +94,39 @@
}
int X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b) {
- int ret;
-
- // Ensure canonical encoding is present and up to date
-
- if (!a->canon_enc || a->modified) {
- ret = i2d_X509_NAME((X509_NAME *)a, NULL);
- if (ret < 0) {
- return -2;
- }
+ const X509_NAME_CACHE *a_cache = x509_name_get_cache(a);
+ if (a_cache == nullptr) {
+ return -2;
}
-
- if (!b->canon_enc || b->modified) {
- ret = i2d_X509_NAME((X509_NAME *)b, NULL);
- if (ret < 0) {
- return -2;
- }
+ const X509_NAME_CACHE *b_cache = x509_name_get_cache(b);
+ if (b_cache == nullptr) {
+ return -2;
}
-
- ret = a->canon_enclen - b->canon_enclen;
-
- if (ret) {
- return ret;
+ if (a_cache->canon_len < b_cache->canon_len) {
+ return -1;
}
-
- return OPENSSL_memcmp(a->canon_enc, b->canon_enc, a->canon_enclen);
+ if (a_cache->canon_len > b_cache->canon_len) {
+ return 1;
+ }
+ int ret = OPENSSL_memcmp(a_cache->canon, b_cache->canon, a_cache->canon_len);
+ // Canonicalize the return value so it is even possible to distinguish the
+ // error case from a < b, though ideally we would not have an error case.
+ if (ret < 0) {
+ return -1;
+ }
+ if (ret > 0) {
+ return 1;
+ }
+ return 0;
}
uint32_t X509_NAME_hash(X509_NAME *x) {
- // Make sure the X509_NAME structure contains a valid cached encoding.
- if (i2d_X509_NAME(x, NULL) < 0) {
+ const X509_NAME_CACHE *cache = x509_name_get_cache(x);
+ if (cache == nullptr) {
return 0;
}
-
uint8_t md[SHA_DIGEST_LENGTH];
- SHA1(x->canon_enc, x->canon_enclen, md);
+ SHA1(cache->canon, cache->canon_len, md);
return CRYPTO_load_u32_le(md);
}
@@ -136,13 +134,12 @@
// this is reasonably efficient.
uint32_t X509_NAME_hash_old(X509_NAME *x) {
- // Make sure the X509_NAME structure contains a valid cached encoding.
- if (i2d_X509_NAME(x, NULL) < 0) {
+ const X509_NAME_CACHE *cache = x509_name_get_cache(x);
+ if (cache == nullptr) {
return 0;
}
-
- uint8_t md[SHA_DIGEST_LENGTH];
- MD5((const uint8_t *)x->bytes->data, x->bytes->length, md);
+ uint8_t md[MD5_DIGEST_LENGTH];
+ MD5(cache->der, cache->der_len, md);
return CRYPTO_load_u32_le(md);
}
diff --git a/crypto/x509/x509_obj.cc b/crypto/x509/x509_obj.cc
index 6eb5df9..66ef386 100644
--- a/crypto/x509/x509_obj.cc
+++ b/crypto/x509/x509_obj.cc
@@ -73,13 +73,13 @@
}
l1 = strlen(s);
- type = ne->value->type;
- num = ne->value->length;
+ type = ne->value.type;
+ num = ne->value.length;
if (num > NAME_ONELINE_MAX) {
OPENSSL_PUT_ERROR(X509, X509_R_NAME_TOO_LONG);
goto err;
}
- q = ne->value->data;
+ q = ne->value.data;
if ((type == V_ASN1_GENERALSTRING) && ((num % 4) == 0)) {
gs_doit[0] = gs_doit[1] = gs_doit[2] = gs_doit[3] = 0;
@@ -130,7 +130,7 @@
p += l1;
*(p++) = '=';
- q = ne->value->data;
+ q = ne->value.data;
for (j = 0; j < num; j++) {
if (!gs_doit[j & 3]) {
diff --git a/crypto/x509/x509name.cc b/crypto/x509/x509name.cc
index 8f9792c..72c8883 100644
--- a/crypto/x509/x509name.cc
+++ b/crypto/x509/x509name.cc
@@ -130,7 +130,7 @@
STACK_OF(X509_NAME_ENTRY) *sk = name->entries;
X509_NAME_ENTRY *ret = sk_X509_NAME_ENTRY_delete(sk, loc);
size_t n = sk_X509_NAME_ENTRY_num(sk);
- name->modified = 1;
+ x509_name_invalidate_cache(name);
if ((size_t)loc == n) {
return ret;
}
@@ -196,14 +196,17 @@
// guy we are about to stomp on.
int X509_NAME_add_entry(X509_NAME *name, const X509_NAME_ENTRY *entry, int loc,
int set) {
- X509_NAME_ENTRY *new_name = NULL;
- int i, inc;
- STACK_OF(X509_NAME_ENTRY) *sk;
-
- if (name == NULL) {
+ if (name == nullptr) {
return 0;
}
- sk = name->entries;
+ if (name->entries == nullptr) {
+ name->entries = sk_X509_NAME_ENTRY_new_null();
+ if (name->entries == nullptr) {
+ return 0;
+ }
+ }
+
+ STACK_OF(X509_NAME_ENTRY) *sk = name->entries;
int n = (int)sk_X509_NAME_ENTRY_num(sk);
if (loc > n) {
loc = n;
@@ -211,8 +214,8 @@
loc = n;
}
- inc = (set == 0);
- name->modified = 1;
+ bool inc = set == 0;
+ x509_name_invalidate_cache(name);
if (set == -1) {
if (loc == 0) {
@@ -222,7 +225,6 @@
set = sk_X509_NAME_ENTRY_value(sk, loc - 1)->set;
}
} else { // if (set >= 0)
-
if (loc >= n) {
if (loc != 0) {
set = sk_X509_NAME_ENTRY_value(sk, loc - 1)->set + 1;
@@ -234,25 +236,22 @@
}
}
- if ((new_name = X509_NAME_ENTRY_dup(entry)) == NULL) {
- goto err;
+ bssl::UniquePtr<X509_NAME_ENTRY> new_entry(X509_NAME_ENTRY_dup(entry));
+ if (new_entry == nullptr) {
+ return 0;
}
- new_name->set = set;
- if (!sk_X509_NAME_ENTRY_insert(sk, new_name, loc)) {
- goto err;
+ new_entry->set = set;
+ if (!sk_X509_NAME_ENTRY_insert(sk, new_entry.get(), loc)) {
+ return 0;
}
+ new_entry.release(); // |sk| took ownership.
if (inc) {
n = (int)sk_X509_NAME_ENTRY_num(sk);
- for (i = loc + 1; i < n; i++) {
+ for (int i = loc + 1; i < n; i++) {
sk_X509_NAME_ENTRY_value(sk, i)->set += 1;
}
}
return 1;
-err:
- if (new_name != NULL) {
- X509_NAME_ENTRY_free(new_name);
- }
- return 0;
}
X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(X509_NAME_ENTRY **ne,
@@ -333,19 +332,18 @@
return 0;
}
if ((type > 0) && (type & MBSTRING_FLAG)) {
- return ASN1_STRING_set_by_NID(&ne->value, bytes, len, type,
- OBJ_obj2nid(ne->object))
- ? 1
- : 0;
+ ASN1_STRING *dst = &ne->value;
+ return ASN1_STRING_set_by_NID(&dst, bytes, len, type,
+ OBJ_obj2nid(ne->object)) != nullptr;
}
if (len < 0) {
len = strlen((const char *)bytes);
}
- if (!ASN1_STRING_set(ne->value, bytes, len)) {
+ if (!ASN1_STRING_set(&ne->value, bytes, len)) {
return 0;
}
if (type != V_ASN1_UNDEF) {
- ne->value->type = type;
+ ne->value.type = type;
}
return 1;
}
@@ -361,5 +359,6 @@
if (ne == NULL) {
return NULL;
}
- return ne->value;
+ // This function is not const-correct for OpenSSL compatibility.
+ return const_cast<ASN1_STRING*>(&ne->value);
}
diff --git a/crypto/x509/x_name.cc b/crypto/x509/x_name.cc
index 7ec83f6..0e80ab4 100644
--- a/crypto/x509/x_name.cc
+++ b/crypto/x509/x_name.cc
@@ -13,8 +13,11 @@
// limitations under the License.
#include <ctype.h>
+#include <limits.h>
#include <string.h>
+#include <utility>
+
#include <openssl/asn1.h>
#include <openssl/asn1t.h>
#include <openssl/buf.h>
@@ -23,440 +26,364 @@
#include <openssl/obj.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
-#include <cstdint>
#include "../asn1/internal.h"
+#include "../bytestring/internal.h"
#include "../internal.h"
+#include "../mem_internal.h"
#include "internal.h"
-typedef STACK_OF(X509_NAME_ENTRY) STACK_OF_X509_NAME_ENTRY;
-DEFINE_STACK_OF(STACK_OF_X509_NAME_ENTRY)
-
-// Maximum length of X509_NAME: much larger than anything we should
-// ever see in practice.
-
+// X509_NAME_MAX is the length of the maximum encoded |X509_NAME| we accept.
#define X509_NAME_MAX (1024 * 1024)
-static int x509_name_encode(X509_NAME *a);
-static int x509_name_canon(X509_NAME *a);
-static int asn1_string_canon(ASN1_STRING *out, ASN1_STRING *in);
-static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname,
- unsigned char **in);
+static int asn1_marshal_string_canon(CBB *cbb, const ASN1_STRING *in);
-ASN1_SEQUENCE(X509_NAME_ENTRY) = {
- ASN1_SIMPLE(X509_NAME_ENTRY, object, ASN1_OBJECT),
- ASN1_SIMPLE(X509_NAME_ENTRY, value, ASN1_ANY_AS_STRING),
-} ASN1_SEQUENCE_END(X509_NAME_ENTRY)
-
-IMPLEMENT_ASN1_ALLOC_FUNCTIONS(X509_NAME_ENTRY)
-IMPLEMENT_ASN1_DUP_FUNCTION_const(X509_NAME_ENTRY)
-
-// For the "Name" type we need a SEQUENCE OF { SET OF X509_NAME_ENTRY } so
-// declare two template wrappers for this
-
-ASN1_ITEM_TEMPLATE(X509_NAME_ENTRIES) = ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SET_OF,
- 0, RDNS,
- X509_NAME_ENTRY)
-ASN1_ITEM_TEMPLATE_END(X509_NAME_ENTRIES)
-
-ASN1_ITEM_TEMPLATE(X509_NAME_INTERNAL) =
- ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Name, X509_NAME_ENTRIES)
-ASN1_ITEM_TEMPLATE_END(X509_NAME_INTERNAL)
-
-// Normally that's where it would end: we'd have two nested STACK structures
-// representing the ASN1. Unfortunately X509_NAME uses a completely different
-// form and caches encodings so we have to process the internal form and
-// convert to the external form.
-//
-// TODO(crbug.com/42290417): Rewrite all this with |CBS| and |CBB|.
-
-static int x509_name_ex_new(ASN1_VALUE **val, const ASN1_ITEM *it) {
- X509_NAME *ret = NULL;
- ret = reinterpret_cast<X509_NAME *>(OPENSSL_malloc(sizeof(X509_NAME)));
- if (!ret) {
- goto memerr;
+X509_NAME_ENTRY *X509_NAME_ENTRY_new(void) {
+ bssl::UniquePtr<X509_NAME_ENTRY> ret = bssl::MakeUnique<X509_NAME_ENTRY>();
+ if (ret == nullptr) {
+ return nullptr;
}
- if ((ret->entries = sk_X509_NAME_ENTRY_new_null()) == NULL) {
- goto memerr;
- }
- if ((ret->bytes = BUF_MEM_new()) == NULL) {
- goto memerr;
- }
- ret->canon_enc = NULL;
- ret->canon_enclen = 0;
- ret->modified = 1;
- *val = (ASN1_VALUE *)ret;
- return 1;
-
-memerr:
- if (ret) {
- if (ret->entries) {
- sk_X509_NAME_ENTRY_free(ret->entries);
- }
- OPENSSL_free(ret);
- }
- return 0;
+ ret->object = const_cast<ASN1_OBJECT *>(OBJ_get_undef());
+ asn1_string_init(&ret->value, -1);
+ ret->set = 0;
+ return ret.release();
}
-static void x509_name_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it) {
- X509_NAME *a;
- if (!pval || !*pval) {
- return;
+void X509_NAME_ENTRY_free(X509_NAME_ENTRY *entry) {
+ if (entry != nullptr) {
+ ASN1_OBJECT_free(entry->object);
+ asn1_string_cleanup(&entry->value);
+ OPENSSL_free(entry);
}
- a = (X509_NAME *)*pval;
-
- BUF_MEM_free(a->bytes);
- sk_X509_NAME_ENTRY_pop_free(a->entries, X509_NAME_ENTRY_free);
- if (a->canon_enc) {
- OPENSSL_free(a->canon_enc);
- }
- OPENSSL_free(a);
- *pval = NULL;
}
-static void local_sk_X509_NAME_ENTRY_free(STACK_OF(X509_NAME_ENTRY) *ne) {
- sk_X509_NAME_ENTRY_free(ne);
-}
-
-static void local_sk_X509_NAME_ENTRY_pop_free(STACK_OF(X509_NAME_ENTRY) *ne) {
- sk_X509_NAME_ENTRY_pop_free(ne, X509_NAME_ENTRY_free);
-}
-
-static int x509_name_ex_parse(ASN1_VALUE **val, CBS *cbs, const ASN1_ITEM *it,
- int opt) {
- if (opt && !CBS_peek_asn1_tag(cbs, CBS_ASN1_SEQUENCE)) {
- return 1;
- }
-
- CBS elem;
- if (!CBS_get_asn1_element(cbs, &elem, CBS_ASN1_SEQUENCE) ||
- // Bound the size of an X509_NAME we are willing to parse.
- CBS_len(&elem) > X509_NAME_MAX) {
+static int x509_parse_name_entry(CBS *cbs, X509_NAME_ENTRY *out) {
+ CBS seq;
+ if (!CBS_get_asn1(cbs, &seq, CBS_ASN1_SEQUENCE)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return 0;
}
-
- // TODO(crbug.com/42290417): Rewrite the parser and canonicalization code with
- // CBS and CBB. For now this calls into the original two-layer d2i code.
- long len = static_cast<long>(CBS_len(&elem));
- const unsigned char *p = CBS_data(&elem), *q;
- STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname = NULL;
- X509_NAME *nm = NULL;
- size_t i, j;
- STACK_OF(X509_NAME_ENTRY) *entries;
- X509_NAME_ENTRY *entry;
- q = p;
-
- // Get internal representation of Name
- ASN1_VALUE *intname_val = NULL;
- if (ASN1_item_ex_d2i(&intname_val, &p, len,
- ASN1_ITEM_rptr(X509_NAME_INTERNAL), /*tag=*/-1,
- /*aclass=*/0, /*opt=*/0) <= 0) {
+ ASN1_OBJECT_free(out->object);
+ out->object = asn1_parse_object(&seq, /*tag=*/0);
+ if (out->object == nullptr || //
+ !asn1_parse_any_as_string(&seq, &out->value) || //
+ CBS_len(&seq) != 0) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return 0;
}
- intname = (STACK_OF(STACK_OF_X509_NAME_ENTRY) *)intname_val;
-
- if (*val) {
- x509_name_ex_free(val, NULL);
- }
- ASN1_VALUE *nm_val = NULL;
- if (!x509_name_ex_new(&nm_val, NULL)) {
- goto err;
- }
- nm = (X509_NAME *)nm_val;
- // We've decoded it: now cache encoding
- if (!BUF_MEM_grow(nm->bytes, p - q)) {
- goto err;
- }
- OPENSSL_memcpy(nm->bytes->data, q, p - q);
-
- // Convert internal representation to X509_NAME structure
- for (i = 0; i < sk_STACK_OF_X509_NAME_ENTRY_num(intname); i++) {
- entries = sk_STACK_OF_X509_NAME_ENTRY_value(intname, i);
- for (j = 0; j < sk_X509_NAME_ENTRY_num(entries); j++) {
- entry = sk_X509_NAME_ENTRY_value(entries, j);
- entry->set = (int)i;
- if (!sk_X509_NAME_ENTRY_push(nm->entries, entry)) {
- goto err;
- }
- (void)sk_X509_NAME_ENTRY_set(entries, j, NULL);
- }
- }
- if (!x509_name_canon(nm)) {
- goto err;
- }
- sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname, local_sk_X509_NAME_ENTRY_free);
- nm->modified = 0;
- *val = (ASN1_VALUE *)nm;
return 1;
-err:
- X509_NAME_free(nm);
- sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname,
- local_sk_X509_NAME_ENTRY_pop_free);
- OPENSSL_PUT_ERROR(X509, ERR_R_ASN1_LIB);
- return 0;
}
-static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out,
- const ASN1_ITEM *it) {
- X509_NAME *a = (X509_NAME *)*val;
- if (a->modified && (!x509_name_encode(a) || !x509_name_canon(a))) {
- return -1;
+static int x509_marshal_name_entry(CBB *cbb, const X509_NAME_ENTRY *entry,
+ int canonicalize) {
+ CBB seq;
+ if (!CBB_add_asn1(cbb, &seq, CBS_ASN1_SEQUENCE) ||
+ !asn1_marshal_object(&seq, entry->object, /*tag=*/0)) {
+ return 0;
}
- int ret = a->bytes->length;
- if (out != NULL) {
- OPENSSL_memcpy(*out, a->bytes->data, ret);
- *out += ret;
+ int ok = canonicalize ? asn1_marshal_string_canon(&seq, &entry->value)
+ : asn1_marshal_any_string(&seq, &entry->value);
+ if (!ok) {
+ return 0;
}
- return ret;
+ return CBB_flush(cbb);
}
-static const ASN1_EXTERN_FUNCS x509_name_ff = {
- x509_name_ex_new, x509_name_ex_free, x509_name_ex_parse, x509_name_ex_i2d};
-IMPLEMENT_EXTERN_ASN1(X509_NAME, x509_name_ff)
-IMPLEMENT_ASN1_FUNCTIONS(X509_NAME)
-IMPLEMENT_ASN1_DUP_FUNCTION(X509_NAME)
+static int i2d_x509_name_entry(const X509_NAME_ENTRY *entry, uint8_t **out) {
+ return bssl::I2DFromCBB(/*initial_capacity=*/16, out, [&](CBB *cbb) -> bool {
+ return x509_marshal_name_entry(cbb, entry, /*canonicalize=*/0);
+ });
+}
-static int x509_name_encode(X509_NAME *a) {
- int len;
- unsigned char *p;
- STACK_OF(X509_NAME_ENTRY) *entries = NULL;
- X509_NAME_ENTRY *entry;
- int set = -1;
- size_t i;
- STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname =
- sk_STACK_OF_X509_NAME_ENTRY_new_null();
+IMPLEMENT_EXTERN_ASN1_SIMPLE(X509_NAME_ENTRY, X509_NAME_ENTRY_new,
+ X509_NAME_ENTRY_free, CBS_ASN1_SEQUENCE,
+ x509_parse_name_entry, i2d_x509_name_entry)
- {
- if (!intname) {
- goto err;
+X509_NAME_ENTRY *X509_NAME_ENTRY_dup(const X509_NAME_ENTRY *entry) {
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 16) ||
+ !x509_marshal_name_entry(cbb.get(), entry, /*canonicalize=*/0)) {
+ return nullptr;
+ }
+ CBS cbs;
+ CBS_init(&cbs, CBB_data(cbb.get()), CBB_len(cbb.get()));
+ bssl::UniquePtr<X509_NAME_ENTRY> copy(X509_NAME_ENTRY_new());
+ if (copy == nullptr || !x509_parse_name_entry(&cbs, copy.get())) {
+ return nullptr;
+ }
+ return copy.release();
+}
+
+static void x509_name_cache_free(X509_NAME_CACHE *cache) {
+ if (cache != nullptr) {
+ OPENSSL_free(cache->canon);
+ OPENSSL_free(cache->der);
+ OPENSSL_free(cache);
+ }
+}
+
+void x509_name_init(X509_NAME *name) {
+ OPENSSL_memset(name, 0, sizeof(X509_NAME));
+}
+
+void x509_name_cleanup(X509_NAME *name) {
+ sk_X509_NAME_ENTRY_pop_free(name->entries, X509_NAME_ENTRY_free);
+ x509_name_cache_free(name->cache.exchange(nullptr));
+}
+
+X509_NAME *X509_NAME_new(void) {
+ return static_cast<X509_NAME *>(OPENSSL_zalloc(sizeof(X509_NAME)));
+}
+
+void X509_NAME_free(X509_NAME *name) {
+ if (name != nullptr) {
+ x509_name_cleanup(name);
+ OPENSSL_free(name);
+ }
+}
+
+int x509_parse_name(CBS *cbs, X509_NAME *out) {
+ // Reset the old state.
+ x509_name_cleanup(out);
+ x509_name_init(out);
+
+ out->entries = sk_X509_NAME_ENTRY_new_null();
+ if (out->entries == nullptr) {
+ return 0;
+ }
+ CBS seq, rdn;
+ if (!CBS_get_asn1(cbs, &seq, CBS_ASN1_SEQUENCE) ||
+ // Bound the size of an X509_NAME we are willing to parse.
+ CBS_len(&seq) > X509_NAME_MAX) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ static_assert(X509_NAME_MAX <= INT_MAX, "set may overflow");
+ for (int set = 0; CBS_len(&seq) > 0; set++) {
+ if (!CBS_get_asn1(&seq, &rdn, CBS_ASN1_SET) || //
+ CBS_len(&rdn) == 0) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
}
- for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) {
- entry = sk_X509_NAME_ENTRY_value(a->entries, i);
- if (entry->set != set) {
- entries = sk_X509_NAME_ENTRY_new_null();
- if (!entries) {
- goto err;
- }
- if (!sk_STACK_OF_X509_NAME_ENTRY_push(intname, entries)) {
- sk_X509_NAME_ENTRY_free(entries);
- goto err;
- }
- set = entry->set;
+ while (CBS_len(&rdn) != 0) {
+ bssl::UniquePtr<X509_NAME_ENTRY> entry(X509_NAME_ENTRY_new());
+ if (entry == nullptr || !x509_parse_name_entry(&rdn, entry.get())) {
+ return 0;
}
- if (!sk_X509_NAME_ENTRY_push(entries, entry)) {
- goto err;
+ entry->set = set;
+ if (!bssl::PushToStack(out->entries, std::move(entry))) {
+ return 0;
}
}
- ASN1_VALUE *intname_val = (ASN1_VALUE *)intname;
- len =
- ASN1_item_ex_i2d(&intname_val, NULL, ASN1_ITEM_rptr(X509_NAME_INTERNAL),
- /*tag=*/-1, /*aclass=*/0);
- if (len <= 0) {
- goto err;
- }
- if (!BUF_MEM_grow(a->bytes, len)) {
- goto err;
- }
- p = (unsigned char *)a->bytes->data;
- if (ASN1_item_ex_i2d(&intname_val, &p, ASN1_ITEM_rptr(X509_NAME_INTERNAL),
- /*tag=*/-1, /*aclass=*/0) <= 0) {
- goto err;
- }
- sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname,
- local_sk_X509_NAME_ENTRY_free);
- a->modified = 0;
+ }
+
+ // While we are single-threaded, also fill in the cached state.
+ return x509_name_get_cache(out) != nullptr;
+}
+
+static int x509_marshal_name_entries(CBB *out, const X509_NAME *name,
+ int canonicalize) {
+ if (sk_X509_NAME_ENTRY_num(name->entries) == 0) {
return 1;
}
-err:
- sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname, local_sk_X509_NAME_ENTRY_free);
- return 0;
-}
-
-// This function generates the canonical encoding of the Name structure. In
-// it all strings are converted to UTF8, leading, trailing and multiple
-// spaces collapsed, converted to lower case and the leading SEQUENCE header
-// removed. In future we could also normalize the UTF8 too. By doing this
-// comparison of Name structures can be rapidly perfomed by just using
-// OPENSSL_memcmp() of the canonical encoding. By omitting the leading SEQUENCE
-// name constraints of type dirName can also be checked with a simple
-// OPENSSL_memcmp().
-
-static int x509_name_canon(X509_NAME *a) {
- unsigned char *p;
- STACK_OF(STACK_OF_X509_NAME_ENTRY) *intname = NULL;
- STACK_OF(X509_NAME_ENTRY) *entries = NULL;
- X509_NAME_ENTRY *entry, *tmpentry = NULL;
- int set = -1, ret = 0, len;
- size_t i;
-
- if (a->canon_enc) {
- OPENSSL_free(a->canon_enc);
- a->canon_enc = NULL;
+ // Bootstrap the first RDN.
+ int set = sk_X509_NAME_ENTRY_value(name->entries, 0)->set;
+ CBB rdn;
+ if (!CBB_add_asn1(out, &rdn, CBS_ASN1_SET)) {
+ return 0;
}
- // Special case: empty X509_NAME => null encoding
- if (sk_X509_NAME_ENTRY_num(a->entries) == 0) {
- a->canon_enclen = 0;
- return 1;
- }
- intname = sk_STACK_OF_X509_NAME_ENTRY_new_null();
- if (!intname) {
- goto err;
- }
- for (i = 0; i < sk_X509_NAME_ENTRY_num(a->entries); i++) {
- entry = sk_X509_NAME_ENTRY_value(a->entries, i);
+
+ for (const X509_NAME_ENTRY *entry : name->entries) {
if (entry->set != set) {
- entries = sk_X509_NAME_ENTRY_new_null();
- if (!entries) {
- goto err;
- }
- if (!sk_STACK_OF_X509_NAME_ENTRY_push(intname, entries)) {
- sk_X509_NAME_ENTRY_free(entries);
- goto err;
+ // Flush the previous RDN and start a new one.
+ if (!CBB_flush_asn1_set_of(&rdn) ||
+ !CBB_add_asn1(out, &rdn, CBS_ASN1_SET)) {
+ return 0;
}
set = entry->set;
}
- tmpentry = X509_NAME_ENTRY_new();
- if (tmpentry == NULL) {
- goto err;
- }
- tmpentry->object = OBJ_dup(entry->object);
- if (!asn1_string_canon(tmpentry->value, entry->value)) {
- goto err;
- }
- if (!sk_X509_NAME_ENTRY_push(entries, tmpentry)) {
- goto err;
- }
- tmpentry = NULL;
- }
-
- // Finally generate encoding
-
- len = i2d_name_canon(intname, NULL);
- if (len < 0) {
- goto err;
- }
- a->canon_enclen = len;
-
- p = reinterpret_cast<uint8_t *>(OPENSSL_malloc(a->canon_enclen));
-
- if (!p) {
- goto err;
- }
-
- a->canon_enc = p;
-
- i2d_name_canon(intname, &p);
-
- ret = 1;
-
-err:
-
- if (tmpentry) {
- X509_NAME_ENTRY_free(tmpentry);
- }
- if (intname) {
- sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname,
- local_sk_X509_NAME_ENTRY_pop_free);
- }
- return ret;
-}
-
-// Bitmap of all the types of string that will be canonicalized.
-
-#define ASN1_MASK_CANON \
- (B_ASN1_UTF8STRING | B_ASN1_BMPSTRING | B_ASN1_UNIVERSALSTRING | \
- B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_IA5STRING | \
- B_ASN1_VISIBLESTRING)
-
-static int asn1_string_canon(ASN1_STRING *out, ASN1_STRING *in) {
- unsigned char *to, *from;
- int len, i;
-
- // If type not in bitmask just copy string across
- if (!(ASN1_tag2bit(in->type) & ASN1_MASK_CANON)) {
- if (!ASN1_STRING_copy(out, in)) {
+ if (!x509_marshal_name_entry(&rdn, entry, canonicalize)) {
return 0;
}
- return 1;
}
- out->type = V_ASN1_UTF8STRING;
- out->length = ASN1_STRING_to_UTF8(&out->data, in);
- if (out->length == -1) {
+ return CBB_flush_asn1_set_of(&rdn) && CBB_flush(out);
+}
+
+const X509_NAME_CACHE *x509_name_get_cache(const X509_NAME *name) {
+ const X509_NAME_CACHE *cache = name->cache.load();
+ if (cache != nullptr) {
+ return cache;
+ }
+
+ X509_NAME_CACHE *new_cache =
+ static_cast<X509_NAME_CACHE *>(OPENSSL_zalloc(sizeof(X509_NAME_CACHE)));
+ // Cache the DER encoding, including the outer TLV.
+ bssl::ScopedCBB cbb;
+ CBB seq;
+ if (!CBB_init(cbb.get(), 16) ||
+ !CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE) ||
+ !x509_marshal_name_entries(&seq, name, /*canonicalize=*/0) ||
+ !CBB_finish(cbb.get(), &new_cache->der, &new_cache->der_len)) {
+ x509_name_cache_free(new_cache);
+ return 0;
+ }
+ // Cache the canonicalized form, without the outer TLV.
+ if (!CBB_init(cbb.get(), 16) ||
+ !x509_marshal_name_entries(cbb.get(), name, /*canonicalize=*/1) ||
+ !CBB_finish(cbb.get(), &new_cache->canon, &new_cache->canon_len)) {
+ x509_name_cache_free(new_cache);
return 0;
}
- to = out->data;
- from = to;
-
- len = out->length;
-
- // Convert string in place to canonical form.
-
- // Ignore leading spaces
- while ((len > 0) && OPENSSL_isspace(*from)) {
- from++;
- len--;
+ X509_NAME_CACHE *expected = nullptr;
+ if (name->cache.compare_exchange_strong(expected, new_cache)) {
+ // We won the race. |name| now owns |new_cache|.
+ return new_cache;
}
- to = from + len;
-
- // Ignore trailing spaces
- while ((len > 0) && OPENSSL_isspace(to[-1])) {
- to--;
- len--;
- }
-
- to = out->data;
-
- i = 0;
- while (i < len) {
- // Collapse multiple spaces
- if (OPENSSL_isspace(*from)) {
- // Copy one space across
- *to++ = ' ';
- // Ignore subsequent spaces. Note: don't need to check len here
- // because we know the last character is a non-space so we can't
- // overflow.
- do {
- from++;
- i++;
- } while (OPENSSL_isspace(*from));
- } else {
- *to++ = OPENSSL_tolower(*from);
- from++;
- i++;
- }
- }
-
- out->length = to - out->data;
-
- return 1;
+ // Some other thread installed a (presumably identical) cache. Release the one
+ // we made and return the winning one.
+ assert(expected != nullptr);
+ x509_name_cache_free(new_cache);
+ return expected;
}
-static int i2d_name_canon(STACK_OF(STACK_OF_X509_NAME_ENTRY) *_intname,
- unsigned char **in) {
- int len, ltmp;
- size_t i;
- ASN1_VALUE *v;
- STACK_OF(ASN1_VALUE) *intname = (STACK_OF(ASN1_VALUE) *)_intname;
+void x509_name_invalidate_cache(X509_NAME *name) {
+ x509_name_cache_free(name->cache.exchange(nullptr));
+}
- len = 0;
- for (i = 0; i < sk_ASN1_VALUE_num(intname); i++) {
- v = sk_ASN1_VALUE_value(intname, i);
- ltmp = ASN1_item_ex_i2d(&v, in, ASN1_ITEM_rptr(X509_NAME_ENTRIES),
- /*tag=*/-1, /*aclass=*/0);
- if (ltmp < 0) {
- return ltmp;
- }
- len += ltmp;
+int x509_marshal_name(CBB *out, const X509_NAME *in) {
+ const X509_NAME_CACHE *cache = x509_name_get_cache(in);
+ if (cache == nullptr) {
+ return 0;
}
+ return CBB_add_bytes(out, cache->der, cache->der_len);
+}
+
+X509_NAME *X509_NAME_dup(X509_NAME *name) {
+ const X509_NAME_CACHE *cache = x509_name_get_cache(name);
+ if (cache == nullptr) {
+ return nullptr;
+ }
+ CBS cbs;
+ CBS_init(&cbs, cache->der, cache->der_len);
+ bssl::UniquePtr<X509_NAME> copy(X509_NAME_new());
+ if (copy == nullptr || !x509_parse_name(&cbs, copy.get())) {
+ return nullptr;
+ }
+ assert(CBS_len(&cbs) == 0);
+ return copy.release();
+}
+
+X509_NAME *d2i_X509_NAME(X509_NAME **out, const uint8_t **inp, long len) {
+ return bssl::D2IFromCBS(
+ out, inp, len, [](CBS *cbs) -> bssl::UniquePtr<X509_NAME> {
+ bssl::UniquePtr<X509_NAME> name(X509_NAME_new());
+ if (name == nullptr || !x509_parse_name(cbs, name.get())) {
+ return nullptr;
+ }
+ return name;
+ });
+}
+
+int i2d_X509_NAME(X509_NAME *in, uint8_t **outp) {
+ const X509_NAME_CACHE *cache = x509_name_get_cache(in);
+ if (cache == nullptr) {
+ return -1;
+ }
+ if (cache->der_len > INT_MAX) {
+ OPENSSL_PUT_ERROR(X509, ERR_R_OVERFLOW);
+ return -1;
+ }
+ int len = static_cast<int>(cache->der_len);
+ if (outp == nullptr) {
+ return len;
+ }
+ if (*outp == nullptr) {
+ *outp = static_cast<uint8_t*>(OPENSSL_memdup(cache->der, cache->der_len));
+ return *outp != nullptr ? len : -1;
+ }
+ OPENSSL_memcpy(*outp, cache->der, cache->der_len);
+ *outp += cache->der_len;
return len;
}
+IMPLEMENT_EXTERN_ASN1_SIMPLE(X509_NAME, X509_NAME_new, X509_NAME_free,
+ CBS_ASN1_SEQUENCE, x509_parse_name, i2d_X509_NAME)
+
+static int asn1_marshal_string_canon(CBB *cbb, const ASN1_STRING *in) {
+ int (*decode_func)(CBS *, uint32_t *);
+ int error;
+ switch (in->type) {
+ case V_ASN1_UTF8STRING:
+ decode_func = CBS_get_utf8;
+ error = ASN1_R_INVALID_UTF8STRING;
+ break;
+ case V_ASN1_BMPSTRING:
+ decode_func = CBS_get_ucs2_be;
+ error = ASN1_R_INVALID_BMPSTRING;
+ break;
+ case V_ASN1_UNIVERSALSTRING:
+ decode_func = CBS_get_utf32_be;
+ error = ASN1_R_INVALID_UNIVERSALSTRING;
+ break;
+ case V_ASN1_PRINTABLESTRING:
+ case V_ASN1_T61STRING:
+ case V_ASN1_IA5STRING:
+ case V_ASN1_VISIBLESTRING:
+ decode_func = CBS_get_latin1;
+ error = ERR_R_INTERNAL_ERROR; // Latin-1 inputs are never invalid.
+ break;
+ default:
+ // Other string types are not canonicalized.
+ return asn1_marshal_any_string(cbb, in);
+ }
+
+ CBB child;
+ if (!CBB_add_asn1(cbb, &child, CBS_ASN1_UTF8STRING)) {
+ return 0;
+ }
+
+ bool empty = true;
+ bool in_whitespace = false;
+ CBS cbs;
+ CBS_init(&cbs, in->data, in->length);
+ while (CBS_len(&cbs) != 0) {
+ uint32_t c;
+ if (!decode_func(&cbs, &c)) {
+ OPENSSL_PUT_ERROR(ASN1, error);
+ return 0;
+ }
+ if (OPENSSL_isspace(c)) {
+ if (empty) {
+ continue; // Trim leading whitespace.
+ }
+ in_whitespace = true;
+ } else {
+ if (in_whitespace) {
+ // Collapse the previous run of whitespace into one space.
+ if (!CBB_add_u8(&child, ' ')) {
+ return 0;
+ }
+ }
+ in_whitespace = false;
+ // Lowecase ASCII codepoints.
+ if (c <= 0x7f) {
+ c = OPENSSL_tolower(c);
+ }
+ if (!CBB_add_utf8(&child, c)) {
+ return 0;
+ }
+ empty = false;
+ }
+ }
+
+ return CBB_flush(cbb);
+}
+
int X509_NAME_set(X509_NAME **xn, X509_NAME *name) {
if ((name = X509_NAME_dup(name)) == NULL) {
return 0;
@@ -470,24 +397,15 @@
int X509_NAME_get0_der(X509_NAME *nm, const unsigned char **out_der,
size_t *out_der_len) {
- // Make sure encoding is valid
- if (i2d_X509_NAME(nm, NULL) <= 0) {
+ const X509_NAME_CACHE *cache = x509_name_get_cache(nm);
+ if (cache == nullptr) {
return 0;
}
- if (out_der != NULL) {
- *out_der = (unsigned char *)nm->bytes->data;
+ if (out_der != nullptr) {
+ *out_der = cache->der;
}
- if (out_der_len != NULL) {
- *out_der_len = nm->bytes->length;
+ if (out_der_len != nullptr) {
+ *out_der_len = cache->der_len;
}
return 1;
}
-
-int x509_marshal_name(CBB *out, X509_NAME *in) {
- int len = i2d_X509_NAME(in, nullptr);
- if (len <= 0) {
- return 0;
- }
- uint8_t *ptr;
- return CBB_add_space(out, &ptr, len) && i2d_X509_NAME(in, &ptr) == len;
-}
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 667eede..e490931 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -1370,9 +1370,6 @@
// they are equal, one if |a| sorts after |b|, -1 if |b| sorts after |a|, and -2
// on error.
//
-// TODO(crbug.com/42290269): This function is const, but it is not always
-// thread-safe, notably if |name| was mutated.
-//
// TODO(https://crbug.com/boringssl/355): The -2 return is very inconvenient to
// pass to a sorting function. Can we make this infallible? In the meantime,
// prefer to use this function only for equality checks rather than comparisons.