/src/botan/src/lib/x509/x509_ext.cpp
Line | Count | Source |
1 | | /* |
2 | | * X.509 Certificate Extensions |
3 | | * (C) 1999-2010,2012 Jack Lloyd |
4 | | * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity |
5 | | * (C) 2017 Fabian Weissberg, Rohde & Schwarz Cybersecurity |
6 | | * (C) 2024 Anton Einax, Dominik Schricker |
7 | | * |
8 | | * Botan is released under the Simplified BSD License (see license.txt) |
9 | | */ |
10 | | |
11 | | #include <botan/x509_ext.h> |
12 | | |
13 | | #include <botan/assert.h> |
14 | | #include <botan/ber_dec.h> |
15 | | #include <botan/der_enc.h> |
16 | | #include <botan/hash.h> |
17 | | #include <botan/pk_keys.h> |
18 | | #include <botan/x509cert.h> |
19 | | #include <botan/internal/fmt.h> |
20 | | #include <botan/internal/int_utils.h> |
21 | | #include <botan/internal/loadstor.h> |
22 | | #include <botan/internal/x509_utils.h> |
23 | | #include <algorithm> |
24 | | #include <set> |
25 | | #include <span> |
26 | | |
27 | | namespace Botan { |
28 | | |
29 | | namespace { |
30 | | |
31 | | constexpr size_t MaximumKeyIdentifierLength = 64; |
32 | | |
33 | | /* |
34 | | * Encode an AlternativeName as `GeneralNames` but with an outer IMPLICIT |
35 | | * context-specific tag rather than the universal SEQUENCE tag. Used for |
36 | | * fullName [0] / cRLIssuer [2] / similar. |
37 | | */ |
38 | 0 | void emit_general_names_implicit(DER_Encoder& der, const AlternativeName& names, uint32_t tag) { |
39 | | // RFC 5280 4.2.1.6: GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName |
40 | 0 | if(!names.has_items()) { |
41 | 0 | throw Encoding_Error("Cannot encode empty GeneralNames"); |
42 | 0 | } |
43 | 0 | if(std::ranges::any_of(names.directory_names(), [](const X509_DN& dn) { return dn.empty(); })) { |
44 | 0 | throw Encoding_Error("GeneralNames must not contain an empty directoryName"); |
45 | 0 | } |
46 | 0 | der.encode_implicit(names, ASN1_Type(tag)); |
47 | 0 | } |
48 | | |
49 | | template <std::derived_from<Certificate_Extension> T> |
50 | 57.1k | auto make_extension([[maybe_unused]] const OID& oid) { |
51 | 57.1k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); |
52 | 57.1k | return std::make_unique<T>(); |
53 | 57.1k | } x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension14Subject_Key_IDEEEDaRKNS_3OIDE Line | Count | Source | 50 | 8.80k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 8.80k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 8.80k | return std::make_unique<T>(); | 53 | 8.80k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension9Key_UsageEEEDaRKNS_3OIDE Line | Count | Source | 50 | 3.85k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 3.85k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 3.85k | return std::make_unique<T>(); | 53 | 3.85k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension24Subject_Alternative_NameEEEDaRKNS_3OIDE Line | Count | Source | 50 | 8.22k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 8.22k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 8.22k | return std::make_unique<T>(); | 53 | 8.22k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension23Issuer_Alternative_NameEEEDaRKNS_3OIDE Line | Count | Source | 50 | 2.71k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 2.71k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 2.71k | return std::make_unique<T>(); | 53 | 2.71k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension17Basic_ConstraintsEEEDaRKNS_3OIDE Line | Count | Source | 50 | 10.2k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 10.2k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 10.2k | return std::make_unique<T>(); | 53 | 10.2k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension10CRL_NumberEEEDaRKNS_3OIDE Line | Count | Source | 50 | 51 | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 51 | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 51 | return std::make_unique<T>(); | 53 | 51 | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension14CRL_ReasonCodeEEEDaRKNS_3OIDE Line | Count | Source | 50 | 3.91k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 3.91k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 3.91k | return std::make_unique<T>(); | 53 | 3.91k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension30CRL_Issuing_Distribution_PointEEEDaRKNS_3OIDE Line | Count | Source | 50 | 188 | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 188 | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 188 | return std::make_unique<T>(); | 53 | 188 | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension16Name_ConstraintsEEEDaRKNS_3OIDE Line | Count | Source | 50 | 690 | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 690 | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 690 | return std::make_unique<T>(); | 53 | 690 | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension23CRL_Distribution_PointsEEEDaRKNS_3OIDE Line | Count | Source | 50 | 1.55k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 1.55k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 1.55k | return std::make_unique<T>(); | 53 | 1.55k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension20Certificate_PoliciesEEEDaRKNS_3OIDE Line | Count | Source | 50 | 846 | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 846 | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 846 | return std::make_unique<T>(); | 53 | 846 | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension16Authority_Key_IDEEEDaRKNS_3OIDE Line | Count | Source | 50 | 8.96k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 8.96k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 8.96k | return std::make_unique<T>(); | 53 | 8.96k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension18Extended_Key_UsageEEEDaRKNS_3OIDE Line | Count | Source | 50 | 1.76k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 1.76k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 1.76k | return std::make_unique<T>(); | 53 | 1.76k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension21NoRevocationAvailableEEEDaRKNS_3OIDE Line | Count | Source | 50 | 57 | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 57 | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 57 | return std::make_unique<T>(); | 53 | 57 | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension28Authority_Information_AccessEEEDaRKNS_3OIDE Line | Count | Source | 50 | 1.75k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 1.75k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 1.75k | return std::make_unique<T>(); | 53 | 1.75k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension15IPAddressBlocksEEEDaRKNS_3OIDE Line | Count | Source | 50 | 1.28k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 1.28k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 1.28k | return std::make_unique<T>(); | 53 | 1.28k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension8ASBlocksEEEDaRKNS_3OIDE Line | Count | Source | 50 | 2.03k | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 2.03k | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 2.03k | return std::make_unique<T>(); | 53 | 2.03k | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension10TNAuthListEEEDaRKNS_3OIDE Line | Count | Source | 50 | 218 | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 218 | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 218 | return std::make_unique<T>(); | 53 | 218 | } |
x509_ext.cpp:_ZN5Botan12_GLOBAL__N_114make_extensionITkNSt3__112derived_fromINS_21Certificate_ExtensionEEENS_14Cert_Extension12OCSP_NoCheckEEEDaRKNS_3OIDE Line | Count | Source | 50 | 6 | auto make_extension([[maybe_unused]] const OID& oid) { | 51 | 6 | BOTAN_DEBUG_ASSERT(oid == T::static_oid()); | 52 | 6 | return std::make_unique<T>(); | 53 | 6 | } |
|
54 | | |
55 | 82.8k | std::unique_ptr<Certificate_Extension> extension_from_oid(const OID& oid) { |
56 | 82.8k | if(auto iso_ext = is_sub_element_of(oid, {2, 5, 29})) { |
57 | | // NOLINTNEXTLINE(*-switch-missing-default-case) |
58 | 61.4k | switch(*iso_ext) { |
59 | 8.80k | case 14: |
60 | 8.80k | return make_extension<Cert_Extension::Subject_Key_ID>(oid); |
61 | 3.85k | case 15: |
62 | 3.85k | return make_extension<Cert_Extension::Key_Usage>(oid); |
63 | 8.22k | case 17: |
64 | 8.22k | return make_extension<Cert_Extension::Subject_Alternative_Name>(oid); |
65 | 2.71k | case 18: |
66 | 2.71k | return make_extension<Cert_Extension::Issuer_Alternative_Name>(oid); |
67 | 10.2k | case 19: |
68 | 10.2k | return make_extension<Cert_Extension::Basic_Constraints>(oid); |
69 | 51 | case 20: |
70 | 51 | return make_extension<Cert_Extension::CRL_Number>(oid); |
71 | 3.91k | case 21: |
72 | 3.91k | return make_extension<Cert_Extension::CRL_ReasonCode>(oid); |
73 | 188 | case 28: |
74 | 188 | return make_extension<Cert_Extension::CRL_Issuing_Distribution_Point>(oid); |
75 | 690 | case 30: |
76 | 690 | return make_extension<Cert_Extension::Name_Constraints>(oid); |
77 | 1.55k | case 31: |
78 | 1.55k | return make_extension<Cert_Extension::CRL_Distribution_Points>(oid); |
79 | 846 | case 32: |
80 | 846 | return make_extension<Cert_Extension::Certificate_Policies>(oid); |
81 | 8.96k | case 35: |
82 | 8.96k | return make_extension<Cert_Extension::Authority_Key_ID>(oid); |
83 | 1.76k | case 37: |
84 | 1.76k | return make_extension<Cert_Extension::Extended_Key_Usage>(oid); |
85 | 57 | case 56: |
86 | 57 | return make_extension<Cert_Extension::NoRevocationAvailable>(oid); |
87 | 61.4k | } |
88 | 61.4k | } |
89 | | |
90 | 30.9k | if(auto pkix_ext = is_sub_element_of(oid, {1, 3, 6, 1, 5, 5, 7, 1})) { |
91 | | // NOLINTNEXTLINE(*-switch-missing-default-case) |
92 | 5.62k | switch(*pkix_ext) { |
93 | 1.75k | case 1: |
94 | 1.75k | return make_extension<Cert_Extension::Authority_Information_Access>(oid); |
95 | 1.28k | case 7: |
96 | 1.28k | return make_extension<Cert_Extension::IPAddressBlocks>(oid); |
97 | 2.03k | case 8: |
98 | 2.03k | return make_extension<Cert_Extension::ASBlocks>(oid); |
99 | 218 | case 26: |
100 | 218 | return make_extension<Cert_Extension::TNAuthList>(oid); |
101 | 5.62k | } |
102 | 5.62k | } |
103 | | |
104 | 25.6k | if(oid == Cert_Extension::OCSP_NoCheck::static_oid()) { |
105 | 6 | return make_extension<Cert_Extension::OCSP_NoCheck>(oid); |
106 | 6 | } |
107 | | |
108 | 25.6k | return nullptr; // unknown |
109 | 25.6k | } |
110 | | |
111 | 118 | bool is_valid_telephone_number(const ASN1_String& tn) { |
112 | | //TelephoneNumber ::= IA5String (SIZE (1..15)) (FROM ("0123456789#*")) |
113 | 118 | const std::string valid_tn_chars("0123456789#*"); |
114 | | |
115 | 118 | if(tn.empty() || (tn.size() > 15)) { |
116 | 23 | return false; |
117 | 23 | } |
118 | | |
119 | 95 | if(tn.value().find_first_not_of(valid_tn_chars) != std::string::npos) { |
120 | 18 | return false; |
121 | 18 | } |
122 | | |
123 | 77 | return true; |
124 | 95 | } |
125 | | |
126 | | } // namespace |
127 | | |
128 | 0 | std::vector<OID> Extensions::critical_extensions() const { |
129 | 0 | std::vector<OID> crit; |
130 | |
|
131 | 0 | for(const auto& oid : m_extension_oids) { |
132 | 0 | auto ext_info = m_extension_info.find(oid); |
133 | 0 | BOTAN_ASSERT_NOMSG(ext_info != m_extension_info.end()); |
134 | 0 | if(ext_info->second.is_critical()) { |
135 | 0 | crit.push_back(oid); |
136 | 0 | } |
137 | 0 | } |
138 | |
|
139 | 0 | return crit; |
140 | 0 | } |
141 | | |
142 | | /* |
143 | | * Create a Certificate_Extension object of some kind to handle |
144 | | */ |
145 | | std::unique_ptr<Certificate_Extension> Extensions::create_extn_obj(const OID& oid, |
146 | | bool critical, |
147 | | const std::vector<uint8_t>& body, |
148 | 82.8k | std::optional<Extension_Context> context) { |
149 | 82.8k | auto extn = extension_from_oid(oid); |
150 | | |
151 | 82.8k | if(!extn) { |
152 | | // some other unknown extension type |
153 | 25.6k | extn = std::make_unique<Cert_Extension::Unknown_Extension>(oid, critical); |
154 | 57.1k | } else { |
155 | 57.1k | if(context.has_value() && !extn->is_appropriate_context(*context)) { |
156 | 30 | throw Decoding_Error(fmt("Extension {} is not allowed in this context", extn->oid_name())); |
157 | 30 | } |
158 | | |
159 | 57.1k | try { |
160 | 57.1k | extn->decode_inner(body); |
161 | 57.1k | return extn; |
162 | 57.1k | } catch(const Exception&) { |
163 | | // OID was recognized but contents failed to decode |
164 | 20.8k | extn = std::make_unique<Cert_Extension::Unknown_Extension>(oid, critical, /*failed_to_decode=*/true); |
165 | 20.8k | } |
166 | 57.1k | } |
167 | | |
168 | | // This is always Unknown_Extension: |
169 | 46.5k | extn->decode_inner(body); |
170 | 46.5k | return extn; |
171 | 82.8k | } |
172 | | |
173 | 43.4k | const Certificate_Extension& Extensions::Extensions_Info::obj() const { |
174 | 43.4k | BOTAN_ASSERT_NONNULL(m_obj.get()); |
175 | 43.4k | return *m_obj; |
176 | 43.4k | } |
177 | | |
178 | | /* |
179 | | * Validate the extension (the default implementation is a NOP) |
180 | | */ |
181 | | void Certificate_Extension::validate(const X509_Certificate& /*unused*/, |
182 | | const std::optional<X509_Certificate>& /*unused*/, |
183 | | const std::vector<X509_Certificate>& /*unused*/, |
184 | | std::vector<std::set<Certificate_Status_Code>>& /*unused*/, |
185 | 7 | size_t /*unused*/) const {} |
186 | | |
187 | | /* |
188 | | * Add a new cert |
189 | | */ |
190 | 0 | void Extensions::add(std::unique_ptr<Certificate_Extension> extn, bool critical) { |
191 | | // sanity check: we don't want to have the same extension more than once |
192 | 0 | if(m_extension_info.contains(extn->oid_of())) { |
193 | 0 | const std::string name = extn->oid_name(); |
194 | 0 | throw Invalid_Argument("Extension " + name + " already present in Extensions::add"); |
195 | 0 | } |
196 | | |
197 | 0 | const OID oid = extn->oid_of(); |
198 | 0 | Extensions_Info info(critical, std::move(extn)); |
199 | 0 | m_extension_oids.push_back(oid); |
200 | 0 | m_extension_info.emplace(oid, info); |
201 | 0 | } |
202 | | |
203 | 0 | bool Extensions::add_new(std::unique_ptr<Certificate_Extension> extn, bool critical) { |
204 | 0 | if(m_extension_info.contains(extn->oid_of())) { |
205 | 0 | return false; // already exists |
206 | 0 | } |
207 | | |
208 | 0 | const OID oid = extn->oid_of(); |
209 | 0 | Extensions_Info info(critical, std::move(extn)); |
210 | 0 | m_extension_oids.push_back(oid); |
211 | 0 | m_extension_info.emplace(oid, info); |
212 | 0 | return true; |
213 | 0 | } |
214 | | |
215 | 0 | bool Extensions::remove(const OID& oid) { |
216 | 0 | const bool erased = m_extension_info.erase(oid) > 0; |
217 | |
|
218 | 0 | if(erased) { |
219 | 0 | m_extension_oids.erase(std::find(m_extension_oids.begin(), m_extension_oids.end(), oid)); |
220 | 0 | } |
221 | |
|
222 | 0 | return erased; |
223 | 0 | } |
224 | | |
225 | 0 | void Extensions::replace(std::unique_ptr<Certificate_Extension> extn, bool critical) { |
226 | | // Remove it if it existed |
227 | 0 | remove(extn->oid_of()); |
228 | |
|
229 | 0 | const OID oid = extn->oid_of(); |
230 | 0 | Extensions_Info info(critical, std::move(extn)); |
231 | 0 | m_extension_oids.push_back(oid); |
232 | 0 | m_extension_info.emplace(oid, info); |
233 | 0 | } |
234 | | |
235 | 54.3k | bool Extensions::extension_set(const OID& oid) const { |
236 | 54.3k | return m_extension_info.contains(oid); |
237 | 54.3k | } |
238 | | |
239 | 0 | bool Extensions::critical_extension_set(const OID& oid) const { |
240 | 0 | auto i = m_extension_info.find(oid); |
241 | 0 | if(i != m_extension_info.end()) { |
242 | 0 | return i->second.is_critical(); |
243 | 0 | } |
244 | 0 | return false; |
245 | 0 | } |
246 | | |
247 | 0 | std::vector<uint8_t> Extensions::get_extension_bits(const OID& oid) const { |
248 | 0 | auto i = m_extension_info.find(oid); |
249 | 0 | if(i == m_extension_info.end()) { |
250 | 0 | throw Invalid_Argument("Extensions::get_extension_bits no such extension set"); |
251 | 0 | } |
252 | | |
253 | 0 | return i->second.bits(); |
254 | 0 | } |
255 | | |
256 | 206k | const Certificate_Extension* Extensions::get_extension_object(const OID& oid) const { |
257 | 206k | auto extn = m_extension_info.find(oid); |
258 | 206k | if(extn == m_extension_info.end()) { |
259 | 163k | return nullptr; |
260 | 163k | } |
261 | | |
262 | 43.4k | return &extn->second.obj(); |
263 | 206k | } |
264 | | |
265 | 0 | std::unique_ptr<Certificate_Extension> Extensions::get(const OID& oid) const { |
266 | 0 | if(const Certificate_Extension* ext = this->get_extension_object(oid)) { |
267 | 0 | return ext->copy(); |
268 | 0 | } |
269 | 0 | return nullptr; |
270 | 0 | } |
271 | | |
272 | 0 | std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> Extensions::extensions() const { |
273 | 0 | std::vector<std::pair<std::unique_ptr<Certificate_Extension>, bool>> exts; |
274 | 0 | exts.reserve(m_extension_info.size()); |
275 | 0 | for(auto&& ext : m_extension_info) { |
276 | 0 | exts.push_back(std::make_pair(ext.second.obj().copy(), ext.second.is_critical())); |
277 | 0 | } |
278 | 0 | return exts; |
279 | 0 | } |
280 | | |
281 | | void Extensions::validate(const X509_Certificate& subject, |
282 | | const std::optional<X509_Certificate>& issuer, |
283 | | const std::vector<X509_Certificate>& cert_path, |
284 | | std::vector<std::set<Certificate_Status_Code>>& cert_status, |
285 | 3 | size_t pos) const { |
286 | 10 | for(const auto& ext : m_extension_info) { |
287 | 10 | ext.second.obj().validate(subject, issuer, cert_path, cert_status, pos); |
288 | 10 | } |
289 | 3 | } |
290 | | |
291 | 0 | std::map<OID, std::pair<std::vector<uint8_t>, bool>> Extensions::extensions_raw() const { |
292 | 0 | std::map<OID, std::pair<std::vector<uint8_t>, bool>> out; |
293 | 0 | for(auto&& ext : m_extension_info) { |
294 | 0 | out.emplace(ext.first, std::make_pair(ext.second.bits(), ext.second.is_critical())); |
295 | 0 | } |
296 | 0 | return out; |
297 | 0 | } |
298 | | |
299 | | /* |
300 | | * Encode an Extensions list |
301 | | */ |
302 | 0 | void Extensions::encode_into(DER_Encoder& to_object) const { |
303 | 0 | for(const auto& [oid, extn] : m_extension_info) { |
304 | 0 | const bool should_encode = extn.obj().should_encode(); |
305 | |
|
306 | 0 | if(should_encode) { |
307 | 0 | const auto is_critical = extn.is_critical() ? std::optional<bool>{true} : std::nullopt; |
308 | 0 | const std::vector<uint8_t>& ext_value = extn.bits(); |
309 | |
|
310 | 0 | to_object.start_sequence() |
311 | 0 | .encode(oid) |
312 | 0 | .encode_optional(is_critical) |
313 | 0 | .encode(ext_value, ASN1_Type::OctetString) |
314 | 0 | .end_cons(); |
315 | 0 | } |
316 | 0 | } |
317 | 0 | } |
318 | | |
319 | | /* |
320 | | * Decode a list of Extensions |
321 | | */ |
322 | 0 | void Extensions::decode_from(BER_Decoder& from_source) { |
323 | 0 | decode_from(from_source, std::nullopt); |
324 | 0 | } |
325 | | |
326 | 25.9k | void Extensions::decode_from(BER_Decoder& from_source, std::optional<Extension_Context> context) { |
327 | 25.9k | m_extension_oids.clear(); |
328 | 25.9k | m_extension_info.clear(); |
329 | 25.9k | m_has_unknown_critical_extension = false; |
330 | | |
331 | 25.9k | BER_Decoder sequence = from_source.start_sequence(); |
332 | | |
333 | 108k | while(sequence.more_items()) { |
334 | 85.0k | OID oid; |
335 | 85.0k | bool critical = false; |
336 | 85.0k | std::vector<uint8_t> bits; |
337 | | |
338 | 85.0k | sequence.start_sequence() |
339 | 85.0k | .decode(oid) |
340 | 85.0k | .decode_optional(critical, ASN1_Type::Boolean, ASN1_Class::Universal, false) |
341 | 85.0k | .decode(bits, ASN1_Type::OctetString) |
342 | 85.0k | .end_cons(); |
343 | | |
344 | 85.0k | auto obj = create_extn_obj(oid, critical, bits, context); |
345 | | // Unknown_Extension is the only Certificate_Extension with an empty oid_name |
346 | 85.0k | if(critical && obj->oid_name().empty()) { |
347 | 4.73k | m_has_unknown_critical_extension = true; |
348 | 4.73k | } |
349 | 85.0k | Extensions_Info info(critical, std::move(bits), std::move(obj)); |
350 | | |
351 | 85.0k | m_extension_oids.push_back(oid); |
352 | | |
353 | | // RFC 5280 4.2: "A certificate MUST NOT include more than one |
354 | | // instance of a particular extension." |
355 | 85.0k | if(!m_extension_info.emplace(std::move(oid), std::move(info)).second) { |
356 | 2.54k | throw Decoding_Error("Duplicate certificate extension encountered"); |
357 | 2.54k | } |
358 | 85.0k | } |
359 | 23.4k | sequence.verify_end(); |
360 | 23.4k | } |
361 | | |
362 | | namespace Cert_Extension { |
363 | | |
364 | 10.2k | bool Basic_Constraints::is_appropriate_context(Extension_Context context) const { |
365 | 10.2k | return context == Extension_Context::Certificate; |
366 | 10.2k | } |
367 | | |
368 | 3.85k | bool Key_Usage::is_appropriate_context(Extension_Context context) const { |
369 | 3.85k | return context == Extension_Context::Certificate; |
370 | 3.85k | } |
371 | | |
372 | 8.80k | bool Subject_Key_ID::is_appropriate_context(Extension_Context context) const { |
373 | 8.80k | return context == Extension_Context::Certificate; |
374 | 8.80k | } |
375 | | |
376 | 8.96k | bool Authority_Key_ID::is_appropriate_context(Extension_Context context) const { |
377 | 8.96k | return context == Extension_Context::Certificate || context == Extension_Context::CRL; |
378 | 8.96k | } |
379 | | |
380 | 8.22k | bool Subject_Alternative_Name::is_appropriate_context(Extension_Context context) const { |
381 | 8.22k | return context == Extension_Context::Certificate; |
382 | 8.22k | } |
383 | | |
384 | 2.71k | bool Issuer_Alternative_Name::is_appropriate_context(Extension_Context context) const { |
385 | 2.71k | return context == Extension_Context::Certificate || context == Extension_Context::CRL; |
386 | 2.71k | } |
387 | | |
388 | 1.76k | bool Extended_Key_Usage::is_appropriate_context(Extension_Context context) const { |
389 | 1.76k | return context == Extension_Context::Certificate; |
390 | 1.76k | } |
391 | | |
392 | 690 | bool Name_Constraints::is_appropriate_context(Extension_Context context) const { |
393 | 690 | return context == Extension_Context::Certificate; |
394 | 690 | } |
395 | | |
396 | 846 | bool Certificate_Policies::is_appropriate_context(Extension_Context context) const { |
397 | 846 | return context == Extension_Context::Certificate; |
398 | 846 | } |
399 | | |
400 | 1.75k | bool Authority_Information_Access::is_appropriate_context(Extension_Context context) const { |
401 | 1.75k | return context == Extension_Context::Certificate || context == Extension_Context::CRL; |
402 | 1.75k | } |
403 | | |
404 | 51 | bool CRL_Number::is_appropriate_context(Extension_Context context) const { |
405 | 51 | return context == Extension_Context::CRL; |
406 | 51 | } |
407 | | |
408 | 3.91k | bool CRL_ReasonCode::is_appropriate_context(Extension_Context context) const { |
409 | | // RFC 6960 4.4.5: "All the extensions specified as CRL entry extensions |
410 | | // -- in Section 5.3 of [RFC5280] -- are also supported as singleExtensions." |
411 | 3.91k | return context == Extension_Context::CRL_Entry || context == Extension_Context::OCSP_Response; |
412 | 3.91k | } |
413 | | |
414 | 1.55k | bool CRL_Distribution_Points::is_appropriate_context(Extension_Context context) const { |
415 | 1.55k | return context == Extension_Context::Certificate; |
416 | 1.55k | } |
417 | | |
418 | 188 | bool CRL_Issuing_Distribution_Point::is_appropriate_context(Extension_Context context) const { |
419 | 188 | return context == Extension_Context::CRL; |
420 | 188 | } |
421 | | |
422 | 6 | bool OCSP_NoCheck::is_appropriate_context(Extension_Context context) const { |
423 | 6 | return context == Extension_Context::Certificate; |
424 | 6 | } |
425 | | |
426 | 57 | bool NoRevocationAvailable::is_appropriate_context(Extension_Context context) const { |
427 | 57 | return context == Extension_Context::Certificate; |
428 | 57 | } |
429 | | |
430 | 218 | bool TNAuthList::is_appropriate_context(Extension_Context context) const { |
431 | 218 | return context == Extension_Context::Certificate; |
432 | 218 | } |
433 | | |
434 | 1.28k | bool IPAddressBlocks::is_appropriate_context(Extension_Context context) const { |
435 | 1.28k | return context == Extension_Context::Certificate; |
436 | 1.28k | } |
437 | | |
438 | 2.03k | bool ASBlocks::is_appropriate_context(Extension_Context context) const { |
439 | 2.03k | return context == Extension_Context::Certificate; |
440 | 2.03k | } |
441 | | |
442 | 0 | bool Unknown_Extension::is_appropriate_context(Extension_Context /*context*/) const { |
443 | 0 | return true; |
444 | 0 | } |
445 | | |
446 | | Basic_Constraints::Basic_Constraints(bool is_ca, size_t path_length_constraint) : |
447 | 10.2k | Basic_Constraints(is_ca, is_ca ? std::optional<size_t>(path_length_constraint) : std::nullopt) {} |
448 | | |
449 | | Basic_Constraints::Basic_Constraints(bool is_ca, std::optional<size_t> path_length_constraint) : |
450 | 10.2k | m_is_ca(is_ca), m_path_length_constraint(path_length_constraint) { |
451 | 10.2k | if(!m_is_ca && m_path_length_constraint.has_value()) { |
452 | | // RFC 5280 Sec 4.2.1.9 "CAs MUST NOT include the pathLenConstraint field unless the cA boolean is asserted" |
453 | 0 | throw Invalid_Argument( |
454 | 0 | "Basic_Constraints nonsensical to set a path length constraint for a non-CA basicConstraints"); |
455 | 0 | } |
456 | 10.2k | } |
457 | | |
458 | | /* |
459 | | * Checked accessor for the path_length_constraint member |
460 | | */ |
461 | 0 | size_t Basic_Constraints::get_path_limit() const { |
462 | 0 | if(m_is_ca) { |
463 | 0 | return m_path_length_constraint.value_or(NO_CERT_PATH_LIMIT); |
464 | 0 | } else { |
465 | 0 | throw Invalid_State("Basic_Constraints::get_path_limit: Not a CA"); |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | | /* |
470 | | * Encode the extension |
471 | | */ |
472 | 0 | std::vector<uint8_t> Basic_Constraints::encode_inner() const { |
473 | 0 | std::vector<uint8_t> output; |
474 | |
|
475 | 0 | if(m_is_ca) { |
476 | 0 | DER_Encoder(output).start_sequence().encode(m_is_ca).encode_optional(m_path_length_constraint).end_cons(); |
477 | 0 | } else { |
478 | 0 | DER_Encoder(output).start_sequence().end_cons(); |
479 | 0 | } |
480 | |
|
481 | 0 | return output; |
482 | 0 | } |
483 | | |
484 | | /* |
485 | | * Decode the extension |
486 | | */ |
487 | 10.2k | void Basic_Constraints::decode_inner(const std::vector<uint8_t>& in) { |
488 | | /* |
489 | | * RFC 5280 Section 4.2.1.9 |
490 | | * |
491 | | * BasicConstraints ::= SEQUENCE { |
492 | | * cA BOOLEAN DEFAULT FALSE, |
493 | | * pathLenConstraint INTEGER (0..MAX) OPTIONAL } |
494 | | */ |
495 | 10.2k | BER_Decoder(in, BER_Decoder::Limits::DER()) |
496 | 10.2k | .start_sequence() |
497 | 10.2k | .decode_optional(m_is_ca, ASN1_Type::Boolean, ASN1_Class::Universal, false) |
498 | 10.2k | .decode_optional(m_path_length_constraint, ASN1_Type::Integer, ASN1_Class::Universal) |
499 | 10.2k | .end_cons() |
500 | 10.2k | .verify_end(); |
501 | | |
502 | | /* RFC 5280 Section 4.2.1.9: |
503 | | * "CAs MUST NOT include the pathLenConstraint field unless the cA boolean |
504 | | * is asserted and the key usage extension asserts the keyCertSign bit" */ |
505 | 10.2k | if(!m_is_ca && m_path_length_constraint.has_value()) { |
506 | 56 | throw Decoding_Error("BasicConstraints pathLenConstraint must not be present when cA is FALSE"); |
507 | 56 | } |
508 | 10.2k | } |
509 | | |
510 | | /* |
511 | | * Encode the extension |
512 | | */ |
513 | 0 | std::vector<uint8_t> Key_Usage::encode_inner() const { |
514 | 0 | if(m_constraints.empty()) { |
515 | 0 | throw Encoding_Error("Cannot encode empty PKIX key constraints"); |
516 | 0 | } |
517 | | |
518 | 0 | std::vector<uint8_t> der; |
519 | 0 | DER_Encoder(der).encode_named_bitstring(m_constraints.value(), 16); |
520 | 0 | return der; |
521 | 0 | } |
522 | | |
523 | | /* |
524 | | * Decode the extension |
525 | | */ |
526 | 3.85k | void Key_Usage::decode_inner(const std::vector<uint8_t>& in) { |
527 | | /* RFC 5280 Section 4.2.1.3 - KeyUsage ::= BIT STRING */ |
528 | 3.85k | uint64_t usage = 0; |
529 | 3.85k | BER_Decoder(in, BER_Decoder::Limits::DER()) |
530 | 3.85k | .decode_named_bitstring(usage, 16, ASN1_Type::BitString, ASN1_Class::Universal) |
531 | 3.85k | .verify_end(); |
532 | | |
533 | | /* RFC 5280 Section 4.2.1.3: |
534 | | * "When the keyUsage extension appears in a certificate, at least one of |
535 | | * the bits MUST be set to 1." */ |
536 | 3.85k | if(usage == 0) { |
537 | 0 | throw Decoding_Error("KeyUsage extension must have at least one bit set"); |
538 | 0 | } |
539 | | |
540 | 3.85k | m_constraints = Key_Constraints(static_cast<uint32_t>(usage)); |
541 | 3.85k | } |
542 | | |
543 | | /* |
544 | | * Encode the extension |
545 | | */ |
546 | 0 | std::vector<uint8_t> Subject_Key_ID::encode_inner() const { |
547 | 0 | std::vector<uint8_t> output; |
548 | 0 | DER_Encoder(output).encode(m_key_id, ASN1_Type::OctetString); |
549 | 0 | return output; |
550 | 0 | } |
551 | | |
552 | | /* |
553 | | * Decode the extension |
554 | | */ |
555 | 8.80k | void Subject_Key_ID::decode_inner(const std::vector<uint8_t>& in) { |
556 | | /* RFC 5280 Section 4.2.1.2 - SubjectKeyIdentifier ::= KeyIdentifier */ |
557 | 8.80k | BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_key_id, ASN1_Type::OctetString).verify_end(); |
558 | | |
559 | 8.80k | if(m_key_id.empty()) { |
560 | 3 | throw Decoding_Error("SubjectKeyIdentifier must not be empty"); |
561 | 3 | } |
562 | 8.79k | if(m_key_id.size() > MaximumKeyIdentifierLength) { |
563 | 0 | throw Decoding_Error( |
564 | 0 | fmt("SubjectKeyIdentifier length {} exceeds limit of {} bytes", m_key_id.size(), MaximumKeyIdentifierLength)); |
565 | 0 | } |
566 | 8.79k | } |
567 | | |
568 | | /* |
569 | | * Subject_Key_ID Constructor |
570 | | */ |
571 | 0 | Subject_Key_ID::Subject_Key_ID(const Public_Key& pub_key) { |
572 | | /* |
573 | | * RFC 5280 4.2.1.2: |
574 | | * (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the |
575 | | * value of the BIT STRING subjectPublicKey (excluding the tag, length, |
576 | | * and number of unused bits). |
577 | | */ |
578 | 0 | auto hash = HashFunction::create_or_throw("SHA-1"); |
579 | |
|
580 | 0 | m_key_id.resize(hash->output_length()); |
581 | |
|
582 | 0 | hash->update(pub_key.public_key_bits()); |
583 | 0 | hash->final(m_key_id.data()); |
584 | 0 | } |
585 | | |
586 | | /* |
587 | | * Subject_Key_ID Constructor |
588 | | */ |
589 | 0 | Subject_Key_ID::Subject_Key_ID(const std::vector<uint8_t>& pub_key, std::string_view hash_name) { |
590 | 0 | auto hash = HashFunction::create_or_throw(hash_name); |
591 | |
|
592 | 0 | m_key_id.resize(hash->output_length()); |
593 | |
|
594 | 0 | hash->update(pub_key); |
595 | 0 | hash->final(m_key_id.data()); |
596 | | |
597 | | // Truncate longer hashes, 192 bits here seems plenty |
598 | 0 | const size_t max_skid_len = (192 / 8); |
599 | 0 | if(m_key_id.size() > max_skid_len) { |
600 | 0 | m_key_id.resize(max_skid_len); |
601 | 0 | } |
602 | 0 | } |
603 | | |
604 | | /* |
605 | | * Encode the extension |
606 | | */ |
607 | 0 | std::vector<uint8_t> Authority_Key_ID::encode_inner() const { |
608 | 0 | std::vector<uint8_t> output; |
609 | 0 | DER_Encoder der(output); |
610 | 0 | der.start_sequence(); |
611 | 0 | if(!m_key_id.empty()) { |
612 | 0 | der.encode(m_key_id, ASN1_Type::OctetString, ASN1_Type(0), ASN1_Class::ContextSpecific); |
613 | 0 | } |
614 | 0 | if(m_authority_cert.has_value()) { |
615 | 0 | emit_general_names_implicit(der, m_authority_cert->issuer, 1); |
616 | 0 | der.encode(m_authority_cert->serial_number.to_bigint(), ASN1_Type(2), ASN1_Class::ContextSpecific); |
617 | 0 | } |
618 | 0 | der.end_cons(); |
619 | 0 | return output; |
620 | 0 | } |
621 | | |
622 | | /* |
623 | | * Decode the extension |
624 | | */ |
625 | 8.95k | void Authority_Key_ID::decode_inner(const std::vector<uint8_t>& in) { |
626 | | /* |
627 | | * RFC 5280 Section 4.2.1.1 |
628 | | * |
629 | | * AuthorityKeyIdentifier ::= SEQUENCE { |
630 | | * keyIdentifier [0] KeyIdentifier OPTIONAL, |
631 | | * authorityCertIssuer [1] GeneralNames OPTIONAL, |
632 | | * authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } |
633 | | */ |
634 | 8.95k | BER_Decoder ber(in, BER_Decoder::Limits::DER()); |
635 | 8.95k | BER_Decoder seq = ber.start_sequence(); |
636 | | |
637 | 8.95k | m_key_id.clear(); |
638 | 8.95k | m_authority_cert.reset(); |
639 | | |
640 | 8.95k | bool key_id_present = false; |
641 | 8.95k | std::optional<AlternativeName> authority_cert_issuer; |
642 | 8.95k | std::optional<X509_Serial_Number> authority_cert_serial; |
643 | | |
644 | 8.95k | seq.decode_optional_field(0, |
645 | 8.95k | ASN1_Class::ContextSpecific, |
646 | 8.95k | [&](BER_Decoder& d) { |
647 | 7.30k | d.decode(m_key_id, ASN1_Type::OctetString, ASN1_Type(0), ASN1_Class::ContextSpecific); |
648 | 7.30k | key_id_present = true; |
649 | 7.30k | }) |
650 | 8.95k | .decode_optional_field(1, |
651 | 8.95k | ASN1_Class::ContextSpecific | ASN1_Class::Constructed, |
652 | 8.95k | [&](BER_Decoder& d) { |
653 | 233 | AlternativeName names; |
654 | 233 | d.decode_implicit(names, |
655 | 233 | ASN1_Type(1), |
656 | 233 | ASN1_Class::ContextSpecific | ASN1_Class::Constructed, |
657 | 233 | ASN1_Type::Sequence, |
658 | 233 | ASN1_Class::Constructed); |
659 | 233 | authority_cert_issuer = std::move(names); |
660 | 233 | }) |
661 | 8.95k | .decode_optional_field(2, ASN1_Class::ContextSpecific, [&](BER_Decoder& d) { |
662 | 126 | X509_Serial_Number serial; |
663 | 126 | d.decode_implicit( |
664 | 126 | serial, ASN1_Type(2), ASN1_Class::ContextSpecific, ASN1_Type::Integer, ASN1_Class::Universal); |
665 | 126 | authority_cert_serial = std::move(serial); |
666 | 126 | }); |
667 | | |
668 | 8.95k | seq.end_cons(); |
669 | 8.95k | ber.verify_end(); |
670 | | |
671 | 8.95k | if(key_id_present) { |
672 | 7.05k | if(m_key_id.empty()) { |
673 | 0 | throw Decoding_Error("AuthorityKeyIdentifier keyIdentifier must not be empty"); |
674 | 0 | } |
675 | 7.05k | if(m_key_id.size() > MaximumKeyIdentifierLength) { |
676 | 6 | throw Decoding_Error(fmt("AuthorityKeyIdentifier keyIdentifier length {} exceeds limit of {} bytes", |
677 | 6 | m_key_id.size(), |
678 | 6 | MaximumKeyIdentifierLength)); |
679 | 6 | } |
680 | 7.05k | } |
681 | | |
682 | | // RFC 5280 4.2.1.6: GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName |
683 | 8.95k | if(authority_cert_issuer.has_value() && authority_cert_issuer->is_empty()) { |
684 | 5 | throw Decoding_Error("AuthorityKeyIdentifier authorityCertIssuer must contain at least one GeneralName"); |
685 | 5 | } |
686 | | |
687 | | /* |
688 | | * RFC 5280 Appendix A.2: |
689 | | * |
690 | | * authorityCertIssuer and authorityCertSerialNumber MUST both be |
691 | | * present or both be absent |
692 | | */ |
693 | 8.94k | if(authority_cert_issuer.has_value() != authority_cert_serial.has_value()) { |
694 | 32 | throw Decoding_Error( |
695 | 32 | "AuthorityKeyIdentifier authorityCertIssuer and authorityCertSerialNumber must both be present or absent"); |
696 | 32 | } |
697 | | |
698 | 8.91k | if(authority_cert_issuer.has_value()) { |
699 | 52 | m_authority_cert = |
700 | 52 | Authority_Cert_Identifier{std::move(*authority_cert_issuer), std::move(*authority_cert_serial)}; |
701 | 52 | } |
702 | 8.91k | } |
703 | | |
704 | | /* |
705 | | * Encode the extension |
706 | | */ |
707 | 0 | std::vector<uint8_t> Subject_Alternative_Name::encode_inner() const { |
708 | 0 | std::vector<uint8_t> output; |
709 | 0 | DER_Encoder(output).encode(m_alt_name); |
710 | 0 | return output; |
711 | 0 | } |
712 | | |
713 | | /* |
714 | | * Encode the extension |
715 | | */ |
716 | 0 | std::vector<uint8_t> Issuer_Alternative_Name::encode_inner() const { |
717 | 0 | std::vector<uint8_t> output; |
718 | 0 | DER_Encoder(output).encode(m_alt_name); |
719 | 0 | return output; |
720 | 0 | } |
721 | | |
722 | | /* |
723 | | * Decode the extension |
724 | | */ |
725 | 8.21k | void Subject_Alternative_Name::decode_inner(const std::vector<uint8_t>& in) { |
726 | | /* RFC 5280 Section 4.2.1.6 - SubjectAltName ::= GeneralNames |
727 | | * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName */ |
728 | 8.21k | BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_alt_name).verify_end(); |
729 | 8.21k | if(!m_alt_name.has_items()) { |
730 | 95 | throw Decoding_Error("SubjectAlternativeName extension must contain at least one GeneralName"); |
731 | 95 | } |
732 | 8.21k | } |
733 | | |
734 | | /* |
735 | | * Decode the extension |
736 | | */ |
737 | 2.71k | void Issuer_Alternative_Name::decode_inner(const std::vector<uint8_t>& in) { |
738 | | /* RFC 5280 Section 4.2.1.7 - IssuerAltName ::= GeneralNames |
739 | | * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName */ |
740 | 2.71k | BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_alt_name).verify_end(); |
741 | 2.71k | if(!m_alt_name.has_items()) { |
742 | 73 | throw Decoding_Error("IssuerAlternativeName extension must contain at least one GeneralName"); |
743 | 73 | } |
744 | 2.71k | } |
745 | | |
746 | | /* |
747 | | * Encode the extension |
748 | | */ |
749 | 0 | std::vector<uint8_t> Extended_Key_Usage::encode_inner() const { |
750 | 0 | std::vector<uint8_t> output; |
751 | 0 | DER_Encoder(output).start_sequence().encode_list(m_oids).end_cons(); |
752 | 0 | return output; |
753 | 0 | } |
754 | | |
755 | | /* |
756 | | * Decode the extension |
757 | | */ |
758 | 1.76k | void Extended_Key_Usage::decode_inner(const std::vector<uint8_t>& in) { |
759 | | /* RFC 5280 Section 4.2.1.12 - ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId */ |
760 | 1.76k | BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(m_oids).verify_end(); |
761 | 1.76k | if(m_oids.empty()) { |
762 | 40 | throw Decoding_Error("ExtendedKeyUsage extension must contain at least one KeyPurposeId"); |
763 | 40 | } |
764 | 1.76k | } |
765 | | |
766 | | /* |
767 | | * Encode the extension |
768 | | */ |
769 | 0 | std::vector<uint8_t> Name_Constraints::encode_inner() const { |
770 | 0 | const auto& permitted = m_name_constraints.permitted(); |
771 | 0 | const auto& excluded = m_name_constraints.excluded(); |
772 | |
|
773 | 0 | if(permitted.empty() && excluded.empty()) { |
774 | 0 | throw Encoding_Error("Refusing to encode empty NameConstraints"); |
775 | 0 | } |
776 | | |
777 | 0 | std::vector<uint8_t> output; |
778 | 0 | DER_Encoder der(output); |
779 | 0 | der.start_sequence(); |
780 | 0 | if(!permitted.empty()) { |
781 | 0 | der.start_explicit_context_specific(0).encode_list(permitted).end_cons(); |
782 | 0 | } |
783 | 0 | if(!excluded.empty()) { |
784 | 0 | der.start_explicit_context_specific(1).encode_list(excluded).end_cons(); |
785 | 0 | } |
786 | 0 | der.end_cons(); |
787 | 0 | return output; |
788 | 0 | } |
789 | | |
790 | | /* |
791 | | * Decode the extension |
792 | | */ |
793 | 689 | void Name_Constraints::decode_inner(const std::vector<uint8_t>& in) { |
794 | | /* |
795 | | * RFC 5280 Section 4.2.1.10 |
796 | | * |
797 | | * NameConstraints ::= SEQUENCE { |
798 | | * permittedSubtrees [0] GeneralSubtrees OPTIONAL, |
799 | | * excludedSubtrees [1] GeneralSubtrees OPTIONAL } |
800 | | */ |
801 | 689 | BER_Decoder ber(in, BER_Decoder::Limits::DER()); |
802 | 689 | BER_Decoder inner = ber.start_sequence(); |
803 | | |
804 | 689 | std::vector<GeneralSubtree> permitted; |
805 | 689 | if(inner.decode_optional_list(permitted, ASN1_Type(0), ASN1_Class::ExplicitContextSpecific)) { |
806 | 192 | if(permitted.empty()) { |
807 | 11 | throw Decoding_Error("Empty NameConstraint permitted list"); |
808 | 11 | } |
809 | 192 | } |
810 | | |
811 | 678 | std::vector<GeneralSubtree> excluded; |
812 | 678 | if(inner.decode_optional_list(excluded, ASN1_Type(1), ASN1_Class::ExplicitContextSpecific)) { |
813 | 88 | if(excluded.empty()) { |
814 | 6 | throw Decoding_Error("Empty NameConstraint excluded list"); |
815 | 6 | } |
816 | 88 | } |
817 | | |
818 | 672 | inner.end_cons(); |
819 | 672 | ber.verify_end(); |
820 | | |
821 | 672 | if(permitted.empty() && excluded.empty()) { |
822 | 4 | throw Decoding_Error("Empty NameConstraint extension"); |
823 | 4 | } |
824 | | |
825 | 668 | m_name_constraints = NameConstraints(std::move(permitted), std::move(excluded)); |
826 | 668 | } |
827 | | |
828 | | void Name_Constraints::validate(const X509_Certificate& subject, |
829 | | const std::optional<X509_Certificate>& /*issuer*/, |
830 | | const std::vector<X509_Certificate>& cert_path, |
831 | | std::vector<std::set<Certificate_Status_Code>>& cert_status, |
832 | 0 | size_t pos) const { |
833 | 0 | if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty()) { |
834 | 0 | if(!subject.is_CA_cert()) { |
835 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR); |
836 | 0 | } |
837 | |
|
838 | 0 | const bool issuer_name_constraint_critical = subject.is_critical("X509v3.NameConstraints"); |
839 | | |
840 | | // Check that all subordinate certs pass the name constraint |
841 | 0 | for(size_t j = 0; j < pos; ++j) { |
842 | 0 | const auto& cert = cert_path.at(j); |
843 | | |
844 | | // RFC 5280 6.1.4(b): "Name constraints are not applied to self-issued |
845 | | // certificates (unless the certificate is the final certificate in the path)" |
846 | | // Position 0 is the end entity (final certificate); skip self-issued intermediates. |
847 | 0 | if(j > 0 && cert.issuer_dn() == cert.subject_dn()) { |
848 | 0 | continue; |
849 | 0 | } |
850 | | |
851 | 0 | if(!m_name_constraints.is_permitted(cert, issuer_name_constraint_critical)) { |
852 | 0 | cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR); |
853 | 0 | continue; |
854 | 0 | } |
855 | | |
856 | 0 | if(m_name_constraints.is_excluded(cert, issuer_name_constraint_critical)) { |
857 | 0 | cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR); |
858 | 0 | continue; |
859 | 0 | } |
860 | 0 | } |
861 | 0 | } |
862 | 0 | } |
863 | | |
864 | | namespace { |
865 | | |
866 | | /* |
867 | | * A policy specifier |
868 | | */ |
869 | | class Policy_Information final : public ASN1_Object { |
870 | | public: |
871 | 1.68k | Policy_Information() = default; |
872 | | |
873 | 0 | explicit Policy_Information(const OID& oid) : m_oid(oid) {} |
874 | | |
875 | 549 | const OID& oid() const { return m_oid; } |
876 | | |
877 | 0 | void encode_into(DER_Encoder& codec) const override { codec.start_sequence().encode(m_oid).end_cons(); } |
878 | | |
879 | 1.68k | void decode_from(BER_Decoder& codec) override { |
880 | 1.68k | codec.start_sequence().decode(m_oid).discard_remaining().end_cons(); |
881 | 1.68k | } |
882 | | |
883 | | private: |
884 | | OID m_oid; |
885 | | }; |
886 | | |
887 | 103 | bool policy_oids_have_duplicate(const std::vector<OID>& oids) { |
888 | 103 | std::set<OID> seen; |
889 | 508 | for(const auto& oid : oids) { |
890 | 508 | if(!seen.insert(oid).second) { |
891 | 10 | return true; |
892 | 10 | } |
893 | 508 | } |
894 | 93 | return false; |
895 | 103 | } |
896 | | |
897 | | } // namespace |
898 | | |
899 | | Certificate_Policies::Certificate_Policies(const std::vector<OID>& oids) : |
900 | 0 | m_oids(oids), m_has_duplicate(policy_oids_have_duplicate(m_oids)) {} |
901 | | |
902 | | /* |
903 | | * Encode the extension |
904 | | */ |
905 | 0 | std::vector<uint8_t> Certificate_Policies::encode_inner() const { |
906 | 0 | std::vector<Policy_Information> policies; |
907 | |
|
908 | 0 | policies.reserve(m_oids.size()); |
909 | 0 | for(const auto& oid : m_oids) { |
910 | 0 | policies.push_back(Policy_Information(oid)); |
911 | 0 | } |
912 | |
|
913 | 0 | std::vector<uint8_t> output; |
914 | 0 | DER_Encoder(output).start_sequence().encode_list(policies).end_cons(); |
915 | 0 | return output; |
916 | 0 | } |
917 | | |
918 | | /* |
919 | | * Decode the extension |
920 | | */ |
921 | 844 | void Certificate_Policies::decode_inner(const std::vector<uint8_t>& in) { |
922 | | /* RFC 5280 Section 4.2.1.4 - CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation */ |
923 | 844 | std::vector<Policy_Information> policies; |
924 | | |
925 | 844 | BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(policies).verify_end(); |
926 | 844 | if(policies.empty()) { |
927 | 6 | throw Decoding_Error("CertificatePolicies extension must contain at least one PolicyInformation"); |
928 | 6 | } |
929 | 838 | m_oids.clear(); |
930 | 838 | for(const auto& policy : policies) { |
931 | 549 | m_oids.push_back(policy.oid()); |
932 | 549 | } |
933 | 838 | m_has_duplicate = policy_oids_have_duplicate(m_oids); |
934 | 838 | } |
935 | | |
936 | | void Certificate_Policies::validate(const X509_Certificate& /*subject*/, |
937 | | const std::optional<X509_Certificate>& /*issuer*/, |
938 | | const std::vector<X509_Certificate>& /*cert_path*/, |
939 | | std::vector<std::set<Certificate_Status_Code>>& cert_status, |
940 | 0 | size_t pos) const { |
941 | 0 | if(m_has_duplicate) { |
942 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::DUPLICATE_CERT_POLICY); |
943 | 0 | } |
944 | 0 | } |
945 | | |
946 | | namespace { |
947 | | |
948 | 0 | std::vector<URI> parse_aia_uris(const std::vector<std::string>& uris, const char* context) { |
949 | 0 | std::vector<URI> out; |
950 | 0 | out.reserve(uris.size()); |
951 | 0 | for(const auto& uri : uris) { |
952 | 0 | if(auto parsed = URI::from_string(uri)) { |
953 | 0 | out.push_back(std::move(*parsed)); |
954 | 0 | } else { |
955 | 0 | throw Invalid_Argument(fmt("Invalid URI in {}", context)); |
956 | 0 | } |
957 | 0 | } |
958 | 0 | return out; |
959 | 0 | } |
960 | | |
961 | | // Convert the application provided URIs into AccessDescription entries |
962 | | std::vector<Authority_Information_Access::AccessDescription> uri_access_descriptions( |
963 | 0 | const std::vector<URI>& ocsp_responders, const std::vector<URI>& ca_issuers) { |
964 | 0 | std::vector<Authority_Information_Access::AccessDescription> out; |
965 | 0 | out.reserve(ocsp_responders.size() + ca_issuers.size()); |
966 | |
|
967 | 0 | const auto append = [&](const OID& method, const std::vector<URI>& uris) { |
968 | 0 | for(const auto& uri : uris) { |
969 | 0 | const ASN1_String value(uri.original_input(), ASN1_Type::Ia5String); |
970 | 0 | out.emplace_back(method, |
971 | 0 | ASN1_Type(6), |
972 | 0 | ASN1_Class::ContextSpecific, |
973 | 0 | std::vector<uint8_t>(value.value().begin(), value.value().end())); |
974 | 0 | } |
975 | 0 | }; |
976 | |
|
977 | 0 | append(OID::from_string("PKIX.OCSP"), ocsp_responders); |
978 | 0 | append(OID::from_string("PKIX.CertificateAuthorityIssuers"), ca_issuers); |
979 | 0 | return out; |
980 | 0 | } |
981 | | |
982 | | } // namespace |
983 | | |
984 | | Authority_Information_Access::Authority_Information_Access(std::string_view ocsp, |
985 | | const std::vector<std::string>& ca_issuers) : |
986 | 0 | m_ca_issuers(parse_aia_uris(ca_issuers, "AuthorityInformationAccess CA issuers")) { |
987 | 0 | if(!ocsp.empty()) { |
988 | 0 | if(auto parsed = URI::from_string(ocsp)) { |
989 | 0 | m_ocsp_responders.push_back(std::move(*parsed)); |
990 | 0 | } else { |
991 | 0 | throw Invalid_Argument("Invalid URI in AuthorityInformationAccess OCSP responder"); |
992 | 0 | } |
993 | 0 | } |
994 | 0 | m_access_descriptions = uri_access_descriptions(m_ocsp_responders, m_ca_issuers); |
995 | 0 | } |
996 | | |
997 | | Authority_Information_Access::Authority_Information_Access(const std::vector<std::string>& ocsp_responders, |
998 | | const std::vector<std::string>& ca_issuers) : |
999 | 0 | m_ocsp_responders(parse_aia_uris(ocsp_responders, "AuthorityInformationAccess OCSP responders")), |
1000 | 0 | m_ca_issuers(parse_aia_uris(ca_issuers, "AuthorityInformationAccess CA issuers")), |
1001 | 0 | m_access_descriptions(uri_access_descriptions(m_ocsp_responders, m_ca_issuers)) {} |
1002 | | |
1003 | | Authority_Information_Access::Authority_Information_Access(std::vector<URI> ocsp_responders, |
1004 | | std::vector<URI> ca_issuers) : |
1005 | 0 | m_ocsp_responders(std::move(ocsp_responders)), |
1006 | 0 | m_ca_issuers(std::move(ca_issuers)), |
1007 | 0 | m_access_descriptions(uri_access_descriptions(m_ocsp_responders, m_ca_issuers)) {} |
1008 | | |
1009 | 0 | std::vector<std::string> Authority_Information_Access::ocsp_responders() const { |
1010 | 0 | std::vector<std::string> out; |
1011 | 0 | out.reserve(m_ocsp_responders.size()); |
1012 | 0 | for(const auto& uri : m_ocsp_responders) { |
1013 | 0 | out.push_back(uri.original_input()); |
1014 | 0 | } |
1015 | 0 | return out; |
1016 | 0 | } |
1017 | | |
1018 | 0 | std::unique_ptr<Certificate_Extension> Authority_Information_Access::copy() const { |
1019 | 0 | return std::make_unique<Authority_Information_Access>(*this); |
1020 | 0 | } |
1021 | | |
1022 | | namespace { |
1023 | | |
1024 | 1.69k | void validate_general_name_encoding(ASN1_Type tag, ASN1_Class cls, std::span<const uint8_t> value) { |
1025 | | // AlternativeName decodes GeneralNames; AIA accessLocation is a single GeneralName. |
1026 | 1.69k | std::vector<uint8_t> wrapped_name; |
1027 | 1.69k | DER_Encoder(wrapped_name).start_sequence().add_object(tag, cls, value).end_cons(); |
1028 | | |
1029 | 1.69k | AlternativeName decoded_name; |
1030 | 1.69k | BER_Decoder(wrapped_name, BER_Decoder::Limits::DER()).decode(decoded_name).verify_end(); |
1031 | | |
1032 | 1.69k | if((tag == ASN1_Type(1) || tag == ASN1_Type(2) || tag == ASN1_Type(6)) && value.empty()) { |
1033 | 12 | throw Decoding_Error("GeneralName IA5String value must not be empty"); |
1034 | 12 | } |
1035 | 1.68k | if(tag == ASN1_Type(4) && |
1036 | 7 | std::ranges::any_of(decoded_name.directory_names(), [](const X509_DN& dn) { return dn.empty(); })) { |
1037 | 1 | throw Decoding_Error("GeneralName directoryName must not be empty"); |
1038 | 1 | } |
1039 | 1.68k | } |
1040 | | |
1041 | | // Construction-time validation for an AccessDescription entering |
1042 | | // m_access_descriptions. encode_inner repeats this as a safety net; doing it |
1043 | | // here means the throw lands where the caller is building the AIA. |
1044 | 0 | void validate_access_description(const Authority_Information_Access::AccessDescription& ad) { |
1045 | 0 | try { |
1046 | 0 | validate_general_name_encoding(ad.location_tag(), ad.location_class(), ad.location_value()); |
1047 | 0 | } catch(const Exception&) { |
1048 | 0 | throw Invalid_Argument("AccessDescription accessLocation is not a valid GeneralName"); |
1049 | 0 | } |
1050 | 0 | } |
1051 | | |
1052 | | // Mirror the decode-time logic that populates the typed URI accessors from |
1053 | | // id-ad-ocsp / id-ad-caIssuers entries. Used by the AccessDescription-based |
1054 | | // constructor and add_access_description so the two views stay consistent. |
1055 | | // An id-ad-ocsp / id-ad-caIssuers entry whose URI fails to parse is rejected |
1056 | | // here (mirroring decode_inner) so the typed accessors and m_access_descriptions |
1057 | | // cannot disagree, and so the AIA cannot re-encode bytes that its own decoder |
1058 | | // would reject. |
1059 | | void populate_uri_view_from_access_description(const Authority_Information_Access::AccessDescription& ad, |
1060 | | std::vector<URI>& ocsp_responders, |
1061 | 0 | std::vector<URI>& ca_issuers) { |
1062 | 0 | const auto oid_ocsp_responders = OID::from_string("PKIX.OCSP"); |
1063 | 0 | const auto oid_ca_issuers = OID::from_string("PKIX.CertificateAuthorityIssuers"); |
1064 | 0 | if(const auto uri_str = ad.location_as_uri_string()) { |
1065 | 0 | if(ad.access_method() == oid_ocsp_responders) { |
1066 | 0 | if(auto uri = URI::from_string(*uri_str)) { |
1067 | 0 | ocsp_responders.push_back(std::move(*uri)); |
1068 | 0 | } else { |
1069 | 0 | throw Invalid_Argument("Invalid URI in AuthorityInformationAccess OCSP responder"); |
1070 | 0 | } |
1071 | 0 | } else if(ad.access_method() == oid_ca_issuers) { |
1072 | 0 | if(auto uri = URI::from_string(*uri_str)) { |
1073 | 0 | ca_issuers.push_back(std::move(*uri)); |
1074 | 0 | } else { |
1075 | 0 | throw Invalid_Argument("Invalid URI in AuthorityInformationAccess CA issuers"); |
1076 | 0 | } |
1077 | 0 | } |
1078 | 0 | } |
1079 | 0 | } |
1080 | | |
1081 | | } // namespace |
1082 | | |
1083 | | Authority_Information_Access::Authority_Information_Access(std::vector<AccessDescription> access_descriptions) : |
1084 | 0 | m_access_descriptions(std::move(access_descriptions)) { |
1085 | 0 | for(const auto& ad : m_access_descriptions) { |
1086 | 0 | validate_access_description(ad); |
1087 | 0 | populate_uri_view_from_access_description(ad, m_ocsp_responders, m_ca_issuers); |
1088 | 0 | } |
1089 | 0 | } |
1090 | | |
1091 | 0 | void Authority_Information_Access::add_access_description(AccessDescription ad) { |
1092 | 0 | validate_access_description(ad); |
1093 | 0 | populate_uri_view_from_access_description(ad, m_ocsp_responders, m_ca_issuers); |
1094 | 0 | m_access_descriptions.push_back(std::move(ad)); |
1095 | 0 | } |
1096 | | |
1097 | 0 | std::vector<std::string> Authority_Information_Access::ca_issuers() const { |
1098 | 0 | std::vector<std::string> out; |
1099 | 0 | out.reserve(m_ca_issuers.size()); |
1100 | 0 | for(const auto& uri : m_ca_issuers) { |
1101 | 0 | out.push_back(uri.original_input()); |
1102 | 0 | } |
1103 | 0 | return out; |
1104 | 0 | } |
1105 | | |
1106 | 0 | std::optional<std::string> Authority_Information_Access::AccessDescription::location_as_uri_string() const { |
1107 | 0 | if(m_location_class == ASN1_Class::ContextSpecific && m_location_tag == ASN1_Type(6)) { |
1108 | 0 | return std::string(m_location_value.begin(), m_location_value.end()); |
1109 | 0 | } |
1110 | 0 | return std::nullopt; |
1111 | 0 | } |
1112 | | |
1113 | 0 | std::vector<uint8_t> Authority_Information_Access::encode_inner() const { |
1114 | 0 | std::vector<uint8_t> output; |
1115 | 0 | DER_Encoder der(output); |
1116 | |
|
1117 | 0 | der.start_sequence(); |
1118 | |
|
1119 | 0 | for(const auto& ad : m_access_descriptions) { |
1120 | 0 | try { |
1121 | 0 | validate_general_name_encoding(ad.location_tag(), ad.location_class(), ad.location_value()); |
1122 | 0 | } catch(const Exception&) { |
1123 | 0 | throw Encoding_Error("AccessDescription accessLocation is not a valid GeneralName"); |
1124 | 0 | } |
1125 | 0 | der.start_sequence() |
1126 | 0 | .encode(ad.access_method()) |
1127 | 0 | .add_object(ad.location_tag(), ad.location_class(), ad.location_value()) |
1128 | 0 | .end_cons(); |
1129 | 0 | } |
1130 | | |
1131 | 0 | der.end_cons(); |
1132 | 0 | return output; |
1133 | 0 | } |
1134 | | |
1135 | 1.75k | void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in) { |
1136 | | /* |
1137 | | * RFC 5280 Section 4.2.2.1 |
1138 | | * |
1139 | | * AuthorityInfoAccessSyntax ::= SEQUENCE SIZE (1..MAX) OF AccessDescription |
1140 | | * AccessDescription ::= SEQUENCE { |
1141 | | * accessMethod OBJECT IDENTIFIER, |
1142 | | * accessLocation GeneralName } |
1143 | | */ |
1144 | 1.75k | BER_Decoder outer(in, BER_Decoder::Limits::DER()); |
1145 | 1.75k | BER_Decoder ber = outer.start_sequence(); |
1146 | | |
1147 | 1.75k | const OID ocsp_responder = OID::from_string("PKIX.OCSP"); |
1148 | 1.75k | const OID ca_issuer = OID::from_string("PKIX.CertificateAuthorityIssuers"); |
1149 | | |
1150 | 1.75k | m_access_descriptions.clear(); |
1151 | 1.75k | m_ocsp_responders.clear(); |
1152 | 1.75k | m_ca_issuers.clear(); |
1153 | | |
1154 | 3.67k | while(ber.more_items()) { |
1155 | 1.91k | OID oid; |
1156 | | |
1157 | 1.91k | BER_Decoder info = ber.start_sequence(); |
1158 | | |
1159 | 1.91k | info.decode(oid); |
1160 | 1.91k | const BER_Object name = info.get_next_object(); |
1161 | | |
1162 | | /* RFC 5280 4.2.2.1: |
1163 | | * AccessDescription ::= SEQUENCE { |
1164 | | * accessMethod OBJECT IDENTIFIER, |
1165 | | * accessLocation GeneralName } |
1166 | | */ |
1167 | 1.91k | if(!name.is_set()) { |
1168 | 3 | throw Decoding_Error("AuthorityInformationAccess AccessDescription missing accessLocation"); |
1169 | 3 | } |
1170 | 1.91k | validate_general_name_encoding(name.type_tag(), name.get_class(), name.data()); |
1171 | 1.91k | info.end_cons(); |
1172 | | |
1173 | 1.91k | m_access_descriptions.emplace_back( |
1174 | 1.91k | oid, name.type_tag(), name.get_class(), std::vector<uint8_t>(name.data().begin(), name.data().end())); |
1175 | | |
1176 | 1.91k | if(name.is_a(6, ASN1_Class::ContextSpecific)) { |
1177 | 692 | if(oid == ocsp_responder) { |
1178 | 209 | if(auto parsed = URI::from_string(ASN1::to_string(name))) { |
1179 | 209 | m_ocsp_responders.push_back(std::move(*parsed)); |
1180 | 209 | } else { |
1181 | 0 | throw Decoding_Error("Invalid URI in AuthorityInformationAccess OCSP responder"); |
1182 | 0 | } |
1183 | 483 | } else if(oid == ca_issuer) { |
1184 | 214 | if(auto parsed = URI::from_string(ASN1::to_string(name))) { |
1185 | 214 | m_ca_issuers.push_back(std::move(*parsed)); |
1186 | 214 | } else { |
1187 | 0 | throw Decoding_Error("Invalid URI in AuthorityInformationAccess CA issuers"); |
1188 | 0 | } |
1189 | 214 | } |
1190 | 692 | } |
1191 | 1.91k | } |
1192 | | |
1193 | 1.75k | ber.end_cons(); |
1194 | 1.75k | outer.verify_end(); |
1195 | | |
1196 | 1.75k | if(m_access_descriptions.empty()) { |
1197 | 109 | throw Decoding_Error("AuthorityInformationAccess extension must contain at least one AccessDescription"); |
1198 | 109 | } |
1199 | 1.75k | } |
1200 | | |
1201 | 0 | CRL_Number::CRL_Number(BigInt n) : m_has_value(true), m_crl_number(std::move(n)) { |
1202 | 0 | BOTAN_ARG_CHECK(m_crl_number.signum() >= 0, "CRL number cannot be negative"); |
1203 | 0 | } |
1204 | | |
1205 | 18 | const BigInt& CRL_Number::crl_number() const { |
1206 | | // This can only happen via a misuse of the CRL_Number default constructor |
1207 | 18 | BOTAN_STATE_CHECK(m_has_value); |
1208 | 18 | return m_crl_number; |
1209 | 18 | } |
1210 | | |
1211 | | /* |
1212 | | * Checked accessor for the crl_number member |
1213 | | */ |
1214 | 0 | size_t CRL_Number::get_crl_number() const { |
1215 | | // This can only happen via a misuse of the CRL_Number default constructor |
1216 | 0 | BOTAN_STATE_CHECK(m_has_value); |
1217 | 0 | return m_crl_number.to_u32bit(); |
1218 | 0 | } |
1219 | | |
1220 | | /* |
1221 | | * Copy a CRL_Number extension |
1222 | | */ |
1223 | 0 | std::unique_ptr<Certificate_Extension> CRL_Number::copy() const { |
1224 | 0 | return std::make_unique<CRL_Number>(*this); |
1225 | 0 | } |
1226 | | |
1227 | | /* |
1228 | | * Encode the extension |
1229 | | */ |
1230 | 0 | std::vector<uint8_t> CRL_Number::encode_inner() const { |
1231 | 0 | std::vector<uint8_t> output; |
1232 | 0 | DER_Encoder(output).encode(m_crl_number); |
1233 | 0 | return output; |
1234 | 0 | } |
1235 | | |
1236 | | /* |
1237 | | * Decode the extension |
1238 | | */ |
1239 | 47 | void CRL_Number::decode_inner(const std::vector<uint8_t>& in) { |
1240 | | /* RFC 5280 Section 5.2.3 - CRLNumber ::= INTEGER (0..MAX) */ |
1241 | 47 | BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_crl_number).verify_end(); |
1242 | 47 | if(m_crl_number.signum() < 0) { |
1243 | 3 | throw Decoding_Error("CRL number cannot be negative"); |
1244 | 3 | } |
1245 | 44 | m_has_value = true; |
1246 | 44 | } |
1247 | | |
1248 | | /* |
1249 | | * Encode the extension |
1250 | | */ |
1251 | 0 | std::vector<uint8_t> CRL_ReasonCode::encode_inner() const { |
1252 | 0 | std::vector<uint8_t> output; |
1253 | 0 | DER_Encoder(output).encode(static_cast<size_t>(m_reason), ASN1_Type::Enumerated, ASN1_Class::Universal); |
1254 | 0 | return output; |
1255 | 0 | } |
1256 | | |
1257 | | /* |
1258 | | * Decode the extension |
1259 | | */ |
1260 | 3.90k | void CRL_ReasonCode::decode_inner(const std::vector<uint8_t>& in) { |
1261 | | /* |
1262 | | * RFC 5280 Section 5.3.1 |
1263 | | * |
1264 | | * CRLReason ::= ENUMERATED { |
1265 | | * unspecified (0), |
1266 | | * keyCompromise (1), |
1267 | | * cACompromise (2), |
1268 | | * affiliationChanged (3), |
1269 | | * superseded (4), |
1270 | | * cessationOfOperation (5), |
1271 | | * certificateHold (6), |
1272 | | * -- value 7 is not used |
1273 | | * removeFromCRL (8), |
1274 | | * privilegeWithdrawn (9), |
1275 | | * aACompromise (10) } |
1276 | | */ |
1277 | 3.90k | size_t reason_code = 0; |
1278 | 3.90k | BER_Decoder(in, BER_Decoder::Limits::DER()) |
1279 | 3.90k | .decode(reason_code, ASN1_Type::Enumerated, ASN1_Class::Universal) |
1280 | 3.90k | .verify_end(); |
1281 | | |
1282 | 3.90k | if(reason_code == 7 || reason_code > 10) { |
1283 | 417 | throw Decoding_Error(fmt("CRLReason has unknown enumeration value {}", reason_code)); |
1284 | 417 | } |
1285 | | |
1286 | 3.48k | m_reason = static_cast<CRL_Code>(reason_code); |
1287 | 3.48k | } |
1288 | | |
1289 | | namespace { |
1290 | | |
1291 | | constexpr size_t ReasonFlagsNamedBitWidth = 9; |
1292 | | |
1293 | 0 | void emit_reason_flags_implicit(DER_Encoder& der, uint32_t tag, ReasonFlags reasons) { |
1294 | 0 | der.encode_named_bitstring(reasons.value(), ReasonFlagsNamedBitWidth, ASN1_Type(tag), ASN1_Class::ContextSpecific); |
1295 | 0 | } |
1296 | | |
1297 | 67 | ReasonFlags decode_reason_flags_implicit(BER_Decoder& decoder, uint32_t tag) { |
1298 | 67 | uint64_t bits = 0; |
1299 | 67 | decoder.decode_named_bitstring(bits, ReasonFlagsNamedBitWidth, ASN1_Type(tag), ASN1_Class::ContextSpecific); |
1300 | 67 | return ReasonFlags(checked_cast_to<uint16_t>(bits)); |
1301 | 67 | } |
1302 | | |
1303 | | /* |
1304 | | * RFC 5280 4.2.1.13: "If present, the cRLIssuer MUST only contain the |
1305 | | * distinguished name (DN) from the issuer field of the CRL to which the |
1306 | | * DistributionPoint is pointing." |
1307 | | * |
1308 | | * We don't know the value of the CRL issuer at this point so we can only |
1309 | | * enforce that the cRLIssuer name is exactly one non-empty DN. |
1310 | | */ |
1311 | 2 | bool crl_issuer_is_well_formed(const AlternativeName& crl_issuer) { |
1312 | 2 | const auto& dn = crl_issuer.directory_names(); |
1313 | 2 | return crl_issuer.count() == 1 && dn.size() == 1 && !dn.begin()->empty(); |
1314 | 2 | } |
1315 | | |
1316 | | std::vector<URI> crl_distribution_point_uris_from_distribution_points( |
1317 | 161 | const std::vector<CRL_Distribution_Points::Distribution_Point>& dps) { |
1318 | 161 | std::vector<URI> out; |
1319 | 328 | for(const auto& dp : dps) { |
1320 | 328 | const auto& dpn = dp.distribution_point_name(); |
1321 | 328 | if(dpn.has_value() && dpn->full_name().has_value()) { |
1322 | 523 | for(const auto& uri : dpn->full_name()->uri_names()) { |
1323 | 523 | out.push_back(uri); |
1324 | 523 | } |
1325 | 328 | } |
1326 | 328 | } |
1327 | 161 | return out; |
1328 | 161 | } |
1329 | | |
1330 | | } // namespace |
1331 | | |
1332 | 0 | const AlternativeName& CRL_Distribution_Points::Distribution_Point::point() const { |
1333 | 0 | BOTAN_STATE_CHECK(m_dp_name.has_value() && m_dp_name->full_name().has_value()); |
1334 | 0 | return *m_dp_name->full_name(); |
1335 | 0 | } |
1336 | | |
1337 | | CRL_Distribution_Points::CRL_Distribution_Points(const std::vector<Distribution_Point>& points) : |
1338 | 0 | m_distribution_points(points), |
1339 | 0 | m_crl_distribution_urls(crl_distribution_point_uris_from_distribution_points(m_distribution_points)) {} |
1340 | | |
1341 | 0 | const AlternativeName& CRL_Issuing_Distribution_Point::get_point() const { |
1342 | 0 | BOTAN_STATE_CHECK(m_dp_name.has_value() && m_dp_name->full_name().has_value()); |
1343 | 0 | return *m_dp_name->full_name(); |
1344 | 0 | } |
1345 | | |
1346 | 0 | void DistributionPointName::encode_into(DER_Encoder& der) const { |
1347 | 0 | if(!m_full_name.has_value()) { |
1348 | 0 | throw Encoding_Error("DistributionPointName has no fullName to encode"); |
1349 | 0 | } |
1350 | | // fullName [0] IMPLICIT GeneralNames. emit_general_names_implicit rejects |
1351 | | // empty AlternativeNames per RFC 5280 4.2.1.6: GeneralNames ::= SEQUENCE |
1352 | | // SIZE (1..MAX). |
1353 | 0 | emit_general_names_implicit(der, *m_full_name, 0); |
1354 | 0 | } |
1355 | | |
1356 | 1.55k | void DistributionPointName::decode_from(BER_Decoder& ber) { |
1357 | 1.55k | const BER_Object& obj = ber.peek_next_object(); |
1358 | 1.55k | if(obj.is_a(0, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { |
1359 | 1.43k | AlternativeName full_name; |
1360 | 1.43k | ber.decode_implicit(full_name, |
1361 | 1.43k | ASN1_Type(0), |
1362 | 1.43k | ASN1_Class::ContextSpecific | ASN1_Class::Constructed, |
1363 | 1.43k | ASN1_Type::Sequence, |
1364 | 1.43k | ASN1_Class::Constructed); |
1365 | | // RFC 5280 4.2.1.6: GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName |
1366 | 1.43k | if(!full_name.has_items()) { |
1367 | 19 | throw Decoding_Error("DistributionPointName fullName must contain at least one GeneralName"); |
1368 | 19 | } |
1369 | 1.41k | if(std::ranges::any_of(full_name.directory_names(), [](const X509_DN& dn) { return dn.empty(); })) { |
1370 | 0 | throw Decoding_Error("DistributionPointName fullName must not contain an empty directoryName"); |
1371 | 0 | } |
1372 | 1.41k | m_full_name = std::move(full_name); |
1373 | 1.41k | } else if(obj.is_a(1, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { |
1374 | 7 | throw Decoding_Error("nameRelativeToCrlIssuer not supported in DistributionPointName"); |
1375 | 112 | } else { |
1376 | 112 | throw Decoding_Error("DistributionPointName CHOICE is neither fullName nor nameRelativeToCRLIssuer"); |
1377 | 112 | } |
1378 | 1.55k | } |
1379 | | |
1380 | 0 | std::vector<uint8_t> CRL_Distribution_Points::encode_inner() const { |
1381 | 0 | std::vector<uint8_t> output; |
1382 | 0 | DER_Encoder(output).start_sequence().encode_list(m_distribution_points).end_cons(); |
1383 | 0 | return output; |
1384 | 0 | } |
1385 | | |
1386 | 1.55k | void CRL_Distribution_Points::decode_inner(const std::vector<uint8_t>& buf) { |
1387 | | /* |
1388 | | * RFC 5280 Section 4.2.1.13 |
1389 | | * |
1390 | | * CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint |
1391 | | */ |
1392 | 1.55k | BER_Decoder(buf, BER_Decoder::Limits::DER()).decode_list(m_distribution_points).verify_end(); |
1393 | | |
1394 | 1.55k | if(m_distribution_points.empty()) { |
1395 | 12 | throw Decoding_Error("CRLDistributionPoints extension must contain at least one DistributionPoint"); |
1396 | 12 | } |
1397 | | |
1398 | 1.54k | m_crl_distribution_urls = crl_distribution_point_uris_from_distribution_points(m_distribution_points); |
1399 | 1.54k | } |
1400 | | |
1401 | 0 | std::vector<std::string> CRL_Distribution_Points::crl_distribution_urls() const { |
1402 | 0 | std::vector<std::string> out; |
1403 | 0 | out.reserve(m_crl_distribution_urls.size()); |
1404 | 0 | for(const auto& uri : m_crl_distribution_urls) { |
1405 | 0 | out.push_back(uri.original_input()); |
1406 | 0 | } |
1407 | 0 | return out; |
1408 | 0 | } |
1409 | | |
1410 | 0 | void CRL_Distribution_Points::Distribution_Point::encode_into(DER_Encoder& der) const { |
1411 | | /* |
1412 | | * DistributionPoint ::= SEQUENCE { |
1413 | | * distributionPoint [0] DistributionPointName OPTIONAL, |
1414 | | * reasons [1] ReasonFlags OPTIONAL, |
1415 | | * cRLIssuer [2] GeneralNames OPTIONAL } |
1416 | | * |
1417 | | * RFC 5280 4.2.1.13: "either distributionPoint or cRLIssuer MUST be present". |
1418 | | */ |
1419 | 0 | const bool has_dp_name = m_dp_name.has_value(); |
1420 | 0 | const bool has_crl_issuer = m_crl_issuer.has_value(); |
1421 | 0 | if(!has_dp_name && !has_crl_issuer) { |
1422 | 0 | throw Encoding_Error("DistributionPoint must contain either distributionPoint or cRLIssuer"); |
1423 | 0 | } |
1424 | 0 | if(has_crl_issuer && !crl_issuer_is_well_formed(*m_crl_issuer)) { |
1425 | | /* RFC 5280 4.2.1.13: "If present, the cRLIssuer MUST only contain the |
1426 | | * distinguished name (DN) from the issuer field of the CRL". */ |
1427 | 0 | throw Encoding_Error("cRLIssuer must contain exactly one non-empty directoryName GeneralName"); |
1428 | 0 | } |
1429 | | |
1430 | 0 | der.start_sequence(); |
1431 | |
|
1432 | 0 | if(has_dp_name) { |
1433 | | // distributionPoint [0] EXPLICIT DistributionPointName |
1434 | 0 | der.start_explicit_context_specific(0).encode(*m_dp_name).end_cons(); |
1435 | 0 | } |
1436 | |
|
1437 | 0 | if(m_reasons) { |
1438 | 0 | emit_reason_flags_implicit(der, 1, *m_reasons); |
1439 | 0 | } |
1440 | |
|
1441 | 0 | if(has_crl_issuer) { |
1442 | 0 | emit_general_names_implicit(der, *m_crl_issuer, 2); |
1443 | 0 | } |
1444 | |
|
1445 | 0 | der.end_cons(); |
1446 | 0 | } |
1447 | | |
1448 | 1.74k | void CRL_Distribution_Points::Distribution_Point::decode_from(BER_Decoder& ber) { |
1449 | | /* |
1450 | | * DistributionPoint ::= SEQUENCE { |
1451 | | * distributionPoint [0] DistributionPointName OPTIONAL, |
1452 | | * reasons [1] ReasonFlags OPTIONAL, |
1453 | | * cRLIssuer [2] GeneralNames OPTIONAL } |
1454 | | */ |
1455 | 1.74k | BER_Decoder dp = ber.start_sequence(); |
1456 | | |
1457 | 1.74k | m_dp_name.reset(); |
1458 | 1.74k | m_reasons.reset(); |
1459 | 1.74k | m_crl_issuer.reset(); |
1460 | | |
1461 | | // DER: these optional fields appear at most once and in increasing tag |
1462 | | // order. Decoding them in tag order and then rejecting anything left over |
1463 | | // (see end_cons below) catches out-of-order, duplicate, and unknown fields. |
1464 | 1.74k | dp.decode_optional_field(0, |
1465 | 1.74k | ASN1_Class::ContextSpecific | ASN1_Class::Constructed, |
1466 | 1.74k | [&](BER_Decoder& d) { |
1467 | 1.51k | DistributionPointName name; |
1468 | 1.51k | d.start_context_specific(0).decode(name).verify_end(); |
1469 | 1.51k | m_dp_name = std::move(name); |
1470 | 1.51k | }) |
1471 | 1.74k | .decode_optional_field( |
1472 | 1.74k | 1, ASN1_Class::ContextSpecific, [&](BER_Decoder& d) { m_reasons = decode_reason_flags_implicit(d, 1); }) |
1473 | 1.74k | .decode_optional_field(2, ASN1_Class::ContextSpecific | ASN1_Class::Constructed, [&](BER_Decoder& d) { |
1474 | 19 | AlternativeName crl_issuer; |
1475 | 19 | d.decode_implicit(crl_issuer, |
1476 | 19 | ASN1_Type(2), |
1477 | 19 | ASN1_Class::ContextSpecific | ASN1_Class::Constructed, |
1478 | 19 | ASN1_Type::Sequence, |
1479 | 19 | ASN1_Class::Constructed); |
1480 | 19 | m_crl_issuer = std::move(crl_issuer); |
1481 | 19 | }); |
1482 | | |
1483 | 1.74k | dp.end_cons(); |
1484 | | |
1485 | | // RFC 5280 4.2.1.6: GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName |
1486 | 1.74k | if(m_crl_issuer.has_value() && m_crl_issuer->is_empty()) { |
1487 | 2 | throw Decoding_Error("cRLIssuer GeneralNames must contain at least one GeneralName"); |
1488 | 2 | } |
1489 | | |
1490 | 1.74k | if(m_crl_issuer.has_value() && !crl_issuer_is_well_formed(*m_crl_issuer)) { |
1491 | | /* RFC 5280 4.2.1.13: "If present, the cRLIssuer MUST only contain the |
1492 | | * distinguished name (DN) from the issuer field of the CRL". */ |
1493 | 2 | throw Decoding_Error("cRLIssuer must contain exactly one non-empty directoryName GeneralName"); |
1494 | 2 | } |
1495 | | |
1496 | 1.74k | if(!m_dp_name.has_value() && !m_crl_issuer.has_value()) { |
1497 | 8 | throw Decoding_Error("DistributionPoint must contain either distributionPoint or cRLIssuer"); |
1498 | 8 | } |
1499 | 1.74k | } |
1500 | | |
1501 | 0 | std::vector<uint8_t> CRL_Issuing_Distribution_Point::encode_inner() const { |
1502 | | /* RFC 5280 Section 5.2.5 |
1503 | | * |
1504 | | * Conforming CRL issuers MUST NOT issue CRLs where the DER encoding of the |
1505 | | * issuing distribution point extension is an empty sequence. [...] at most one |
1506 | | * of onlyContainsUserCerts, onlyContainsCACerts, and onlyContainsAttributeCerts |
1507 | | * may be set to TRUE |
1508 | | */ |
1509 | 0 | if(!should_encode()) { |
1510 | 0 | throw Encoding_Error("Refusing to encode empty IssuingDistributionPoint"); |
1511 | 0 | } |
1512 | | |
1513 | 0 | const size_t scope_set = static_cast<size_t>(m_only_contains_user_certs) + |
1514 | 0 | static_cast<size_t>(m_only_contains_ca_certs) + |
1515 | 0 | static_cast<size_t>(m_only_contains_attribute_certs); |
1516 | 0 | if(scope_set > 1) { |
1517 | 0 | throw Encoding_Error( |
1518 | 0 | "At most one of onlyContainsUserCerts, onlyContainsCACerts, onlyContainsAttributeCerts may be TRUE"); |
1519 | 0 | } |
1520 | | |
1521 | 0 | auto emit_optional_boolean_implicit = [](DER_Encoder& der, uint32_t tag, bool value) { |
1522 | | // All of the values encoded here are DEFAULT FALSE so skip encoding if false |
1523 | 0 | if(value == true) { |
1524 | | // Encode a BOOLEAN TRUE (0xFF) as [tag] IMPLICIT BOOLEAN |
1525 | 0 | const uint8_t val = 0xFF; |
1526 | 0 | der.add_object(ASN1_Type(tag), ASN1_Class::ContextSpecific, &val, 1); |
1527 | 0 | } |
1528 | 0 | }; |
1529 | |
|
1530 | 0 | std::vector<uint8_t> output; |
1531 | 0 | DER_Encoder der(output); |
1532 | 0 | der.start_sequence(); |
1533 | |
|
1534 | 0 | if(m_dp_name.has_value()) { |
1535 | 0 | der.start_explicit_context_specific(0).encode(*m_dp_name).end_cons(); |
1536 | 0 | } |
1537 | |
|
1538 | 0 | emit_optional_boolean_implicit(der, 1, m_only_contains_user_certs); |
1539 | 0 | emit_optional_boolean_implicit(der, 2, m_only_contains_ca_certs); |
1540 | |
|
1541 | 0 | if(m_only_some_reasons) { |
1542 | 0 | emit_reason_flags_implicit(der, 3, *m_only_some_reasons); |
1543 | 0 | } |
1544 | |
|
1545 | 0 | emit_optional_boolean_implicit(der, 4, m_indirect_crl); |
1546 | 0 | emit_optional_boolean_implicit(der, 5, m_only_contains_attribute_certs); |
1547 | |
|
1548 | 0 | der.end_cons(); |
1549 | 0 | return output; |
1550 | 0 | } |
1551 | | |
1552 | 185 | void CRL_Issuing_Distribution_Point::decode_inner(const std::vector<uint8_t>& buf) { |
1553 | | /* |
1554 | | * RFC 5280 Section 5.2.5 |
1555 | | * |
1556 | | * IssuingDistributionPoint ::= SEQUENCE { |
1557 | | * distributionPoint [0] DistributionPointName OPTIONAL, |
1558 | | * onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, |
1559 | | * onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, |
1560 | | * onlySomeReasons [3] ReasonFlags OPTIONAL, |
1561 | | * indirectCRL [4] BOOLEAN DEFAULT FALSE, |
1562 | | * onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } |
1563 | | */ |
1564 | 185 | BER_Decoder outer(buf, BER_Decoder::Limits::DER()); |
1565 | 185 | BER_Decoder seq = outer.start_sequence(); |
1566 | | |
1567 | 185 | m_dp_name.reset(); |
1568 | 185 | m_only_contains_user_certs = false; |
1569 | 185 | m_only_contains_ca_certs = false; |
1570 | 185 | m_only_some_reasons = {}; |
1571 | 185 | m_indirect_crl = false; |
1572 | 185 | m_only_contains_attribute_certs = false; |
1573 | | |
1574 | 185 | auto decode_implicit_bool = [&](BER_Decoder& dec, uint32_t tag) -> bool { |
1575 | 35 | bool value = false; |
1576 | 35 | dec.decode(value, ASN1_Type(tag), ASN1_Class::ContextSpecific); |
1577 | 35 | return value; |
1578 | 35 | }; |
1579 | | |
1580 | | // DER: these optional fields appear at most once and in increasing tag |
1581 | | // order. Decoding them in tag order and then rejecting anything left over |
1582 | | // (see end_cons below) catches out-of-order, duplicate, and unknown fields. |
1583 | 185 | seq.decode_optional_field(0, |
1584 | 185 | ASN1_Class::ContextSpecific | ASN1_Class::Constructed, |
1585 | 185 | [&](BER_Decoder& d) { |
1586 | 36 | DistributionPointName name; |
1587 | 36 | d.start_context_specific(0).decode(name).verify_end(); |
1588 | 36 | m_dp_name = std::move(name); |
1589 | 36 | }) |
1590 | 185 | .decode_optional_field(1, |
1591 | 185 | ASN1_Class::ContextSpecific, |
1592 | 185 | [&](BER_Decoder& d) { m_only_contains_user_certs = decode_implicit_bool(d, 1); }) |
1593 | 185 | .decode_optional_field( |
1594 | 185 | 2, ASN1_Class::ContextSpecific, [&](BER_Decoder& d) { m_only_contains_ca_certs = decode_implicit_bool(d, 2); }) |
1595 | 185 | .decode_optional_field(3, |
1596 | 185 | ASN1_Class::ContextSpecific, |
1597 | 185 | [&](BER_Decoder& d) { m_only_some_reasons = decode_reason_flags_implicit(d, 3); }) |
1598 | 185 | .decode_optional_field( |
1599 | 185 | 4, ASN1_Class::ContextSpecific, [&](BER_Decoder& d) { m_indirect_crl = decode_implicit_bool(d, 4); }) |
1600 | 185 | .decode_optional_field(5, ASN1_Class::ContextSpecific, [&](BER_Decoder& d) { |
1601 | 6 | m_only_contains_attribute_certs = decode_implicit_bool(d, 5); |
1602 | 6 | }); |
1603 | | |
1604 | 185 | seq.end_cons(); |
1605 | 185 | outer.verify_end(); |
1606 | | |
1607 | | /* RFC 5280 5.2.5: "Conforming CRLs issuers MUST NOT issue CRLs where the |
1608 | | * DER encoding of the issuing distribution point extension is an empty |
1609 | | * sequence." Empty here means none of the fields above were present. */ |
1610 | 185 | if(!m_dp_name.has_value() && !m_only_contains_user_certs && !m_only_contains_ca_certs && |
1611 | 0 | !m_only_some_reasons.has_value() && !m_indirect_crl && !m_only_contains_attribute_certs) { |
1612 | 0 | throw Decoding_Error("IssuingDistributionPoint must contain at least one field"); |
1613 | 0 | } |
1614 | | |
1615 | | /* RFC 5280 5.2.5: "at most one of onlyContainsUserCerts, |
1616 | | * onlyContainsCACerts, and onlyContainsAttributeCerts may be set to TRUE." */ |
1617 | 185 | const size_t scope_set = static_cast<size_t>(m_only_contains_user_certs) + |
1618 | 185 | static_cast<size_t>(m_only_contains_ca_certs) + |
1619 | 185 | static_cast<size_t>(m_only_contains_attribute_certs); |
1620 | 185 | if(scope_set > 1) { |
1621 | 0 | throw Decoding_Error( |
1622 | 0 | "IssuingDistributionPoint sets more than one of onlyContainsUserCerts/CACerts/AttributeCerts"); |
1623 | 0 | } |
1624 | 185 | } |
1625 | | |
1626 | 0 | void TNAuthList::Entry::encode_into(DER_Encoder& /*to*/) const { |
1627 | 0 | throw Not_Implemented("TNAuthList extension entry serialization is not supported"); |
1628 | 0 | } |
1629 | | |
1630 | 267 | void TNAuthList::Entry::decode_from(class BER_Decoder& ber) { |
1631 | 267 | const BER_Object obj = ber.get_next_object(); |
1632 | | |
1633 | 267 | if(obj.get_class() != (ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) { |
1634 | 32 | throw Decoding_Error(fmt("Unexpected TNEntry class tag {}", static_cast<uint32_t>(obj.get_class()))); |
1635 | 32 | } |
1636 | | |
1637 | 235 | const uint32_t type_tag = static_cast<uint32_t>(obj.type_tag()); |
1638 | | |
1639 | 235 | if(type_tag == ServiceProviderCode) { |
1640 | 76 | m_type = ServiceProviderCode; |
1641 | 76 | ASN1_String spc_string; |
1642 | 76 | BER_Decoder(obj, ber.limits()).decode(spc_string).verify_end(); |
1643 | 76 | m_data = std::move(spc_string); |
1644 | 159 | } else if(type_tag == TelephoneNumberRange) { |
1645 | 101 | m_type = TelephoneNumberRange; |
1646 | 101 | m_data = RangeContainer(); |
1647 | 101 | auto& range_items = std::get<RangeContainer>(m_data); |
1648 | 101 | BER_Decoder outer(obj, ber.limits()); |
1649 | 101 | BER_Decoder list = outer.start_sequence(); |
1650 | 209 | while(list.more_items()) { |
1651 | 143 | TelephoneNumberRangeData entry; |
1652 | | |
1653 | 143 | list.decode(entry.start); |
1654 | 143 | if(!is_valid_telephone_number(entry.start)) { |
1655 | 31 | throw Decoding_Error(fmt("Invalid TelephoneNumberRange start {}", entry.start.value())); |
1656 | 31 | } |
1657 | | |
1658 | 112 | list.decode(entry.count); |
1659 | 112 | if(entry.count < 2) { |
1660 | 4 | throw Decoding_Error(fmt("Invalid TelephoneNumberRange count {}", entry.count)); |
1661 | 4 | } |
1662 | | |
1663 | 108 | range_items.emplace_back(std::move(entry)); |
1664 | 108 | } |
1665 | 66 | list.end_cons(); |
1666 | 66 | outer.verify_end(); |
1667 | | |
1668 | 66 | if(range_items.empty()) { |
1669 | 4 | throw Decoding_Error("TelephoneNumberRange is empty"); |
1670 | 4 | } |
1671 | 66 | } else if(type_tag == TelephoneNumber) { |
1672 | 32 | m_type = TelephoneNumber; |
1673 | 32 | ASN1_String one_string; |
1674 | 32 | BER_Decoder(obj, ber.limits()).decode(one_string).verify_end(); |
1675 | 32 | if(!is_valid_telephone_number(one_string)) { |
1676 | 10 | throw Decoding_Error(fmt("Invalid TelephoneNumber {}", one_string.value())); |
1677 | 10 | } |
1678 | 22 | m_data = std::move(one_string); |
1679 | 26 | } else { |
1680 | 26 | throw Decoding_Error(fmt("Unexpected TNEntry type code {}", type_tag)); |
1681 | 160 | }; |
1682 | 160 | } |
1683 | | |
1684 | 0 | std::vector<uint8_t> TNAuthList::encode_inner() const { |
1685 | 0 | throw Not_Implemented("TNAuthList extension serialization is not supported"); |
1686 | 0 | } |
1687 | | |
1688 | 217 | void TNAuthList::decode_inner(const std::vector<uint8_t>& in) { |
1689 | | /* RFC 8226 Section 9 - TNAuthorizationList ::= SEQUENCE SIZE (1..MAX) OF TNEntry */ |
1690 | 217 | BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(m_tn_entries).verify_end(); |
1691 | 217 | if(m_tn_entries.empty()) { |
1692 | 4 | throw Decoding_Error("TNAuthorizationList is empty"); |
1693 | 4 | } |
1694 | 217 | } |
1695 | | |
1696 | 0 | const std::string& TNAuthList::Entry::service_provider_code() const { |
1697 | 0 | BOTAN_STATE_CHECK(type() == Type::ServiceProviderCode); |
1698 | 0 | return std::get<ASN1_String>(m_data).value(); |
1699 | 0 | } |
1700 | | |
1701 | 0 | const TNAuthList::Entry::RangeContainer& TNAuthList::Entry::telephone_number_range() const { |
1702 | 0 | BOTAN_STATE_CHECK(type() == Type::TelephoneNumberRange); |
1703 | 0 | return std::get<RangeContainer>(m_data); |
1704 | 0 | } |
1705 | | |
1706 | 0 | const std::string& TNAuthList::Entry::telephone_number() const { |
1707 | 0 | BOTAN_STATE_CHECK(type() == Type::TelephoneNumber); |
1708 | 0 | return std::get<ASN1_String>(m_data).value(); |
1709 | 0 | } |
1710 | | |
1711 | 0 | std::vector<uint8_t> IPAddressBlocks::encode_inner() const { |
1712 | 0 | std::vector<uint8_t> output; |
1713 | 0 | DER_Encoder(output).start_sequence().encode_list(m_ip_addr_blocks).end_cons(); |
1714 | 0 | return output; |
1715 | 0 | } |
1716 | | |
1717 | 1.27k | void IPAddressBlocks::decode_inner(const std::vector<uint8_t>& in) { |
1718 | | /* RFC 3779 Section 2.2.3.1 - IPAddrBlocks ::= SEQUENCE OF IPAddressFamily */ |
1719 | 1.27k | BER_Decoder(in, BER_Decoder::Limits::DER()).decode_list(m_ip_addr_blocks).verify_end(); |
1720 | 1.27k | sort_and_merge(); |
1721 | 1.27k | } |
1722 | | |
1723 | 0 | void IPAddressBlocks::IPAddressFamily::encode_into(Botan::DER_Encoder& into) const { |
1724 | 0 | into.start_sequence(); |
1725 | |
|
1726 | 0 | std::vector<uint8_t> afam = {get_byte<0>(m_afi), get_byte<1>(m_afi)}; |
1727 | |
|
1728 | 0 | if(m_safi.has_value()) { |
1729 | 0 | afam.push_back(m_safi.value()); |
1730 | 0 | } |
1731 | |
|
1732 | 0 | into.add_object(ASN1_Type::OctetString, ASN1_Class::Universal, afam); |
1733 | |
|
1734 | 0 | if(std::holds_alternative<IPAddressChoice<Version::IPv4>>(m_ip_addr_choice)) { |
1735 | 0 | into.encode(std::get<IPAddressChoice<Version::IPv4>>(m_ip_addr_choice)); |
1736 | 0 | } else { |
1737 | 0 | into.encode(std::get<IPAddressChoice<Version::IPv6>>(m_ip_addr_choice)); |
1738 | 0 | } |
1739 | 0 | into.end_cons(); |
1740 | 0 | } |
1741 | | |
1742 | 1.42k | void IPAddressBlocks::IPAddressFamily::decode_from(Botan::BER_Decoder& from) { |
1743 | 1.42k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); |
1744 | 1.42k | if(next_tag != ASN1_Type::Sequence) { |
1745 | 79 | throw Decoding_Error(fmt("Unexpected type for IPAddressFamily {}", static_cast<uint32_t>(next_tag))); |
1746 | 79 | } |
1747 | | |
1748 | 1.34k | BER_Decoder seq_dec = from.start_sequence(); |
1749 | | |
1750 | 1.34k | std::vector<uint8_t> addr_family; |
1751 | 1.34k | seq_dec.decode(addr_family, ASN1_Type::OctetString); |
1752 | 1.34k | const size_t addr_family_length = addr_family.size(); |
1753 | | |
1754 | 1.34k | if(addr_family_length != 2 && addr_family_length != 3) { |
1755 | 13 | throw Decoding_Error("(S)AFI can only contain 2 or 3 bytes"); |
1756 | 13 | } |
1757 | | |
1758 | 1.33k | m_afi = (addr_family[0] << 8) | addr_family[1]; |
1759 | | |
1760 | 1.33k | if(addr_family_length == 3) { |
1761 | 867 | m_safi = addr_family[2]; |
1762 | 867 | } |
1763 | | |
1764 | 1.33k | if(m_afi == 1) { |
1765 | 532 | IPAddressChoice<Version::IPv4> addr_choice; |
1766 | 532 | seq_dec.decode(addr_choice); |
1767 | 532 | m_ip_addr_choice = addr_choice; |
1768 | 800 | } else if(m_afi == 2) { |
1769 | 576 | IPAddressChoice<Version::IPv6> addr_choice; |
1770 | 576 | seq_dec.decode(addr_choice); |
1771 | 576 | m_ip_addr_choice = addr_choice; |
1772 | 576 | } else { |
1773 | 224 | throw Decoding_Error("Only AFI IPv4 and IPv6 are supported."); |
1774 | 224 | } |
1775 | | |
1776 | 1.10k | seq_dec.end_cons(); |
1777 | 1.10k | } |
1778 | | |
1779 | 3 | void IPAddressBlocks::sort_and_merge() { |
1780 | | // Sort IPAddressFamilies by afi/safi values |
1781 | | // |
1782 | | // see: https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.3 |
1783 | | // |
1784 | | // v4 families are ordered before v6 families (i.e. they are sorted by afis, primarily), |
1785 | | // families with no safis are ordered before families with safis |
1786 | | // |
1787 | | // families with the same afi/safi combination are then merged |
1788 | | |
1789 | | // std::map is ordered, so using a pair (afi, optional(safi)) here works - std::nullopt is sorted before any actual values |
1790 | 3 | std::map<std::pair<uint16_t, std::optional<uint8_t>>, std::vector<IPAddressFamily>> afam_map; |
1791 | 3 | for(const IPAddressFamily& block : m_ip_addr_blocks) { |
1792 | 0 | auto key = std::make_pair(block.afi(), block.safi()); |
1793 | 0 | std::vector<IPAddressFamily>& fams = afam_map[key]; |
1794 | 0 | fams.push_back(block); |
1795 | 0 | } |
1796 | | |
1797 | 3 | std::vector<IPAddressFamily> merged_blocks; |
1798 | 3 | size_t v4_count = 0; |
1799 | 3 | size_t v6_count = 0; |
1800 | 3 | for(auto& it : afam_map) { |
1801 | | // fams consists of families with the same afi/safi combination |
1802 | 0 | std::vector<IPAddressFamily>& fams = it.second; |
1803 | | // since at least 1 block has to belong to a afi/safi combination for it to appear in the map, |
1804 | | // fams cannot be empty |
1805 | 0 | BOTAN_ASSERT_NOMSG(!fams.empty()); |
1806 | | |
1807 | | // fams[0] has to have the same choice type as the fams in the same bucket |
1808 | 0 | if(std::holds_alternative<IPAddressChoice<Version::IPv4>>(fams[0].addr_choice())) { |
1809 | 0 | merged_blocks.push_back(merge<Version::IPv4>(fams)); |
1810 | 0 | v4_count++; |
1811 | 0 | } else { |
1812 | 0 | merged_blocks.push_back(merge<Version::IPv6>(fams)); |
1813 | 0 | v6_count++; |
1814 | 0 | } |
1815 | 0 | } |
1816 | 3 | BOTAN_ASSERT_NOMSG(v4_count + v6_count == merged_blocks.size()); |
1817 | 3 | m_ip_addr_blocks = merged_blocks; |
1818 | 3 | m_v4_count = v4_count; |
1819 | 3 | m_v6_count = v6_count; |
1820 | 3 | } |
1821 | | |
1822 | | template <IPAddressBlocks::Version V> |
1823 | 0 | IPAddressBlocks::IPAddressFamily IPAddressBlocks::merge(std::vector<IPAddressFamily>& blocks) { |
1824 | | // Merge IPAddressFamilies that have the same afi/safi combination |
1825 | | // |
1826 | | // see: https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.3 |
1827 | |
|
1828 | 0 | BOTAN_ASSERT(!blocks.empty(), "Cannot merge an empty set of IP address blocks into a single family"); |
1829 | | |
1830 | | // nothing to merge |
1831 | 0 | if(blocks.size() == 1) { |
1832 | 0 | return blocks[0]; |
1833 | 0 | } |
1834 | | |
1835 | 0 | bool all_inherit = true; |
1836 | 0 | bool none_inherit = true; |
1837 | 0 | for(const IPAddressFamily& block : blocks) { |
1838 | 0 | const IPAddressChoice<V> choice = std::get<IPAddressChoice<V>>(block.addr_choice()); |
1839 | 0 | all_inherit = !choice.ranges().has_value() && all_inherit; // all the blocks have the 'inherit' value |
1840 | 0 | none_inherit = choice.ranges().has_value() && none_inherit; |
1841 | 0 | } |
1842 | | |
1843 | | // they are all 'inherit', short-circuit using default constructor for nullopt |
1844 | 0 | if(all_inherit) { |
1845 | 0 | return IPAddressFamily(IPAddressChoice<V>(), blocks[0].safi()); |
1846 | 0 | } |
1847 | | |
1848 | | // some are inherit, and some have values - no sensible way to merge them |
1849 | 0 | if(!all_inherit && !none_inherit) { |
1850 | 0 | throw Decoding_Error("Invalid IPAddressBlocks: Only one of 'inherit' or 'do not inherit' is allowed per family"); |
1851 | 0 | } |
1852 | | |
1853 | 0 | std::vector<IPAddressOrRange<V>> merged_ranges; |
1854 | 0 | for(const IPAddressFamily& block : blocks) { |
1855 | 0 | const IPAddressChoice<V> choice = std::get<IPAddressChoice<V>>(block.addr_choice()); |
1856 | 0 | const std::vector<IPAddressOrRange<V>> ranges = choice.ranges().value(); |
1857 | 0 | for(const IPAddressOrRange<V>& r : ranges) { |
1858 | 0 | merged_ranges.push_back(r); |
1859 | 0 | } |
1860 | 0 | } |
1861 | | |
1862 | | // we have extracted all the ranges, and now rely on the constructor of IPAddressChoice to merge them |
1863 | 0 | IPAddressChoice<V> choice(merged_ranges); |
1864 | 0 | IPAddressFamily fam(choice, blocks[0].safi()); |
1865 | 0 | return fam; |
1866 | 0 | } Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressFamily Botan::Cert_Extension::IPAddressBlocks::merge<(Botan::Cert_Extension::IPAddressBlocks::Version)4>(std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressFamily, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressFamily> >&) Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressFamily Botan::Cert_Extension::IPAddressBlocks::merge<(Botan::Cert_Extension::IPAddressBlocks::Version)16>(std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressFamily, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressFamily> >&) |
1867 | | |
1868 | | namespace { |
1869 | | |
1870 | | constexpr auto IPv4 = IPAddressBlocks::Version::IPv4; |
1871 | | constexpr auto IPv6 = IPAddressBlocks::Version::IPv6; |
1872 | | |
1873 | | template <IPAddressBlocks::Version V> |
1874 | | using IPRangeVec = std::vector<IPAddressBlocks::IPAddressOrRange<V>>; |
1875 | | |
1876 | | // (S)AFI -> (needs_check, ptr to IPRangeVec) |
1877 | | // the pointer can be null, in which case the boolean will be false, as such the pointer's value will never be looked at |
1878 | | template <IPAddressBlocks::Version V> |
1879 | | using IPValidationMap = std::map<uint32_t, std::pair<bool, const IPRangeVec<V>*>>; |
1880 | | |
1881 | | template <typename T> |
1882 | 1.90k | std::optional<std::vector<T>> sort_and_merge_ranges(std::optional<std::span<const T>> ranges) { |
1883 | | // Sort and merge overlapping/adjacent IPAddressOrRange or ASIdOrRange objects. |
1884 | | // cf. https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.6 and https://www.rfc-editor.org/rfc/rfc3779.html#section-3.2.3.4 |
1885 | | // This implementation uses only min-max ranges internally, so sorting by the prefix length is not necessary / impossible here. |
1886 | | |
1887 | 1.90k | if(!ranges.has_value()) { |
1888 | 0 | return std::nullopt; |
1889 | 0 | } |
1890 | | |
1891 | 1.90k | std::vector<T> sorted(ranges.value().begin(), ranges.value().end()); |
1892 | | |
1893 | 1.90k | if(sorted.empty()) { |
1894 | 73 | return sorted; |
1895 | 73 | } |
1896 | | |
1897 | | // sort by the min value |
1898 | 10.0k | std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); });x509_ext.cpp:Botan::Cert_Extension::(anonymous namespace)::sort_and_merge_ranges<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> >(std::__1::optional<std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> const, 18446744073709551615ul> >)::{lambda(Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>&, Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>&)#1}::operator()(Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>&, Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>&) constLine | Count | Source | 1898 | 1.12k | std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); }); |
x509_ext.cpp:Botan::Cert_Extension::(anonymous namespace)::sort_and_merge_ranges<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> >(std::__1::optional<std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> const, 18446744073709551615ul> >)::{lambda(Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>&, Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>&)#1}::operator()(Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>&, Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>&) constLine | Count | Source | 1898 | 1.48k | std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); }); |
x509_ext.cpp:Botan::Cert_Extension::(anonymous namespace)::sort_and_merge_ranges<Botan::Cert_Extension::ASBlocks::ASIdOrRange>(std::__1::optional<std::__1::span<Botan::Cert_Extension::ASBlocks::ASIdOrRange const, 18446744073709551615ul> >)::{lambda(Botan::Cert_Extension::ASBlocks::ASIdOrRange&, Botan::Cert_Extension::ASBlocks::ASIdOrRange&)#1}::operator()(Botan::Cert_Extension::ASBlocks::ASIdOrRange&, Botan::Cert_Extension::ASBlocks::ASIdOrRange&) constLine | Count | Source | 1898 | 7.48k | std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); }); |
|
1899 | | |
1900 | | // Single-pass merge: extend the last merged range or start a new one |
1901 | 1.83k | std::vector<T> merged; |
1902 | 1.83k | merged.reserve(sorted.size()); |
1903 | 1.83k | merged.push_back(sorted[0]); |
1904 | | |
1905 | 7.49k | for(size_t i = 1; i < sorted.size(); ++i) { |
1906 | 5.65k | auto& back = merged.back(); |
1907 | | // they either overlap or are adjacent |
1908 | 5.65k | if(sorted[i].min() <= back.max() || sorted[i].min() == (back.max() + 1)) { |
1909 | 996 | back = T(back.min(), std::max(back.max(), sorted[i].max())); |
1910 | 4.66k | } else { |
1911 | 4.66k | merged.push_back(sorted[i]); |
1912 | 4.66k | } |
1913 | 5.65k | } |
1914 | | |
1915 | 1.83k | return merged; |
1916 | 1.90k | } x509_ext.cpp:std::__1::optional<std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > > Botan::Cert_Extension::(anonymous namespace)::sort_and_merge_ranges<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> >(std::__1::optional<std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> const, 18446744073709551615ul> >) Line | Count | Source | 1882 | 241 | std::optional<std::vector<T>> sort_and_merge_ranges(std::optional<std::span<const T>> ranges) { | 1883 | | // Sort and merge overlapping/adjacent IPAddressOrRange or ASIdOrRange objects. | 1884 | | // cf. https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.6 and https://www.rfc-editor.org/rfc/rfc3779.html#section-3.2.3.4 | 1885 | | // This implementation uses only min-max ranges internally, so sorting by the prefix length is not necessary / impossible here. | 1886 | | | 1887 | 241 | if(!ranges.has_value()) { | 1888 | 0 | return std::nullopt; | 1889 | 0 | } | 1890 | | | 1891 | 241 | std::vector<T> sorted(ranges.value().begin(), ranges.value().end()); | 1892 | | | 1893 | 241 | if(sorted.empty()) { | 1894 | 29 | return sorted; | 1895 | 29 | } | 1896 | | | 1897 | | // sort by the min value | 1898 | 212 | std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); }); | 1899 | | | 1900 | | // Single-pass merge: extend the last merged range or start a new one | 1901 | 212 | std::vector<T> merged; | 1902 | 212 | merged.reserve(sorted.size()); | 1903 | 212 | merged.push_back(sorted[0]); | 1904 | | | 1905 | 846 | for(size_t i = 1; i < sorted.size(); ++i) { | 1906 | 634 | auto& back = merged.back(); | 1907 | | // they either overlap or are adjacent | 1908 | 634 | if(sorted[i].min() <= back.max() || sorted[i].min() == (back.max() + 1)) { | 1909 | 351 | back = T(back.min(), std::max(back.max(), sorted[i].max())); | 1910 | 351 | } else { | 1911 | 283 | merged.push_back(sorted[i]); | 1912 | 283 | } | 1913 | 634 | } | 1914 | | | 1915 | 212 | return merged; | 1916 | 241 | } |
x509_ext.cpp:std::__1::optional<std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > > Botan::Cert_Extension::(anonymous namespace)::sort_and_merge_ranges<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> >(std::__1::optional<std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> const, 18446744073709551615ul> >) Line | Count | Source | 1882 | 304 | std::optional<std::vector<T>> sort_and_merge_ranges(std::optional<std::span<const T>> ranges) { | 1883 | | // Sort and merge overlapping/adjacent IPAddressOrRange or ASIdOrRange objects. | 1884 | | // cf. https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.6 and https://www.rfc-editor.org/rfc/rfc3779.html#section-3.2.3.4 | 1885 | | // This implementation uses only min-max ranges internally, so sorting by the prefix length is not necessary / impossible here. | 1886 | | | 1887 | 304 | if(!ranges.has_value()) { | 1888 | 0 | return std::nullopt; | 1889 | 0 | } | 1890 | | | 1891 | 304 | std::vector<T> sorted(ranges.value().begin(), ranges.value().end()); | 1892 | | | 1893 | 304 | if(sorted.empty()) { | 1894 | 31 | return sorted; | 1895 | 31 | } | 1896 | | | 1897 | | // sort by the min value | 1898 | 273 | std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); }); | 1899 | | | 1900 | | // Single-pass merge: extend the last merged range or start a new one | 1901 | 273 | std::vector<T> merged; | 1902 | 273 | merged.reserve(sorted.size()); | 1903 | 273 | merged.push_back(sorted[0]); | 1904 | | | 1905 | 1.11k | for(size_t i = 1; i < sorted.size(); ++i) { | 1906 | 839 | auto& back = merged.back(); | 1907 | | // they either overlap or are adjacent | 1908 | 839 | if(sorted[i].min() <= back.max() || sorted[i].min() == (back.max() + 1)) { | 1909 | 349 | back = T(back.min(), std::max(back.max(), sorted[i].max())); | 1910 | 490 | } else { | 1911 | 490 | merged.push_back(sorted[i]); | 1912 | 490 | } | 1913 | 839 | } | 1914 | | | 1915 | 273 | return merged; | 1916 | 304 | } |
x509_ext.cpp:std::__1::optional<std::__1::vector<Botan::Cert_Extension::ASBlocks::ASIdOrRange, std::__1::allocator<Botan::Cert_Extension::ASBlocks::ASIdOrRange> > > Botan::Cert_Extension::(anonymous namespace)::sort_and_merge_ranges<Botan::Cert_Extension::ASBlocks::ASIdOrRange>(std::__1::optional<std::__1::span<Botan::Cert_Extension::ASBlocks::ASIdOrRange const, 18446744073709551615ul> >) Line | Count | Source | 1882 | 1.36k | std::optional<std::vector<T>> sort_and_merge_ranges(std::optional<std::span<const T>> ranges) { | 1883 | | // Sort and merge overlapping/adjacent IPAddressOrRange or ASIdOrRange objects. | 1884 | | // cf. https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.6 and https://www.rfc-editor.org/rfc/rfc3779.html#section-3.2.3.4 | 1885 | | // This implementation uses only min-max ranges internally, so sorting by the prefix length is not necessary / impossible here. | 1886 | | | 1887 | 1.36k | if(!ranges.has_value()) { | 1888 | 0 | return std::nullopt; | 1889 | 0 | } | 1890 | | | 1891 | 1.36k | std::vector<T> sorted(ranges.value().begin(), ranges.value().end()); | 1892 | | | 1893 | 1.36k | if(sorted.empty()) { | 1894 | 13 | return sorted; | 1895 | 13 | } | 1896 | | | 1897 | | // sort by the min value | 1898 | 1.35k | std::sort(sorted.begin(), sorted.end(), [](T& a, T& b) { return a.min() < b.min(); }); | 1899 | | | 1900 | | // Single-pass merge: extend the last merged range or start a new one | 1901 | 1.35k | std::vector<T> merged; | 1902 | 1.35k | merged.reserve(sorted.size()); | 1903 | 1.35k | merged.push_back(sorted[0]); | 1904 | | | 1905 | 5.53k | for(size_t i = 1; i < sorted.size(); ++i) { | 1906 | 4.18k | auto& back = merged.back(); | 1907 | | // they either overlap or are adjacent | 1908 | 4.18k | if(sorted[i].min() <= back.max() || sorted[i].min() == (back.max() + 1)) { | 1909 | 296 | back = T(back.min(), std::max(back.max(), sorted[i].max())); | 1910 | 3.89k | } else { | 1911 | 3.89k | merged.push_back(sorted[i]); | 1912 | 3.89k | } | 1913 | 4.18k | } | 1914 | | | 1915 | 1.35k | return merged; | 1916 | 1.36k | } |
|
1917 | | |
1918 | | template <typename T> |
1919 | 0 | bool validate_subject_in_issuer(std::span<const T> subject, std::span<const T> issuer) { |
1920 | | // ensures that the subject ranges are enclosed by the issuer ranges |
1921 | | // both vectors are already sorted, so we can do this in O(n+m) |
1922 | | |
1923 | | // the issuer has 0 ranges to validate against, so this can only work if the subject also has none |
1924 | 0 | if(issuer.empty()) { |
1925 | 0 | return subject.empty(); |
1926 | 0 | } |
1927 | 0 | for(auto subj = subject.begin(), issu = issuer.begin(); subj != subject.end();) { |
1928 | | // the issuer range is smaller than the subject range, step to the next issuer range to check next round |
1929 | 0 | if(subj->min() > issu->max()) { |
1930 | 0 | issu++; |
1931 | | // we have run out of issuer ranges, but still have subject ranges left to validate |
1932 | 0 | if(issu == issuer.end() && subj != subject.end()) { |
1933 | 0 | return false; |
1934 | 0 | } |
1935 | 0 | } else { |
1936 | | // the subject is outside of the closest issuer range on the left (min) side |
1937 | 0 | if(subj->min() < issu->min()) { |
1938 | 0 | return false; |
1939 | 0 | } |
1940 | | // the subject is outside of the closest issuer range on the right (max) side |
1941 | 0 | if(subj->max() > issu->max()) { |
1942 | 0 | return false; |
1943 | 0 | } |
1944 | | // this range is contained within the issuer, advance to the next subject range |
1945 | 0 | subj++; |
1946 | 0 | } |
1947 | 0 | } |
1948 | 0 | return true; |
1949 | 0 | } Unexecuted instantiation: x509_ext.cpp:bool Botan::Cert_Extension::(anonymous namespace)::validate_subject_in_issuer<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> >(std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> const, 18446744073709551615ul>, std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> const, 18446744073709551615ul>) Unexecuted instantiation: x509_ext.cpp:bool Botan::Cert_Extension::(anonymous namespace)::validate_subject_in_issuer<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> >(std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> const, 18446744073709551615ul>, std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> const, 18446744073709551615ul>) Unexecuted instantiation: x509_ext.cpp:bool Botan::Cert_Extension::(anonymous namespace)::validate_subject_in_issuer<Botan::Cert_Extension::ASBlocks::ASIdOrRange>(std::__1::span<Botan::Cert_Extension::ASBlocks::ASIdOrRange const, 18446744073709551615ul>, std::__1::span<Botan::Cert_Extension::ASBlocks::ASIdOrRange const, 18446744073709551615ul>) |
1950 | | |
1951 | | template <IPAddressBlocks::Version V> |
1952 | | void populate_validation_map(uint32_t afam, |
1953 | | const IPAddressBlocks::IPAddressFamily::AddrChoice& choice, |
1954 | 0 | IPValidationMap<V>& map) { |
1955 | 0 | const std::optional<IPRangeVec<V>>& ranges = std::get<IPAddressBlocks::IPAddressChoice<V>>(choice).ranges(); |
1956 | 0 | const bool has_value = ranges.has_value(); |
1957 | 0 | const IPRangeVec<V>* value = has_value ? &ranges.value() : nullptr; |
1958 | 0 | map.emplace(afam, std::make_pair(has_value, std::move(value))); |
1959 | 0 | } Unexecuted instantiation: x509_ext.cpp:void Botan::Cert_Extension::(anonymous namespace)::populate_validation_map<(Botan::Cert_Extension::IPAddressBlocks::Version)4>(unsigned int, std::__1::variant<Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > const&, std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > >&) Unexecuted instantiation: x509_ext.cpp:void Botan::Cert_Extension::(anonymous namespace)::populate_validation_map<(Botan::Cert_Extension::IPAddressBlocks::Version)16>(unsigned int, std::__1::variant<Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > const&, std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > >&) |
1960 | | |
1961 | | std::pair<IPValidationMap<IPv4>, IPValidationMap<IPv6>> create_validation_map( |
1962 | 0 | const std::vector<IPAddressBlocks::IPAddressFamily>& addr_blocks) { |
1963 | 0 | IPValidationMap<IPv4> v4_map; |
1964 | 0 | IPValidationMap<IPv6> v6_map; |
1965 | |
|
1966 | 0 | for(const IPAddressBlocks::IPAddressFamily& block : addr_blocks) { |
1967 | 0 | uint32_t afam = block.afi(); |
1968 | 0 | if(block.safi().has_value()) { |
1969 | 0 | afam = static_cast<uint32_t>(afam << 8) | block.safi().value(); |
1970 | 0 | } |
1971 | |
|
1972 | 0 | const IPAddressBlocks::IPAddressFamily::AddrChoice& a_choice = block.addr_choice(); |
1973 | 0 | if(std::holds_alternative<IPAddressBlocks::IPAddressChoice<IPv4>>(a_choice)) { |
1974 | 0 | populate_validation_map(afam, a_choice, v4_map); |
1975 | 0 | } else { |
1976 | 0 | populate_validation_map(afam, a_choice, v6_map); |
1977 | 0 | } |
1978 | 0 | } |
1979 | |
|
1980 | 0 | return std::make_pair(v4_map, v6_map); |
1981 | 0 | } |
1982 | | |
1983 | | } // namespace |
1984 | | |
1985 | | template <IPAddressBlocks::Version V> |
1986 | | IPAddressBlocks::IPAddressChoice<V>::IPAddressChoice( |
1987 | 0 | std::optional<std::span<const IPAddressBlocks::IPAddressOrRange<V>>> ranges) { |
1988 | | // NOLINTNEXTLINE(*-prefer-member-initializer) |
1989 | 0 | m_ip_addr_ranges = sort_and_merge_ranges<IPAddressOrRange<V>>(ranges); |
1990 | 0 | } Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)4>::IPAddressChoice(std::__1::optional<std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> const, 18446744073709551615ul> >) Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)16>::IPAddressChoice(std::__1::optional<std::__1::span<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> const, 18446744073709551615ul> >) |
1991 | | |
1992 | | template <IPAddressBlocks::Version V> |
1993 | 0 | void IPAddressBlocks::IPAddressChoice<V>::encode_into(Botan::DER_Encoder& into) const { |
1994 | 0 | if(m_ip_addr_ranges.has_value()) { |
1995 | 0 | into.start_sequence().encode_list(m_ip_addr_ranges.value()).end_cons(); |
1996 | 0 | } else { |
1997 | 0 | into.encode_null(); |
1998 | 0 | } |
1999 | 0 | } Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)4>::encode_into(Botan::DER_Encoder&) const Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)16>::encode_into(Botan::DER_Encoder&) const |
2000 | | |
2001 | | template <IPAddressBlocks::Version V> |
2002 | 1.10k | void IPAddressBlocks::IPAddressChoice<V>::decode_from(Botan::BER_Decoder& from) { |
2003 | 1.10k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); |
2004 | | |
2005 | 1.10k | if(next_tag == ASN1_Type::Null) { |
2006 | 51 | from.decode_null(); |
2007 | 51 | m_ip_addr_ranges = std::nullopt; |
2008 | 1.05k | } else if(next_tag == ASN1_Type::Sequence) { |
2009 | 899 | std::vector<IPAddressOrRange<V>> ip_ranges; |
2010 | 899 | from.decode_list(ip_ranges); |
2011 | 899 | m_ip_addr_ranges = sort_and_merge_ranges<IPAddressOrRange<V>>(ip_ranges); |
2012 | 899 | } else { |
2013 | 158 | throw Decoding_Error(fmt("Unexpected type for IPAddressChoice {}", static_cast<uint32_t>(next_tag))); |
2014 | 158 | } |
2015 | 1.10k | } Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)4>::decode_from(Botan::BER_Decoder&) Line | Count | Source | 2002 | 532 | void IPAddressBlocks::IPAddressChoice<V>::decode_from(Botan::BER_Decoder& from) { | 2003 | 532 | const ASN1_Type next_tag = from.peek_next_object().type_tag(); | 2004 | | | 2005 | 532 | if(next_tag == ASN1_Type::Null) { | 2006 | 23 | from.decode_null(); | 2007 | 23 | m_ip_addr_ranges = std::nullopt; | 2008 | 509 | } else if(next_tag == ASN1_Type::Sequence) { | 2009 | 413 | std::vector<IPAddressOrRange<V>> ip_ranges; | 2010 | 413 | from.decode_list(ip_ranges); | 2011 | 413 | m_ip_addr_ranges = sort_and_merge_ranges<IPAddressOrRange<V>>(ip_ranges); | 2012 | 413 | } else { | 2013 | 96 | throw Decoding_Error(fmt("Unexpected type for IPAddressChoice {}", static_cast<uint32_t>(next_tag))); | 2014 | 96 | } | 2015 | 532 | } |
Botan::Cert_Extension::IPAddressBlocks::IPAddressChoice<(Botan::Cert_Extension::IPAddressBlocks::Version)16>::decode_from(Botan::BER_Decoder&) Line | Count | Source | 2002 | 576 | void IPAddressBlocks::IPAddressChoice<V>::decode_from(Botan::BER_Decoder& from) { | 2003 | 576 | const ASN1_Type next_tag = from.peek_next_object().type_tag(); | 2004 | | | 2005 | 576 | if(next_tag == ASN1_Type::Null) { | 2006 | 28 | from.decode_null(); | 2007 | 28 | m_ip_addr_ranges = std::nullopt; | 2008 | 548 | } else if(next_tag == ASN1_Type::Sequence) { | 2009 | 486 | std::vector<IPAddressOrRange<V>> ip_ranges; | 2010 | 486 | from.decode_list(ip_ranges); | 2011 | 486 | m_ip_addr_ranges = sort_and_merge_ranges<IPAddressOrRange<V>>(ip_ranges); | 2012 | 486 | } else { | 2013 | 62 | throw Decoding_Error(fmt("Unexpected type for IPAddressChoice {}", static_cast<uint32_t>(next_tag))); | 2014 | 62 | } | 2015 | 576 | } |
|
2016 | | |
2017 | | template <IPAddressBlocks::Version V> |
2018 | 0 | void IPAddressBlocks::IPAddressOrRange<V>::encode_into(Botan::DER_Encoder& into) const { |
2019 | | // Compress IPAddressOrRange as much as possible |
2020 | | // cf. https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.7 - https://www.rfc-editor.org/rfc/rfc3779.html#section-2.2.3.9 |
2021 | | // |
2022 | | // If possible encode as a prefix x.x.x.x/x, else encode as a range of min-max. |
2023 | | // Single addresses are encoded as is (technically a /32 or /128 prefix). |
2024 | | // |
2025 | | // A range can be encoded as a prefix if the lowest n bits of the min address are 0 |
2026 | | // and the highest n bits of the max address are 1, or in other words, contiguous sequences of 0s and 1s are omitted. |
2027 | | // To make reconstruction possible, an 'unused' octet is included at the start, since in the case of e.g. /25 only |
2028 | | // the highest bit of the last octet is actually meaningful. |
2029 | | // |
2030 | | // If encoding requires a range, the individual elements can still be compressed using the above method, |
2031 | | // but the number of used bits varies between them. |
2032 | |
|
2033 | 0 | const size_t version_octets = static_cast<size_t>(V); |
2034 | |
|
2035 | 0 | std::array<uint8_t, version_octets> min = m_min.value(); |
2036 | 0 | std::array<uint8_t, version_octets> max = m_max.value(); |
2037 | |
|
2038 | 0 | uint8_t zeros = 0; |
2039 | 0 | uint8_t ones = 0; |
2040 | |
|
2041 | 0 | bool zeros_done = false; |
2042 | 0 | bool ones_done = false; |
2043 | | |
2044 | | // count contiguous 0s/1s from the right of the min/max addresses |
2045 | 0 | for(size_t i = version_octets; i > 0; i--) { |
2046 | 0 | if(!zeros_done) { |
2047 | 0 | const uint8_t local_zeros = static_cast<uint8_t>(std::countr_zero(min[i - 1])); |
2048 | 0 | zeros += local_zeros; |
2049 | 0 | zeros_done = (local_zeros != 8); |
2050 | 0 | } |
2051 | |
|
2052 | 0 | if(!ones_done) { |
2053 | 0 | const uint8_t local_ones = static_cast<uint8_t>(std::countr_one(max[i - 1])); |
2054 | 0 | ones += local_ones; |
2055 | 0 | ones_done = (local_ones != 8); |
2056 | 0 | } |
2057 | |
|
2058 | 0 | if(zeros_done && ones_done) { |
2059 | 0 | break; |
2060 | 0 | } |
2061 | 0 | } |
2062 | | |
2063 | | // the part we want to compress |
2064 | 0 | const uint8_t host = std::min(zeros, ones); |
2065 | | |
2066 | | // these we can outright drop |
2067 | 0 | const uint8_t discarded_octets = host / 8; |
2068 | | // in a partially used octet |
2069 | 0 | const uint8_t unused_bits = host % 8; |
2070 | |
|
2071 | 0 | bool octets_match = true; |
2072 | 0 | bool used_bits_match = true; |
2073 | | |
2074 | | // we have octets to check |
2075 | 0 | if(discarded_octets < version_octets) { |
2076 | | // check all but the last octet |
2077 | 0 | for(size_t i = 0; i < static_cast<uint8_t>(version_octets - discarded_octets - 1); i++) { |
2078 | 0 | if(min[i] != max[i]) { |
2079 | 0 | octets_match = false; |
2080 | 0 | break; |
2081 | 0 | } |
2082 | 0 | } |
2083 | | // check the last significant octet if we have matched so far |
2084 | 0 | if(octets_match) { |
2085 | 0 | const uint8_t shifted_min = (min[version_octets - 1 - discarded_octets] >> unused_bits); |
2086 | 0 | const uint8_t shifted_max = (max[version_octets - 1 - discarded_octets] >> unused_bits); |
2087 | 0 | used_bits_match = (shifted_min == shifted_max); |
2088 | 0 | } |
2089 | 0 | } |
2090 | | |
2091 | | // both the full octets and the partially used one match |
2092 | 0 | if(octets_match && used_bits_match) { |
2093 | | // at this point the range can be encoded as a prefix |
2094 | 0 | into.encode_bitstring(std::span{min}.first(version_octets - discarded_octets), unused_bits); |
2095 | 0 | } else { |
2096 | 0 | const uint8_t discarded_octets_min = zeros / 8; |
2097 | 0 | const uint8_t unused_bits_min = zeros % 8; |
2098 | |
|
2099 | 0 | const uint8_t discarded_octets_max = ones / 8; |
2100 | 0 | const uint8_t unused_bits_max = ones % 8; |
2101 | | |
2102 | | // compress the max address by setting unused bits to 0, for the min address these are already 0 |
2103 | 0 | if(unused_bits_max != 0) { |
2104 | 0 | BOTAN_ASSERT_NOMSG(discarded_octets_max < version_octets); |
2105 | 0 | max[version_octets - 1 - discarded_octets_max] >>= unused_bits_max; |
2106 | 0 | max[version_octets - 1 - discarded_octets_max] <<= unused_bits_max; |
2107 | 0 | } |
2108 | |
|
2109 | 0 | into.start_sequence() |
2110 | 0 | .encode_bitstring(std::span{min}.first(version_octets - discarded_octets_min), unused_bits_min) |
2111 | 0 | .encode_bitstring(std::span{max}.first(version_octets - discarded_octets_max), unused_bits_max) |
2112 | 0 | .end_cons(); |
2113 | 0 | } |
2114 | 0 | } Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>::encode_into(Botan::DER_Encoder&) const Unexecuted instantiation: Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>::encode_into(Botan::DER_Encoder&) const |
2115 | | |
2116 | | template <IPAddressBlocks::Version V> |
2117 | 2.56k | void IPAddressBlocks::IPAddressOrRange<V>::decode_from(Botan::BER_Decoder& from) { |
2118 | 2.56k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); |
2119 | | |
2120 | | // this can either be a prefix or a single address |
2121 | 2.56k | if(next_tag == ASN1_Type::BitString) { |
2122 | | // construct a min and a max address from the prefix |
2123 | | |
2124 | 2.08k | ASN1_BitString prefix; |
2125 | 2.08k | from.decode_bitstring(prefix); |
2126 | | |
2127 | | // min address gets filled with 0's |
2128 | 2.08k | m_min = decode_single_address(prefix, true); |
2129 | | // max address with 1's |
2130 | 2.08k | m_max = decode_single_address(prefix, false); |
2131 | 2.08k | } else if(next_tag == ASN1_Type::Sequence) { |
2132 | | // this is a range |
2133 | | |
2134 | 257 | ASN1_BitString addr_min; |
2135 | 257 | ASN1_BitString addr_max; |
2136 | | |
2137 | 257 | from.start_sequence().decode_bitstring(addr_min).decode_bitstring(addr_max).end_cons(); |
2138 | | |
2139 | 257 | m_min = decode_single_address(addr_min, true); |
2140 | 257 | m_max = decode_single_address(addr_max, false); |
2141 | | |
2142 | 257 | if(m_min > m_max) { |
2143 | 18 | throw Decoding_Error("IP address ranges must be sorted."); |
2144 | 18 | } |
2145 | 257 | } else { |
2146 | 225 | throw Decoding_Error(fmt("Unexpected type for IPAddressOrRange {}", static_cast<uint32_t>(next_tag))); |
2147 | 225 | } |
2148 | 2.56k | } Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>::decode_from(Botan::BER_Decoder&) Line | Count | Source | 2117 | 1.12k | void IPAddressBlocks::IPAddressOrRange<V>::decode_from(Botan::BER_Decoder& from) { | 2118 | 1.12k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); | 2119 | | | 2120 | | // this can either be a prefix or a single address | 2121 | 1.12k | if(next_tag == ASN1_Type::BitString) { | 2122 | | // construct a min and a max address from the prefix | 2123 | | | 2124 | 859 | ASN1_BitString prefix; | 2125 | 859 | from.decode_bitstring(prefix); | 2126 | | | 2127 | | // min address gets filled with 0's | 2128 | 859 | m_min = decode_single_address(prefix, true); | 2129 | | // max address with 1's | 2130 | 859 | m_max = decode_single_address(prefix, false); | 2131 | 859 | } else if(next_tag == ASN1_Type::Sequence) { | 2132 | | // this is a range | 2133 | | | 2134 | 156 | ASN1_BitString addr_min; | 2135 | 156 | ASN1_BitString addr_max; | 2136 | | | 2137 | 156 | from.start_sequence().decode_bitstring(addr_min).decode_bitstring(addr_max).end_cons(); | 2138 | | | 2139 | 156 | m_min = decode_single_address(addr_min, true); | 2140 | 156 | m_max = decode_single_address(addr_max, false); | 2141 | | | 2142 | 156 | if(m_min > m_max) { | 2143 | 11 | throw Decoding_Error("IP address ranges must be sorted."); | 2144 | 11 | } | 2145 | 156 | } else { | 2146 | 106 | throw Decoding_Error(fmt("Unexpected type for IPAddressOrRange {}", static_cast<uint32_t>(next_tag))); | 2147 | 106 | } | 2148 | 1.12k | } |
Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>::decode_from(Botan::BER_Decoder&) Line | Count | Source | 2117 | 1.44k | void IPAddressBlocks::IPAddressOrRange<V>::decode_from(Botan::BER_Decoder& from) { | 2118 | 1.44k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); | 2119 | | | 2120 | | // this can either be a prefix or a single address | 2121 | 1.44k | if(next_tag == ASN1_Type::BitString) { | 2122 | | // construct a min and a max address from the prefix | 2123 | | | 2124 | 1.22k | ASN1_BitString prefix; | 2125 | 1.22k | from.decode_bitstring(prefix); | 2126 | | | 2127 | | // min address gets filled with 0's | 2128 | 1.22k | m_min = decode_single_address(prefix, true); | 2129 | | // max address with 1's | 2130 | 1.22k | m_max = decode_single_address(prefix, false); | 2131 | 1.22k | } else if(next_tag == ASN1_Type::Sequence) { | 2132 | | // this is a range | 2133 | | | 2134 | 101 | ASN1_BitString addr_min; | 2135 | 101 | ASN1_BitString addr_max; | 2136 | | | 2137 | 101 | from.start_sequence().decode_bitstring(addr_min).decode_bitstring(addr_max).end_cons(); | 2138 | | | 2139 | 101 | m_min = decode_single_address(addr_min, true); | 2140 | 101 | m_max = decode_single_address(addr_max, false); | 2141 | | | 2142 | 101 | if(m_min > m_max) { | 2143 | 7 | throw Decoding_Error("IP address ranges must be sorted."); | 2144 | 7 | } | 2145 | 119 | } else { | 2146 | 119 | throw Decoding_Error(fmt("Unexpected type for IPAddressOrRange {}", static_cast<uint32_t>(next_tag))); | 2147 | 119 | } | 2148 | 1.44k | } |
|
2149 | | |
2150 | | template <IPAddressBlocks::Version V> |
2151 | | IPAddressBlocks::IPAddress<V> IPAddressBlocks::IPAddressOrRange<V>::decode_single_address(const ASN1_BitString& decoded, |
2152 | 4.47k | bool min) { |
2153 | 4.47k | const size_t version_octets = static_cast<size_t>(V); |
2154 | | |
2155 | | // decode a single address according to https://datatracker.ietf.org/doc/html/rfc3779#section-2.1.1 and following |
2156 | | |
2157 | 4.47k | if(decoded.bytes().size() > version_octets) { |
2158 | 15 | throw Decoding_Error(fmt("IP address range entries must have a length between 0 and {} bytes.", version_octets)); |
2159 | 15 | } |
2160 | | |
2161 | 4.46k | const uint8_t unused = static_cast<uint8_t>(decoded.unused_bits()); |
2162 | 4.46k | const uint8_t discarded_octets = version_octets - static_cast<uint8_t>(decoded.bytes().size()); |
2163 | | |
2164 | 4.46k | std::vector<uint8_t> address(decoded.bytes().begin(), decoded.bytes().end()); |
2165 | | |
2166 | 4.46k | if(address.empty() && unused != 0) { |
2167 | 0 | throw Decoding_Error("IP address range entry specified unused bits, but did not provide any octets."); |
2168 | 0 | } |
2169 | | |
2170 | | // pad to version length with 0's for min addresses, 255's (0xff) for max addresses |
2171 | 4.46k | const uint8_t fill_discarded = min ? 0 : 0xff; |
2172 | 39.0k | for(size_t i = 0; i < discarded_octets; i++) { |
2173 | 34.5k | address.push_back(fill_discarded); |
2174 | 34.5k | } |
2175 | | |
2176 | | // for min addresses they should already be 0, but we set them to zero regardless |
2177 | | // for max addresses this turns the unused bits to 1 |
2178 | 12.6k | for(size_t i = 0; i < unused; i++) { |
2179 | 8.15k | if(min) { |
2180 | 3.90k | address[version_octets - 1 - discarded_octets] &= ~(1 << i); |
2181 | 4.24k | } else { |
2182 | 4.24k | address[version_octets - 1 - discarded_octets] |= (1 << i); |
2183 | 4.24k | } |
2184 | 8.15k | } |
2185 | | |
2186 | 4.46k | return IPAddressBlocks::IPAddress<V>(address); |
2187 | 4.46k | } Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>::decode_single_address(Botan::ASN1_BitString const&, bool) Line | Count | Source | 2152 | 1.93k | bool min) { | 2153 | 1.93k | const size_t version_octets = static_cast<size_t>(V); | 2154 | | | 2155 | | // decode a single address according to https://datatracker.ietf.org/doc/html/rfc3779#section-2.1.1 and following | 2156 | | | 2157 | 1.93k | if(decoded.bytes().size() > version_octets) { | 2158 | 10 | throw Decoding_Error(fmt("IP address range entries must have a length between 0 and {} bytes.", version_octets)); | 2159 | 10 | } | 2160 | | | 2161 | 1.92k | const uint8_t unused = static_cast<uint8_t>(decoded.unused_bits()); | 2162 | 1.92k | const uint8_t discarded_octets = version_octets - static_cast<uint8_t>(decoded.bytes().size()); | 2163 | | | 2164 | 1.92k | std::vector<uint8_t> address(decoded.bytes().begin(), decoded.bytes().end()); | 2165 | | | 2166 | 1.92k | if(address.empty() && unused != 0) { | 2167 | 0 | throw Decoding_Error("IP address range entry specified unused bits, but did not provide any octets."); | 2168 | 0 | } | 2169 | | | 2170 | | // pad to version length with 0's for min addresses, 255's (0xff) for max addresses | 2171 | 1.92k | const uint8_t fill_discarded = min ? 0 : 0xff; | 2172 | 4.47k | for(size_t i = 0; i < discarded_octets; i++) { | 2173 | 2.55k | address.push_back(fill_discarded); | 2174 | 2.55k | } | 2175 | | | 2176 | | // for min addresses they should already be 0, but we set them to zero regardless | 2177 | | // for max addresses this turns the unused bits to 1 | 2178 | 5.31k | for(size_t i = 0; i < unused; i++) { | 2179 | 3.39k | if(min) { | 2180 | 1.59k | address[version_octets - 1 - discarded_octets] &= ~(1 << i); | 2181 | 1.80k | } else { | 2182 | 1.80k | address[version_octets - 1 - discarded_octets] |= (1 << i); | 2183 | 1.80k | } | 2184 | 3.39k | } | 2185 | | | 2186 | 1.92k | return IPAddressBlocks::IPAddress<V>(address); | 2187 | 1.92k | } |
Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>::decode_single_address(Botan::ASN1_BitString const&, bool) Line | Count | Source | 2152 | 2.54k | bool min) { | 2153 | 2.54k | const size_t version_octets = static_cast<size_t>(V); | 2154 | | | 2155 | | // decode a single address according to https://datatracker.ietf.org/doc/html/rfc3779#section-2.1.1 and following | 2156 | | | 2157 | 2.54k | if(decoded.bytes().size() > version_octets) { | 2158 | 5 | throw Decoding_Error(fmt("IP address range entries must have a length between 0 and {} bytes.", version_octets)); | 2159 | 5 | } | 2160 | | | 2161 | 2.54k | const uint8_t unused = static_cast<uint8_t>(decoded.unused_bits()); | 2162 | 2.54k | const uint8_t discarded_octets = version_octets - static_cast<uint8_t>(decoded.bytes().size()); | 2163 | | | 2164 | 2.54k | std::vector<uint8_t> address(decoded.bytes().begin(), decoded.bytes().end()); | 2165 | | | 2166 | 2.54k | if(address.empty() && unused != 0) { | 2167 | 0 | throw Decoding_Error("IP address range entry specified unused bits, but did not provide any octets."); | 2168 | 0 | } | 2169 | | | 2170 | | // pad to version length with 0's for min addresses, 255's (0xff) for max addresses | 2171 | 2.54k | const uint8_t fill_discarded = min ? 0 : 0xff; | 2172 | 34.5k | for(size_t i = 0; i < discarded_octets; i++) { | 2173 | 32.0k | address.push_back(fill_discarded); | 2174 | 32.0k | } | 2175 | | | 2176 | | // for min addresses they should already be 0, but we set them to zero regardless | 2177 | | // for max addresses this turns the unused bits to 1 | 2178 | 7.29k | for(size_t i = 0; i < unused; i++) { | 2179 | 4.75k | if(min) { | 2180 | 2.30k | address[version_octets - 1 - discarded_octets] &= ~(1 << i); | 2181 | 2.44k | } else { | 2182 | 2.44k | address[version_octets - 1 - discarded_octets] |= (1 << i); | 2183 | 2.44k | } | 2184 | 4.75k | } | 2185 | | | 2186 | 2.54k | return IPAddressBlocks::IPAddress<V>(address); | 2187 | 2.54k | } |
|
2188 | | |
2189 | | template <IPAddressBlocks::Version V> |
2190 | 4.46k | IPAddressBlocks::IPAddress<V>::IPAddress(std::span<const uint8_t> v) { |
2191 | 4.46k | if(v.size() != Length) { |
2192 | 0 | throw Decoding_Error("number of bytes does not match IP version used"); |
2193 | 0 | } |
2194 | | |
2195 | 52.7k | for(size_t i = 0; i < Length; i++) { |
2196 | 48.3k | m_value[i] = v[i]; |
2197 | 48.3k | } |
2198 | 4.46k | } Botan::Cert_Extension::IPAddressBlocks::IPAddress<(Botan::Cert_Extension::IPAddressBlocks::Version)4>::IPAddress(std::__1::span<unsigned char const, 18446744073709551615ul>) Line | Count | Source | 2190 | 1.92k | IPAddressBlocks::IPAddress<V>::IPAddress(std::span<const uint8_t> v) { | 2191 | 1.92k | if(v.size() != Length) { | 2192 | 0 | throw Decoding_Error("number of bytes does not match IP version used"); | 2193 | 0 | } | 2194 | | | 2195 | 9.60k | for(size_t i = 0; i < Length; i++) { | 2196 | 7.68k | m_value[i] = v[i]; | 2197 | 7.68k | } | 2198 | 1.92k | } |
Botan::Cert_Extension::IPAddressBlocks::IPAddress<(Botan::Cert_Extension::IPAddressBlocks::Version)16>::IPAddress(std::__1::span<unsigned char const, 18446744073709551615ul>) Line | Count | Source | 2190 | 2.54k | IPAddressBlocks::IPAddress<V>::IPAddress(std::span<const uint8_t> v) { | 2191 | 2.54k | if(v.size() != Length) { | 2192 | 0 | throw Decoding_Error("number of bytes does not match IP version used"); | 2193 | 0 | } | 2194 | | | 2195 | 43.1k | for(size_t i = 0; i < Length; i++) { | 2196 | 40.6k | m_value[i] = v[i]; | 2197 | 40.6k | } | 2198 | 2.54k | } |
|
2199 | | |
2200 | | void IPAddressBlocks::validate(const X509_Certificate& /* unused */, |
2201 | | const std::optional<X509_Certificate>& /* unused */, |
2202 | | const std::vector<X509_Certificate>& cert_path, |
2203 | | std::vector<std::set<Certificate_Status_Code>>& cert_status, |
2204 | 0 | size_t pos) const { |
2205 | | // maps in the form of (s)afi -> (needs_checking, ranges) |
2206 | 0 | auto [v4_needs_check, v6_needs_check] = create_validation_map(m_ip_addr_blocks); |
2207 | |
|
2208 | 0 | if(pos == cert_path.size() - 1) { |
2209 | | // checks if any range / family has 'inherit' as a value somewhere, not allowed for the root cert |
2210 | 0 | auto validate_root_cert_ext = [&](const auto& map) { |
2211 | | // check if any range has a value of 'false', indicating 'inherit' |
2212 | 0 | return std::any_of(map.begin(), map.end(), [&](const auto& it) { |
2213 | 0 | const auto& [_1, validation_info] = it; |
2214 | 0 | const auto& [needs_checking, _2] = validation_info; |
2215 | 0 | return !needs_checking; |
2216 | 0 | }); Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_0::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > >(std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > const&) constUnexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_0::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > >(std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > const&) const |
2217 | 0 | }; Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_0::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > const&) const Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_0::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > const&) const |
2218 | 0 | if(validate_root_cert_ext(v4_needs_check) || validate_root_cert_ext(v6_needs_check)) { |
2219 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); |
2220 | 0 | } |
2221 | 0 | return; |
2222 | 0 | } |
2223 | | |
2224 | | // traverse the chain until we find a cert with concrete values for the extension (so not 'inherit') |
2225 | 0 | for(auto cert_path_it = cert_path.begin() + pos + 1; cert_path_it != cert_path.end(); cert_path_it++) { |
2226 | 0 | const IPAddressBlocks* const parent_ip = cert_path_it->v3_extensions().get_extension_object_as<IPAddressBlocks>(); |
2227 | | // extension not present for parent |
2228 | 0 | if(parent_ip == nullptr) { |
2229 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); |
2230 | 0 | return; |
2231 | 0 | } |
2232 | 0 | auto [issuer_v4, issuer_v6] = create_validation_map(parent_ip->addr_blocks()); |
2233 | |
|
2234 | 0 | auto validate_against_issuer = [&](auto& subject_map, const auto& issuer_map) { |
2235 | 0 | for(auto map_it = subject_map.begin(); map_it != subject_map.end(); map_it++) { |
2236 | 0 | auto& [afam, validation_info] = *map_it; |
2237 | | |
2238 | | // the issuer does not have this combination of afi/safi |
2239 | 0 | if(issuer_map.count(afam) == 0) { |
2240 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); |
2241 | 0 | return false; |
2242 | 0 | } |
2243 | | |
2244 | 0 | auto& [needs_check, subject_value] = validation_info; |
2245 | 0 | const auto& [issuer_has_value, issuer_value] = issuer_map.at(afam); |
2246 | 0 | BOTAN_ASSERT_NOMSG(!needs_check || subject_value != nullptr); |
2247 | 0 | BOTAN_ASSERT_NOMSG(!issuer_has_value || issuer_value != nullptr); |
2248 | | |
2249 | | // we still need to check this range and the issuer has an actual value for it (so not 'inherit') |
2250 | 0 | if(needs_check && issuer_has_value) { |
2251 | 0 | if(!validate_subject_in_issuer(std::span(*subject_value), std::span(*issuer_value))) { |
2252 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::IPADDR_BLOCKS_ERROR); |
2253 | 0 | return false; |
2254 | 0 | } |
2255 | 0 | needs_check = false; |
2256 | 0 | } |
2257 | 0 | } |
2258 | 0 | return true; |
2259 | 0 | }; Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_1::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > >, std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > >&, std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > const&) const Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_1::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > >, std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > >&, std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > const&) const |
2260 | |
|
2261 | 0 | if(!validate_against_issuer(v4_needs_check, issuer_v4) || !validate_against_issuer(v6_needs_check, issuer_v6)) { |
2262 | 0 | return; |
2263 | 0 | } |
2264 | | |
2265 | 0 | auto validate_no_checks_left = [&](const auto& map) { |
2266 | | // check if all ranges have been checked, either by comparing their ranges if they have any, |
2267 | | // or if they are inherit, their parent(s) will be validated later |
2268 | 0 | return std::all_of(map.begin(), map.end(), [&](const auto& it) { |
2269 | 0 | const auto& [_1, validation_info] = it; |
2270 | 0 | const auto& [needs_checking, _2] = validation_info; |
2271 | 0 | return !needs_checking; |
2272 | 0 | }); Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_2::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > >(std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > const&) constUnexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_2::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > >(std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > const&) const |
2273 | 0 | }; Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_2::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)4> > > const*> > > > const&) const Unexecuted instantiation: x509_ext.cpp:auto Botan::Cert_Extension::IPAddressBlocks::validate(Botan::X509_Certificate const&, std::__1::optional<Botan::X509_Certificate> const&, std::__1::vector<Botan::X509_Certificate, std::__1::allocator<Botan::X509_Certificate> > const&, std::__1::vector<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> >, std::__1::allocator<std::__1::set<Botan::Certificate_Status_Code, std::__1::less<Botan::Certificate_Status_Code>, std::__1::allocator<Botan::Certificate_Status_Code> > > >&, unsigned long) const::$_2::operator()<std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > >(std::__1::map<unsigned int, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*>, std::__1::less<unsigned int>, std::__1::allocator<std::__1::pair<unsigned int const, std::__1::pair<bool, std::__1::vector<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16>, std::__1::allocator<Botan::Cert_Extension::IPAddressBlocks::IPAddressOrRange<(Botan::Cert_Extension::IPAddressBlocks::Version)16> > > const*> > > > const&) const |
2274 | |
|
2275 | 0 | if(validate_no_checks_left(v4_needs_check) && validate_no_checks_left(v6_needs_check)) { |
2276 | | // we've validated what we need to and can stop traversing the cert chain |
2277 | 0 | return; |
2278 | 0 | } |
2279 | 0 | } |
2280 | 0 | } |
2281 | | |
2282 | | template class IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv4>; |
2283 | | template class IPAddressBlocks::IPAddress<IPAddressBlocks::Version::IPv6>; |
2284 | | template class IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv4>; |
2285 | | template class IPAddressBlocks::IPAddressOrRange<IPAddressBlocks::Version::IPv6>; |
2286 | | template class IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv4>; |
2287 | | template class IPAddressBlocks::IPAddressChoice<IPAddressBlocks::Version::IPv6>; |
2288 | | |
2289 | 0 | std::vector<uint8_t> ASBlocks::encode_inner() const { |
2290 | 0 | std::vector<uint8_t> output; |
2291 | 0 | DER_Encoder(output).encode(m_as_identifiers); |
2292 | 0 | return output; |
2293 | 0 | } |
2294 | | |
2295 | 2.03k | void ASBlocks::decode_inner(const std::vector<uint8_t>& in) { |
2296 | | /* RFC 3779 Section 3.2.3.1 - ASIdentifiers ::= SEQUENCE { ... } */ |
2297 | 2.03k | BER_Decoder(in, BER_Decoder::Limits::DER()).decode(m_as_identifiers).verify_end(); |
2298 | 2.03k | } |
2299 | | |
2300 | 0 | ASBlocks::ASIdentifierChoice ASBlocks::add_new(const std::optional<ASIdentifierChoice>& old, asnum_t min, asnum_t max) { |
2301 | 0 | std::vector<ASIdOrRange> range; |
2302 | 0 | if(!old.has_value() || !old.value().ranges().has_value()) { |
2303 | 0 | range = {ASIdOrRange(min, max)}; |
2304 | 0 | } else { |
2305 | 0 | range = old.value().ranges().value(); |
2306 | 0 | range.push_back(ASIdOrRange(min, max)); |
2307 | 0 | } |
2308 | 0 | return ASIdentifierChoice(range); |
2309 | 0 | } |
2310 | | |
2311 | 0 | void ASBlocks::ASIdentifiers::encode_into(Botan::DER_Encoder& into) const { |
2312 | 0 | into.start_sequence(); |
2313 | |
|
2314 | 0 | if(!m_asnum.has_value() && !m_rdi.has_value()) { |
2315 | 0 | throw Encoding_Error("One of asnum, rdi must be present"); |
2316 | 0 | } |
2317 | | |
2318 | 0 | if(m_asnum.has_value()) { |
2319 | 0 | into.start_explicit(0); |
2320 | 0 | into.encode(m_asnum.value()); |
2321 | 0 | into.end_explicit(); |
2322 | 0 | } |
2323 | |
|
2324 | 0 | if(m_rdi.has_value()) { |
2325 | 0 | into.start_explicit(1); |
2326 | 0 | into.encode(m_rdi.value()); |
2327 | 0 | into.end_explicit(); |
2328 | 0 | } |
2329 | |
|
2330 | 0 | into.end_cons(); |
2331 | 0 | } |
2332 | | |
2333 | 2.03k | void ASBlocks::ASIdentifiers::decode_from(Botan::BER_Decoder& from) { |
2334 | 2.03k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); |
2335 | 2.03k | if(next_tag != ASN1_Type::Sequence) { |
2336 | 120 | throw Decoding_Error(fmt("Unexpected type for ASIdentifiers {}", static_cast<uint32_t>(next_tag))); |
2337 | 120 | } |
2338 | | |
2339 | 1.91k | BER_Decoder seq_dec = from.start_sequence(); |
2340 | | |
2341 | 1.91k | const BER_Object elem_obj = seq_dec.get_next_object(); |
2342 | 1.91k | const uint32_t elem_type_tag = static_cast<uint32_t>(elem_obj.type_tag()); |
2343 | | |
2344 | | // asnum, potentially followed by an rdi |
2345 | 1.91k | if(elem_type_tag == 0) { |
2346 | 882 | BER_Decoder as_obj_ber = BER_Decoder(elem_obj, seq_dec.limits()); |
2347 | 882 | ASIdentifierChoice asnum; |
2348 | 882 | as_obj_ber.decode(asnum).verify_end(); |
2349 | 882 | m_asnum = asnum; |
2350 | | |
2351 | 882 | const BER_Object rdi_obj = seq_dec.get_next_object(); |
2352 | 882 | const ASN1_Type rdi_type_tag = rdi_obj.type_tag(); |
2353 | 882 | if(static_cast<uint32_t>(rdi_type_tag) == 1) { |
2354 | 96 | BER_Decoder rdi_obj_ber = BER_Decoder(rdi_obj, seq_dec.limits()); |
2355 | 96 | ASIdentifierChoice rdi; |
2356 | 96 | rdi_obj_ber.decode(rdi).verify_end(); |
2357 | 96 | m_rdi = rdi; |
2358 | 786 | } else if(rdi_type_tag != ASN1_Type::NoObject) { |
2359 | 102 | throw Decoding_Error(fmt("Unexpected type for ASIdentifiers rdi: {}", static_cast<uint32_t>(rdi_type_tag))); |
2360 | 102 | } |
2361 | 882 | } |
2362 | | |
2363 | | // just an rdi |
2364 | 1.81k | if(elem_type_tag == 1) { |
2365 | 890 | BER_Decoder rdi_obj_ber = BER_Decoder(elem_obj, seq_dec.limits()); |
2366 | 890 | ASIdentifierChoice rdi; |
2367 | 890 | rdi_obj_ber.decode(rdi).verify_end(); |
2368 | 890 | m_rdi = rdi; |
2369 | 890 | const BER_Object end = seq_dec.get_next_object(); |
2370 | 890 | const ASN1_Type end_type_tag = end.type_tag(); |
2371 | 890 | if(end_type_tag != ASN1_Type::NoObject) { |
2372 | 129 | throw Decoding_Error( |
2373 | 129 | fmt("Unexpected element with type {} in ASIdentifiers", static_cast<uint32_t>(end_type_tag))); |
2374 | 129 | } |
2375 | 890 | } |
2376 | | |
2377 | 1.68k | seq_dec.end_cons(); |
2378 | | |
2379 | 1.68k | if(!m_asnum.has_value() && !m_rdi.has_value()) { |
2380 | 63 | throw Decoding_Error("Invalid encoding for ASIdentifiers"); |
2381 | 63 | } |
2382 | 1.68k | } |
2383 | | |
2384 | 0 | void ASBlocks::ASIdentifierChoice::encode_into(Botan::DER_Encoder& into) const { |
2385 | 0 | if(m_as_ranges.has_value()) { |
2386 | 0 | into.start_sequence().encode_list(m_as_ranges.value()).end_cons(); |
2387 | 0 | } else { |
2388 | 0 | into.encode_null(); |
2389 | 0 | } |
2390 | 0 | } |
2391 | | |
2392 | 0 | ASBlocks::ASIdentifierChoice::ASIdentifierChoice(const std::optional<std::vector<ASIdOrRange>>& ranges) { |
2393 | 0 | m_as_ranges = sort_and_merge_ranges<ASIdOrRange>(ranges); |
2394 | 0 | } |
2395 | | |
2396 | 1.86k | void ASBlocks::ASIdentifierChoice::decode_from(Botan::BER_Decoder& from) { |
2397 | 1.86k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); |
2398 | | |
2399 | 1.86k | if(next_tag == ASN1_Type::Null) { |
2400 | 27 | from.decode_null(); |
2401 | 27 | m_as_ranges = std::nullopt; |
2402 | 1.84k | } else if(next_tag == ASN1_Type::Sequence) { |
2403 | 1.66k | std::vector<ASIdOrRange> as_ranges; |
2404 | 1.66k | from.decode_list(as_ranges); |
2405 | | |
2406 | 1.66k | m_as_ranges = sort_and_merge_ranges<ASIdOrRange>(as_ranges); |
2407 | 1.66k | } else { |
2408 | 176 | throw Decoding_Error(fmt("Unexpected type for ASIdentifierChoice {}", static_cast<uint32_t>(next_tag))); |
2409 | 176 | } |
2410 | 1.86k | } |
2411 | | |
2412 | 0 | void ASBlocks::ASIdOrRange::encode_into(Botan::DER_Encoder& into) const { |
2413 | 0 | if(m_min == m_max) { |
2414 | 0 | into.encode(static_cast<size_t>(m_min)); |
2415 | 0 | } else { |
2416 | 0 | if(m_min >= m_max) { |
2417 | 0 | throw Encoding_Error("AS range numbers must be sorted"); |
2418 | 0 | } |
2419 | 0 | into.start_sequence().encode(static_cast<size_t>(m_min)).encode(static_cast<size_t>(m_max)).end_cons(); |
2420 | 0 | } |
2421 | 0 | } |
2422 | | |
2423 | 6.27k | void ASBlocks::ASIdOrRange::decode_from(BER_Decoder& from) { |
2424 | 6.27k | const ASN1_Type next_tag = from.peek_next_object().type_tag(); |
2425 | | |
2426 | 6.27k | size_t min = 0; |
2427 | 6.27k | size_t max = 0; |
2428 | | |
2429 | 6.27k | if(next_tag == ASN1_Type::Integer) { |
2430 | 5.83k | from.decode(min); |
2431 | 5.83k | m_min = checked_cast_to<asnum_t>(min); |
2432 | 5.83k | m_max = m_min; |
2433 | 5.83k | } else if(next_tag == ASN1_Type::Sequence) { |
2434 | 223 | from.start_sequence().decode(min).decode(max).end_cons(); |
2435 | 223 | m_min = checked_cast_to<asnum_t>(min); |
2436 | 223 | m_max = checked_cast_to<asnum_t>(max); |
2437 | 223 | if(m_min >= m_max) { |
2438 | 8 | throw Decoding_Error("ASIdOrRange has min greater than max"); |
2439 | 8 | } |
2440 | 223 | } else { |
2441 | 220 | throw Decoding_Error(fmt("Unexpected type for ASIdOrRange {}", static_cast<uint32_t>(next_tag))); |
2442 | 220 | } |
2443 | 6.27k | } |
2444 | | |
2445 | | void ASBlocks::validate(const X509_Certificate& /* unused */, |
2446 | | const std::optional<X509_Certificate>& /* unused */, |
2447 | | const std::vector<X509_Certificate>& cert_path, |
2448 | | std::vector<std::set<Certificate_Status_Code>>& cert_status, |
2449 | 0 | size_t pos) const { |
2450 | | // the extension may not contain asnums or rdis, but one of them is always present |
2451 | 0 | const bool asnum_present = m_as_identifiers.asnum().has_value(); |
2452 | 0 | const bool rdi_present = m_as_identifiers.rdi().has_value(); |
2453 | |
|
2454 | 0 | if(!asnum_present && !rdi_present) { |
2455 | | // Invalid, should have been caught during decoding |
2456 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); |
2457 | 0 | return; |
2458 | 0 | } |
2459 | | |
2460 | 0 | bool asnum_needs_check = asnum_present ? m_as_identifiers.asnum().value().ranges().has_value() : false; |
2461 | 0 | bool rdi_needs_check = rdi_present ? m_as_identifiers.rdi().value().ranges().has_value() : false; |
2462 | | |
2463 | | // we are at the (trusted) root cert, there is no parent to verify against |
2464 | 0 | if(pos == cert_path.size() - 1) { |
2465 | | // asnum / rdi is present, but has 'inherit' value, but there is nothing to inherit from |
2466 | 0 | if((asnum_present && !asnum_needs_check) || (rdi_present && !rdi_needs_check)) { |
2467 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); |
2468 | 0 | } |
2469 | 0 | return; |
2470 | 0 | } |
2471 | | |
2472 | | // traverse the chain until we find a cert with concrete values for the extension (so not 'inherit') |
2473 | 0 | for(auto it = cert_path.begin() + pos + 1; it != cert_path.end(); it++) { |
2474 | 0 | const ASBlocks* const parent_as = it->v3_extensions().get_extension_object_as<ASBlocks>(); |
2475 | | // no extension at all or no asnums or no rdis (if needed) |
2476 | 0 | if(parent_as == nullptr || (asnum_present && !parent_as->as_identifiers().asnum().has_value()) || |
2477 | 0 | (rdi_present && !parent_as->as_identifiers().rdi().has_value())) { |
2478 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); |
2479 | 0 | return; |
2480 | 0 | } |
2481 | 0 | const auto as_identifiers = parent_as->as_identifiers(); |
2482 | | |
2483 | | // only something to validate if the subject does not have 'inherit' as a value |
2484 | 0 | if(asnum_needs_check && as_identifiers.asnum().value().ranges().has_value()) { |
2485 | 0 | const std::vector<ASBlocks::ASIdOrRange>& subject_asnums = m_as_identifiers.asnum()->ranges().value(); |
2486 | 0 | const std::vector<ASBlocks::ASIdOrRange>& issuer_asnums = as_identifiers.asnum()->ranges().value(); |
2487 | |
|
2488 | 0 | if(!validate_subject_in_issuer<ASBlocks::ASIdOrRange>(subject_asnums, issuer_asnums)) { |
2489 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); |
2490 | 0 | return; |
2491 | 0 | } |
2492 | | // successfully validated the asnums, but we may need to step further for rdis |
2493 | 0 | asnum_needs_check = false; |
2494 | 0 | } |
2495 | | |
2496 | 0 | if(rdi_needs_check && as_identifiers.rdi().value().ranges().has_value()) { |
2497 | 0 | const std::vector<ASBlocks::ASIdOrRange>& subject_rdis = m_as_identifiers.rdi()->ranges().value(); |
2498 | 0 | const std::vector<ASBlocks::ASIdOrRange>& issuer_rdis = as_identifiers.rdi()->ranges().value(); |
2499 | |
|
2500 | 0 | if(!validate_subject_in_issuer<ASBlocks::ASIdOrRange>(subject_rdis, issuer_rdis)) { |
2501 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::AS_BLOCKS_ERROR); |
2502 | 0 | return; |
2503 | 0 | } |
2504 | | // successfully validated the rdis, but we may need to step further for asnums |
2505 | 0 | rdi_needs_check = false; |
2506 | 0 | } |
2507 | | |
2508 | 0 | if(!asnum_needs_check && !rdi_needs_check) { |
2509 | | // we've validated what we need to and can stop traversing the cert chain |
2510 | 0 | return; |
2511 | 0 | } |
2512 | 0 | } |
2513 | 0 | } |
2514 | | |
2515 | | void OCSP_NoCheck::validate(const X509_Certificate& subject, |
2516 | | const std::optional<X509_Certificate>& /*issuer*/, |
2517 | | const std::vector<X509_Certificate>& /*cert_path*/, |
2518 | | std::vector<std::set<Certificate_Status_Code>>& cert_status, |
2519 | 0 | size_t pos) const { |
2520 | | /* |
2521 | | * RFC 6960 is not particularly explicit about when id-pkix-ocsp-nocheck can |
2522 | | * or cannot be included in a certificate, but reasonably we should require |
2523 | | * that id-pkix-ocsp-nocheck is only included for certificates that are marked |
2524 | | * as OCSP responders. This checks for compatible key usage and also the OCSP |
2525 | | * signer extended key usage. |
2526 | | */ |
2527 | 0 | if(!subject.allowed_usage(Usage_Type::OCSP_RESPONDER)) { |
2528 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::INVALID_OCSP_NOCHECK); |
2529 | 0 | } |
2530 | 0 | } |
2531 | | |
2532 | 0 | std::vector<uint8_t> OCSP_NoCheck::encode_inner() const { |
2533 | 0 | return {0x05, 0x00}; // NULL |
2534 | 0 | } |
2535 | | |
2536 | 6 | void OCSP_NoCheck::decode_inner(const std::vector<uint8_t>& buf) { |
2537 | | /* RFC 6960 Section 4.2.2.2.1 - id-pkix-ocsp-nocheck (value SHALL be NULL) */ |
2538 | 6 | BER_Decoder(buf, BER_Decoder::Limits::DER()).decode_null().verify_end(); |
2539 | 6 | } |
2540 | | |
2541 | 0 | std::vector<uint8_t> NoRevocationAvailable::encode_inner() const { |
2542 | 0 | return {0x05, 0x00}; // NULL |
2543 | 0 | } |
2544 | | |
2545 | 56 | void NoRevocationAvailable::decode_inner(const std::vector<uint8_t>& buf) { |
2546 | | // RFC 9608 Section 2, it's just a NULL |
2547 | 56 | BER_Decoder(buf, BER_Decoder::Limits::DER()).decode_null().verify_end(); |
2548 | 56 | } |
2549 | | |
2550 | | void NoRevocationAvailable::validate(const X509_Certificate& subject, |
2551 | | const std::optional<X509_Certificate>& /*issuer*/, |
2552 | | const std::vector<X509_Certificate>& /*cert_path*/, |
2553 | | std::vector<std::set<Certificate_Status_Code>>& cert_status, |
2554 | 0 | size_t pos) const { |
2555 | | // RFC 9608 Section 2: |
2556 | | // This extension MUST NOT be present in CA public key certificates. |
2557 | | // |
2558 | | // RFC 9608 Section 3: |
2559 | | // Certificates that include the noRevAvail extension MUST NOT include |
2560 | | // certificate extensions that point to CRL repositories or provide |
2561 | | // locations of OCSP responders. |
2562 | | // |
2563 | | // Additionally (and unusually) the requirements of RFC 9608 Section 3 |
2564 | | // are not just on issuing parties but also on verifiers: |
2565 | | // |
2566 | | // If any of the above are violated in a certificate, then the relying |
2567 | | // party MUST consider the certificate invalid. |
2568 | |
|
2569 | 0 | const Extensions& exts = subject.v3_extensions(); |
2570 | |
|
2571 | 0 | if(const auto* bc = exts.get_extension_object_as<Basic_Constraints>(); bc != nullptr && bc->is_ca()) { |
2572 | | // RFC 9608 Section 3: |
2573 | | // The certificate MUST NOT also include the basic constraints |
2574 | | // certificate extension with the cA BOOLEAN set to TRUE |
2575 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::NO_REV_AVAIL_INVALID_USE); |
2576 | 0 | } |
2577 | | |
2578 | | // RFC 9608 Section 3: |
2579 | | // The certificate MUST NOT also include the CRL Distribution Points |
2580 | | // certificate extension |
2581 | 0 | if(exts.extension_set(CRL_Distribution_Points::static_oid())) { |
2582 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::NO_REV_AVAIL_INVALID_USE); |
2583 | 0 | } |
2584 | | |
2585 | | // RFC 9608 Section 3: |
2586 | | // The certificate MUST NOT also include the Freshest CRL certificate |
2587 | | // extension |
2588 | 0 | if(exts.extension_set(OID({2, 5, 29, 46}))) { |
2589 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::NO_REV_AVAIL_INVALID_USE); |
2590 | 0 | } |
2591 | | |
2592 | | // RFC 9608 Section 3: |
2593 | | // The Authority Information Access certificate extension, if |
2594 | | // present, MUST NOT include an id-ad-ocsp accessMethod |
2595 | | // |
2596 | | // Walk the raw AccessDescription list rather than the URI-only typed |
2597 | | // accessor so a non-URI OCSP accessLocation also triggers the rejection. |
2598 | 0 | if(const auto* aia = exts.get_extension_object_as<Authority_Information_Access>(); aia != nullptr) { |
2599 | 0 | const OID id_ad_ocsp = OID::from_string("PKIX.OCSP"); |
2600 | 0 | const bool has_ocsp = !aia->ocsp_responder_uris().empty() || |
2601 | 0 | std::ranges::any_of(aia->access_descriptions(), |
2602 | 0 | [&](const auto& ad) { return ad.access_method() == id_ad_ocsp; }); |
2603 | 0 | if(has_ocsp) { |
2604 | 0 | cert_status.at(pos).insert(Certificate_Status_Code::NO_REV_AVAIL_INVALID_USE); |
2605 | 0 | } |
2606 | 0 | } |
2607 | 0 | } |
2608 | | |
2609 | 0 | std::vector<uint8_t> Unknown_Extension::encode_inner() const { |
2610 | 0 | return m_bytes; |
2611 | 0 | } |
2612 | | |
2613 | 46.5k | void Unknown_Extension::decode_inner(const std::vector<uint8_t>& bytes) { |
2614 | | // Just treat as an opaque blob at this level |
2615 | 46.5k | m_bytes = bytes; |
2616 | 46.5k | } |
2617 | | |
2618 | | } // namespace Cert_Extension |
2619 | | |
2620 | | } // namespace Botan |