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

randombit / botan / 22038388571

15 Feb 2026 03:39PM UTC coverage: 90.056% (-0.003%) from 90.059%
22038388571

Pull #5341

github

web-flow
Merge e5a537aa4 into 76dfe61e9
Pull Request #5341: Cleanup test predicates on binary strings

102343 of 113644 relevant lines covered (90.06%)

11670901.95 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
namespace Botan_Tests {
23

24
namespace {
25

26
#if defined(BOTAN_HAS_X509_CERTIFICATES) && \
27
   (defined(BOTAN_HAS_ECDSA) || defined(BOTAN_HAS_RSA) || defined(BOTAN_HAS_ED25519))
28

29
struct CA_Creation_Result {
30
      Botan::X509_Certificate ca_cert;
31
      Botan::X509_CA ca;
32
      std::unique_ptr<Botan::Private_Key> sub_key;
33
      std::string sig_algo;
34
      std::string hash_fn;
35
};
36

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

40
   const Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
2,728✔
41
   return Botan::X509_Time(t.to_std_timepoint());
2,728✔
42
}
43

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

47
   opts.uri = "https://botan.randombit.net";
149✔
48
   opts.dns = "botan.randombit.net";
149✔
49
   opts.email = "testing@randombit.net";
149✔
50
   opts.set_padding_scheme(sig_padding);
149✔
51

52
   opts.CA_key(1);
149✔
53

54
   return opts;
149✔
55
}
×
56

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

60
   opts.uri = "https://botan.randombit.net";
1,276✔
61
   opts.dns = "botan.randombit.net";
1,276✔
62
   opts.email = "testing@randombit.net";
1,276✔
63
   opts.set_padding_scheme(sig_padding);
1,276✔
64

65
   opts.not_before("160101200000Z");
1,276✔
66
   opts.not_after("300101200000Z");
1,276✔
67

68
   opts.challenge = "zoom";
1,276✔
69

70
   if(algo == "RSA") {
1,276✔
71
      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
×
72
   } else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") {
1,276✔
73
      opts.constraints = Botan::Key_Constraints::DigitalSignature;
1,276✔
74
   }
75

76
   return opts;
1,276✔
77
}
×
78

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

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

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

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

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

119
   return CA_Creation_Result{ca_cert, std::move(ca), std::move(sub_key), sig_algo, hash_fn};
204✔
120
}
68✔
121

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

128
   Botan::X509_Cert_Options opts = ca_opts();
76✔
129
   opts.extensions.add(std::move(ext));
76✔
130

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

135
   return std::make_pair(std::move(cert), std::move(ca));
76✔
136
}
76✔
137

138
constexpr auto IPv4 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv4;
139
constexpr auto IPv6 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv6;
140

141
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
142

143
Test::Result test_x509_ip_addr_blocks_extension_decode() {
1✔
144
   Test::Result result("X509 IP Address Block decode");
1✔
145
   result.start_timer();
1✔
146
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
147

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

153
      const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
154
      result.test_is_true("cert has IPAddrBlocks extension", ip_addr_blocks != nullptr);
1✔
155
      result.test_sz_eq("cert has two IpAddrBlocks", addr_blocks.size(), 2);
1✔
156

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

160
      const auto& v4_blocks = ipv4block.ranges().value();
1✔
161

162
      // cert contains (in this order)
163
      // 192.168.0.0 - 192.168.127.255 (192.168.0.0/17)
164
      // 193.168.0.0 - 193.169.255.255 (193.168.0.0/15)
165
      // 194.168.0.0 - 195.175.1.2
166
      // 196.168.0.1 - 196.168.0.1 (196.168.0.1/32)
167

168
      result.test_bin_eq("ipv4 block 0 min", v4_blocks[0].min().value(), {192, 168, 0, 0});
1✔
169
      result.test_bin_eq("ipv4 block 0 max", v4_blocks[0].max().value(), {192, 168, 127, 255});
1✔
170

171
      result.test_bin_eq("ipv4 block 1 min", v4_blocks[1].min().value(), {193, 168, 0, 0});
1✔
172
      result.test_bin_eq("ipv4 block 1 max", v4_blocks[1].max().value(), {193, 169, 255, 255});
1✔
173
      result.test_bin_eq("ipv4 block 2 min", v4_blocks[2].min().value(), {194, 168, 0, 0});
1✔
174
      result.test_bin_eq("ipv4 block 2 max", v4_blocks[2].max().value(), {195, 175, 1, 2});
1✔
175

176
      result.test_bin_eq("ipv4 block 3 min", v4_blocks[3].min().value(), {196, 168, 0, 1});
1✔
177
      result.test_bin_eq("ipv4 block 3 max", v4_blocks[3].max().value(), {196, 168, 0, 1});
1✔
178

179
      const auto& v6_blocks = ipv6block.ranges().value();
1✔
180

181
      // cert contains (in this order)
182
      // fa80::/65
183
      // fe20::/37
184
      // 2003:0:6829:3435:420:10c5:0:c4/128
185
      // ab01:0:0:0:0:0:0:1-cd02:0:0:0:0:0:0:2
186

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

225
      // cert contains (in this order)
226
      // IPv6 (1) inherit
227
      // IPv6 0xff....0xff
228
      // IPv4 (2) inherit
229
      // IPv4 (1) 192.168.0.0 - 192.168.2.1
230
      // IPv4 (1) 192.168.2.2 - 200.0.0.0
231
      // IPv4 inherit
232

233
      // IPv4 ranges should be merged, IPv4 should come before IPv6, all should be sorted by safi
234

235
      const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
236
      result.test_sz_eq("cert has two IpAddrBlocks", addr_blocks.size(), 5);
1✔
237

238
      result.test_opt_u8_eq("block 0 has no safi", addr_blocks[0].safi(), std::nullopt);
1✔
239
      result.test_is_true(
1✔
240
         "block 0 is inherited",
241
         !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[0].addr_choice()).ranges().has_value());
1✔
242

243
      result.test_opt_u8_eq("block 1 has correct safi", addr_blocks[1].safi(), 1);
1✔
244
      const auto& block_1 =
1✔
245
         std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[1].addr_choice()).ranges().value();
1✔
246

247
      result.test_is_true("block 1 has correct size", block_1.size() == 1);
1✔
248
      result.test_bin_eq("block 1 min is correct", block_1[0].min().value(), {192, 168, 0, 0});
1✔
249
      result.test_bin_eq("block 1 max is correct", block_1[0].max().value(), {200, 0, 0, 0});
1✔
250

251
      result.test_opt_u8_eq("block 2 has correct safi", addr_blocks[2].safi(), 2);
1✔
252
      result.test_is_true(
1✔
253
         "block 2 is inherited",
254
         !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[2].addr_choice()).ranges().has_value());
1✔
255

256
      result.test_opt_u8_eq("block 3 has no safi", addr_blocks[3].safi(), std::nullopt);
1✔
257
      const auto& block_3 =
1✔
258
         std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[3].addr_choice()).ranges().value();
1✔
259

260
      result.test_is_true("block 3 has correct size", block_3.size() == 1);
1✔
261
      result.test_bin_eq(
1✔
262
         "block 3 min is correct",
263
         block_3[0].min().value(),
1✔
264
         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
265
      result.test_bin_eq(
1✔
266
         "block 3 max is correct",
267
         block_3[0].max().value(),
1✔
268
         {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
269

270
      result.test_opt_u8_eq("block 24 has correct safi", addr_blocks[4].safi(), 1);
1✔
271
      result.test_is_true(
1✔
272
         "block 4 is inherited",
273
         !std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[4].addr_choice()).ranges().has_value());
1✔
274
   }
1✔
275
   {
1✔
276
      const std::string filename("InvalidIPAddrBlocks.pem");
1✔
277
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
278

279
      // cert contains the 10.0.32.0/20 prefix, but with a 9 for the unused bits
280

281
      result.test_is_true("extension is present", cert.v3_extensions().extension_set(IPAddressBlocks::static_oid()));
1✔
282

283
      const auto* ext = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
284
      result.test_is_true("extension is not decoded", ext == nullptr);
1✔
285
   }
1✔
286

287
   result.end_timer();
1✔
288
   return result;
1✔
289
}
×
290

291
Test::Result test_x509_as_blocks_extension_decode() {
1✔
292
   Test::Result result("X509 AS Block decode");
1✔
293
   result.start_timer();
1✔
294
   using Botan::Cert_Extension::ASBlocks;
1✔
295

296
   {
1✔
297
      const std::string filename("ASNumberCert.pem");
1✔
298
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
299

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

302
      const auto& identifier = as_blocks->as_identifiers();
1✔
303
      result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
304

305
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
306
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
307

308
      // cert contains asnum 0-999, 5042, 0-4294967295
309
      result.test_is_true("asnum entry 0 min", asnum[0].min() == 0);
1✔
310
      result.test_is_true("asnum entry 0 max", asnum[0].max() == 4294967295);
1✔
311

312
      // and rdi 1234-5678, 32768, 0-4294967295
313
      result.test_is_true("rdi entry 0 min", rdi[0].min() == 0);
1✔
314
      result.test_is_true("rdi entry 0 max", rdi[0].max() == 4294967295);
1✔
315
   }
1✔
316
   {
1✔
317
      const std::string filename("ASNumberOnly.pem");
1✔
318
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
319

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

322
      const auto& identifier = as_blocks->as_identifiers();
1✔
323
      result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
324

325
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
326
      result.test_is_false("cert has no RDI entries", identifier.rdi().has_value());
1✔
327

328
      // contains 0-999, 0-4294967295
329
      result.test_is_true("asnum entry 0 min", asnum[0].min() == 0);
1✔
330
      result.test_is_true("asnum entry 0 max", asnum[0].max() == 4294967295);
1✔
331
   }
1✔
332
   {
1✔
333
      const std::string filename("ASRdiOnly.pem");
1✔
334
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
335

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

338
      const auto& identifier = as_blocks->as_identifiers();
1✔
339
      result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
340

341
      result.test_is_false("cert has no ASNUM entries", identifier.asnum().has_value());
1✔
342
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
343

344
      // contains 1234-5678, 0-4294967295
345
      result.test_is_true("rdi entry 0 min", rdi[0].min() == 0);
1✔
346
      result.test_is_true("rdi entry 0 max", rdi[0].max() == 4294967295);
1✔
347
   }
1✔
348
   {
1✔
349
      const std::string filename("ASNumberInherit.pem");
1✔
350
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
351

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

354
      const auto& identifier = as_blocks->as_identifiers();
1✔
355
      result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
356

357
      result.test_is_false("asnum has no entries", identifier.asnum().value().ranges().has_value());
1✔
358
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
359

360
      // contains 1234-5678, 0-4294967295
361
      result.test_is_true("rdi entry 0 min", rdi[0].min() == 0);
1✔
362
      result.test_is_true("rdi entry 0 max", rdi[0].max() == 4294967295);
1✔
363
   }
1✔
364

365
   result.end_timer();
1✔
366
   return result;
1✔
367
}
×
368

369
   #endif
370

371
Test::Result test_x509_ip_addr_blocks_rfc3779_example() {
1✔
372
   Test::Result result("X509 IP Address Blocks rfc3779 example");
1✔
373
   result.start_timer();
1✔
374

375
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
376
   auto rng = Test::new_rng(__func__);
1✔
377

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

388
   Botan::X509_Cert_Options opts_1 = ca_opts();
1✔
389
   opts_1.extensions.add(std::move(blocks_1));
1✔
390

391
   auto cert_1 = make_self_signed(rng, opts_1);
1✔
392

393
   auto bits_1 = cert_1.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
394

395
   result.test_bin_eq(
1✔
396
      "extension is encoded as specified",
397
      bits_1,
398
      "3035302B040300010130240304040A00200304000A00400303000A01300C0304040A02300304000A02400303000A033006040200020500");
399

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

402
   auto ext_1_addr_fam_1 = ext_1->addr_blocks()[0];
1✔
403
   result.test_opt_u8_eq("extension 1 ipv4 safi", ext_1_addr_fam_1.safi(), 1);
1✔
404
   auto ext_1_ranges =
1✔
405
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_1_addr_fam_1.addr_choice()).ranges().value();
1✔
406
   result.test_bin_eq("extension 1 range 1 min", ext_1_ranges[0].min().value(), {10, 0, 32, 0});
1✔
407
   result.test_bin_eq("extension 1 range 1 max", ext_1_ranges[0].max().value(), {10, 0, 47, 255});
1✔
408

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

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

415
   result.test_bin_eq("extension 1 range 4 min", ext_1_ranges[3].min().value(), {10, 2, 48, 0});
1✔
416
   result.test_bin_eq("extension 1 range 4 max", ext_1_ranges[3].max().value(), {10, 2, 64, 255});
1✔
417

418
   result.test_bin_eq("extension 1 range 5 min", ext_1_ranges[4].min().value(), {10, 3, 0, 0});
1✔
419
   result.test_bin_eq("extension 1 range 5 max", ext_1_ranges[4].max().value(), {10, 3, 255, 255});
1✔
420

421
   result.test_opt_u8_eq("extension 1 ipv6 safi", ext_1->addr_blocks()[1].safi(), std::nullopt);
1✔
422
   result.test_is_true(
1✔
423
      "extension 1 ipv6 inherited",
424
      !std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_1->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
425

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

435
   Botan::X509_Cert_Options opts_2 = ca_opts();
1✔
436
   opts_2.extensions.add(std::move(blocks_2));
1✔
437

438
   auto cert_2 = make_self_signed(rng, opts_2);
1✔
439

440
   auto bits_2 = cert_2.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
441

442
   // see https://www.rfc-editor.org/errata/eid6792 as to why the B0 specified in the RFC is a AC here
443
   result.test_bin_eq("extension is encoded as specified",
1✔
444
                      bits_2,
445
                      "302C3010040300010130090302000A030304AC10300704030001020500300F040200023009030700200100000002");
446

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

449
   auto ext_2_addr_fam_1 = ext_2->addr_blocks()[0];
1✔
450
   result.test_opt_u8_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_1.safi(), 1);
1✔
451
   auto ext_2_ranges_1 =
1✔
452
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2_addr_fam_1.addr_choice()).ranges().value();
1✔
453
   result.test_bin_eq("extension 2 fam 1 range 1 min", ext_2_ranges_1[0].min().value(), {10, 0, 0, 0});
1✔
454
   result.test_bin_eq("extension 2 fam 1 range 1 max", ext_2_ranges_1[0].max().value(), {10, 255, 255, 255});
1✔
455

456
   result.test_bin_eq("extension 2 fam 1 range 2 min", ext_2_ranges_1[1].min().value(), {172, 16, 0, 0});
1✔
457
   result.test_bin_eq("extension 2 fam 1 range 2 max", ext_2_ranges_1[1].max().value(), {172, 31, 255, 255});
1✔
458

459
   result.test_opt_u8_eq("extension 2 ipv4 2 safi", ext_2->addr_blocks()[1].safi(), 2);
1✔
460
   result.test_is_true(
1✔
461
      "extension 2 ipv4 2 inherited",
462
      !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
463

464
   auto ext_2_addr_fam_3 = ext_2->addr_blocks()[2];
1✔
465
   result.test_opt_u8_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_3.safi(), std::nullopt);
1✔
466
   auto ext_2_ranges_3 =
1✔
467
      std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_2_addr_fam_3.addr_choice()).ranges().value();
1✔
468
   result.test_bin_eq("extension 2 fam 3 range 1 min",
1✔
469
                      ext_2_ranges_3[0].min().value(),
1✔
470
                      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
471
   result.test_bin_eq("extension 2 fam 3 range 1 max",
1✔
472
                      ext_2_ranges_3[0].max().value(),
1✔
473
                      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
474

475
   result.end_timer();
1✔
476
   return result;
2✔
477
}
7✔
478

479
Test::Result test_x509_ip_addr_blocks_encode_builder() {
1✔
480
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
481
   result.start_timer();
1✔
482

483
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
484
   auto rng = Test::new_rng(__func__);
1✔
485

486
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
1✔
487

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

491
   blocks->add_address<IPv4>({255, 255, 255, 255});
1✔
492
   // encoded as prefix
493
   blocks->add_address<IPv4>({190, 5, 0, 0}, {190, 5, 0b01111111, 255});
1✔
494
   // encoded as min, max
495
   blocks->add_address<IPv4>({127, 0, 0, 1}, {189, 5, 7, 255});
1✔
496

497
   // full address range
498
   blocks->add_address<IPv4>({0, 0, 0, 0}, {255, 255, 255, 255}, 1);
1✔
499

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

502
   Botan::X509_Cert_Options opts = ca_opts();
1✔
503
   opts.extensions.add(std::move(blocks));
1✔
504

505
   auto cert = make_self_signed(rng, opts);
1✔
506
   auto bits = cert.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
507

508
   // hand validated with https://lapo.it/asn1js/
509
   result.test_bin_eq(
1✔
510
      "extension is encoded as specified",
511
      bits,
512
      "304630290402000130230305007B7B0201300D0305007F000001030403BD0500030407BE0500030500FFFFFFFF300A04030001013003030100300D04030001023006030406C0A840");
513

514
   result.end_timer();
1✔
515
   return result;
1✔
516
}
2✔
517

518
namespace {
519

520
template <size_t I>
521
bool bit_set(size_t v) {
648✔
522
   if(((v >> I) & 1) == 1) {
648✔
523
      return true;
524
   } else {
525
      return false;
324✔
526
   }
527
}
528

529
}  // namespace
530

531
Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() {
1✔
532
   Test::Result result("X509 IP Address Block encode (ctor)");
1✔
533
   result.start_timer();
1✔
534

535
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
536

537
   auto rng = Test::new_rng(__func__);
1✔
538

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

541
   for(size_t i = 0; i < 64; i++) {
65✔
542
      const bool push_ipv4_ranges = bit_set<0>(i);
64✔
543
      const bool push_ipv6_ranges = bit_set<1>(i);
64✔
544
      const bool inherit_ipv4 = bit_set<2>(i);
64✔
545
      const bool inherit_ipv6 = bit_set<3>(i);
64✔
546
      const bool push_ipv4_family = bit_set<4>(i);
64✔
547
      const bool push_ipv6_family = bit_set<5>(i);
64✔
548

549
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
64✔
550

551
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
552
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
553
      a = {255, 255, 255, 255};
64✔
554
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
555

556
      // encoded as min, max
557
      a = {127, 0, 0, 1};
64✔
558
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
559
      a = {189, 5, 7, 255};
64✔
560
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
561

562
      // encoded as prefix
563
      a = {190, 5, 0, 0};
64✔
564
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
565
      a = {190, 5, 127, 255};
64✔
566
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
567

568
      a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
569
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
570
      a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
571
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
572

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

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

580
      // encoded as prefix
581
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
582
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
583
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
584
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
585

586
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
64✔
587
      auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
588
      auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
589
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
64✔
590

591
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
64✔
592
      auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
593
      auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
594
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
64✔
595

596
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
64✔
597
      if(push_ipv4_ranges) {
64✔
598
         ipv4_ranges.push_back(ipv4_range_1);
32✔
599
         ipv4_ranges.push_back(ipv4_range_2);
32✔
600
         ipv4_ranges.push_back(ipv4_range_3);
32✔
601
         ipv4_ranges.push_back(ipv4_range_4);
32✔
602
      }
603

604
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
64✔
605
      if(push_ipv6_ranges) {
64✔
606
         ipv6_ranges.push_back(ipv6_range_1);
32✔
607
         ipv6_ranges.push_back(ipv6_range_2);
32✔
608
         ipv6_ranges.push_back(ipv6_range_3);
32✔
609
         ipv6_ranges.push_back(ipv6_range_4);
32✔
610
      }
611

612
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
64✔
613
      if(!inherit_ipv4) {
64✔
614
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
96✔
615
      }
616

617
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
64✔
618
      if(!inherit_ipv6) {
64✔
619
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
96✔
620
      }
621

622
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
623
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
624

625
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
626
      if(push_ipv4_family) {
64✔
627
         addr_blocks.push_back(ipv4_addr_family);
32✔
628
      }
629
      if(push_ipv6_family) {
64✔
630
         addr_blocks.push_back(ipv6_addr_family);
32✔
631
      }
632

633
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
634

635
      opts.extensions.add(std::move(blocks));
64✔
636

637
      const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
64✔
638
      const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
639
      {
64✔
640
         const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
64✔
641
         result.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
64✔
642

643
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
644
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
645
            result.test_is_true("no address family entries", dec_addr_blocks.empty());
16✔
646
            continue;
16✔
647
         }
648

649
         if(push_ipv4_family) {
48✔
650
            auto family = dec_addr_blocks[0];
32✔
651
            result.test_is_true("ipv4 family afi", ipv4_addr_family.afi() == family.afi());
32✔
652
            result.test_opt_u8_eq("ipv4 family safi", ipv4_addr_family.safi(), family.safi());
32✔
653
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
32✔
654

655
            if(!inherit_ipv4) {
32✔
656
               auto ranges = choice.ranges().value();
16✔
657
               if(push_ipv4_ranges) {
16✔
658
                  result.test_bin_eq("ipv4 entry 0 min", ranges[0].min().value(), ipv4_range_1.min().value());
8✔
659
                  result.test_bin_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
8✔
660
                  result.test_bin_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
8✔
661
                  result.test_bin_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
8✔
662
                  result.test_bin_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
8✔
663
                  result.test_bin_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
8✔
664
                  result.test_bin_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
8✔
665
                  result.test_bin_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
8✔
666
               } else {
667
                  result.test_is_true("ipv4 range has no entries", ranges.empty());
8✔
668
               }
669
            } else {
16✔
670
               result.test_is_false("ipv4 family inherit", choice.ranges().has_value());
16✔
671
            }
672
         }
64✔
673

674
         if(push_ipv6_family) {
48✔
675
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
676
            result.test_is_true("ipv6 family afi", ipv6_addr_family.afi() == family.afi());
32✔
677
            result.test_opt_u8_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
32✔
678
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
32✔
679
            if(!inherit_ipv6) {
32✔
680
               auto ranges = choice.ranges().value();
16✔
681
               if(push_ipv6_ranges) {
16✔
682
                  result.test_bin_eq("ipv6 entry 0 min", ranges[0].min().value(), ipv6_range_1.min().value());
8✔
683
                  result.test_bin_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
8✔
684
                  result.test_bin_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
8✔
685
                  result.test_bin_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
8✔
686
                  result.test_bin_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
8✔
687
                  result.test_bin_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
8✔
688
                  result.test_bin_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
8✔
689
                  result.test_bin_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
8✔
690
               } else {
691
                  result.test_is_true("ipv6 range has no entries", ranges.empty());
8✔
692
               }
693
            } else {
16✔
694
               result.test_is_false("ipv6 family inherit", choice.ranges().has_value());
16✔
695
            }
696
         }
64✔
697
      }
698
   }
320✔
699

700
   result.end_timer();
1✔
701
   return result;
2✔
702
}
2✔
703

704
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() {
1✔
705
   Test::Result result("X509 IP Address Block encode edge cases (ctor)");
1✔
706
   result.start_timer();
1✔
707

708
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
709

710
   auto rng = Test::new_rng(__func__);
1✔
711

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

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

718
   for(size_t i = 0; i < edge_values.size(); i++) {
22✔
719
      for(size_t j = 0; j < 4; j++) {
105✔
720
         const bool modify_min = bit_set<0>(j);
84✔
721
         const bool modify_max = bit_set<1>(j);
84✔
722

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

730
            Botan::X509_Cert_Options opts = req_opts(sig_algo);
1,135✔
731

732
            std::vector<uint8_t> min_bytes(16, 0x00);
1,135✔
733
            std::vector<uint8_t> max_bytes(16, 0xFF);
1,135✔
734

735
            if(modify_min) {
1,135✔
736
               min_bytes[15 - (k < 2 ? 0 : k - 2)] = edge_values[i];
756✔
737
            }
738
            if(modify_max) {
1,135✔
739
               max_bytes[15 - (k > 15 ? 15 : k)] = edge_values[i];
756✔
740
            }
741

742
            auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
1,135✔
743
            auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
1,135✔
744

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

747
            std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
1,135✔
748
            ipv6_ranges.push_back(ipv6_range);
1,135✔
749

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

752
            auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3,405✔
753

754
            std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1,135✔
755
            addr_blocks.push_back(ipv6_addr_family);
1,135✔
756

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

759
            opts.extensions.add(std::move(blocks));
1,135✔
760

761
            const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1,135✔
762
            const Botan::X509_Certificate cert =
1,135✔
763
               ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1,135✔
764
            {
1,135✔
765
               const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1,135✔
766
               result.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
1,135✔
767
               const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1,135✔
768
               auto family = dec_addr_blocks[0];
1,135✔
769
               result.test_is_true("ipv6 family afi", ipv6_addr_family.afi() == family.afi());
1,135✔
770
               result.test_opt_u8_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
1,135✔
771
               auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
1,135✔
772
               auto ranges = choice.ranges().value();
1,135✔
773

774
               result.test_bin_eq("ipv6 edge case min", ranges[0].min().value(), ipv6_range.min().value());
1,135✔
775
               result.test_bin_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
1,135✔
776
            }
2,270✔
777
         }
5,675✔
778
      }
779
   }
780
   result.end_timer();
1✔
781
   return result;
2✔
782
}
3✔
783

784
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
785
   Test::Result result("X509 IP Address Block range merge");
1✔
786
   result.start_timer();
1✔
787

788
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
789

790
   auto rng = Test::new_rng(__func__);
1✔
791

792
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
793
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
794

795
   const std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
796
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
797
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
798
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
799
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
800
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
801
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
802
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
803
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
804
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
805
   };
46✔
806

807
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv6_ranges;
1✔
808
   for(auto pair : addresses) {
10✔
809
      auto address_min = IPAddressBlocks::IPAddress<IPv4>(pair[0]);
9✔
810
      auto address_max = IPAddressBlocks::IPAddress<IPv4>(pair[1]);
9✔
811
      auto range = IPAddressBlocks::IPAddressOrRange<IPv4>(address_min, address_max);
9✔
812
      ipv6_ranges.push_back(range);
9✔
813
   }
9✔
814

815
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
1✔
816
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
817

818
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
819
   addr_blocks.push_back(ipv6_addr_family);
1✔
820

821
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
822

823
   opts.extensions.add(std::move(blocks));
1✔
824

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

835
      const std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
836
      const std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
837

838
      result.test_bin_eq("range expected min", ranges[0].min().value(), expected_min);
1✔
839
      result.test_bin_eq("range expected max", ranges[0].max().value(), expected_max);
1✔
840
      result.test_sz_eq("range length", ranges.size(), 1);
1✔
841
   }
2✔
842

843
   result.end_timer();
1✔
844
   return result;
2✔
845
}
24✔
846

847
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
848
   Test::Result result("X509 IP Address Block family merge");
1✔
849
   result.start_timer();
1✔
850

851
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
852

853
   auto rng = Test::new_rng(__func__);
1✔
854

855
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
856
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
857

858
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
859

860
   IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
1✔
861
   IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
1✔
862

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

867
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> v4_choice_vec{
1✔
868
      IPAddressBlocks::IPAddressOrRange<IPv4>(IPAddressBlocks::IPAddress<IPv4>({v4_addr_1}))};
1✔
869
   IPAddressBlocks::IPAddressChoice<IPv4> v4_choice_dupl(v4_choice_vec);
1✔
870
   result.test_is_true("IPAddressChoice v4 merges ranges already in constructor",
1✔
871
                       v4_choice_dupl.ranges().value().size() == 1);
1✔
872
   const IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
873

874
   const uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
875
   const IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
1✔
876

877
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> v6_choice_vec{
1✔
878
      IPAddressBlocks::IPAddressOrRange<IPv6>(IPAddressBlocks::IPAddress<IPv6>({v6_addr_1}))};
1✔
879
   IPAddressBlocks::IPAddressChoice<IPv6> v6_choice_dupl(v6_choice_vec);
1✔
880
   result.test_is_true("IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1);
1✔
881
   const IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
882

883
   const IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
884
   const IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
885

886
   const IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
887
   const IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
888

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

905
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
906
      v4_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_empty_fam, v6_fam_dupl, v6_empty_fam_safi};
7✔
907

908
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
909

910
   opts.extensions.add(std::move(blocks));
1✔
911

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

915
   const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
916
   result.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
1✔
917
   const auto& dec_blocks = ip_blocks->addr_blocks();
1✔
918

919
   result.test_is_true("blocks got merged lengthwise", dec_blocks.size() == expected_blocks.size());
1✔
920

921
   bool sorted = true;
1✔
922
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
923
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
924
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
925

926
      uint32_t afam_a = a.afi();
5✔
927
      if(a.safi().has_value()) {
5✔
928
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
929
      } else {
930
         afam_a = static_cast<uint32_t>(afam_a << 8);
2✔
931
      }
932

933
      uint32_t afam_b = b.afi();
5✔
934
      if(b.safi().has_value()) {
5✔
935
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
936
      } else {
937
         afam_b = static_cast<uint32_t>(afam_b << 8);
1✔
938
      }
939

940
      if(afam_a > afam_b) {
5✔
941
         sorted = false;
942
         break;
943
      }
944
   }
945

946
   result.test_is_true("blocks got sorted", sorted);
1✔
947

948
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
949
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
950
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
951

952
      result.test_is_true("blocks match push order by afi at index " + std::to_string(i), dec.afi() == exp.afi());
18✔
953
      result.test_opt_u8_eq("blocks match push order by safi at index " + std::to_string(i), dec.safi(), exp.safi());
18✔
954

955
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
956
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
3✔
957
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
3✔
958

959
         if(!exp_choice.ranges().has_value()) {
3✔
960
            result.test_is_false("block ranges should inherit at index " + std::to_string(i),
4✔
961
                                 dec_choice.ranges().has_value());
2✔
962
         } else {
963
            result.test_is_true("block ranges should not inherit at index " + std::to_string(i),
2✔
964
                                dec_choice.ranges().has_value());
1✔
965

966
            if(dec_choice.ranges().has_value() == false) {
1✔
967
               continue;
×
968
            }
969

970
            auto dec_ranges = dec_choice.ranges().value();
1✔
971
            auto exp_ranges = exp_choice.ranges().value();
1✔
972
            result.test_sz_eq("block ranges got merged lengthwise at index " + std::to_string(i),
3✔
973
                              dec_ranges.size(),
974
                              exp_ranges.size());
975

976
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
977
               continue;
×
978
            }
979

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

995
         if(!exp_choice.ranges().has_value()) {
3✔
996
            result.test_is_false("block ranges should inherit at index " + std::to_string(i),
4✔
997
                                 dec_choice.ranges().has_value());
2✔
998
         } else {
999
            result.test_is_true("block ranges should not inherit at index " + std::to_string(i),
2✔
1000
                                dec_choice.ranges().has_value());
1✔
1001

1002
            if(dec_choice.ranges().has_value() == false) {
1✔
1003
               continue;
×
1004
            }
1005

1006
            auto dec_ranges = dec_choice.ranges().value();
1✔
1007
            auto exp_ranges = exp_choice.ranges().value();
1✔
1008
            result.test_sz_eq("block ranges got merged lengthwise at index " + std::to_string(i),
3✔
1009
                              dec_ranges.size(),
1010
                              exp_ranges.size());
1011

1012
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
1013
               continue;
×
1014
            }
1015

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

1030
   result.end_timer();
1✔
1031
   return result;
2✔
1032
}
19✔
1033

1034
Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() {
1✔
1035
   Test::Result result("X509 IP Address Blocks path validation success (builder)");
1✔
1036
   result.start_timer();
1✔
1037

1038
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1039
   auto rng = Test::new_rng(__func__);
1✔
1040

1041
   /*
1042
   Creates a certificate chain of length 4.
1043
   Root: ipv4 and ipv6
1044
   Inherit: has both values as 'inherit'
1045
   Dynamic: has either both 'inherit', both with values, or just one with a value
1046
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1047
   */
1048

1049
   // Root cert
1050
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
1✔
1051

1052
   root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
1✔
1053
   root_blocks->add_address<IPv4>({10, 0, 0, 1}, {10, 255, 255, 255}, 42);
1✔
1054

1055
   root_blocks->add_address<IPv6>(
1✔
1056
      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1057
      {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1058
   root_blocks->add_address<IPv6>(
1✔
1059
      {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1060
      {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1061

1062
   // Inherit cert
1063
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>();
1✔
1064

1065
   inherit_blocks->inherit<IPv4>(42);
1✔
1066
   inherit_blocks->inherit<IPv6>();
1✔
1067

1068
   // Subject cert
1069
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
1✔
1070

1071
   sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, 42);
1✔
1072
   sub_blocks->add_address<IPv4>({10, 0, 2, 1}, {10, 42, 0, 255}, 42);
1✔
1073

1074
   sub_blocks->add_address<IPv6>(
1✔
1075
      {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1076
      {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1077

1078
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1079
   root_opts.extensions.add(std::move(root_blocks));
1✔
1080
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1081
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1082
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1083
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1084

1085
   Botan::Certificate_Store_In_Memory trusted;
1✔
1086
   trusted.add_certificate(root_cert);
1✔
1087

1088
   for(size_t i = 0; i < 4; i++) {
5✔
1089
      const bool include_v4 = bit_set<0>(i);
4✔
1090
      const bool include_v6 = bit_set<1>(i);
4✔
1091

1092
      // Dynamic Cert
1093
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>();
4✔
1094
      if(include_v4) {
4✔
1095
         dyn_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
2✔
1096
         dyn_blocks->add_address<IPv4>({10, 0, 0, 255}, {10, 255, 0, 1}, 42);
2✔
1097
      } else {
1098
         dyn_blocks->inherit<IPv4>(42);
2✔
1099
      }
1100

1101
      if(include_v6) {
4✔
1102
         dyn_blocks->add_address<IPv6>(
2✔
1103
            {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
1104
            {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1105
      } else {
1106
         dyn_blocks->inherit<IPv6>();
2✔
1107
      }
1108

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

1111
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1112
      const Botan::X509_Certificate sub_cert =
4✔
1113
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1114

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

1118
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1119
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1120
   }
8✔
1121

1122
   result.end_timer();
1✔
1123
   return result;
2✔
1124
}
7✔
1125

1126
Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() {
1✔
1127
   Test::Result result("X509 IP Address Block path validation success (ctor)");
1✔
1128
   result.start_timer();
1✔
1129

1130
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1131
   auto rng = Test::new_rng(__func__);
1✔
1132

1133
   /*
1134
   Creates a certificate chain of length 4.
1135
   Root: ipv4 and ipv6
1136
   Inherit: has both values as 'inherit'
1137
   Dynamic: has either both 'inherit', both with values, or just one with a value
1138
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1139
   */
1140

1141
   // Root cert
1142
   std::vector<uint8_t> a = {120, 0, 0, 1};
1✔
1143
   auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1144
   a = {130, 140, 150, 160};
1✔
1145
   auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1146

1147
   a = {10, 0, 0, 1};
1✔
1148
   auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1149
   a = {10, 255, 255, 255};
1✔
1150
   auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1151

1152
   a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1153
   auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1154
   a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1155
   auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1156

1157
   a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1158
   auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1159
   a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1160
   auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1161

1162
   auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
1✔
1163
   auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
1✔
1164
   auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
1✔
1165
   auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
1✔
1166

1167
   auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
1✔
1168
   auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
1✔
1169

1170
   auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
1✔
1171
   auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
1✔
1172

1173
   auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
3✔
1174
   auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
3✔
1175

1176
   auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
6✔
1177
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
1✔
1178

1179
   // Inherit cert
1180
   auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
1✔
1181
   auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
1✔
1182

1183
   auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
2✔
1184
   auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
2✔
1185

1186
   auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
6✔
1187
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
1✔
1188

1189
   // Dynamic Cert
1190
   a = {122, 0, 0, 255};
1✔
1191
   auto dyn_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1192
   a = {128, 255, 255, 255};
1✔
1193
   auto dyn_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1194
   a = {10, 0, 0, 255};
1✔
1195
   auto dyn_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1196
   a = {10, 255, 0, 1};
1✔
1197
   auto dyn_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1198

1199
   a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1200
   auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1201
   a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1202
   auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1203

1204
   auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
1✔
1205
   auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
1✔
1206
   auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
1✔
1207

1208
   auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
1✔
1209
   auto dyn_ipv6_ranges = {dyn_ipv6_range};
1✔
1210

1211
   // Subject cert
1212
   a = {124, 0, 255, 0};
1✔
1213
   auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1214
   a = {126, 0, 0, 1};
1✔
1215
   auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1216

1217
   a = {10, 0, 2, 1};
1✔
1218
   auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1219
   a = {10, 42, 0, 255};
1✔
1220
   auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1221

1222
   a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1223
   auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1224
   a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1225
   auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1226

1227
   auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
1✔
1228
   auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
1✔
1229
   auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
1✔
1230

1231
   auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
1✔
1232
   auto sub_ipv6_ranges = {sub_ipv6_range};
1✔
1233

1234
   auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
1✔
1235
   auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
1✔
1236

1237
   auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
3✔
1238
   auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
3✔
1239

1240
   auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
6✔
1241
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
1✔
1242

1243
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1244
   root_opts.extensions.add(std::move(root_blocks));
1✔
1245
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1246
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1247
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1248
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1249

1250
   Botan::Certificate_Store_In_Memory trusted;
1✔
1251
   trusted.add_certificate(root_cert);
1✔
1252

1253
   for(size_t i = 0; i < 4; i++) {
5✔
1254
      const bool include_v4 = bit_set<0>(i);
4✔
1255
      const bool include_v6 = bit_set<1>(i);
4✔
1256

1257
      auto dyn_ipv4_choice =
4✔
1258
         IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
8✔
1259
      auto dyn_ipv6_choice =
4✔
1260
         IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
8✔
1261

1262
      auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
10✔
1263
      auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
10✔
1264

1265
      auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
24✔
1266
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
4✔
1267

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

1270
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1271
      const Botan::X509_Certificate sub_cert =
4✔
1272
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1273

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

1277
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1278
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1279
   }
28✔
1280

1281
   result.end_timer();
1✔
1282
   return result;
2✔
1283
}
24✔
1284

1285
Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() {
1✔
1286
   Test::Result result("X509 IP Address Blocks path validation failure (builder)");
1✔
1287
   result.start_timer();
1✔
1288

1289
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1290
   auto rng = Test::new_rng(__func__);
1✔
1291

1292
   for(size_t i = 0; i < 7; i++) {
8✔
1293
      const bool all_inherit = (i == 0);
7✔
1294
      const bool different_safi = (i == 1);
7✔
1295
      const bool too_small_subrange = (i == 2);
7✔
1296
      const bool too_large_subrange = (i == 3);
7✔
1297
      const bool no_more_issuer_ranges = (i == 4);
7✔
1298
      const bool empty_issuer_ranges = (i == 5);
7✔
1299
      const bool nullptr_extensions = (i == 6);
7✔
1300

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

1309
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1310
      if(!nullptr_extensions) {
7✔
1311
         root_opts.extensions.add(std::move(root_blocks));
12✔
1312
      }
1313
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1314

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

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

1329
      // Subject cert
1330
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
7✔
1331

1332
      uint8_t safi = different_safi ? 41 : 42;
7✔
1333

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

1348
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1349
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1350

1351
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1352
      const Botan::X509_Certificate sub_cert =
7✔
1353
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1354

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

1358
      Botan::Certificate_Store_In_Memory trusted;
7✔
1359
      trusted.add_certificate(root_cert);
7✔
1360

1361
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1362
      result.require("path validation fails", !path_result.successful_validation());
7✔
1363
   }
15✔
1364

1365
   result.end_timer();
1✔
1366
   return result;
1✔
1367
}
8✔
1368

1369
Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() {
1✔
1370
   Test::Result result("X509 IP Address Block path validation failure (ctor)");
1✔
1371
   result.start_timer();
1✔
1372

1373
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1374
   auto rng = Test::new_rng(__func__);
1✔
1375

1376
   for(size_t i = 0; i < 7; i++) {
8✔
1377
      const bool all_inherit = (i == 0);
7✔
1378
      const bool different_safi = (i == 1);
7✔
1379
      const bool too_small_subrange = (i == 2);
7✔
1380
      const bool too_large_subrange = (i == 3);
7✔
1381
      const bool no_more_issuer_ranges = (i == 4);
7✔
1382
      const bool empty_issuer_ranges = (i == 5);
7✔
1383
      const bool nullptr_extensions = (i == 6);
7✔
1384

1385
      // Root cert
1386
      std::vector<uint8_t> a = {120, 0, 0, 1};
7✔
1387
      auto root_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1388
      a = {130, 140, 150, 160};
7✔
1389
      auto root_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1390

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

1399
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1400
      if(!nullptr_extensions) {
7✔
1401
         root_opts.extensions.add(std::move(root_blocks));
12✔
1402
      }
1403
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1404

1405
      // Issuer Cert
1406
      a = {122, 0, 0, 255};
7✔
1407
      auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1408
      a = {128, 255, 255, 255};
7✔
1409
      auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1410
      auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
7✔
1411

1412
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
7✔
1413

1414
      if(!empty_issuer_ranges) {
7✔
1415
         iss_ranges.push_back(iss_range_1);
6✔
1416
      }
1417

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

1424
      // Subject cert
1425
      if(too_small_subrange) {
7✔
1426
         a = {118, 0, 255, 0};
2✔
1427
      } else if(no_more_issuer_ranges) {
6✔
1428
         a = {140, 0, 0, 1};
2✔
1429
      } else {
1430
         a = {124, 0, 255, 0};
10✔
1431
      }
1432

1433
      auto sub_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1434
      if(too_large_subrange) {
7✔
1435
         a = {134, 0, 0, 1};
2✔
1436
      } else if(no_more_issuer_ranges) {
6✔
1437
         a = {150, 0, 0, 1};
2✔
1438
      } else {
1439
         a = {126, 0, 0, 1};
10✔
1440
      }
1441
      auto sub_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1442

1443
      auto sub_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_range_1_min, sub_range_1_max);
7✔
1444
      auto sub_ranges = {sub_range_1};
7✔
1445
      auto sub_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(sub_ranges));
14✔
1446
      auto sub_family = IPAddressBlocks::IPAddressFamily(sub_choice, different_safi ? 41 : 42);
26✔
1447

1448
      auto sub_addr_blocks = {sub_family};
21✔
1449
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
7✔
1450

1451
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1452
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1453

1454
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1455
      const Botan::X509_Certificate sub_cert =
7✔
1456
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1457

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

1461
      Botan::Certificate_Store_In_Memory trusted;
7✔
1462
      trusted.add_certificate(root_cert);
7✔
1463

1464
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1465
      result.require("path validation fails", !path_result.successful_validation());
7✔
1466
   }
82✔
1467

1468
   result.end_timer();
1✔
1469
   return result;
1✔
1470
}
8✔
1471

1472
Test::Result test_x509_as_blocks_rfc3779_example() {
1✔
1473
   Test::Result result("X509 AS Blocks rfc3779 example");
1✔
1474
   result.start_timer();
1✔
1475

1476
   using Botan::Cert_Extension::ASBlocks;
1✔
1477
   auto rng = Test::new_rng(__func__);
1✔
1478

1479
   // construct like in https://datatracker.ietf.org/doc/html/rfc3779#page-21
1480
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1481
   blocks->add_asnum(135);
1✔
1482
   blocks->add_asnum(3000, 3999);
1✔
1483
   blocks->add_asnum(5001);
1✔
1484
   blocks->inherit_rdi();
1✔
1485

1486
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1487
   opts.extensions.add(std::move(blocks));
1✔
1488

1489
   auto cert = make_self_signed(rng, opts);
1✔
1490
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1491

1492
   result.test_bin_eq(
1✔
1493
      "extension is encoded as specified", bits, "301AA014301202020087300802020BB802020F9F02021389A1020500");
1494

1495
   auto as_idents = cert.v3_extensions().get_extension_object_as<ASBlocks>()->as_identifiers();
1✔
1496
   auto as_ids = as_idents.asnum().value().ranges().value();
1✔
1497

1498
   result.test_is_true("extension has correct data", as_ids[0].min() == 135);
1✔
1499

1500
   result.end_timer();
1✔
1501
   return result;
2✔
1502
}
3✔
1503

1504
Test::Result test_x509_as_blocks_encode_builder() {
1✔
1505
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
1506
   result.start_timer();
1✔
1507

1508
   using Botan::Cert_Extension::ASBlocks;
1✔
1509
   auto rng = Test::new_rng(__func__);
1✔
1510

1511
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1512

1513
   blocks->add_rdi(10);
1✔
1514
   blocks->add_rdi(20, 30);
1✔
1515
   blocks->add_rdi(42, 300);
1✔
1516
   blocks->add_rdi(9, 301);
1✔
1517

1518
   blocks->inherit_asnum();
1✔
1519
   blocks->add_asnum(20);
1✔
1520
   // this overwrites the previous two
1521
   blocks->restrict_asnum();
1✔
1522

1523
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1524
   opts.extensions.add(std::move(blocks));
1✔
1525

1526
   auto cert = make_self_signed(rng, opts);
1✔
1527
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1528

1529
   result.test_bin_eq("extension is encoded as specified", bits, "3011A0023000A10B300930070201090202012D");
1✔
1530

1531
   result.end_timer();
1✔
1532
   return result;
1✔
1533
}
2✔
1534

1535
Test::Result test_x509_as_blocks_extension_encode_ctor() {
1✔
1536
   Test::Result result("X509 AS Blocks encode (ctor)");
1✔
1537
   result.start_timer();
1✔
1538

1539
   using Botan::Cert_Extension::ASBlocks;
1✔
1540

1541
   auto rng = Test::new_rng(__func__);
1✔
1542

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

1545
   for(size_t i = 0; i < 16; i++) {
17✔
1546
      const bool push_asnum = bit_set<0>(i);
16✔
1547
      const bool push_rdi = bit_set<1>(i);
16✔
1548
      const bool include_asnum = bit_set<2>(i);
16✔
1549
      const bool include_rdi = bit_set<3>(i);
16✔
1550
      if(!include_asnum && !include_rdi) {
16✔
1551
         continue;
4✔
1552
      }
1553

1554
      const ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
12✔
1555
      const ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
12✔
1556
      const ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
12✔
1557

1558
      const ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
12✔
1559
      const ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
12✔
1560
      const ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
12✔
1561

1562
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
12✔
1563
      if(push_asnum) {
12✔
1564
         as_ranges.push_back(asnum_id_or_range0);
6✔
1565
         as_ranges.push_back(asnum_id_or_range1);
6✔
1566
         as_ranges.push_back(asnum_id_or_range2);
6✔
1567
      }
1568

1569
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
12✔
1570
      if(push_rdi) {
12✔
1571
         rdi_ranges.push_back(rdi_id_or_range0);
6✔
1572
         rdi_ranges.push_back(rdi_id_or_range1);
6✔
1573
         rdi_ranges.push_back(rdi_id_or_range2);
6✔
1574
      }
1575

1576
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
12✔
1577
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
12✔
1578

1579
      const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
32✔
1580
                                                                    include_rdi ? std::optional(rdi) : std::nullopt);
32✔
1581

1582
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
12✔
1583

1584
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
12✔
1585
      opts.extensions.add(std::move(blocks));
12✔
1586

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

1590
      {
12✔
1591
         const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
12✔
1592
         result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
12✔
1593

1594
         const auto& identifier = as_blocks->as_identifiers();
12✔
1595

1596
         if(include_asnum) {
12✔
1597
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
1598

1599
            if(push_asnum) {
8✔
1600
               result.test_is_true("asnum entry 0 min", asnum_entries[0].min() == 0);
4✔
1601
               result.test_is_true("asnum entry 0 max", asnum_entries[0].max() == 999);
4✔
1602

1603
               result.test_is_true("asnum entry 1 min", asnum_entries[1].min() == 5042);
4✔
1604
               result.test_is_true("asnum entry 1 max", asnum_entries[1].max() == 4294967295);
4✔
1605
            } else {
1606
               result.test_is_true("asnum has no entries", asnum_entries.empty());
4✔
1607
            }
1608
         } else {
1609
            result.test_is_false("no asnum entry", identifier.asnum().has_value());
4✔
1610
         }
1611

1612
         if(include_rdi) {
12✔
1613
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
1614

1615
            if(push_rdi) {
8✔
1616
               result.test_is_true("rdi entry 0 min", rdi_entries[0].min() == 1234);
4✔
1617
               result.test_is_true("rdi entry 0 max", rdi_entries[0].max() == 5678);
4✔
1618

1619
               result.test_is_true("rdi entry 1 min", rdi_entries[1].min() == 32768);
4✔
1620
               result.test_is_true("rdi entry 1 max", rdi_entries[1].max() == 4294967295);
4✔
1621
            } else {
1622
               result.test_is_true("rdi has no entries", rdi_entries.empty());
4✔
1623
            }
1624
         } else {
1625
            result.test_is_false("rdi has no entry", identifier.rdi().has_value());
4✔
1626
         }
1627
      }
1628
   }
36✔
1629

1630
   result.end_timer();
1✔
1631
   return result;
2✔
1632
}
2✔
1633

1634
Test::Result test_x509_as_blocks_range_merge() {
1✔
1635
   Test::Result result("X509 AS Block range merge");
1✔
1636
   result.start_timer();
1✔
1637

1638
   using Botan::Cert_Extension::ASBlocks;
1✔
1639

1640
   auto rng = Test::new_rng(__func__);
1✔
1641

1642
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1643
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
1644

1645
   const std::vector<std::vector<uint16_t>> ranges = {
1✔
1646
      {2005, 37005},
1647
      {60, 70},
1648
      {22, 50},
1649
      {35, 2000},
1650
      {2001, 2004},
1651
      {21, 21},
1652
      {0, 20},
1653
   };
9✔
1654

1655
   std::vector<ASBlocks::ASIdOrRange> as_ranges;
1✔
1656
   as_ranges.reserve(ranges.size());
1✔
1657
   for(const auto& pair : ranges) {
8✔
1658
      as_ranges.emplace_back(pair[0], pair[1]);
7✔
1659
   }
1660

1661
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
1662

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

1665
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
1✔
1666

1667
   opts.extensions.add(std::move(blocks));
1✔
1668

1669
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
1670
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1671
   {
1✔
1672
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
1673
      result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
1674

1675
      const auto& identifier = as_blocks->as_identifiers();
1✔
1676

1677
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
1678

1679
      result.test_is_true("asnum entry 0 min", asnum_entries[0].min() == 0);
1✔
1680
      result.test_is_true("asnum entry 0 max", asnum_entries[0].max() == 37005);
1✔
1681
      result.test_is_true("asnum length", asnum_entries.size() == 1);
1✔
1682
   }
1683

1684
   result.end_timer();
1✔
1685
   return result;
2✔
1686
}
5✔
1687

1688
Test::Result test_x509_as_blocks_path_validation_success_builder() {
1✔
1689
   Test::Result result("X509 AS Block path validation success (builder)");
1✔
1690
   result.start_timer();
1✔
1691

1692
   using Botan::Cert_Extension::ASBlocks;
1✔
1693
   auto rng = Test::new_rng(__func__);
1✔
1694

1695
   /*
1696
   Creates a certificate chain of length 4.
1697
   Root: both asnum and rdi
1698
   Inherit: has both values as 'inherit'
1699
   Dynamic: has either both 'inherit', both with values, or just one with a value
1700
   Subject: both asnum and rdi as a subset of Root / Dynamic
1701
   */
1702

1703
   // Root Cert, both as and rdi
1704

1705
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
1✔
1706

1707
   root_blocks->add_asnum(0, 999);
1✔
1708
   root_blocks->add_asnum(5042);
1✔
1709
   root_blocks->add_asnum(5043, 4294967295);
1✔
1710

1711
   root_blocks->add_rdi(1234, 5678);
1✔
1712
   root_blocks->add_rdi(32768);
1✔
1713
   root_blocks->add_rdi(32769, 4294967295);
1✔
1714

1715
   // Inherit cert, both as 'inherit'
1716
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>();
1✔
1717
   inherit_blocks->inherit_asnum();
1✔
1718
   inherit_blocks->inherit_rdi();
1✔
1719

1720
   // Subject cert
1721

1722
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1723

1724
   sub_blocks->add_asnum(120, 180);
1✔
1725
   sub_blocks->add_asnum(220, 240);
1✔
1726
   sub_blocks->add_asnum(260, 511);
1✔
1727
   sub_blocks->add_asnum(678);
1✔
1728
   sub_blocks->add_asnum(5043, 5100);
1✔
1729

1730
   sub_blocks->add_rdi(1500, 2300);
1✔
1731
   sub_blocks->add_rdi(2500, 4000);
1✔
1732
   sub_blocks->add_rdi(1567);
1✔
1733
   sub_blocks->add_rdi(33100, 40000);
1✔
1734

1735
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1736
   root_opts.extensions.add(std::move(root_blocks));
1✔
1737
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1738
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1739
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1740
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1741

1742
   Botan::Certificate_Store_In_Memory trusted;
1✔
1743
   trusted.add_certificate(root_cert);
1✔
1744

1745
   for(size_t i = 0; i < 4; i++) {
5✔
1746
      const bool include_asnum = bit_set<0>(i);
4✔
1747
      const bool include_rdi = bit_set<1>(i);
4✔
1748

1749
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>();
4✔
1750
      if(include_asnum) {
4✔
1751
         dyn_blocks->add_asnum(100, 600);
2✔
1752
         dyn_blocks->add_asnum(678);
2✔
1753
         dyn_blocks->add_asnum(5042, 5101);
2✔
1754
      } else {
1755
         dyn_blocks->inherit_asnum();
2✔
1756
      }
1757

1758
      if(include_rdi) {
4✔
1759
         dyn_blocks->add_rdi(1500, 5000);
2✔
1760
         dyn_blocks->add_rdi(33000, 60000);
2✔
1761
      } else {
1762
         dyn_blocks->inherit_rdi();
2✔
1763
      }
1764

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

1767
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1768
      const Botan::X509_Certificate sub_cert =
4✔
1769
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1770

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

1774
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1775
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1776
   }
8✔
1777

1778
   result.end_timer();
1✔
1779
   return result;
2✔
1780
}
7✔
1781

1782
Test::Result test_x509_as_blocks_path_validation_success_ctor() {
1✔
1783
   Test::Result result("X509 AS Block path validation success (ctor)");
1✔
1784
   result.start_timer();
1✔
1785

1786
   using Botan::Cert_Extension::ASBlocks;
1✔
1787
   auto rng = Test::new_rng(__func__);
1✔
1788

1789
   /*
1790
   Creates a certificate chain of length 4.
1791
   Root: both asnum and rdi
1792
   Inherit: has both values as 'inherit'
1793
   Dynamic: has either both 'inherit', both with values, or just one with a value
1794
   Subject: both asnum and rdi as a subset of Root / Dynamic
1795
   */
1796

1797
   // Root Cert, both as and rdi
1798
   const ASBlocks::ASIdOrRange root_asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
1✔
1799
   const ASBlocks::ASIdOrRange root_asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
1✔
1800
   const ASBlocks::ASIdOrRange root_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
1✔
1801

1802
   const ASBlocks::ASIdOrRange root_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
1✔
1803
   const ASBlocks::ASIdOrRange root_rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
1✔
1804
   const ASBlocks::ASIdOrRange root_rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
1✔
1805

1806
   std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
1✔
1807
   root_as_ranges.push_back(root_asnum_id_or_range0);
1✔
1808
   root_as_ranges.push_back(root_asnum_id_or_range1);
1✔
1809
   root_as_ranges.push_back(root_asnum_id_or_range2);
1✔
1810

1811
   std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
1✔
1812
   root_rdi_ranges.push_back(root_rdi_id_or_range0);
1✔
1813
   root_rdi_ranges.push_back(root_rdi_id_or_range1);
1✔
1814
   root_rdi_ranges.push_back(root_rdi_id_or_range2);
1✔
1815

1816
   ASBlocks::ASIdentifierChoice root_asnum = ASBlocks::ASIdentifierChoice(root_as_ranges);
1✔
1817
   ASBlocks::ASIdentifierChoice root_rdi = ASBlocks::ASIdentifierChoice(root_rdi_ranges);
1✔
1818
   const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
4✔
1819
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
1✔
1820

1821
   // Inherit cert, both as 'inherit'
1822
   ASBlocks::ASIdentifierChoice inherit_asnum = ASBlocks::ASIdentifierChoice();
1✔
1823
   ASBlocks::ASIdentifierChoice inherit_rdi = ASBlocks::ASIdentifierChoice();
1✔
1824
   const ASBlocks::ASIdentifiers inherit_ident = ASBlocks::ASIdentifiers(inherit_asnum, inherit_rdi);
2✔
1825
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>(inherit_ident);
1✔
1826

1827
   // Dynamic cert
1828
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range0 = ASBlocks::ASIdOrRange(100, 600);
1✔
1829
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range1 = ASBlocks::ASIdOrRange(678);
1✔
1830
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5042, 5101);
1✔
1831

1832
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 5000);
1✔
1833
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range1 = ASBlocks::ASIdOrRange(33000, 60000);
1✔
1834

1835
   std::vector<ASBlocks::ASIdOrRange> dyn_as_ranges;
1✔
1836
   dyn_as_ranges.push_back(dyn_asnum_id_or_range0);
1✔
1837
   dyn_as_ranges.push_back(dyn_asnum_id_or_range1);
1✔
1838
   dyn_as_ranges.push_back(dyn_asnum_id_or_range2);
1✔
1839

1840
   std::vector<ASBlocks::ASIdOrRange> dyn_rdi_ranges;
1✔
1841
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range0);
1✔
1842
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range1);
1✔
1843

1844
   // Subject cert
1845
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1846
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1847
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1848
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1849
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1850

1851
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1852
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1853
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1854
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1855

1856
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1857
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1858
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1859
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1860
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1861
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1862

1863
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1864
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1865
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1866
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1867
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1868

1869
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1870
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1871
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1872
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1873

1874
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1875
   root_opts.extensions.add(std::move(root_blocks));
1✔
1876
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1877
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1878
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1879
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1880

1881
   Botan::Certificate_Store_In_Memory trusted;
1✔
1882
   trusted.add_certificate(root_cert);
1✔
1883

1884
   for(size_t i = 0; i < 4; i++) {
5✔
1885
      const bool include_asnum = bit_set<0>(i);
4✔
1886
      const bool include_rdi = bit_set<1>(i);
4✔
1887

1888
      ASBlocks::ASIdentifierChoice dyn_asnum =
4✔
1889
         ASBlocks::ASIdentifierChoice(include_asnum ? std::optional(dyn_as_ranges) : std::nullopt);
6✔
1890
      ASBlocks::ASIdentifierChoice dyn_rdi =
4✔
1891
         ASBlocks::ASIdentifierChoice(include_rdi ? std::optional(dyn_rdi_ranges) : std::nullopt);
6✔
1892
      const ASBlocks::ASIdentifiers dyn_ident = ASBlocks::ASIdentifiers(dyn_asnum, dyn_rdi);
12✔
1893
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>(dyn_ident);
4✔
1894

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

1897
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1898
      const Botan::X509_Certificate sub_cert =
4✔
1899
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1900

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

1904
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1905
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1906
   }
12✔
1907

1908
   result.end_timer();
1✔
1909
   return result;
2✔
1910
}
11✔
1911

1912
Test::Result test_x509_as_blocks_path_validation_extension_not_present_builder() {
1✔
1913
   Test::Result result("X509 AS Block path validation extension not present (builder)");
1✔
1914
   result.start_timer();
1✔
1915

1916
   using Botan::Cert_Extension::ASBlocks;
1✔
1917
   auto rng = Test::new_rng(__func__);
1✔
1918

1919
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1920
   sub_blocks->add_asnum(120, 180);
1✔
1921
   sub_blocks->add_asnum(220, 224);
1✔
1922
   sub_blocks->add_asnum(260, 511);
1✔
1923
   sub_blocks->add_asnum(678);
1✔
1924
   sub_blocks->add_asnum(5043, 5100);
1✔
1925

1926
   sub_blocks->add_rdi(1500, 2300);
1✔
1927
   sub_blocks->add_rdi(2500, 4000);
1✔
1928
   sub_blocks->add_rdi(1567);
1✔
1929
   sub_blocks->add_rdi(33100, 40000);
1✔
1930

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

1940
   Botan::Certificate_Store_In_Memory trusted;
1✔
1941
   trusted.add_certificate(root_cert);
1✔
1942

1943
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1944
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1945

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

1949
   result.end_timer();
1✔
1950
   return result;
2✔
1951
}
3✔
1952

1953
Test::Result test_x509_as_blocks_path_validation_extension_not_present_ctor() {
1✔
1954
   Test::Result result("X509 AS Block path validation extension not present (ctor)");
1✔
1955
   result.start_timer();
1✔
1956

1957
   using Botan::Cert_Extension::ASBlocks;
1✔
1958
   auto rng = Test::new_rng(__func__);
1✔
1959

1960
   // Subject cert
1961
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1962
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1963
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1964
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1965
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1966

1967
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1968
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1969
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1970
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1971

1972
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1973
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1974
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1975
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1976
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1977
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1978

1979
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1980
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1981
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1982
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1983
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1984

1985
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1986
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1987
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1988
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1989

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

1999
   Botan::Certificate_Store_In_Memory trusted;
1✔
2000
   trusted.add_certificate(root_cert);
1✔
2001

2002
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
2003
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
2004

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

2008
   result.end_timer();
1✔
2009
   return result;
2✔
2010
}
5✔
2011

2012
Test::Result test_x509_as_blocks_path_validation_failure_builder() {
1✔
2013
   Test::Result result("X509 AS Block path validation failure (builder)");
1✔
2014
   result.start_timer();
1✔
2015

2016
   using Botan::Cert_Extension::ASBlocks;
1✔
2017
   auto rng = Test::new_rng(__func__);
1✔
2018

2019
   /*
2020
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2021

2022
   Each test is expected to fail and creates the following certificate chain:
2023
   Root -> Issuer -> Subject
2024

2025
   00: set all the asnum choices to 'inherit' for each cert
2026
   01: 00 but for rdis
2027
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
2028
   03: 02 but for rdis
2029
   04: both 02 and 03
2030
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
2031
   06: 05 but for rdis
2032
   07: both 05 and 06
2033
   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
2034
   09: 08 but for rdis
2035
   10: both 08 and 09
2036
   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)
2037
   12: 11 but for rdis
2038
   13: both 11 and 12
2039
   14: 08 but using the minimum instead of the maximum
2040
   15: 14 but for rdis
2041
   16: both 14 and 15
2042
   17: same as 11 but using the minimum instead of the maximum
2043
   18: 17 but for rdis
2044
   19: both 18 and 19
2045
   20: make the issuer ranges empty but have an entry in the subject ranges
2046
   */
2047
   for(size_t i = 0; i < 21; i++) {
22✔
2048
      // enable / disable all the different edge cases
2049
      const bool inherit_all_asnums = (i == 0);
21✔
2050
      const bool inherit_all_rdis = (i == 1);
21✔
2051
      const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
2052
      const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
2053
      const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
2054
      const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
2055
      const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
2056
      const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
2057
      const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
2058
      const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
2059
      const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
2060
      const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
2061
      const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
2062
      const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
2063
      const bool empty_issuer_non_empty_subject = (i == 20);
21✔
2064

2065
      // Root cert
2066
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
21✔
2067

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

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

2104
      if(empty_issuer_non_empty_subject) {
21✔
2105
         root_blocks->restrict_asnum();
1✔
2106
         root_blocks->restrict_rdi();
1✔
2107
      }
2108

2109
      // Issuer cert
2110
      // the issuer cert has the same ranges as the root cert
2111
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2112
      auto issu_blocks = root_blocks->copy();
21✔
2113

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

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

2185
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2186
      root_opts.extensions.add(std::move(root_blocks));
21✔
2187
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2188
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2189

2190
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2191
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2192
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2193
      const Botan::X509_Certificate sub_cert =
21✔
2194
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2195

2196
      Botan::Certificate_Store_In_Memory trusted;
21✔
2197
      trusted.add_certificate(root_cert);
21✔
2198

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

2202
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
21✔
2203
      // in all cases, the validation should fail, since we are creating invalid scenarios
2204
      result.test_is_true("path validation fails at iteration " + std::to_string(i),
42✔
2205
                          !path_result.successful_validation());
21✔
2206
   }
42✔
2207

2208
   result.end_timer();
1✔
2209
   return result;
1✔
2210
}
22✔
2211

2212
Test::Result test_x509_as_blocks_path_validation_failure_ctor() {
1✔
2213
   Test::Result result("X509 AS Block path validation failure (ctor)");
1✔
2214
   result.start_timer();
1✔
2215

2216
   using Botan::Cert_Extension::ASBlocks;
1✔
2217
   auto rng = Test::new_rng(__func__);
1✔
2218

2219
   /*
2220
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2221

2222
   Each test is expected to fail and creates the following certificate chain:
2223
   Root -> Issuer -> Subject
2224

2225
   00: set all the asnum choices to 'inherit' for each cert
2226
   01: 00 but for rdis
2227
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
2228
   03: 02 but for rdis
2229
   04: both 02 and 03
2230
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
2231
   06: 05 but for rdis
2232
   07: both 05 and 06
2233
   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
2234
   09: 08 but for rdis
2235
   10: both 08 and 09
2236
   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)
2237
   12: 11 but for rdis
2238
   13: both 11 and 12
2239
   14: 08 but using the minimum instead of the maximum
2240
   15: 14 but for rdis
2241
   16: both 14 and 15
2242
   17: same as 11 but using the minimum instead of the maximum
2243
   18: 17 but for rdis
2244
   19: both 18 and 19
2245
   20: make the issuer ranges empty but have an entry in the subject ranges
2246
   */
2247
   for(size_t i = 0; i < 21; i++) {
22✔
2248
      // enable / disable all the different edge cases
2249
      const bool inherit_all_asnums = (i == 0);
21✔
2250
      const bool inherit_all_rdis = (i == 1);
21✔
2251
      const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
2252
      const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
2253
      const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
2254
      const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
2255
      const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
2256
      const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
2257
      const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
2258
      const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
2259
      const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
2260
      const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
2261
      const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
2262
      const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
2263
      const bool empty_issuer_non_empty_subject = (i == 20);
21✔
2264

2265
      // Root cert
2266
      std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
21✔
2267
      std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
21✔
2268

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

2285
      // same values but for rdis
2286
      if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
21✔
2287
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
2288
      } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
2289
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2290
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
2291
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
2292
      } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
2293
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2294
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
2295
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
2296
      }
2297

2298
      // Issuer cert
2299
      // the issuer cert has the same ranges as the root cert
2300
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2301
      const std::vector<ASBlocks::ASIdOrRange> issu_as_ranges;
21✔
2302
      const std::vector<ASBlocks::ASIdOrRange> issu_rdi_ranges;
21✔
2303

2304
      // Subject cert
2305
      std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
21✔
2306
      std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
21✔
2307

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

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

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

2371
      const ASBlocks::ASIdentifiers& issu_ident = root_ident;
21✔
2372
      std::unique_ptr<ASBlocks> issu_blocks = std::make_unique<ASBlocks>(issu_ident);
21✔
2373

2374
      ASBlocks::ASIdentifierChoice sub_asnum =
21✔
2375
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(sub_as_ranges));
41✔
2376
      ASBlocks::ASIdentifierChoice sub_rdi =
21✔
2377
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(sub_rdi_ranges));
41✔
2378
      const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
82✔
2379
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
21✔
2380

2381
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2382
      root_opts.extensions.add(std::move(root_blocks));
21✔
2383
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2384
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2385

2386
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2387
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2388
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2389
      const Botan::X509_Certificate sub_cert =
21✔
2390
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2391

2392
      Botan::Certificate_Store_In_Memory trusted;
21✔
2393
      trusted.add_certificate(root_cert);
21✔
2394

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

2398
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
21✔
2399
      // in all cases, the validation should fail, since we are creating invalid scenarios
2400
      result.test_is_true("path validation fails at iteration " + std::to_string(i),
42✔
2401
                          !path_result.successful_validation());
21✔
2402
   }
122✔
2403

2404
   result.end_timer();
1✔
2405
   return result;
1✔
2406
}
22✔
2407

2408
class X509_RPKI_Tests final : public Test {
1✔
2409
   public:
2410
      std::vector<Test::Result> run() override {
1✔
2411
         std::vector<Test::Result> results;
1✔
2412

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

2441
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
2442

2443
#endif
2444

2445
}  // namespace
2446

2447
}  // 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