Give X509 an ASN1_ITEM again

I mistakenly thought no one needed X509 as an ASN1_ITEM, but that wasn't
true. wpa_supplicant relies on this. Restore this and add a test for it.

As with the rest of the rewrite, this is currently a little tedious. I'm
hoping that, as the internals are rewritten with CBS and CBB, we can
establish some cleaner patterns and abstractions.

Bug: 547
Change-Id: I761ee058f8ec916b2ec7f4730a764d46d72f1f10
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58285
Commit-Queue: Bob Beck <bbe@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
This commit is contained in:
David Benjamin 2023-03-22 14:20:57 -04:00 committed by Boringssl LUCI CQ
parent 8ebfea76db
commit b6a50fd62d
3 changed files with 144 additions and 0 deletions

View File

@ -28,8 +28,10 @@
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/obj.h>
#include <openssl/pem.h>
#include <openssl/span.h>
#include <openssl/time.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include "../test/test_util.h"
@ -2759,4 +2761,93 @@ TEST(ASN1Test, OptionalChoice) {
TestSerialize(obj.get(), i2d_OPTIONAL_CHOICE, kTrue);
}
struct EMBED_X509 {
X509 *x509;
X509 *x509_opt;
STACK_OF(X509) *x509_seq;
};
DECLARE_ASN1_FUNCTIONS(EMBED_X509)
ASN1_SEQUENCE(EMBED_X509) = {
ASN1_SIMPLE(EMBED_X509, x509, X509),
ASN1_EXP_OPT(EMBED_X509, x509_opt, X509, 0),
ASN1_IMP_SEQUENCE_OF_OPT(EMBED_X509, x509_seq, X509, 1),
} ASN1_SEQUENCE_END(EMBED_X509)
IMPLEMENT_ASN1_FUNCTIONS(EMBED_X509)
// Test that X.509 types defined in this library can be embedded into other
// types, as we rewrite them away from the templating system.
TEST(ASN1Test, EmbedX509) {
// Set up a test certificate.
static const char kTestCert[] = R"(
-----BEGIN CERTIFICATE-----
MIIBzzCCAXagAwIBAgIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC
QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp
dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ
BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l
dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni
v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa
HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw
HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ
BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E
BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=
-----END CERTIFICATE-----
)";
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kTestCert, sizeof(kTestCert)));
ASSERT_TRUE(bio);
bssl::UniquePtr<X509> cert(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
ASSERT_TRUE(cert);
uint8_t *cert_der = nullptr;
int cert_len = i2d_X509(cert.get(), &cert_der);
ASSERT_GT(cert_len, 0);
bssl::UniquePtr<uint8_t> free_cert_der(cert_der);
std::unique_ptr<EMBED_X509, decltype(&EMBED_X509_free)> obj(nullptr,
EMBED_X509_free);
// Test only the first field present.
bssl::ScopedCBB cbb;
ASSERT_TRUE(CBB_init(cbb.get(), 64));
CBB seq;
ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&seq, cert_der, cert_len));
ASSERT_TRUE(CBB_flush(cbb.get()));
const uint8_t *ptr = CBB_data(cbb.get());
obj.reset(d2i_EMBED_X509(nullptr, &ptr, CBB_len(cbb.get())));
ASSERT_TRUE(obj);
ASSERT_TRUE(obj->x509);
EXPECT_EQ(X509_cmp(obj->x509, cert.get()), 0);
EXPECT_FALSE(obj->x509_opt);
EXPECT_FALSE(obj->x509_seq);
TestSerialize(obj.get(), i2d_EMBED_X509,
{CBB_data(cbb.get()), CBB_len(cbb.get())});
// Test all fields present.
cbb.Reset();
ASSERT_TRUE(CBB_init(cbb.get(), 64));
ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
ASSERT_TRUE(CBB_add_bytes(&seq, cert_der, cert_len));
CBB child;
ASSERT_TRUE(CBB_add_asn1(
&seq, &child, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0));
ASSERT_TRUE(CBB_add_bytes(&child, cert_der, cert_len));
ASSERT_TRUE(CBB_add_asn1(
&seq, &child, CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1));
ASSERT_TRUE(CBB_add_bytes(&child, cert_der, cert_len));
ASSERT_TRUE(CBB_add_bytes(&child, cert_der, cert_len));
ASSERT_TRUE(CBB_flush(cbb.get()));
ptr = CBB_data(cbb.get());
obj.reset(d2i_EMBED_X509(nullptr, &ptr, CBB_len(cbb.get())));
ASSERT_TRUE(obj);
ASSERT_TRUE(obj->x509);
EXPECT_EQ(X509_cmp(obj->x509, cert.get()), 0);
ASSERT_TRUE(obj->x509_opt);
EXPECT_EQ(X509_cmp(obj->x509_opt, cert.get()), 0);
ASSERT_EQ(sk_X509_num(obj->x509_seq), 2u);
EXPECT_EQ(X509_cmp(sk_X509_value(obj->x509_seq, 0), cert.get()), 0);
EXPECT_EQ(X509_cmp(sk_X509_value(obj->x509_seq, 1), cert.get()), 0);
TestSerialize(obj.get(), i2d_EMBED_X509,
{CBB_data(cbb.get()), CBB_len(cbb.get())});
}
#endif // !WINDOWS || !SHARED_LIBRARY

View File

@ -301,6 +301,55 @@ err:
return -1;
}
static int x509_new_cb(ASN1_VALUE **pval, const ASN1_ITEM *it) {
*pval = (ASN1_VALUE *)X509_new();
return *pval != NULL;
}
static void x509_free_cb(ASN1_VALUE **pval, const ASN1_ITEM *it) {
X509_free((X509 *)*pval);
*pval = NULL;
}
static int x509_d2i_cb(ASN1_VALUE **pval, const unsigned char **in, long len,
const ASN1_ITEM *it, int opt, ASN1_TLC *ctx) {
if (len < 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BUFFER_TOO_SMALL);
return 0;
}
CBS cbs;
CBS_init(&cbs, *in, len);
if (opt && !CBS_peek_asn1_tag(&cbs, CBS_ASN1_SEQUENCE)) {
return -1;
}
X509 *ret = x509_parse(&cbs, NULL);
if (ret == NULL) {
return 0;
}
*in = CBS_data(&cbs);
X509_free((X509 *)*pval);
*pval = (ASN1_VALUE *)ret;
return 1;
}
static int x509_i2d_cb(ASN1_VALUE **pval, unsigned char **out,
const ASN1_ITEM *it) {
return i2d_X509((X509 *)*pval, out);
}
static const ASN1_EXTERN_FUNCS x509_extern_funcs = {
x509_new_cb,
x509_free_cb,
/*asn1_ex_clear=*/NULL,
x509_d2i_cb,
x509_i2d_cb,
};
IMPLEMENT_EXTERN_ASN1(X509, V_ASN1_SEQUENCE, x509_extern_funcs)
X509 *X509_dup(X509 *x509) {
uint8_t *der = NULL;
int len = i2d_X509(x509, &der);

View File

@ -118,6 +118,10 @@ extern "C" {
DEFINE_STACK_OF(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)
// X509_up_ref adds one to the reference count of |x509| and returns one.
OPENSSL_EXPORT int X509_up_ref(X509 *x509);