Botan 3.12.0
Crypto and TLS for C&
x509_crl.cpp
Go to the documentation of this file.
1/*
2* X.509 CRL
3* (C) 1999-2007 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/x509_crl.h>
9
10#include <botan/asn1_obj.h>
11#include <botan/asn1_time.h>
12#include <botan/ber_dec.h>
13#include <botan/data_src.h>
14#include <botan/x509_ext.h>
15#include <botan/x509cert.h>
16#include <algorithm>
17#include <set>
18
19namespace Botan {
20
21class CRL_Data final {
22 public:
23 CRL_Data(const X509_DN& issuer,
24 const X509_Time& this_update,
25 const X509_Time& next_update,
26 const std::vector<CRL_Entry>& revoked) :
27 m_issuer(issuer), m_this_update(this_update), m_next_update(next_update), m_entries(revoked) {
28 this->update_index();
29 }
30
31 CRL_Data() = default;
32
33 void update_index() {
34 m_revoked_serials.clear();
35 for(const auto& entry : m_entries) {
36 if(entry.reason_code() == CRL_Code::RemoveFromCrl) {
37 m_revoked_serials.erase(entry.serial_number());
38 } else {
39 m_revoked_serials.insert(entry.serial_number());
40 }
41 }
42 }
43
44 // NOLINTBEGIN(*non-private-member-variables-in-classes)
45 X509_DN m_issuer;
46 size_t m_version{};
47 X509_Time m_this_update;
48 X509_Time m_next_update;
49 std::vector<CRL_Entry> m_entries;
50 Extensions m_extensions;
51
52 // cached values from entries
53 std::set<std::vector<uint8_t>> m_revoked_serials;
54
55 // cached values from extensions
56 size_t m_crl_number = 0;
57 std::vector<uint8_t> m_auth_key_id;
58 std::vector<std::string> m_idp_urls;
59 // NOLINTEND(*non-private-member-variables-in-classes)
60};
61
62std::string X509_CRL::PEM_label() const {
63 return "X509 CRL";
64}
65
66std::vector<std::string> X509_CRL::alternate_PEM_labels() const {
67 return {"CRL"};
68}
69
73
74X509_CRL::X509_CRL(const std::vector<uint8_t>& vec) {
75 DataSource_Memory src(vec.data(), vec.size());
76 load_data(src);
77}
78
79#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
80X509_CRL::X509_CRL(std::string_view fsname) {
81 DataSource_Stream src(fsname, true);
82 load_data(src);
83}
84#endif
85
89 const std::vector<CRL_Entry>& revoked) {
90 m_data = std::make_shared<CRL_Data>(issuer, this_update, next_update, revoked);
91}
92
93/**
94* Check if this particular certificate is listed in the CRL
95*/
96bool X509_CRL::is_revoked(const X509_Certificate& cert) const {
97 /*
98 If the cert wasn't issued by the CRL issuer, it's possible the cert
99 is revoked, but not by this CRL. Maybe throw an exception instead?
100 */
101 if(cert.issuer_dn() != issuer_dn()) {
102 return false;
103 }
104
105 const std::vector<uint8_t> crl_akid = authority_key_id();
106 const std::vector<uint8_t>& cert_akid = cert.authority_key_id();
107
108 if(!crl_akid.empty() && !cert_akid.empty()) {
109 if(crl_akid != cert_akid) {
110 return false;
111 }
112 }
113
114 return data().m_revoked_serials.contains(cert.serial_number());
115}
116
117/*
118* Decode the TBSCertList data
119*/
120namespace {
121
122std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body, const AlgorithmIdentifier& sig_algo) {
123 auto data = std::make_unique<CRL_Data>();
124
125 BER_Decoder tbs_crl(body, BER_Decoder::Limits::DER());
126
127 tbs_crl.decode_optional(data->m_version, ASN1_Type::Integer, ASN1_Class::Universal);
128 data->m_version += 1; // wire-format is 0-based
129
130 if(data->m_version != 1 && data->m_version != 2) {
131 throw Decoding_Error("Unknown X.509 CRL version " + std::to_string(data->m_version));
132 }
133
134 AlgorithmIdentifier sig_algo_inner;
135 tbs_crl.decode(sig_algo_inner);
136
137 if(sig_algo != sig_algo_inner) {
138 throw Decoding_Error("Algorithm identifier mismatch in CRL");
139 }
140
141 tbs_crl.decode(data->m_issuer).decode(data->m_this_update);
142
143 // According to RFC 5280 Section 5.1, nextUpdate is OPTIONAL and may be
144 // encoded as either a UTCTime or a GeneralizedTime. Section 5.1.2.5
145 // further states that "[c]onforming CRL issuers MUST include the nextUpdate
146 // field in all CRLs". Obviously, not everyone complies...
147 //
148 // See https://github.com/randombit/botan/issues/4722 for more details.
149 {
150 const auto& next_update = tbs_crl.peek_next_object();
151 if(next_update.is_a(ASN1_Type::UtcTime, ASN1_Class::Universal) ||
153 tbs_crl.decode(data->m_next_update);
154 }
155 }
156
157 BER_Object next = tbs_crl.get_next_object();
158
160 BER_Decoder cert_list(next, tbs_crl.limits());
161
162 while(cert_list.more_items()) {
163 CRL_Entry entry;
164 cert_list.decode(entry);
165 data->m_entries.push_back(entry);
166 }
167 next = tbs_crl.get_next_object();
168 }
169
171 BER_Decoder crl_options(next, tbs_crl.limits());
172 crl_options.decode(data->m_extensions).verify_end();
173 next = tbs_crl.get_next_object();
174 }
175
176 if(next.is_set()) {
177 throw Decoding_Error("Unknown tag following extensions in CRL");
178 }
179
180 tbs_crl.verify_end();
181
182 // Now cache some fields from the extensions
183 if(const auto* ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Number>()) {
184 data->m_crl_number = ext->get_crl_number();
185 }
186 if(const auto* ext = data->m_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>()) {
187 data->m_auth_key_id = ext->get_key_id();
188 }
189 if(const auto* ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Issuing_Distribution_Point>()) {
190 for(const auto& uri : ext->get_point().uris()) {
191 data->m_idp_urls.push_back(uri);
192 }
193 }
194
195 data->update_index();
196
197 return data;
198}
199
200} // namespace
201
202void X509_CRL::force_decode() {
203 m_data.reset(decode_crl_body(signed_body(), signature_algorithm()).release());
204}
205
206const CRL_Data& X509_CRL::data() const {
207 if(!m_data) {
208 throw Invalid_State("X509_CRL uninitialized");
209 }
210 return *m_data;
211}
212
214 return data().m_extensions;
215}
216
217/*
218* Return the list of revoked certificates
219*/
220const std::vector<CRL_Entry>& X509_CRL::get_revoked() const {
221 return data().m_entries;
222}
223
224uint32_t X509_CRL::x509_version() const {
225 return static_cast<uint32_t>(data().m_version);
226}
227
228/*
229* Return the distinguished name of the issuer
230*/
232 return data().m_issuer;
233}
234
235/*
236* Return the key identifier of the issuer
237*/
238const std::vector<uint8_t>& X509_CRL::authority_key_id() const {
239 return data().m_auth_key_id;
240}
241
242/*
243* Return the CRL number of this CRL
244*/
245uint32_t X509_CRL::crl_number() const {
246 return static_cast<uint32_t>(data().m_crl_number);
247}
248
249/*
250* Return the issue data of the CRL
251*/
253 return data().m_this_update;
254}
255
256/*
257* Return the date when a new CRL will be issued
258*/
260 return data().m_next_update;
261}
262
263/*
264* Return the CRL's distribution point
265*/
267 if(!data().m_idp_urls.empty()) {
268 return data().m_idp_urls[0];
269 }
270 return "";
271}
272
273/*
274* Return the CRL's issuing distribution point
275*/
276std::vector<std::string> X509_CRL::issuing_distribution_points() const {
277 return data().m_idp_urls;
278}
279
280namespace {
281
282/*
283* Compare two distribution point names for overlap, per RFC 5280 section 6.3.3
284* step (b)(2). In practice CRLDP/IDP general names are either uniformResourceIdentifier
285* or directoryName; the other GeneralName variants have no defined semantics for a
286* distribution point (RFC 5280 4.2.1.13 and 5.2.5) so they are ignored here.
287*/
288bool dp_names_overlap(const AlternativeName& a, const AlternativeName& b) {
289 auto has_common = [](const auto& s1, const auto& s2) {
290 return std::ranges::any_of(s1, [&](const auto& e) { return s2.contains(e); });
291 };
292
293 return has_common(a.uris(), b.uris()) || has_common(a.directory_names(), b.directory_names());
294}
295
296} // namespace
297
300 if(cdp_ext == nullptr || cdp_ext->distribution_points().empty()) {
301 return true;
302 }
303
305 if(idp_ext == nullptr) {
306 return false;
307 }
308
309 return std::ranges::any_of(cdp_ext->distribution_points(),
310 [&](const auto& dp) { return dp_names_overlap(dp.point(), idp_ext->get_point()); });
311}
312
313} // namespace Botan
const std::set< X509_DN > & directory_names() const
Return the set of directory names included in this alternative name.
Definition pkix_types.h:198
const std::set< std::string > & uris() const
Return the set of URIs included in this alternative name.
Definition pkix_types.h:177
static Limits DER()
Definition ber_dec.h:35
Definition x509_crl.h:29
const T * get_extension_object_as(const OID &oid=T::static_oid()) const
Definition pkix_types.h:551
const std::vector< CRL_Entry > & get_revoked() const
Definition x509_crl.cpp:220
const std::vector< uint8_t > & authority_key_id() const
Definition x509_crl.cpp:238
const X509_Time & this_update() const
Definition x509_crl.cpp:252
std::vector< std::string > issuing_distribution_points() const
Definition x509_crl.cpp:276
X509_CRL()=default
const Extensions & extensions() const
Definition x509_crl.cpp:213
uint32_t crl_number() const
Definition x509_crl.cpp:245
const X509_Time & next_update() const
Definition x509_crl.cpp:259
const X509_DN & issuer_dn() const
Definition x509_crl.cpp:231
bool has_matching_distribution_point(const X509_Certificate &cert) const
Definition x509_crl.cpp:298
bool is_revoked(const X509_Certificate &cert) const
Definition x509_crl.cpp:96
std::string crl_issuing_distribution_point() const
Definition x509_crl.cpp:266
uint32_t x509_version() const
Definition x509_crl.cpp:224
const std::vector< uint8_t > & serial_number() const
Definition x509cert.cpp:406
const std::vector< uint8_t > & authority_key_id() const
Definition x509cert.cpp:398
const Extensions & v3_extensions() const
Definition x509cert.cpp:477
const X509_DN & issuer_dn() const
Definition x509cert.cpp:414
const std::vector< uint8_t > & signed_body() const
Definition x509_obj.cpp:66
const AlgorithmIdentifier & signature_algorithm() const
Definition x509_obj.cpp:73
virtual std::vector< std::string > alternate_PEM_labels() const
Definition x509_obj.h:102
void load_data(DataSource &src)
Definition x509_obj.cpp:24
virtual std::string PEM_label() const =0
ASN1_Time X509_Time
Definition asn1_obj.h:23