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

randombit / botan / 21966968252

12 Feb 2026 10:36PM UTC coverage: 90.07% (+0.003%) from 90.067%
21966968252

Pull #5321

github

web-flow
Merge fd30428c7 into e7443105f
Pull Request #5321: Avoid various unneeded include files

102234 of 113505 relevant lines covered (90.07%)

11432801.46 hits per line

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

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

8
#include "tests.h"
9

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

22
namespace Botan_Tests {
23

24
namespace {
25

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

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

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

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

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

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

52
   opts.CA_key(1);
149✔
53

54
   return opts;
149✔
55
}
×
56

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

141
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
142

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

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

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

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

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

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

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

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

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

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

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

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

217
      // cert contains (in this order)
218
      // IPv6 (1) inherit
219
      // IPv6 0xff....0xff
220
      // IPv4 (2) inherit
221
      // IPv4 (1) 192.168.0.0 - 192.168.2.1
222
      // IPv4 (1) 192.168.2.2 - 200.0.0.0
223
      // IPv4 inherit
224

225
      // IPv4 ranges should be merged, IPv4 should come before IPv6, all should be sorted by safi
226

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

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

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

239
      result.confirm("block 1 has correct size", block_1.size() == 1);
2✔
240
      result.test_eq("block 1 min is correct", block_1[0].min().value(), {192, 168, 0, 0});
2✔
241
      result.test_eq("block 1 max is correct", block_1[0].max().value(), {200, 0, 0, 0});
2✔
242

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

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

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

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

269
      // cert contains the 10.0.32.0/20 prefix, but with a 9 for the unused bits
270

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

273
      const auto* ext = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
274
      result.confirm("extension is not decoded", ext == nullptr);
2✔
275
   }
1✔
276

277
   result.end_timer();
1✔
278
   return result;
1✔
279
}
×
280

281
Test::Result test_x509_as_blocks_extension_decode() {
1✔
282
   Test::Result result("X509 AS Block decode");
1✔
283
   result.start_timer();
1✔
284
   using Botan::Cert_Extension::ASBlocks;
1✔
285

286
   {
1✔
287
      const std::string filename("ASNumberCert.pem");
1✔
288
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
289

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

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

295
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
296
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
297

298
      // cert contains asnum 0-999, 5042, 0-4294967295
299
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
300
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
301

302
      // and rdi 1234-5678, 32768, 0-4294967295
303
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
304
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
305
   }
1✔
306
   {
1✔
307
      const std::string filename("ASNumberOnly.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
      result.confirm("cert has no RDI entries", identifier.rdi().has_value(), false);
2✔
317

318
      // contains 0-999, 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
   }
1✔
322
   {
1✔
323
      const std::string filename("ASRdiOnly.pem");
1✔
324
      const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
325

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

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

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

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

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

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

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

350
      // contains 1234-5678, 0-4294967295
351
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
352
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
353
   }
1✔
354

355
   result.end_timer();
1✔
356
   return result;
1✔
357
}
×
358

359
   #endif
360

361
Test::Result test_x509_ip_addr_blocks_rfc3779_example() {
1✔
362
   Test::Result result("X509 IP Address Blocks rfc3779 example");
1✔
363
   result.start_timer();
1✔
364

365
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
366
   auto rng = Test::new_rng(__func__);
1✔
367

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

378
   Botan::X509_Cert_Options opts_1 = ca_opts();
1✔
379
   opts_1.extensions.add(std::move(blocks_1));
1✔
380

381
   auto cert_1 = make_self_signed(rng, opts_1);
1✔
382

383
   auto bits_1 = cert_1.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
384

385
   result.test_eq(
1✔
386
      "extension is encoded as specified",
387
      bits_1,
388
      "3035302B040300010130240304040A00200304000A00400303000A01300C0304040A02300304000A02400303000A033006040200020500");
389

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

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

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

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

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

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

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

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

425
   Botan::X509_Cert_Options opts_2 = ca_opts();
1✔
426
   opts_2.extensions.add(std::move(blocks_2));
1✔
427

428
   auto cert_2 = make_self_signed(rng, opts_2);
1✔
429

430
   auto bits_2 = cert_2.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
431

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

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

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

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

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

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

465
   result.end_timer();
1✔
466
   return result;
2✔
467
}
7✔
468

469
Test::Result test_x509_ip_addr_blocks_encode_builder() {
1✔
470
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
471
   result.start_timer();
1✔
472

473
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
474
   auto rng = Test::new_rng(__func__);
1✔
475

476
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
1✔
477

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

481
   blocks->add_address<IPv4>({255, 255, 255, 255});
1✔
482
   // encoded as prefix
483
   blocks->add_address<IPv4>({190, 5, 0, 0}, {190, 5, 0b01111111, 255});
1✔
484
   // encoded as min, max
485
   blocks->add_address<IPv4>({127, 0, 0, 1}, {189, 5, 7, 255});
1✔
486

487
   // full address range
488
   blocks->add_address<IPv4>({0, 0, 0, 0}, {255, 255, 255, 255}, 1);
1✔
489

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

492
   Botan::X509_Cert_Options opts = ca_opts();
1✔
493
   opts.extensions.add(std::move(blocks));
1✔
494

495
   auto cert = make_self_signed(rng, opts);
1✔
496
   auto bits = cert.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
497

498
   // hand validated with https://lapo.it/asn1js/
499
   result.test_eq(
1✔
500
      "extension is encoded as specified",
501
      bits,
502
      "304630290402000130230305007B7B0201300D0305007F000001030403BD0500030407BE0500030500FFFFFFFF300A04030001013003030100300D04030001023006030406C0A840");
503

504
   result.end_timer();
1✔
505
   return result;
1✔
506
}
2✔
507

508
namespace {
509

510
template <size_t I>
511
bool bit_set(size_t v) {
648✔
512
   if(((v >> I) & 1) == 1) {
648✔
513
      return true;
514
   } else {
515
      return false;
324✔
516
   }
517
}
518

519
}  // namespace
520

521
Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() {
1✔
522
   Test::Result result("X509 IP Address Block encode (ctor)");
1✔
523
   result.start_timer();
1✔
524

525
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
526

527
   auto rng = Test::new_rng(__func__);
1✔
528

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

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

539
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
64✔
540

541
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
542
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
543
      a = {255, 255, 255, 255};
64✔
544
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
545

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

552
      // encoded as prefix
553
      a = {190, 5, 0, 0};
64✔
554
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
555
      a = {190, 5, 127, 255};
64✔
556
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
557

558
      a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
559
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
560
      a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
561
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
562

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

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

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

576
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
64✔
577
      auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
578
      auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
579
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
64✔
580

581
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
64✔
582
      auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
583
      auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
584
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
64✔
585

586
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
64✔
587
      if(push_ipv4_ranges) {
64✔
588
         ipv4_ranges.push_back(ipv4_range_1);
32✔
589
         ipv4_ranges.push_back(ipv4_range_2);
32✔
590
         ipv4_ranges.push_back(ipv4_range_3);
32✔
591
         ipv4_ranges.push_back(ipv4_range_4);
32✔
592
      }
593

594
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
64✔
595
      if(push_ipv6_ranges) {
64✔
596
         ipv6_ranges.push_back(ipv6_range_1);
32✔
597
         ipv6_ranges.push_back(ipv6_range_2);
32✔
598
         ipv6_ranges.push_back(ipv6_range_3);
32✔
599
         ipv6_ranges.push_back(ipv6_range_4);
32✔
600
      }
601

602
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
64✔
603
      if(!inherit_ipv4) {
64✔
604
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
96✔
605
      }
606

607
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
64✔
608
      if(!inherit_ipv6) {
64✔
609
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
96✔
610
      }
611

612
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
613
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
614

615
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
616
      if(push_ipv4_family) {
64✔
617
         addr_blocks.push_back(ipv4_addr_family);
32✔
618
      }
619
      if(push_ipv6_family) {
64✔
620
         addr_blocks.push_back(ipv6_addr_family);
32✔
621
      }
622

623
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
624

625
      opts.extensions.add(std::move(blocks));
64✔
626

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

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

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

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

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

690
   result.end_timer();
1✔
691
   return result;
2✔
692
}
2✔
693

694
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() {
1✔
695
   Test::Result result("X509 IP Address Block encode edge cases (ctor)");
1✔
696
   result.start_timer();
1✔
697

698
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
699

700
   auto rng = Test::new_rng(__func__);
1✔
701

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

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

708
   for(size_t i = 0; i < edge_values.size(); i++) {
22✔
709
      for(size_t j = 0; j < 4; j++) {
105✔
710
         const bool modify_min = bit_set<0>(j);
84✔
711
         const bool modify_max = bit_set<1>(j);
84✔
712

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

720
            Botan::X509_Cert_Options opts = req_opts(sig_algo);
1,135✔
721

722
            std::vector<uint8_t> min_bytes(16, 0x00);
1,135✔
723
            std::vector<uint8_t> max_bytes(16, 0xFF);
1,135✔
724

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

732
            auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
1,135✔
733
            auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
1,135✔
734

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

737
            std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
1,135✔
738
            ipv6_ranges.push_back(ipv6_range);
1,135✔
739

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

742
            auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3,405✔
743

744
            std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1,135✔
745
            addr_blocks.push_back(ipv6_addr_family);
1,135✔
746

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

749
            opts.extensions.add(std::move(blocks));
1,135✔
750

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

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

774
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
775
   Test::Result result("X509 IP Address Block range merge");
1✔
776
   result.start_timer();
1✔
777

778
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
779

780
   auto rng = Test::new_rng(__func__);
1✔
781

782
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
783
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
784

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

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

805
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
1✔
806
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
807

808
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
809
   addr_blocks.push_back(ipv6_addr_family);
1✔
810

811
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
812

813
   opts.extensions.add(std::move(blocks));
1✔
814

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

825
      const std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
826
      const std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
827

828
      result.test_eq("range expected min", ranges[0].min().value(), expected_min);
2✔
829
      result.test_eq("range expected max", ranges[0].max().value(), expected_max);
2✔
830
      result.test_eq("range length", ranges.size(), 1);
1✔
831
   }
2✔
832

833
   result.end_timer();
1✔
834
   return result;
2✔
835
}
24✔
836

837
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
838
   Test::Result result("X509 IP Address Block family merge");
1✔
839
   result.start_timer();
1✔
840

841
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
842

843
   auto rng = Test::new_rng(__func__);
1✔
844

845
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
846
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
847

848
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
849

850
   IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
1✔
851
   IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
1✔
852

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

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

864
   const uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
865
   const IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
1✔
866

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

874
   const IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
875
   const IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
876

877
   const IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
878
   const IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
879

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

896
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
897
      v4_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_empty_fam, v6_fam_dupl, v6_empty_fam_safi};
7✔
898

899
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
900

901
   opts.extensions.add(std::move(blocks));
1✔
902

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

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

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

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

917
      uint32_t afam_a = a.afi();
5✔
918
      if(a.safi().has_value()) {
5✔
919
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
920
      } else {
921
         afam_a = static_cast<uint32_t>(afam_a << 8);
2✔
922
      }
923

924
      uint32_t afam_b = b.afi();
5✔
925
      if(b.safi().has_value()) {
5✔
926
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
927
      } else {
928
         afam_b = static_cast<uint32_t>(afam_b << 8);
1✔
929
      }
930

931
      if(afam_a > afam_b) {
5✔
932
         sorted = false;
933
         break;
934
      }
935
   }
936

937
   result.confirm("blocks got sorted", sorted, true);
2✔
938

939
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
940
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
941
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
942

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

946
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
947
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
3✔
948
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
3✔
949

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

957
            if(dec_choice.ranges().has_value() == false) {
1✔
958
               continue;
×
959
            }
960

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

967
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
968
               continue;
×
969
            }
970

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

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

993
            if(dec_choice.ranges().has_value() == false) {
1✔
994
               continue;
×
995
            }
996

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

1003
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
1004
               continue;
×
1005
            }
1006

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

1021
   result.end_timer();
1✔
1022
   return result;
2✔
1023
}
19✔
1024

1025
Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() {
1✔
1026
   Test::Result result("X509 IP Address Blocks path validation success (builder)");
1✔
1027
   result.start_timer();
1✔
1028

1029
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1030
   auto rng = Test::new_rng(__func__);
1✔
1031

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

1040
   // Root cert
1041
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
1✔
1042

1043
   root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
1✔
1044
   root_blocks->add_address<IPv4>({10, 0, 0, 1}, {10, 255, 255, 255}, 42);
1✔
1045

1046
   root_blocks->add_address<IPv6>(
1✔
1047
      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1048
      {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1049
   root_blocks->add_address<IPv6>(
1✔
1050
      {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1051
      {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1052

1053
   // Inherit cert
1054
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>();
1✔
1055

1056
   inherit_blocks->inherit<IPv4>(42);
1✔
1057
   inherit_blocks->inherit<IPv6>();
1✔
1058

1059
   // Subject cert
1060
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
1✔
1061

1062
   sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, 42);
1✔
1063
   sub_blocks->add_address<IPv4>({10, 0, 2, 1}, {10, 42, 0, 255}, 42);
1✔
1064

1065
   sub_blocks->add_address<IPv6>(
1✔
1066
      {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1067
      {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1068

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

1076
   Botan::Certificate_Store_In_Memory trusted;
1✔
1077
   trusted.add_certificate(root_cert);
1✔
1078

1079
   for(size_t i = 0; i < 4; i++) {
5✔
1080
      const bool include_v4 = bit_set<0>(i);
4✔
1081
      const bool include_v6 = bit_set<1>(i);
4✔
1082

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

1092
      if(include_v6) {
4✔
1093
         dyn_blocks->add_address<IPv6>(
2✔
1094
            {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
1095
            {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1096
      } else {
1097
         dyn_blocks->inherit<IPv6>();
2✔
1098
      }
1099

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

1102
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1103
      const Botan::X509_Certificate sub_cert =
4✔
1104
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1105

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

1109
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1110
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1111
   }
8✔
1112

1113
   result.end_timer();
1✔
1114
   return result;
2✔
1115
}
7✔
1116

1117
Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() {
1✔
1118
   Test::Result result("X509 IP Address Block path validation success (ctor)");
1✔
1119
   result.start_timer();
1✔
1120

1121
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1122
   auto rng = Test::new_rng(__func__);
1✔
1123

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

1132
   // Root cert
1133
   std::vector<uint8_t> a = {120, 0, 0, 1};
1✔
1134
   auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1135
   a = {130, 140, 150, 160};
1✔
1136
   auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1137

1138
   a = {10, 0, 0, 1};
1✔
1139
   auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1140
   a = {10, 255, 255, 255};
1✔
1141
   auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1142

1143
   a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1144
   auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1145
   a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1146
   auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1147

1148
   a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1149
   auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1150
   a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1151
   auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1152

1153
   auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
1✔
1154
   auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
1✔
1155
   auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
1✔
1156
   auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
1✔
1157

1158
   auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
1✔
1159
   auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
1✔
1160

1161
   auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
1✔
1162
   auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
1✔
1163

1164
   auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
3✔
1165
   auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
3✔
1166

1167
   auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
6✔
1168
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
1✔
1169

1170
   // Inherit cert
1171
   auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
1✔
1172
   auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
1✔
1173

1174
   auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
2✔
1175
   auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
2✔
1176

1177
   auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
6✔
1178
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
1✔
1179

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

1190
   a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1191
   auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1192
   a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1193
   auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1194

1195
   auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
1✔
1196
   auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
1✔
1197
   auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
1✔
1198

1199
   auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
1✔
1200
   auto dyn_ipv6_ranges = {dyn_ipv6_range};
1✔
1201

1202
   // Subject cert
1203
   a = {124, 0, 255, 0};
1✔
1204
   auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1205
   a = {126, 0, 0, 1};
1✔
1206
   auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1207

1208
   a = {10, 0, 2, 1};
1✔
1209
   auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1210
   a = {10, 42, 0, 255};
1✔
1211
   auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1212

1213
   a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1214
   auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1215
   a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1216
   auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1217

1218
   auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
1✔
1219
   auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
1✔
1220
   auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
1✔
1221

1222
   auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
1✔
1223
   auto sub_ipv6_ranges = {sub_ipv6_range};
1✔
1224

1225
   auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
1✔
1226
   auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
1✔
1227

1228
   auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
3✔
1229
   auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
3✔
1230

1231
   auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
6✔
1232
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
1✔
1233

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

1241
   Botan::Certificate_Store_In_Memory trusted;
1✔
1242
   trusted.add_certificate(root_cert);
1✔
1243

1244
   for(size_t i = 0; i < 4; i++) {
5✔
1245
      const bool include_v4 = bit_set<0>(i);
4✔
1246
      const bool include_v6 = bit_set<1>(i);
4✔
1247

1248
      auto dyn_ipv4_choice =
4✔
1249
         IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
8✔
1250
      auto dyn_ipv6_choice =
4✔
1251
         IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
8✔
1252

1253
      auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
10✔
1254
      auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
10✔
1255

1256
      auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
24✔
1257
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
4✔
1258

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

1261
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1262
      const Botan::X509_Certificate sub_cert =
4✔
1263
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1264

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

1268
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1269
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1270
   }
28✔
1271

1272
   result.end_timer();
1✔
1273
   return result;
2✔
1274
}
24✔
1275

1276
Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() {
1✔
1277
   Test::Result result("X509 IP Address Blocks path validation failure (builder)");
1✔
1278
   result.start_timer();
1✔
1279

1280
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1281
   auto rng = Test::new_rng(__func__);
1✔
1282

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

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

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

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

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

1320
      // Subject cert
1321
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
7✔
1322

1323
      uint8_t safi = different_safi ? 41 : 42;
7✔
1324

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

1339
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1340
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1341

1342
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1343
      const Botan::X509_Certificate sub_cert =
7✔
1344
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1345

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

1349
      Botan::Certificate_Store_In_Memory trusted;
7✔
1350
      trusted.add_certificate(root_cert);
7✔
1351

1352
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1353
      result.require("path validation fails", !path_result.successful_validation());
7✔
1354
   }
15✔
1355

1356
   result.end_timer();
1✔
1357
   return result;
1✔
1358
}
8✔
1359

1360
Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() {
1✔
1361
   Test::Result result("X509 IP Address Block path validation failure (ctor)");
1✔
1362
   result.start_timer();
1✔
1363

1364
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1365
   auto rng = Test::new_rng(__func__);
1✔
1366

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

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

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

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

1396
      // Issuer Cert
1397
      a = {122, 0, 0, 255};
7✔
1398
      auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1399
      a = {128, 255, 255, 255};
7✔
1400
      auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1401
      auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
7✔
1402

1403
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
7✔
1404

1405
      if(!empty_issuer_ranges) {
7✔
1406
         iss_ranges.push_back(iss_range_1);
6✔
1407
      }
1408

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

1415
      // Subject cert
1416
      if(too_small_subrange) {
7✔
1417
         a = {118, 0, 255, 0};
2✔
1418
      } else if(no_more_issuer_ranges) {
6✔
1419
         a = {140, 0, 0, 1};
2✔
1420
      } else {
1421
         a = {124, 0, 255, 0};
10✔
1422
      }
1423

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

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

1439
      auto sub_addr_blocks = {sub_family};
21✔
1440
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
7✔
1441

1442
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1443
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1444

1445
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1446
      const Botan::X509_Certificate sub_cert =
7✔
1447
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1448

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

1452
      Botan::Certificate_Store_In_Memory trusted;
7✔
1453
      trusted.add_certificate(root_cert);
7✔
1454

1455
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1456
      result.require("path validation fails", !path_result.successful_validation());
7✔
1457
   }
82✔
1458

1459
   result.end_timer();
1✔
1460
   return result;
1✔
1461
}
8✔
1462

1463
Test::Result test_x509_as_blocks_rfc3779_example() {
1✔
1464
   Test::Result result("X509 AS Blocks rfc3779 example");
1✔
1465
   result.start_timer();
1✔
1466

1467
   using Botan::Cert_Extension::ASBlocks;
1✔
1468
   auto rng = Test::new_rng(__func__);
1✔
1469

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

1477
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1478
   opts.extensions.add(std::move(blocks));
1✔
1479

1480
   auto cert = make_self_signed(rng, opts);
1✔
1481
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1482

1483
   result.test_eq(
1✔
1484
      "extension is encoded as specified", bits, "301AA014301202020087300802020BB802020F9F02021389A1020500");
1485

1486
   auto as_idents = cert.v3_extensions().get_extension_object_as<ASBlocks>()->as_identifiers();
1✔
1487
   auto as_ids = as_idents.asnum().value().ranges().value();
1✔
1488

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

1491
   result.end_timer();
1✔
1492
   return result;
2✔
1493
}
3✔
1494

1495
Test::Result test_x509_as_blocks_encode_builder() {
1✔
1496
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
1497
   result.start_timer();
1✔
1498

1499
   using Botan::Cert_Extension::ASBlocks;
1✔
1500
   auto rng = Test::new_rng(__func__);
1✔
1501

1502
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1503

1504
   blocks->add_rdi(10);
1✔
1505
   blocks->add_rdi(20, 30);
1✔
1506
   blocks->add_rdi(42, 300);
1✔
1507
   blocks->add_rdi(9, 301);
1✔
1508

1509
   blocks->inherit_asnum();
1✔
1510
   blocks->add_asnum(20);
1✔
1511
   // this overwrites the previous two
1512
   blocks->restrict_asnum();
1✔
1513

1514
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1515
   opts.extensions.add(std::move(blocks));
1✔
1516

1517
   auto cert = make_self_signed(rng, opts);
1✔
1518
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1519

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

1522
   result.end_timer();
1✔
1523
   return result;
1✔
1524
}
2✔
1525

1526
Test::Result test_x509_as_blocks_extension_encode_ctor() {
1✔
1527
   Test::Result result("X509 AS Blocks encode (ctor)");
1✔
1528
   result.start_timer();
1✔
1529

1530
   using Botan::Cert_Extension::ASBlocks;
1✔
1531

1532
   auto rng = Test::new_rng(__func__);
1✔
1533

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

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

1545
      const ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
12✔
1546
      const ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
12✔
1547
      const ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
12✔
1548

1549
      const ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
12✔
1550
      const ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
12✔
1551
      const ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
12✔
1552

1553
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
12✔
1554
      if(push_asnum) {
12✔
1555
         as_ranges.push_back(asnum_id_or_range0);
6✔
1556
         as_ranges.push_back(asnum_id_or_range1);
6✔
1557
         as_ranges.push_back(asnum_id_or_range2);
6✔
1558
      }
1559

1560
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
12✔
1561
      if(push_rdi) {
12✔
1562
         rdi_ranges.push_back(rdi_id_or_range0);
6✔
1563
         rdi_ranges.push_back(rdi_id_or_range1);
6✔
1564
         rdi_ranges.push_back(rdi_id_or_range2);
6✔
1565
      }
1566

1567
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
12✔
1568
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
12✔
1569

1570
      const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
32✔
1571
                                                                    include_rdi ? std::optional(rdi) : std::nullopt);
32✔
1572

1573
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
12✔
1574

1575
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
12✔
1576
      opts.extensions.add(std::move(blocks));
12✔
1577

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

1581
      {
12✔
1582
         const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
12✔
1583
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
24✔
1584

1585
         const auto& identifier = as_blocks->as_identifiers();
12✔
1586

1587
         if(include_asnum) {
12✔
1588
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
1589

1590
            if(push_asnum) {
8✔
1591
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
1592
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
1593

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

1603
         if(include_rdi) {
12✔
1604
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
1605

1606
            if(push_rdi) {
8✔
1607
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
1608
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
1609

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

1621
   result.end_timer();
1✔
1622
   return result;
2✔
1623
}
2✔
1624

1625
Test::Result test_x509_as_blocks_range_merge() {
1✔
1626
   Test::Result result("X509 AS Block range merge");
1✔
1627
   result.start_timer();
1✔
1628

1629
   using Botan::Cert_Extension::ASBlocks;
1✔
1630

1631
   auto rng = Test::new_rng(__func__);
1✔
1632

1633
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1634
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
1635

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

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

1652
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
1653

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

1656
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
1✔
1657

1658
   opts.extensions.add(std::move(blocks));
1✔
1659

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

1666
      const auto& identifier = as_blocks->as_identifiers();
1✔
1667

1668
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
1669

1670
      result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
2✔
1671
      result.confirm("asnum entry 0 max", asnum_entries[0].max() == 37005, true);
2✔
1672
      result.confirm("asnum length", asnum_entries.size() == 1, true);
2✔
1673
   }
1674

1675
   result.end_timer();
1✔
1676
   return result;
2✔
1677
}
5✔
1678

1679
Test::Result test_x509_as_blocks_path_validation_success_builder() {
1✔
1680
   Test::Result result("X509 AS Block path validation success (builder)");
1✔
1681
   result.start_timer();
1✔
1682

1683
   using Botan::Cert_Extension::ASBlocks;
1✔
1684
   auto rng = Test::new_rng(__func__);
1✔
1685

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

1694
   // Root Cert, both as and rdi
1695

1696
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
1✔
1697

1698
   root_blocks->add_asnum(0, 999);
1✔
1699
   root_blocks->add_asnum(5042);
1✔
1700
   root_blocks->add_asnum(5043, 4294967295);
1✔
1701

1702
   root_blocks->add_rdi(1234, 5678);
1✔
1703
   root_blocks->add_rdi(32768);
1✔
1704
   root_blocks->add_rdi(32769, 4294967295);
1✔
1705

1706
   // Inherit cert, both as 'inherit'
1707
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>();
1✔
1708
   inherit_blocks->inherit_asnum();
1✔
1709
   inherit_blocks->inherit_rdi();
1✔
1710

1711
   // Subject cert
1712

1713
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1714

1715
   sub_blocks->add_asnum(120, 180);
1✔
1716
   sub_blocks->add_asnum(220, 240);
1✔
1717
   sub_blocks->add_asnum(260, 511);
1✔
1718
   sub_blocks->add_asnum(678);
1✔
1719
   sub_blocks->add_asnum(5043, 5100);
1✔
1720

1721
   sub_blocks->add_rdi(1500, 2300);
1✔
1722
   sub_blocks->add_rdi(2500, 4000);
1✔
1723
   sub_blocks->add_rdi(1567);
1✔
1724
   sub_blocks->add_rdi(33100, 40000);
1✔
1725

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

1733
   Botan::Certificate_Store_In_Memory trusted;
1✔
1734
   trusted.add_certificate(root_cert);
1✔
1735

1736
   for(size_t i = 0; i < 4; i++) {
5✔
1737
      const bool include_asnum = bit_set<0>(i);
4✔
1738
      const bool include_rdi = bit_set<1>(i);
4✔
1739

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

1749
      if(include_rdi) {
4✔
1750
         dyn_blocks->add_rdi(1500, 5000);
2✔
1751
         dyn_blocks->add_rdi(33000, 60000);
2✔
1752
      } else {
1753
         dyn_blocks->inherit_rdi();
2✔
1754
      }
1755

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

1758
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1759
      const Botan::X509_Certificate sub_cert =
4✔
1760
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1761

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

1765
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1766
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1767
   }
8✔
1768

1769
   result.end_timer();
1✔
1770
   return result;
2✔
1771
}
7✔
1772

1773
Test::Result test_x509_as_blocks_path_validation_success_ctor() {
1✔
1774
   Test::Result result("X509 AS Block path validation success (ctor)");
1✔
1775
   result.start_timer();
1✔
1776

1777
   using Botan::Cert_Extension::ASBlocks;
1✔
1778
   auto rng = Test::new_rng(__func__);
1✔
1779

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

1788
   // Root Cert, both as and rdi
1789
   const ASBlocks::ASIdOrRange root_asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
1✔
1790
   const ASBlocks::ASIdOrRange root_asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
1✔
1791
   const ASBlocks::ASIdOrRange root_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
1✔
1792

1793
   const ASBlocks::ASIdOrRange root_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
1✔
1794
   const ASBlocks::ASIdOrRange root_rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
1✔
1795
   const ASBlocks::ASIdOrRange root_rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
1✔
1796

1797
   std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
1✔
1798
   root_as_ranges.push_back(root_asnum_id_or_range0);
1✔
1799
   root_as_ranges.push_back(root_asnum_id_or_range1);
1✔
1800
   root_as_ranges.push_back(root_asnum_id_or_range2);
1✔
1801

1802
   std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
1✔
1803
   root_rdi_ranges.push_back(root_rdi_id_or_range0);
1✔
1804
   root_rdi_ranges.push_back(root_rdi_id_or_range1);
1✔
1805
   root_rdi_ranges.push_back(root_rdi_id_or_range2);
1✔
1806

1807
   ASBlocks::ASIdentifierChoice root_asnum = ASBlocks::ASIdentifierChoice(root_as_ranges);
1✔
1808
   ASBlocks::ASIdentifierChoice root_rdi = ASBlocks::ASIdentifierChoice(root_rdi_ranges);
1✔
1809
   const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
4✔
1810
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
1✔
1811

1812
   // Inherit cert, both as 'inherit'
1813
   ASBlocks::ASIdentifierChoice inherit_asnum = ASBlocks::ASIdentifierChoice();
1✔
1814
   ASBlocks::ASIdentifierChoice inherit_rdi = ASBlocks::ASIdentifierChoice();
1✔
1815
   const ASBlocks::ASIdentifiers inherit_ident = ASBlocks::ASIdentifiers(inherit_asnum, inherit_rdi);
2✔
1816
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>(inherit_ident);
1✔
1817

1818
   // Dynamic cert
1819
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range0 = ASBlocks::ASIdOrRange(100, 600);
1✔
1820
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range1 = ASBlocks::ASIdOrRange(678);
1✔
1821
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5042, 5101);
1✔
1822

1823
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 5000);
1✔
1824
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range1 = ASBlocks::ASIdOrRange(33000, 60000);
1✔
1825

1826
   std::vector<ASBlocks::ASIdOrRange> dyn_as_ranges;
1✔
1827
   dyn_as_ranges.push_back(dyn_asnum_id_or_range0);
1✔
1828
   dyn_as_ranges.push_back(dyn_asnum_id_or_range1);
1✔
1829
   dyn_as_ranges.push_back(dyn_asnum_id_or_range2);
1✔
1830

1831
   std::vector<ASBlocks::ASIdOrRange> dyn_rdi_ranges;
1✔
1832
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range0);
1✔
1833
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range1);
1✔
1834

1835
   // Subject cert
1836
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1837
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1838
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1839
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1840
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1841

1842
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1843
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1844
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1845
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1846

1847
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1848
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1849
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1850
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1851
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1852
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1853

1854
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1855
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1856
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1857
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1858
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1859

1860
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1861
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1862
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1863
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1864

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

1872
   Botan::Certificate_Store_In_Memory trusted;
1✔
1873
   trusted.add_certificate(root_cert);
1✔
1874

1875
   for(size_t i = 0; i < 4; i++) {
5✔
1876
      const bool include_asnum = bit_set<0>(i);
4✔
1877
      const bool include_rdi = bit_set<1>(i);
4✔
1878

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

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

1888
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1889
      const Botan::X509_Certificate sub_cert =
4✔
1890
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1891

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

1895
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1896
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1897
   }
12✔
1898

1899
   result.end_timer();
1✔
1900
   return result;
2✔
1901
}
11✔
1902

1903
Test::Result test_x509_as_blocks_path_validation_extension_not_present_builder() {
1✔
1904
   Test::Result result("X509 AS Block path validation extension not present (builder)");
1✔
1905
   result.start_timer();
1✔
1906

1907
   using Botan::Cert_Extension::ASBlocks;
1✔
1908
   auto rng = Test::new_rng(__func__);
1✔
1909

1910
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1911
   sub_blocks->add_asnum(120, 180);
1✔
1912
   sub_blocks->add_asnum(220, 224);
1✔
1913
   sub_blocks->add_asnum(260, 511);
1✔
1914
   sub_blocks->add_asnum(678);
1✔
1915
   sub_blocks->add_asnum(5043, 5100);
1✔
1916

1917
   sub_blocks->add_rdi(1500, 2300);
1✔
1918
   sub_blocks->add_rdi(2500, 4000);
1✔
1919
   sub_blocks->add_rdi(1567);
1✔
1920
   sub_blocks->add_rdi(33100, 40000);
1✔
1921

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

1931
   Botan::Certificate_Store_In_Memory trusted;
1✔
1932
   trusted.add_certificate(root_cert);
1✔
1933

1934
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1935
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1936

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

1940
   result.end_timer();
1✔
1941
   return result;
2✔
1942
}
3✔
1943

1944
Test::Result test_x509_as_blocks_path_validation_extension_not_present_ctor() {
1✔
1945
   Test::Result result("X509 AS Block path validation extension not present (ctor)");
1✔
1946
   result.start_timer();
1✔
1947

1948
   using Botan::Cert_Extension::ASBlocks;
1✔
1949
   auto rng = Test::new_rng(__func__);
1✔
1950

1951
   // Subject cert
1952
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1953
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1954
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1955
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1956
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1957

1958
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1959
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1960
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1961
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1962

1963
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1964
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1965
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1966
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1967
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1968
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1969

1970
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1971
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1972
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1973
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1974
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1975

1976
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1977
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1978
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1979
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1980

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

1990
   Botan::Certificate_Store_In_Memory trusted;
1✔
1991
   trusted.add_certificate(root_cert);
1✔
1992

1993
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1994
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1995

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

1999
   result.end_timer();
1✔
2000
   return result;
2✔
2001
}
5✔
2002

2003
Test::Result test_x509_as_blocks_path_validation_failure_builder() {
1✔
2004
   Test::Result result("X509 AS Block path validation failure (builder)");
1✔
2005
   result.start_timer();
1✔
2006

2007
   using Botan::Cert_Extension::ASBlocks;
1✔
2008
   auto rng = Test::new_rng(__func__);
1✔
2009

2010
   /*
2011
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2012

2013
   Each test is expected to fail and creates the following certificate chain:
2014
   Root -> Issuer -> Subject
2015

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

2056
      // Root cert
2057
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
21✔
2058

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

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

2095
      if(empty_issuer_non_empty_subject) {
21✔
2096
         root_blocks->restrict_asnum();
1✔
2097
         root_blocks->restrict_rdi();
1✔
2098
      }
2099

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

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

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

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

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

2187
      Botan::Certificate_Store_In_Memory trusted;
21✔
2188
      trusted.add_certificate(root_cert);
21✔
2189

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

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

2198
   result.end_timer();
1✔
2199
   return result;
1✔
2200
}
22✔
2201

2202
Test::Result test_x509_as_blocks_path_validation_failure_ctor() {
1✔
2203
   Test::Result result("X509 AS Block path validation failure (ctor)");
1✔
2204
   result.start_timer();
1✔
2205

2206
   using Botan::Cert_Extension::ASBlocks;
1✔
2207
   auto rng = Test::new_rng(__func__);
1✔
2208

2209
   /*
2210
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2211

2212
   Each test is expected to fail and creates the following certificate chain:
2213
   Root -> Issuer -> Subject
2214

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

2255
      // Root cert
2256
      std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
21✔
2257
      std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
21✔
2258

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

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

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

2294
      // Subject cert
2295
      std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
21✔
2296
      std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
21✔
2297

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

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

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

2361
      const ASBlocks::ASIdentifiers& issu_ident = root_ident;
21✔
2362
      std::unique_ptr<ASBlocks> issu_blocks = std::make_unique<ASBlocks>(issu_ident);
21✔
2363

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

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

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

2382
      Botan::Certificate_Store_In_Memory trusted;
21✔
2383
      trusted.add_certificate(root_cert);
21✔
2384

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

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

2393
   result.end_timer();
1✔
2394
   return result;
1✔
2395
}
22✔
2396

2397
class X509_RPKI_Tests final : public Test {
1✔
2398
   public:
2399
      std::vector<Test::Result> run() override {
1✔
2400
         std::vector<Test::Result> results;
1✔
2401

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

2430
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
2431

2432
#endif
2433

2434
}  // namespace
2435

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