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

randombit / botan / 15221827800

24 May 2025 01:06AM UTC coverage: 90.978%. Remained the same
15221827800

Pull #4884

github

web-flow
Merge 534fe1866 into d7620b408
Pull Request #4884: Reduce overhead from X.509 RPKI tests

98002 of 107721 relevant lines covered (90.98%)

12501993.98 hits per line

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

98.43
/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/x509_ca.h>
15
   #include <botan/x509_ext.h>
16
   #include <botan/x509path.h>
17
   #include <botan/x509self.h>
18
   #include <botan/internal/calendar.h>
19
#endif
20

21
namespace Botan_Tests {
22

23
namespace {
24

25
#if defined(BOTAN_HAS_X509_CERTIFICATES)
26

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

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

38
   Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
2,578✔
39
   return Botan::X509_Time(t.to_std_timepoint());
2,578✔
40
}
41

42
std::unique_ptr<Botan::Private_Key> generate_key(const std::string& algo, Botan::RandomNumberGenerator& rng) {
112✔
43
   std::string params;
112✔
44
   if(algo == "ECDSA") {
112✔
45
      params = "secp192r1";
112✔
46
   } else if(algo == "Ed25519") {
×
47
      params = "";
×
48
   } else if(algo == "RSA") {
×
49
      params = "1536";
×
50
   }
51

52
   return Botan::create_private_key(algo, rng, params);
112✔
53
}
112✔
54

55
Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "") {
75✔
56
   Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
75✔
57

58
   opts.uri = "https://botan.randombit.net";
75✔
59
   opts.dns = "botan.randombit.net";
75✔
60
   opts.email = "testing@randombit.net";
75✔
61
   opts.set_padding_scheme(sig_padding);
75✔
62

63
   opts.CA_key(1);
75✔
64

65
   return opts;
75✔
66
}
×
67

68
Botan::X509_Cert_Options req_opts(const std::string& algo, const std::string& sig_padding = "") {
1,245✔
69
   Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
1,245✔
70

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

76
   opts.not_before("160101200000Z");
1,245✔
77
   opts.not_after("300101200000Z");
1,245✔
78

79
   opts.challenge = "zoom";
1,245✔
80

81
   if(algo == "RSA") {
1,245✔
82
      opts.constraints = Botan::Key_Constraints::KeyEncipherment;
×
83
   } else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") {
1,245✔
84
      opts.constraints = Botan::Key_Constraints::DigitalSignature;
1,245✔
85
   }
86

87
   return opts;
1,245✔
88
}
×
89

90
std::tuple<std::string, std::string, std::string> get_sig_algo_padding() {
75✔
91
   #if defined(BOTAN_HAS_ECDSA)
92
   const std::string sig_algo{"ECDSA"};
75✔
93
   const std::string padding_method;
75✔
94
   const std::string hash_fn{"SHA-256"};
75✔
95
   #elif defined(BOTAN_HAS_ED25519)
96
   const std::string sig_algo{"Ed25519"};
97
   const std::string padding_method;
98
   const std::string hash_fn{"SHA-512"};
99
   #elif defined(BOTAN_HAS_RSA)
100
   const std::string sig_algo{"RSA"};
101
   const std::string padding_method{"EMSA3(SHA-256)"};
102
   const std::string hash_fn{"SHA-256"};
103
   #endif
104

105
   return std::make_tuple(sig_algo, padding_method, hash_fn);
150✔
106
}
75✔
107

108
CA_Creation_Result make_ca(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
37✔
109
                           const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
110
   auto [sig_algo, padding_method, hash_fn] = get_sig_algo_padding();
37✔
111
   auto ca_key = generate_key(sig_algo, *rng);
37✔
112
   const auto ca_cert = Botan::X509::create_self_signed_cert(opts, *ca_key, hash_fn, *rng);
37✔
113
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
37✔
114
   auto sub_key = generate_key(sig_algo, *rng);
37✔
115

116
   return CA_Creation_Result{ca_cert, std::move(ca), std::move(sub_key), sig_algo, hash_fn};
111✔
117
}
74✔
118

119
std::pair<Botan::X509_Certificate, Botan::X509_CA> make_and_sign_ca(
38✔
120
   std::unique_ptr<Botan::Certificate_Extension> ext,
121
   Botan::X509_CA& parent_ca,
122
   std::unique_ptr<Botan::RandomNumberGenerator>& rng) {
123
   auto [sig_algo, padding_method, hash_fn] = get_sig_algo_padding();
38✔
124

125
   Botan::X509_Cert_Options opts = ca_opts();
38✔
126
   opts.extensions.add(std::move(ext));
38✔
127

128
   std::unique_ptr<Botan::Private_Key> key = generate_key(sig_algo, *rng);
38✔
129

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

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

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

140
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
141

142
Test::Result test_x509_ip_addr_blocks_extension_decode() {
1✔
143
   Test::Result result("X509 IP Address Block decode");
1✔
144
   result.start_timer();
1✔
145
   const std::string filename("IPAddrBlocksAll.pem");
1✔
146

147
   Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
148

149
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
150

151
   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
   auto& v4_blocks = ipv4block.ranges().value();
1✔
161

162
   // 192.168.0.0
163
   result.test_eq("ipv4 block 0 min", v4_blocks[0].min().value(), "C0A80000");
1✔
164
   // 192.168.127.255
165
   result.test_eq("ipv4 block 0 max", v4_blocks[0].max().value(), "C0A87FFF");
1✔
166

167
   // 193.168.0.0
168
   result.test_eq("ipv4 block 1 min", v4_blocks[1].min().value(), "C1A80000");
1✔
169
   // 193.169.255.255
170
   result.test_eq("ipv4 block 1 max", v4_blocks[1].max().value(), "C1A9FFFF");
1✔
171

172
   // 194.168.0.0
173
   result.test_eq("ipv4 block 2 min", v4_blocks[2].min().value(), "C2A80000");
1✔
174
   // 195.175.1.2
175
   result.test_eq("ipv4 block 2 max", v4_blocks[2].max().value(), "C3AF0102");
1✔
176

177
   // 196.168.0.1
178
   result.test_eq("ipv4 block 3 min", v4_blocks[3].min().value(), "C4A80001");
1✔
179
   // 196.168.0.1
180
   result.test_eq("ipv4 block 3 max", v4_blocks[3].max().value(), "C4A80001");
1✔
181

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

184
   result.test_eq("ipv6 block 0 min", v6_blocks[0].min().value(), "FA800000000000000000000000000000");
1✔
185
   result.test_eq("ipv6 block 0 max", v6_blocks[0].max().value(), "FA800000000000007FFFFFFFFFFFFFFF");
1✔
186
   result.test_eq("ipv6 block 1 min", v6_blocks[1].min().value(), "FE200000000000000000000000000000");
1✔
187
   result.test_eq("ipv6 block 1 max", v6_blocks[1].max().value(), "FE20000007FFFFFFFFFFFFFFFFFFFFFF");
1✔
188
   result.test_eq("ipv6 block 2 min", v6_blocks[2].min().value(), "2003000068293435042010C5000000C4");
1✔
189
   result.test_eq("ipv6 block 2 max", v6_blocks[2].max().value(), "2003000068293435042010C5000000C4");
1✔
190
   result.test_eq("ipv6 block 3 min", v6_blocks[3].min().value(), "AB010000000000000000000000000001");
1✔
191
   result.test_eq("ipv6 block 3 max", v6_blocks[3].max().value(), "CD020000000000000000000000000002");
1✔
192

193
   result.end_timer();
1✔
194
   return result;
2✔
195
}
1✔
196

197
Test::Result test_x509_as_blocks_extension_decode() {
1✔
198
   Test::Result result("X509 AS Block decode");
1✔
199
   result.start_timer();
1✔
200
   using Botan::Cert_Extension::ASBlocks;
1✔
201

202
   {
1✔
203
      const std::string filename("ASNumberCert.pem");
1✔
204
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
205

206
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
207

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

211
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
212
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
213

214
      // cert contains asnum 0-999, 5042, 0-4294967295
215
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
216
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
217

218
      // and rdi 1234-5678, 32768, 0-4294967295
219
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
220
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
221
   }
1✔
222
   {
1✔
223
      const std::string filename("ASNumberOnly.pem");
1✔
224
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
225

226
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
227

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

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

234
      // contains 0-999, 0-4294967295
235
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
236
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
237
   }
1✔
238
   {
1✔
239
      const std::string filename("ASRdiOnly.pem");
1✔
240
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
241

242
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
243

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

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

250
      // contains 1234-5678, 0-4294967295
251
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
252
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
253
   }
1✔
254
   {
1✔
255
      const std::string filename("ASNumberInherit.pem");
1✔
256
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
257

258
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
259

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

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

266
      // contains 1234-5678, 0-4294967295
267
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
268
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
269
   }
1✔
270

271
   result.end_timer();
1✔
272
   return result;
1✔
273
}
×
274

275
   #endif
276

277
Test::Result test_x509_ip_addr_blocks_extension_encode() {
1✔
278
   Test::Result result("X509 IP Address Block encode");
1✔
279
   result.start_timer();
1✔
280

281
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
282

283
   auto rng = Test::new_rng(__func__);
1✔
284

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

287
   for(size_t i = 0; i < 64; i++) {
65✔
288
      bool push_ipv4_ranges = i & 1;
64✔
289
      bool push_ipv6_ranges = i >> 1 & 1;
64✔
290
      bool inherit_ipv4 = i >> 2 & 1;
64✔
291
      bool inherit_ipv6 = i >> 3 & 1;
64✔
292
      bool push_ipv4_family = i >> 4 & 1;
64✔
293
      bool push_ipv6_family = i >> 5 & 1;
64✔
294

295
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
64✔
296

297
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
298
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
299
      a = {255, 255, 255, 255};
64✔
300
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
301

302
      // encoded as min, max
303
      a = {127, 0, 0, 1};
64✔
304
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
305
      a = {189, 5, 7, 255};
64✔
306
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
307

308
      // encoded as prefix
309
      a = {190, 5, 0, 0};
64✔
310
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
311
      a = {190, 5, 127, 255};
64✔
312
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
313

314
      a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
315
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
316
      a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
317
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
318

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

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

326
      // encoded as prefix
327
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
328
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
329
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
330
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
331

332
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
64✔
333
      auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
334
      auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
335
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
64✔
336

337
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
64✔
338
      auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
339
      auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
340
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
64✔
341

342
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
64✔
343
      if(push_ipv4_ranges) {
64✔
344
         ipv4_ranges.push_back(ipv4_range_1);
32✔
345
         ipv4_ranges.push_back(ipv4_range_2);
32✔
346
         ipv4_ranges.push_back(ipv4_range_3);
32✔
347
         ipv4_ranges.push_back(ipv4_range_4);
32✔
348
      }
349

350
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
64✔
351
      if(push_ipv6_ranges) {
64✔
352
         ipv6_ranges.push_back(ipv6_range_1);
32✔
353
         ipv6_ranges.push_back(ipv6_range_2);
32✔
354
         ipv6_ranges.push_back(ipv6_range_3);
32✔
355
         ipv6_ranges.push_back(ipv6_range_4);
32✔
356
      }
357

358
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
64✔
359
      if(!inherit_ipv4) {
64✔
360
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
96✔
361
      }
362

363
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
64✔
364
      if(!inherit_ipv6) {
64✔
365
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
96✔
366
      }
367

368
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
369
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
370

371
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
372
      if(push_ipv4_family) {
64✔
373
         addr_blocks.push_back(ipv4_addr_family);
32✔
374
      }
375
      if(push_ipv6_family) {
64✔
376
         addr_blocks.push_back(ipv6_addr_family);
32✔
377
      }
378

379
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
380

381
      opts.extensions.add(std::move(blocks));
64✔
382

383
      Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
64✔
384
      Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
385
      {
64✔
386
         auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
64✔
387
         result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
128✔
388

389
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
390
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
391
            result.confirm("no address family entries", dec_addr_blocks.empty(), true);
32✔
392
            continue;
16✔
393
         }
394

395
         if(push_ipv4_family) {
48✔
396
            auto family = dec_addr_blocks[0];
32✔
397
            result.confirm("ipv4 family afi", ipv4_addr_family.afi() == family.afi(), true);
64✔
398
            result.confirm("ipv4 family safi", ipv4_addr_family.safi() == family.safi(), true);
96✔
399
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
32✔
400

401
            if(!inherit_ipv4) {
32✔
402
               auto ranges = choice.ranges().value();
16✔
403
               if(push_ipv4_ranges) {
16✔
404
                  result.test_eq("ipv4 entry 0 min", ranges[0].min().value(), ipv4_range_1.min().value());
16✔
405
                  result.test_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
16✔
406
                  result.test_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
16✔
407
                  result.test_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
16✔
408
                  result.test_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
16✔
409
                  result.test_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
16✔
410
                  result.test_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
16✔
411
                  result.test_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
16✔
412
               } else {
413
                  result.confirm("ipv4 range has no entries", ranges.empty(), true);
16✔
414
               }
415
            } else {
16✔
416
               result.confirm("ipv4 family inherit", choice.ranges().has_value(), false);
32✔
417
            }
418
         }
64✔
419

420
         if(push_ipv6_family) {
48✔
421
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
422
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
64✔
423
            result.confirm("ipv6 family safi", ipv6_addr_family.safi() == family.safi(), true);
96✔
424
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
32✔
425
            if(!inherit_ipv6) {
32✔
426
               auto ranges = choice.ranges().value();
16✔
427
               if(push_ipv6_ranges) {
16✔
428
                  result.test_eq("ipv6 entry 0 min", ranges[0].min().value(), ipv6_range_1.min().value());
16✔
429
                  result.test_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
16✔
430
                  result.test_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
16✔
431
                  result.test_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
16✔
432
                  result.test_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
16✔
433
                  result.test_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
16✔
434
                  result.test_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
16✔
435
                  result.test_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
16✔
436
               } else {
437
                  result.confirm("ipv6 range has no entries", ranges.empty(), true);
16✔
438
               }
439
            } else {
16✔
440
               result.confirm("ipv6 family inherit", choice.ranges().has_value(), false);
32✔
441
            }
442
         }
64✔
443
      }
444
   }
320✔
445

446
   result.end_timer();
1✔
447
   return result;
2✔
448
}
2✔
449

450
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases() {
1✔
451
   Test::Result result("X509 IP Address Block encode edge cases");
1✔
452
   result.start_timer();
1✔
453

454
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
455

456
   auto rng = Test::new_rng(__func__);
1✔
457

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

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

464
   for(size_t i = 0; i < edge_values.size(); i++) {
22✔
465
      for(size_t j = 0; j < 4; j++) {
105✔
466
         bool modify_min = j & 1;
84✔
467
         bool modify_max = (j >> 1) & 1;
84✔
468

469
         for(size_t k = 0; k < 18; k++) {
1,219✔
470
            if(!modify_min && !modify_max && (k > 0 || i > 0)) {
1,156✔
471
               // we don't modify anything, this is the extreme edge case of 0.0 ... - 255.255. ...
472
               // so we only need to do this once
473
               break;
474
            }
475

476
            Botan::X509_Cert_Options opts = req_opts(sig_algo);
1,135✔
477

478
            std::vector<uint8_t> min_bytes(16, 0x00);
1,135✔
479
            std::vector<uint8_t> max_bytes(16, 0xFF);
1,135✔
480

481
            if(modify_min) {
1,135✔
482
               min_bytes[15 - (k < 2 ? 0 : k - 2)] = edge_values[i];
756✔
483
            }
484
            if(modify_max) {
1,135✔
485
               max_bytes[15 - (k > 15 ? 15 : k)] = edge_values[i];
756✔
486
            }
487

488
            auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
1,135✔
489
            auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
1,135✔
490

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

493
            std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
1,135✔
494
            ipv6_ranges.push_back(ipv6_range);
1,135✔
495

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

498
            auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3,405✔
499

500
            std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1,135✔
501
            addr_blocks.push_back(ipv6_addr_family);
1,135✔
502

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

505
            opts.extensions.add(std::move(blocks));
1,135✔
506

507
            Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1,135✔
508
            Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1,135✔
509
            {
1,135✔
510
               auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1,135✔
511
               result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2,270✔
512
               const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1,135✔
513
               auto family = dec_addr_blocks[0];
1,135✔
514
               result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
2,270✔
515
               result.confirm("ipv6 family safi", ipv6_addr_family.safi() == family.safi(), true);
3,405✔
516
               auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
1,135✔
517
               auto ranges = choice.ranges().value();
1,135✔
518

519
               result.test_eq("ipv6 edge case min", ranges[0].min().value(), ipv6_range.min().value());
2,270✔
520
               result.test_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
2,270✔
521
            }
2,270✔
522
         }
5,675✔
523
      }
524
   }
525
   result.end_timer();
1✔
526
   return result;
2✔
527
}
3✔
528

529
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
530
   Test::Result result("X509 IP Address Block range merge");
1✔
531
   result.start_timer();
1✔
532

533
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
534

535
   auto rng = Test::new_rng(__func__);
1✔
536

537
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
538
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
539

540
   std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
541
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
542
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
543
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
544
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
545
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
546
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
547
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
548
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
549
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
550
   };
46✔
551

552
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv6_ranges;
1✔
553
   for(auto pair : addresses) {
10✔
554
      auto address_min = IPAddressBlocks::IPAddress<IPv4>(pair[0]);
9✔
555
      auto address_max = IPAddressBlocks::IPAddress<IPv4>(pair[1]);
9✔
556
      auto range = IPAddressBlocks::IPAddressOrRange<IPv4>(address_min, address_max);
9✔
557
      ipv6_ranges.push_back(range);
9✔
558
   }
9✔
559

560
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
1✔
561
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
562

563
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
564
   addr_blocks.push_back(ipv6_addr_family);
1✔
565

566
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
567

568
   opts.extensions.add(std::move(blocks));
1✔
569

570
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
571
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
572
   {
1✔
573
      auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
574
      result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
575
      const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1✔
576
      auto family = dec_addr_blocks[0];
1✔
577
      auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
1✔
578
      auto ranges = choice.ranges().value();
1✔
579

580
      std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
581
      std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
582

583
      result.test_eq("range expected min", ranges[0].min().value(), expected_min);
2✔
584
      result.test_eq("range expected max", ranges[0].max().value(), expected_max);
2✔
585
      result.test_eq("range length", ranges.size(), 1);
1✔
586
   }
2✔
587

588
   result.end_timer();
1✔
589
   return result;
2✔
590
}
24✔
591

592
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
593
   Test::Result result("X509 IP Address Block family merge");
1✔
594
   result.start_timer();
1✔
595

596
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
597

598
   auto rng = Test::new_rng(__func__);
1✔
599

600
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
601
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
602

603
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
604

605
   IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
1✔
606
   IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
1✔
607

608
   uint8_t v4_bytes_1[4] = {123, 123, 123, 123};
1✔
609
   IPAddressBlocks::IPAddress<IPv4> v4_addr_1(v4_bytes_1);
1✔
610
   // create 2 prefixes from the v4 addresses -> they should be merged
611
   IPAddressBlocks::IPAddressChoice<IPv4> v4_choice_dupl({{{{v4_addr_1}, {v4_addr_1}}}});
×
612
   result.confirm(
2✔
613
      "IPAddressChoice v4 merges ranges already in constructor", v4_choice_dupl.ranges().value().size() == 1, true);
1✔
614
   IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
615

616
   uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
617
   IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
1✔
618
   IPAddressBlocks::IPAddressChoice<IPv6> v6_choice_dupl({{{{v6_addr_1}, {v6_addr_1}}}});
×
619
   result.confirm(
2✔
620
      "IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1, true);
1✔
621
   IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
622

623
   IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
624
   IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
625

626
   IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
627
   IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
628

629
   /*
630
   considering the push order, the resulting order should be
631
   [0] v4 no safi
632
   [1] v6 no safi
633
   [2] v4 safi
634
   [3] v6 safi
635
   */
636
   for(size_t i = 0; i < 3; i++) {
4✔
637
      addr_blocks.push_back(v4_empty_fam_safi);
3✔
638
      addr_blocks.push_back(v6_empty_fam);
3✔
639
      addr_blocks.push_back(v4_fam_dupl);
3✔
640
      addr_blocks.push_back(v6_empty_fam_safi);
3✔
641
      addr_blocks.push_back(v6_fam_dupl);
3✔
642
      addr_blocks.push_back(v4_empty_fam);
3✔
643
   }
644

645
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
646
      v4_empty_fam, v6_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_fam_dupl, v6_empty_fam_safi};
7✔
647

648
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
649

650
   opts.extensions.add(std::move(blocks));
1✔
651

652
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
653
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
654

655
   auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
656
   result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
657
   const auto& dec_blocks = ip_blocks->addr_blocks();
1✔
658

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

661
   bool sorted = true;
1✔
662
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
663
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
664
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
665

666
      uint32_t afam_a = a.afi();
5✔
667
      if(a.safi().has_value()) {
5✔
668
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
669
      }
670

671
      uint32_t afam_b = b.afi();
5✔
672
      if(b.safi().has_value()) {
5✔
673
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
674
      }
675

676
      if(afam_a > afam_b) {
5✔
677
         sorted = false;
678
         break;
679
      }
680
   }
681

682
   result.confirm("blocks got sorted", sorted, true);
2✔
683

684
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
685
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
686
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
687

688
      result.confirm("blocks match push order by afi at index " + std::to_string(i), dec.afi() == exp.afi(), true);
12✔
689
      result.confirm("blocks match push order by safi at index " + std::to_string(i), dec.safi() == exp.safi(), true);
24✔
690

691
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
692
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
3✔
693
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
3✔
694

695
         if(!exp_choice.ranges().has_value()) {
3✔
696
            result.confirm(
2✔
697
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
698
         } else {
699
            result.confirm(
1✔
700
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
701

702
            if(dec_choice.ranges().has_value() == false) {
1✔
703
               continue;
×
704
            }
705

706
            auto dec_ranges = dec_choice.ranges().value();
1✔
707
            auto exp_ranges = exp_choice.ranges().value();
1✔
708
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
709
                           dec_ranges.size() == exp_ranges.size(),
1✔
710
                           true);
711

712
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
713
               continue;
×
714
            }
715

716
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
717
               result.test_eq(
2✔
718
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
719
                  exp_ranges[j].min().value(),
1✔
720
                  dec_ranges[j].min().value());
1✔
721
               result.test_eq(
2✔
722
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
723
                  exp_ranges[j].max().value(),
1✔
724
                  dec_ranges[j].max().value());
2✔
725
            }
726
         }
1✔
727
      } else if((exp.afi() == 2) && (dec.afi() == 2)) {
7✔
728
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(dec.addr_choice());
3✔
729
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(exp.addr_choice());
3✔
730

731
         if(!exp_choice.ranges().has_value()) {
3✔
732
            result.confirm(
2✔
733
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
734
         } else {
735
            result.confirm(
1✔
736
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
737

738
            if(dec_choice.ranges().has_value() == false) {
1✔
739
               continue;
×
740
            }
741

742
            auto dec_ranges = dec_choice.ranges().value();
1✔
743
            auto exp_ranges = exp_choice.ranges().value();
1✔
744
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
745
                           dec_ranges.size() == exp_ranges.size(),
1✔
746
                           true);
747

748
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
749
               continue;
×
750
            }
751

752
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
753
               result.test_eq(
2✔
754
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
755
                  exp_ranges[j].min().value(),
1✔
756
                  dec_ranges[j].min().value());
1✔
757
               result.test_eq(
2✔
758
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
759
                  exp_ranges[j].max().value(),
1✔
760
                  dec_ranges[j].max().value());
2✔
761
            }
762
         }
1✔
763
      }
4✔
764
   }
765

766
   result.end_timer();
1✔
767
   return result;
2✔
768
}
19✔
769

770
Test::Result test_x509_ip_addr_blocks_path_validation_success() {
1✔
771
   Test::Result result("X509 IP Address Block path validation success");
1✔
772
   result.start_timer();
1✔
773

774
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
775
   auto rng = Test::new_rng(__func__);
1✔
776

777
   /*
778
   Creates a certificate chain of length 4.
779
   Root: ipv4 and ipv6
780
   Inherit: has both values as 'inherit'
781
   Dynamic: has either both 'inherit', both with values, or just one with a value
782
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
783
   */
784

785
   // Root cert
786
   std::vector<uint8_t> a = {120, 0, 0, 1};
1✔
787
   auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
788
   a = {130, 140, 150, 160};
1✔
789
   auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
790

791
   a = {10, 0, 0, 1};
1✔
792
   auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
793
   a = {10, 255, 255, 255};
1✔
794
   auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
795

796
   a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
797
   auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
798
   a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
799
   auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
800

801
   a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
802
   auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
803
   a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
804
   auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
805

806
   auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
1✔
807
   auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
1✔
808
   auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
1✔
809
   auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
1✔
810

811
   auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
1✔
812
   auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
1✔
813

814
   auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
1✔
815
   auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
1✔
816

817
   auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
3✔
818
   auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
3✔
819

820
   auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
6✔
821
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
1✔
822

823
   // Inherit cert
824
   auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
1✔
825
   auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
1✔
826

827
   auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
2✔
828
   auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
2✔
829

830
   auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
6✔
831
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
1✔
832

833
   // Dynamic Cert
834
   a = {122, 0, 0, 255};
1✔
835
   auto dyn_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
836
   a = {128, 255, 255, 255};
1✔
837
   auto dyn_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
838
   a = {10, 0, 0, 255};
1✔
839
   auto dyn_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
840
   a = {10, 255, 0, 1};
1✔
841
   auto dyn_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
842

843
   a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
844
   auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
845
   a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
846
   auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
847

848
   auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
1✔
849
   auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
1✔
850
   auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
1✔
851

852
   auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
1✔
853
   auto dyn_ipv6_ranges = {dyn_ipv6_range};
1✔
854

855
   // Subject cert
856
   a = {124, 0, 255, 0};
1✔
857
   auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
858
   a = {126, 0, 0, 1};
1✔
859
   auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
860

861
   a = {10, 0, 2, 1};
1✔
862
   auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
863
   a = {10, 42, 0, 255};
1✔
864
   auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
865

866
   a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
867
   auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
868
   a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
869
   auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
870

871
   auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
1✔
872
   auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
1✔
873
   auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
1✔
874

875
   auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
1✔
876
   auto sub_ipv6_ranges = {sub_ipv6_range};
1✔
877

878
   auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
1✔
879
   auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
1✔
880

881
   auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
3✔
882
   auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
3✔
883

884
   auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
6✔
885
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
1✔
886

887
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
888
   root_opts.extensions.add(std::move(root_blocks));
1✔
889
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
890
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
891
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
892
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
893

894
   Botan::Certificate_Store_In_Memory trusted;
1✔
895
   trusted.add_certificate(root_cert);
1✔
896

897
   for(size_t i = 0; i < 4; i++) {
5✔
898
      bool include_v4 = i & 1;
4✔
899
      bool include_v6 = (i >> 1) & 1;
4✔
900

901
      auto dyn_ipv4_choice =
4✔
902
         IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
8✔
903
      auto dyn_ipv6_choice =
4✔
904
         IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
8✔
905

906
      auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
10✔
907
      auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
10✔
908

909
      auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
24✔
910
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
4✔
911

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

914
      Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
915
      Botan::X509_Certificate sub_cert =
4✔
916
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
917

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

921
      Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
922
      result.require("path validation succeeds", path_result.successful_validation());
4✔
923
   }
28✔
924

925
   result.end_timer();
1✔
926
   return result;
2✔
927
}
24✔
928

929
Test::Result test_x509_ip_addr_blocks_path_validation_failure() {
1✔
930
   Test::Result result("X509 IP Address Block path validation failure");
1✔
931
   result.start_timer();
1✔
932

933
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
934
   auto rng = Test::new_rng(__func__);
1✔
935

936
   for(size_t i = 0; i < 7; i++) {
8✔
937
      bool all_inherit = (i == 0);
7✔
938
      bool different_safi = (i == 1);
7✔
939
      bool too_small_subrange = (i == 2);
7✔
940
      bool too_large_subrange = (i == 3);
7✔
941
      bool no_more_issuer_ranges = (i == 4);
7✔
942
      bool empty_issuer_ranges = (i == 5);
7✔
943
      bool nullptr_extensions = (i == 6);
7✔
944

945
      // Root cert
946
      std::vector<uint8_t> a = {120, 0, 0, 1};
7✔
947
      auto root_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
948
      a = {130, 140, 150, 160};
7✔
949
      auto root_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
950

951
      auto root_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_range_1_min, root_range_1_max);
7✔
952
      auto root_ranges = {root_range_1};
7✔
953
      auto root_choice =
7✔
954
         IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(root_ranges));
14✔
955
      auto root_family = IPAddressBlocks::IPAddressFamily(root_choice, 42);
20✔
956
      auto root_addr_blocks = {root_family};
21✔
957
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
7✔
958

959
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
960
      if(!nullptr_extensions) {
7✔
961
         root_opts.extensions.add(std::move(root_blocks));
12✔
962
      }
963
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
964

965
      // Issuer Cert
966
      a = {122, 0, 0, 255};
7✔
967
      auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
968
      a = {128, 255, 255, 255};
7✔
969
      auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
970
      auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
7✔
971

972
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
7✔
973

974
      if(!empty_issuer_ranges) {
7✔
975
         iss_ranges.push_back(iss_range_1);
6✔
976
      }
977

978
      auto iss_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(iss_ranges));
19✔
979
      auto iss_family = IPAddressBlocks::IPAddressFamily(iss_choice, 42);
20✔
980
      auto iss_addr_blocks = {iss_family};
21✔
981
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>(iss_addr_blocks);
7✔
982
      auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
14✔
983

984
      // Subject cert
985
      if(too_small_subrange) {
7✔
986
         a = {118, 0, 255, 0};
2✔
987
      } else if(no_more_issuer_ranges) {
6✔
988
         a = {140, 0, 0, 1};
2✔
989
      } else {
990
         a = {124, 0, 255, 0};
10✔
991
      }
992

993
      auto sub_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
994
      if(too_large_subrange) {
7✔
995
         a = {134, 0, 0, 1};
2✔
996
      } else if(no_more_issuer_ranges) {
6✔
997
         a = {150, 0, 0, 1};
2✔
998
      } else {
999
         a = {126, 0, 0, 1};
10✔
1000
      }
1001
      auto sub_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1002

1003
      auto sub_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_range_1_min, sub_range_1_max);
7✔
1004
      auto sub_ranges = {sub_range_1};
7✔
1005
      auto sub_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(sub_ranges));
14✔
1006
      auto sub_family = IPAddressBlocks::IPAddressFamily(sub_choice, different_safi ? 41 : 42);
26✔
1007

1008
      auto sub_addr_blocks = {sub_family};
21✔
1009
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
7✔
1010

1011
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1012
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1013

1014
      Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1015
      Botan::X509_Certificate sub_cert =
7✔
1016
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1017

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

1021
      Botan::Certificate_Store_In_Memory trusted;
7✔
1022
      trusted.add_certificate(root_cert);
7✔
1023

1024
      Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1025
      result.require("path validation fails", !path_result.successful_validation());
7✔
1026
   }
82✔
1027

1028
   result.end_timer();
1✔
1029
   return result;
1✔
1030
}
8✔
1031

1032
Test::Result test_x509_as_blocks_extension_encode() {
1✔
1033
   Test::Result result("X509 AS Blocks encode");
1✔
1034
   result.start_timer();
1✔
1035

1036
   using Botan::Cert_Extension::ASBlocks;
1✔
1037

1038
   auto rng = Test::new_rng(__func__);
1✔
1039

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

1042
   for(size_t i = 0; i < 16; i++) {
17✔
1043
      bool push_asnum = i & 1;
16✔
1044
      bool push_rdi = (i >> 1) & 1;
16✔
1045
      bool include_asnum = (i >> 2) & 1;
16✔
1046
      bool include_rdi = (i >> 3) & 1;
16✔
1047
      if(!include_asnum && !include_rdi) {
16✔
1048
         continue;
4✔
1049
      }
1050

1051
      ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
12✔
1052
      ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
12✔
1053
      ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
12✔
1054

1055
      ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
12✔
1056
      ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
12✔
1057
      ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
12✔
1058

1059
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
12✔
1060
      if(push_asnum) {
12✔
1061
         as_ranges.push_back(asnum_id_or_range0);
6✔
1062
         as_ranges.push_back(asnum_id_or_range1);
6✔
1063
         as_ranges.push_back(asnum_id_or_range2);
6✔
1064
      }
1065

1066
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
12✔
1067
      if(push_rdi) {
12✔
1068
         rdi_ranges.push_back(rdi_id_or_range0);
6✔
1069
         rdi_ranges.push_back(rdi_id_or_range1);
6✔
1070
         rdi_ranges.push_back(rdi_id_or_range2);
6✔
1071
      }
1072

1073
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
12✔
1074
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
12✔
1075

1076
      ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
32✔
1077
                                                              include_rdi ? std::optional(rdi) : std::nullopt);
32✔
1078

1079
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
12✔
1080

1081
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
12✔
1082
      opts.extensions.add(std::move(blocks));
12✔
1083

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

1087
      {
12✔
1088
         auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
12✔
1089
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
24✔
1090

1091
         const auto& identifier = as_blocks->as_identifiers();
12✔
1092

1093
         if(include_asnum) {
12✔
1094
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
1095

1096
            if(push_asnum) {
8✔
1097
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
1098
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
1099

1100
               result.confirm("asnum entry 1 min", asnum_entries[1].min() == 5042, true);
8✔
1101
               result.confirm("asnum entry 1 max", asnum_entries[1].max() == 4294967295, true);
8✔
1102
            } else {
1103
               result.confirm("asnum has no entries", asnum_entries.empty(), true);
8✔
1104
            }
1105
         } else {
1106
            result.confirm("no asnum entry", identifier.asnum().has_value(), false);
8✔
1107
         }
1108

1109
         if(include_rdi) {
12✔
1110
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
1111

1112
            if(push_rdi) {
8✔
1113
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
1114
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
1115

1116
               result.confirm("rdi entry 1 min", rdi_entries[1].min() == 32768, true);
8✔
1117
               result.confirm("rdi entry 1 max", rdi_entries[1].max() == 4294967295, true);
8✔
1118
            } else {
1119
               result.confirm("rdi has no entries", rdi_entries.empty(), true);
8✔
1120
            }
1121
         } else {
1122
            result.confirm("rdi has no entry", identifier.rdi().has_value(), false);
8✔
1123
         }
1124
      }
1125
   }
36✔
1126

1127
   result.end_timer();
1✔
1128
   return result;
2✔
1129
}
2✔
1130

1131
Test::Result test_x509_as_blocks_range_merge() {
1✔
1132
   Test::Result result("X509 AS Block range merge");
1✔
1133
   result.start_timer();
1✔
1134

1135
   using Botan::Cert_Extension::ASBlocks;
1✔
1136

1137
   auto rng = Test::new_rng(__func__);
1✔
1138

1139
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1140
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
1141

1142
   std::vector<std::vector<uint16_t>> ranges = {
1✔
1143
      {2005, 37005},
1144
      {60, 70},
1145
      {22, 50},
1146
      {35, 2000},
1147
      {2001, 2004},
1148
      {21, 21},
1149
      {0, 20},
1150
   };
9✔
1151

1152
   std::vector<ASBlocks::ASIdOrRange> as_ranges;
1✔
1153
   for(auto pair : ranges) {
8✔
1154
      auto range = ASBlocks::ASIdOrRange(pair[0], pair[1]);
7✔
1155
      as_ranges.push_back(range);
7✔
1156
   }
7✔
1157

1158
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
1159

1160
   ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(std::optional(asnum), std::nullopt);
3✔
1161

1162
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
1✔
1163

1164
   opts.extensions.add(std::move(blocks));
1✔
1165

1166
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
1167
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1168
   {
1✔
1169
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
1170
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1171

1172
      const auto& identifier = as_blocks->as_identifiers();
1✔
1173

1174
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
1175

1176
      result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
2✔
1177
      result.confirm("asnum entry 0 max", asnum_entries[0].max() == 37005, true);
2✔
1178
      result.confirm("asnum length", asnum_entries.size() == 1, true);
2✔
1179
   }
1180

1181
   result.end_timer();
1✔
1182
   return result;
2✔
1183
}
5✔
1184

1185
Test::Result test_x509_as_blocks_path_validation_success() {
1✔
1186
   Test::Result result("X509 AS Block path validation success");
1✔
1187
   result.start_timer();
1✔
1188

1189
   using Botan::Cert_Extension::ASBlocks;
1✔
1190
   auto rng = Test::new_rng(__func__);
1✔
1191

1192
   /*
1193
   Creates a certificate chain of length 4.
1194
   Root: both asnum and rdi
1195
   Inherit: has both values as 'inherit'
1196
   Dynamic: has either both 'inherit', both with values, or just one with a value
1197
   Subject: both asnum and rdi as a subset of Root / Dynamic
1198
   */
1199

1200
   // Root Cert, both as and rdi
1201
   ASBlocks::ASIdOrRange root_asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
1✔
1202
   ASBlocks::ASIdOrRange root_asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
1✔
1203
   ASBlocks::ASIdOrRange root_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
1✔
1204

1205
   ASBlocks::ASIdOrRange root_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
1✔
1206
   ASBlocks::ASIdOrRange root_rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
1✔
1207
   ASBlocks::ASIdOrRange root_rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
1✔
1208

1209
   std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
1✔
1210
   root_as_ranges.push_back(root_asnum_id_or_range0);
1✔
1211
   root_as_ranges.push_back(root_asnum_id_or_range1);
1✔
1212
   root_as_ranges.push_back(root_asnum_id_or_range2);
1✔
1213

1214
   std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
1✔
1215
   root_rdi_ranges.push_back(root_rdi_id_or_range0);
1✔
1216
   root_rdi_ranges.push_back(root_rdi_id_or_range1);
1✔
1217
   root_rdi_ranges.push_back(root_rdi_id_or_range2);
1✔
1218

1219
   ASBlocks::ASIdentifierChoice root_asnum = ASBlocks::ASIdentifierChoice(root_as_ranges);
1✔
1220
   ASBlocks::ASIdentifierChoice root_rdi = ASBlocks::ASIdentifierChoice(root_rdi_ranges);
1✔
1221
   ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
4✔
1222
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
1✔
1223

1224
   // Inherit cert, both as 'inherit'
1225
   ASBlocks::ASIdentifierChoice inherit_asnum = ASBlocks::ASIdentifierChoice();
1✔
1226
   ASBlocks::ASIdentifierChoice inherit_rdi = ASBlocks::ASIdentifierChoice();
1✔
1227
   ASBlocks::ASIdentifiers inherit_ident = ASBlocks::ASIdentifiers(inherit_asnum, inherit_rdi);
2✔
1228
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>(inherit_ident);
1✔
1229

1230
   // Dynamic cert
1231
   ASBlocks::ASIdOrRange dyn_asnum_id_or_range0 = ASBlocks::ASIdOrRange(100, 600);
1✔
1232
   ASBlocks::ASIdOrRange dyn_asnum_id_or_range1 = ASBlocks::ASIdOrRange(678);
1✔
1233
   ASBlocks::ASIdOrRange dyn_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5042, 5101);
1✔
1234

1235
   ASBlocks::ASIdOrRange dyn_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 5000);
1✔
1236
   ASBlocks::ASIdOrRange dyn_rdi_id_or_range1 = ASBlocks::ASIdOrRange(33000, 60000);
1✔
1237

1238
   std::vector<ASBlocks::ASIdOrRange> dyn_as_ranges;
1✔
1239
   dyn_as_ranges.push_back(dyn_asnum_id_or_range0);
1✔
1240
   dyn_as_ranges.push_back(dyn_asnum_id_or_range1);
1✔
1241
   dyn_as_ranges.push_back(dyn_asnum_id_or_range2);
1✔
1242

1243
   std::vector<ASBlocks::ASIdOrRange> dyn_rdi_ranges;
1✔
1244
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range0);
1✔
1245
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range1);
1✔
1246

1247
   // Subject cert
1248
   ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1249
   ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1250
   ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1251
   ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1252
   ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1253

1254
   ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1255
   ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1256
   ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1257
   ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1258

1259
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1260
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1261
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1262
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1263
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1264
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1265

1266
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1267
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1268
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1269
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1270
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1271

1272
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1273
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1274
   ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1275
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1276

1277
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1278
   root_opts.extensions.add(std::move(root_blocks));
1✔
1279
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1280
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1281
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1282
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1283

1284
   Botan::Certificate_Store_In_Memory trusted;
1✔
1285
   trusted.add_certificate(root_cert);
1✔
1286

1287
   for(size_t i = 0; i < 4; i++) {
5✔
1288
      bool include_asnum = i & 1;
4✔
1289
      bool include_rdi = (i >> 1) & 1;
4✔
1290

1291
      ASBlocks::ASIdentifierChoice dyn_asnum =
4✔
1292
         ASBlocks::ASIdentifierChoice(include_asnum ? std::optional(dyn_as_ranges) : std::nullopt);
6✔
1293
      ASBlocks::ASIdentifierChoice dyn_rdi =
4✔
1294
         ASBlocks::ASIdentifierChoice(include_rdi ? std::optional(dyn_rdi_ranges) : std::nullopt);
6✔
1295
      ASBlocks::ASIdentifiers dyn_ident = ASBlocks::ASIdentifiers(dyn_asnum, dyn_rdi);
12✔
1296
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>(dyn_ident);
4✔
1297

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

1300
      Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1301
      Botan::X509_Certificate sub_cert =
4✔
1302
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1303

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

1307
      Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1308
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1309
   }
12✔
1310

1311
   result.end_timer();
1✔
1312
   return result;
2✔
1313
}
11✔
1314

1315
Test::Result test_x509_as_blocks_path_validation_extension_not_present() {
1✔
1316
   Test::Result result("X509 AS Block path validation extension not present");
1✔
1317
   result.start_timer();
1✔
1318

1319
   using Botan::Cert_Extension::ASBlocks;
1✔
1320
   auto rng = Test::new_rng(__func__);
1✔
1321

1322
   // Subject cert
1323
   ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1324
   ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1325
   ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1326
   ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1327
   ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1328

1329
   ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1330
   ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1331
   ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1332
   ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1333

1334
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1335
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1336
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1337
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1338
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1339
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1340

1341
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1342
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1343
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1344
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1345
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1346

1347
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1348
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1349
   ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1350
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1351

1352
   // create a root ca that does not have any extension
1353
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1354
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1355
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1356
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1357
   Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
1358
   Botan::X509_Certificate sub_cert = root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1359

1360
   Botan::Certificate_Store_In_Memory trusted;
1✔
1361
   trusted.add_certificate(root_cert);
1✔
1362

1363
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1364
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1365

1366
   Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
1✔
1367
   result.require("path validation fails", !path_result.successful_validation());
1✔
1368

1369
   result.end_timer();
1✔
1370
   return result;
2✔
1371
}
5✔
1372

1373
Test::Result test_x509_as_blocks_path_validation_failure() {
1✔
1374
   Test::Result result("X509 AS Block path validation failure");
1✔
1375
   result.start_timer();
1✔
1376

1377
   using Botan::Cert_Extension::ASBlocks;
1✔
1378
   auto rng = Test::new_rng(__func__);
1✔
1379

1380
   /*
1381
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
1382

1383
   Each test is expected to fail and creates the following certificate chain:
1384
   Root -> Issuer -> Subject
1385

1386
   00: set all the asnum choices to 'inherit' for each cert
1387
   01: 00 but for rdis
1388
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
1389
   03: 02 but for rdis
1390
   04: both 02 and 03
1391
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
1392
   06: 05 but for rdis
1393
   07: both 05 and 06
1394
   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
1395
   09: 08 but for rdis
1396
   10: both 08 and 09
1397
   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)
1398
   12: 11 but for rdis
1399
   13: both 11 and 12
1400
   14: 08 but using the minimum instead of the maximum
1401
   15: 14 but for rdis
1402
   16: both 14 and 15
1403
   17: same as 11 but using the minimum instead of the maximum
1404
   18: 17 but for rdis
1405
   19: both 18 and 19
1406
   20: make the issuer ranges empty but have an entry in the subject ranges
1407
   */
1408
   for(size_t i = 0; i < 21; i++) {
22✔
1409
      // enable / disable all the different edge cases
1410
      bool inherit_all_asnums = (i == 0);
21✔
1411
      bool inherit_all_rdis = (i == 1);
21✔
1412
      bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
1413
      bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
1414
      bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
1415
      bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
1416
      bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
1417
      bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
1418
      bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
1419
      bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
1420
      bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
1421
      bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
1422
      bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
1423
      bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
1424
      bool empty_issuer_non_empty_subject = (i == 20);
21✔
1425

1426
      // Root cert
1427
      std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
21✔
1428
      std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
21✔
1429

1430
      // assign the root ranges
1431
      if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
21✔
1432
         // 100-200 for 02,03,04
1433
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
1434
      } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
17✔
1435
         // 10-20,30-40,50-60 for 08,09,10
1436
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
1437
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
1438
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
1439
      } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
13✔
1440
         // 10-20,30-50,60-70 for 11,12,13
1441
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
1442
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
1443
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
1444
      }
1445

1446
      // same values but for rdis
1447
      if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
21✔
1448
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
1449
      } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
1450
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
1451
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
1452
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
1453
      } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
1454
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
1455
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
1456
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
1457
      }
1458

1459
      // Issuer cert
1460
      // the issuer cert has the same ranges as the root cert
1461
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
1462
      std::vector<ASBlocks::ASIdOrRange> issu_as_ranges;
21✔
1463
      std::vector<ASBlocks::ASIdOrRange> issu_rdi_ranges;
21✔
1464

1465
      // Subject cert
1466
      std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
21✔
1467
      std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
21✔
1468

1469
      // assign the subject asnum ranges
1470
      if(push_asnum_min_edge_ranges) {
21✔
1471
         // 99-200 for 02 (so overlapping to the left)
1472
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
4✔
1473
      } else if(push_asnum_max_edge_ranges) {
1474
         // 100-201 for 03 (so overlapping to the right)
1475
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
4✔
1476
      } else if(push_asnum_max_middle_ranges) {
1477
         // just change the range in the middle to overlap to the right for 08
1478
         sub_as_ranges = root_as_ranges;
2✔
1479
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
2✔
1480
      } else if(push_asnum_max_split_ranges) {
1481
         // change the range in the middle to be cut at 45 for case 11
1482
         // the left range is 30-44
1483
         // the right range is 46-51 (overlapping the issuer range to the right)
1484
         sub_as_ranges = root_as_ranges;
2✔
1485
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
2✔
1486
         // pushing the new range created by splitting to the back since they will be sorted anyway
1487
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
4✔
1488
      } else if(push_asnum_min_middle_ranges) {
1489
         // just change the test in the middle to overlap to the left for case 14
1490
         sub_as_ranges = root_as_ranges;
2✔
1491
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
2✔
1492
      } else if(push_asnum_min_split_ranges) {
1493
         // again split the range in the middle at 45 for case 17
1494
         // creating two ranges 29-44 and 46-50 (so overlapping to the left)
1495
         sub_as_ranges = root_as_ranges;
2✔
1496
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
2✔
1497
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
4✔
1498
      } else if(empty_issuer_non_empty_subject) {
1499
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(50));
1✔
1500
      }
1501

1502
      // same values but for rdis
1503
      if(push_rdi_min_edge_ranges) {
21✔
1504
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
4✔
1505
      } else if(push_rdi_max_edge_ranges) {
1506
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
4✔
1507
      } else if(push_rdi_max_middle_ranges) {
1508
         sub_rdi_ranges = root_rdi_ranges;
2✔
1509
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
2✔
1510
      } else if(push_rdi_max_split_ranges) {
1511
         sub_rdi_ranges = root_rdi_ranges;
2✔
1512
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
2✔
1513
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
4✔
1514
      } else if(push_rdi_min_middle_ranges) {
1515
         sub_rdi_ranges = root_rdi_ranges;
2✔
1516
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
2✔
1517
      } else if(push_rdi_min_split_ranges) {
1518
         sub_rdi_ranges = root_rdi_ranges;
2✔
1519
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
2✔
1520
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
4✔
1521
      }
1522

1523
      // for cases 00 and 01, set all certs to inherit (so std::nullopt)
1524
      // in all other cases use the ranges created beforehand
1525
      ASBlocks::ASIdentifierChoice root_asnum =
21✔
1526
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(root_as_ranges));
41✔
1527
      ASBlocks::ASIdentifierChoice root_rdi =
21✔
1528
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(root_rdi_ranges));
41✔
1529
      ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
82✔
1530
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
21✔
1531

1532
      ASBlocks::ASIdentifiers issu_ident = root_ident;
21✔
1533
      std::unique_ptr<ASBlocks> issu_blocks = std::make_unique<ASBlocks>(issu_ident);
21✔
1534

1535
      ASBlocks::ASIdentifierChoice sub_asnum =
21✔
1536
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(sub_as_ranges));
41✔
1537
      ASBlocks::ASIdentifierChoice sub_rdi =
21✔
1538
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(sub_rdi_ranges));
41✔
1539
      ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
82✔
1540
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
21✔
1541

1542
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
1543
      root_opts.extensions.add(std::move(root_blocks));
21✔
1544
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
1545
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
1546

1547
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
1548
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
1549
      Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
1550
      Botan::X509_Certificate sub_cert =
21✔
1551
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
1552

1553
      Botan::Certificate_Store_In_Memory trusted;
21✔
1554
      trusted.add_certificate(root_cert);
21✔
1555

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

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

1564
   result.end_timer();
1✔
1565
   return result;
1✔
1566
}
22✔
1567

1568
class X509_RPKI_Tests final : public Test {
×
1569
   public:
1570
      std::vector<Test::Result> run() override {
1✔
1571
         std::vector<Test::Result> results;
1✔
1572

1573
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1574
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
1575
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
1576
   #endif
1577

1578
         results.push_back(test_x509_ip_addr_blocks_extension_encode());
2✔
1579
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases());
2✔
1580
         results.push_back(test_x509_ip_addr_blocks_range_merge());
2✔
1581
         results.push_back(test_x509_ip_addr_blocks_family_merge());
2✔
1582
         results.push_back(test_x509_ip_addr_blocks_path_validation_success());
2✔
1583
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure());
2✔
1584
         results.push_back(test_x509_as_blocks_extension_encode());
2✔
1585
         results.push_back(test_x509_as_blocks_range_merge());
2✔
1586
         results.push_back(test_x509_as_blocks_path_validation_success());
2✔
1587
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present());
2✔
1588
         results.push_back(test_x509_as_blocks_path_validation_failure());
2✔
1589
         return results;
1✔
1590
      }
×
1591
};
1592

1593
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
1594

1595
#endif
1596

1597
}  // namespace
1598

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