• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 21240376859

22 Jan 2026 07:50AM UTC coverage: 90.071% (-0.004%) from 90.075%
21240376859

push

github

web-flow
Merge pull request #5254 from Rohde-Schwarz/chore/tls_does_not_depend_on_secp

Avoid hard dependency on `pcurves_secp*` in the TLS module

102101 of 113356 relevant lines covered (90.07%)

11524547.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.2
/src/tests/test_x509_rpki.cpp
1
/*
2
* (C) 2025 Jack Lloyd
3
* (C) 2025 Anton Einax, Dominik Schricker
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_X509_CERTIFICATES)
11
   #include <botan/certstor.h>
12
   #include <botan/pk_algs.h>
13
   #include <botan/pubkey.h>
14
   #include <botan/rng.h>
15
   #include <botan/x509_ca.h>
16
   #include <botan/x509_ext.h>
17
   #include <botan/x509path.h>
18
   #include <botan/x509self.h>
19
   #include <botan/internal/calendar.h>
20
#endif
21

22
#if defined(BOTAN_HAS_ECC_GROUP)
23
   #include <botan/ec_group.h>
24
#endif
25

26
namespace Botan_Tests {
27

28
namespace {
29

30
#if defined(BOTAN_HAS_X509_CERTIFICATES) && \
31
   (defined(BOTAN_HAS_ECDSA) || defined(BOTAN_HAS_RSA) || defined(BOTAN_HAS_ED25519))
32

33
struct CA_Creation_Result {
34
      Botan::X509_Certificate ca_cert;
35
      Botan::X509_CA ca;
36
      std::unique_ptr<Botan::Private_Key> sub_key;
37
      std::string sig_algo;
38
      std::string hash_fn;
39
};
40

41
Botan::X509_Time from_date(const int y, const int m, const int d) {
2,728✔
42
   const size_t this_year = Botan::calendar_point(std::chrono::system_clock::now()).year();
2,728✔
43

44
   const Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
2,728✔
45
   return Botan::X509_Time(t.to_std_timepoint());
2,728✔
46
}
47

48
Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "") {
149✔
49
   Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
149✔
50

51
   opts.uri = "https://botan.randombit.net";
149✔
52
   opts.dns = "botan.randombit.net";
149✔
53
   opts.email = "testing@randombit.net";
149✔
54
   opts.set_padding_scheme(sig_padding);
149✔
55

56
   opts.CA_key(1);
149✔
57

58
   return opts;
149✔
59
}
×
60

61
Botan::X509_Cert_Options req_opts(const std::string& algo, const std::string& sig_padding = "") {
1,276✔
62
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
1,276✔
63

64
   opts.uri = "https://botan.randombit.net";
1,276✔
65
   opts.dns = "botan.randombit.net";
1,276✔
66
   opts.email = "testing@randombit.net";
1,276✔
67
   opts.set_padding_scheme(sig_padding);
1,276✔
68

69
   opts.not_before("160101200000Z");
1,276✔
70
   opts.not_after("300101200000Z");
1,276✔
71

72
   opts.challenge = "zoom";
1,276✔
73

74
   if(algo == "RSA") {
1,276✔
75
      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
×
76
   } else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") {
1,276✔
77
      opts.constraints = Botan::Key_Constraints::DigitalSignature;
1,276✔
78
   }
79

80
   return opts;
1,276✔
81
}
×
82

83
std::tuple<std::string, std::string, std::string, std::unique_ptr<Botan::Private_Key>>
84
get_sig_algo_padding_and_generate_key(Botan::RandomNumberGenerator& rng) {
149✔
85
   const auto generate_key = [&](const std::string& alg_name, const std::string& params = "") {
298✔
86
      auto key = Botan::create_private_key(alg_name, rng, params);
149✔
87
      if(!key) {
149✔
88
         throw Test_Error("Could not generate key for algorithm " + alg_name);
×
89
      }
90
      return key;
149✔
91
   };
149✔
92

93
   #if defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_SHA2_32)
94
   // Try to find a group that is supported in this build
95
   const auto group_name = Test::supported_ec_group_name();
149✔
96
   if(group_name) {
149✔
97
      return {"ECDSA", "", "SHA-256", generate_key("ECDSA", *group_name)};
298✔
98
   }
99
   #endif
100

101
   #if defined(BOTAN_HAS_ED25519)
102
   return {"Ed25519", "", "SHA-512", generate_key("Ed25519")};
×
103
   #elif defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
104
   return {"RSA", "PKCS1v15(SHA-256)", "SHA-256", generate_key("RSA", "1536")};
105
   #else
106
   throw Test_Error("No suitable signature algorithm available in this build");
107
   #endif
108
}
149✔
109

110
Botan::X509_Certificate make_self_signed(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
5✔
111
                                         const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
112
   auto [sig_algo, padding_method, hash_fn, key] = get_sig_algo_padding_and_generate_key(*rng);
5✔
113
   return Botan::X509::create_self_signed_cert(opts, *key, hash_fn, *rng);
5✔
114
}
5✔
115

116
CA_Creation_Result make_ca(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
68✔
117
                           const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
118
   auto [sig_algo, padding_method, hash_fn, ca_key] = get_sig_algo_padding_and_generate_key(*rng);
68✔
119
   const auto ca_cert = Botan::X509::create_self_signed_cert(opts, *ca_key, hash_fn, *rng);
68✔
120
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
68✔
121
   auto sub_key = ca_key->generate_another(*rng);
68✔
122

123
   return CA_Creation_Result{ca_cert, std::move(ca), std::move(sub_key), sig_algo, hash_fn};
204✔
124
}
68✔
125

126
std::pair<Botan::X509_Certificate, Botan::X509_CA> make_and_sign_ca(
76✔
127
   std::unique_ptr<Botan::Certificate_Extension> ext,
128
   Botan::X509_CA& parent_ca,
129
   std::unique_ptr<Botan::RandomNumberGenerator>& rng) {
130
   auto [sig_algo, padding_method, hash_fn, key] = get_sig_algo_padding_and_generate_key(*rng);
76✔
131

132
   Botan::X509_Cert_Options opts = ca_opts();
76✔
133
   opts.extensions.add(std::move(ext));
76✔
134

135
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, hash_fn, *rng);
76✔
136
   Botan::X509_Certificate cert = parent_ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
76✔
137
   Botan::X509_CA ca(cert, *key, hash_fn, padding_method, *rng);
76✔
138

139
   return std::make_pair(std::move(cert), std::move(ca));
76✔
140
}
76✔
141

142
constexpr auto IPv4 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv4;
143
constexpr auto IPv6 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv6;
144

145
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
146

147
Test::Result test_x509_ip_addr_blocks_extension_decode() {
1✔
148
   Test::Result result("X509 IP Address Block decode");
1✔
149
   result.start_timer();
1✔
150
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
151

152
   {
1✔
153
      const std::string filename("IPAddrBlocksAll.pem");
1✔
154
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
155
      const auto* ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
156

157
      const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
158
      result.confirm("cert has IPAddrBlocks extension", ip_addr_blocks != nullptr, true);
2✔
159
      result.test_eq("cert has two IpAddrBlocks", addr_blocks.size(), 2);
1✔
160

161
      const auto& ipv4block = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[0].addr_choice());
1✔
162
      const auto& ipv6block = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[1].addr_choice());
1✔
163

164
      const auto& v4_blocks = ipv4block.ranges().value();
1✔
165

166
      // cert contains (in this order)
167
      // 192.168.0.0 - 192.168.127.255 (192.168.0.0/17)
168
      // 193.168.0.0 - 193.169.255.255 (193.168.0.0/15)
169
      // 194.168.0.0 - 195.175.1.2
170
      // 196.168.0.1 - 196.168.0.1 (196.168.0.1/32)
171

172
      result.test_eq("ipv4 block 0 min", v4_blocks[0].min().value(), {192, 168, 0, 0});
2✔
173
      result.test_eq("ipv4 block 0 max", v4_blocks[0].max().value(), {192, 168, 127, 255});
2✔
174

175
      result.test_eq("ipv4 block 1 min", v4_blocks[1].min().value(), {193, 168, 0, 0});
2✔
176
      result.test_eq("ipv4 block 1 max", v4_blocks[1].max().value(), {193, 169, 255, 255});
2✔
177
      result.test_eq("ipv4 block 2 min", v4_blocks[2].min().value(), {194, 168, 0, 0});
2✔
178
      result.test_eq("ipv4 block 2 max", v4_blocks[2].max().value(), {195, 175, 1, 2});
2✔
179

180
      result.test_eq("ipv4 block 3 min", v4_blocks[3].min().value(), {196, 168, 0, 1});
2✔
181
      result.test_eq("ipv4 block 3 max", v4_blocks[3].max().value(), {196, 168, 0, 1});
2✔
182

183
      const auto& v6_blocks = ipv6block.ranges().value();
1✔
184

185
      // cert contains (in this order)
186
      // fa80::/65
187
      // fe20::/37
188
      // 2003:0:6829:3435:420:10c5:0:c4/128
189
      // ab01:0:0:0:0:0:0:1-cd02:0:0:0:0:0:0:2
190

191
      result.test_eq("ipv6 block 0 min",
2✔
192
                     v6_blocks[0].min().value(),
1✔
193
                     {0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4});
194
      result.test_eq("ipv6 block 0 max",
2✔
195
                     v6_blocks[0].max().value(),
1✔
196
                     {0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4});
197
      result.test_eq("ipv6 block 1 min",
2✔
198
                     v6_blocks[1].min().value(),
1✔
199
                     {0xab, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01});
200
      result.test_eq("ipv6 block 1 max",
2✔
201
                     v6_blocks[1].max().value(),
1✔
202
                     {0xcd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02});
203
      result.test_eq("ipv6 block 2 min",
2✔
204
                     v6_blocks[2].min().value(),
1✔
205
                     {0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
206
      result.test_eq("ipv6 block 2 max",
2✔
207
                     v6_blocks[2].max().value(),
1✔
208
                     {0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
209
      result.test_eq("ipv6 block 3 min",
2✔
210
                     v6_blocks[3].min().value(),
1✔
211
                     {0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
212
      result.test_eq("ipv6 block 3 max",
2✔
213
                     v6_blocks[3].max().value(),
1✔
214
                     {0xfe, 0x20, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
215
   }
1✔
216
   {
1✔
217
      const std::string filename("IPAddrBlocksUnsorted.pem");
1✔
218
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
219
      const auto* ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
220

221
      // cert contains (in this order)
222
      // IPv6 (1) inherit
223
      // IPv6 0xff....0xff
224
      // IPv4 (2) inherit
225
      // IPv4 (1) 192.168.0.0 - 192.168.2.1
226
      // IPv4 (1) 192.168.2.2 - 200.0.0.0
227
      // IPv4 inherit
228

229
      // IPv4 ranges should be merged, IPv4 should come before IPv6, all should be sorted by safi
230

231
      const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
232
      result.test_eq("cert has two IpAddrBlocks", addr_blocks.size(), 5);
1✔
233

234
      result.test_eq("block 0 has no safi", addr_blocks[0].safi(), std::optional<uint8_t>{std::nullopt});
1✔
235
      result.confirm(
2✔
236
         "block 0 is inherited",
237
         !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[0].addr_choice()).ranges().has_value());
1✔
238

239
      result.test_eq("block 1 has correct safi", addr_blocks[1].safi(), std::optional<uint8_t>{1});
1✔
240
      const auto& block_1 =
1✔
241
         std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[1].addr_choice()).ranges().value();
1✔
242

243
      result.confirm("block 1 has correct size", block_1.size() == 1);
2✔
244
      result.test_eq("block 1 min is correct", block_1[0].min().value(), {192, 168, 0, 0});
2✔
245
      result.test_eq("block 1 max is correct", block_1[0].max().value(), {200, 0, 0, 0});
2✔
246

247
      result.test_eq("block 2 has correct safi", addr_blocks[2].safi(), std::optional<uint8_t>{2});
1✔
248
      result.confirm(
2✔
249
         "block 2 is inherited",
250
         !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[2].addr_choice()).ranges().has_value());
1✔
251

252
      result.test_eq("block 3 has no safi", addr_blocks[3].safi(), std::optional<uint8_t>{std::nullopt});
1✔
253
      const auto& block_3 =
1✔
254
         std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[3].addr_choice()).ranges().value();
1✔
255

256
      result.confirm("block 3 has correct size", block_3.size() == 1);
2✔
257
      result.test_eq("block 3 min is correct",
2✔
258
                     block_3[0].min().value(),
1✔
259
                     {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
260
      result.test_eq("block 3 max is correct",
2✔
261
                     block_3[0].max().value(),
1✔
262
                     {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
263

264
      result.test_eq("block 24 has correct safi", addr_blocks[4].safi(), std::optional<uint8_t>{1});
1✔
265
      result.confirm(
2✔
266
         "block 4 is inherited",
267
         !std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[4].addr_choice()).ranges().has_value());
1✔
268
   }
1✔
269
   {
1✔
270
      const std::string filename("InvalidIPAddrBlocks.pem");
1✔
271
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
272

273
      // cert contains the 10.0.32.0/20 prefix, but with a 9 for the unused bits
274

275
      result.confirm("extension is present", cert.v3_extensions().extension_set(IPAddressBlocks::static_oid()));
2✔
276

277
      const auto* ext = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
278
      result.confirm("extension is not decoded", ext == nullptr);
2✔
279
   }
1✔
280

281
   result.end_timer();
1✔
282
   return result;
1✔
283
}
×
284

285
Test::Result test_x509_as_blocks_extension_decode() {
1✔
286
   Test::Result result("X509 AS Block decode");
1✔
287
   result.start_timer();
1✔
288
   using Botan::Cert_Extension::ASBlocks;
1✔
289

290
   {
1✔
291
      const std::string filename("ASNumberCert.pem");
1✔
292
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
293

294
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
295

296
      const auto& identifier = as_blocks->as_identifiers();
1✔
297
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
298

299
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
300
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
301

302
      // cert contains asnum 0-999, 5042, 0-4294967295
303
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
304
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
305

306
      // and rdi 1234-5678, 32768, 0-4294967295
307
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
308
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
309
   }
1✔
310
   {
1✔
311
      const std::string filename("ASNumberOnly.pem");
1✔
312
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
313

314
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
315

316
      const auto& identifier = as_blocks->as_identifiers();
1✔
317
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
318

319
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
320
      result.confirm("cert has no RDI entries", identifier.rdi().has_value(), false);
2✔
321

322
      // contains 0-999, 0-4294967295
323
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
324
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
325
   }
1✔
326
   {
1✔
327
      const std::string filename("ASRdiOnly.pem");
1✔
328
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
329

330
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
331

332
      const auto& identifier = as_blocks->as_identifiers();
1✔
333
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
334

335
      result.confirm("cert has no ASNUM entries", identifier.asnum().has_value(), false);
2✔
336
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
337

338
      // contains 1234-5678, 0-4294967295
339
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
340
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
341
   }
1✔
342
   {
1✔
343
      const std::string filename("ASNumberInherit.pem");
1✔
344
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
345

346
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
347

348
      const auto& identifier = as_blocks->as_identifiers();
1✔
349
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
350

351
      result.confirm("asnum has no entries", identifier.asnum().value().ranges().has_value(), false);
2✔
352
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
353

354
      // contains 1234-5678, 0-4294967295
355
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
356
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
357
   }
1✔
358

359
   result.end_timer();
1✔
360
   return result;
1✔
361
}
×
362

363
   #endif
364

365
Test::Result test_x509_ip_addr_blocks_rfc3779_example() {
1✔
366
   Test::Result result("X509 IP Address Blocks rfc3779 example");
1✔
367
   result.start_timer();
1✔
368

369
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
370
   auto rng = Test::new_rng(__func__);
1✔
371

372
   // construct like in https://datatracker.ietf.org/doc/html/rfc3779#page-18
373
   std::unique_ptr<IPAddressBlocks> blocks_1 = std::make_unique<IPAddressBlocks>();
1✔
374
   blocks_1->add_address<IPv4>({10, 0, 32, 0}, {10, 0, 47, 255}, 1);
1✔
375
   blocks_1->add_address<IPv4>({10, 0, 64, 0}, {10, 0, 64, 255}, 1);
1✔
376
   blocks_1->add_address<IPv4>({10, 1, 0, 0}, {10, 1, 255, 255}, 1);
1✔
377
   blocks_1->add_address<IPv4>({10, 2, 48, 0}, {10, 2, 63, 255}, 1);
1✔
378
   blocks_1->add_address<IPv4>({10, 2, 64, 0}, {10, 2, 64, 255}, 1);
1✔
379
   blocks_1->add_address<IPv4>({10, 3, 0, 0}, {10, 3, 255, 255}, 1);
1✔
380
   blocks_1->inherit<IPv6>();
1✔
381

382
   Botan::X509_Cert_Options opts_1 = ca_opts();
1✔
383
   opts_1.extensions.add(std::move(blocks_1));
1✔
384

385
   auto cert_1 = make_self_signed(rng, opts_1);
1✔
386

387
   auto bits_1 = cert_1.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
388

389
   result.test_eq(
1✔
390
      "extension is encoded as specified",
391
      bits_1,
392
      "3035302B040300010130240304040A00200304000A00400303000A01300C0304040A02300304000A02400303000A033006040200020500");
393

394
   const auto* ext_1 = cert_1.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
395

396
   auto ext_1_addr_fam_1 = ext_1->addr_blocks()[0];
1✔
397
   result.test_eq("extension 1 ipv4 safi", ext_1_addr_fam_1.safi(), std::optional<uint8_t>(1));
1✔
398
   auto ext_1_ranges =
1✔
399
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_1_addr_fam_1.addr_choice()).ranges().value();
1✔
400
   result.test_eq("extension 1 range 1 min", ext_1_ranges[0].min().value(), {10, 0, 32, 0});
2✔
401
   result.test_eq("extension 1 range 1 max", ext_1_ranges[0].max().value(), {10, 0, 47, 255});
2✔
402

403
   result.test_eq("extension 1 range 2 min", ext_1_ranges[1].min().value(), {10, 0, 64, 0});
2✔
404
   result.test_eq("extension 1 range 2 max", ext_1_ranges[1].max().value(), {10, 0, 64, 255});
2✔
405

406
   result.test_eq("extension 1 range 3 min", ext_1_ranges[2].min().value(), {10, 1, 0, 0});
2✔
407
   result.test_eq("extension 1 range 3 max", ext_1_ranges[2].max().value(), {10, 1, 255, 255});
2✔
408

409
   result.test_eq("extension 1 range 4 min", ext_1_ranges[3].min().value(), {10, 2, 48, 0});
2✔
410
   result.test_eq("extension 1 range 4 max", ext_1_ranges[3].max().value(), {10, 2, 64, 255});
2✔
411

412
   result.test_eq("extension 1 range 5 min", ext_1_ranges[4].min().value(), {10, 3, 0, 0});
2✔
413
   result.test_eq("extension 1 range 5 max", ext_1_ranges[4].max().value(), {10, 3, 255, 255});
2✔
414

415
   result.test_eq("extension 1 ipv6 safi", ext_1->addr_blocks()[1].safi(), std::optional<uint8_t>{std::nullopt});
1✔
416
   result.confirm(
2✔
417
      "extension 1 ipv6 inherited",
418
      !std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_1->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
419

420
   // https://datatracker.ietf.org/doc/html/rfc3779#page-20
421
   std::unique_ptr<IPAddressBlocks> blocks_2 = std::make_unique<IPAddressBlocks>();
1✔
422
   blocks_2->add_address<IPv6>(
1✔
423
      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
424
      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
425
   blocks_2->add_address<IPv4>({10, 0, 0, 0}, {10, 255, 255, 255}, 1);
1✔
426
   blocks_2->add_address<IPv4>({172, 16, 0, 0}, {172, 31, 255, 255}, 1);
1✔
427
   blocks_2->inherit<IPv4>(2);
1✔
428

429
   Botan::X509_Cert_Options opts_2 = ca_opts();
1✔
430
   opts_2.extensions.add(std::move(blocks_2));
1✔
431

432
   auto cert_2 = make_self_signed(rng, opts_2);
1✔
433

434
   auto bits_2 = cert_2.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
435

436
   // see https://www.rfc-editor.org/errata/eid6792 as to why the B0 specified in the RFC is a AC here
437
   result.test_eq("extension is encoded as specified",
1✔
438
                  bits_2,
439
                  "302C3010040300010130090302000A030304AC10300704030001020500300F040200023009030700200100000002");
440

441
   const auto* ext_2 = cert_2.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
442

443
   auto ext_2_addr_fam_1 = ext_2->addr_blocks()[0];
1✔
444
   result.test_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_1.safi(), std::optional<uint8_t>(1));
1✔
445
   auto ext_2_ranges_1 =
1✔
446
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2_addr_fam_1.addr_choice()).ranges().value();
1✔
447
   result.test_eq("extension 2 fam 1 range 1 min", ext_2_ranges_1[0].min().value(), {10, 0, 0, 0});
2✔
448
   result.test_eq("extension 2 fam 1 range 1 max", ext_2_ranges_1[0].max().value(), {10, 255, 255, 255});
2✔
449

450
   result.test_eq("extension 2 fam 1 range 2 min", ext_2_ranges_1[1].min().value(), {172, 16, 0, 0});
2✔
451
   result.test_eq("extension 2 fam 1 range 2 max", ext_2_ranges_1[1].max().value(), {172, 31, 255, 255});
2✔
452

453
   result.test_eq("extension 2 ipv4 2 safi", ext_2->addr_blocks()[1].safi(), std::optional<uint8_t>{2});
1✔
454
   result.confirm(
2✔
455
      "extension 2 ipv4 2 inherited",
456
      !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
457

458
   auto ext_2_addr_fam_3 = ext_2->addr_blocks()[2];
1✔
459
   result.test_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_3.safi(), std::optional<uint8_t>(std::nullopt));
1✔
460
   auto ext_2_ranges_3 =
1✔
461
      std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_2_addr_fam_3.addr_choice()).ranges().value();
1✔
462
   result.test_eq("extension 2 fam 3 range 1 min",
2✔
463
                  ext_2_ranges_3[0].min().value(),
1✔
464
                  {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
465
   result.test_eq("extension 2 fam 3 range 1 max",
2✔
466
                  ext_2_ranges_3[0].max().value(),
1✔
467
                  {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
468

469
   result.end_timer();
1✔
470
   return result;
2✔
471
}
7✔
472

473
Test::Result test_x509_ip_addr_blocks_encode_builder() {
1✔
474
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
475
   result.start_timer();
1✔
476

477
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
478
   auto rng = Test::new_rng(__func__);
1✔
479

480
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
1✔
481

482
   // 64 - 127
483
   blocks->add_address<IPv4>({192, 168, 0b01000000, 0}, {192, 168, 0b01111111, 255}, 2);
1✔
484

485
   blocks->add_address<IPv4>({255, 255, 255, 255});
1✔
486
   // encoded as prefix
487
   blocks->add_address<IPv4>({190, 5, 0, 0}, {190, 5, 0b01111111, 255});
1✔
488
   // encoded as min, max
489
   blocks->add_address<IPv4>({127, 0, 0, 1}, {189, 5, 7, 255});
1✔
490

491
   // full address range
492
   blocks->add_address<IPv4>({0, 0, 0, 0}, {255, 255, 255, 255}, 1);
1✔
493

494
   blocks->add_address<IPv4>({123, 123, 2, 1});
1✔
495

496
   Botan::X509_Cert_Options opts = ca_opts();
1✔
497
   opts.extensions.add(std::move(blocks));
1✔
498

499
   auto cert = make_self_signed(rng, opts);
1✔
500
   auto bits = cert.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
501

502
   // hand validated with https://lapo.it/asn1js/
503
   result.test_eq(
1✔
504
      "extension is encoded as specified",
505
      bits,
506
      "304630290402000130230305007B7B0201300D0305007F000001030403BD0500030407BE0500030500FFFFFFFF300A04030001013003030100300D04030001023006030406C0A840");
507

508
   result.end_timer();
1✔
509
   return result;
1✔
510
}
2✔
511

512
namespace {
513

514
template <size_t I>
515
bool bit_set(size_t v) {
648✔
516
   if(((v >> I) & 1) == 1) {
648✔
517
      return true;
518
   } else {
519
      return false;
324✔
520
   }
521
}
522

523
}  // namespace
524

525
Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() {
1✔
526
   Test::Result result("X509 IP Address Block encode (ctor)");
1✔
527
   result.start_timer();
1✔
528

529
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
530

531
   auto rng = Test::new_rng(__func__);
1✔
532

533
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
534

535
   for(size_t i = 0; i < 64; i++) {
65✔
536
      const bool push_ipv4_ranges = bit_set<0>(i);
64✔
537
      const bool push_ipv6_ranges = bit_set<1>(i);
64✔
538
      const bool inherit_ipv4 = bit_set<2>(i);
64✔
539
      const bool inherit_ipv6 = bit_set<3>(i);
64✔
540
      const bool push_ipv4_family = bit_set<4>(i);
64✔
541
      const bool push_ipv6_family = bit_set<5>(i);
64✔
542

543
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
64✔
544

545
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
546
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
547
      a = {255, 255, 255, 255};
64✔
548
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
549

550
      // encoded as min, max
551
      a = {127, 0, 0, 1};
64✔
552
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
553
      a = {189, 5, 7, 255};
64✔
554
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
555

556
      // encoded as prefix
557
      a = {190, 5, 0, 0};
64✔
558
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
559
      a = {190, 5, 127, 255};
64✔
560
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
561

562
      a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
563
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
564
      a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
565
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
566

567
      // encoded as min, max
568
      a = {0xAF, 0x23, 0x34, 0x45, 0x67, 0x2A, 0x7A, 0xEF, 0x8C, 0x00, 0x00, 0x00, 0x66, 0x00, 0x52, 0x00};
64✔
569
      auto ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
570

571
      a = {0xAF, 0xCD, 0xDE, 0xF0, 0x00, 0x0F, 0xEE, 0x00, 0xBB, 0x4A, 0x9B, 0x00, 0x00, 0x4C, 0x00, 0xCC};
64✔
572
      auto ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
573

574
      // encoded as prefix
575
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
576
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
577
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
578
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
579

580
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
64✔
581
      auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
582
      auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
583
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
64✔
584

585
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
64✔
586
      auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
587
      auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
588
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
64✔
589

590
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
64✔
591
      if(push_ipv4_ranges) {
64✔
592
         ipv4_ranges.push_back(ipv4_range_1);
32✔
593
         ipv4_ranges.push_back(ipv4_range_2);
32✔
594
         ipv4_ranges.push_back(ipv4_range_3);
32✔
595
         ipv4_ranges.push_back(ipv4_range_4);
32✔
596
      }
597

598
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
64✔
599
      if(push_ipv6_ranges) {
64✔
600
         ipv6_ranges.push_back(ipv6_range_1);
32✔
601
         ipv6_ranges.push_back(ipv6_range_2);
32✔
602
         ipv6_ranges.push_back(ipv6_range_3);
32✔
603
         ipv6_ranges.push_back(ipv6_range_4);
32✔
604
      }
605

606
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
64✔
607
      if(!inherit_ipv4) {
64✔
608
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
96✔
609
      }
610

611
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
64✔
612
      if(!inherit_ipv6) {
64✔
613
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
96✔
614
      }
615

616
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
617
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
618

619
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
620
      if(push_ipv4_family) {
64✔
621
         addr_blocks.push_back(ipv4_addr_family);
32✔
622
      }
623
      if(push_ipv6_family) {
64✔
624
         addr_blocks.push_back(ipv6_addr_family);
32✔
625
      }
626

627
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
628

629
      opts.extensions.add(std::move(blocks));
64✔
630

631
      const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
64✔
632
      const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
633
      {
64✔
634
         const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
64✔
635
         result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
128✔
636

637
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
638
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
639
            result.confirm("no address family entries", dec_addr_blocks.empty(), true);
32✔
640
            continue;
16✔
641
         }
642

643
         if(push_ipv4_family) {
48✔
644
            auto family = dec_addr_blocks[0];
32✔
645
            result.confirm("ipv4 family afi", ipv4_addr_family.afi() == family.afi(), true);
64✔
646
            result.test_eq("ipv4 family safi", ipv4_addr_family.safi(), family.safi());
32✔
647
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
32✔
648

649
            if(!inherit_ipv4) {
32✔
650
               auto ranges = choice.ranges().value();
16✔
651
               if(push_ipv4_ranges) {
16✔
652
                  result.test_eq("ipv4 entry 0 min", ranges[0].min().value(), ipv4_range_1.min().value());
16✔
653
                  result.test_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
16✔
654
                  result.test_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
16✔
655
                  result.test_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
16✔
656
                  result.test_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
16✔
657
                  result.test_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
16✔
658
                  result.test_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
16✔
659
                  result.test_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
16✔
660
               } else {
661
                  result.confirm("ipv4 range has no entries", ranges.empty(), true);
16✔
662
               }
663
            } else {
16✔
664
               result.confirm("ipv4 family inherit", choice.ranges().has_value(), false);
32✔
665
            }
666
         }
64✔
667

668
         if(push_ipv6_family) {
48✔
669
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
670
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
64✔
671
            result.test_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
32✔
672
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
32✔
673
            if(!inherit_ipv6) {
32✔
674
               auto ranges = choice.ranges().value();
16✔
675
               if(push_ipv6_ranges) {
16✔
676
                  result.test_eq("ipv6 entry 0 min", ranges[0].min().value(), ipv6_range_1.min().value());
16✔
677
                  result.test_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
16✔
678
                  result.test_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
16✔
679
                  result.test_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
16✔
680
                  result.test_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
16✔
681
                  result.test_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
16✔
682
                  result.test_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
16✔
683
                  result.test_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
16✔
684
               } else {
685
                  result.confirm("ipv6 range has no entries", ranges.empty(), true);
16✔
686
               }
687
            } else {
16✔
688
               result.confirm("ipv6 family inherit", choice.ranges().has_value(), false);
32✔
689
            }
690
         }
96✔
691
      }
692
   }
320✔
693

694
   result.end_timer();
1✔
695
   return result;
2✔
696
}
2✔
697

698
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() {
1✔
699
   Test::Result result("X509 IP Address Block encode edge cases (ctor)");
1✔
700
   result.start_timer();
1✔
701

702
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
703

704
   auto rng = Test::new_rng(__func__);
1✔
705

706
   // trailing 0s, trailing 1s, and some arbitrary values
707
   std::vector<uint8_t> edge_values = {0,  2,  4,  8,   16,  32, 64, 128, 1,   3,  7,
1✔
708
                                       15, 31, 63, 127, 255, 12, 46, 123, 160, 234};
1✔
709

710
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
711

712
   for(size_t i = 0; i < edge_values.size(); i++) {
22✔
713
      for(size_t j = 0; j < 4; j++) {
105✔
714
         const bool modify_min = bit_set<0>(j);
84✔
715
         const bool modify_max = bit_set<1>(j);
84✔
716

717
         for(size_t k = 0; k < 18; k++) {
1,219✔
718
            if(!modify_min && !modify_max && (k > 0 || i > 0)) {
1,156✔
719
               // we don't modify anything, this is the extreme edge case of 0.0 ... - 255.255. ...
720
               // so we only need to do this once
721
               break;
722
            }
723

724
            Botan::X509_Cert_Options opts = req_opts(sig_algo);
1,135✔
725

726
            std::vector<uint8_t> min_bytes(16, 0x00);
1,135✔
727
            std::vector<uint8_t> max_bytes(16, 0xFF);
1,135✔
728

729
            if(modify_min) {
1,135✔
730
               min_bytes[15 - (k < 2 ? 0 : k - 2)] = edge_values[i];
756✔
731
            }
732
            if(modify_max) {
1,135✔
733
               max_bytes[15 - (k > 15 ? 15 : k)] = edge_values[i];
756✔
734
            }
735

736
            auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
1,135✔
737
            auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
1,135✔
738

739
            auto ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(address_min, address_max);
1,135✔
740

741
            std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
1,135✔
742
            ipv6_ranges.push_back(ipv6_range);
1,135✔
743

744
            auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
1,135✔
745

746
            auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3,405✔
747

748
            std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1,135✔
749
            addr_blocks.push_back(ipv6_addr_family);
1,135✔
750

751
            std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1,135✔
752

753
            opts.extensions.add(std::move(blocks));
1,135✔
754

755
            const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1,135✔
756
            const Botan::X509_Certificate cert =
1,135✔
757
               ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1,135✔
758
            {
1,135✔
759
               const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1,135✔
760
               result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2,270✔
761
               const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1,135✔
762
               auto family = dec_addr_blocks[0];
1,135✔
763
               result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
2,270✔
764
               result.test_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
1,135✔
765
               auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
1,135✔
766
               auto ranges = choice.ranges().value();
1,135✔
767

768
               result.test_eq("ipv6 edge case min", ranges[0].min().value(), ipv6_range.min().value());
2,270✔
769
               result.test_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
2,270✔
770
            }
2,270✔
771
         }
5,675✔
772
      }
773
   }
774
   result.end_timer();
1✔
775
   return result;
2✔
776
}
3✔
777

778
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
779
   Test::Result result("X509 IP Address Block range merge");
1✔
780
   result.start_timer();
1✔
781

782
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
783

784
   auto rng = Test::new_rng(__func__);
1✔
785

786
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
787
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
788

789
   const std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
790
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
791
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
792
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
793
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
794
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
795
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
796
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
797
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
798
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
799
   };
46✔
800

801
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv6_ranges;
1✔
802
   for(auto pair : addresses) {
10✔
803
      auto address_min = IPAddressBlocks::IPAddress<IPv4>(pair[0]);
9✔
804
      auto address_max = IPAddressBlocks::IPAddress<IPv4>(pair[1]);
9✔
805
      auto range = IPAddressBlocks::IPAddressOrRange<IPv4>(address_min, address_max);
9✔
806
      ipv6_ranges.push_back(range);
9✔
807
   }
9✔
808

809
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
1✔
810
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
811

812
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
813
   addr_blocks.push_back(ipv6_addr_family);
1✔
814

815
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
816

817
   opts.extensions.add(std::move(blocks));
1✔
818

819
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
820
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
821
   {
1✔
822
      const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
823
      result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
824
      const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1✔
825
      auto family = dec_addr_blocks[0];
1✔
826
      auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
1✔
827
      auto ranges = choice.ranges().value();
1✔
828

829
      const std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
830
      const std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
831

832
      result.test_eq("range expected min", ranges[0].min().value(), expected_min);
2✔
833
      result.test_eq("range expected max", ranges[0].max().value(), expected_max);
2✔
834
      result.test_eq("range length", ranges.size(), 1);
1✔
835
   }
2✔
836

837
   result.end_timer();
1✔
838
   return result;
2✔
839
}
24✔
840

841
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
842
   Test::Result result("X509 IP Address Block family merge");
1✔
843
   result.start_timer();
1✔
844

845
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
846

847
   auto rng = Test::new_rng(__func__);
1✔
848

849
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
850
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
851

852
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
853

854
   IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
1✔
855
   IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
1✔
856

857
   const uint8_t v4_bytes_1[4] = {123, 123, 123, 123};
1✔
858
   const IPAddressBlocks::IPAddress<IPv4> v4_addr_1(v4_bytes_1);
1✔
859
   // create 2 prefixes from the v4 addresses -> they should be merged
860

861
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> v4_choice_vec{
1✔
862
      IPAddressBlocks::IPAddressOrRange<IPv4>(IPAddressBlocks::IPAddress<IPv4>({v4_addr_1}))};
1✔
863
   IPAddressBlocks::IPAddressChoice<IPv4> v4_choice_dupl(v4_choice_vec);
1✔
864
   result.confirm(
2✔
865
      "IPAddressChoice v4 merges ranges already in constructor", v4_choice_dupl.ranges().value().size() == 1, true);
1✔
866
   const IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
867

868
   const uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
869
   const IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
1✔
870

871
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> v6_choice_vec{
1✔
872
      IPAddressBlocks::IPAddressOrRange<IPv6>(IPAddressBlocks::IPAddress<IPv6>({v6_addr_1}))};
1✔
873
   IPAddressBlocks::IPAddressChoice<IPv6> v6_choice_dupl(v6_choice_vec);
1✔
874
   result.confirm(
2✔
875
      "IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1, true);
1✔
876
   const IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
877

878
   const IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
879
   const IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
880

881
   const IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
882
   const IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
883

884
   /*
885
   considering the push order, the resulting order should be
886
   [0] v4 no safi
887
   [2] v4 safi
888
   [1] v6 no safi
889
   [3] v6 safi
890
   */
891
   for(size_t i = 0; i < 3; i++) {
4✔
892
      addr_blocks.push_back(v4_empty_fam_safi);
3✔
893
      addr_blocks.push_back(v6_empty_fam);
3✔
894
      addr_blocks.push_back(v4_fam_dupl);
3✔
895
      addr_blocks.push_back(v6_empty_fam_safi);
3✔
896
      addr_blocks.push_back(v6_fam_dupl);
3✔
897
      addr_blocks.push_back(v4_empty_fam);
3✔
898
   }
899

900
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
901
      v4_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_empty_fam, v6_fam_dupl, v6_empty_fam_safi};
7✔
902

903
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
904

905
   opts.extensions.add(std::move(blocks));
1✔
906

907
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
908
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
909

910
   const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
911
   result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
912
   const auto& dec_blocks = ip_blocks->addr_blocks();
1✔
913

914
   result.confirm("blocks got merged lengthwise", dec_blocks.size() == expected_blocks.size(), true);
2✔
915

916
   bool sorted = true;
1✔
917
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
918
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
919
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
920

921
      uint32_t afam_a = a.afi();
5✔
922
      if(a.safi().has_value()) {
5✔
923
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
924
      } else {
925
         afam_a = static_cast<uint32_t>(afam_a << 8);
2✔
926
      }
927

928
      uint32_t afam_b = b.afi();
5✔
929
      if(b.safi().has_value()) {
5✔
930
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
931
      } else {
932
         afam_b = static_cast<uint32_t>(afam_b << 8);
1✔
933
      }
934

935
      if(afam_a > afam_b) {
5✔
936
         sorted = false;
937
         break;
938
      }
939
   }
940

941
   result.confirm("blocks got sorted", sorted, true);
2✔
942

943
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
944
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
945
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
946

947
      result.confirm("blocks match push order by afi at index " + std::to_string(i), dec.afi() == exp.afi(), true);
12✔
948
      result.test_eq("blocks match push order by safi at index " + std::to_string(i), dec.safi(), exp.safi());
18✔
949

950
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
951
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
3✔
952
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
3✔
953

954
         if(!exp_choice.ranges().has_value()) {
3✔
955
            result.confirm(
2✔
956
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
957
         } else {
958
            result.confirm(
1✔
959
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
960

961
            if(dec_choice.ranges().has_value() == false) {
1✔
962
               continue;
×
963
            }
964

965
            auto dec_ranges = dec_choice.ranges().value();
1✔
966
            auto exp_ranges = exp_choice.ranges().value();
1✔
967
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
968
                           dec_ranges.size() == exp_ranges.size(),
1✔
969
                           true);
970

971
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
972
               continue;
×
973
            }
974

975
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
976
               result.test_eq(
2✔
977
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
978
                  exp_ranges[j].min().value(),
1✔
979
                  dec_ranges[j].min().value());
1✔
980
               result.test_eq(
2✔
981
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
982
                  exp_ranges[j].max().value(),
1✔
983
                  dec_ranges[j].max().value());
2✔
984
            }
985
         }
1✔
986
      } else if((exp.afi() == 2) && (dec.afi() == 2)) {
7✔
987
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(dec.addr_choice());
3✔
988
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(exp.addr_choice());
3✔
989

990
         if(!exp_choice.ranges().has_value()) {
3✔
991
            result.confirm(
2✔
992
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
993
         } else {
994
            result.confirm(
1✔
995
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
996

997
            if(dec_choice.ranges().has_value() == false) {
1✔
998
               continue;
×
999
            }
1000

1001
            auto dec_ranges = dec_choice.ranges().value();
1✔
1002
            auto exp_ranges = exp_choice.ranges().value();
1✔
1003
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
1004
                           dec_ranges.size() == exp_ranges.size(),
1✔
1005
                           true);
1006

1007
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
1008
               continue;
×
1009
            }
1010

1011
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
1012
               result.test_eq(
2✔
1013
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1014
                  exp_ranges[j].min().value(),
1✔
1015
                  dec_ranges[j].min().value());
1✔
1016
               result.test_eq(
2✔
1017
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1018
                  exp_ranges[j].max().value(),
1✔
1019
                  dec_ranges[j].max().value());
2✔
1020
            }
1021
         }
1✔
1022
      }
4✔
1023
   }
1024

1025
   result.end_timer();
1✔
1026
   return result;
2✔
1027
}
19✔
1028

1029
Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() {
1✔
1030
   Test::Result result("X509 IP Address Blocks path validation success (builder)");
1✔
1031
   result.start_timer();
1✔
1032

1033
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1034
   auto rng = Test::new_rng(__func__);
1✔
1035

1036
   /*
1037
   Creates a certificate chain of length 4.
1038
   Root: ipv4 and ipv6
1039
   Inherit: has both values as 'inherit'
1040
   Dynamic: has either both 'inherit', both with values, or just one with a value
1041
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1042
   */
1043

1044
   // Root cert
1045
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
1✔
1046

1047
   root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
1✔
1048
   root_blocks->add_address<IPv4>({10, 0, 0, 1}, {10, 255, 255, 255}, 42);
1✔
1049

1050
   root_blocks->add_address<IPv6>(
1✔
1051
      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1052
      {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1053
   root_blocks->add_address<IPv6>(
1✔
1054
      {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1055
      {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1056

1057
   // Inherit cert
1058
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>();
1✔
1059

1060
   inherit_blocks->inherit<IPv4>(42);
1✔
1061
   inherit_blocks->inherit<IPv6>();
1✔
1062

1063
   // Subject cert
1064
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
1✔
1065

1066
   sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, 42);
1✔
1067
   sub_blocks->add_address<IPv4>({10, 0, 2, 1}, {10, 42, 0, 255}, 42);
1✔
1068

1069
   sub_blocks->add_address<IPv6>(
1✔
1070
      {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1071
      {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1072

1073
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1074
   root_opts.extensions.add(std::move(root_blocks));
1✔
1075
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1076
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1077
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1078
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1079

1080
   Botan::Certificate_Store_In_Memory trusted;
1✔
1081
   trusted.add_certificate(root_cert);
1✔
1082

1083
   for(size_t i = 0; i < 4; i++) {
5✔
1084
      const bool include_v4 = bit_set<0>(i);
4✔
1085
      const bool include_v6 = bit_set<1>(i);
4✔
1086

1087
      // Dynamic Cert
1088
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>();
4✔
1089
      if(include_v4) {
4✔
1090
         dyn_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
2✔
1091
         dyn_blocks->add_address<IPv4>({10, 0, 0, 255}, {10, 255, 0, 1}, 42);
2✔
1092
      } else {
1093
         dyn_blocks->inherit<IPv4>(42);
2✔
1094
      }
1095

1096
      if(include_v6) {
4✔
1097
         dyn_blocks->add_address<IPv6>(
2✔
1098
            {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
1099
            {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1100
      } else {
1101
         dyn_blocks->inherit<IPv6>();
2✔
1102
      }
1103

1104
      auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
8✔
1105

1106
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1107
      const Botan::X509_Certificate sub_cert =
4✔
1108
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1109

1110
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
8✔
1111
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
16✔
1112

1113
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1114
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1115
   }
8✔
1116

1117
   result.end_timer();
1✔
1118
   return result;
2✔
1119
}
7✔
1120

1121
Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() {
1✔
1122
   Test::Result result("X509 IP Address Block path validation success (ctor)");
1✔
1123
   result.start_timer();
1✔
1124

1125
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1126
   auto rng = Test::new_rng(__func__);
1✔
1127

1128
   /*
1129
   Creates a certificate chain of length 4.
1130
   Root: ipv4 and ipv6
1131
   Inherit: has both values as 'inherit'
1132
   Dynamic: has either both 'inherit', both with values, or just one with a value
1133
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1134
   */
1135

1136
   // Root cert
1137
   std::vector<uint8_t> a = {120, 0, 0, 1};
1✔
1138
   auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1139
   a = {130, 140, 150, 160};
1✔
1140
   auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1141

1142
   a = {10, 0, 0, 1};
1✔
1143
   auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1144
   a = {10, 255, 255, 255};
1✔
1145
   auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1146

1147
   a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1148
   auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1149
   a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1150
   auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1151

1152
   a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1153
   auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1154
   a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1155
   auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1156

1157
   auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
1✔
1158
   auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
1✔
1159
   auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
1✔
1160
   auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
1✔
1161

1162
   auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
1✔
1163
   auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
1✔
1164

1165
   auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
1✔
1166
   auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
1✔
1167

1168
   auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
3✔
1169
   auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
3✔
1170

1171
   auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
6✔
1172
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
1✔
1173

1174
   // Inherit cert
1175
   auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
1✔
1176
   auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
1✔
1177

1178
   auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
2✔
1179
   auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
2✔
1180

1181
   auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
6✔
1182
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
1✔
1183

1184
   // Dynamic Cert
1185
   a = {122, 0, 0, 255};
1✔
1186
   auto dyn_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1187
   a = {128, 255, 255, 255};
1✔
1188
   auto dyn_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1189
   a = {10, 0, 0, 255};
1✔
1190
   auto dyn_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1191
   a = {10, 255, 0, 1};
1✔
1192
   auto dyn_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1193

1194
   a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1195
   auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1196
   a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1197
   auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1198

1199
   auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
1✔
1200
   auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
1✔
1201
   auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
1✔
1202

1203
   auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
1✔
1204
   auto dyn_ipv6_ranges = {dyn_ipv6_range};
1✔
1205

1206
   // Subject cert
1207
   a = {124, 0, 255, 0};
1✔
1208
   auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1209
   a = {126, 0, 0, 1};
1✔
1210
   auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1211

1212
   a = {10, 0, 2, 1};
1✔
1213
   auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1214
   a = {10, 42, 0, 255};
1✔
1215
   auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1216

1217
   a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1218
   auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1219
   a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1220
   auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1221

1222
   auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
1✔
1223
   auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
1✔
1224
   auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
1✔
1225

1226
   auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
1✔
1227
   auto sub_ipv6_ranges = {sub_ipv6_range};
1✔
1228

1229
   auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
1✔
1230
   auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
1✔
1231

1232
   auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
3✔
1233
   auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
3✔
1234

1235
   auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
6✔
1236
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
1✔
1237

1238
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1239
   root_opts.extensions.add(std::move(root_blocks));
1✔
1240
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1241
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1242
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1243
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1244

1245
   Botan::Certificate_Store_In_Memory trusted;
1✔
1246
   trusted.add_certificate(root_cert);
1✔
1247

1248
   for(size_t i = 0; i < 4; i++) {
5✔
1249
      const bool include_v4 = bit_set<0>(i);
4✔
1250
      const bool include_v6 = bit_set<1>(i);
4✔
1251

1252
      auto dyn_ipv4_choice =
4✔
1253
         IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
8✔
1254
      auto dyn_ipv6_choice =
4✔
1255
         IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
8✔
1256

1257
      auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
10✔
1258
      auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
10✔
1259

1260
      auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
24✔
1261
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
4✔
1262

1263
      auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
8✔
1264

1265
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1266
      const Botan::X509_Certificate sub_cert =
4✔
1267
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1268

1269
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
8✔
1270
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
16✔
1271

1272
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1273
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1274
   }
28✔
1275

1276
   result.end_timer();
1✔
1277
   return result;
2✔
1278
}
24✔
1279

1280
Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() {
1✔
1281
   Test::Result result("X509 IP Address Blocks path validation failure (builder)");
1✔
1282
   result.start_timer();
1✔
1283

1284
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1285
   auto rng = Test::new_rng(__func__);
1✔
1286

1287
   for(size_t i = 0; i < 7; i++) {
8✔
1288
      const bool all_inherit = (i == 0);
7✔
1289
      const bool different_safi = (i == 1);
7✔
1290
      const bool too_small_subrange = (i == 2);
7✔
1291
      const bool too_large_subrange = (i == 3);
7✔
1292
      const bool no_more_issuer_ranges = (i == 4);
7✔
1293
      const bool empty_issuer_ranges = (i == 5);
7✔
1294
      const bool nullptr_extensions = (i == 6);
7✔
1295

1296
      // Root cert
1297
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
7✔
1298
      if(!all_inherit) {
7✔
1299
         root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
6✔
1300
      } else {
1301
         root_blocks->inherit<IPv4>(42);
1✔
1302
      }
1303

1304
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1305
      if(!nullptr_extensions) {
7✔
1306
         root_opts.extensions.add(std::move(root_blocks));
12✔
1307
      }
1308
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1309

1310
      // Issuer Cert
1311
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>();
7✔
1312
      if(!all_inherit) {
7✔
1313
         if(empty_issuer_ranges) {
6✔
1314
            iss_blocks->restrict<IPv4>(42);
1✔
1315
         } else {
1316
            iss_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
5✔
1317
         }
1318
      } else {
1319
         iss_blocks->inherit<IPv4>(42);
1✔
1320
      }
1321

1322
      auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
14✔
1323

1324
      // Subject cert
1325
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
7✔
1326

1327
      uint8_t safi = different_safi ? 41 : 42;
7✔
1328

1329
      if(!all_inherit) {
7✔
1330
         if(too_small_subrange) {
6✔
1331
            sub_blocks->add_address<IPv4>({118, 0, 255, 0}, {126, 0, 0, 1}, safi);
1✔
1332
         } else if(too_large_subrange) {
5✔
1333
            sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {134, 0, 0, 1}, safi);
1✔
1334
         } else if(no_more_issuer_ranges) {
4✔
1335
            sub_blocks->add_address<IPv4>({140, 0, 0, 1}, {150, 0, 0, 1}, safi);
1✔
1336
         } else {
1337
            sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, safi);
3✔
1338
         }
1339
      } else {
1340
         sub_blocks->inherit<IPv4>(safi);
1✔
1341
      }
1342

1343
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1344
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1345

1346
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1347
      const Botan::X509_Certificate sub_cert =
7✔
1348
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1349

1350
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
14✔
1351
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, iss_cert};
21✔
1352

1353
      Botan::Certificate_Store_In_Memory trusted;
7✔
1354
      trusted.add_certificate(root_cert);
7✔
1355

1356
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1357
      result.require("path validation fails", !path_result.successful_validation());
7✔
1358
   }
15✔
1359

1360
   result.end_timer();
1✔
1361
   return result;
1✔
1362
}
8✔
1363

1364
Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() {
1✔
1365
   Test::Result result("X509 IP Address Block path validation failure (ctor)");
1✔
1366
   result.start_timer();
1✔
1367

1368
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1369
   auto rng = Test::new_rng(__func__);
1✔
1370

1371
   for(size_t i = 0; i < 7; i++) {
8✔
1372
      const bool all_inherit = (i == 0);
7✔
1373
      const bool different_safi = (i == 1);
7✔
1374
      const bool too_small_subrange = (i == 2);
7✔
1375
      const bool too_large_subrange = (i == 3);
7✔
1376
      const bool no_more_issuer_ranges = (i == 4);
7✔
1377
      const bool empty_issuer_ranges = (i == 5);
7✔
1378
      const bool nullptr_extensions = (i == 6);
7✔
1379

1380
      // Root cert
1381
      std::vector<uint8_t> a = {120, 0, 0, 1};
7✔
1382
      auto root_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1383
      a = {130, 140, 150, 160};
7✔
1384
      auto root_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1385

1386
      auto root_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_range_1_min, root_range_1_max);
7✔
1387
      auto root_ranges = {root_range_1};
7✔
1388
      auto root_choice =
7✔
1389
         IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(root_ranges));
14✔
1390
      auto root_family = IPAddressBlocks::IPAddressFamily(root_choice, 42);
20✔
1391
      auto root_addr_blocks = {root_family};
21✔
1392
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
7✔
1393

1394
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1395
      if(!nullptr_extensions) {
7✔
1396
         root_opts.extensions.add(std::move(root_blocks));
12✔
1397
      }
1398
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1399

1400
      // Issuer Cert
1401
      a = {122, 0, 0, 255};
7✔
1402
      auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1403
      a = {128, 255, 255, 255};
7✔
1404
      auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1405
      auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
7✔
1406

1407
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
7✔
1408

1409
      if(!empty_issuer_ranges) {
7✔
1410
         iss_ranges.push_back(iss_range_1);
6✔
1411
      }
1412

1413
      auto iss_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(iss_ranges));
19✔
1414
      auto iss_family = IPAddressBlocks::IPAddressFamily(iss_choice, 42);
20✔
1415
      auto iss_addr_blocks = {iss_family};
21✔
1416
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>(iss_addr_blocks);
7✔
1417
      auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
14✔
1418

1419
      // Subject cert
1420
      if(too_small_subrange) {
7✔
1421
         a = {118, 0, 255, 0};
2✔
1422
      } else if(no_more_issuer_ranges) {
6✔
1423
         a = {140, 0, 0, 1};
2✔
1424
      } else {
1425
         a = {124, 0, 255, 0};
10✔
1426
      }
1427

1428
      auto sub_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1429
      if(too_large_subrange) {
7✔
1430
         a = {134, 0, 0, 1};
2✔
1431
      } else if(no_more_issuer_ranges) {
6✔
1432
         a = {150, 0, 0, 1};
2✔
1433
      } else {
1434
         a = {126, 0, 0, 1};
10✔
1435
      }
1436
      auto sub_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1437

1438
      auto sub_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_range_1_min, sub_range_1_max);
7✔
1439
      auto sub_ranges = {sub_range_1};
7✔
1440
      auto sub_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(sub_ranges));
14✔
1441
      auto sub_family = IPAddressBlocks::IPAddressFamily(sub_choice, different_safi ? 41 : 42);
26✔
1442

1443
      auto sub_addr_blocks = {sub_family};
21✔
1444
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
7✔
1445

1446
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1447
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1448

1449
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1450
      const Botan::X509_Certificate sub_cert =
7✔
1451
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1452

1453
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
14✔
1454
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, iss_cert};
21✔
1455

1456
      Botan::Certificate_Store_In_Memory trusted;
7✔
1457
      trusted.add_certificate(root_cert);
7✔
1458

1459
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1460
      result.require("path validation fails", !path_result.successful_validation());
7✔
1461
   }
82✔
1462

1463
   result.end_timer();
1✔
1464
   return result;
1✔
1465
}
8✔
1466

1467
Test::Result test_x509_as_blocks_rfc3779_example() {
1✔
1468
   Test::Result result("X509 AS Blocks rfc3779 example");
1✔
1469
   result.start_timer();
1✔
1470

1471
   using Botan::Cert_Extension::ASBlocks;
1✔
1472
   auto rng = Test::new_rng(__func__);
1✔
1473

1474
   // construct like in https://datatracker.ietf.org/doc/html/rfc3779#page-21
1475
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1476
   blocks->add_asnum(135);
1✔
1477
   blocks->add_asnum(3000, 3999);
1✔
1478
   blocks->add_asnum(5001);
1✔
1479
   blocks->inherit_rdi();
1✔
1480

1481
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1482
   opts.extensions.add(std::move(blocks));
1✔
1483

1484
   auto cert = make_self_signed(rng, opts);
1✔
1485
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1486

1487
   result.test_eq(
1✔
1488
      "extension is encoded as specified", bits, "301AA014301202020087300802020BB802020F9F02021389A1020500");
1489

1490
   auto as_idents = cert.v3_extensions().get_extension_object_as<ASBlocks>()->as_identifiers();
1✔
1491
   auto as_ids = as_idents.asnum().value().ranges().value();
1✔
1492

1493
   result.confirm("extension has correct data", as_ids[0].min() == 135);
2✔
1494

1495
   result.end_timer();
1✔
1496
   return result;
2✔
1497
}
3✔
1498

1499
Test::Result test_x509_as_blocks_encode_builder() {
1✔
1500
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
1501
   result.start_timer();
1✔
1502

1503
   using Botan::Cert_Extension::ASBlocks;
1✔
1504
   auto rng = Test::new_rng(__func__);
1✔
1505

1506
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1507

1508
   blocks->add_rdi(10);
1✔
1509
   blocks->add_rdi(20, 30);
1✔
1510
   blocks->add_rdi(42, 300);
1✔
1511
   blocks->add_rdi(9, 301);
1✔
1512

1513
   blocks->inherit_asnum();
1✔
1514
   blocks->add_asnum(20);
1✔
1515
   // this overwrites the previous two
1516
   blocks->restrict_asnum();
1✔
1517

1518
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1519
   opts.extensions.add(std::move(blocks));
1✔
1520

1521
   auto cert = make_self_signed(rng, opts);
1✔
1522
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1523

1524
   result.test_eq("extension is encoded as specified", bits, "3011A0023000A10B300930070201090202012D");
1✔
1525

1526
   result.end_timer();
1✔
1527
   return result;
1✔
1528
}
2✔
1529

1530
Test::Result test_x509_as_blocks_extension_encode_ctor() {
1✔
1531
   Test::Result result("X509 AS Blocks encode (ctor)");
1✔
1532
   result.start_timer();
1✔
1533

1534
   using Botan::Cert_Extension::ASBlocks;
1✔
1535

1536
   auto rng = Test::new_rng(__func__);
1✔
1537

1538
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1539

1540
   for(size_t i = 0; i < 16; i++) {
17✔
1541
      const bool push_asnum = bit_set<0>(i);
16✔
1542
      const bool push_rdi = bit_set<1>(i);
16✔
1543
      const bool include_asnum = bit_set<2>(i);
16✔
1544
      const bool include_rdi = bit_set<3>(i);
16✔
1545
      if(!include_asnum && !include_rdi) {
16✔
1546
         continue;
4✔
1547
      }
1548

1549
      const ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
12✔
1550
      const ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
12✔
1551
      const ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
12✔
1552

1553
      const ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
12✔
1554
      const ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
12✔
1555
      const ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
12✔
1556

1557
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
12✔
1558
      if(push_asnum) {
12✔
1559
         as_ranges.push_back(asnum_id_or_range0);
6✔
1560
         as_ranges.push_back(asnum_id_or_range1);
6✔
1561
         as_ranges.push_back(asnum_id_or_range2);
6✔
1562
      }
1563

1564
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
12✔
1565
      if(push_rdi) {
12✔
1566
         rdi_ranges.push_back(rdi_id_or_range0);
6✔
1567
         rdi_ranges.push_back(rdi_id_or_range1);
6✔
1568
         rdi_ranges.push_back(rdi_id_or_range2);
6✔
1569
      }
1570

1571
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
12✔
1572
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
12✔
1573

1574
      const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
32✔
1575
                                                                    include_rdi ? std::optional(rdi) : std::nullopt);
32✔
1576

1577
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
12✔
1578

1579
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
12✔
1580
      opts.extensions.add(std::move(blocks));
12✔
1581

1582
      const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
12✔
1583
      const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
12✔
1584

1585
      {
12✔
1586
         const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
12✔
1587
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
24✔
1588

1589
         const auto& identifier = as_blocks->as_identifiers();
12✔
1590

1591
         if(include_asnum) {
12✔
1592
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
1593

1594
            if(push_asnum) {
8✔
1595
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
1596
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
1597

1598
               result.confirm("asnum entry 1 min", asnum_entries[1].min() == 5042, true);
8✔
1599
               result.confirm("asnum entry 1 max", asnum_entries[1].max() == 4294967295, true);
8✔
1600
            } else {
1601
               result.confirm("asnum has no entries", asnum_entries.empty(), true);
8✔
1602
            }
1603
         } else {
1604
            result.confirm("no asnum entry", identifier.asnum().has_value(), false);
8✔
1605
         }
1606

1607
         if(include_rdi) {
12✔
1608
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
1609

1610
            if(push_rdi) {
8✔
1611
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
1612
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
1613

1614
               result.confirm("rdi entry 1 min", rdi_entries[1].min() == 32768, true);
8✔
1615
               result.confirm("rdi entry 1 max", rdi_entries[1].max() == 4294967295, true);
8✔
1616
            } else {
1617
               result.confirm("rdi has no entries", rdi_entries.empty(), true);
8✔
1618
            }
1619
         } else {
1620
            result.confirm("rdi has no entry", identifier.rdi().has_value(), false);
8✔
1621
         }
1622
      }
1623
   }
36✔
1624

1625
   result.end_timer();
1✔
1626
   return result;
2✔
1627
}
2✔
1628

1629
Test::Result test_x509_as_blocks_range_merge() {
1✔
1630
   Test::Result result("X509 AS Block range merge");
1✔
1631
   result.start_timer();
1✔
1632

1633
   using Botan::Cert_Extension::ASBlocks;
1✔
1634

1635
   auto rng = Test::new_rng(__func__);
1✔
1636

1637
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1638
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
1639

1640
   const std::vector<std::vector<uint16_t>> ranges = {
1✔
1641
      {2005, 37005},
1642
      {60, 70},
1643
      {22, 50},
1644
      {35, 2000},
1645
      {2001, 2004},
1646
      {21, 21},
1647
      {0, 20},
1648
   };
9✔
1649

1650
   std::vector<ASBlocks::ASIdOrRange> as_ranges;
1✔
1651
   as_ranges.reserve(ranges.size());
1✔
1652
   for(const auto& pair : ranges) {
8✔
1653
      as_ranges.emplace_back(pair[0], pair[1]);
7✔
1654
   }
1655

1656
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
1657

1658
   const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(std::optional(asnum), std::nullopt);
3✔
1659

1660
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
1✔
1661

1662
   opts.extensions.add(std::move(blocks));
1✔
1663

1664
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
1665
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1666
   {
1✔
1667
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
1668
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1669

1670
      const auto& identifier = as_blocks->as_identifiers();
1✔
1671

1672
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
1673

1674
      result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
2✔
1675
      result.confirm("asnum entry 0 max", asnum_entries[0].max() == 37005, true);
2✔
1676
      result.confirm("asnum length", asnum_entries.size() == 1, true);
2✔
1677
   }
1678

1679
   result.end_timer();
1✔
1680
   return result;
2✔
1681
}
5✔
1682

1683
Test::Result test_x509_as_blocks_path_validation_success_builder() {
1✔
1684
   Test::Result result("X509 AS Block path validation success (builder)");
1✔
1685
   result.start_timer();
1✔
1686

1687
   using Botan::Cert_Extension::ASBlocks;
1✔
1688
   auto rng = Test::new_rng(__func__);
1✔
1689

1690
   /*
1691
   Creates a certificate chain of length 4.
1692
   Root: both asnum and rdi
1693
   Inherit: has both values as 'inherit'
1694
   Dynamic: has either both 'inherit', both with values, or just one with a value
1695
   Subject: both asnum and rdi as a subset of Root / Dynamic
1696
   */
1697

1698
   // Root Cert, both as and rdi
1699

1700
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
1✔
1701

1702
   root_blocks->add_asnum(0, 999);
1✔
1703
   root_blocks->add_asnum(5042);
1✔
1704
   root_blocks->add_asnum(5043, 4294967295);
1✔
1705

1706
   root_blocks->add_rdi(1234, 5678);
1✔
1707
   root_blocks->add_rdi(32768);
1✔
1708
   root_blocks->add_rdi(32769, 4294967295);
1✔
1709

1710
   // Inherit cert, both as 'inherit'
1711
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>();
1✔
1712
   inherit_blocks->inherit_asnum();
1✔
1713
   inherit_blocks->inherit_rdi();
1✔
1714

1715
   // Subject cert
1716

1717
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1718

1719
   sub_blocks->add_asnum(120, 180);
1✔
1720
   sub_blocks->add_asnum(220, 240);
1✔
1721
   sub_blocks->add_asnum(260, 511);
1✔
1722
   sub_blocks->add_asnum(678);
1✔
1723
   sub_blocks->add_asnum(5043, 5100);
1✔
1724

1725
   sub_blocks->add_rdi(1500, 2300);
1✔
1726
   sub_blocks->add_rdi(2500, 4000);
1✔
1727
   sub_blocks->add_rdi(1567);
1✔
1728
   sub_blocks->add_rdi(33100, 40000);
1✔
1729

1730
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1731
   root_opts.extensions.add(std::move(root_blocks));
1✔
1732
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1733
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1734
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1735
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1736

1737
   Botan::Certificate_Store_In_Memory trusted;
1✔
1738
   trusted.add_certificate(root_cert);
1✔
1739

1740
   for(size_t i = 0; i < 4; i++) {
5✔
1741
      const bool include_asnum = bit_set<0>(i);
4✔
1742
      const bool include_rdi = bit_set<1>(i);
4✔
1743

1744
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>();
4✔
1745
      if(include_asnum) {
4✔
1746
         dyn_blocks->add_asnum(100, 600);
2✔
1747
         dyn_blocks->add_asnum(678);
2✔
1748
         dyn_blocks->add_asnum(5042, 5101);
2✔
1749
      } else {
1750
         dyn_blocks->inherit_asnum();
2✔
1751
      }
1752

1753
      if(include_rdi) {
4✔
1754
         dyn_blocks->add_rdi(1500, 5000);
2✔
1755
         dyn_blocks->add_rdi(33000, 60000);
2✔
1756
      } else {
1757
         dyn_blocks->inherit_rdi();
2✔
1758
      }
1759

1760
      auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
8✔
1761

1762
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1763
      const Botan::X509_Certificate sub_cert =
4✔
1764
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1765

1766
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
8✔
1767
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
16✔
1768

1769
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1770
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1771
   }
8✔
1772

1773
   result.end_timer();
1✔
1774
   return result;
2✔
1775
}
7✔
1776

1777
Test::Result test_x509_as_blocks_path_validation_success_ctor() {
1✔
1778
   Test::Result result("X509 AS Block path validation success (ctor)");
1✔
1779
   result.start_timer();
1✔
1780

1781
   using Botan::Cert_Extension::ASBlocks;
1✔
1782
   auto rng = Test::new_rng(__func__);
1✔
1783

1784
   /*
1785
   Creates a certificate chain of length 4.
1786
   Root: both asnum and rdi
1787
   Inherit: has both values as 'inherit'
1788
   Dynamic: has either both 'inherit', both with values, or just one with a value
1789
   Subject: both asnum and rdi as a subset of Root / Dynamic
1790
   */
1791

1792
   // Root Cert, both as and rdi
1793
   const ASBlocks::ASIdOrRange root_asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
1✔
1794
   const ASBlocks::ASIdOrRange root_asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
1✔
1795
   const ASBlocks::ASIdOrRange root_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
1✔
1796

1797
   const ASBlocks::ASIdOrRange root_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
1✔
1798
   const ASBlocks::ASIdOrRange root_rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
1✔
1799
   const ASBlocks::ASIdOrRange root_rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
1✔
1800

1801
   std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
1✔
1802
   root_as_ranges.push_back(root_asnum_id_or_range0);
1✔
1803
   root_as_ranges.push_back(root_asnum_id_or_range1);
1✔
1804
   root_as_ranges.push_back(root_asnum_id_or_range2);
1✔
1805

1806
   std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
1✔
1807
   root_rdi_ranges.push_back(root_rdi_id_or_range0);
1✔
1808
   root_rdi_ranges.push_back(root_rdi_id_or_range1);
1✔
1809
   root_rdi_ranges.push_back(root_rdi_id_or_range2);
1✔
1810

1811
   ASBlocks::ASIdentifierChoice root_asnum = ASBlocks::ASIdentifierChoice(root_as_ranges);
1✔
1812
   ASBlocks::ASIdentifierChoice root_rdi = ASBlocks::ASIdentifierChoice(root_rdi_ranges);
1✔
1813
   const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
4✔
1814
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
1✔
1815

1816
   // Inherit cert, both as 'inherit'
1817
   ASBlocks::ASIdentifierChoice inherit_asnum = ASBlocks::ASIdentifierChoice();
1✔
1818
   ASBlocks::ASIdentifierChoice inherit_rdi = ASBlocks::ASIdentifierChoice();
1✔
1819
   const ASBlocks::ASIdentifiers inherit_ident = ASBlocks::ASIdentifiers(inherit_asnum, inherit_rdi);
2✔
1820
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>(inherit_ident);
1✔
1821

1822
   // Dynamic cert
1823
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range0 = ASBlocks::ASIdOrRange(100, 600);
1✔
1824
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range1 = ASBlocks::ASIdOrRange(678);
1✔
1825
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5042, 5101);
1✔
1826

1827
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 5000);
1✔
1828
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range1 = ASBlocks::ASIdOrRange(33000, 60000);
1✔
1829

1830
   std::vector<ASBlocks::ASIdOrRange> dyn_as_ranges;
1✔
1831
   dyn_as_ranges.push_back(dyn_asnum_id_or_range0);
1✔
1832
   dyn_as_ranges.push_back(dyn_asnum_id_or_range1);
1✔
1833
   dyn_as_ranges.push_back(dyn_asnum_id_or_range2);
1✔
1834

1835
   std::vector<ASBlocks::ASIdOrRange> dyn_rdi_ranges;
1✔
1836
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range0);
1✔
1837
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range1);
1✔
1838

1839
   // Subject cert
1840
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1841
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1842
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1843
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1844
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1845

1846
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1847
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1848
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1849
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1850

1851
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1852
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1853
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1854
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1855
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1856
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1857

1858
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1859
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1860
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1861
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1862
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1863

1864
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1865
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1866
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1867
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1868

1869
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1870
   root_opts.extensions.add(std::move(root_blocks));
1✔
1871
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1872
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1873
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1874
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1875

1876
   Botan::Certificate_Store_In_Memory trusted;
1✔
1877
   trusted.add_certificate(root_cert);
1✔
1878

1879
   for(size_t i = 0; i < 4; i++) {
5✔
1880
      const bool include_asnum = bit_set<0>(i);
4✔
1881
      const bool include_rdi = bit_set<1>(i);
4✔
1882

1883
      ASBlocks::ASIdentifierChoice dyn_asnum =
4✔
1884
         ASBlocks::ASIdentifierChoice(include_asnum ? std::optional(dyn_as_ranges) : std::nullopt);
6✔
1885
      ASBlocks::ASIdentifierChoice dyn_rdi =
4✔
1886
         ASBlocks::ASIdentifierChoice(include_rdi ? std::optional(dyn_rdi_ranges) : std::nullopt);
6✔
1887
      const ASBlocks::ASIdentifiers dyn_ident = ASBlocks::ASIdentifiers(dyn_asnum, dyn_rdi);
12✔
1888
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>(dyn_ident);
4✔
1889

1890
      auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
8✔
1891

1892
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1893
      const Botan::X509_Certificate sub_cert =
4✔
1894
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1895

1896
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
8✔
1897
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
16✔
1898

1899
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1900
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1901
   }
12✔
1902

1903
   result.end_timer();
1✔
1904
   return result;
2✔
1905
}
11✔
1906

1907
Test::Result test_x509_as_blocks_path_validation_extension_not_present_builder() {
1✔
1908
   Test::Result result("X509 AS Block path validation extension not present (builder)");
1✔
1909
   result.start_timer();
1✔
1910

1911
   using Botan::Cert_Extension::ASBlocks;
1✔
1912
   auto rng = Test::new_rng(__func__);
1✔
1913

1914
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1915
   sub_blocks->add_asnum(120, 180);
1✔
1916
   sub_blocks->add_asnum(220, 224);
1✔
1917
   sub_blocks->add_asnum(260, 511);
1✔
1918
   sub_blocks->add_asnum(678);
1✔
1919
   sub_blocks->add_asnum(5043, 5100);
1✔
1920

1921
   sub_blocks->add_rdi(1500, 2300);
1✔
1922
   sub_blocks->add_rdi(2500, 4000);
1✔
1923
   sub_blocks->add_rdi(1567);
1✔
1924
   sub_blocks->add_rdi(33100, 40000);
1✔
1925

1926
   // create a root ca that does not have any extension
1927
   const Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1928
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1929
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1930
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1931
   const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
1932
   const Botan::X509_Certificate sub_cert =
1✔
1933
      root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1934

1935
   Botan::Certificate_Store_In_Memory trusted;
1✔
1936
   trusted.add_certificate(root_cert);
1✔
1937

1938
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1939
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1940

1941
   const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
1✔
1942
   result.require("path validation fails", !path_result.successful_validation());
1✔
1943

1944
   result.end_timer();
1✔
1945
   return result;
2✔
1946
}
3✔
1947

1948
Test::Result test_x509_as_blocks_path_validation_extension_not_present_ctor() {
1✔
1949
   Test::Result result("X509 AS Block path validation extension not present (ctor)");
1✔
1950
   result.start_timer();
1✔
1951

1952
   using Botan::Cert_Extension::ASBlocks;
1✔
1953
   auto rng = Test::new_rng(__func__);
1✔
1954

1955
   // Subject cert
1956
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1957
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1958
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1959
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1960
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1961

1962
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1963
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1964
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1965
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1966

1967
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1968
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1969
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1970
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1971
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1972
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1973

1974
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1975
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1976
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1977
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1978
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1979

1980
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1981
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1982
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1983
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1984

1985
   // create a root ca that does not have any extension
1986
   const Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1987
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1988
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1989
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1990
   const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
1991
   const Botan::X509_Certificate sub_cert =
1✔
1992
      root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1993

1994
   Botan::Certificate_Store_In_Memory trusted;
1✔
1995
   trusted.add_certificate(root_cert);
1✔
1996

1997
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1998
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1999

2000
   const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
1✔
2001
   result.require("path validation fails", !path_result.successful_validation());
1✔
2002

2003
   result.end_timer();
1✔
2004
   return result;
2✔
2005
}
5✔
2006

2007
Test::Result test_x509_as_blocks_path_validation_failure_builder() {
1✔
2008
   Test::Result result("X509 AS Block path validation failure (builder)");
1✔
2009
   result.start_timer();
1✔
2010

2011
   using Botan::Cert_Extension::ASBlocks;
1✔
2012
   auto rng = Test::new_rng(__func__);
1✔
2013

2014
   /*
2015
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2016

2017
   Each test is expected to fail and creates the following certificate chain:
2018
   Root -> Issuer -> Subject
2019

2020
   00: set all the asnum choices to 'inherit' for each cert
2021
   01: 00 but for rdis
2022
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
2023
   03: 02 but for rdis
2024
   04: both 02 and 03
2025
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
2026
   06: 05 but for rdis
2027
   07: both 05 and 06
2028
   08: make the certs have multiple ranges and make one asnum range that is not the smallest and not the largest overlap with it's maximum
2029
   09: 08 but for rdis
2030
   10: both 08 and 09
2031
   11: same as 08 but the range in the subject is not contiguous, instead it is the issuers range but split into two ranges (e.g issuer range is 40-60, subject ranges are 40-49 and 51-61)
2032
   12: 11 but for rdis
2033
   13: both 11 and 12
2034
   14: 08 but using the minimum instead of the maximum
2035
   15: 14 but for rdis
2036
   16: both 14 and 15
2037
   17: same as 11 but using the minimum instead of the maximum
2038
   18: 17 but for rdis
2039
   19: both 18 and 19
2040
   20: make the issuer ranges empty but have an entry in the subject ranges
2041
   */
2042
   for(size_t i = 0; i < 21; i++) {
22✔
2043
      // enable / disable all the different edge cases
2044
      const bool inherit_all_asnums = (i == 0);
21✔
2045
      const bool inherit_all_rdis = (i == 1);
21✔
2046
      const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
2047
      const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
2048
      const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
2049
      const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
2050
      const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
2051
      const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
2052
      const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
2053
      const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
2054
      const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
2055
      const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
2056
      const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
2057
      const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
2058
      const bool empty_issuer_non_empty_subject = (i == 20);
21✔
2059

2060
      // Root cert
2061
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
21✔
2062

2063
      if(!inherit_all_asnums) {
21✔
2064
         if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
20✔
2065
            // 100-200 for 02,03,04
2066
            root_blocks->add_asnum(100, 200);
4✔
2067
         } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
16✔
2068
            // 10-20,30-40,50-60 for 08,09,10
2069
            root_blocks->add_asnum(10, 20);
4✔
2070
            root_blocks->add_asnum(30, 40);
4✔
2071
            root_blocks->add_asnum(50, 60);
4✔
2072
         } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
12✔
2073
            // 10-20,30-50,60-70 for 11,12,13
2074
            root_blocks->add_asnum(10, 20);
4✔
2075
            root_blocks->add_asnum(30, 50);
4✔
2076
            root_blocks->add_asnum(60, 70);
4✔
2077
         }
2078
      } else {
2079
         root_blocks->inherit_asnum();
1✔
2080
      }
2081

2082
      // same values but for rdis
2083
      if(!inherit_all_rdis) {
21✔
2084
         if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
2085
            root_blocks->add_rdi(100, 200);
4✔
2086
         } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
2087
            root_blocks->add_rdi(10, 20);
4✔
2088
            root_blocks->add_rdi(30, 40);
4✔
2089
            root_blocks->add_rdi(50, 60);
4✔
2090
         } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
2091
            root_blocks->add_rdi(10, 20);
4✔
2092
            root_blocks->add_rdi(30, 50);
4✔
2093
            root_blocks->add_rdi(60, 70);
4✔
2094
         }
2095
      } else {
2096
         root_blocks->inherit_rdi();
1✔
2097
      }
2098

2099
      if(empty_issuer_non_empty_subject) {
21✔
2100
         root_blocks->restrict_asnum();
1✔
2101
         root_blocks->restrict_rdi();
1✔
2102
      }
2103

2104
      // Issuer cert
2105
      // the issuer cert has the same ranges as the root cert
2106
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2107
      auto issu_blocks = root_blocks->copy();
21✔
2108

2109
      // Subject cert
2110
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
21✔
2111
      if(!inherit_all_asnums) {
21✔
2112
         // assign the subject asnum ranges
2113
         if(push_asnum_min_edge_ranges) {
2114
            // 99-200 for 02 (so overlapping to the left)
2115
            sub_blocks->add_asnum(99, 200);
2✔
2116
         } else if(push_asnum_max_edge_ranges) {
2117
            // 100-201 for 03 (so overlapping to the right)
2118
            sub_blocks->add_asnum(100, 201);
2✔
2119
         } else if(push_asnum_max_middle_ranges) {
2120
            // same as root, but change the range in the middle to overlap to the right for 08
2121
            sub_blocks->add_asnum(10, 20);
2✔
2122
            sub_blocks->add_asnum(30, 41);
2✔
2123
            sub_blocks->add_asnum(50, 60);
2✔
2124
         } else if(push_asnum_max_split_ranges) {
2125
            // change the range in the middle to be cut at 45 for case 11
2126
            // the left range is 30-44
2127
            // the right range is 46-51 (overlapping the issuer range to the right)
2128
            sub_blocks->add_asnum(10, 20);
2✔
2129
            sub_blocks->add_asnum(30, 44);
2✔
2130
            sub_blocks->add_asnum(46, 51);
2✔
2131
            sub_blocks->add_asnum(60, 70);
2✔
2132
         } else if(push_asnum_min_middle_ranges) {
2133
            // just change the test in the middle to overlap to the left for case 14
2134
            sub_blocks->add_asnum(10, 20);
2✔
2135
            sub_blocks->add_asnum(29, 40);
2✔
2136
            sub_blocks->add_asnum(50, 60);
2✔
2137
         } else if(push_asnum_min_split_ranges) {
2138
            // again split the range in the middle at 45 for case 17
2139
            // creating two ranges 29-44 and 46-50 (so overlapping to the left)
2140
            sub_blocks->add_asnum(10, 20);
2✔
2141
            sub_blocks->add_asnum(29, 44);
2✔
2142
            sub_blocks->add_asnum(46, 50);
2✔
2143
            sub_blocks->add_asnum(60, 70);
2✔
2144
         } else if(empty_issuer_non_empty_subject) {
2145
            sub_blocks->add_asnum(50);
1✔
2146
         }
2147
      } else {
2148
         sub_blocks->inherit_asnum();
1✔
2149
      }
2150

2151
      if(!inherit_all_rdis) {
21✔
2152
         // same values but for rdis
2153
         if(push_rdi_min_edge_ranges) {
2154
            sub_blocks->add_rdi(99, 200);
2✔
2155
         } else if(push_rdi_max_edge_ranges) {
2156
            sub_blocks->add_rdi(100, 201);
2✔
2157
         } else if(push_rdi_max_middle_ranges) {
2158
            sub_blocks->add_rdi(10, 20);
2✔
2159
            sub_blocks->add_rdi(30, 41);
2✔
2160
            sub_blocks->add_rdi(50, 60);
2✔
2161
         } else if(push_rdi_max_split_ranges) {
2162
            sub_blocks->add_rdi(10, 20);
2✔
2163
            sub_blocks->add_rdi(30, 44);
2✔
2164
            sub_blocks->add_rdi(46, 51);
2✔
2165
            sub_blocks->add_rdi(60, 70);
2✔
2166
         } else if(push_rdi_min_middle_ranges) {
2167
            sub_blocks->add_rdi(10, 20);
2✔
2168
            sub_blocks->add_rdi(29, 40);
2✔
2169
            sub_blocks->add_rdi(50, 60);
2✔
2170
         } else if(push_rdi_min_split_ranges) {
2171
            sub_blocks->add_rdi(10, 20);
2✔
2172
            sub_blocks->add_rdi(29, 44);
2✔
2173
            sub_blocks->add_rdi(46, 50);
2✔
2174
            sub_blocks->add_rdi(60, 70);
2✔
2175
         }
2176
      } else {
2177
         sub_blocks->inherit_rdi();
1✔
2178
      }
2179

2180
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2181
      root_opts.extensions.add(std::move(root_blocks));
21✔
2182
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2183
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2184

2185
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2186
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2187
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2188
      const Botan::X509_Certificate sub_cert =
21✔
2189
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2190

2191
      Botan::Certificate_Store_In_Memory trusted;
21✔
2192
      trusted.add_certificate(root_cert);
21✔
2193

2194
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
42✔
2195
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, issu_cert};
63✔
2196

2197
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
21✔
2198
      // in all cases, the validation should fail, since we are creating invalid scenarios
2199
      result.confirm("path validation fails at iteration " + std::to_string(i), !path_result.successful_validation());
42✔
2200
   }
42✔
2201

2202
   result.end_timer();
1✔
2203
   return result;
1✔
2204
}
22✔
2205

2206
Test::Result test_x509_as_blocks_path_validation_failure_ctor() {
1✔
2207
   Test::Result result("X509 AS Block path validation failure (ctor)");
1✔
2208
   result.start_timer();
1✔
2209

2210
   using Botan::Cert_Extension::ASBlocks;
1✔
2211
   auto rng = Test::new_rng(__func__);
1✔
2212

2213
   /*
2214
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2215

2216
   Each test is expected to fail and creates the following certificate chain:
2217
   Root -> Issuer -> Subject
2218

2219
   00: set all the asnum choices to 'inherit' for each cert
2220
   01: 00 but for rdis
2221
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
2222
   03: 02 but for rdis
2223
   04: both 02 and 03
2224
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
2225
   06: 05 but for rdis
2226
   07: both 05 and 06
2227
   08: make the certs have multiple ranges and make one asnum range that is not the smallest and not the largest overlap with it's maximum
2228
   09: 08 but for rdis
2229
   10: both 08 and 09
2230
   11: same as 08 but the range in the subject is not contiguous, instead it is the issuers range but split into two ranges (e.g issuer range is 40-60, subject ranges are 40-49 and 51-61)
2231
   12: 11 but for rdis
2232
   13: both 11 and 12
2233
   14: 08 but using the minimum instead of the maximum
2234
   15: 14 but for rdis
2235
   16: both 14 and 15
2236
   17: same as 11 but using the minimum instead of the maximum
2237
   18: 17 but for rdis
2238
   19: both 18 and 19
2239
   20: make the issuer ranges empty but have an entry in the subject ranges
2240
   */
2241
   for(size_t i = 0; i < 21; i++) {
22✔
2242
      // enable / disable all the different edge cases
2243
      const bool inherit_all_asnums = (i == 0);
21✔
2244
      const bool inherit_all_rdis = (i == 1);
21✔
2245
      const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
2246
      const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
2247
      const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
2248
      const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
2249
      const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
2250
      const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
2251
      const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
2252
      const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
2253
      const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
2254
      const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
2255
      const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
2256
      const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
2257
      const bool empty_issuer_non_empty_subject = (i == 20);
21✔
2258

2259
      // Root cert
2260
      std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
21✔
2261
      std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
21✔
2262

2263
      // assign the root ranges
2264
      if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
21✔
2265
         // 100-200 for 02,03,04
2266
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
2267
      } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
17✔
2268
         // 10-20,30-40,50-60 for 08,09,10
2269
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2270
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
2271
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
2272
      } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
13✔
2273
         // 10-20,30-50,60-70 for 11,12,13
2274
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2275
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
2276
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
2277
      }
2278

2279
      // same values but for rdis
2280
      if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
21✔
2281
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
2282
      } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
2283
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2284
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
2285
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
2286
      } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
2287
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2288
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
2289
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
2290
      }
2291

2292
      // Issuer cert
2293
      // the issuer cert has the same ranges as the root cert
2294
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2295
      const std::vector<ASBlocks::ASIdOrRange> issu_as_ranges;
21✔
2296
      const std::vector<ASBlocks::ASIdOrRange> issu_rdi_ranges;
21✔
2297

2298
      // Subject cert
2299
      std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
21✔
2300
      std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
21✔
2301

2302
      // assign the subject asnum ranges
2303
      if(push_asnum_min_edge_ranges) {
21✔
2304
         // 99-200 for 02 (so overlapping to the left)
2305
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
4✔
2306
      } else if(push_asnum_max_edge_ranges) {
2307
         // 100-201 for 03 (so overlapping to the right)
2308
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
4✔
2309
      } else if(push_asnum_max_middle_ranges) {
2310
         // just change the range in the middle to overlap to the right for 08
2311
         sub_as_ranges = root_as_ranges;
2✔
2312
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
2✔
2313
      } else if(push_asnum_max_split_ranges) {
2314
         // change the range in the middle to be cut at 45 for case 11
2315
         // the left range is 30-44
2316
         // the right range is 46-51 (overlapping the issuer range to the right)
2317
         sub_as_ranges = root_as_ranges;
2✔
2318
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
2✔
2319
         // pushing the new range created by splitting to the back since they will be sorted anyway
2320
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
4✔
2321
      } else if(push_asnum_min_middle_ranges) {
2322
         // just change the test in the middle to overlap to the left for case 14
2323
         sub_as_ranges = root_as_ranges;
2✔
2324
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
2✔
2325
      } else if(push_asnum_min_split_ranges) {
2326
         // again split the range in the middle at 45 for case 17
2327
         // creating two ranges 29-44 and 46-50 (so overlapping to the left)
2328
         sub_as_ranges = root_as_ranges;
2✔
2329
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
2✔
2330
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
4✔
2331
      } else if(empty_issuer_non_empty_subject) {
2332
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(50));
1✔
2333
      }
2334

2335
      // same values but for rdis
2336
      if(push_rdi_min_edge_ranges) {
21✔
2337
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
4✔
2338
      } else if(push_rdi_max_edge_ranges) {
2339
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
4✔
2340
      } else if(push_rdi_max_middle_ranges) {
2341
         sub_rdi_ranges = root_rdi_ranges;
2✔
2342
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
2✔
2343
      } else if(push_rdi_max_split_ranges) {
2344
         sub_rdi_ranges = root_rdi_ranges;
2✔
2345
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
2✔
2346
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
4✔
2347
      } else if(push_rdi_min_middle_ranges) {
2348
         sub_rdi_ranges = root_rdi_ranges;
2✔
2349
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
2✔
2350
      } else if(push_rdi_min_split_ranges) {
2351
         sub_rdi_ranges = root_rdi_ranges;
2✔
2352
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
2✔
2353
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
4✔
2354
      }
2355

2356
      // for cases 00 and 01, set all certs to inherit (so std::nullopt)
2357
      // in all other cases use the ranges created beforehand
2358
      ASBlocks::ASIdentifierChoice root_asnum =
21✔
2359
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(root_as_ranges));
41✔
2360
      ASBlocks::ASIdentifierChoice root_rdi =
21✔
2361
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(root_rdi_ranges));
41✔
2362
      const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
82✔
2363
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
21✔
2364

2365
      const ASBlocks::ASIdentifiers& issu_ident = root_ident;
21✔
2366
      std::unique_ptr<ASBlocks> issu_blocks = std::make_unique<ASBlocks>(issu_ident);
21✔
2367

2368
      ASBlocks::ASIdentifierChoice sub_asnum =
21✔
2369
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(sub_as_ranges));
41✔
2370
      ASBlocks::ASIdentifierChoice sub_rdi =
21✔
2371
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(sub_rdi_ranges));
41✔
2372
      const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
82✔
2373
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
21✔
2374

2375
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2376
      root_opts.extensions.add(std::move(root_blocks));
21✔
2377
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2378
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2379

2380
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2381
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2382
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2383
      const Botan::X509_Certificate sub_cert =
21✔
2384
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2385

2386
      Botan::Certificate_Store_In_Memory trusted;
21✔
2387
      trusted.add_certificate(root_cert);
21✔
2388

2389
      const Botan::Path_Validation_Restrictions restrictions(false, 80);
42✔
2390
      const std::vector<Botan::X509_Certificate> certs = {sub_cert, issu_cert};
63✔
2391

2392
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
21✔
2393
      // in all cases, the validation should fail, since we are creating invalid scenarios
2394
      result.confirm("path validation fails at iteration " + std::to_string(i), !path_result.successful_validation());
42✔
2395
   }
122✔
2396

2397
   result.end_timer();
1✔
2398
   return result;
1✔
2399
}
22✔
2400

2401
class X509_RPKI_Tests final : public Test {
1✔
2402
   public:
2403
      std::vector<Test::Result> run() override {
1✔
2404
         std::vector<Test::Result> results;
1✔
2405

2406
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
2407
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
2408
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
2409
   #endif
2410
         results.push_back(test_x509_ip_addr_blocks_rfc3779_example());
2✔
2411
         results.push_back(test_x509_ip_addr_blocks_encode_builder());
2✔
2412
         results.push_back(test_x509_ip_addr_blocks_extension_encode_ctor());
2✔
2413
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor());
2✔
2414
         results.push_back(test_x509_ip_addr_blocks_range_merge());
2✔
2415
         results.push_back(test_x509_ip_addr_blocks_family_merge());
2✔
2416
         results.push_back(test_x509_ip_addr_blocks_path_validation_success_builder());
2✔
2417
         results.push_back(test_x509_ip_addr_blocks_path_validation_success_ctor());
2✔
2418
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure_builder());
2✔
2419
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure_ctor());
2✔
2420
         results.push_back(test_x509_as_blocks_rfc3779_example());
2✔
2421
         results.push_back(test_x509_as_blocks_encode_builder());
2✔
2422
         results.push_back(test_x509_as_blocks_extension_encode_ctor());
2✔
2423
         results.push_back(test_x509_as_blocks_range_merge());
2✔
2424
         results.push_back(test_x509_as_blocks_path_validation_success_builder());
2✔
2425
         results.push_back(test_x509_as_blocks_path_validation_success_ctor());
2✔
2426
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present_builder());
2✔
2427
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present_ctor());
2✔
2428
         results.push_back(test_x509_as_blocks_path_validation_failure_builder());
2✔
2429
         results.push_back(test_x509_as_blocks_path_validation_failure_ctor());
2✔
2430
         return results;
1✔
2431
      }
×
2432
};
2433

2434
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
2435

2436
#endif
2437

2438
}  // namespace
2439

2440
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc