Add a test for CRLDP parsing This, specifically the error-handling exposed by trailing data tests, exercises some interesting cases in tasn_dec.cc. Change-Id: Ic27c802737834add90ebd4e79ce401b5d40b6276 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/97167 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/x509/x509_extension_test.cc b/crypto/x509/x509_extension_test.cc index 07a6331..ea1299c 100644 --- a/crypto/x509/x509_extension_test.cc +++ b/crypto/x509/x509_extension_test.cc
@@ -19,8 +19,10 @@ #include <gtest/gtest.h> +#include <openssl/asn1.h> #include <openssl/obj.h> #include <openssl/span.h> +#include <openssl/x509v3.h> #include "../test/der_trailing_data.h" #include "../test/test_util.h" @@ -263,5 +265,124 @@ } } +TEST(X509ExtensionTest, ParseCRLDistributionPoints) { + // CRL distribution points are very complex. This input is a sequence of + // three CRL distribution points that try to exercise various cases. + static const uint8_t kInput[] = { + 0x30, 0x81, 0x87, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x10, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x2e, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x86, 0x10, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x62, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x30, 0x30, 0xa0, + 0x0f, 0xa1, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, + 0x54, 0x65, 0x73, 0x74, 0x81, 0x02, 0x05, 0x60, 0xa2, 0x19, 0xa4, 0x17, + 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x0a, 0x43, 0x52, 0x4c, 0x20, 0x49, 0x73, 0x73, 0x75, 0x65, 0x72, 0x30, + 0x29, 0xa2, 0x27, 0x86, 0x12, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x82, + 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x87, 0x04, 0x7f, 0x00, 0x00, 0x01}; + const uint8_t *inp = kInput; + UniquePtr<CRL_DIST_POINTS> crldp( + d2i_CRL_DIST_POINTS(nullptr, &inp, sizeof(kInput))); + ASSERT_TRUE(crldp); + EXPECT_EQ(inp, kInput + sizeof(kInput)); + + ASSERT_EQ(sk_DIST_POINT_num(crldp.get()), 3u); + + // A distribution point with two URIs. + const DIST_POINT *dp1 = sk_DIST_POINT_value(crldp.get(), 0); + ASSERT_NE(dp1->distpoint, nullptr); + ASSERT_EQ(dp1->distpoint->type, 0); // fullName + ASSERT_NE(dp1->distpoint->name.fullname, nullptr); + ASSERT_EQ(sk_GENERAL_NAME_num(dp1->distpoint->name.fullname), 2u); + + const GENERAL_NAME *gn1_1 = + sk_GENERAL_NAME_value(dp1->distpoint->name.fullname, 0); + ASSERT_EQ(gn1_1->type, GEN_URI); + EXPECT_EQ(ASN1StringAsView(gn1_1->d.uniformResourceIdentifier), + "http://a.example"); + + const GENERAL_NAME *gn1_2 = + sk_GENERAL_NAME_value(dp1->distpoint->name.fullname, 1); + ASSERT_EQ(gn1_2->type, GEN_URI); + EXPECT_EQ(ASN1StringAsView(gn1_2->d.uniformResourceIdentifier), + "http://b.example"); + + EXPECT_EQ(dp1->reasons, nullptr); + EXPECT_EQ(dp1->CRLissuer, nullptr); + + // A distribution point relative to the CRL issuer, some reason flags, and a + // CRL issuer + const DIST_POINT *dp2 = sk_DIST_POINT_value(crldp.get(), 1); + ASSERT_NE(dp2->distpoint, nullptr); + ASSERT_EQ(dp2->distpoint->type, 1); // relativename + ASSERT_NE(dp2->distpoint->name.relativename, nullptr); + ASSERT_EQ(sk_X509_NAME_ENTRY_num(dp2->distpoint->name.relativename), 1u); + + const X509_NAME_ENTRY *entry = + sk_X509_NAME_ENTRY_value(dp2->distpoint->name.relativename, 0); + EXPECT_EQ(OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)), NID_commonName); + EXPECT_EQ(ASN1StringAsView(X509_NAME_ENTRY_get_data(entry)), "Test"); + + ASSERT_NE(dp2->reasons, nullptr); + UniquePtr<ASN1_BIT_STRING> expected_reasons(ASN1_BIT_STRING_new()); + ASSERT_TRUE(expected_reasons); + ASSERT_TRUE(ASN1_BIT_STRING_set_bit(expected_reasons.get(), + CRL_REASON_KEY_COMPROMISE, 1)); + ASSERT_TRUE(ASN1_BIT_STRING_set_bit(expected_reasons.get(), + CRL_REASON_CA_COMPROMISE, 1)); + EXPECT_EQ(ASN1_STRING_cmp(dp2->reasons, expected_reasons.get()), 0); + + ASSERT_NE(dp2->CRLissuer, nullptr); + ASSERT_EQ(sk_GENERAL_NAME_num(dp2->CRLissuer), 1u); + const GENERAL_NAME *issuer_gn = sk_GENERAL_NAME_value(dp2->CRLissuer, 0); + ASSERT_EQ(issuer_gn->type, GEN_DIRNAME); + ASSERT_NE(issuer_gn->d.directoryName, nullptr); + ASSERT_EQ(X509_NAME_entry_count(issuer_gn->d.directoryName), 1); + const X509_NAME_ENTRY *issuer_entry = + X509_NAME_get_entry(issuer_gn->d.directoryName, 0); + EXPECT_EQ(OBJ_obj2nid(X509_NAME_ENTRY_get_object(issuer_entry)), + NID_commonName); + EXPECT_EQ(ASN1StringAsView(X509_NAME_ENTRY_get_data(issuer_entry)), + "CRL Issuer"); + + // Only a CRL issuer with various general names. + const DIST_POINT *dp3 = sk_DIST_POINT_value(crldp.get(), 2); + EXPECT_EQ(dp3->distpoint, nullptr); + EXPECT_EQ(dp3->reasons, nullptr); + ASSERT_NE(dp3->CRLissuer, nullptr); + ASSERT_EQ(sk_GENERAL_NAME_num(dp3->CRLissuer), 3u); + + const GENERAL_NAME *gn3_1 = sk_GENERAL_NAME_value(dp3->CRLissuer, 0); + ASSERT_EQ(gn3_1->type, GEN_URI); + EXPECT_EQ(ASN1StringAsView(gn3_1->d.uniformResourceIdentifier), + "http://example.com"); + + const GENERAL_NAME *gn3_2 = sk_GENERAL_NAME_value(dp3->CRLissuer, 1); + ASSERT_EQ(gn3_2->type, GEN_DNS); + EXPECT_EQ(ASN1StringAsView(gn3_2->d.dNSName), "example.com"); + + const GENERAL_NAME *gn3_3 = sk_GENERAL_NAME_value(dp3->CRLissuer, 2); + ASSERT_EQ(gn3_3->type, GEN_IPADD); + EXPECT_EQ(Bytes(ASN1StringAsBytes(gn3_3->d.iPAddress)), + Bytes(std::vector<uint8_t>{127, 0, 0, 1})); + + // The object should roundtrip to the original input. + uint8_t *der = nullptr; + int der_len = i2d_CRL_DIST_POINTS(crldp.get(), &der); + EXPECT_GT(der_len, 0); + UniquePtr<uint8_t> free_der(der); + EXPECT_EQ(Bytes(der, der_len), Bytes(kInput)); + + // Trailing data should be rejected. + TestDERTrailingData(kInput, [](Span<const uint8_t> rewritten, size_t n) { + SCOPED_TRACE(n); + SCOPED_TRACE(EncodeHex(rewritten)); + const uint8_t *p = rewritten.data(); + EXPECT_FALSE(UniquePtr<CRL_DIST_POINTS>( + d2i_CRL_DIST_POINTS(nullptr, &p, rewritten.size()))); + }); +} + } // namespace BSSL_NAMESPACE_END