Rewrite X509's parser with CBS/CBB
In doing so, embed a whole host of types into X509. With all this, we
can *finally* add extra parameters to the parser, like lists of
supported algorithms or flag-protecting future changes. In the previous
state, it went through a very messy reflection-based framework.
Since we now control the parser entrypoint, this also means we can
handle the cached encoding differently. Just to make the
X509_parse_from_buffer and d2i_X509 flows more uniform, we now always
save a CRYPTO_BUFFER containing the whole certificate, and then
TBSCertificate marshalling finds the TBSCertificate from there. (Though
it is still possible to create X509 objects without CRYPTO_BUFFERs.)
That in turn means the generic ASN1_ENCODING path doesn't need to store
a CRYPTO_BUFFER anymore, so all that is now unwound. If we rewrite the
CSR and CRL parsers, then ASN1_ENCODING can be removed entirely.
As a bonus, this new parser is significantly faster!
Before:
Did 1037634 Parse X.509 certificate operations in 5000690us (207498.2 ops/sec)
After:
Did 1318068 Parse X.509 certificate operations in 5000462us (263589.2 ops/sec)
(The X509_NAME parser is still using tasn_dec, so there's still some
overhead there. This also does not remove the overhead from X509's
in-memory representation where lots of parts of the certificate get
copied separate allocations.)
Update-Note: This CL is not expected to change the external behavior of
the X.509 parser, but it's a lot of code that had to get shuffled
around, so if something funny happens around X.509, this is a likely
culprit.
Bug: 42290417
Change-Id: Ia4142f5e8d31b041176a545379c7df30e946a670
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81781
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/build.json b/build.json
index 53551d8..39ba40e 100644
--- a/build.json
+++ b/build.json
@@ -404,7 +404,6 @@
"crypto/x509/x_req.cc",
"crypto/x509/x_sig.cc",
"crypto/x509/x_spki.cc",
- "crypto/x509/x_val.cc",
"crypto/x509/x_x509.cc",
"crypto/x509/x_x509a.cc",
"crypto/xwing/xwing.cc"
diff --git a/crypto/asn1/a_time.cc b/crypto/asn1/a_time.cc
index 46785f6..d77a99a 100644
--- a/crypto/asn1/a_time.cc
+++ b/crypto/asn1/a_time.cc
@@ -222,3 +222,19 @@
}
return OPENSSL_tm_to_posix(&tm, out_time);
}
+
+int asn1_parse_time(CBS *cbs, ASN1_TIME *out, int allow_utc_timezone_offset) {
+ if (CBS_peek_asn1_tag(cbs, CBS_ASN1_UTCTIME)) {
+ return asn1_parse_utc_time(cbs, out, /*tag=*/0, allow_utc_timezone_offset);
+ }
+ return asn1_parse_generalized_time(cbs, out, /*tag=*/0);
+}
+
+int asn1_marshal_time(CBB *cbb, const ASN1_TIME *in) {
+ if (in->type != V_ASN1_UTCTIME && in->type != V_ASN1_GENERALIZEDTIME) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
+ return 0;
+ }
+ return asn1_marshal_octet_string(cbb, in,
+ static_cast<CBS_ASN1_TAG>(in->type));
+}
diff --git a/crypto/asn1/internal.h b/crypto/asn1/internal.h
index a828957..9be2fe3 100644
--- a/crypto/asn1/internal.h
+++ b/crypto/asn1/internal.h
@@ -159,6 +159,9 @@
OPENSSL_EXPORT int asn1_generalizedtime_to_tm(struct tm *tm,
const ASN1_GENERALIZEDTIME *d);
+int asn1_parse_time(CBS *cbs, ASN1_TIME *out, int allow_utc_timezone_offset);
+int asn1_marshal_time(CBB *cbb, const ASN1_TIME *in);
+
// The ASN.1 ANY type.
@@ -201,9 +204,6 @@
uint8_t *enc;
// len is the length of |enc|. If zero, there is no saved encoding.
size_t len;
- // buf, if non-NULL, is the |CRYPTO_BUFFER| that |enc| points into. If NULL,
- // |enc| must be released with |OPENSSL_free|.
- CRYPTO_BUFFER *buf;
} ASN1_ENCODING;
int ASN1_item_ex_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
@@ -214,14 +214,12 @@
// ASN1_item_ex_d2i parses |len| bytes from |*in| as a structure of type |it|
// and writes the result to |*pval|. If |tag| is non-negative, |it| is
// implicitly tagged with the tag specified by |tag| and |aclass|. If |opt| is
-// non-zero, the value is optional. If |buf| is non-NULL, |*in| must point into
-// |buf|.
+// non-zero, the value is optional.
//
// This function returns one and advances |*in| if an object was successfully
// parsed, -1 if an optional value was successfully skipped, and zero on error.
int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
- const ASN1_ITEM *it, int tag, int aclass, char opt,
- CRYPTO_BUFFER *buf);
+ const ASN1_ITEM *it, int tag, int aclass, char opt);
// ASN1_item_ex_i2d encodes |*pval| as a value of type |it| to |out| under the
// i2d output convention. It returns a non-zero length on success and -1 on
@@ -267,7 +265,7 @@
// returns one on success and zero on error. If |buf| is non-NULL, |in| must
// point into |buf|.
int asn1_enc_save(ASN1_VALUE **pval, const uint8_t *in, size_t inlen,
- const ASN1_ITEM *it, CRYPTO_BUFFER *buf);
+ const ASN1_ITEM *it);
// asn1_encoding_clear clears the cached encoding in |enc|.
void asn1_encoding_clear(ASN1_ENCODING *enc);
diff --git a/crypto/asn1/tasn_dec.cc b/crypto/asn1/tasn_dec.cc
index 2f53a79..c749d88 100644
--- a/crypto/asn1/tasn_dec.cc
+++ b/crypto/asn1/tasn_dec.cc
@@ -39,16 +39,16 @@
static int asn1_template_ex_d2i(ASN1_VALUE **pval, const unsigned char **in,
long len, const ASN1_TEMPLATE *tt, char opt,
- CRYPTO_BUFFER *buf, int depth);
+ int depth);
static int asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in,
long len, const ASN1_TEMPLATE *tt, char opt,
- CRYPTO_BUFFER *buf, int depth);
+ int depth);
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in,
long len, const ASN1_ITEM *it, int tag,
int aclass, char opt);
static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in,
long len, const ASN1_ITEM *it, int tag, int aclass,
- char opt, CRYPTO_BUFFER *buf, int depth);
+ char opt, int depth);
unsigned long ASN1_tag2bit(int tag) {
switch (tag) {
@@ -98,7 +98,7 @@
const ASN1_ITEM *it) {
ASN1_VALUE *ret = NULL;
if (asn1_item_ex_d2i(&ret, in, len, it, /*tag=*/-1, /*aclass=*/0, /*opt=*/0,
- /*buf=*/NULL, /*depth=*/0) <= 0) {
+ /*depth=*/0) <= 0) {
// Clean up, in case the caller left a partial object.
//
// TODO(davidben): I don't think it can leave one, but the codepaths below
@@ -128,7 +128,7 @@
static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in,
long len, const ASN1_ITEM *it, int tag, int aclass,
- char opt, CRYPTO_BUFFER *buf, int depth) {
+ char opt, int depth) {
const ASN1_TEMPLATE *tt, *errtt = NULL;
const unsigned char *p = NULL, *q;
unsigned char oclass;
@@ -145,11 +145,6 @@
goto err;
}
- if (buf != NULL) {
- assert(CRYPTO_BUFFER_data(buf) <= *in &&
- *in + len <= CRYPTO_BUFFER_data(buf) + CRYPTO_BUFFER_len(buf));
- }
-
// Bound |len| to comfortably fit in an int. Lengths in this module often
// switch between int and long without overflow checks.
if (len > INT_MAX / 2) {
@@ -172,8 +167,7 @@
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE);
goto err;
}
- return asn1_template_ex_d2i(pval, in, len, it->templates, opt, buf,
- depth);
+ return asn1_template_ex_d2i(pval, in, len, it->templates, opt, depth);
}
return asn1_d2i_ex_primitive(pval, in, len, it, tag, aclass, opt);
@@ -267,7 +261,7 @@
for (i = 0, tt = it->templates; i < it->tcount; i++, tt++) {
pchptr = asn1_get_field_ptr(pval, tt);
// We mark field as OPTIONAL so its absence can be recognised.
- ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, buf, depth);
+ ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, depth);
// If field not present, try the next one
if (ret == -1) {
continue;
@@ -373,7 +367,7 @@
}
// attempt to read in field, allowing each to be OPTIONAL
- ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, buf, depth);
+ ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, depth);
if (!ret) {
errtt = seqtt;
goto err;
@@ -412,7 +406,7 @@
}
}
// Save encoding
- if (!asn1_enc_save(pval, *in, p - *in, it, buf)) {
+ if (!asn1_enc_save(pval, *in, p - *in, it)) {
goto auxerr;
}
if (asn1_cb && !asn1_cb(ASN1_OP_D2I_POST, pval, it, NULL)) {
@@ -438,10 +432,8 @@
}
int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
- const ASN1_ITEM *it, int tag, int aclass, char opt,
- CRYPTO_BUFFER *buf) {
- return asn1_item_ex_d2i(pval, in, len, it, tag, aclass, opt, buf,
- /*depth=*/0);
+ const ASN1_ITEM *it, int tag, int aclass, char opt) {
+ return asn1_item_ex_d2i(pval, in, len, it, tag, aclass, opt, /*depth=*/0);
}
// Templates are handled with two separate functions. One handles any
@@ -449,7 +441,7 @@
static int asn1_template_ex_d2i(ASN1_VALUE **val, const unsigned char **in,
long inlen, const ASN1_TEMPLATE *tt, char opt,
- CRYPTO_BUFFER *buf, int depth) {
+ int depth) {
int aclass;
int ret;
long len;
@@ -481,7 +473,7 @@
return 0;
}
// We've found the field so it can't be OPTIONAL now
- ret = asn1_template_noexp_d2i(val, &p, len, tt, /*opt=*/0, buf, depth);
+ ret = asn1_template_noexp_d2i(val, &p, len, tt, /*opt=*/0, depth);
if (!ret) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
return 0;
@@ -494,7 +486,7 @@
goto err;
}
} else {
- return asn1_template_noexp_d2i(val, in, inlen, tt, opt, buf, depth);
+ return asn1_template_noexp_d2i(val, in, inlen, tt, opt, depth);
}
*in = p;
@@ -507,7 +499,7 @@
static int asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in,
long len, const ASN1_TEMPLATE *tt, char opt,
- CRYPTO_BUFFER *buf, int depth) {
+ int depth) {
int aclass;
int ret;
const unsigned char *p;
@@ -565,7 +557,7 @@
const unsigned char *q = p;
skfield = NULL;
if (!asn1_item_ex_d2i(&skfield, &p, len, ASN1_ITEM_ptr(tt->item),
- /*tag=*/-1, /*aclass=*/0, /*opt=*/0, buf, depth)) {
+ /*tag=*/-1, /*aclass=*/0, /*opt=*/0, depth)) {
ASN1_item_ex_free(&skfield, ASN1_ITEM_ptr(tt->item));
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
goto err;
@@ -579,7 +571,7 @@
} else if (flags & ASN1_TFLG_IMPTAG) {
// IMPLICIT tagging
ret = asn1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item), tt->tag,
- aclass, opt, buf, depth);
+ aclass, opt, depth);
if (!ret) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
goto err;
@@ -589,7 +581,7 @@
} else {
// Nothing special
ret = asn1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item), /*tag=*/-1,
- /*aclass=*/0, opt, buf, depth);
+ /*aclass=*/0, opt, depth);
if (!ret) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
goto err;
@@ -771,9 +763,9 @@
case V_ASN1_UTF8STRING:
return asn1_parse_utf8_string(cbs, str, cbs_tag);
case V_ASN1_UTCTIME:
- // TODO(crbug.com/42290417): Once |X509|'s parser is written by hand, we
- // won't have any known compatibility constraints forcing an invalid
- // parser here. At that point, we can make the general case strict.
+ // TODO(crbug.com/42290221): Reject timezone offsets. We need to parse
+ // invalid timestamps in |X509| objects, but that parser no longer uses
+ // this code.
return asn1_parse_utc_time(cbs, str, cbs_tag,
/*allow_timezone_offset=*/1);
case V_ASN1_GENERALIZEDTIME:
diff --git a/crypto/asn1/tasn_utl.cc b/crypto/asn1/tasn_utl.cc
index 3b1dbfc..f7233f5 100644
--- a/crypto/asn1/tasn_utl.cc
+++ b/crypto/asn1/tasn_utl.cc
@@ -94,7 +94,6 @@
if (enc) {
enc->enc = NULL;
enc->len = 0;
- enc->buf = NULL;
}
}
@@ -106,7 +105,7 @@
}
int asn1_enc_save(ASN1_VALUE **pval, const uint8_t *in, size_t in_len,
- const ASN1_ITEM *it, CRYPTO_BUFFER *buf) {
+ const ASN1_ITEM *it) {
ASN1_ENCODING *enc;
enc = asn1_get_enc_ptr(pval, it);
if (!enc) {
@@ -114,17 +113,9 @@
}
asn1_encoding_clear(enc);
- if (buf != NULL) {
- assert(CRYPTO_BUFFER_data(buf) <= in &&
- in + in_len <= CRYPTO_BUFFER_data(buf) + CRYPTO_BUFFER_len(buf));
- CRYPTO_BUFFER_up_ref(buf);
- enc->buf = buf;
- enc->enc = (uint8_t *)in;
- } else {
- enc->enc = reinterpret_cast<uint8_t *>(OPENSSL_memdup(in, in_len));
- if (!enc->enc) {
- return 0;
- }
+ enc->enc = reinterpret_cast<uint8_t *>(OPENSSL_memdup(in, in_len));
+ if (!enc->enc) {
+ return 0;
}
enc->len = in_len;
@@ -132,14 +123,9 @@
}
void asn1_encoding_clear(ASN1_ENCODING *enc) {
- if (enc->buf != NULL) {
- CRYPTO_BUFFER_free(enc->buf);
- } else {
- OPENSSL_free(enc->enc);
- }
+ OPENSSL_free(enc->enc);
enc->enc = NULL;
enc->len = 0;
- enc->buf = NULL;
}
int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval,
diff --git a/crypto/internal.h b/crypto/internal.h
index 95d60e1..82ba64d 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -23,6 +23,9 @@
#include <stdlib.h>
#include <string.h>
+#include <utility>
+#include <type_traits>
+
#if defined(BORINGSSL_CONSTANT_TIME_VALIDATION)
#include <valgrind/memcheck.h>
#endif
@@ -1604,4 +1607,28 @@
#endif
+BSSL_NAMESPACE_BEGIN
+// Cleanup implements a custom scope guard, when the cleanup logic does not fit
+// in a destructor. Usage:
+//
+// bssl::Cleanup cleanup = [&] { SomeCleanupWork(local_var); };
+template <typename F>
+class Cleanup {
+ public:
+ static_assert(std::is_invocable_v<F>);
+ static_assert(std::is_same_v<std::invoke_result_t<F>, void>);
+
+ Cleanup(F func) : func_(func) {}
+ Cleanup(const Cleanup &) = delete;
+ Cleanup &operator=(const Cleanup &) = delete;
+ ~Cleanup() { func_(); }
+
+ private:
+ F func_;
+};
+template <typename F>
+Cleanup(F func) -> Cleanup<F>;
+BSSL_NAMESPACE_END
+
+
#endif // OPENSSL_HEADER_CRYPTO_INTERNAL_H
diff --git a/crypto/x509/a_sign.cc b/crypto/x509/a_sign.cc
index 018a31b..90b8784 100644
--- a/crypto/x509/a_sign.cc
+++ b/crypto/x509/a_sign.cc
@@ -18,12 +18,16 @@
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
+#include <openssl/span.h>
#include <openssl/x509.h>
#include <limits.h>
+#include "../internal.h"
+#include "../mem_internal.h"
#include "internal.h"
+
int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1, X509_ALGOR *algor2,
ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey,
const EVP_MD *type) {
@@ -41,55 +45,56 @@
int ASN1_item_sign_ctx(const ASN1_ITEM *it, X509_ALGOR *algor1,
X509_ALGOR *algor2, ASN1_BIT_STRING *signature,
void *asn, EVP_MD_CTX *ctx) {
- int ret = 0;
- uint8_t *in = NULL, *out = NULL;
+ // Historically, this function called |EVP_MD_CTX_cleanup| on return. Some
+ // callers rely on this to avoid memory leaks.
+ bssl::Cleanup cleanup = [&] { EVP_MD_CTX_cleanup(ctx); };
- {
- if (signature->type != V_ASN1_BIT_STRING) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
- goto err;
- }
-
- // Write out the requested copies of the AlgorithmIdentifier.
- if (algor1 && !x509_digest_sign_algorithm(ctx, algor1)) {
- goto err;
- }
- if (algor2 && !x509_digest_sign_algorithm(ctx, algor2)) {
- goto err;
- }
-
- int in_len = ASN1_item_i2d(reinterpret_cast<ASN1_VALUE *>(asn), &in, it);
- if (in_len < 0) {
- goto err;
- }
-
- EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx->pctx);
- size_t out_len = EVP_PKEY_size(pkey);
- if (out_len > INT_MAX) {
- OPENSSL_PUT_ERROR(X509, ERR_R_OVERFLOW);
- goto err;
- }
-
- out = reinterpret_cast<uint8_t *>(OPENSSL_malloc(out_len));
- if (out == NULL) {
- goto err;
- }
-
- if (!EVP_DigestSign(ctx, out, &out_len, in, in_len)) {
- OPENSSL_PUT_ERROR(X509, ERR_R_EVP_LIB);
- goto err;
- }
-
- ASN1_STRING_set0(signature, out, (int)out_len);
- out = NULL;
- signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
- signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
- ret = (int)out_len;
+ // Write out the requested copies of the AlgorithmIdentifier. This may modify
+ // |asn|, so we must do it first.
+ if ((algor1 != nullptr && !x509_digest_sign_algorithm(ctx, algor1)) ||
+ (algor2 != nullptr && !x509_digest_sign_algorithm(ctx, algor2))) {
+ return 0;
}
-err:
- EVP_MD_CTX_cleanup(ctx);
- OPENSSL_free(in);
- OPENSSL_free(out);
- return ret;
+ uint8_t *in = nullptr;
+ int in_len = ASN1_item_i2d(reinterpret_cast<ASN1_VALUE *>(asn), &in, it);
+ if (in_len < 0) {
+ return 0;
+ }
+ bssl::UniquePtr<uint8_t> free_in(in);
+
+ return x509_sign_to_bit_string(ctx, signature, bssl::Span(in, in_len));
+}
+
+int x509_sign_to_bit_string(EVP_MD_CTX *ctx, ASN1_BIT_STRING *out,
+ bssl::Span<const uint8_t> in) {
+ if (out->type != V_ASN1_BIT_STRING) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
+ return 0;
+ }
+
+ EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx->pctx);
+ size_t sig_len = EVP_PKEY_size(pkey);
+ if (sig_len > INT_MAX) {
+ // Ensure the signature will fit in |out|.
+ OPENSSL_PUT_ERROR(X509, ERR_R_OVERFLOW);
+ return 0;
+ }
+ bssl::Array<uint8_t> sig;
+ if (!sig.Init(sig_len)) {
+ return 0;
+ }
+
+ if (!EVP_DigestSign(ctx, sig.data(), &sig_len, in.data(), in.size())) {
+ OPENSSL_PUT_ERROR(X509, ERR_R_EVP_LIB);
+ return 0;
+ }
+ sig.Shrink(sig_len);
+
+ uint8_t *sig_data;
+ sig.Release(&sig_data, &sig_len);
+ ASN1_STRING_set0(out, sig_data, static_cast<int>(sig_len));
+ out->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ out->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+ return static_cast<int>(sig_len);
}
diff --git a/crypto/x509/a_verify.cc b/crypto/x509/a_verify.cc
index 58085ed..9a1483e 100644
--- a/crypto/x509/a_verify.cc
+++ b/crypto/x509/a_verify.cc
@@ -23,12 +23,14 @@
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
+#include <openssl/span.h>
#include "internal.h"
-int ASN1_item_verify(const ASN1_ITEM *it, const X509_ALGOR *a,
- const ASN1_BIT_STRING *signature, void *asn,
- EVP_PKEY *pkey) {
+
+int x509_verify_signature(const X509_ALGOR *sigalg,
+ const ASN1_BIT_STRING *signature,
+ bssl::Span<const uint8_t> in, EVP_PKEY *pkey) {
if (!pkey) {
OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER);
return 0;
@@ -41,34 +43,29 @@
return 0;
}
} else {
- sig_len = (size_t)ASN1_STRING_length(signature);
+ sig_len = static_cast<size_t>(ASN1_STRING_length(signature));
}
- EVP_MD_CTX ctx;
- uint8_t *buf_in = NULL;
- int ret = 0, inl = 0;
- EVP_MD_CTX_init(&ctx);
-
- if (!x509_digest_verify_init(&ctx, a, pkey)) {
- goto err;
+ bssl::ScopedEVP_MD_CTX ctx;
+ if (!x509_digest_verify_init(ctx.get(), sigalg, pkey)) {
+ return 0;
}
-
- inl = ASN1_item_i2d(reinterpret_cast<ASN1_VALUE *>(asn), &buf_in, it);
-
- if (buf_in == NULL) {
- goto err;
- }
-
- if (!EVP_DigestVerify(&ctx, ASN1_STRING_get0_data(signature), sig_len, buf_in,
- inl)) {
+ if (!EVP_DigestVerify(ctx.get(), ASN1_STRING_get0_data(signature), sig_len,
+ in.data(), in.size())) {
OPENSSL_PUT_ERROR(X509, ERR_R_EVP_LIB);
- goto err;
+ return 0;
}
+ return 1;
+}
- ret = 1;
-
-err:
- OPENSSL_free(buf_in);
- EVP_MD_CTX_cleanup(&ctx);
- return ret;
+int ASN1_item_verify(const ASN1_ITEM *it, const X509_ALGOR *sigalg,
+ const ASN1_BIT_STRING *signature, void *asn,
+ EVP_PKEY *pkey) {
+ uint8_t *in = nullptr;
+ int in_len = ASN1_item_i2d(reinterpret_cast<ASN1_VALUE *>(asn), &in, it);
+ if (in_len < 0) {
+ return 0;
+ }
+ bssl::UniquePtr<uint8_t> free_in(in);
+ return x509_verify_signature(sigalg, signature, bssl::Span(in, in_len), pkey);
}
diff --git a/crypto/x509/internal.h b/crypto/x509/internal.h
index dcff849..b4d48ca 100644
--- a/crypto/x509/internal.h
+++ b/crypto/x509/internal.h
@@ -30,13 +30,6 @@
// Internal structures.
-typedef struct X509_val_st {
- ASN1_TIME *notBefore;
- ASN1_TIME *notAfter;
-} X509_VAL;
-
-DECLARE_ASN1_FUNCTIONS_const(X509_VAL)
-
struct X509_pubkey_st {
X509_ALGOR algor;
ASN1_BIT_STRING public_key;
@@ -49,6 +42,7 @@
int x509_parse_public_key(CBS *cbs, X509_PUBKEY *out,
bssl::Span<const EVP_PKEY_ALG *const> algs);
int x509_marshal_public_key(CBB *cbb, const X509_PUBKEY *in);
+int x509_pubkey_set1(X509_PUBKEY *key, EVP_PKEY *pkey);
// X509_PUBKEY is an |ASN1_ITEM| whose ASN.1 type is SubjectPublicKeyInfo and C
// type is |X509_PUBKEY*|.
@@ -107,28 +101,31 @@
// (RFC 5280) and C type is |STACK_OF(X509_EXTENSION)*|.
DECLARE_ASN1_ITEM(X509_EXTENSIONS)
-typedef struct {
- ASN1_INTEGER *version; // [ 0 ] default of v1
- ASN1_INTEGER *serialNumber;
- X509_ALGOR *tbs_sig_alg;
+struct x509_st {
+ // TBSCertificate fields:
+ uint8_t version; // One of the |X509_VERSION_*| constants.
+ ASN1_INTEGER serialNumber;
+ X509_ALGOR tbs_sig_alg;
+ // TODO(crbug.com/42290417): When |X509_NAME| no longer uses the macro system,
+ // try to embed this struct.
X509_NAME *issuer;
- X509_VAL *validity;
+ ASN1_TIME notBefore;
+ ASN1_TIME notAfter;
+ // TODO(crbug.com/42290417): When |X509_NAME| no longer uses the macro system,
+ // try to embed this struct.
X509_NAME *subject;
- X509_PUBKEY *key;
+ X509_PUBKEY key;
ASN1_BIT_STRING *issuerUID; // [ 1 ] optional in v2
ASN1_BIT_STRING *subjectUID; // [ 2 ] optional in v2
STACK_OF(X509_EXTENSION) *extensions; // [ 3 ] optional in v3
- ASN1_ENCODING enc;
-} X509_CINF;
-
-// TODO(https://crbug.com/boringssl/407): This is not const because it contains
-// an |X509_NAME|.
-DECLARE_ASN1_FUNCTIONS(X509_CINF)
-
-struct x509_st {
- X509_CINF *cert_info;
+ // Certificate fields:
X509_ALGOR sig_alg;
ASN1_BIT_STRING signature;
+ // Other state:
+ // buf, if not nullptr, contains a copy of the serialized Certificate.
+ // TODO(davidben): Now every parsed |X509| has an underlying |CRYPTO_BUFFER|,
+ // but |X509|s created peacemeal do not. Can we make this more uniform?
+ CRYPTO_BUFFER *buf;
CRYPTO_refcount_t references;
CRYPTO_EX_DATA ex_data;
// These contain copies of various extension values
@@ -146,6 +143,8 @@
CRYPTO_MUTEX lock;
} /* X509 */;
+int x509_marshal_tbs_cert(CBB *cbb, X509 *x509);
+
// X509 is an |ASN1_ITEM| whose ASN.1 type is X.509 Certificate (RFC 5280) and C
// type is |X509*|.
DECLARE_ASN1_ITEM(X509)
@@ -395,6 +394,17 @@
int x509_digest_verify_init(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg,
EVP_PKEY *pkey);
+// x509_verify_signature verifies a |signature| using |sigalg| and |pkey| over
+// |in|. It returns one if the signature is valid and zero on error.
+int x509_verify_signature(const X509_ALGOR *sigalg,
+ const ASN1_BIT_STRING *signature,
+ bssl::Span<const uint8_t> in, EVP_PKEY *pkey);
+
+// x509_sign_to_bit_string signs |in| using |ctx| and saves the result in |out|.
+// It returns the length of the signature on success and zero on error.
+int x509_sign_to_bit_string(EVP_MD_CTX *ctx, ASN1_BIT_STRING *out,
+ bssl::Span<const uint8_t> in);
+
// Path-building functions.
diff --git a/crypto/x509/t_x509.cc b/crypto/x509/t_x509.cc
index 1264cb0..94d64f7 100644
--- a/crypto/x509/t_x509.cc
+++ b/crypto/x509/t_x509.cc
@@ -59,7 +59,6 @@
nmindent = 16;
}
- const X509_CINF *ci = x->cert_info;
if (!(cflag & X509_FLAG_NO_HEADER)) {
if (BIO_write(bp, "Certificate:\n", 13) <= 0) {
return 0;
@@ -107,7 +106,7 @@
}
if (!(cflag & X509_FLAG_NO_SIGNAME)) {
- if (X509_signature_print(bp, ci->tbs_sig_alg, NULL) <= 0) {
+ if (X509_signature_print(bp, &x->tbs_sig_alg, NULL) <= 0) {
return 0;
}
}
@@ -163,7 +162,7 @@
if (BIO_printf(bp, "%12sPublic Key Algorithm: ", "") <= 0) {
return 0;
}
- if (i2a_ASN1_OBJECT(bp, ci->key->algor.algorithm) <= 0) {
+ if (i2a_ASN1_OBJECT(bp, x->key.algor.algorithm) <= 0) {
return 0;
}
if (BIO_puts(bp, "\n") <= 0) {
@@ -180,26 +179,26 @@
}
if (!(cflag & X509_FLAG_NO_IDS)) {
- if (ci->issuerUID) {
+ if (x->issuerUID) {
if (BIO_printf(bp, "%8sIssuer Unique ID: ", "") <= 0) {
return 0;
}
- if (!X509_signature_dump(bp, ci->issuerUID, 12)) {
+ if (!X509_signature_dump(bp, x->issuerUID, 12)) {
return 0;
}
}
- if (ci->subjectUID) {
+ if (x->subjectUID) {
if (BIO_printf(bp, "%8sSubject Unique ID: ", "") <= 0) {
return 0;
}
- if (!X509_signature_dump(bp, ci->subjectUID, 12)) {
+ if (!X509_signature_dump(bp, x->subjectUID, 12)) {
return 0;
}
}
}
if (!(cflag & X509_FLAG_NO_EXTENSIONS)) {
- X509V3_extensions_print(bp, "X509v3 extensions", ci->extensions, cflag, 8);
+ X509V3_extensions_print(bp, "X509v3 extensions", x->extensions, cflag, 8);
}
if (!(cflag & X509_FLAG_NO_SIGDUMP)) {
diff --git a/crypto/x509/v3_conf.cc b/crypto/x509/v3_conf.cc
index f48c128..e4cb758 100644
--- a/crypto/x509/v3_conf.cc
+++ b/crypto/x509/v3_conf.cc
@@ -308,7 +308,7 @@
const char *section, X509 *cert) {
STACK_OF(X509_EXTENSION) **sk = NULL;
if (cert) {
- sk = &cert->cert_info->extensions;
+ sk = &cert->extensions;
}
return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
}
diff --git a/crypto/x509/v3_skey.cc b/crypto/x509/v3_skey.cc
index 8cf754a..bec89ce 100644
--- a/crypto/x509/v3_skey.cc
+++ b/crypto/x509/v3_skey.cc
@@ -89,7 +89,7 @@
if (ctx->subject_req) {
pk = &ctx->subject_req->req_info->pubkey->public_key;
} else {
- pk = &ctx->subject_cert->cert_info->key->public_key;
+ pk = &ctx->subject_cert->key.public_key;
}
if (!EVP_Digest(pk->data, pk->length, pkey_dig, &diglen, EVP_sha1(), NULL)) {
diff --git a/crypto/x509/x509_cmp.cc b/crypto/x509/x509_cmp.cc
index 4085d12..be08ec8 100644
--- a/crypto/x509/x509_cmp.cc
+++ b/crypto/x509/x509_cmp.cc
@@ -29,15 +29,15 @@
int X509_issuer_name_cmp(const X509 *a, const X509 *b) {
- return (X509_NAME_cmp(a->cert_info->issuer, b->cert_info->issuer));
+ return X509_NAME_cmp(a->issuer, b->issuer);
}
int X509_subject_name_cmp(const X509 *a, const X509 *b) {
- return (X509_NAME_cmp(a->cert_info->subject, b->cert_info->subject));
+ return X509_NAME_cmp(a->subject, b->subject);
}
int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b) {
- return (X509_NAME_cmp(a->crl->issuer, b->crl->issuer));
+ return X509_NAME_cmp(a->crl->issuer, b->crl->issuer);
}
int X509_CRL_match(const X509_CRL *a, const X509_CRL *b) {
@@ -45,35 +45,31 @@
}
X509_NAME *X509_get_issuer_name(const X509 *a) {
- return a->cert_info->issuer;
+ // This function is not const-correct for OpenSSL compatibility.
+ return a->issuer;
}
-uint32_t X509_issuer_name_hash(X509 *x) {
- return X509_NAME_hash(x->cert_info->issuer);
-}
+uint32_t X509_issuer_name_hash(X509 *x) { return X509_NAME_hash(x->issuer); }
uint32_t X509_issuer_name_hash_old(X509 *x) {
- return (X509_NAME_hash_old(x->cert_info->issuer));
+ return X509_NAME_hash_old(x->issuer);
}
X509_NAME *X509_get_subject_name(const X509 *a) {
- return a->cert_info->subject;
+ // This function is not const-correct for OpenSSL compatibility.
+ return a->subject;
}
-ASN1_INTEGER *X509_get_serialNumber(X509 *a) {
- return a->cert_info->serialNumber;
-}
+ASN1_INTEGER *X509_get_serialNumber(X509 *a) { return &a->serialNumber; }
const ASN1_INTEGER *X509_get0_serialNumber(const X509 *x509) {
- return x509->cert_info->serialNumber;
+ return &x509->serialNumber;
}
-uint32_t X509_subject_name_hash(X509 *x) {
- return X509_NAME_hash(x->cert_info->subject);
-}
+uint32_t X509_subject_name_hash(X509 *x) { return X509_NAME_hash(x->subject); }
uint32_t X509_subject_name_hash_old(X509 *x) {
- return X509_NAME_hash_old(x->cert_info->subject);
+ return X509_NAME_hash_old(x->subject);
}
// Compare two certificates: they must be identical for this to work. NB:
@@ -180,21 +176,22 @@
if (x == NULL) {
return NULL;
}
- return X509_PUBKEY_get0(x->cert_info->key);
+ return X509_PUBKEY_get0(&x->key);
}
EVP_PKEY *X509_get_pubkey(const X509 *x) {
if (x == NULL) {
return NULL;
}
- return X509_PUBKEY_get(x->cert_info->key);
+ return X509_PUBKEY_get(&x->key);
}
ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x) {
if (!x) {
return NULL;
}
- return &x->cert_info->key->public_key;
+ // This function is not const-correct for OpenSSL compatibility.
+ return const_cast<ASN1_BIT_STRING*>(&x->key.public_key);
}
int X509_check_private_key(const X509 *x, const EVP_PKEY *k) {
diff --git a/crypto/x509/x509_ext.cc b/crypto/x509/x509_ext.cc
index baddf5a..bb896dc 100644
--- a/crypto/x509/x509_ext.cc
+++ b/crypto/x509/x509_ext.cc
@@ -70,42 +70,41 @@
}
int X509_get_ext_count(const X509 *x) {
- return X509v3_get_ext_count(x->cert_info->extensions);
+ return X509v3_get_ext_count(x->extensions);
}
int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos) {
- return X509v3_get_ext_by_NID(x->cert_info->extensions, nid, lastpos);
+ return X509v3_get_ext_by_NID(x->extensions, nid, lastpos);
}
int X509_get_ext_by_OBJ(const X509 *x, const ASN1_OBJECT *obj, int lastpos) {
- return X509v3_get_ext_by_OBJ(x->cert_info->extensions, obj, lastpos);
+ return X509v3_get_ext_by_OBJ(x->extensions, obj, lastpos);
}
int X509_get_ext_by_critical(const X509 *x, int crit, int lastpos) {
- return X509v3_get_ext_by_critical(x->cert_info->extensions, crit, lastpos);
+ return X509v3_get_ext_by_critical(x->extensions, crit, lastpos);
}
X509_EXTENSION *X509_get_ext(const X509 *x, int loc) {
- return X509v3_get_ext(x->cert_info->extensions, loc);
+ return X509v3_get_ext(x->extensions, loc);
}
X509_EXTENSION *X509_delete_ext(X509 *x, int loc) {
- return delete_ext(&x->cert_info->extensions, loc);
+ return delete_ext(&x->extensions, loc);
}
int X509_add_ext(X509 *x, const X509_EXTENSION *ex, int loc) {
- return X509v3_add_ext(&x->cert_info->extensions, ex, loc) != NULL;
+ return X509v3_add_ext(&x->extensions, ex, loc) != NULL;
}
void *X509_get_ext_d2i(const X509 *x509, int nid, int *out_critical,
int *out_idx) {
- return X509V3_get_d2i(x509->cert_info->extensions, nid, out_critical,
- out_idx);
+ return X509V3_get_d2i(x509->extensions, nid, out_critical, out_idx);
}
int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit,
unsigned long flags) {
- return X509V3_add1_i2d(&x->cert_info->extensions, nid, value, crit, flags);
+ return X509V3_add1_i2d(&x->extensions, nid, value, crit, flags);
}
int X509_REVOKED_get_ext_count(const X509_REVOKED *x) {
diff --git a/crypto/x509/x509_lu.cc b/crypto/x509/x509_lu.cc
index eb9624a..6646f4d 100644
--- a/crypto/x509/x509_lu.cc
+++ b/crypto/x509/x509_lu.cc
@@ -287,7 +287,6 @@
X509_NAME *name, int *pnmatch) {
X509_OBJECT stmp;
X509 x509_s;
- X509_CINF cinf_s;
X509_CRL crl_s;
X509_CRL_INFO crl_info_s;
@@ -295,8 +294,7 @@
switch (type) {
case X509_LU_X509:
stmp.data.x509 = &x509_s;
- x509_s.cert_info = &cinf_s;
- cinf_s.subject = name;
+ x509_s.subject = name;
break;
case X509_LU_CRL:
stmp.data.crl = &crl_s;
diff --git a/crypto/x509/x509_set.cc b/crypto/x509/x509_set.cc
index 76c86d3..98ab04f 100644
--- a/crypto/x509/x509_set.cc
+++ b/crypto/x509/x509_set.cc
@@ -21,13 +21,7 @@
#include "internal.h"
-long X509_get_version(const X509 *x509) {
- // The default version is v1(0).
- if (x509->cert_info->version == NULL) {
- return X509_VERSION_1;
- }
- return ASN1_INTEGER_get(x509->cert_info->version);
-}
+long X509_get_version(const X509 *x509) { return x509->version; }
int X509_set_version(X509 *x, long version) {
if (x == NULL) {
@@ -39,20 +33,8 @@
return 0;
}
- // v1(0) is default and is represented by omitting the version.
- if (version == X509_VERSION_1) {
- ASN1_INTEGER_free(x->cert_info->version);
- x->cert_info->version = NULL;
- return 1;
- }
-
- if (x->cert_info->version == NULL) {
- x->cert_info->version = ASN1_INTEGER_new();
- if (x->cert_info->version == NULL) {
- return 0;
- }
- }
- return ASN1_INTEGER_set_int64(x->cert_info->version, version);
+ x->version = static_cast<uint8_t>(version);
+ return 1;
}
int X509_set_serialNumber(X509 *x, const ASN1_INTEGER *serial) {
@@ -61,138 +43,99 @@
return 0;
}
- ASN1_INTEGER *in;
- if (x == NULL) {
- return 0;
- }
- in = x->cert_info->serialNumber;
- if (in != serial) {
- in = ASN1_INTEGER_dup(serial);
- if (in != NULL) {
- ASN1_INTEGER_free(x->cert_info->serialNumber);
- x->cert_info->serialNumber = in;
- }
- }
- return in != NULL;
+ return ASN1_STRING_copy(&x->serialNumber, serial);
}
int X509_set_issuer_name(X509 *x, X509_NAME *name) {
- if ((x == NULL) || (x->cert_info == NULL)) {
+ if (x == NULL) {
return 0;
}
- return (X509_NAME_set(&x->cert_info->issuer, name));
+ return (X509_NAME_set(&x->issuer, name));
}
int X509_set_subject_name(X509 *x, X509_NAME *name) {
- if ((x == NULL) || (x->cert_info == NULL)) {
+ if (x == NULL) {
return 0;
}
- return (X509_NAME_set(&x->cert_info->subject, name));
+ return (X509_NAME_set(&x->subject, name));
}
int X509_set1_notBefore(X509 *x, const ASN1_TIME *tm) {
- ASN1_TIME *in;
-
- if ((x == NULL) || (x->cert_info->validity == NULL)) {
- return 0;
- }
- in = x->cert_info->validity->notBefore;
- if (in != tm) {
- in = ASN1_STRING_dup(tm);
- if (in != NULL) {
- ASN1_TIME_free(x->cert_info->validity->notBefore);
- x->cert_info->validity->notBefore = in;
- }
- }
- return in != NULL;
+ // TODO(crbug.com/42290309): Check that |tm->type| is correct.
+ return ASN1_STRING_copy(&x->notBefore, tm);
}
int X509_set_notBefore(X509 *x, const ASN1_TIME *tm) {
return X509_set1_notBefore(x, tm);
}
-const ASN1_TIME *X509_get0_notBefore(const X509 *x) {
- return x->cert_info->validity->notBefore;
-}
+const ASN1_TIME *X509_get0_notBefore(const X509 *x) { return &x->notBefore; }
ASN1_TIME *X509_getm_notBefore(X509 *x) {
// Note this function takes a const |X509| pointer in OpenSSL. We require
// non-const as this allows mutating |x|. If it comes up for compatibility,
// we can relax this.
- return x->cert_info->validity->notBefore;
+ return &x->notBefore;
}
ASN1_TIME *X509_get_notBefore(const X509 *x509) {
// In OpenSSL, this function is an alias for |X509_getm_notBefore|, but our
// |X509_getm_notBefore| is const-correct. |X509_get_notBefore| was
// originally a macro, so it needs to capture both get0 and getm use cases.
- return x509->cert_info->validity->notBefore;
+ return const_cast<ASN1_TIME *>(&x509->notBefore);
}
int X509_set1_notAfter(X509 *x, const ASN1_TIME *tm) {
- ASN1_TIME *in;
-
- if ((x == NULL) || (x->cert_info->validity == NULL)) {
- return 0;
- }
- in = x->cert_info->validity->notAfter;
- if (in != tm) {
- in = ASN1_STRING_dup(tm);
- if (in != NULL) {
- ASN1_TIME_free(x->cert_info->validity->notAfter);
- x->cert_info->validity->notAfter = in;
- }
- }
- return in != NULL;
+ // TODO(crbug.com/42290309): Check that |tm->type| is correct.
+ return ASN1_STRING_copy(&x->notAfter, tm);
}
int X509_set_notAfter(X509 *x, const ASN1_TIME *tm) {
return X509_set1_notAfter(x, tm);
}
-const ASN1_TIME *X509_get0_notAfter(const X509 *x) {
- return x->cert_info->validity->notAfter;
-}
+const ASN1_TIME *X509_get0_notAfter(const X509 *x) { return &x->notAfter; }
ASN1_TIME *X509_getm_notAfter(X509 *x) {
// Note this function takes a const |X509| pointer in OpenSSL. We require
// non-const as this allows mutating |x|. If it comes up for compatibility,
// we can relax this.
- return x->cert_info->validity->notAfter;
+ return &x->notAfter;
}
ASN1_TIME *X509_get_notAfter(const X509 *x509) {
// In OpenSSL, this function is an alias for |X509_getm_notAfter|, but our
// |X509_getm_notAfter| is const-correct. |X509_get_notAfter| was
// originally a macro, so it needs to capture both get0 and getm use cases.
- return x509->cert_info->validity->notAfter;
-}
+ return const_cast<ASN1_TIME *>(&x509->notAfter);
+ }
void X509_get0_uids(const X509 *x509, const ASN1_BIT_STRING **out_issuer_uid,
const ASN1_BIT_STRING **out_subject_uid) {
if (out_issuer_uid != NULL) {
- *out_issuer_uid = x509->cert_info->issuerUID;
+ *out_issuer_uid = x509->issuerUID;
}
if (out_subject_uid != NULL) {
- *out_subject_uid = x509->cert_info->subjectUID;
+ *out_subject_uid = x509->subjectUID;
}
}
int X509_set_pubkey(X509 *x, EVP_PKEY *pkey) {
- if ((x == NULL) || (x->cert_info == NULL)) {
+ if (x == nullptr) {
return 0;
}
- return (X509_PUBKEY_set(&(x->cert_info->key), pkey));
+ return x509_pubkey_set1(&x->key, pkey);
}
const STACK_OF(X509_EXTENSION) *X509_get0_extensions(const X509 *x) {
- return x->cert_info->extensions;
+ return x->extensions;
}
const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x) {
- return x->cert_info->tbs_sig_alg;
+ return &x->tbs_sig_alg;
}
X509_PUBKEY *X509_get_X509_PUBKEY(const X509 *x509) {
- return x509->cert_info->key;
+ // This function is not const-correct for OpenSSL compatibility.
+ return const_cast<X509_PUBKEY *>(&x509->key);
}
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 94aeb4e..c2eb764 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -2663,15 +2663,12 @@
bssl::UniquePtr<X509> root(X509_parse_from_buffer(buf.get()));
ASSERT_TRUE(root);
- const uint8_t *enc_pointer = root->cert_info->enc.enc;
- const uint8_t *buf_pointer = CRYPTO_BUFFER_data(buf.get());
- ASSERT_GE(enc_pointer, buf_pointer);
- ASSERT_LT(enc_pointer, buf_pointer + CRYPTO_BUFFER_len(buf.get()));
+ EXPECT_EQ(buf.get(), root->buf);
buf.reset();
- /* 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]);
+ // This ensures the X509 took a reference to |buf|, otherwise this will be a
+ // reference to free memory and ASAN should notice.
+ CRYPTO_BUFFER_len(root->buf);
}
TEST(X509Test, TestFromBufferWithTrailingData) {
@@ -2730,28 +2727,23 @@
size_t data2_len;
bssl::UniquePtr<uint8_t> data2;
ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM));
- EXPECT_TRUE(buffers_alias(root->cert_info->enc.enc, root->cert_info->enc.len,
- CRYPTO_BUFFER_data(buf.get()),
- CRYPTO_BUFFER_len(buf.get())));
+ EXPECT_EQ(root->buf, buf.get());
// Historically, this function tested the interaction betweeen
// |X509_parse_from_buffer| and object reuse. We no longer support object
// reuse, so |d2i_X509| will replace |raw| with a new object. However, we
// retain this test to verify that releasing objects from |d2i_X509| works
- // correctly.
+ // correctly and doesn't keep the old buffer.
X509 *raw = root.release();
const uint8_t *inp = data2.get();
X509 *ret = d2i_X509(&raw, &inp, data2_len);
root.reset(raw);
ASSERT_EQ(root.get(), ret);
- ASSERT_EQ(nullptr, root->cert_info->enc.buf);
- EXPECT_FALSE(buffers_alias(root->cert_info->enc.enc, root->cert_info->enc.len,
- CRYPTO_BUFFER_data(buf.get()),
- CRYPTO_BUFFER_len(buf.get())));
+ ASSERT_NE(buf.get(), root->buf);
- // Free |data2| and ensure that |root| took its own copy. Otherwise the
- // following will trigger a use-after-free.
+ // Free |data2| and ensure that |root| took its own copy. Otherwise
+ // serializing |root|, below, will trigger a use-after-free.
data2.reset();
uint8_t *i2d = nullptr;
@@ -2760,10 +2752,7 @@
bssl::UniquePtr<uint8_t> i2d_storage(i2d);
ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM));
-
- ASSERT_EQ(static_cast<long>(data2_len), i2d_len);
- ASSERT_EQ(0, OPENSSL_memcmp(data2.get(), i2d, i2d_len));
- ASSERT_EQ(nullptr, root->cert_info->enc.buf);
+ EXPECT_EQ(Bytes(i2d, i2d_len), Bytes(data2.get(), data2_len));
}
TEST(X509Test, TestFailedParseFromBuffer) {
diff --git a/crypto/x509/x_all.cc b/crypto/x509/x_all.cc
index 73f620a..e98d046 100644
--- a/crypto/x509/x_all.cc
+++ b/crypto/x509/x_all.cc
@@ -17,24 +17,33 @@
#include <limits.h>
#include <openssl/asn1.h>
+#include <openssl/bytestring.h>
#include <openssl/digest.h>
#include <openssl/dsa.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/rsa.h>
+#include <openssl/span.h>
#include <openssl/stack.h>
#include "../asn1/internal.h"
+#include "../internal.h"
#include "internal.h"
int X509_verify(X509 *x509, EVP_PKEY *pkey) {
- if (X509_ALGOR_cmp(&x509->sig_alg, x509->cert_info->tbs_sig_alg)) {
+ if (X509_ALGOR_cmp(&x509->sig_alg, &x509->tbs_sig_alg)) {
OPENSSL_PUT_ERROR(X509, X509_R_SIGNATURE_ALGORITHM_MISMATCH);
return 0;
}
- return ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF), &x509->sig_alg,
- &x509->signature, x509->cert_info, pkey);
+ // This uses the cached TBSCertificate encoding, if any.
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 128) || !x509_marshal_tbs_cert(cbb.get(), x509)) {
+ return 0;
+ }
+ return x509_verify_signature(
+ &x509->sig_alg, &x509->signature,
+ bssl::Span(CBB_data(cbb.get()), CBB_len(cbb.get())), pkey);
}
int X509_REQ_verify(X509_REQ *req, EVP_PKEY *pkey) {
@@ -43,21 +52,41 @@
}
int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md) {
- asn1_encoding_clear(&x->cert_info->enc);
- return (ASN1_item_sign(ASN1_ITEM_rptr(X509_CINF), x->cert_info->tbs_sig_alg,
- &x->sig_alg, &x->signature, x->cert_info, pkey, md));
+ bssl::ScopedEVP_MD_CTX ctx;
+ if (!EVP_DigestSignInit(ctx.get(), nullptr, md, nullptr, pkey)) {
+ return 0;
+ }
+ return X509_sign_ctx(x, ctx.get());
}
int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx) {
- asn1_encoding_clear(&x->cert_info->enc);
- return ASN1_item_sign_ctx(ASN1_ITEM_rptr(X509_CINF), x->cert_info->tbs_sig_alg,
- &x->sig_alg, &x->signature, x->cert_info, ctx);
+ // Historically, this function called |EVP_MD_CTX_cleanup| on return. Some
+ // callers rely on this to avoid memory leaks.
+ bssl::Cleanup cleanup = [&] { EVP_MD_CTX_cleanup(ctx); };
+
+ // Fill in the two copies of AlgorithmIdentifier. Note one of these modifies
+ // the TBSCertificate.
+ if (!x509_digest_sign_algorithm(ctx, &x->tbs_sig_alg) ||
+ !x509_digest_sign_algorithm(ctx, &x->sig_alg)) {
+ return 0;
+ }
+
+ // Discard the cached encoding. (We just modified it.)
+ CRYPTO_BUFFER_free(x->buf);
+ x->buf = nullptr;
+
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 128) || !x509_marshal_tbs_cert(cbb.get(), x)) {
+ return 0;
+ }
+ return x509_sign_to_bit_string(
+ ctx, &x->signature, bssl::Span(CBB_data(cbb.get()), CBB_len(cbb.get())));
}
int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md) {
asn1_encoding_clear(&x->req_info->enc);
- return (ASN1_item_sign(ASN1_ITEM_rptr(X509_REQ_INFO), x->sig_alg, NULL,
- x->signature, x->req_info, pkey, md));
+ return ASN1_item_sign(ASN1_ITEM_rptr(X509_REQ_INFO), x->sig_alg, NULL,
+ x->signature, x->req_info, pkey, md);
}
int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx) {
@@ -68,8 +97,8 @@
int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md) {
asn1_encoding_clear(&x->crl->enc);
- return (ASN1_item_sign(ASN1_ITEM_rptr(X509_CRL_INFO), x->crl->sig_alg,
- x->sig_alg, x->signature, x->crl, pkey, md));
+ return ASN1_item_sign(ASN1_ITEM_rptr(X509_CRL_INFO), x->crl->sig_alg,
+ x->sig_alg, x->signature, x->crl, pkey, md);
}
int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx) {
@@ -79,13 +108,13 @@
}
int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md) {
- return (ASN1_item_sign(ASN1_ITEM_rptr(NETSCAPE_SPKAC), x->sig_algor, NULL,
- x->signature, x->spkac, pkey, md));
+ return ASN1_item_sign(ASN1_ITEM_rptr(NETSCAPE_SPKAC), x->sig_algor, NULL,
+ x->signature, x->spkac, pkey, md);
}
int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *spki, EVP_PKEY *pkey) {
- return (ASN1_item_verify(ASN1_ITEM_rptr(NETSCAPE_SPKAC), spki->sig_algor,
- spki->signature, spki->spkac, pkey));
+ return ASN1_item_verify(ASN1_ITEM_rptr(NETSCAPE_SPKAC), spki->sig_algor,
+ spki->signature, spki->spkac, pkey);
}
X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl) {
diff --git a/crypto/x509/x_name.cc b/crypto/x509/x_name.cc
index 6dad6a2..7ec83f6 100644
--- a/crypto/x509/x_name.cc
+++ b/crypto/x509/x_name.cc
@@ -152,7 +152,7 @@
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, /*buf=*/NULL) <= 0) {
+ /*aclass=*/0, /*opt=*/0) <= 0) {
return 0;
}
intname = (STACK_OF(STACK_OF_X509_NAME_ENTRY) *)intname_val;
diff --git a/crypto/x509/x_pubkey.cc b/crypto/x509/x_pubkey.cc
index be6c17f..8b74f65 100644
--- a/crypto/x509/x_pubkey.cc
+++ b/crypto/x509/x_pubkey.cc
@@ -132,42 +132,30 @@
IMPLEMENT_EXTERN_ASN1_SIMPLE(X509_PUBKEY, X509_PUBKEY_new, X509_PUBKEY_free,
x509_parse_public_key_default, i2d_X509_PUBKEY)
-int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) {
- X509_PUBKEY *pk = NULL;
- uint8_t *spki = NULL;
- size_t spki_len;
-
- if (x == NULL) {
+int x509_pubkey_set1(X509_PUBKEY *key, EVP_PKEY *pkey) {
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 64) ||
+ !EVP_marshal_public_key(cbb.get(), pkey)) {
+ OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR);
return 0;
}
- CBB cbb;
- const uint8_t *p;
- if (!CBB_init(&cbb, 0) || //
- !EVP_marshal_public_key(&cbb, pkey) ||
- !CBB_finish(&cbb, &spki, &spki_len) || //
- spki_len > LONG_MAX) {
- CBB_cleanup(&cbb);
- OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR);
- goto error;
- }
+ CBS cbs;
+ CBS_init(&cbs, CBB_data(cbb.get()), CBB_len(cbb.get()));
+ // TODO(crbug.com/42290364): Use an |EVP_PKEY_ALG| derived from |pkey|.
+ // |X509_PUBKEY_get0| does not currently work when setting, say, an
+ // |EVP_PKEY_RSA_PSS| key.
+ return x509_parse_public_key(&cbs, key, bssl::GetDefaultEVPAlgorithms());
+}
- p = spki;
- pk = d2i_X509_PUBKEY(NULL, &p, (long)spki_len);
- if (pk == NULL || p != spki + spki_len) {
- OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR);
- goto error;
+int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) {
+ bssl::UniquePtr<X509_PUBKEY> new_key(X509_PUBKEY_new());
+ if (new_key == nullptr || !x509_pubkey_set1(new_key.get(), pkey)) {
+ return 0;
}
-
- OPENSSL_free(spki);
X509_PUBKEY_free(*x);
- *x = pk;
-
+ *x = new_key.release();
return 1;
-error:
- X509_PUBKEY_free(pk);
- OPENSSL_free(spki);
- return 0;
}
EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key) {
diff --git a/crypto/x509/x_val.cc b/crypto/x509/x_val.cc
deleted file mode 100644
index c9bc0a0..0000000
--- a/crypto/x509/x_val.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <stdio.h>
-
-#include <openssl/asn1t.h>
-#include <openssl/x509.h>
-
-#include "internal.h"
-
-
-ASN1_SEQUENCE(X509_VAL) = {
- ASN1_SIMPLE(X509_VAL, notBefore, ASN1_TIME),
- ASN1_SIMPLE(X509_VAL, notAfter, ASN1_TIME),
-} ASN1_SEQUENCE_END(X509_VAL)
-
-IMPLEMENT_ASN1_FUNCTIONS_const(X509_VAL)
diff --git a/crypto/x509/x_x509.cc b/crypto/x509/x_x509.cc
index f4793d4..520ae63 100644
--- a/crypto/x509/x_x509.cc
+++ b/crypto/x509/x_x509.cc
@@ -27,36 +27,36 @@
#include "../asn1/internal.h"
#include "../bytestring/internal.h"
+#include "../evp/internal.h"
#include "../internal.h"
#include "internal.h"
static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
-ASN1_SEQUENCE_enc(X509_CINF, enc, 0) = {
- ASN1_EXP_OPT(X509_CINF, version, ASN1_INTEGER, 0),
- ASN1_SIMPLE(X509_CINF, serialNumber, ASN1_INTEGER),
- ASN1_SIMPLE(X509_CINF, tbs_sig_alg, X509_ALGOR),
- ASN1_SIMPLE(X509_CINF, issuer, X509_NAME),
- ASN1_SIMPLE(X509_CINF, validity, X509_VAL),
- ASN1_SIMPLE(X509_CINF, subject, X509_NAME),
- ASN1_SIMPLE(X509_CINF, key, X509_PUBKEY),
- ASN1_IMP_OPT(X509_CINF, issuerUID, ASN1_BIT_STRING, 1),
- ASN1_IMP_OPT(X509_CINF, subjectUID, ASN1_BIT_STRING, 2),
- ASN1_EXP_SEQUENCE_OF_OPT(X509_CINF, extensions, X509_EXTENSION, 3),
-} ASN1_SEQUENCE_END_enc(X509_CINF, X509_CINF)
+static constexpr CBS_ASN1_TAG kVersionTag =
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0;
+static constexpr CBS_ASN1_TAG kIssuerUIDTag = CBS_ASN1_CONTEXT_SPECIFIC | 1;
+static constexpr CBS_ASN1_TAG kSubjectUIDTag = CBS_ASN1_CONTEXT_SPECIFIC | 2;
+static constexpr CBS_ASN1_TAG kExtensionsTag =
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3;
-IMPLEMENT_ASN1_FUNCTIONS(X509_CINF)
-
-// x509_new_null returns a new |X509| object where the |cert_info|, |sig_alg|,
-// and |signature| fields are not yet filled in.
-static X509 *x509_new_null(void) {
- X509 *ret = reinterpret_cast<X509 *>(OPENSSL_zalloc(sizeof(X509)));
- if (ret == NULL) {
- return NULL;
+// x509_new_null returns a new |X509| object where the |issuer| and |subject|
+// fields are not yet filled in.
+static bssl::UniquePtr<X509> x509_new_null(void) {
+ bssl::UniquePtr<X509> ret(
+ reinterpret_cast<X509 *>(OPENSSL_zalloc(sizeof(X509))));
+ if (ret == nullptr) {
+ return nullptr;
}
ret->references = 1;
ret->ex_pathlen = -1;
+ ret->version = X509_VERSION_1;
+ asn1_string_init(&ret->serialNumber, V_ASN1_INTEGER);
+ x509_algor_init(&ret->tbs_sig_alg);
+ asn1_string_init(&ret->notBefore, -1);
+ asn1_string_init(&ret->notAfter, -1);
+ x509_pubkey_init(&ret->key);
x509_algor_init(&ret->sig_alg);
asn1_string_init(&ret->signature, V_ASN1_BIT_STRING);
CRYPTO_new_ex_data(&ret->ex_data);
@@ -65,18 +65,19 @@
}
X509 *X509_new(void) {
- X509 *ret = x509_new_null();
- if (ret == NULL) {
- return NULL;
+ bssl::UniquePtr<X509> ret = x509_new_null();
+ if (ret == nullptr) {
+ return nullptr;
}
-
- ret->cert_info = X509_CINF_new();
- if (ret->cert_info == NULL) {
- X509_free(ret);
- return NULL;
+ // TODO(crbug.com/42290417): When the |X509_NAME| parser is CBS-based and
+ // writes into a pre-existing |X509_NAME|, we will no longer need the
+ // |X509_new| and |x509_new_null| split.
+ ret->issuer = X509_NAME_new();
+ ret->subject = X509_NAME_new();
+ if (ret->issuer == nullptr || ret->subject == nullptr) {
+ return nullptr;
}
-
- return ret;
+ return ret.release();
}
void X509_free(X509 *x509) {
@@ -86,9 +87,19 @@
CRYPTO_free_ex_data(&g_ex_data_class, &x509->ex_data);
- X509_CINF_free(x509->cert_info);
+ asn1_string_cleanup(&x509->serialNumber);
+ x509_algor_cleanup(&x509->tbs_sig_alg);
+ X509_NAME_free(x509->issuer);
+ asn1_string_cleanup(&x509->notBefore);
+ asn1_string_cleanup(&x509->notAfter);
+ X509_NAME_free(x509->subject);
+ x509_pubkey_cleanup(&x509->key);
+ ASN1_BIT_STRING_free(x509->issuerUID);
+ ASN1_BIT_STRING_free(x509->subjectUID);
+ sk_X509_EXTENSION_pop_free(x509->extensions, X509_EXTENSION_free);
x509_algor_cleanup(&x509->sig_alg);
asn1_string_cleanup(&x509->signature);
+ CRYPTO_BUFFER_free(x509->buf);
ASN1_OCTET_STRING_free(x509->skid);
AUTHORITY_KEYID_free(x509->akid);
CRL_DIST_POINTS_free(x509->crldp);
@@ -100,18 +111,38 @@
OPENSSL_free(x509);
}
-static X509 *x509_parse(CBS *cbs, CRYPTO_BUFFER *buf) {
+static int parse_name(CBS *cbs, X509_NAME **out) {
+ // TODO(crbug.com/42290417): Make the |X509_NAME| parser CBS-based and avoid
+ // this awkward conversion.
+ const uint8_t *p = CBS_data(cbs);
+ X509_NAME_free(*out);
+ *out = d2i_X509_NAME(nullptr, &p, CBS_len(cbs));
+ if (*out == nullptr) {
+ return 0;
+ }
+ BSSL_CHECK(CBS_skip(cbs, p - CBS_data(cbs)));
+ return 1;
+}
+
+
+X509 *X509_parse_from_buffer(CRYPTO_BUFFER *buf) {
bssl::UniquePtr<X509> ret(x509_new_null());
if (ret == nullptr) {
return nullptr;
}
- CBS cert, tbs;
- if (!CBS_get_asn1(cbs, &cert, CBS_ASN1_SEQUENCE) ||
+ // Save the buffer to cache the original encoding.
+ ret->buf = bssl::UpRef(buf).release();
+
+ // Parse the Certificate.
+ CBS cbs, cert, tbs;
+ CRYPTO_BUFFER_init_CBS(buf, &cbs);
+ if (!CBS_get_asn1(&cbs, &cert, CBS_ASN1_SEQUENCE) || //
+ CBS_len(&cbs) != 0 ||
// Bound the length to comfortably fit in an int. Lengths in this
// module often omit overflow checks.
CBS_len(&cert) > INT_MAX / 2 ||
- !CBS_get_asn1_element(&cert, &tbs, CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1(&cert, &tbs, CBS_ASN1_SEQUENCE) ||
!x509_parse_algorithm(&cert, &ret->sig_alg) ||
// For just the signature field, we accept non-minimal BER lengths, though
// not indefinite-length encoding. See b/18228011.
@@ -124,49 +155,168 @@
return nullptr;
}
- // TODO(crbug.com/boringssl/443): When the rest of the library is decoupled
- // from the tasn_*.c implementation, replace this with |CBS|-based
- // functions.
- const uint8_t *inp = CBS_data(&tbs);
- if (ASN1_item_ex_d2i((ASN1_VALUE **)&ret->cert_info, &inp, CBS_len(&tbs),
- ASN1_ITEM_rptr(X509_CINF), /*tag=*/-1,
- /*aclass=*/0, /*opt=*/0, buf) <= 0 ||
- inp != CBS_data(&tbs) + CBS_len(&tbs)) {
- return nullptr;
- }
-
- // The version must be one of v1(0), v2(1), or v3(2).
- long version = X509_VERSION_1;
- if (ret->cert_info->version != nullptr) {
- version = ASN1_INTEGER_get(ret->cert_info->version);
- // TODO(https://crbug.com/boringssl/364): |X509_VERSION_1| should
- // also be rejected here. This means an explicitly-encoded X.509v1
- // version. v1 is DEFAULT, so DER requires it be omitted.
- if (version < X509_VERSION_1 || version > X509_VERSION_3) {
+ // Parse the TBSCertificate.
+ if (CBS_peek_asn1_tag(&tbs, kVersionTag)) {
+ CBS wrapper;
+ uint64_t version;
+ if (!CBS_get_asn1(&tbs, &wrapper, kVersionTag) ||
+ !CBS_get_asn1_uint64(&wrapper, &version) || //
+ CBS_len(&wrapper) != 0) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return nullptr;
+ }
+ // The version must be one of v1(0), v2(1), or v3(2).
+ // TODO(https://crbug.com/42290225): Also reject |X509_VERSION_1|. v1 is
+ // DEFAULT, so DER requires it be omitted.
+ if (version != X509_VERSION_1 && version != X509_VERSION_2 &&
+ version != X509_VERSION_3) {
OPENSSL_PUT_ERROR(X509, X509_R_INVALID_VERSION);
return nullptr;
}
+ ret->version = static_cast<uint8_t>(version);
+ } else {
+ ret->version = X509_VERSION_1;
}
-
- // Per RFC 5280, section 4.1.2.8, these fields require v2 or v3.
- if (version == X509_VERSION_1 && (ret->cert_info->issuerUID != nullptr ||
- ret->cert_info->subjectUID != nullptr)) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_FIELD_FOR_VERSION);
+ CBS validity;
+ if (!asn1_parse_integer(&tbs, &ret->serialNumber, /*tag=*/0) ||
+ !x509_parse_algorithm(&tbs, &ret->tbs_sig_alg) ||
+ !parse_name(&tbs, &ret->issuer) ||
+ !CBS_get_asn1(&tbs, &validity, CBS_ASN1_SEQUENCE) ||
+ !asn1_parse_time(&validity, &ret->notBefore,
+ /*allow_utc_timezone_offset=*/1) ||
+ !asn1_parse_time(&validity, &ret->notAfter,
+ /*allow_utc_timezone_offset=*/1) ||
+ CBS_len(&validity) != 0 || //
+ !parse_name(&tbs, &ret->subject) ||
+ // TODO(crbug.com/42290364): Expose an API to use different algorithms.
+ !x509_parse_public_key(&tbs, &ret->key,
+ bssl::GetDefaultEVPAlgorithms())) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return nullptr;
}
-
- // Per RFC 5280, section 4.1.2.9, extensions require v3.
- if (version != X509_VERSION_3 && ret->cert_info->extensions != nullptr) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_FIELD_FOR_VERSION);
+ // Per RFC 5280, section 4.1.2.8, these fields require v2 or v3:
+ if (ret->version >= X509_VERSION_2 &&
+ CBS_peek_asn1_tag(&tbs, kIssuerUIDTag)) {
+ ret->issuerUID = ASN1_BIT_STRING_new();
+ if (ret->issuerUID == nullptr ||
+ !asn1_parse_bit_string(&tbs, ret->issuerUID, kIssuerUIDTag)) {
+ return nullptr;
+ }
+ }
+ if (ret->version >= X509_VERSION_2 &&
+ CBS_peek_asn1_tag(&tbs, kSubjectUIDTag)) {
+ ret->subjectUID = ASN1_BIT_STRING_new();
+ if (ret->subjectUID == nullptr ||
+ !asn1_parse_bit_string(&tbs, ret->subjectUID, kSubjectUIDTag)) {
+ return nullptr;
+ }
+ }
+ // Per RFC 5280, section 4.1.2.9, extensions require v3:
+ if (ret->version >= X509_VERSION_3 &&
+ CBS_peek_asn1_tag(&tbs, kExtensionsTag)) {
+ CBS wrapper;
+ if (!CBS_get_asn1(&tbs, &wrapper, kExtensionsTag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return nullptr;
+ }
+ // TODO(crbug.com/42290219): Empty extension lists should be rejected. An
+ // empty extensions list is encoded by omitting the field altogether. libpki
+ // already rejects this.
+ const uint8_t *p = CBS_data(&wrapper);
+ ret->extensions = d2i_X509_EXTENSIONS(nullptr, &p, CBS_len(&wrapper));
+ if (ret->extensions == nullptr ||
+ p != CBS_data(&wrapper) + CBS_len(&wrapper)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return nullptr;
+ }
+ }
+ if (CBS_len(&tbs) != 0) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
return nullptr;
}
return ret.release();
}
+static bssl::UniquePtr<X509> x509_parse(CBS *cbs) {
+ CBS cert;
+ if (!CBS_get_asn1_element(cbs, &cert, CBS_ASN1_SEQUENCE)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return nullptr;
+ }
+
+ bssl::UniquePtr<CRYPTO_BUFFER> buf(CRYPTO_BUFFER_new_from_CBS(&cert, nullptr));
+ if (buf == nullptr) {
+ return nullptr;
+ }
+ return bssl::UniquePtr<X509>(X509_parse_from_buffer(buf.get()));
+}
+
+int x509_marshal_tbs_cert(CBB *cbb, X509 *x509) {
+ if (x509->buf != nullptr) {
+ // Replay the saved TBSCertificate from the |CRYPTO_BUFFER|, to verify
+ // exactly what we parsed. The |CRYPTO_BUFFER| contains the full
+ // Certificate, so we need to find the TBSCertificate portion.
+ CBS cbs, cert, tbs;
+ CRYPTO_BUFFER_init_CBS(x509->buf, &cbs);
+ if (!CBS_get_asn1(&cbs, &cert, CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1_element(&cert, &tbs, CBS_ASN1_SEQUENCE)) {
+ // This should be impossible.
+ OPENSSL_PUT_ERROR(X509, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ return CBB_add_bytes(cbb, CBS_data(&tbs), CBS_len(&tbs));
+ }
+
+ // No saved TBSCertificate encoding. Encode it anew.
+ CBB tbs, version, validity, extensions;
+ if (!CBB_add_asn1(cbb, &tbs, CBS_ASN1_SEQUENCE)) {
+ return 0;
+ }
+ if (x509->version != X509_VERSION_1) {
+ if (!CBB_add_asn1(&tbs, &version, kVersionTag) ||
+ !CBB_add_asn1_uint64(&version, x509->version)) {
+ return 0;
+ }
+ }
+ if (!asn1_marshal_integer(&tbs, &x509->serialNumber, /*tag=*/0) ||
+ !x509_marshal_algorithm(&tbs, &x509->tbs_sig_alg) ||
+ !x509_marshal_name(&tbs, x509->issuer) ||
+ !CBB_add_asn1(&tbs, &validity, CBS_ASN1_SEQUENCE) ||
+ !asn1_marshal_time(&validity, &x509->notBefore) ||
+ !asn1_marshal_time(&validity, &x509->notAfter) ||
+ !x509_marshal_name(&tbs, x509->subject) ||
+ !x509_marshal_public_key(&tbs, &x509->key) ||
+ (x509->issuerUID != nullptr &&
+ !asn1_marshal_bit_string(&tbs, x509->issuerUID, kIssuerUIDTag)) ||
+ (x509->subjectUID != nullptr &&
+ !asn1_marshal_bit_string(&tbs, x509->subjectUID, kSubjectUIDTag))) {
+ return 0;
+ }
+ if (x509->extensions != nullptr) {
+ int len = i2d_X509_EXTENSIONS(x509->extensions, nullptr);
+ uint8_t *out;
+ if (len <= 0 || //
+ !CBB_add_asn1(&tbs, &extensions, kExtensionsTag) ||
+ !CBB_add_space(&extensions, &out, len) ||
+ i2d_X509_EXTENSIONS(x509->extensions, &out) != len) {
+ return 0;
+ }
+ }
+ return CBB_flush(cbb);
+}
+
+static int x509_marshal(CBB *cbb, X509 *x509) {
+ CBB cert;
+ return CBB_add_asn1(cbb, &cert, CBS_ASN1_SEQUENCE) &&
+ x509_marshal_tbs_cert(&cert, x509) &&
+ x509_marshal_algorithm(&cert, &x509->sig_alg) &&
+ asn1_marshal_bit_string(&cert, &x509->signature, /*tag=*/0) &&
+ CBB_flush(cbb);
+}
+
X509 *d2i_X509(X509 **out, const uint8_t **inp, long len) {
- return bssl::D2IFromCBS(out, inp, len,
- [](CBS *cbs) { return x509_parse(cbs, nullptr); });
+ return bssl::D2IFromCBS(out, inp, len, x509_parse);
}
int i2d_X509(X509 *x509, uint8_t **outp) {
@@ -176,27 +326,8 @@
}
return bssl::I2DFromCBB(
- /*initial_capacity=*/64, outp, [&](CBB *cbb) -> bool {
- CBB cert;
- if (!CBB_add_asn1(cbb, &cert, CBS_ASN1_SEQUENCE)) {
- return false;
- }
-
- // TODO(crbug.com/boringssl/443): When the rest of the library is
- // decoupled from the tasn_*.c implementation, replace this with
- // |CBB|-based functions.
- uint8_t *out;
- int len = i2d_X509_CINF(x509->cert_info, NULL);
- if (len < 0 || //
- !CBB_add_space(&cert, &out, static_cast<size_t>(len)) ||
- i2d_X509_CINF(x509->cert_info, &out) != len ||
- !x509_marshal_algorithm(&cert, &x509->sig_alg) ||
- !asn1_marshal_bit_string(&cert, &x509->signature, /*tag=*/0) ||
- !CBB_flush(cbb)) {
- return false;
- }
- return true;
- });
+ /*initial_capacity=*/256, outp,
+ [&](CBB *cbb) -> bool { return x509_marshal(cbb, x509); });
}
static int x509_new_cb(ASN1_VALUE **pval, const ASN1_ITEM *it) {
@@ -215,13 +346,13 @@
return 1;
}
- X509 *ret = x509_parse(cbs, nullptr);
+ bssl::UniquePtr<X509> ret = x509_parse(cbs);
if (ret == nullptr) {
return 0;
}
X509_free((X509 *)*pval);
- *pval = (ASN1_VALUE *)ret;
+ *pval = (ASN1_VALUE *)ret.release();
return 1;
}
@@ -247,18 +378,6 @@
return ret;
}
-X509 *X509_parse_from_buffer(CRYPTO_BUFFER *buf) {
- CBS cbs;
- CBS_init(&cbs, CRYPTO_BUFFER_data(buf), CRYPTO_BUFFER_len(buf));
- X509 *ret = x509_parse(&cbs, buf);
- if (ret == NULL || CBS_len(&cbs) != 0) {
- X509_free(ret);
- return NULL;
- }
-
- return ret;
-}
-
int X509_up_ref(X509 *x) {
CRYPTO_refcount_inc(&x->references);
return 1;
@@ -383,17 +502,20 @@
}
int i2d_re_X509_tbs(X509 *x509, unsigned char **outp) {
- asn1_encoding_clear(&x509->cert_info->enc);
- return i2d_X509_CINF(x509->cert_info, outp);
+ CRYPTO_BUFFER_free(x509->buf);
+ x509->buf = nullptr;
+ return i2d_X509_tbs(x509, outp);
}
int i2d_X509_tbs(X509 *x509, unsigned char **outp) {
- return i2d_X509_CINF(x509->cert_info, outp);
+ return bssl::I2DFromCBB(/*initial_capacity=*/128, outp, [&](CBB *cbb) -> bool {
+ return x509_marshal_tbs_cert(cbb, x509);
+ });
}
int X509_set1_signature_algo(X509 *x509, const X509_ALGOR *algo) {
return X509_ALGOR_copy(&x509->sig_alg, algo) &&
- X509_ALGOR_copy(x509->cert_info->tbs_sig_alg, algo);
+ X509_ALGOR_copy(&x509->tbs_sig_alg, algo);
}
int X509_set1_signature_value(X509 *x509, const uint8_t *sig, size_t sig_len) {
diff --git a/gen/sources.bzl b/gen/sources.bzl
index 72d897b..0bfbe31 100644
--- a/gen/sources.bzl
+++ b/gen/sources.bzl
@@ -503,7 +503,6 @@
"crypto/x509/x_req.cc",
"crypto/x509/x_sig.cc",
"crypto/x509/x_spki.cc",
- "crypto/x509/x_val.cc",
"crypto/x509/x_x509.cc",
"crypto/x509/x_x509a.cc",
"crypto/xwing/xwing.cc",
diff --git a/gen/sources.cmake b/gen/sources.cmake
index be6608b..6f24612 100644
--- a/gen/sources.cmake
+++ b/gen/sources.cmake
@@ -517,7 +517,6 @@
crypto/x509/x_req.cc
crypto/x509/x_sig.cc
crypto/x509/x_spki.cc
- crypto/x509/x_val.cc
crypto/x509/x_x509.cc
crypto/x509/x_x509a.cc
crypto/xwing/xwing.cc
diff --git a/gen/sources.gni b/gen/sources.gni
index 83740e0..14dce85 100644
--- a/gen/sources.gni
+++ b/gen/sources.gni
@@ -503,7 +503,6 @@
"crypto/x509/x_req.cc",
"crypto/x509/x_sig.cc",
"crypto/x509/x_spki.cc",
- "crypto/x509/x_val.cc",
"crypto/x509/x_x509.cc",
"crypto/x509/x_x509a.cc",
"crypto/xwing/xwing.cc",
diff --git a/gen/sources.json b/gen/sources.json
index 6334c44..a3a0569 100644
--- a/gen/sources.json
+++ b/gen/sources.json
@@ -487,7 +487,6 @@
"crypto/x509/x_req.cc",
"crypto/x509/x_sig.cc",
"crypto/x509/x_spki.cc",
- "crypto/x509/x_val.cc",
"crypto/x509/x_x509.cc",
"crypto/x509/x_x509a.cc",
"crypto/xwing/xwing.cc",
diff --git a/gen/sources.mk b/gen/sources.mk
index 7de81ef..5c2c045 100644
--- a/gen/sources.mk
+++ b/gen/sources.mk
@@ -497,7 +497,6 @@
crypto/x509/x_req.cc \
crypto/x509/x_sig.cc \
crypto/x509/x_spki.cc \
- crypto/x509/x_val.cc \
crypto/x509/x_x509.cc \
crypto/x509/x_x509a.cc \
crypto/xwing/xwing.cc \