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

randombit / botan / 22022566023

14 Feb 2026 06:55PM UTC coverage: 90.067%. Remained the same
22022566023

push

github

web-flow
Merge pull request #5334 from randombit/jack/test-h-remove-confirm

Remove confirm test helper, use test_is_true or test_is_false instead

102260 of 113538 relevant lines covered (90.07%)

11510296.11 hits per line

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

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

8
#include "tests.h"
9

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

22
namespace Botan_Tests {
23

24
namespace {
25

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

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

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

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

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

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

52
   opts.CA_key(1);
149✔
53

54
   return opts;
149✔
55
}
×
56

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

141
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
142

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

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

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

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

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

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

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

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

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

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

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

187
      result.test_eq("ipv6 block 0 min",
1✔
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",
1✔
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",
1✔
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",
1✔
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",
1✔
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",
1✔
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",
1✔
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",
1✔
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_sz_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.test_is_true(
1✔
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.test_is_true("block 1 has correct size", block_1.size() == 1);
1✔
240
      result.test_eq("block 1 min is correct", block_1[0].min().value(), {192, 168, 0, 0});
1✔
241
      result.test_eq("block 1 max is correct", block_1[0].max().value(), {200, 0, 0, 0});
1✔
242

243
      result.test_eq("block 2 has correct safi", addr_blocks[2].safi(), std::optional<uint8_t>{2});
1✔
244
      result.test_is_true(
1✔
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.test_is_true("block 3 has correct size", block_3.size() == 1);
1✔
253
      result.test_eq("block 3 min is correct",
1✔
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",
1✔
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.test_is_true(
1✔
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.test_is_true("extension is present", cert.v3_extensions().extension_set(IPAddressBlocks::static_oid()));
1✔
272

273
      const auto* ext = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
274
      result.test_is_true("extension is not decoded", ext == nullptr);
1✔
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.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
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.test_is_true("asnum entry 0 min", asnum[0].min() == 0);
1✔
300
      result.test_is_true("asnum entry 0 max", asnum[0].max() == 4294967295);
1✔
301

302
      // and rdi 1234-5678, 32768, 0-4294967295
303
      result.test_is_true("rdi entry 0 min", rdi[0].min() == 0);
1✔
304
      result.test_is_true("rdi entry 0 max", rdi[0].max() == 4294967295);
1✔
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.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
314

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

318
      // contains 0-999, 0-4294967295
319
      result.test_is_true("asnum entry 0 min", asnum[0].min() == 0);
1✔
320
      result.test_is_true("asnum entry 0 max", asnum[0].max() == 4294967295);
1✔
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.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
330

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

334
      // contains 1234-5678, 0-4294967295
335
      result.test_is_true("rdi entry 0 min", rdi[0].min() == 0);
1✔
336
      result.test_is_true("rdi entry 0 max", rdi[0].max() == 4294967295);
1✔
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.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
346

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

350
      // contains 1234-5678, 0-4294967295
351
      result.test_is_true("rdi entry 0 min", rdi[0].min() == 0);
1✔
352
      result.test_is_true("rdi entry 0 max", rdi[0].max() == 4294967295);
1✔
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});
1✔
397
   result.test_eq("extension 1 range 1 max", ext_1_ranges[0].max().value(), {10, 0, 47, 255});
1✔
398

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

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

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

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

411
   result.test_eq("extension 1 ipv6 safi", ext_1->addr_blocks()[1].safi(), std::optional<uint8_t>{std::nullopt});
1✔
412
   result.test_is_true(
1✔
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});
1✔
444
   result.test_eq("extension 2 fam 1 range 1 max", ext_2_ranges_1[0].max().value(), {10, 255, 255, 255});
1✔
445

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

449
   result.test_eq("extension 2 ipv4 2 safi", ext_2->addr_blocks()[1].safi(), std::optional<uint8_t>{2});
1✔
450
   result.test_is_true(
1✔
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",
1✔
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",
1✔
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.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
64✔
632

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

639
         if(push_ipv4_family) {
48✔
640
            auto family = dec_addr_blocks[0];
32✔
641
            result.test_is_true("ipv4 family afi", ipv4_addr_family.afi() == family.afi());
32✔
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());
8✔
649
                  result.test_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
8✔
650
                  result.test_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
8✔
651
                  result.test_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
8✔
652
                  result.test_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
8✔
653
                  result.test_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
8✔
654
                  result.test_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
8✔
655
                  result.test_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
8✔
656
               } else {
657
                  result.test_is_true("ipv4 range has no entries", ranges.empty());
8✔
658
               }
659
            } else {
16✔
660
               result.test_is_false("ipv4 family inherit", choice.ranges().has_value());
16✔
661
            }
662
         }
64✔
663

664
         if(push_ipv6_family) {
48✔
665
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
666
            result.test_is_true("ipv6 family afi", ipv6_addr_family.afi() == family.afi());
32✔
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());
8✔
673
                  result.test_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
8✔
674
                  result.test_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
8✔
675
                  result.test_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
8✔
676
                  result.test_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
8✔
677
                  result.test_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
8✔
678
                  result.test_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
8✔
679
                  result.test_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
8✔
680
               } else {
681
                  result.test_is_true("ipv6 range has no entries", ranges.empty());
8✔
682
               }
683
            } else {
16✔
684
               result.test_is_false("ipv6 family inherit", choice.ranges().has_value());
16✔
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.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
1,135✔
757
               const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1,135✔
758
               auto family = dec_addr_blocks[0];
1,135✔
759
               result.test_is_true("ipv6 family afi", ipv6_addr_family.afi() == family.afi());
1,135✔
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());
1,135✔
765
               result.test_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
1,135✔
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.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
1✔
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);
1✔
829
      result.test_eq("range expected max", ranges[0].max().value(), expected_max);
1✔
830
      result.test_sz_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.test_is_true("IPAddressChoice v4 merges ranges already in constructor",
1✔
861
                       v4_choice_dupl.ranges().value().size() == 1);
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.test_is_true("IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1);
1✔
871
   const IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
872

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

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

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

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

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

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

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

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

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

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

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

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

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

936
   result.test_is_true("blocks got sorted", sorted);
1✔
937

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1693
   // Root Cert, both as and rdi
1694

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

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

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

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

1710
   // Subject cert
1711

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2192
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
21✔
2193
      // in all cases, the validation should fail, since we are creating invalid scenarios
2194
      result.test_is_true("path validation fails at iteration " + std::to_string(i),
42✔
2195
                          !path_result.successful_validation());
21✔
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.test_is_true("path validation fails at iteration " + std::to_string(i),
42✔
2391
                          !path_result.successful_validation());
21✔
2392
   }
122✔
2393

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

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

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

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

2433
#endif
2434

2435
}  // namespace
2436

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