xref: /aosp_15_r20/external/cronet/third_party/boringssl/src/pki/crl_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "crl.h"
6 
7 #include <string_view>
8 
9 #include <gtest/gtest.h>
10 #include <openssl/pool.h>
11 #include "cert_errors.h"
12 #include "parsed_certificate.h"
13 #include "string_util.h"
14 #include "test_helpers.h"
15 
16 namespace bssl {
17 
18 namespace {
19 
20 constexpr int64_t kAgeOneWeek = 7 * 24 * 60 * 60;
21 
GetFilePath(std::string_view file_name)22 std::string GetFilePath(std::string_view file_name) {
23   return std::string("testdata/crl_unittest/") + std::string(file_name);
24 }
25 
ParseCertificate(std::string_view data)26 std::shared_ptr<const ParsedCertificate> ParseCertificate(
27     std::string_view data) {
28   CertErrors errors;
29   return ParsedCertificate::Create(
30       bssl::UniquePtr<CRYPTO_BUFFER>(
31           CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t *>(data.data()),
32                             data.size(), nullptr)),
33       {}, &errors);
34 }
35 
36 class CheckCRLTest : public ::testing::TestWithParam<const char *> {};
37 
38 // Test prefix naming scheme:
39 //   good = valid CRL, cert affirmatively not revoked
40 //   revoked = valid CRL, cert affirmatively revoked
41 //   bad = valid CRL, but cert status is unknown (cases like unhandled features,
42 //           mismatching issuer or signature, etc)
43 //   invalid = corrupt or violates some spec requirement
44 constexpr char const *kTestParams[] = {
45     "good.pem",
46     "good_issuer_name_normalization.pem",
47     "good_issuer_no_keyusage.pem",
48     "good_no_nextupdate.pem",
49     "good_fake_extension.pem",
50     "good_fake_extension_no_nextupdate.pem",
51     "good_generalizedtime.pem",
52     "good_no_version.pem",
53     "good_no_crldp.pem",
54     "good_key_rollover.pem",
55     "good_idp_contains_uri.pem",
56     "good_idp_onlycontainsusercerts.pem",
57     "good_idp_onlycontainsusercerts_no_basic_constraints.pem",
58     "good_idp_onlycontainscacerts.pem",
59     "good_idp_uri_and_onlycontainsusercerts.pem",
60     "good_idp_uri_and_onlycontainscacerts.pem",
61     "revoked.pem",
62     "revoked_no_nextupdate.pem",
63     "revoked_fake_crlentryextension.pem",
64     "revoked_generalized_revocationdate.pem",
65     "revoked_key_rollover.pem",
66     "bad_crldp_has_crlissuer.pem",
67     "bad_fake_critical_extension.pem",
68     "bad_fake_critical_crlentryextension.pem",
69     "bad_signature.pem",
70     "bad_thisupdate_in_future.pem",
71     "bad_thisupdate_too_old.pem",
72     "bad_nextupdate_too_old.pem",
73     "bad_wrong_issuer.pem",
74     "bad_key_rollover_signature.pem",
75     "bad_idp_contains_wrong_uri.pem",
76     "bad_idp_indirectcrl.pem",
77     "bad_idp_onlycontainsusercerts.pem",
78     "bad_idp_onlycontainscacerts.pem",
79     "bad_idp_onlycontainscacerts_no_basic_constraints.pem",
80     "bad_idp_uri_and_onlycontainsusercerts.pem",
81     "bad_idp_uri_and_onlycontainscacerts.pem",
82     "invalid_mismatched_signature_algorithm.pem",
83     "invalid_revoked_empty_sequence.pem",
84     "invalid_v1_with_extension.pem",
85     "invalid_v1_with_crlentryextension.pem",
86     "invalid_v1_explicit.pem",
87     "invalid_v3.pem",
88     "invalid_issuer_keyusage_no_crlsign.pem",
89     "invalid_key_rollover_issuer_keyusage_no_crlsign.pem",
90     "invalid_garbage_version.pem",
91     "invalid_garbage_tbs_signature_algorithm.pem",
92     "invalid_garbage_issuer_name.pem",
93     "invalid_garbage_thisupdate.pem",
94     "invalid_garbage_after_thisupdate.pem",
95     "invalid_garbage_after_nextupdate.pem",
96     "invalid_garbage_after_revokedcerts.pem",
97     "invalid_garbage_after_extensions.pem",
98     "invalid_garbage_tbscertlist.pem",
99     "invalid_garbage_signaturealgorithm.pem",
100     "invalid_garbage_signaturevalue.pem",
101     "invalid_garbage_after_signaturevalue.pem",
102     "invalid_garbage_revoked_serial_number.pem",
103     "invalid_garbage_revocationdate.pem",
104     "invalid_garbage_after_revocationdate.pem",
105     "invalid_garbage_after_crlentryextensions.pem",
106     "invalid_garbage_crlentry.pem",
107     "invalid_idp_dpname_choice_extra_data.pem",
108     "invalid_idp_empty_sequence.pem",
109     "invalid_idp_onlycontains_user_and_ca_certs.pem",
110     "invalid_idp_onlycontainsusercerts_v1_leaf.pem",
111 };
112 
113 struct PrintTestName {
operator ()bssl::__anon303221da0111::PrintTestName114   std::string operator()(
115       const testing::TestParamInfo<const char *> &info) const {
116     std::string_view name(info.param);
117     // Strip ".pem" from the end as GTest names cannot contain period.
118     name.remove_suffix(4);
119     return std::string(name);
120   }
121 };
122 
123 INSTANTIATE_TEST_SUITE_P(All, CheckCRLTest, ::testing::ValuesIn(kTestParams),
124                          PrintTestName());
125 
TEST_P(CheckCRLTest,FromFile)126 TEST_P(CheckCRLTest, FromFile) {
127   std::string_view file_name(GetParam());
128 
129   std::string crl_data;
130   std::string ca_data_2;
131   std::string ca_data;
132   std::string cert_data;
133   const PemBlockMapping mappings[] = {
134       {"CRL", &crl_data},
135       {"CA CERTIFICATE 2", &ca_data_2, /*optional=*/true},
136       {"CA CERTIFICATE", &ca_data},
137       {"CERTIFICATE", &cert_data},
138   };
139 
140   ASSERT_TRUE(ReadTestDataFromPemFile(GetFilePath(file_name), mappings));
141 
142   std::shared_ptr<const ParsedCertificate> cert = ParseCertificate(cert_data);
143   ASSERT_TRUE(cert);
144   std::shared_ptr<const ParsedCertificate> issuer_cert =
145       ParseCertificate(ca_data);
146   ASSERT_TRUE(issuer_cert);
147   ParsedCertificateList certs = {cert, issuer_cert};
148   if (!ca_data_2.empty()) {
149     std::shared_ptr<const ParsedCertificate> issuer_cert_2 =
150         ParseCertificate(ca_data_2);
151     ASSERT_TRUE(issuer_cert_2);
152     certs.push_back(issuer_cert_2);
153   }
154 
155   // Assumes that all the target certs in the test data certs have at most 1
156   // CRL distributionPoint. If the cert has a CRL distributionPoint, it is
157   // used for verifying the CRL, otherwise the CRL is verified with a
158   // synthesized distributionPoint. This is allowed since there are some
159   // conditions that require a V1 certificate to test, which cannot have a
160   // crlDistributionPoints extension.
161   // TODO(https://crbug.com/749276): This seems slightly hacky. Maybe the
162   // distribution point to use should be specified separately in the test PEM?
163   ParsedDistributionPoint fake_cert_dp;
164   ParsedDistributionPoint *cert_dp = &fake_cert_dp;
165   std::vector<ParsedDistributionPoint> distribution_points;
166   ParsedExtension crl_dp_extension;
167   if (cert->GetExtension(der::Input(kCrlDistributionPointsOid),
168                          &crl_dp_extension)) {
169     ASSERT_TRUE(ParseCrlDistributionPoints(crl_dp_extension.value,
170                                            &distribution_points));
171     ASSERT_LE(distribution_points.size(), 1U);
172     if (!distribution_points.empty()) {
173       cert_dp = &distribution_points[0];
174     }
175   }
176   ASSERT_TRUE(cert_dp);
177 
178   // Mar 9 00:00:00 2017 GMT
179   int64_t kVerifyTime = 1489017600;
180 
181   CRLRevocationStatus expected_revocation_status = CRLRevocationStatus::UNKNOWN;
182   if (string_util::StartsWith(file_name, "good")) {
183     expected_revocation_status = CRLRevocationStatus::GOOD;
184   } else if (string_util::StartsWith(file_name, "revoked")) {
185     expected_revocation_status = CRLRevocationStatus::REVOKED;
186   }
187 
188   CRLRevocationStatus revocation_status =
189       CheckCRL(crl_data, certs, /*target_cert_index=*/0, *cert_dp, kVerifyTime,
190                kAgeOneWeek);
191   EXPECT_EQ(expected_revocation_status, revocation_status);
192 
193   // Test with a random cert added to the front of the chain and
194   // |target_cert_index=1|. This is a hacky way to verify that
195   // target_cert_index is actually being honored.
196   ParsedCertificateList other_certs;
197   ASSERT_TRUE(ReadCertChainFromFile(
198       "testdata/parse_certificate_unittest/cert_version3.pem", &other_certs));
199   ASSERT_FALSE(other_certs.empty());
200   certs.insert(certs.begin(), other_certs[0]);
201   revocation_status = CheckCRL(crl_data, certs, /*target_cert_index=*/1,
202                                *cert_dp, kVerifyTime, kAgeOneWeek);
203   EXPECT_EQ(expected_revocation_status, revocation_status);
204 }
205 
206 }  // namespace
207 
208 }  // namespace bssl
209