blob: b9be70fc0b100455f7ecaecdc860622e4e1481e5 [file] [log] [blame]
Bob Beckfb260742023-07-24 14:29:34 -07001// 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
Bob Beck5c7a2a02023-11-20 17:28:21 -07009#include <gtest/gtest.h>
10#include <openssl/pool.h>
Bob Beckfb260742023-07-24 14:29:34 -070011#include "cert_errors.h"
12#include "parsed_certificate.h"
13#include "string_util.h"
14#include "test_helpers.h"
Bob Beckfb260742023-07-24 14:29:34 -070015
16namespace bssl {
17
18namespace {
19
20constexpr int64_t kAgeOneWeek = 7 * 24 * 60 * 60;
21
22std::string GetFilePath(std::string_view file_name) {
23 return std::string("testdata/crl_unittest/") + std::string(file_name);
24}
25
26std::shared_ptr<const ParsedCertificate> ParseCertificate(
27 std::string_view data) {
28 CertErrors errors;
29 return ParsedCertificate::Create(
Bob Beck5c7a2a02023-11-20 17:28:21 -070030 bssl::UniquePtr<CRYPTO_BUFFER>(
31 CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t *>(data.data()),
32 data.size(), nullptr)),
Bob Beckfb260742023-07-24 14:29:34 -070033 {}, &errors);
34}
35
Bob Beck5c7a2a02023-11-20 17:28:21 -070036class CheckCRLTest : public ::testing::TestWithParam<const char *> {};
Bob Beckfb260742023-07-24 14:29:34 -070037
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
Bob Beck5c7a2a02023-11-20 17:28:21 -070044constexpr char const *kTestParams[] = {
Bob Beckfb260742023-07-24 14:29:34 -070045 "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
113struct PrintTestName {
114 std::string operator()(
Bob Beck5c7a2a02023-11-20 17:28:21 -0700115 const testing::TestParamInfo<const char *> &info) const {
Bob Beckfb260742023-07-24 14:29:34 -0700116 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
Bob Beck5c7a2a02023-11-20 17:28:21 -0700123INSTANTIATE_TEST_SUITE_P(All, CheckCRLTest, ::testing::ValuesIn(kTestParams),
Bob Beckfb260742023-07-24 14:29:34 -0700124 PrintTestName());
125
126TEST_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;
Bob Beck5c7a2a02023-11-20 17:28:21 -0700164 ParsedDistributionPoint *cert_dp = &fake_cert_dp;
Bob Beckfb260742023-07-24 14:29:34 -0700165 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
Bob Beck5c7a2a02023-11-20 17:28:21 -0700208} // namespace bssl