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

randombit / botan / 20283898778

16 Dec 2025 09:52PM UTC coverage: 90.52% (+0.2%) from 90.36%
20283898778

Pull #5167

github

web-flow
Merge 795a38954 into 3d96b675e
Pull Request #5167: Changes to reduce unnecessary inclusions

101154 of 111748 relevant lines covered (90.52%)

12682929.61 hits per line

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

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

8
#include "tests.h"
9

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

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

26
namespace Botan_Tests {
27

28
namespace {
29

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

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

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

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

48
std::unique_ptr<Botan::Private_Key> generate_key(const std::string& algo, Botan::RandomNumberGenerator& rng) {
217✔
49
   std::string params;
217✔
50
   if(algo == "ECDSA") {
217✔
51
      params = "secp256r1";
217✔
52

53
   #if defined(BOTAN_HAS_ECC_GROUP)
54
      if(Botan::EC_Group::supports_named_group("secp192r1")) {
217✔
55
         params = "secp192r1";
217✔
56
      }
57
   #endif
58
   } else if(algo == "Ed25519") {
×
59
      params = "";
×
60
   } else if(algo == "RSA") {
×
61
      params = "1536";
×
62
   }
63

64
   return Botan::create_private_key(algo, rng, params);
217✔
65
}
217✔
66

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

70
   opts.uri = "https://botan.randombit.net";
149✔
71
   opts.dns = "botan.randombit.net";
149✔
72
   opts.email = "testing@randombit.net";
149✔
73
   opts.set_padding_scheme(sig_padding);
149✔
74

75
   opts.CA_key(1);
149✔
76

77
   return opts;
149✔
78
}
×
79

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

83
   opts.uri = "https://botan.randombit.net";
1,276✔
84
   opts.dns = "botan.randombit.net";
1,276✔
85
   opts.email = "testing@randombit.net";
1,276✔
86
   opts.set_padding_scheme(sig_padding);
1,276✔
87

88
   opts.not_before("160101200000Z");
1,276✔
89
   opts.not_after("300101200000Z");
1,276✔
90

91
   opts.challenge = "zoom";
1,276✔
92

93
   if(algo == "RSA") {
1,276✔
94
      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
×
95
   } else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") {
1,276✔
96
      opts.constraints = Botan::Key_Constraints::DigitalSignature;
1,276✔
97
   }
98

99
   return opts;
1,276✔
100
}
×
101

102
std::tuple<std::string, std::string, std::string> get_sig_algo_padding() {
149✔
103
   #if defined(BOTAN_HAS_ECDSA)
104
   const std::string sig_algo{"ECDSA"};
149✔
105
   const std::string padding_method;
149✔
106
   const std::string hash_fn{"SHA-256"};
149✔
107
   #elif defined(BOTAN_HAS_ED25519)
108
   const std::string sig_algo{"Ed25519"};
109
   const std::string padding_method;
110
   const std::string hash_fn{"SHA-512"};
111
   #elif defined(BOTAN_HAS_RSA)
112
   const std::string sig_algo{"RSA"};
113
   const std::string padding_method{"PKCS1v15(SHA-256)"};
114
   const std::string hash_fn{"SHA-256"};
115
   #endif
116

117
   return std::make_tuple(sig_algo, padding_method, hash_fn);
298✔
118
}
149✔
119

120
Botan::X509_Certificate make_self_signed(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
5✔
121
                                         const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
122
   auto [sig_algo, padding_method, hash_fn] = get_sig_algo_padding();
5✔
123
   auto key = generate_key(sig_algo, *rng);
5✔
124
   const auto cert = Botan::X509::create_self_signed_cert(opts, *key, hash_fn, *rng);
5✔
125

126
   return cert;
5✔
127
}
5✔
128

129
CA_Creation_Result make_ca(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
68✔
130
                           const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
131
   auto [sig_algo, padding_method, hash_fn] = get_sig_algo_padding();
68✔
132
   auto ca_key = generate_key(sig_algo, *rng);
68✔
133
   const auto ca_cert = Botan::X509::create_self_signed_cert(opts, *ca_key, hash_fn, *rng);
68✔
134
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
68✔
135
   auto sub_key = generate_key(sig_algo, *rng);
68✔
136

137
   return CA_Creation_Result{ca_cert, std::move(ca), std::move(sub_key), sig_algo, hash_fn};
204✔
138
}
136✔
139

140
std::pair<Botan::X509_Certificate, Botan::X509_CA> make_and_sign_ca(
76✔
141
   std::unique_ptr<Botan::Certificate_Extension> ext,
142
   Botan::X509_CA& parent_ca,
143
   std::unique_ptr<Botan::RandomNumberGenerator>& rng) {
144
   auto [sig_algo, padding_method, hash_fn] = get_sig_algo_padding();
76✔
145

146
   Botan::X509_Cert_Options opts = ca_opts();
76✔
147
   opts.extensions.add(std::move(ext));
76✔
148

149
   const std::unique_ptr<Botan::Private_Key> key = generate_key(sig_algo, *rng);
76✔
150

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

155
   return std::make_pair(std::move(cert), std::move(ca));
76✔
156
}
152✔
157

158
constexpr auto IPv4 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv4;
159
constexpr auto IPv6 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv6;
160

161
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
162

163
Test::Result test_x509_ip_addr_blocks_extension_decode() {
1✔
164
   Test::Result result("X509 IP Address Block decode");
1✔
165
   result.start_timer();
1✔
166
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
167

168
   {
1✔
169
      const std::string filename("IPAddrBlocksAll.pem");
1✔
170
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
171
      const auto* ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
172

173
      const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
174
      result.confirm("cert has IPAddrBlocks extension", ip_addr_blocks != nullptr, true);
2✔
175
      result.test_eq("cert has two IpAddrBlocks", addr_blocks.size(), 2);
1✔
176

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

180
      const auto& v4_blocks = ipv4block.ranges().value();
1✔
181

182
      // cert contains (in this order)
183
      // 192.168.0.0 - 192.168.127.255 (192.168.0.0/17)
184
      // 193.168.0.0 - 193.169.255.255 (193.168.0.0/15)
185
      // 194.168.0.0 - 195.175.1.2
186
      // 196.168.0.1 - 196.168.0.1 (196.168.0.1/32)
187

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

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

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

199
      const auto& v6_blocks = ipv6block.ranges().value();
1✔
200

201
      // cert contains (in this order)
202
      // fa80::/65
203
      // fe20::/37
204
      // 2003:0:6829:3435:420:10c5:0:c4/128
205
      // ab01:0:0:0:0:0:0:1-cd02:0:0:0:0:0:0:2
206

207
      result.test_eq("ipv6 block 0 min",
2✔
208
                     v6_blocks[0].min().value(),
1✔
209
                     {0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4});
210
      result.test_eq("ipv6 block 0 max",
2✔
211
                     v6_blocks[0].max().value(),
1✔
212
                     {0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4});
213
      result.test_eq("ipv6 block 1 min",
2✔
214
                     v6_blocks[1].min().value(),
1✔
215
                     {0xab, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01});
216
      result.test_eq("ipv6 block 1 max",
2✔
217
                     v6_blocks[1].max().value(),
1✔
218
                     {0xcd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02});
219
      result.test_eq("ipv6 block 2 min",
2✔
220
                     v6_blocks[2].min().value(),
1✔
221
                     {0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
222
      result.test_eq("ipv6 block 2 max",
2✔
223
                     v6_blocks[2].max().value(),
1✔
224
                     {0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
225
      result.test_eq("ipv6 block 3 min",
2✔
226
                     v6_blocks[3].min().value(),
1✔
227
                     {0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
228
      result.test_eq("ipv6 block 3 max",
2✔
229
                     v6_blocks[3].max().value(),
1✔
230
                     {0xfe, 0x20, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
231
   }
1✔
232
   {
1✔
233
      const std::string filename("IPAddrBlocksUnsorted.pem");
1✔
234
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
235
      const auto* ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
236

237
      // cert contains (in this order)
238
      // IPv6 (1) inherit
239
      // IPv6 0xff....0xff
240
      // IPv4 (2) inherit
241
      // IPv4 (1) 192.168.0.0 - 192.168.2.1
242
      // IPv4 (1) 192.168.2.2 - 200.0.0.0
243
      // IPv4 inherit
244

245
      // IPv4 ranges should be merged, IPv4 should come before IPv6, all should be sorted by safi
246

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

250
      result.test_eq("block 0 has no safi", addr_blocks[0].safi(), std::optional<uint8_t>{std::nullopt});
1✔
251
      result.confirm(
2✔
252
         "block 0 is inherited",
253
         !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[0].addr_choice()).ranges().has_value());
1✔
254

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

259
      result.confirm("block 1 has correct size", block_1.size() == 1);
2✔
260
      result.test_eq("block 1 min is correct", block_1[0].min().value(), {192, 168, 0, 0});
2✔
261
      result.test_eq("block 1 max is correct", block_1[0].max().value(), {200, 0, 0, 0});
2✔
262

263
      result.test_eq("block 2 has correct safi", addr_blocks[2].safi(), std::optional<uint8_t>{2});
1✔
264
      result.confirm(
2✔
265
         "block 2 is inherited",
266
         !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[2].addr_choice()).ranges().has_value());
1✔
267

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

272
      result.confirm("block 3 has correct size", block_3.size() == 1);
2✔
273
      result.test_eq("block 3 min is correct",
2✔
274
                     block_3[0].min().value(),
1✔
275
                     {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
276
      result.test_eq("block 3 max is correct",
2✔
277
                     block_3[0].max().value(),
1✔
278
                     {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
279

280
      result.test_eq("block 24 has correct safi", addr_blocks[4].safi(), std::optional<uint8_t>{1});
1✔
281
      result.confirm(
2✔
282
         "block 4 is inherited",
283
         !std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[4].addr_choice()).ranges().has_value());
1✔
284
   }
1✔
285
   {
1✔
286
      const std::string filename("InvalidIPAddrBlocks.pem");
1✔
287
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
288

289
      // cert contains the 10.0.32.0/20 prefix, but with a 9 for the unused bits
290

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

293
      const auto* ext = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
294
      result.confirm("extension is not decoded", ext == nullptr);
2✔
295
   }
1✔
296

297
   result.end_timer();
1✔
298
   return result;
1✔
299
}
×
300

301
Test::Result test_x509_as_blocks_extension_decode() {
1✔
302
   Test::Result result("X509 AS Block decode");
1✔
303
   result.start_timer();
1✔
304
   using Botan::Cert_Extension::ASBlocks;
1✔
305

306
   {
1✔
307
      const std::string filename("ASNumberCert.pem");
1✔
308
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
309

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

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

315
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
316
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
317

318
      // cert contains asnum 0-999, 5042, 0-4294967295
319
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
320
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
321

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

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

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

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

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

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

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

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

354
      // contains 1234-5678, 0-4294967295
355
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
356
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
357
   }
1✔
358
   {
1✔
359
      const std::string filename("ASNumberInherit.pem");
1✔
360
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
361

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

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

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

370
      // contains 1234-5678, 0-4294967295
371
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
372
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
373
   }
1✔
374

375
   result.end_timer();
1✔
376
   return result;
1✔
377
}
×
378

379
   #endif
380

381
Test::Result test_x509_ip_addr_blocks_rfc3779_example() {
1✔
382
   Test::Result result("X509 IP Address Blocks rfc3779 example");
1✔
383
   result.start_timer();
1✔
384

385
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
386
   auto rng = Test::new_rng(__func__);
1✔
387

388
   // construct like in https://datatracker.ietf.org/doc/html/rfc3779#page-18
389
   std::unique_ptr<IPAddressBlocks> blocks_1 = std::make_unique<IPAddressBlocks>();
1✔
390
   blocks_1->add_address<IPv4>({10, 0, 32, 0}, {10, 0, 47, 255}, 1);
1✔
391
   blocks_1->add_address<IPv4>({10, 0, 64, 0}, {10, 0, 64, 255}, 1);
1✔
392
   blocks_1->add_address<IPv4>({10, 1, 0, 0}, {10, 1, 255, 255}, 1);
1✔
393
   blocks_1->add_address<IPv4>({10, 2, 48, 0}, {10, 2, 63, 255}, 1);
1✔
394
   blocks_1->add_address<IPv4>({10, 2, 64, 0}, {10, 2, 64, 255}, 1);
1✔
395
   blocks_1->add_address<IPv4>({10, 3, 0, 0}, {10, 3, 255, 255}, 1);
1✔
396
   blocks_1->inherit<IPv6>();
1✔
397

398
   Botan::X509_Cert_Options opts_1 = ca_opts();
1✔
399
   opts_1.extensions.add(std::move(blocks_1));
1✔
400

401
   auto cert_1 = make_self_signed(rng, opts_1);
1✔
402

403
   auto bits_1 = cert_1.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
404

405
   result.test_eq(
1✔
406
      "extension is encoded as specified",
407
      bits_1,
408
      "3035302B040300010130240304040A00200304000A00400303000A01300C0304040A02300304000A02400303000A033006040200020500");
409

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

412
   auto ext_1_addr_fam_1 = ext_1->addr_blocks()[0];
1✔
413
   result.test_eq("extension 1 ipv4 safi", ext_1_addr_fam_1.safi(), std::optional<uint8_t>(1));
1✔
414
   auto ext_1_ranges =
1✔
415
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_1_addr_fam_1.addr_choice()).ranges().value();
1✔
416
   result.test_eq("extension 1 range 1 min", ext_1_ranges[0].min().value(), {10, 0, 32, 0});
2✔
417
   result.test_eq("extension 1 range 1 max", ext_1_ranges[0].max().value(), {10, 0, 47, 255});
2✔
418

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

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

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

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

431
   result.test_eq("extension 1 ipv6 safi", ext_1->addr_blocks()[1].safi(), std::optional<uint8_t>{std::nullopt});
1✔
432
   result.confirm(
2✔
433
      "extension 1 ipv6 inherited",
434
      !std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_1->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
435

436
   // https://datatracker.ietf.org/doc/html/rfc3779#page-20
437
   std::unique_ptr<IPAddressBlocks> blocks_2 = std::make_unique<IPAddressBlocks>();
1✔
438
   blocks_2->add_address<IPv6>(
1✔
439
      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
440
      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
441
   blocks_2->add_address<IPv4>({10, 0, 0, 0}, {10, 255, 255, 255}, 1);
1✔
442
   blocks_2->add_address<IPv4>({172, 16, 0, 0}, {172, 31, 255, 255}, 1);
1✔
443
   blocks_2->inherit<IPv4>(2);
1✔
444

445
   Botan::X509_Cert_Options opts_2 = ca_opts();
1✔
446
   opts_2.extensions.add(std::move(blocks_2));
1✔
447

448
   auto cert_2 = make_self_signed(rng, opts_2);
1✔
449

450
   auto bits_2 = cert_2.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
451

452
   // see https://www.rfc-editor.org/errata/eid6792 as to why the B0 specified in the RFC is a AC here
453
   result.test_eq("extension is encoded as specified",
1✔
454
                  bits_2,
455
                  "302C3010040300010130090302000A030304AC10300704030001020500300F040200023009030700200100000002");
456

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

459
   auto ext_2_addr_fam_1 = ext_2->addr_blocks()[0];
1✔
460
   result.test_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_1.safi(), std::optional<uint8_t>(1));
1✔
461
   auto ext_2_ranges_1 =
1✔
462
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2_addr_fam_1.addr_choice()).ranges().value();
1✔
463
   result.test_eq("extension 2 fam 1 range 1 min", ext_2_ranges_1[0].min().value(), {10, 0, 0, 0});
2✔
464
   result.test_eq("extension 2 fam 1 range 1 max", ext_2_ranges_1[0].max().value(), {10, 255, 255, 255});
2✔
465

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

469
   result.test_eq("extension 2 ipv4 2 safi", ext_2->addr_blocks()[1].safi(), std::optional<uint8_t>{2});
1✔
470
   result.confirm(
2✔
471
      "extension 2 ipv4 2 inherited",
472
      !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
473

474
   auto ext_2_addr_fam_3 = ext_2->addr_blocks()[2];
1✔
475
   result.test_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_3.safi(), std::optional<uint8_t>(std::nullopt));
1✔
476
   auto ext_2_ranges_3 =
1✔
477
      std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_2_addr_fam_3.addr_choice()).ranges().value();
1✔
478
   result.test_eq("extension 2 fam 3 range 1 min",
2✔
479
                  ext_2_ranges_3[0].min().value(),
1✔
480
                  {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
481
   result.test_eq("extension 2 fam 3 range 1 max",
2✔
482
                  ext_2_ranges_3[0].max().value(),
1✔
483
                  {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
484

485
   result.end_timer();
1✔
486
   return result;
2✔
487
}
7✔
488

489
Test::Result test_x509_ip_addr_blocks_encode_builder() {
1✔
490
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
491
   result.start_timer();
1✔
492

493
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
494
   auto rng = Test::new_rng(__func__);
1✔
495

496
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
1✔
497

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

501
   blocks->add_address<IPv4>({255, 255, 255, 255});
1✔
502
   // encoded as prefix
503
   blocks->add_address<IPv4>({190, 5, 0, 0}, {190, 5, 0b01111111, 255});
1✔
504
   // encoded as min, max
505
   blocks->add_address<IPv4>({127, 0, 0, 1}, {189, 5, 7, 255});
1✔
506

507
   // full address range
508
   blocks->add_address<IPv4>({0, 0, 0, 0}, {255, 255, 255, 255}, 1);
1✔
509

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

512
   Botan::X509_Cert_Options opts = ca_opts();
1✔
513
   opts.extensions.add(std::move(blocks));
1✔
514

515
   auto cert = make_self_signed(rng, opts);
1✔
516
   auto bits = cert.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
517

518
   // hand validated with https://lapo.it/asn1js/
519
   result.test_eq(
1✔
520
      "extension is encoded as specified",
521
      bits,
522
      "304630290402000130230305007B7B0201300D0305007F000001030403BD0500030407BE0500030500FFFFFFFF300A04030001013003030100300D04030001023006030406C0A840");
523

524
   result.end_timer();
1✔
525
   return result;
1✔
526
}
2✔
527

528
namespace {
529

530
template <size_t I>
531
bool bit_set(size_t v) {
648✔
532
   if(((v >> I) & 1) == 1) {
648✔
533
      return true;
534
   } else {
535
      return false;
324✔
536
   }
537
}
538

539
}  // namespace
540

541
Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() {
1✔
542
   Test::Result result("X509 IP Address Block encode (ctor)");
1✔
543
   result.start_timer();
1✔
544

545
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
546

547
   auto rng = Test::new_rng(__func__);
1✔
548

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

551
   for(size_t i = 0; i < 64; i++) {
65✔
552
      const bool push_ipv4_ranges = bit_set<0>(i);
64✔
553
      const bool push_ipv6_ranges = bit_set<1>(i);
64✔
554
      const bool inherit_ipv4 = bit_set<2>(i);
64✔
555
      const bool inherit_ipv6 = bit_set<3>(i);
64✔
556
      const bool push_ipv4_family = bit_set<4>(i);
64✔
557
      const bool push_ipv6_family = bit_set<5>(i);
64✔
558

559
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
64✔
560

561
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
562
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
563
      a = {255, 255, 255, 255};
64✔
564
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
565

566
      // encoded as min, max
567
      a = {127, 0, 0, 1};
64✔
568
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
569
      a = {189, 5, 7, 255};
64✔
570
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
571

572
      // encoded as prefix
573
      a = {190, 5, 0, 0};
64✔
574
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
575
      a = {190, 5, 127, 255};
64✔
576
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
577

578
      a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
579
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
580
      a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
581
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
582

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

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

590
      // encoded as prefix
591
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
592
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
593
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
594
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
595

596
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
64✔
597
      auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
598
      auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
599
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
64✔
600

601
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
64✔
602
      auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
603
      auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
604
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
64✔
605

606
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
64✔
607
      if(push_ipv4_ranges) {
64✔
608
         ipv4_ranges.push_back(ipv4_range_1);
32✔
609
         ipv4_ranges.push_back(ipv4_range_2);
32✔
610
         ipv4_ranges.push_back(ipv4_range_3);
32✔
611
         ipv4_ranges.push_back(ipv4_range_4);
32✔
612
      }
613

614
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
64✔
615
      if(push_ipv6_ranges) {
64✔
616
         ipv6_ranges.push_back(ipv6_range_1);
32✔
617
         ipv6_ranges.push_back(ipv6_range_2);
32✔
618
         ipv6_ranges.push_back(ipv6_range_3);
32✔
619
         ipv6_ranges.push_back(ipv6_range_4);
32✔
620
      }
621

622
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
64✔
623
      if(!inherit_ipv4) {
64✔
624
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
96✔
625
      }
626

627
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
64✔
628
      if(!inherit_ipv6) {
64✔
629
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
96✔
630
      }
631

632
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
633
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
634

635
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
636
      if(push_ipv4_family) {
64✔
637
         addr_blocks.push_back(ipv4_addr_family);
32✔
638
      }
639
      if(push_ipv6_family) {
64✔
640
         addr_blocks.push_back(ipv6_addr_family);
32✔
641
      }
642

643
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
644

645
      opts.extensions.add(std::move(blocks));
64✔
646

647
      const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
64✔
648
      const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
649
      {
64✔
650
         const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
64✔
651
         result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
128✔
652

653
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
654
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
655
            result.confirm("no address family entries", dec_addr_blocks.empty(), true);
32✔
656
            continue;
16✔
657
         }
658

659
         if(push_ipv4_family) {
48✔
660
            auto family = dec_addr_blocks[0];
32✔
661
            result.confirm("ipv4 family afi", ipv4_addr_family.afi() == family.afi(), true);
64✔
662
            result.test_eq("ipv4 family safi", ipv4_addr_family.safi(), family.safi());
32✔
663
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
32✔
664

665
            if(!inherit_ipv4) {
32✔
666
               auto ranges = choice.ranges().value();
16✔
667
               if(push_ipv4_ranges) {
16✔
668
                  result.test_eq("ipv4 entry 0 min", ranges[0].min().value(), ipv4_range_1.min().value());
16✔
669
                  result.test_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
16✔
670
                  result.test_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
16✔
671
                  result.test_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
16✔
672
                  result.test_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
16✔
673
                  result.test_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
16✔
674
                  result.test_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
16✔
675
                  result.test_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
16✔
676
               } else {
677
                  result.confirm("ipv4 range has no entries", ranges.empty(), true);
16✔
678
               }
679
            } else {
16✔
680
               result.confirm("ipv4 family inherit", choice.ranges().has_value(), false);
32✔
681
            }
682
         }
64✔
683

684
         if(push_ipv6_family) {
48✔
685
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
686
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
64✔
687
            result.test_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
32✔
688
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
32✔
689
            if(!inherit_ipv6) {
32✔
690
               auto ranges = choice.ranges().value();
16✔
691
               if(push_ipv6_ranges) {
16✔
692
                  result.test_eq("ipv6 entry 0 min", ranges[0].min().value(), ipv6_range_1.min().value());
16✔
693
                  result.test_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
16✔
694
                  result.test_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
16✔
695
                  result.test_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
16✔
696
                  result.test_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
16✔
697
                  result.test_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
16✔
698
                  result.test_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
16✔
699
                  result.test_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
16✔
700
               } else {
701
                  result.confirm("ipv6 range has no entries", ranges.empty(), true);
16✔
702
               }
703
            } else {
16✔
704
               result.confirm("ipv6 family inherit", choice.ranges().has_value(), false);
32✔
705
            }
706
         }
64✔
707
      }
708
   }
320✔
709

710
   result.end_timer();
1✔
711
   return result;
2✔
712
}
2✔
713

714
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() {
1✔
715
   Test::Result result("X509 IP Address Block encode edge cases (ctor)");
1✔
716
   result.start_timer();
1✔
717

718
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
719

720
   auto rng = Test::new_rng(__func__);
1✔
721

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

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

728
   for(size_t i = 0; i < edge_values.size(); i++) {
22✔
729
      for(size_t j = 0; j < 4; j++) {
105✔
730
         const bool modify_min = bit_set<0>(j);
84✔
731
         const bool modify_max = bit_set<1>(j);
84✔
732

733
         for(size_t k = 0; k < 18; k++) {
1,219✔
734
            if(!modify_min && !modify_max && (k > 0 || i > 0)) {
1,156✔
735
               // we don't modify anything, this is the extreme edge case of 0.0 ... - 255.255. ...
736
               // so we only need to do this once
737
               break;
738
            }
739

740
            Botan::X509_Cert_Options opts = req_opts(sig_algo);
1,135✔
741

742
            std::vector<uint8_t> min_bytes(16, 0x00);
1,135✔
743
            std::vector<uint8_t> max_bytes(16, 0xFF);
1,135✔
744

745
            if(modify_min) {
1,135✔
746
               min_bytes[15 - (k < 2 ? 0 : k - 2)] = edge_values[i];
756✔
747
            }
748
            if(modify_max) {
1,135✔
749
               max_bytes[15 - (k > 15 ? 15 : k)] = edge_values[i];
756✔
750
            }
751

752
            auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
1,135✔
753
            auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
1,135✔
754

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

757
            std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
1,135✔
758
            ipv6_ranges.push_back(ipv6_range);
1,135✔
759

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

762
            auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3,405✔
763

764
            std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1,135✔
765
            addr_blocks.push_back(ipv6_addr_family);
1,135✔
766

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

769
            opts.extensions.add(std::move(blocks));
1,135✔
770

771
            const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1,135✔
772
            const Botan::X509_Certificate cert =
1,135✔
773
               ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1,135✔
774
            {
1,135✔
775
               const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1,135✔
776
               result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2,270✔
777
               const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1,135✔
778
               auto family = dec_addr_blocks[0];
1,135✔
779
               result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
2,270✔
780
               result.test_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
1,135✔
781
               auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
1,135✔
782
               auto ranges = choice.ranges().value();
1,135✔
783

784
               result.test_eq("ipv6 edge case min", ranges[0].min().value(), ipv6_range.min().value());
2,270✔
785
               result.test_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
2,270✔
786
            }
2,270✔
787
         }
5,675✔
788
      }
789
   }
790
   result.end_timer();
1✔
791
   return result;
2✔
792
}
3✔
793

794
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
795
   Test::Result result("X509 IP Address Block range merge");
1✔
796
   result.start_timer();
1✔
797

798
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
799

800
   auto rng = Test::new_rng(__func__);
1✔
801

802
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
803
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
804

805
   const std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
806
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
807
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
808
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
809
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
810
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
811
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
812
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
813
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
814
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
815
   };
46✔
816

817
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv6_ranges;
1✔
818
   for(auto pair : addresses) {
10✔
819
      auto address_min = IPAddressBlocks::IPAddress<IPv4>(pair[0]);
9✔
820
      auto address_max = IPAddressBlocks::IPAddress<IPv4>(pair[1]);
9✔
821
      auto range = IPAddressBlocks::IPAddressOrRange<IPv4>(address_min, address_max);
9✔
822
      ipv6_ranges.push_back(range);
9✔
823
   }
9✔
824

825
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
1✔
826
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
827

828
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
829
   addr_blocks.push_back(ipv6_addr_family);
1✔
830

831
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
832

833
   opts.extensions.add(std::move(blocks));
1✔
834

835
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
836
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
837
   {
1✔
838
      const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
839
      result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
840
      const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1✔
841
      auto family = dec_addr_blocks[0];
1✔
842
      auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
1✔
843
      auto ranges = choice.ranges().value();
1✔
844

845
      const std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
846
      const std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
847

848
      result.test_eq("range expected min", ranges[0].min().value(), expected_min);
2✔
849
      result.test_eq("range expected max", ranges[0].max().value(), expected_max);
2✔
850
      result.test_eq("range length", ranges.size(), 1);
1✔
851
   }
2✔
852

853
   result.end_timer();
1✔
854
   return result;
2✔
855
}
24✔
856

857
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
858
   Test::Result result("X509 IP Address Block family merge");
1✔
859
   result.start_timer();
1✔
860

861
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
862

863
   auto rng = Test::new_rng(__func__);
1✔
864

865
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
866
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
867

868
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
869

870
   IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
1✔
871
   IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
1✔
872

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

877
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> v4_choice_vec{
1✔
878
      IPAddressBlocks::IPAddressOrRange<IPv4>(IPAddressBlocks::IPAddress<IPv4>({v4_addr_1}))};
1✔
879
   IPAddressBlocks::IPAddressChoice<IPv4> v4_choice_dupl(v4_choice_vec);
1✔
880
   result.confirm(
2✔
881
      "IPAddressChoice v4 merges ranges already in constructor", v4_choice_dupl.ranges().value().size() == 1, true);
1✔
882
   const IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
883

884
   const uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
885
   const IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
1✔
886

887
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> v6_choice_vec{
1✔
888
      IPAddressBlocks::IPAddressOrRange<IPv6>(IPAddressBlocks::IPAddress<IPv6>({v6_addr_1}))};
1✔
889
   IPAddressBlocks::IPAddressChoice<IPv6> v6_choice_dupl(v6_choice_vec);
1✔
890
   result.confirm(
2✔
891
      "IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1, true);
1✔
892
   const IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
893

894
   const IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
895
   const IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
896

897
   const IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
898
   const IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
899

900
   /*
901
   considering the push order, the resulting order should be
902
   [0] v4 no safi
903
   [2] v4 safi
904
   [1] v6 no safi
905
   [3] v6 safi
906
   */
907
   for(size_t i = 0; i < 3; i++) {
4✔
908
      addr_blocks.push_back(v4_empty_fam_safi);
3✔
909
      addr_blocks.push_back(v6_empty_fam);
3✔
910
      addr_blocks.push_back(v4_fam_dupl);
3✔
911
      addr_blocks.push_back(v6_empty_fam_safi);
3✔
912
      addr_blocks.push_back(v6_fam_dupl);
3✔
913
      addr_blocks.push_back(v4_empty_fam);
3✔
914
   }
915

916
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
917
      v4_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_empty_fam, v6_fam_dupl, v6_empty_fam_safi};
7✔
918

919
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
920

921
   opts.extensions.add(std::move(blocks));
1✔
922

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

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

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

932
   bool sorted = true;
1✔
933
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
934
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
935
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
936

937
      uint32_t afam_a = a.afi();
5✔
938
      if(a.safi().has_value()) {
5✔
939
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
940
      } else {
941
         afam_a = static_cast<uint32_t>(afam_a << 8);
2✔
942
      }
943

944
      uint32_t afam_b = b.afi();
5✔
945
      if(b.safi().has_value()) {
5✔
946
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
947
      } else {
948
         afam_b = static_cast<uint32_t>(afam_b << 8);
1✔
949
      }
950

951
      if(afam_a > afam_b) {
5✔
952
         sorted = false;
953
         break;
954
      }
955
   }
956

957
   result.confirm("blocks got sorted", sorted, true);
2✔
958

959
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
960
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
961
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
962

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

966
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
967
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
3✔
968
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
3✔
969

970
         if(!exp_choice.ranges().has_value()) {
3✔
971
            result.confirm(
2✔
972
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
973
         } else {
974
            result.confirm(
1✔
975
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
976

977
            if(dec_choice.ranges().has_value() == false) {
1✔
978
               continue;
×
979
            }
980

981
            auto dec_ranges = dec_choice.ranges().value();
1✔
982
            auto exp_ranges = exp_choice.ranges().value();
1✔
983
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
984
                           dec_ranges.size() == exp_ranges.size(),
1✔
985
                           true);
986

987
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
988
               continue;
×
989
            }
990

991
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
992
               result.test_eq(
2✔
993
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
994
                  exp_ranges[j].min().value(),
1✔
995
                  dec_ranges[j].min().value());
1✔
996
               result.test_eq(
2✔
997
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
998
                  exp_ranges[j].max().value(),
1✔
999
                  dec_ranges[j].max().value());
2✔
1000
            }
1001
         }
1✔
1002
      } else if((exp.afi() == 2) && (dec.afi() == 2)) {
7✔
1003
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(dec.addr_choice());
3✔
1004
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(exp.addr_choice());
3✔
1005

1006
         if(!exp_choice.ranges().has_value()) {
3✔
1007
            result.confirm(
2✔
1008
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
1009
         } else {
1010
            result.confirm(
1✔
1011
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
1012

1013
            if(dec_choice.ranges().has_value() == false) {
1✔
1014
               continue;
×
1015
            }
1016

1017
            auto dec_ranges = dec_choice.ranges().value();
1✔
1018
            auto exp_ranges = exp_choice.ranges().value();
1✔
1019
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
1020
                           dec_ranges.size() == exp_ranges.size(),
1✔
1021
                           true);
1022

1023
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
1024
               continue;
×
1025
            }
1026

1027
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
1028
               result.test_eq(
2✔
1029
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1030
                  exp_ranges[j].min().value(),
1✔
1031
                  dec_ranges[j].min().value());
1✔
1032
               result.test_eq(
2✔
1033
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1034
                  exp_ranges[j].max().value(),
1✔
1035
                  dec_ranges[j].max().value());
2✔
1036
            }
1037
         }
1✔
1038
      }
4✔
1039
   }
1040

1041
   result.end_timer();
1✔
1042
   return result;
2✔
1043
}
19✔
1044

1045
Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() {
1✔
1046
   Test::Result result("X509 IP Address Blocks path validation success (builder)");
1✔
1047
   result.start_timer();
1✔
1048

1049
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1050
   auto rng = Test::new_rng(__func__);
1✔
1051

1052
   /*
1053
   Creates a certificate chain of length 4.
1054
   Root: ipv4 and ipv6
1055
   Inherit: has both values as 'inherit'
1056
   Dynamic: has either both 'inherit', both with values, or just one with a value
1057
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1058
   */
1059

1060
   // Root cert
1061
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
1✔
1062

1063
   root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
1✔
1064
   root_blocks->add_address<IPv4>({10, 0, 0, 1}, {10, 255, 255, 255}, 42);
1✔
1065

1066
   root_blocks->add_address<IPv6>(
1✔
1067
      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1068
      {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1069
   root_blocks->add_address<IPv6>(
1✔
1070
      {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1071
      {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1072

1073
   // Inherit cert
1074
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>();
1✔
1075

1076
   inherit_blocks->inherit<IPv4>(42);
1✔
1077
   inherit_blocks->inherit<IPv6>();
1✔
1078

1079
   // Subject cert
1080
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
1✔
1081

1082
   sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, 42);
1✔
1083
   sub_blocks->add_address<IPv4>({10, 0, 2, 1}, {10, 42, 0, 255}, 42);
1✔
1084

1085
   sub_blocks->add_address<IPv6>(
1✔
1086
      {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1087
      {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1088

1089
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1090
   root_opts.extensions.add(std::move(root_blocks));
1✔
1091
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1092
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1093
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1094
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1095

1096
   Botan::Certificate_Store_In_Memory trusted;
1✔
1097
   trusted.add_certificate(root_cert);
1✔
1098

1099
   for(size_t i = 0; i < 4; i++) {
5✔
1100
      const bool include_v4 = bit_set<0>(i);
4✔
1101
      const bool include_v6 = bit_set<1>(i);
4✔
1102

1103
      // Dynamic Cert
1104
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>();
4✔
1105
      if(include_v4) {
4✔
1106
         dyn_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
2✔
1107
         dyn_blocks->add_address<IPv4>({10, 0, 0, 255}, {10, 255, 0, 1}, 42);
2✔
1108
      } else {
1109
         dyn_blocks->inherit<IPv4>(42);
2✔
1110
      }
1111

1112
      if(include_v6) {
4✔
1113
         dyn_blocks->add_address<IPv6>(
2✔
1114
            {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
1115
            {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1116
      } else {
1117
         dyn_blocks->inherit<IPv6>();
2✔
1118
      }
1119

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

1122
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1123
      const Botan::X509_Certificate sub_cert =
4✔
1124
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1125

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

1129
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1130
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1131
   }
8✔
1132

1133
   result.end_timer();
1✔
1134
   return result;
2✔
1135
}
7✔
1136

1137
Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() {
1✔
1138
   Test::Result result("X509 IP Address Block path validation success (ctor)");
1✔
1139
   result.start_timer();
1✔
1140

1141
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1142
   auto rng = Test::new_rng(__func__);
1✔
1143

1144
   /*
1145
   Creates a certificate chain of length 4.
1146
   Root: ipv4 and ipv6
1147
   Inherit: has both values as 'inherit'
1148
   Dynamic: has either both 'inherit', both with values, or just one with a value
1149
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1150
   */
1151

1152
   // Root cert
1153
   std::vector<uint8_t> a = {120, 0, 0, 1};
1✔
1154
   auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1155
   a = {130, 140, 150, 160};
1✔
1156
   auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1157

1158
   a = {10, 0, 0, 1};
1✔
1159
   auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1160
   a = {10, 255, 255, 255};
1✔
1161
   auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1162

1163
   a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1164
   auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1165
   a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1166
   auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1167

1168
   a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1169
   auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1170
   a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1171
   auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1172

1173
   auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
1✔
1174
   auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
1✔
1175
   auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
1✔
1176
   auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
1✔
1177

1178
   auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
1✔
1179
   auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
1✔
1180

1181
   auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
1✔
1182
   auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
1✔
1183

1184
   auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
3✔
1185
   auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
3✔
1186

1187
   auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
6✔
1188
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
1✔
1189

1190
   // Inherit cert
1191
   auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
1✔
1192
   auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
1✔
1193

1194
   auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
2✔
1195
   auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
2✔
1196

1197
   auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
6✔
1198
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
1✔
1199

1200
   // Dynamic Cert
1201
   a = {122, 0, 0, 255};
1✔
1202
   auto dyn_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1203
   a = {128, 255, 255, 255};
1✔
1204
   auto dyn_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1205
   a = {10, 0, 0, 255};
1✔
1206
   auto dyn_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1207
   a = {10, 255, 0, 1};
1✔
1208
   auto dyn_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1209

1210
   a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1211
   auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1212
   a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1213
   auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1214

1215
   auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
1✔
1216
   auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
1✔
1217
   auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
1✔
1218

1219
   auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
1✔
1220
   auto dyn_ipv6_ranges = {dyn_ipv6_range};
1✔
1221

1222
   // Subject cert
1223
   a = {124, 0, 255, 0};
1✔
1224
   auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1225
   a = {126, 0, 0, 1};
1✔
1226
   auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1227

1228
   a = {10, 0, 2, 1};
1✔
1229
   auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1230
   a = {10, 42, 0, 255};
1✔
1231
   auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1232

1233
   a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1234
   auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1235
   a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1236
   auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1237

1238
   auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
1✔
1239
   auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
1✔
1240
   auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
1✔
1241

1242
   auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
1✔
1243
   auto sub_ipv6_ranges = {sub_ipv6_range};
1✔
1244

1245
   auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
1✔
1246
   auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
1✔
1247

1248
   auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
3✔
1249
   auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
3✔
1250

1251
   auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
6✔
1252
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
1✔
1253

1254
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1255
   root_opts.extensions.add(std::move(root_blocks));
1✔
1256
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1257
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1258
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1259
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1260

1261
   Botan::Certificate_Store_In_Memory trusted;
1✔
1262
   trusted.add_certificate(root_cert);
1✔
1263

1264
   for(size_t i = 0; i < 4; i++) {
5✔
1265
      const bool include_v4 = bit_set<0>(i);
4✔
1266
      const bool include_v6 = bit_set<1>(i);
4✔
1267

1268
      auto dyn_ipv4_choice =
4✔
1269
         IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
8✔
1270
      auto dyn_ipv6_choice =
4✔
1271
         IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
8✔
1272

1273
      auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
10✔
1274
      auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
10✔
1275

1276
      auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
24✔
1277
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
4✔
1278

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

1281
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1282
      const Botan::X509_Certificate sub_cert =
4✔
1283
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1284

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

1288
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1289
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1290
   }
28✔
1291

1292
   result.end_timer();
1✔
1293
   return result;
2✔
1294
}
24✔
1295

1296
Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() {
1✔
1297
   Test::Result result("X509 IP Address Blocks path validation failure (builder)");
1✔
1298
   result.start_timer();
1✔
1299

1300
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1301
   auto rng = Test::new_rng(__func__);
1✔
1302

1303
   for(size_t i = 0; i < 7; i++) {
8✔
1304
      const bool all_inherit = (i == 0);
7✔
1305
      const bool different_safi = (i == 1);
7✔
1306
      const bool too_small_subrange = (i == 2);
7✔
1307
      const bool too_large_subrange = (i == 3);
7✔
1308
      const bool no_more_issuer_ranges = (i == 4);
7✔
1309
      const bool empty_issuer_ranges = (i == 5);
7✔
1310
      const bool nullptr_extensions = (i == 6);
7✔
1311

1312
      // Root cert
1313
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
7✔
1314
      if(!all_inherit) {
7✔
1315
         root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
6✔
1316
      } else {
1317
         root_blocks->inherit<IPv4>(42);
1✔
1318
      }
1319

1320
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1321
      if(!nullptr_extensions) {
7✔
1322
         root_opts.extensions.add(std::move(root_blocks));
12✔
1323
      }
1324
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1325

1326
      // Issuer Cert
1327
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>();
7✔
1328
      if(!all_inherit) {
7✔
1329
         if(empty_issuer_ranges) {
6✔
1330
            iss_blocks->restrict<IPv4>(42);
1✔
1331
         } else {
1332
            iss_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
5✔
1333
         }
1334
      } else {
1335
         iss_blocks->inherit<IPv4>(42);
1✔
1336
      }
1337

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

1340
      // Subject cert
1341
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
7✔
1342

1343
      uint8_t safi = different_safi ? 41 : 42;
7✔
1344

1345
      if(!all_inherit) {
7✔
1346
         if(too_small_subrange) {
6✔
1347
            sub_blocks->add_address<IPv4>({118, 0, 255, 0}, {126, 0, 0, 1}, safi);
1✔
1348
         } else if(too_large_subrange) {
5✔
1349
            sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {134, 0, 0, 1}, safi);
1✔
1350
         } else if(no_more_issuer_ranges) {
4✔
1351
            sub_blocks->add_address<IPv4>({140, 0, 0, 1}, {150, 0, 0, 1}, safi);
1✔
1352
         } else {
1353
            sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, safi);
3✔
1354
         }
1355
      } else {
1356
         sub_blocks->inherit<IPv4>(safi);
1✔
1357
      }
1358

1359
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1360
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1361

1362
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1363
      const Botan::X509_Certificate sub_cert =
7✔
1364
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1365

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

1369
      Botan::Certificate_Store_In_Memory trusted;
7✔
1370
      trusted.add_certificate(root_cert);
7✔
1371

1372
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1373
      result.require("path validation fails", !path_result.successful_validation());
7✔
1374
   }
15✔
1375

1376
   result.end_timer();
1✔
1377
   return result;
1✔
1378
}
8✔
1379

1380
Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() {
1✔
1381
   Test::Result result("X509 IP Address Block path validation failure (ctor)");
1✔
1382
   result.start_timer();
1✔
1383

1384
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1385
   auto rng = Test::new_rng(__func__);
1✔
1386

1387
   for(size_t i = 0; i < 7; i++) {
8✔
1388
      const bool all_inherit = (i == 0);
7✔
1389
      const bool different_safi = (i == 1);
7✔
1390
      const bool too_small_subrange = (i == 2);
7✔
1391
      const bool too_large_subrange = (i == 3);
7✔
1392
      const bool no_more_issuer_ranges = (i == 4);
7✔
1393
      const bool empty_issuer_ranges = (i == 5);
7✔
1394
      const bool nullptr_extensions = (i == 6);
7✔
1395

1396
      // Root cert
1397
      std::vector<uint8_t> a = {120, 0, 0, 1};
7✔
1398
      auto root_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1399
      a = {130, 140, 150, 160};
7✔
1400
      auto root_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1401

1402
      auto root_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_range_1_min, root_range_1_max);
7✔
1403
      auto root_ranges = {root_range_1};
7✔
1404
      auto root_choice =
7✔
1405
         IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(root_ranges));
14✔
1406
      auto root_family = IPAddressBlocks::IPAddressFamily(root_choice, 42);
20✔
1407
      auto root_addr_blocks = {root_family};
21✔
1408
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
7✔
1409

1410
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1411
      if(!nullptr_extensions) {
7✔
1412
         root_opts.extensions.add(std::move(root_blocks));
12✔
1413
      }
1414
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1415

1416
      // Issuer Cert
1417
      a = {122, 0, 0, 255};
7✔
1418
      auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1419
      a = {128, 255, 255, 255};
7✔
1420
      auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1421
      auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
7✔
1422

1423
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
7✔
1424

1425
      if(!empty_issuer_ranges) {
7✔
1426
         iss_ranges.push_back(iss_range_1);
6✔
1427
      }
1428

1429
      auto iss_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(iss_ranges));
19✔
1430
      auto iss_family = IPAddressBlocks::IPAddressFamily(iss_choice, 42);
20✔
1431
      auto iss_addr_blocks = {iss_family};
21✔
1432
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>(iss_addr_blocks);
7✔
1433
      auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
14✔
1434

1435
      // Subject cert
1436
      if(too_small_subrange) {
7✔
1437
         a = {118, 0, 255, 0};
2✔
1438
      } else if(no_more_issuer_ranges) {
6✔
1439
         a = {140, 0, 0, 1};
2✔
1440
      } else {
1441
         a = {124, 0, 255, 0};
10✔
1442
      }
1443

1444
      auto sub_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1445
      if(too_large_subrange) {
7✔
1446
         a = {134, 0, 0, 1};
2✔
1447
      } else if(no_more_issuer_ranges) {
6✔
1448
         a = {150, 0, 0, 1};
2✔
1449
      } else {
1450
         a = {126, 0, 0, 1};
10✔
1451
      }
1452
      auto sub_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1453

1454
      auto sub_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_range_1_min, sub_range_1_max);
7✔
1455
      auto sub_ranges = {sub_range_1};
7✔
1456
      auto sub_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(sub_ranges));
14✔
1457
      auto sub_family = IPAddressBlocks::IPAddressFamily(sub_choice, different_safi ? 41 : 42);
26✔
1458

1459
      auto sub_addr_blocks = {sub_family};
21✔
1460
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
7✔
1461

1462
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1463
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1464

1465
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1466
      const Botan::X509_Certificate sub_cert =
7✔
1467
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1468

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

1472
      Botan::Certificate_Store_In_Memory trusted;
7✔
1473
      trusted.add_certificate(root_cert);
7✔
1474

1475
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1476
      result.require("path validation fails", !path_result.successful_validation());
7✔
1477
   }
82✔
1478

1479
   result.end_timer();
1✔
1480
   return result;
1✔
1481
}
8✔
1482

1483
Test::Result test_x509_as_blocks_rfc3779_example() {
1✔
1484
   Test::Result result("X509 AS Blocks rfc3779 example");
1✔
1485
   result.start_timer();
1✔
1486

1487
   using Botan::Cert_Extension::ASBlocks;
1✔
1488
   auto rng = Test::new_rng(__func__);
1✔
1489

1490
   // construct like in https://datatracker.ietf.org/doc/html/rfc3779#page-21
1491
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1492
   blocks->add_asnum(135);
1✔
1493
   blocks->add_asnum(3000, 3999);
1✔
1494
   blocks->add_asnum(5001);
1✔
1495
   blocks->inherit_rdi();
1✔
1496

1497
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1498
   opts.extensions.add(std::move(blocks));
1✔
1499

1500
   auto cert = make_self_signed(rng, opts);
1✔
1501
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1502

1503
   result.test_eq(
1✔
1504
      "extension is encoded as specified", bits, "301AA014301202020087300802020BB802020F9F02021389A1020500");
1505

1506
   auto as_idents = cert.v3_extensions().get_extension_object_as<ASBlocks>()->as_identifiers();
1✔
1507
   auto as_ids = as_idents.asnum().value().ranges().value();
1✔
1508

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

1511
   result.end_timer();
1✔
1512
   return result;
2✔
1513
}
3✔
1514

1515
Test::Result test_x509_as_blocks_encode_builder() {
1✔
1516
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
1517
   result.start_timer();
1✔
1518

1519
   using Botan::Cert_Extension::ASBlocks;
1✔
1520
   auto rng = Test::new_rng(__func__);
1✔
1521

1522
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1523

1524
   blocks->add_rdi(10);
1✔
1525
   blocks->add_rdi(20, 30);
1✔
1526
   blocks->add_rdi(42, 300);
1✔
1527
   blocks->add_rdi(9, 301);
1✔
1528

1529
   blocks->inherit_asnum();
1✔
1530
   blocks->add_asnum(20);
1✔
1531
   // this overwrites the previous two
1532
   blocks->restrict_asnum();
1✔
1533

1534
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1535
   opts.extensions.add(std::move(blocks));
1✔
1536

1537
   auto cert = make_self_signed(rng, opts);
1✔
1538
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1539

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

1542
   result.end_timer();
1✔
1543
   return result;
1✔
1544
}
2✔
1545

1546
Test::Result test_x509_as_blocks_extension_encode_ctor() {
1✔
1547
   Test::Result result("X509 AS Blocks encode (ctor)");
1✔
1548
   result.start_timer();
1✔
1549

1550
   using Botan::Cert_Extension::ASBlocks;
1✔
1551

1552
   auto rng = Test::new_rng(__func__);
1✔
1553

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

1556
   for(size_t i = 0; i < 16; i++) {
17✔
1557
      const bool push_asnum = bit_set<0>(i);
16✔
1558
      const bool push_rdi = bit_set<1>(i);
16✔
1559
      const bool include_asnum = bit_set<2>(i);
16✔
1560
      const bool include_rdi = bit_set<3>(i);
16✔
1561
      if(!include_asnum && !include_rdi) {
16✔
1562
         continue;
4✔
1563
      }
1564

1565
      const ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
12✔
1566
      const ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
12✔
1567
      const ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
12✔
1568

1569
      const ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
12✔
1570
      const ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
12✔
1571
      const ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
12✔
1572

1573
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
12✔
1574
      if(push_asnum) {
12✔
1575
         as_ranges.push_back(asnum_id_or_range0);
6✔
1576
         as_ranges.push_back(asnum_id_or_range1);
6✔
1577
         as_ranges.push_back(asnum_id_or_range2);
6✔
1578
      }
1579

1580
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
12✔
1581
      if(push_rdi) {
12✔
1582
         rdi_ranges.push_back(rdi_id_or_range0);
6✔
1583
         rdi_ranges.push_back(rdi_id_or_range1);
6✔
1584
         rdi_ranges.push_back(rdi_id_or_range2);
6✔
1585
      }
1586

1587
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
12✔
1588
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
12✔
1589

1590
      const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
32✔
1591
                                                                    include_rdi ? std::optional(rdi) : std::nullopt);
32✔
1592

1593
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
12✔
1594

1595
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
12✔
1596
      opts.extensions.add(std::move(blocks));
12✔
1597

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

1601
      {
12✔
1602
         const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
12✔
1603
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
24✔
1604

1605
         const auto& identifier = as_blocks->as_identifiers();
12✔
1606

1607
         if(include_asnum) {
12✔
1608
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
1609

1610
            if(push_asnum) {
8✔
1611
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
1612
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
1613

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

1623
         if(include_rdi) {
12✔
1624
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
1625

1626
            if(push_rdi) {
8✔
1627
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
1628
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
1629

1630
               result.confirm("rdi entry 1 min", rdi_entries[1].min() == 32768, true);
8✔
1631
               result.confirm("rdi entry 1 max", rdi_entries[1].max() == 4294967295, true);
8✔
1632
            } else {
1633
               result.confirm("rdi has no entries", rdi_entries.empty(), true);
8✔
1634
            }
1635
         } else {
1636
            result.confirm("rdi has no entry", identifier.rdi().has_value(), false);
8✔
1637
         }
1638
      }
1639
   }
36✔
1640

1641
   result.end_timer();
1✔
1642
   return result;
2✔
1643
}
2✔
1644

1645
Test::Result test_x509_as_blocks_range_merge() {
1✔
1646
   Test::Result result("X509 AS Block range merge");
1✔
1647
   result.start_timer();
1✔
1648

1649
   using Botan::Cert_Extension::ASBlocks;
1✔
1650

1651
   auto rng = Test::new_rng(__func__);
1✔
1652

1653
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1654
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
1655

1656
   const std::vector<std::vector<uint16_t>> ranges = {
1✔
1657
      {2005, 37005},
1658
      {60, 70},
1659
      {22, 50},
1660
      {35, 2000},
1661
      {2001, 2004},
1662
      {21, 21},
1663
      {0, 20},
1664
   };
9✔
1665

1666
   std::vector<ASBlocks::ASIdOrRange> as_ranges;
1✔
1667
   for(auto pair : ranges) {
8✔
1668
      auto range = ASBlocks::ASIdOrRange(pair[0], pair[1]);
7✔
1669
      as_ranges.push_back(range);
7✔
1670
   }
7✔
1671

1672
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
1673

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

1676
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
1✔
1677

1678
   opts.extensions.add(std::move(blocks));
1✔
1679

1680
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
1681
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1682
   {
1✔
1683
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
1684
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1685

1686
      const auto& identifier = as_blocks->as_identifiers();
1✔
1687

1688
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
1689

1690
      result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
2✔
1691
      result.confirm("asnum entry 0 max", asnum_entries[0].max() == 37005, true);
2✔
1692
      result.confirm("asnum length", asnum_entries.size() == 1, true);
2✔
1693
   }
1694

1695
   result.end_timer();
1✔
1696
   return result;
2✔
1697
}
5✔
1698

1699
Test::Result test_x509_as_blocks_path_validation_success_builder() {
1✔
1700
   Test::Result result("X509 AS Block path validation success (builder)");
1✔
1701
   result.start_timer();
1✔
1702

1703
   using Botan::Cert_Extension::ASBlocks;
1✔
1704
   auto rng = Test::new_rng(__func__);
1✔
1705

1706
   /*
1707
   Creates a certificate chain of length 4.
1708
   Root: both asnum and rdi
1709
   Inherit: has both values as 'inherit'
1710
   Dynamic: has either both 'inherit', both with values, or just one with a value
1711
   Subject: both asnum and rdi as a subset of Root / Dynamic
1712
   */
1713

1714
   // Root Cert, both as and rdi
1715

1716
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
1✔
1717

1718
   root_blocks->add_asnum(0, 999);
1✔
1719
   root_blocks->add_asnum(5042);
1✔
1720
   root_blocks->add_asnum(5043, 4294967295);
1✔
1721

1722
   root_blocks->add_rdi(1234, 5678);
1✔
1723
   root_blocks->add_rdi(32768);
1✔
1724
   root_blocks->add_rdi(32769, 4294967295);
1✔
1725

1726
   // Inherit cert, both as 'inherit'
1727
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>();
1✔
1728
   inherit_blocks->inherit_asnum();
1✔
1729
   inherit_blocks->inherit_rdi();
1✔
1730

1731
   // Subject cert
1732

1733
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1734

1735
   sub_blocks->add_asnum(120, 180);
1✔
1736
   sub_blocks->add_asnum(220, 240);
1✔
1737
   sub_blocks->add_asnum(260, 511);
1✔
1738
   sub_blocks->add_asnum(678);
1✔
1739
   sub_blocks->add_asnum(5043, 5100);
1✔
1740

1741
   sub_blocks->add_rdi(1500, 2300);
1✔
1742
   sub_blocks->add_rdi(2500, 4000);
1✔
1743
   sub_blocks->add_rdi(1567);
1✔
1744
   sub_blocks->add_rdi(33100, 40000);
1✔
1745

1746
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1747
   root_opts.extensions.add(std::move(root_blocks));
1✔
1748
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1749
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1750
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1751
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1752

1753
   Botan::Certificate_Store_In_Memory trusted;
1✔
1754
   trusted.add_certificate(root_cert);
1✔
1755

1756
   for(size_t i = 0; i < 4; i++) {
5✔
1757
      const bool include_asnum = bit_set<0>(i);
4✔
1758
      const bool include_rdi = bit_set<1>(i);
4✔
1759

1760
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>();
4✔
1761
      if(include_asnum) {
4✔
1762
         dyn_blocks->add_asnum(100, 600);
2✔
1763
         dyn_blocks->add_asnum(678);
2✔
1764
         dyn_blocks->add_asnum(5042, 5101);
2✔
1765
      } else {
1766
         dyn_blocks->inherit_asnum();
2✔
1767
      }
1768

1769
      if(include_rdi) {
4✔
1770
         dyn_blocks->add_rdi(1500, 5000);
2✔
1771
         dyn_blocks->add_rdi(33000, 60000);
2✔
1772
      } else {
1773
         dyn_blocks->inherit_rdi();
2✔
1774
      }
1775

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

1778
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1779
      const Botan::X509_Certificate sub_cert =
4✔
1780
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1781

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

1785
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1786
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1787
   }
8✔
1788

1789
   result.end_timer();
1✔
1790
   return result;
2✔
1791
}
7✔
1792

1793
Test::Result test_x509_as_blocks_path_validation_success_ctor() {
1✔
1794
   Test::Result result("X509 AS Block path validation success (ctor)");
1✔
1795
   result.start_timer();
1✔
1796

1797
   using Botan::Cert_Extension::ASBlocks;
1✔
1798
   auto rng = Test::new_rng(__func__);
1✔
1799

1800
   /*
1801
   Creates a certificate chain of length 4.
1802
   Root: both asnum and rdi
1803
   Inherit: has both values as 'inherit'
1804
   Dynamic: has either both 'inherit', both with values, or just one with a value
1805
   Subject: both asnum and rdi as a subset of Root / Dynamic
1806
   */
1807

1808
   // Root Cert, both as and rdi
1809
   const ASBlocks::ASIdOrRange root_asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
1✔
1810
   const ASBlocks::ASIdOrRange root_asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
1✔
1811
   const ASBlocks::ASIdOrRange root_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
1✔
1812

1813
   const ASBlocks::ASIdOrRange root_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
1✔
1814
   const ASBlocks::ASIdOrRange root_rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
1✔
1815
   const ASBlocks::ASIdOrRange root_rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
1✔
1816

1817
   std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
1✔
1818
   root_as_ranges.push_back(root_asnum_id_or_range0);
1✔
1819
   root_as_ranges.push_back(root_asnum_id_or_range1);
1✔
1820
   root_as_ranges.push_back(root_asnum_id_or_range2);
1✔
1821

1822
   std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
1✔
1823
   root_rdi_ranges.push_back(root_rdi_id_or_range0);
1✔
1824
   root_rdi_ranges.push_back(root_rdi_id_or_range1);
1✔
1825
   root_rdi_ranges.push_back(root_rdi_id_or_range2);
1✔
1826

1827
   ASBlocks::ASIdentifierChoice root_asnum = ASBlocks::ASIdentifierChoice(root_as_ranges);
1✔
1828
   ASBlocks::ASIdentifierChoice root_rdi = ASBlocks::ASIdentifierChoice(root_rdi_ranges);
1✔
1829
   const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
4✔
1830
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
1✔
1831

1832
   // Inherit cert, both as 'inherit'
1833
   ASBlocks::ASIdentifierChoice inherit_asnum = ASBlocks::ASIdentifierChoice();
1✔
1834
   ASBlocks::ASIdentifierChoice inherit_rdi = ASBlocks::ASIdentifierChoice();
1✔
1835
   const ASBlocks::ASIdentifiers inherit_ident = ASBlocks::ASIdentifiers(inherit_asnum, inherit_rdi);
2✔
1836
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>(inherit_ident);
1✔
1837

1838
   // Dynamic cert
1839
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range0 = ASBlocks::ASIdOrRange(100, 600);
1✔
1840
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range1 = ASBlocks::ASIdOrRange(678);
1✔
1841
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5042, 5101);
1✔
1842

1843
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 5000);
1✔
1844
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range1 = ASBlocks::ASIdOrRange(33000, 60000);
1✔
1845

1846
   std::vector<ASBlocks::ASIdOrRange> dyn_as_ranges;
1✔
1847
   dyn_as_ranges.push_back(dyn_asnum_id_or_range0);
1✔
1848
   dyn_as_ranges.push_back(dyn_asnum_id_or_range1);
1✔
1849
   dyn_as_ranges.push_back(dyn_asnum_id_or_range2);
1✔
1850

1851
   std::vector<ASBlocks::ASIdOrRange> dyn_rdi_ranges;
1✔
1852
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range0);
1✔
1853
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range1);
1✔
1854

1855
   // Subject cert
1856
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1857
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1858
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1859
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1860
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1861

1862
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1863
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1864
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1865
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1866

1867
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1868
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1869
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1870
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1871
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1872
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1873

1874
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1875
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1876
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1877
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1878
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1879

1880
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1881
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1882
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1883
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1884

1885
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1886
   root_opts.extensions.add(std::move(root_blocks));
1✔
1887
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1888
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1889
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1890
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1891

1892
   Botan::Certificate_Store_In_Memory trusted;
1✔
1893
   trusted.add_certificate(root_cert);
1✔
1894

1895
   for(size_t i = 0; i < 4; i++) {
5✔
1896
      const bool include_asnum = bit_set<0>(i);
4✔
1897
      const bool include_rdi = bit_set<1>(i);
4✔
1898

1899
      ASBlocks::ASIdentifierChoice dyn_asnum =
4✔
1900
         ASBlocks::ASIdentifierChoice(include_asnum ? std::optional(dyn_as_ranges) : std::nullopt);
6✔
1901
      ASBlocks::ASIdentifierChoice dyn_rdi =
4✔
1902
         ASBlocks::ASIdentifierChoice(include_rdi ? std::optional(dyn_rdi_ranges) : std::nullopt);
6✔
1903
      const ASBlocks::ASIdentifiers dyn_ident = ASBlocks::ASIdentifiers(dyn_asnum, dyn_rdi);
12✔
1904
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>(dyn_ident);
4✔
1905

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

1908
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1909
      const Botan::X509_Certificate sub_cert =
4✔
1910
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1911

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

1915
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1916
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1917
   }
12✔
1918

1919
   result.end_timer();
1✔
1920
   return result;
2✔
1921
}
11✔
1922

1923
Test::Result test_x509_as_blocks_path_validation_extension_not_present_builder() {
1✔
1924
   Test::Result result("X509 AS Block path validation extension not present (builder)");
1✔
1925
   result.start_timer();
1✔
1926

1927
   using Botan::Cert_Extension::ASBlocks;
1✔
1928
   auto rng = Test::new_rng(__func__);
1✔
1929

1930
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1931
   sub_blocks->add_asnum(120, 180);
1✔
1932
   sub_blocks->add_asnum(220, 224);
1✔
1933
   sub_blocks->add_asnum(260, 511);
1✔
1934
   sub_blocks->add_asnum(678);
1✔
1935
   sub_blocks->add_asnum(5043, 5100);
1✔
1936

1937
   sub_blocks->add_rdi(1500, 2300);
1✔
1938
   sub_blocks->add_rdi(2500, 4000);
1✔
1939
   sub_blocks->add_rdi(1567);
1✔
1940
   sub_blocks->add_rdi(33100, 40000);
1✔
1941

1942
   // create a root ca that does not have any extension
1943
   const Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1944
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1945
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1946
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1947
   const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
1948
   const Botan::X509_Certificate sub_cert =
1✔
1949
      root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1950

1951
   Botan::Certificate_Store_In_Memory trusted;
1✔
1952
   trusted.add_certificate(root_cert);
1✔
1953

1954
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1955
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1956

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

1960
   result.end_timer();
1✔
1961
   return result;
2✔
1962
}
3✔
1963

1964
Test::Result test_x509_as_blocks_path_validation_extension_not_present_ctor() {
1✔
1965
   Test::Result result("X509 AS Block path validation extension not present (ctor)");
1✔
1966
   result.start_timer();
1✔
1967

1968
   using Botan::Cert_Extension::ASBlocks;
1✔
1969
   auto rng = Test::new_rng(__func__);
1✔
1970

1971
   // Subject cert
1972
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1973
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1974
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1975
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1976
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1977

1978
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1979
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1980
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1981
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1982

1983
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1984
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1985
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1986
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1987
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1988
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1989

1990
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1991
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1992
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1993
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1994
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1995

1996
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1997
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1998
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1999
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
2000

2001
   // create a root ca that does not have any extension
2002
   const Botan::X509_Cert_Options root_opts = ca_opts();
1✔
2003
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
2004
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
2005
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
2006
   const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
2007
   const Botan::X509_Certificate sub_cert =
1✔
2008
      root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
2009

2010
   Botan::Certificate_Store_In_Memory trusted;
1✔
2011
   trusted.add_certificate(root_cert);
1✔
2012

2013
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
2014
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
2015

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

2019
   result.end_timer();
1✔
2020
   return result;
2✔
2021
}
5✔
2022

2023
Test::Result test_x509_as_blocks_path_validation_failure_builder() {
1✔
2024
   Test::Result result("X509 AS Block path validation failure (builder)");
1✔
2025
   result.start_timer();
1✔
2026

2027
   using Botan::Cert_Extension::ASBlocks;
1✔
2028
   auto rng = Test::new_rng(__func__);
1✔
2029

2030
   /*
2031
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2032

2033
   Each test is expected to fail and creates the following certificate chain:
2034
   Root -> Issuer -> Subject
2035

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

2076
      // Root cert
2077
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
21✔
2078

2079
      if(!inherit_all_asnums) {
21✔
2080
         if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
20✔
2081
            // 100-200 for 02,03,04
2082
            root_blocks->add_asnum(100, 200);
4✔
2083
         } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
16✔
2084
            // 10-20,30-40,50-60 for 08,09,10
2085
            root_blocks->add_asnum(10, 20);
4✔
2086
            root_blocks->add_asnum(30, 40);
4✔
2087
            root_blocks->add_asnum(50, 60);
4✔
2088
         } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
12✔
2089
            // 10-20,30-50,60-70 for 11,12,13
2090
            root_blocks->add_asnum(10, 20);
4✔
2091
            root_blocks->add_asnum(30, 50);
4✔
2092
            root_blocks->add_asnum(60, 70);
4✔
2093
         }
2094
      } else {
2095
         root_blocks->inherit_asnum();
1✔
2096
      }
2097

2098
      // same values but for rdis
2099
      if(!inherit_all_rdis) {
21✔
2100
         if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
2101
            root_blocks->add_rdi(100, 200);
4✔
2102
         } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
2103
            root_blocks->add_rdi(10, 20);
4✔
2104
            root_blocks->add_rdi(30, 40);
4✔
2105
            root_blocks->add_rdi(50, 60);
4✔
2106
         } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
2107
            root_blocks->add_rdi(10, 20);
4✔
2108
            root_blocks->add_rdi(30, 50);
4✔
2109
            root_blocks->add_rdi(60, 70);
4✔
2110
         }
2111
      } else {
2112
         root_blocks->inherit_rdi();
1✔
2113
      }
2114

2115
      if(empty_issuer_non_empty_subject) {
21✔
2116
         root_blocks->restrict_asnum();
1✔
2117
         root_blocks->restrict_rdi();
1✔
2118
      }
2119

2120
      // Issuer cert
2121
      // the issuer cert has the same ranges as the root cert
2122
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2123
      auto issu_blocks = root_blocks->copy();
21✔
2124

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

2167
      if(!inherit_all_rdis) {
21✔
2168
         // same values but for rdis
2169
         if(push_rdi_min_edge_ranges) {
2170
            sub_blocks->add_rdi(99, 200);
2✔
2171
         } else if(push_rdi_max_edge_ranges) {
2172
            sub_blocks->add_rdi(100, 201);
2✔
2173
         } else if(push_rdi_max_middle_ranges) {
2174
            sub_blocks->add_rdi(10, 20);
2✔
2175
            sub_blocks->add_rdi(30, 41);
2✔
2176
            sub_blocks->add_rdi(50, 60);
2✔
2177
         } else if(push_rdi_max_split_ranges) {
2178
            sub_blocks->add_rdi(10, 20);
2✔
2179
            sub_blocks->add_rdi(30, 44);
2✔
2180
            sub_blocks->add_rdi(46, 51);
2✔
2181
            sub_blocks->add_rdi(60, 70);
2✔
2182
         } else if(push_rdi_min_middle_ranges) {
2183
            sub_blocks->add_rdi(10, 20);
2✔
2184
            sub_blocks->add_rdi(29, 40);
2✔
2185
            sub_blocks->add_rdi(50, 60);
2✔
2186
         } else if(push_rdi_min_split_ranges) {
2187
            sub_blocks->add_rdi(10, 20);
2✔
2188
            sub_blocks->add_rdi(29, 44);
2✔
2189
            sub_blocks->add_rdi(46, 50);
2✔
2190
            sub_blocks->add_rdi(60, 70);
2✔
2191
         }
2192
      } else {
2193
         sub_blocks->inherit_rdi();
1✔
2194
      }
2195

2196
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2197
      root_opts.extensions.add(std::move(root_blocks));
21✔
2198
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2199
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2200

2201
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2202
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2203
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2204
      const Botan::X509_Certificate sub_cert =
21✔
2205
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2206

2207
      Botan::Certificate_Store_In_Memory trusted;
21✔
2208
      trusted.add_certificate(root_cert);
21✔
2209

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

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

2218
   result.end_timer();
1✔
2219
   return result;
1✔
2220
}
22✔
2221

2222
Test::Result test_x509_as_blocks_path_validation_failure_ctor() {
1✔
2223
   Test::Result result("X509 AS Block path validation failure (ctor)");
1✔
2224
   result.start_timer();
1✔
2225

2226
   using Botan::Cert_Extension::ASBlocks;
1✔
2227
   auto rng = Test::new_rng(__func__);
1✔
2228

2229
   /*
2230
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2231

2232
   Each test is expected to fail and creates the following certificate chain:
2233
   Root -> Issuer -> Subject
2234

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

2275
      // Root cert
2276
      std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
21✔
2277
      std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
21✔
2278

2279
      // assign the root ranges
2280
      if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
21✔
2281
         // 100-200 for 02,03,04
2282
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
2283
      } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
17✔
2284
         // 10-20,30-40,50-60 for 08,09,10
2285
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2286
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
2287
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
2288
      } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
13✔
2289
         // 10-20,30-50,60-70 for 11,12,13
2290
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2291
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
2292
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
2293
      }
2294

2295
      // same values but for rdis
2296
      if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
21✔
2297
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
2298
      } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
2299
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2300
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
2301
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
2302
      } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
2303
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2304
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
2305
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
2306
      }
2307

2308
      // Issuer cert
2309
      // the issuer cert has the same ranges as the root cert
2310
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2311
      const std::vector<ASBlocks::ASIdOrRange> issu_as_ranges;
21✔
2312
      const std::vector<ASBlocks::ASIdOrRange> issu_rdi_ranges;
21✔
2313

2314
      // Subject cert
2315
      std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
21✔
2316
      std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
21✔
2317

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

2351
      // same values but for rdis
2352
      if(push_rdi_min_edge_ranges) {
21✔
2353
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
4✔
2354
      } else if(push_rdi_max_edge_ranges) {
2355
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
4✔
2356
      } else if(push_rdi_max_middle_ranges) {
2357
         sub_rdi_ranges = root_rdi_ranges;
2✔
2358
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
2✔
2359
      } else if(push_rdi_max_split_ranges) {
2360
         sub_rdi_ranges = root_rdi_ranges;
2✔
2361
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
2✔
2362
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
4✔
2363
      } else if(push_rdi_min_middle_ranges) {
2364
         sub_rdi_ranges = root_rdi_ranges;
2✔
2365
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
2✔
2366
      } else if(push_rdi_min_split_ranges) {
2367
         sub_rdi_ranges = root_rdi_ranges;
2✔
2368
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
2✔
2369
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
4✔
2370
      }
2371

2372
      // for cases 00 and 01, set all certs to inherit (so std::nullopt)
2373
      // in all other cases use the ranges created beforehand
2374
      ASBlocks::ASIdentifierChoice root_asnum =
21✔
2375
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(root_as_ranges));
41✔
2376
      ASBlocks::ASIdentifierChoice root_rdi =
21✔
2377
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(root_rdi_ranges));
41✔
2378
      const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
82✔
2379
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
21✔
2380

2381
      const ASBlocks::ASIdentifiers& issu_ident = root_ident;
21✔
2382
      std::unique_ptr<ASBlocks> issu_blocks = std::make_unique<ASBlocks>(issu_ident);
21✔
2383

2384
      ASBlocks::ASIdentifierChoice sub_asnum =
21✔
2385
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(sub_as_ranges));
41✔
2386
      ASBlocks::ASIdentifierChoice sub_rdi =
21✔
2387
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(sub_rdi_ranges));
41✔
2388
      const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
82✔
2389
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
21✔
2390

2391
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2392
      root_opts.extensions.add(std::move(root_blocks));
21✔
2393
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2394
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2395

2396
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2397
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2398
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2399
      const Botan::X509_Certificate sub_cert =
21✔
2400
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2401

2402
      Botan::Certificate_Store_In_Memory trusted;
21✔
2403
      trusted.add_certificate(root_cert);
21✔
2404

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

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

2413
   result.end_timer();
1✔
2414
   return result;
1✔
2415
}
22✔
2416

2417
class X509_RPKI_Tests final : public Test {
1✔
2418
   public:
2419
      std::vector<Test::Result> run() override {
1✔
2420
         std::vector<Test::Result> results;
1✔
2421

2422
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
2423
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
2424
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
2425
   #endif
2426
         results.push_back(test_x509_ip_addr_blocks_rfc3779_example());
2✔
2427
         results.push_back(test_x509_ip_addr_blocks_encode_builder());
2✔
2428
         results.push_back(test_x509_ip_addr_blocks_extension_encode_ctor());
2✔
2429
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor());
2✔
2430
         results.push_back(test_x509_ip_addr_blocks_range_merge());
2✔
2431
         results.push_back(test_x509_ip_addr_blocks_family_merge());
2✔
2432
         results.push_back(test_x509_ip_addr_blocks_path_validation_success_builder());
2✔
2433
         results.push_back(test_x509_ip_addr_blocks_path_validation_success_ctor());
2✔
2434
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure_builder());
2✔
2435
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure_ctor());
2✔
2436
         results.push_back(test_x509_as_blocks_rfc3779_example());
2✔
2437
         results.push_back(test_x509_as_blocks_encode_builder());
2✔
2438
         results.push_back(test_x509_as_blocks_extension_encode_ctor());
2✔
2439
         results.push_back(test_x509_as_blocks_range_merge());
2✔
2440
         results.push_back(test_x509_as_blocks_path_validation_success_builder());
2✔
2441
         results.push_back(test_x509_as_blocks_path_validation_success_ctor());
2✔
2442
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present_builder());
2✔
2443
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present_ctor());
2✔
2444
         results.push_back(test_x509_as_blocks_path_validation_failure_builder());
2✔
2445
         results.push_back(test_x509_as_blocks_path_validation_failure_ctor());
2✔
2446
         return results;
1✔
2447
      }
×
2448
};
2449

2450
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
2451

2452
#endif
2453

2454
}  // namespace
2455

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