Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/x509/x509_crl.cpp
Line
Count
Source (jump to first uncovered line)
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/ber_dec.h>
11
#include <botan/x509_ext.h>
12
#include <botan/x509cert.h>
13
14
#include <sstream>
15
16
namespace Botan {
17
18
struct CRL_Data {
19
      X509_DN m_issuer;
20
      size_t m_version;
21
      X509_Time m_this_update;
22
      X509_Time m_next_update;
23
      std::vector<CRL_Entry> m_entries;
24
      Extensions m_extensions;
25
26
      // cached values from extensions
27
      size_t m_crl_number = 0;
28
      std::vector<uint8_t> m_auth_key_id;
29
      std::vector<std::string> m_idp_urls;
30
};
31
32
4.67k
std::string X509_CRL::PEM_label() const {
33
4.67k
   return "X509 CRL";
34
4.67k
}
35
36
146
std::vector<std::string> X509_CRL::alternate_PEM_labels() const {
37
146
   return {"CRL"};
38
146
}
39
40
5.12k
X509_CRL::X509_CRL(DataSource& src) {
41
5.12k
   load_data(src);
42
5.12k
}
43
44
0
X509_CRL::X509_CRL(const std::vector<uint8_t>& vec) {
45
0
   DataSource_Memory src(vec.data(), vec.size());
46
0
   load_data(src);
47
0
}
48
49
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
50
0
X509_CRL::X509_CRL(std::string_view fsname) {
51
0
   DataSource_Stream src(fsname, true);
52
0
   load_data(src);
53
0
}
54
#endif
55
56
X509_CRL::X509_CRL(const X509_DN& issuer,
57
                   const X509_Time& this_update,
58
                   const X509_Time& next_update,
59
                   const std::vector<CRL_Entry>& revoked) :
60
0
      X509_Object() {
61
0
   m_data = std::make_shared<CRL_Data>();
62
0
   m_data->m_issuer = issuer;
63
0
   m_data->m_this_update = this_update;
64
0
   m_data->m_next_update = next_update;
65
0
   m_data->m_entries = revoked;
66
0
}
67
68
/**
69
* Check if this particular certificate is listed in the CRL
70
*/
71
0
bool X509_CRL::is_revoked(const X509_Certificate& cert) const {
72
   /*
73
   If the cert wasn't issued by the CRL issuer, it's possible the cert
74
   is revoked, but not by this CRL. Maybe throw an exception instead?
75
   */
76
0
   if(cert.issuer_dn() != issuer_dn()) {
77
0
      return false;
78
0
   }
79
80
0
   std::vector<uint8_t> crl_akid = authority_key_id();
81
0
   const std::vector<uint8_t>& cert_akid = cert.authority_key_id();
82
83
0
   if(!crl_akid.empty() && !cert_akid.empty()) {
84
0
      if(crl_akid != cert_akid) {
85
0
         return false;
86
0
      }
87
0
   }
88
89
0
   const std::vector<uint8_t>& cert_serial = cert.serial_number();
90
91
0
   bool is_revoked = false;
92
93
   // FIXME would be nice to avoid a linear scan here - maybe sort the entries?
94
0
   for(const CRL_Entry& entry : get_revoked()) {
95
0
      if(cert_serial == entry.serial_number()) {
96
0
         if(entry.reason_code() == CRL_Code::RemoveFromCrl) {
97
0
            is_revoked = false;
98
0
         } else {
99
0
            is_revoked = true;
100
0
         }
101
0
      }
102
0
   }
103
104
0
   return is_revoked;
105
0
}
106
107
/*
108
* Decode the TBSCertList data
109
*/
110
namespace {
111
112
3.75k
std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body, const AlgorithmIdentifier& sig_algo) {
113
3.75k
   auto data = std::make_unique<CRL_Data>();
114
115
3.75k
   BER_Decoder tbs_crl(body);
116
117
3.75k
   tbs_crl.decode_optional(data->m_version, ASN1_Type::Integer, ASN1_Class::Universal);
118
3.75k
   data->m_version += 1;  // wire-format is 0-based
119
120
3.75k
   if(data->m_version != 1 && data->m_version != 2) {
121
50
      throw Decoding_Error("Unknown X.509 CRL version " + std::to_string(data->m_version));
122
50
   }
123
124
3.70k
   AlgorithmIdentifier sig_algo_inner;
125
3.70k
   tbs_crl.decode(sig_algo_inner);
126
127
3.70k
   if(sig_algo != sig_algo_inner) {
128
245
      throw Decoding_Error("Algorithm identifier mismatch in CRL");
129
245
   }
130
131
3.46k
   tbs_crl.decode(data->m_issuer).decode(data->m_this_update);
132
133
   // According to RFC 5280 Section 5.1, nextUpdate is OPTIONAL and may be
134
   // encoded as either a UTCTime or a GeneralizedTime. Section 5.1.2.5
135
   // further states that "[c]onforming CRL issuers MUST include the nextUpdate
136
   // field in all CRLs". Obviously, not everyone complies...
137
   //
138
   // See https://github.com/randombit/botan/issues/4722 for more details.
139
3.46k
   tbs_crl.decode_optional(data->m_next_update, ASN1_Type::UtcTime, ASN1_Class::Universal);
140
3.46k
   if(!data->m_next_update.time_is_set()) {
141
1.14k
      tbs_crl.decode_optional(data->m_next_update, ASN1_Type::GeneralizedTime, ASN1_Class::Universal);
142
1.14k
   }
143
144
3.46k
   BER_Object next = tbs_crl.get_next_object();
145
146
3.46k
   if(next.is_a(ASN1_Type::Sequence, ASN1_Class::Constructed)) {
147
329
      BER_Decoder cert_list(std::move(next));
148
149
5.84k
      while(cert_list.more_items()) {
150
5.51k
         CRL_Entry entry;
151
5.51k
         cert_list.decode(entry);
152
5.51k
         data->m_entries.push_back(entry);
153
5.51k
      }
154
329
      next = tbs_crl.get_next_object();
155
329
   }
156
157
3.46k
   if(next.is_a(0, ASN1_Class::Constructed | ASN1_Class::ContextSpecific)) {
158
2.15k
      BER_Decoder crl_options(std::move(next));
159
2.15k
      crl_options.decode(data->m_extensions).verify_end();
160
2.15k
      next = tbs_crl.get_next_object();
161
2.15k
   }
162
163
3.46k
   if(next.is_set()) {
164
446
      throw Decoding_Error("Unknown tag following extensions in CRL");
165
446
   }
166
167
3.01k
   tbs_crl.verify_end();
168
169
   // Now cache some fields from the extensions
170
3.01k
   if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Number>()) {
171
6
      data->m_crl_number = ext->get_crl_number();
172
6
   }
173
3.01k
   if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::Authority_Key_ID>()) {
174
2
      data->m_auth_key_id = ext->get_key_id();
175
2
   }
176
3.01k
   if(auto ext = data->m_extensions.get_extension_object_as<Cert_Extension::CRL_Issuing_Distribution_Point>()) {
177
3
      data->m_idp_urls = ext->get_point().get_attribute("URL");
178
3
   }
179
180
3.01k
   return data;
181
3.46k
}
182
183
}  // namespace
184
185
3.75k
void X509_CRL::force_decode() {
186
3.75k
   m_data.reset(decode_crl_body(signed_body(), signature_algorithm()).release());
187
3.75k
}
188
189
0
const CRL_Data& X509_CRL::data() const {
190
0
   if(!m_data) {
191
0
      throw Invalid_State("X509_CRL uninitialized");
192
0
   }
193
0
   return *m_data;
194
0
}
195
196
0
const Extensions& X509_CRL::extensions() const {
197
0
   return data().m_extensions;
198
0
}
199
200
/*
201
* Return the list of revoked certificates
202
*/
203
0
const std::vector<CRL_Entry>& X509_CRL::get_revoked() const {
204
0
   return data().m_entries;
205
0
}
206
207
0
uint32_t X509_CRL::x509_version() const {
208
0
   return static_cast<uint32_t>(data().m_version);
209
0
}
210
211
/*
212
* Return the distinguished name of the issuer
213
*/
214
0
const X509_DN& X509_CRL::issuer_dn() const {
215
0
   return data().m_issuer;
216
0
}
217
218
/*
219
* Return the key identifier of the issuer
220
*/
221
0
const std::vector<uint8_t>& X509_CRL::authority_key_id() const {
222
0
   return data().m_auth_key_id;
223
0
}
224
225
/*
226
* Return the CRL number of this CRL
227
*/
228
0
uint32_t X509_CRL::crl_number() const {
229
0
   return static_cast<uint32_t>(data().m_crl_number);
230
0
}
231
232
/*
233
* Return the issue data of the CRL
234
*/
235
0
const X509_Time& X509_CRL::this_update() const {
236
0
   return data().m_this_update;
237
0
}
238
239
/*
240
* Return the date when a new CRL will be issued
241
*/
242
0
const X509_Time& X509_CRL::next_update() const {
243
0
   return data().m_next_update;
244
0
}
245
246
/*
247
* Return the CRL's distribution point
248
*/
249
0
std::string X509_CRL::crl_issuing_distribution_point() const {
250
0
   if(!data().m_idp_urls.empty()) {
251
0
      return data().m_idp_urls[0];
252
0
   }
253
0
   return "";
254
0
}
255
256
/*
257
* Return the CRL's issuing distribution point
258
*/
259
0
std::vector<std::string> X509_CRL::issuing_distribution_points() const {
260
0
   return data().m_idp_urls;
261
0
}
262
263
}  // namespace Botan