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

randombit / botan / 25457312714

06 May 2026 07:43PM UTC coverage: 89.331% (-2.3%) from 91.667%
25457312714

push

github

randombit
In TLS 1.3 verification of client certs, check the correct extension for OCSP

This was checking if the client asked us (the server) for OCSP, instead of
checking if we asked the client for OCSP when we sent the CertificateRequest.

107574 of 120422 relevant lines covered (89.33%)

11482758.98 hits per line

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

99.21
/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/ber_dec.h>
12
   #include <botan/certstor.h>
13
   #include <botan/pk_algs.h>
14
   #include <botan/pubkey.h>
15
   #include <botan/rng.h>
16
   #include <botan/x509_ca.h>
17
   #include <botan/x509_ext.h>
18
   #include <botan/x509path.h>
19
   #include <botan/x509self.h>
20
   #include <botan/internal/calendar.h>
21
#endif
22

23
namespace Botan_Tests {
24

25
namespace {
26

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

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

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

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

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

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

53
   opts.CA_key(1);
149✔
54

55
   return opts;
149✔
56
}
×
57

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

142
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
143

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

370
   #endif
371

372
Test::Result test_x509_as_blocks_extension_decode_malformed() {
1✔
373
   Test::Result result("X509 AS Block decode malformed");
1✔
374
   result.start_timer();
1✔
375
   using Botan::Cert_Extension::ASBlocks;
1✔
376

377
   // ASIdentifiers default ctor is private; seed via the public ctor and
378
   // overwrite by calling decode_from on hand-crafted BER.
379
   const auto dummy_choice = ASBlocks::ASIdentifierChoice(std::vector<ASBlocks::ASIdOrRange>{ASBlocks::ASIdOrRange(1)});
2✔
380

381
   auto try_decode = [&](const std::string& what, const std::vector<uint8_t>& bytes) {
6✔
382
      result.test_throws(what, [&]() {
5✔
383
         ASBlocks::ASIdentifiers ident(dummy_choice, std::nullopt);
15✔
384
         Botan::BER_Decoder dec(bytes);
5✔
385
         dec.decode(ident).verify_end();
5✔
386
      });
10✔
387
   };
5✔
388

389
   // SEQUENCE { [0] EXPLICIT { NULL with non-zero contents } }
390
   try_decode("ASIdentifiers rejects asnum NULL with non-zero length", {0x30, 0x05, 0xA0, 0x03, 0x05, 0x01, 0x00});
2✔
391

392
   // SEQUENCE { [0] EXPLICIT { NULL, trailing INTEGER } }
393
   try_decode("ASIdentifiers rejects trailing data after asnum NULL",
2✔
394
              {0x30, 0x07, 0xA0, 0x05, 0x05, 0x00, 0x02, 0x01, 0x00});
395

396
   // SEQUENCE { [0] EXPLICIT { SEQUENCE { INTEGER 5042 }, trailing INTEGER } }
397
   try_decode("ASIdentifiers rejects trailing data after asnum SEQUENCE",
2✔
398
              {0x30, 0x0B, 0xA0, 0x09, 0x30, 0x04, 0x02, 0x02, 0x13, 0xB2, 0x02, 0x01, 0x00});
399

400
   // SEQUENCE { [1] EXPLICIT { NULL with non-zero contents } }
401
   try_decode("ASIdentifiers rejects rdi NULL with non-zero length", {0x30, 0x05, 0xA1, 0x03, 0x05, 0x01, 0x00});
2✔
402

403
   // SEQUENCE { [1] EXPLICIT { SEQUENCE { INTEGER 5042 }, trailing INTEGER } }
404
   try_decode("ASIdentifiers rejects trailing data after rdi SEQUENCE",
2✔
405
              {0x30, 0x0B, 0xA1, 0x09, 0x30, 0x04, 0x02, 0x02, 0x13, 0xB2, 0x02, 0x01, 0x00});
406

407
   result.end_timer();
1✔
408
   return result;
1✔
409
}
2✔
410

411
Test::Result test_x509_ip_addr_blocks_rfc3779_example() {
1✔
412
   Test::Result result("X509 IP Address Blocks rfc3779 example");
1✔
413
   result.start_timer();
1✔
414

415
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
416
   auto rng = Test::new_rng(__func__);
1✔
417

418
   // construct like in https://datatracker.ietf.org/doc/html/rfc3779#page-18
419
   std::unique_ptr<IPAddressBlocks> blocks_1 = std::make_unique<IPAddressBlocks>();
1✔
420
   blocks_1->add_address<IPv4>({10, 0, 32, 0}, {10, 0, 47, 255}, 1);
1✔
421
   blocks_1->add_address<IPv4>({10, 0, 64, 0}, {10, 0, 64, 255}, 1);
1✔
422
   blocks_1->add_address<IPv4>({10, 1, 0, 0}, {10, 1, 255, 255}, 1);
1✔
423
   blocks_1->add_address<IPv4>({10, 2, 48, 0}, {10, 2, 63, 255}, 1);
1✔
424
   blocks_1->add_address<IPv4>({10, 2, 64, 0}, {10, 2, 64, 255}, 1);
1✔
425
   blocks_1->add_address<IPv4>({10, 3, 0, 0}, {10, 3, 255, 255}, 1);
1✔
426
   blocks_1->inherit<IPv6>();
1✔
427

428
   Botan::X509_Cert_Options opts_1 = ca_opts();
1✔
429
   opts_1.extensions.add(std::move(blocks_1));
1✔
430

431
   auto cert_1 = make_self_signed(rng, opts_1);
1✔
432

433
   auto bits_1 = cert_1.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
434

435
   result.test_bin_eq(
1✔
436
      "extension is encoded as specified",
437
      bits_1,
438
      "3035302B040300010130240304040A00200304000A00400303000A01300C0304040A02300304000A02400303000A033006040200020500");
439

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

442
   auto ext_1_addr_fam_1 = ext_1->addr_blocks()[0];
1✔
443
   result.test_opt_u8_eq("extension 1 ipv4 safi", ext_1_addr_fam_1.safi(), 1);
1✔
444
   auto ext_1_ranges =
1✔
445
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_1_addr_fam_1.addr_choice()).ranges().value();
1✔
446
   result.test_bin_eq("extension 1 range 1 min", ext_1_ranges[0].min().value(), {10, 0, 32, 0});
1✔
447
   result.test_bin_eq("extension 1 range 1 max", ext_1_ranges[0].max().value(), {10, 0, 47, 255});
1✔
448

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

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

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

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

461
   result.test_opt_u8_eq("extension 1 ipv6 safi", ext_1->addr_blocks()[1].safi(), std::nullopt);
1✔
462
   result.test_is_true(
1✔
463
      "extension 1 ipv6 inherited",
464
      !std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_1->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
465

466
   // https://datatracker.ietf.org/doc/html/rfc3779#page-20
467
   std::unique_ptr<IPAddressBlocks> blocks_2 = std::make_unique<IPAddressBlocks>();
1✔
468
   blocks_2->add_address<IPv6>(
1✔
469
      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
470
      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
471
   blocks_2->add_address<IPv4>({10, 0, 0, 0}, {10, 255, 255, 255}, 1);
1✔
472
   blocks_2->add_address<IPv4>({172, 16, 0, 0}, {172, 31, 255, 255}, 1);
1✔
473
   blocks_2->inherit<IPv4>(2);
1✔
474

475
   Botan::X509_Cert_Options opts_2 = ca_opts();
1✔
476
   opts_2.extensions.add(std::move(blocks_2));
1✔
477

478
   auto cert_2 = make_self_signed(rng, opts_2);
1✔
479

480
   auto bits_2 = cert_2.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
481

482
   // see https://www.rfc-editor.org/errata/eid6792 as to why the B0 specified in the RFC is a AC here
483
   result.test_bin_eq("extension is encoded as specified",
1✔
484
                      bits_2,
485
                      "302C3010040300010130090302000A030304AC10300704030001020500300F040200023009030700200100000002");
486

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

489
   auto ext_2_addr_fam_1 = ext_2->addr_blocks()[0];
1✔
490
   result.test_opt_u8_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_1.safi(), 1);
1✔
491
   auto ext_2_ranges_1 =
1✔
492
      std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2_addr_fam_1.addr_choice()).ranges().value();
1✔
493
   result.test_bin_eq("extension 2 fam 1 range 1 min", ext_2_ranges_1[0].min().value(), {10, 0, 0, 0});
1✔
494
   result.test_bin_eq("extension 2 fam 1 range 1 max", ext_2_ranges_1[0].max().value(), {10, 255, 255, 255});
1✔
495

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

499
   result.test_opt_u8_eq("extension 2 ipv4 2 safi", ext_2->addr_blocks()[1].safi(), 2);
1✔
500
   result.test_is_true(
1✔
501
      "extension 2 ipv4 2 inherited",
502
      !std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2->addr_blocks()[1].addr_choice()).ranges().has_value());
1✔
503

504
   auto ext_2_addr_fam_3 = ext_2->addr_blocks()[2];
1✔
505
   result.test_opt_u8_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_3.safi(), std::nullopt);
1✔
506
   auto ext_2_ranges_3 =
1✔
507
      std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_2_addr_fam_3.addr_choice()).ranges().value();
1✔
508
   result.test_bin_eq("extension 2 fam 3 range 1 min",
1✔
509
                      ext_2_ranges_3[0].min().value(),
1✔
510
                      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
511
   result.test_bin_eq("extension 2 fam 3 range 1 max",
1✔
512
                      ext_2_ranges_3[0].max().value(),
1✔
513
                      {0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
514

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

519
Test::Result test_x509_ip_addr_blocks_encode_builder() {
1✔
520
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
521
   result.start_timer();
1✔
522

523
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
524
   auto rng = Test::new_rng(__func__);
1✔
525

526
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
1✔
527

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

531
   blocks->add_address<IPv4>({255, 255, 255, 255});
1✔
532
   // encoded as prefix
533
   blocks->add_address<IPv4>({190, 5, 0, 0}, {190, 5, 0b01111111, 255});
1✔
534
   // encoded as min, max
535
   blocks->add_address<IPv4>({127, 0, 0, 1}, {189, 5, 7, 255});
1✔
536

537
   // full address range
538
   blocks->add_address<IPv4>({0, 0, 0, 0}, {255, 255, 255, 255}, 1);
1✔
539

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

542
   Botan::X509_Cert_Options opts = ca_opts();
1✔
543
   opts.extensions.add(std::move(blocks));
1✔
544

545
   auto cert = make_self_signed(rng, opts);
1✔
546
   auto bits = cert.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
1✔
547

548
   // hand validated with https://lapo.it/asn1js/
549
   result.test_bin_eq(
1✔
550
      "extension is encoded as specified",
551
      bits,
552
      "304630290402000130230305007B7B0201300D0305007F000001030403BD0500030407BE0500030500FFFFFFFF300A04030001013003030100300D04030001023006030406C0A840");
553

554
   result.end_timer();
1✔
555
   return result;
1✔
556
}
2✔
557

558
namespace {
559

560
template <size_t I>
561
bool bit_set(size_t v) {
648✔
562
   if(((v >> I) & 1) == 1) {
648✔
563
      return true;
564
   } else {
565
      return false;
324✔
566
   }
567
}
568

569
}  // namespace
570

571
Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() {
1✔
572
   Test::Result result("X509 IP Address Block encode (ctor)");
1✔
573
   result.start_timer();
1✔
574

575
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
576

577
   auto rng = Test::new_rng(__func__);
1✔
578

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

581
   for(size_t i = 0; i < 64; i++) {
65✔
582
      const bool push_ipv4_ranges = bit_set<0>(i);
64✔
583
      const bool push_ipv6_ranges = bit_set<1>(i);
64✔
584
      const bool inherit_ipv4 = bit_set<2>(i);
64✔
585
      const bool inherit_ipv6 = bit_set<3>(i);
64✔
586
      const bool push_ipv4_family = bit_set<4>(i);
64✔
587
      const bool push_ipv6_family = bit_set<5>(i);
64✔
588

589
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
64✔
590

591
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
592
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
593
      a = {255, 255, 255, 255};
64✔
594
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
595

596
      // encoded as min, max
597
      a = {127, 0, 0, 1};
64✔
598
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
599
      a = {189, 5, 7, 255};
64✔
600
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
601

602
      // encoded as prefix
603
      a = {190, 5, 0, 0};
64✔
604
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
605
      a = {190, 5, 127, 255};
64✔
606
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
607

608
      a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
609
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
610
      a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
611
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
612

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

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

620
      // encoded as prefix
621
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
622
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
623
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
624
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
625

626
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
64✔
627
      auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
628
      auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
629
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
64✔
630

631
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
64✔
632
      auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
633
      auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
634
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
64✔
635

636
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
64✔
637
      if(push_ipv4_ranges) {
64✔
638
         ipv4_ranges.push_back(ipv4_range_1);
32✔
639
         ipv4_ranges.push_back(ipv4_range_2);
32✔
640
         ipv4_ranges.push_back(ipv4_range_3);
32✔
641
         ipv4_ranges.push_back(ipv4_range_4);
32✔
642
      }
643

644
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
64✔
645
      if(push_ipv6_ranges) {
64✔
646
         ipv6_ranges.push_back(ipv6_range_1);
32✔
647
         ipv6_ranges.push_back(ipv6_range_2);
32✔
648
         ipv6_ranges.push_back(ipv6_range_3);
32✔
649
         ipv6_ranges.push_back(ipv6_range_4);
32✔
650
      }
651

652
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
64✔
653
      if(!inherit_ipv4) {
64✔
654
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
96✔
655
      }
656

657
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
64✔
658
      if(!inherit_ipv6) {
64✔
659
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
96✔
660
      }
661

662
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
663
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
664

665
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
666
      if(push_ipv4_family) {
64✔
667
         addr_blocks.push_back(ipv4_addr_family);
32✔
668
      }
669
      if(push_ipv6_family) {
64✔
670
         addr_blocks.push_back(ipv6_addr_family);
32✔
671
      }
672

673
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
674

675
      opts.extensions.add(std::move(blocks));
64✔
676

677
      const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
64✔
678
      const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
679
      {
64✔
680
         const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
64✔
681
         result.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
64✔
682

683
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
684
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
685
            result.test_is_true("no address family entries", dec_addr_blocks.empty());
16✔
686
            continue;
16✔
687
         }
688

689
         if(push_ipv4_family) {
48✔
690
            auto family = dec_addr_blocks[0];
32✔
691
            result.test_u16_eq("ipv4 family afi", ipv4_addr_family.afi(), family.afi());
32✔
692
            result.test_opt_u8_eq("ipv4 family safi", ipv4_addr_family.safi(), family.safi());
32✔
693
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
32✔
694

695
            if(!inherit_ipv4) {
32✔
696
               auto ranges = choice.ranges().value();
16✔
697
               if(push_ipv4_ranges) {
16✔
698
                  result.test_bin_eq("ipv4 entry 0 min", ranges[0].min().value(), ipv4_range_1.min().value());
8✔
699
                  result.test_bin_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
8✔
700
                  result.test_bin_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
8✔
701
                  result.test_bin_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
8✔
702
                  result.test_bin_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
8✔
703
                  result.test_bin_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
8✔
704
                  result.test_bin_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
8✔
705
                  result.test_bin_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
8✔
706
               } else {
707
                  result.test_is_true("ipv4 range has no entries", ranges.empty());
8✔
708
               }
709
            } else {
16✔
710
               result.test_is_false("ipv4 family inherit", choice.ranges().has_value());
16✔
711
            }
712
         }
64✔
713

714
         if(push_ipv6_family) {
48✔
715
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
716
            result.test_u16_eq("ipv6 family afi", ipv6_addr_family.afi(), family.afi());
32✔
717
            result.test_opt_u8_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
32✔
718
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
32✔
719
            if(!inherit_ipv6) {
32✔
720
               auto ranges = choice.ranges().value();
16✔
721
               if(push_ipv6_ranges) {
16✔
722
                  result.test_bin_eq("ipv6 entry 0 min", ranges[0].min().value(), ipv6_range_1.min().value());
8✔
723
                  result.test_bin_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
8✔
724
                  result.test_bin_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
8✔
725
                  result.test_bin_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
8✔
726
                  result.test_bin_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
8✔
727
                  result.test_bin_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
8✔
728
                  result.test_bin_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
8✔
729
                  result.test_bin_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
8✔
730
               } else {
731
                  result.test_is_true("ipv6 range has no entries", ranges.empty());
8✔
732
               }
733
            } else {
16✔
734
               result.test_is_false("ipv6 family inherit", choice.ranges().has_value());
16✔
735
            }
736
         }
64✔
737
      }
738
   }
256✔
739

740
   result.end_timer();
1✔
741
   return result;
2✔
742
}
2✔
743

744
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() {
1✔
745
   Test::Result result("X509 IP Address Block encode edge cases (ctor)");
1✔
746
   result.start_timer();
1✔
747

748
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
749

750
   auto rng = Test::new_rng(__func__);
1✔
751

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

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

758
   for(size_t i = 0; i < edge_values.size(); i++) {
22✔
759
      for(size_t j = 0; j < 4; j++) {
105✔
760
         const bool modify_min = bit_set<0>(j);
84✔
761
         const bool modify_max = bit_set<1>(j);
84✔
762

763
         for(size_t k = 0; k < 18; k++) {
1,219✔
764
            if(!modify_min && !modify_max && (k > 0 || i > 0)) {
1,156✔
765
               // we don't modify anything, this is the extreme edge case of 0.0 ... - 255.255. ...
766
               // so we only need to do this once
767
               break;
768
            }
769

770
            Botan::X509_Cert_Options opts = req_opts(sig_algo);
1,135✔
771

772
            std::vector<uint8_t> min_bytes(16, 0x00);
1,135✔
773
            std::vector<uint8_t> max_bytes(16, 0xFF);
1,135✔
774

775
            if(modify_min) {
1,135✔
776
               min_bytes[15 - (k < 2 ? 0 : k - 2)] = edge_values[i];
756✔
777
            }
778
            if(modify_max) {
1,135✔
779
               max_bytes[15 - (k > 15 ? 15 : k)] = edge_values[i];
756✔
780
            }
781

782
            auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
1,135✔
783
            auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
1,135✔
784

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

787
            std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
1,135✔
788
            ipv6_ranges.push_back(ipv6_range);
1,135✔
789

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

792
            auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3,405✔
793

794
            std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1,135✔
795
            addr_blocks.push_back(ipv6_addr_family);
1,135✔
796

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

799
            opts.extensions.add(std::move(blocks));
1,135✔
800

801
            const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1,135✔
802
            const Botan::X509_Certificate cert =
1,135✔
803
               ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1,135✔
804
            {
1,135✔
805
               const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1,135✔
806
               result.test_not_null("cert has IPAddrBlocks extension", ip_blocks);
1,135✔
807
               const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1,135✔
808
               auto family = dec_addr_blocks[0];
1,135✔
809
               result.test_u16_eq("ipv6 family afi", ipv6_addr_family.afi(), family.afi());
1,135✔
810
               result.test_opt_u8_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
1,135✔
811
               auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
1,135✔
812
               auto ranges = choice.ranges().value();
1,135✔
813

814
               result.test_bin_eq("ipv6 edge case min", ranges[0].min().value(), ipv6_range.min().value());
1,135✔
815
               result.test_bin_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
1,135✔
816
            }
2,270✔
817
         }
5,675✔
818
      }
819
   }
820
   result.end_timer();
1✔
821
   return result;
2✔
822
}
3✔
823

824
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
825
   Test::Result result("X509 IP Address Block range merge");
1✔
826
   result.start_timer();
1✔
827

828
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
829

830
   auto rng = Test::new_rng(__func__);
1✔
831

832
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
833
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
834

835
   const std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
836
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
837
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
838
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
839
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
840
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
841
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
842
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
843
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
844
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
845
   };
46✔
846

847
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv6_ranges;
1✔
848
   for(auto pair : addresses) {
10✔
849
      auto address_min = IPAddressBlocks::IPAddress<IPv4>(pair[0]);
9✔
850
      auto address_max = IPAddressBlocks::IPAddress<IPv4>(pair[1]);
9✔
851
      auto range = IPAddressBlocks::IPAddressOrRange<IPv4>(address_min, address_max);
9✔
852
      ipv6_ranges.push_back(range);
9✔
853
   }
9✔
854

855
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
1✔
856
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
857

858
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
859
   addr_blocks.push_back(ipv6_addr_family);
1✔
860

861
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
862

863
   opts.extensions.add(std::move(blocks));
1✔
864

865
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
866
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
867
   {
1✔
868
      const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
869
      result.test_not_null("cert has IPAddrBlocks extension", ip_blocks);
1✔
870
      const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1✔
871
      auto family = dec_addr_blocks[0];
1✔
872
      auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
1✔
873
      auto ranges = choice.ranges().value();
1✔
874

875
      const std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
876
      const std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
877

878
      result.test_bin_eq("range expected min", ranges[0].min().value(), expected_min);
1✔
879
      result.test_bin_eq("range expected max", ranges[0].max().value(), expected_max);
1✔
880
      result.test_sz_eq("range length", ranges.size(), 1);
1✔
881
   }
2✔
882

883
   result.end_timer();
1✔
884
   return result;
2✔
885
}
24✔
886

887
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
888
   Test::Result result("X509 IP Address Block family merge");
1✔
889
   result.start_timer();
1✔
890

891
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
892

893
   auto rng = Test::new_rng(__func__);
1✔
894

895
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
896
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
897

898
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
899

900
   IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
1✔
901
   IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
1✔
902

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

907
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> v4_choice_vec{
1✔
908
      IPAddressBlocks::IPAddressOrRange<IPv4>(IPAddressBlocks::IPAddress<IPv4>({v4_addr_1}))};
1✔
909
   IPAddressBlocks::IPAddressChoice<IPv4> v4_choice_dupl(v4_choice_vec);
1✔
910
   result.test_sz_eq(
1✔
911
      "IPAddressChoice v4 merges ranges already in constructor", v4_choice_dupl.ranges().value().size(), 1);
1✔
912
   const IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
913

914
   const uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
915
   const IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
1✔
916

917
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> v6_choice_vec{
1✔
918
      IPAddressBlocks::IPAddressOrRange<IPv6>(IPAddressBlocks::IPAddress<IPv6>({v6_addr_1}))};
1✔
919
   IPAddressBlocks::IPAddressChoice<IPv6> v6_choice_dupl(v6_choice_vec);
1✔
920
   result.test_sz_eq("IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size(), 1);
1✔
921
   const IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
922

923
   const IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
924
   const IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
925

926
   const IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
927
   const IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
928

929
   /*
930
   considering the push order, the resulting order should be
931
   [0] v4 no safi
932
   [2] v4 safi
933
   [1] v6 no safi
934
   [3] v6 safi
935
   */
936
   for(size_t i = 0; i < 3; i++) {
4✔
937
      addr_blocks.push_back(v4_empty_fam_safi);
3✔
938
      addr_blocks.push_back(v6_empty_fam);
3✔
939
      addr_blocks.push_back(v4_fam_dupl);
3✔
940
      addr_blocks.push_back(v6_empty_fam_safi);
3✔
941
      addr_blocks.push_back(v6_fam_dupl);
3✔
942
      addr_blocks.push_back(v4_empty_fam);
3✔
943
   }
944

945
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
946
      v4_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_empty_fam, v6_fam_dupl, v6_empty_fam_safi};
7✔
947

948
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
949

950
   opts.extensions.add(std::move(blocks));
1✔
951

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

955
   const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
956
   result.test_not_null("cert has IPAddrBlocks extension", ip_blocks);
1✔
957
   const auto& dec_blocks = ip_blocks->addr_blocks();
1✔
958

959
   result.test_sz_eq("blocks got merged lengthwise", dec_blocks.size(), expected_blocks.size());
1✔
960

961
   bool sorted = true;
1✔
962
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
963
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
964
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
965

966
      uint32_t afam_a = a.afi();
5✔
967
      if(a.safi().has_value()) {
5✔
968
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
969
      } else {
970
         afam_a = static_cast<uint32_t>(afam_a << 8);
2✔
971
      }
972

973
      uint32_t afam_b = b.afi();
5✔
974
      if(b.safi().has_value()) {
5✔
975
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
976
      } else {
977
         afam_b = static_cast<uint32_t>(afam_b << 8);
1✔
978
      }
979

980
      if(afam_a > afam_b) {
5✔
981
         sorted = false;
982
         break;
983
      }
984
   }
985

986
   result.test_is_true("blocks got sorted", sorted);
1✔
987

988
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
989
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
990
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
991

992
      result.test_u16_eq("blocks match push order by afi at index " + std::to_string(i), dec.afi(), exp.afi());
18✔
993
      result.test_opt_u8_eq("blocks match push order by safi at index " + std::to_string(i), dec.safi(), exp.safi());
18✔
994

995
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
996
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
3✔
997
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
3✔
998

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

1006
            if(dec_choice.ranges().has_value() == false) {
1✔
1007
               continue;
×
1008
            }
1009

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

1016
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
1017
               continue;
×
1018
            }
1019

1020
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
1021
               result.test_bin_eq(
2✔
1022
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1023
                  exp_ranges[j].min().value(),
1✔
1024
                  dec_ranges[j].min().value());
1✔
1025
               result.test_bin_eq(
2✔
1026
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1027
                  exp_ranges[j].max().value(),
1✔
1028
                  dec_ranges[j].max().value());
2✔
1029
            }
1030
         }
1✔
1031
      } else if((exp.afi() == 2) && (dec.afi() == 2)) {
7✔
1032
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(dec.addr_choice());
3✔
1033
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(exp.addr_choice());
3✔
1034

1035
         if(!exp_choice.ranges().has_value()) {
3✔
1036
            result.test_is_false("block ranges should inherit at index " + std::to_string(i),
4✔
1037
                                 dec_choice.ranges().has_value());
2✔
1038
         } else {
1039
            result.test_is_true("block ranges should not inherit at index " + std::to_string(i),
2✔
1040
                                dec_choice.ranges().has_value());
1✔
1041

1042
            if(dec_choice.ranges().has_value() == false) {
1✔
1043
               continue;
×
1044
            }
1045

1046
            auto dec_ranges = dec_choice.ranges().value();
1✔
1047
            auto exp_ranges = exp_choice.ranges().value();
1✔
1048
            result.test_sz_eq("block ranges got merged lengthwise at index " + std::to_string(i),
3✔
1049
                              dec_ranges.size(),
1050
                              exp_ranges.size());
1051

1052
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
1053
               continue;
×
1054
            }
1055

1056
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
1057
               result.test_bin_eq(
2✔
1058
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1059
                  exp_ranges[j].min().value(),
1✔
1060
                  dec_ranges[j].min().value());
1✔
1061
               result.test_bin_eq(
2✔
1062
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
1063
                  exp_ranges[j].max().value(),
1✔
1064
                  dec_ranges[j].max().value());
2✔
1065
            }
1066
         }
1✔
1067
      }
4✔
1068
   }
1069

1070
   result.end_timer();
1✔
1071
   return result;
2✔
1072
}
19✔
1073

1074
Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() {
1✔
1075
   Test::Result result("X509 IP Address Blocks path validation success (builder)");
1✔
1076
   result.start_timer();
1✔
1077

1078
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1079
   auto rng = Test::new_rng(__func__);
1✔
1080

1081
   /*
1082
   Creates a certificate chain of length 4.
1083
   Root: ipv4 and ipv6
1084
   Inherit: has both values as 'inherit'
1085
   Dynamic: has either both 'inherit', both with values, or just one with a value
1086
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1087
   */
1088

1089
   // Root cert
1090
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
1✔
1091

1092
   root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
1✔
1093
   root_blocks->add_address<IPv4>({10, 0, 0, 1}, {10, 255, 255, 255}, 42);
1✔
1094

1095
   root_blocks->add_address<IPv6>(
1✔
1096
      {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1097
      {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1098
   root_blocks->add_address<IPv6>(
1✔
1099
      {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1100
      {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
1101

1102
   // Inherit cert
1103
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>();
1✔
1104

1105
   inherit_blocks->inherit<IPv4>(42);
1✔
1106
   inherit_blocks->inherit<IPv6>();
1✔
1107

1108
   // Subject cert
1109
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
1✔
1110

1111
   sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, 42);
1✔
1112
   sub_blocks->add_address<IPv4>({10, 0, 2, 1}, {10, 42, 0, 255}, 42);
1✔
1113

1114
   sub_blocks->add_address<IPv6>(
1✔
1115
      {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1116
      {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1117

1118
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1119
   root_opts.extensions.add(std::move(root_blocks));
1✔
1120
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1121
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1122
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1123
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1124

1125
   Botan::Certificate_Store_In_Memory trusted;
1✔
1126
   trusted.add_certificate(root_cert);
1✔
1127

1128
   for(size_t i = 0; i < 4; i++) {
5✔
1129
      const bool include_v4 = bit_set<0>(i);
4✔
1130
      const bool include_v6 = bit_set<1>(i);
4✔
1131

1132
      // Dynamic Cert
1133
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>();
4✔
1134
      if(include_v4) {
4✔
1135
         dyn_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
2✔
1136
         dyn_blocks->add_address<IPv4>({10, 0, 0, 255}, {10, 255, 0, 1}, 42);
2✔
1137
      } else {
1138
         dyn_blocks->inherit<IPv4>(42);
2✔
1139
      }
1140

1141
      if(include_v6) {
4✔
1142
         dyn_blocks->add_address<IPv6>(
2✔
1143
            {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
1144
            {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
1145
      } else {
1146
         dyn_blocks->inherit<IPv6>();
2✔
1147
      }
1148

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

1151
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1152
      const Botan::X509_Certificate sub_cert =
4✔
1153
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1154

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

1158
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1159
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1160
   }
8✔
1161

1162
   result.end_timer();
1✔
1163
   return result;
2✔
1164
}
7✔
1165

1166
Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() {
1✔
1167
   Test::Result result("X509 IP Address Block path validation success (ctor)");
1✔
1168
   result.start_timer();
1✔
1169

1170
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1171
   auto rng = Test::new_rng(__func__);
1✔
1172

1173
   /*
1174
   Creates a certificate chain of length 4.
1175
   Root: ipv4 and ipv6
1176
   Inherit: has both values as 'inherit'
1177
   Dynamic: has either both 'inherit', both with values, or just one with a value
1178
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
1179
   */
1180

1181
   // Root cert
1182
   std::vector<uint8_t> a = {120, 0, 0, 1};
1✔
1183
   auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1184
   a = {130, 140, 150, 160};
1✔
1185
   auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
1186

1187
   a = {10, 0, 0, 1};
1✔
1188
   auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1189
   a = {10, 255, 255, 255};
1✔
1190
   auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1191

1192
   a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1193
   auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1194
   a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1195
   auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1196

1197
   a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1198
   auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1199
   a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1200
   auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1201

1202
   auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
1✔
1203
   auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
1✔
1204
   auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
1✔
1205
   auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
1✔
1206

1207
   auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
1✔
1208
   auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
1✔
1209

1210
   auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
1✔
1211
   auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
1✔
1212

1213
   auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
3✔
1214
   auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
3✔
1215

1216
   auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
6✔
1217
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
1✔
1218

1219
   // Inherit cert
1220
   auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
1✔
1221
   auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
1✔
1222

1223
   auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
2✔
1224
   auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
2✔
1225

1226
   auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
6✔
1227
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
1✔
1228

1229
   // Dynamic Cert
1230
   a = {122, 0, 0, 255};
1✔
1231
   auto dyn_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1232
   a = {128, 255, 255, 255};
1✔
1233
   auto dyn_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1234
   a = {10, 0, 0, 255};
1✔
1235
   auto dyn_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1236
   a = {10, 255, 0, 1};
1✔
1237
   auto dyn_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1238

1239
   a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
1240
   auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1241
   a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1242
   auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1243

1244
   auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
1✔
1245
   auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
1✔
1246
   auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
1✔
1247

1248
   auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
1✔
1249
   auto dyn_ipv6_ranges = {dyn_ipv6_range};
1✔
1250

1251
   // Subject cert
1252
   a = {124, 0, 255, 0};
1✔
1253
   auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1254
   a = {126, 0, 0, 1};
1✔
1255
   auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1256

1257
   a = {10, 0, 2, 1};
1✔
1258
   auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1259
   a = {10, 42, 0, 255};
1✔
1260
   auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
1261

1262
   a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1263
   auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1264
   a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
1265
   auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
1266

1267
   auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
1✔
1268
   auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
1✔
1269
   auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
1✔
1270

1271
   auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
1✔
1272
   auto sub_ipv6_ranges = {sub_ipv6_range};
1✔
1273

1274
   auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
1✔
1275
   auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
1✔
1276

1277
   auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
3✔
1278
   auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
3✔
1279

1280
   auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
6✔
1281
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
1✔
1282

1283
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1284
   root_opts.extensions.add(std::move(root_blocks));
1✔
1285
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1286
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1287
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1288
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1289

1290
   Botan::Certificate_Store_In_Memory trusted;
1✔
1291
   trusted.add_certificate(root_cert);
1✔
1292

1293
   for(size_t i = 0; i < 4; i++) {
5✔
1294
      const bool include_v4 = bit_set<0>(i);
4✔
1295
      const bool include_v6 = bit_set<1>(i);
4✔
1296

1297
      auto dyn_ipv4_choice =
4✔
1298
         IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
8✔
1299
      auto dyn_ipv6_choice =
4✔
1300
         IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
8✔
1301

1302
      auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
10✔
1303
      auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
10✔
1304

1305
      auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
24✔
1306
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
4✔
1307

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

1310
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1311
      const Botan::X509_Certificate sub_cert =
4✔
1312
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1313

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

1317
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1318
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1319
   }
28✔
1320

1321
   result.end_timer();
1✔
1322
   return result;
2✔
1323
}
24✔
1324

1325
Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() {
1✔
1326
   Test::Result result("X509 IP Address Blocks path validation failure (builder)");
1✔
1327
   result.start_timer();
1✔
1328

1329
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1330
   auto rng = Test::new_rng(__func__);
1✔
1331

1332
   for(size_t i = 0; i < 7; i++) {
8✔
1333
      const bool all_inherit = (i == 0);
7✔
1334
      const bool different_safi = (i == 1);
7✔
1335
      const bool too_small_subrange = (i == 2);
7✔
1336
      const bool too_large_subrange = (i == 3);
7✔
1337
      const bool no_more_issuer_ranges = (i == 4);
7✔
1338
      const bool empty_issuer_ranges = (i == 5);
7✔
1339
      const bool nullptr_extensions = (i == 6);
7✔
1340

1341
      // Root cert
1342
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
7✔
1343
      if(!all_inherit) {
7✔
1344
         root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
6✔
1345
      } else {
1346
         root_blocks->inherit<IPv4>(42);
1✔
1347
      }
1348

1349
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1350
      if(!nullptr_extensions) {
7✔
1351
         root_opts.extensions.add(std::move(root_blocks));
12✔
1352
      }
1353
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1354

1355
      // Issuer Cert
1356
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>();
7✔
1357
      if(!all_inherit) {
7✔
1358
         if(empty_issuer_ranges) {
6✔
1359
            iss_blocks->restrict<IPv4>(42);
1✔
1360
         } else {
1361
            iss_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
5✔
1362
         }
1363
      } else {
1364
         iss_blocks->inherit<IPv4>(42);
1✔
1365
      }
1366

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

1369
      // Subject cert
1370
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
7✔
1371

1372
      uint8_t safi = different_safi ? 41 : 42;
7✔
1373

1374
      if(!all_inherit) {
7✔
1375
         if(too_small_subrange) {
6✔
1376
            sub_blocks->add_address<IPv4>({118, 0, 255, 0}, {126, 0, 0, 1}, safi);
1✔
1377
         } else if(too_large_subrange) {
5✔
1378
            sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {134, 0, 0, 1}, safi);
1✔
1379
         } else if(no_more_issuer_ranges) {
4✔
1380
            sub_blocks->add_address<IPv4>({140, 0, 0, 1}, {150, 0, 0, 1}, safi);
1✔
1381
         } else {
1382
            sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, safi);
3✔
1383
         }
1384
      } else {
1385
         sub_blocks->inherit<IPv4>(safi);
1✔
1386
      }
1387

1388
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1389
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1390

1391
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1392
      const Botan::X509_Certificate sub_cert =
7✔
1393
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1394

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

1398
      Botan::Certificate_Store_In_Memory trusted;
7✔
1399
      trusted.add_certificate(root_cert);
7✔
1400

1401
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1402
      result.require("path validation fails", !path_result.successful_validation());
7✔
1403
   }
15✔
1404

1405
   result.end_timer();
1✔
1406
   return result;
1✔
1407
}
8✔
1408

1409
Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() {
1✔
1410
   Test::Result result("X509 IP Address Block path validation failure (ctor)");
1✔
1411
   result.start_timer();
1✔
1412

1413
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
1414
   auto rng = Test::new_rng(__func__);
1✔
1415

1416
   for(size_t i = 0; i < 7; i++) {
8✔
1417
      const bool all_inherit = (i == 0);
7✔
1418
      const bool different_safi = (i == 1);
7✔
1419
      const bool too_small_subrange = (i == 2);
7✔
1420
      const bool too_large_subrange = (i == 3);
7✔
1421
      const bool no_more_issuer_ranges = (i == 4);
7✔
1422
      const bool empty_issuer_ranges = (i == 5);
7✔
1423
      const bool nullptr_extensions = (i == 6);
7✔
1424

1425
      // Root cert
1426
      std::vector<uint8_t> a = {120, 0, 0, 1};
7✔
1427
      auto root_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1428
      a = {130, 140, 150, 160};
7✔
1429
      auto root_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
1430

1431
      auto root_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_range_1_min, root_range_1_max);
7✔
1432
      auto root_ranges = {root_range_1};
7✔
1433
      auto root_choice =
7✔
1434
         IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(root_ranges));
14✔
1435
      auto root_family = IPAddressBlocks::IPAddressFamily(root_choice, 42);
20✔
1436
      auto root_addr_blocks = {root_family};
21✔
1437
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
7✔
1438

1439
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
1440
      if(!nullptr_extensions) {
7✔
1441
         root_opts.extensions.add(std::move(root_blocks));
12✔
1442
      }
1443
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
1444

1445
      // Issuer Cert
1446
      a = {122, 0, 0, 255};
7✔
1447
      auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1448
      a = {128, 255, 255, 255};
7✔
1449
      auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1450
      auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
7✔
1451

1452
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
7✔
1453

1454
      if(!empty_issuer_ranges) {
7✔
1455
         iss_ranges.push_back(iss_range_1);
6✔
1456
      }
1457

1458
      auto iss_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(iss_ranges));
19✔
1459
      auto iss_family = IPAddressBlocks::IPAddressFamily(iss_choice, 42);
20✔
1460
      auto iss_addr_blocks = {iss_family};
21✔
1461
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>(iss_addr_blocks);
7✔
1462
      auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
14✔
1463

1464
      // Subject cert
1465
      if(too_small_subrange) {
7✔
1466
         a = {118, 0, 255, 0};
2✔
1467
      } else if(no_more_issuer_ranges) {
6✔
1468
         a = {140, 0, 0, 1};
2✔
1469
      } else {
1470
         a = {124, 0, 255, 0};
10✔
1471
      }
1472

1473
      auto sub_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1474
      if(too_large_subrange) {
7✔
1475
         a = {134, 0, 0, 1};
2✔
1476
      } else if(no_more_issuer_ranges) {
6✔
1477
         a = {150, 0, 0, 1};
2✔
1478
      } else {
1479
         a = {126, 0, 0, 1};
10✔
1480
      }
1481
      auto sub_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1482

1483
      auto sub_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_range_1_min, sub_range_1_max);
7✔
1484
      auto sub_ranges = {sub_range_1};
7✔
1485
      auto sub_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(sub_ranges));
14✔
1486
      auto sub_family = IPAddressBlocks::IPAddressFamily(sub_choice, different_safi ? 41 : 42);
26✔
1487

1488
      auto sub_addr_blocks = {sub_family};
21✔
1489
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
7✔
1490

1491
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1492
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1493

1494
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1495
      const Botan::X509_Certificate sub_cert =
7✔
1496
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1497

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

1501
      Botan::Certificate_Store_In_Memory trusted;
7✔
1502
      trusted.add_certificate(root_cert);
7✔
1503

1504
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1505
      result.require("path validation fails", !path_result.successful_validation());
7✔
1506
   }
82✔
1507

1508
   result.end_timer();
1✔
1509
   return result;
1✔
1510
}
8✔
1511

1512
Test::Result test_x509_as_blocks_rfc3779_example() {
1✔
1513
   Test::Result result("X509 AS Blocks rfc3779 example");
1✔
1514
   result.start_timer();
1✔
1515

1516
   using Botan::Cert_Extension::ASBlocks;
1✔
1517
   auto rng = Test::new_rng(__func__);
1✔
1518

1519
   // construct like in https://datatracker.ietf.org/doc/html/rfc3779#page-21
1520
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1521
   blocks->add_asnum(135);
1✔
1522
   blocks->add_asnum(3000, 3999);
1✔
1523
   blocks->add_asnum(5001);
1✔
1524
   blocks->inherit_rdi();
1✔
1525

1526
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1527
   opts.extensions.add(std::move(blocks));
1✔
1528

1529
   auto cert = make_self_signed(rng, opts);
1✔
1530
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1531

1532
   result.test_bin_eq(
1✔
1533
      "extension is encoded as specified", bits, "301AA014301202020087300802020BB802020F9F02021389A1020500");
1534

1535
   auto as_idents = cert.v3_extensions().get_extension_object_as<ASBlocks>()->as_identifiers();
1✔
1536
   auto as_ids = as_idents.asnum().value().ranges().value();
1✔
1537

1538
   result.test_u32_eq("extension has correct data", as_ids[0].min(), 135);
1✔
1539

1540
   result.end_timer();
1✔
1541
   return result;
2✔
1542
}
3✔
1543

1544
Test::Result test_x509_as_blocks_encode_builder() {
1✔
1545
   Test::Result result("X509 IP Address Blocks encode (builder)");
1✔
1546
   result.start_timer();
1✔
1547

1548
   using Botan::Cert_Extension::ASBlocks;
1✔
1549
   auto rng = Test::new_rng(__func__);
1✔
1550

1551
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1552

1553
   blocks->add_rdi(10);
1✔
1554
   blocks->add_rdi(20, 30);
1✔
1555
   blocks->add_rdi(42, 300);
1✔
1556
   blocks->add_rdi(9, 301);
1✔
1557

1558
   blocks->inherit_asnum();
1✔
1559
   blocks->add_asnum(20);
1✔
1560
   // this overwrites the previous two
1561
   blocks->restrict_asnum();
1✔
1562

1563
   Botan::X509_Cert_Options opts = ca_opts();
1✔
1564
   opts.extensions.add(std::move(blocks));
1✔
1565

1566
   auto cert = make_self_signed(rng, opts);
1✔
1567
   auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
1✔
1568

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

1571
   result.end_timer();
1✔
1572
   return result;
1✔
1573
}
2✔
1574

1575
Test::Result test_x509_as_blocks_extension_encode_ctor() {
1✔
1576
   Test::Result result("X509 AS Blocks encode (ctor)");
1✔
1577
   result.start_timer();
1✔
1578

1579
   using Botan::Cert_Extension::ASBlocks;
1✔
1580

1581
   auto rng = Test::new_rng(__func__);
1✔
1582

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

1585
   for(size_t i = 0; i < 16; i++) {
17✔
1586
      const bool push_asnum = bit_set<0>(i);
16✔
1587
      const bool push_rdi = bit_set<1>(i);
16✔
1588
      const bool include_asnum = bit_set<2>(i);
16✔
1589
      const bool include_rdi = bit_set<3>(i);
16✔
1590
      if(!include_asnum && !include_rdi) {
16✔
1591
         continue;
4✔
1592
      }
1593

1594
      const ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
12✔
1595
      const ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
12✔
1596
      const ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
12✔
1597

1598
      const ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
12✔
1599
      const ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
12✔
1600
      const ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
12✔
1601

1602
      std::vector<ASBlocks::ASIdOrRange> as_ranges;
12✔
1603
      if(push_asnum) {
12✔
1604
         as_ranges.push_back(asnum_id_or_range0);
6✔
1605
         as_ranges.push_back(asnum_id_or_range1);
6✔
1606
         as_ranges.push_back(asnum_id_or_range2);
6✔
1607
      }
1608

1609
      std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
12✔
1610
      if(push_rdi) {
12✔
1611
         rdi_ranges.push_back(rdi_id_or_range0);
6✔
1612
         rdi_ranges.push_back(rdi_id_or_range1);
6✔
1613
         rdi_ranges.push_back(rdi_id_or_range2);
6✔
1614
      }
1615

1616
      ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
12✔
1617
      ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
12✔
1618

1619
      const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
32✔
1620
                                                                    include_rdi ? std::optional(rdi) : std::nullopt);
32✔
1621

1622
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
12✔
1623

1624
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
12✔
1625
      opts.extensions.add(std::move(blocks));
12✔
1626

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

1630
      {
12✔
1631
         const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
12✔
1632
         result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
12✔
1633

1634
         const auto& identifier = as_blocks->as_identifiers();
12✔
1635

1636
         if(include_asnum) {
12✔
1637
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
1638

1639
            if(push_asnum) {
8✔
1640
               result.test_u32_eq("asnum entry 0 min", asnum_entries[0].min(), 0);
4✔
1641
               result.test_u32_eq("asnum entry 0 max", asnum_entries[0].max(), 999);
4✔
1642

1643
               result.test_u32_eq("asnum entry 1 min", asnum_entries[1].min(), 5042);
4✔
1644
               result.test_u32_eq("asnum entry 1 max", asnum_entries[1].max(), 4294967295);
4✔
1645
            } else {
1646
               result.test_is_true("asnum has no entries", asnum_entries.empty());
4✔
1647
            }
1648
         } else {
1649
            result.test_is_false("no asnum entry", identifier.asnum().has_value());
4✔
1650
         }
1651

1652
         if(include_rdi) {
12✔
1653
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
1654

1655
            if(push_rdi) {
8✔
1656
               result.test_u32_eq("rdi entry 0 min", rdi_entries[0].min(), 1234);
4✔
1657
               result.test_u32_eq("rdi entry 0 max", rdi_entries[0].max(), 5678);
4✔
1658

1659
               result.test_u32_eq("rdi entry 1 min", rdi_entries[1].min(), 32768);
4✔
1660
               result.test_u32_eq("rdi entry 1 max", rdi_entries[1].max(), 4294967295);
4✔
1661
            } else {
1662
               result.test_is_true("rdi has no entries", rdi_entries.empty());
4✔
1663
            }
1664
         } else {
1665
            result.test_is_false("rdi has no entry", identifier.rdi().has_value());
4✔
1666
         }
1667
      }
1668
   }
36✔
1669

1670
   result.end_timer();
1✔
1671
   return result;
2✔
1672
}
2✔
1673

1674
Test::Result test_x509_as_blocks_range_merge() {
1✔
1675
   Test::Result result("X509 AS Block range merge");
1✔
1676
   result.start_timer();
1✔
1677

1678
   using Botan::Cert_Extension::ASBlocks;
1✔
1679

1680
   auto rng = Test::new_rng(__func__);
1✔
1681

1682
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1683
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
1684

1685
   const std::vector<std::vector<uint16_t>> ranges = {
1✔
1686
      {2005, 37005},
1687
      {60, 70},
1688
      {22, 50},
1689
      {35, 2000},
1690
      {2001, 2004},
1691
      {21, 21},
1692
      {0, 20},
1693
   };
9✔
1694

1695
   std::vector<ASBlocks::ASIdOrRange> as_ranges;
1✔
1696
   as_ranges.reserve(ranges.size());
1✔
1697
   for(const auto& pair : ranges) {
8✔
1698
      as_ranges.emplace_back(pair[0], pair[1]);
7✔
1699
   }
1700

1701
   ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
1✔
1702

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

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

1707
   opts.extensions.add(std::move(blocks));
1✔
1708

1709
   const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
1710
   const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1711
   {
1✔
1712
      const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
1713
      result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
1✔
1714

1715
      const auto& identifier = as_blocks->as_identifiers();
1✔
1716

1717
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
1718

1719
      result.test_u32_eq("asnum entry 0 min", asnum_entries[0].min(), 0);
1✔
1720
      result.test_u32_eq("asnum entry 0 max", asnum_entries[0].max(), 37005);
1✔
1721
      result.test_sz_eq("asnum length", asnum_entries.size(), 1);
1✔
1722
   }
1723

1724
   result.end_timer();
1✔
1725
   return result;
2✔
1726
}
5✔
1727

1728
Test::Result test_x509_as_blocks_path_validation_success_builder() {
1✔
1729
   Test::Result result("X509 AS Block path validation success (builder)");
1✔
1730
   result.start_timer();
1✔
1731

1732
   using Botan::Cert_Extension::ASBlocks;
1✔
1733
   auto rng = Test::new_rng(__func__);
1✔
1734

1735
   /*
1736
   Creates a certificate chain of length 4.
1737
   Root: both asnum and rdi
1738
   Inherit: has both values as 'inherit'
1739
   Dynamic: has either both 'inherit', both with values, or just one with a value
1740
   Subject: both asnum and rdi as a subset of Root / Dynamic
1741
   */
1742

1743
   // Root Cert, both as and rdi
1744

1745
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
1✔
1746

1747
   root_blocks->add_asnum(0, 999);
1✔
1748
   root_blocks->add_asnum(5042);
1✔
1749
   root_blocks->add_asnum(5043, 4294967295);
1✔
1750

1751
   root_blocks->add_rdi(1234, 5678);
1✔
1752
   root_blocks->add_rdi(32768);
1✔
1753
   root_blocks->add_rdi(32769, 4294967295);
1✔
1754

1755
   // Inherit cert, both as 'inherit'
1756
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>();
1✔
1757
   inherit_blocks->inherit_asnum();
1✔
1758
   inherit_blocks->inherit_rdi();
1✔
1759

1760
   // Subject cert
1761

1762
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1763

1764
   sub_blocks->add_asnum(120, 180);
1✔
1765
   sub_blocks->add_asnum(220, 240);
1✔
1766
   sub_blocks->add_asnum(260, 511);
1✔
1767
   sub_blocks->add_asnum(678);
1✔
1768
   sub_blocks->add_asnum(5043, 5100);
1✔
1769

1770
   sub_blocks->add_rdi(1500, 2300);
1✔
1771
   sub_blocks->add_rdi(2500, 4000);
1✔
1772
   sub_blocks->add_rdi(1567);
1✔
1773
   sub_blocks->add_rdi(33100, 40000);
1✔
1774

1775
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1776
   root_opts.extensions.add(std::move(root_blocks));
1✔
1777
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1778
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1779
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1780
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1781

1782
   Botan::Certificate_Store_In_Memory trusted;
1✔
1783
   trusted.add_certificate(root_cert);
1✔
1784

1785
   for(size_t i = 0; i < 4; i++) {
5✔
1786
      const bool include_asnum = bit_set<0>(i);
4✔
1787
      const bool include_rdi = bit_set<1>(i);
4✔
1788

1789
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>();
4✔
1790
      if(include_asnum) {
4✔
1791
         dyn_blocks->add_asnum(100, 600);
2✔
1792
         dyn_blocks->add_asnum(678);
2✔
1793
         dyn_blocks->add_asnum(5042, 5101);
2✔
1794
      } else {
1795
         dyn_blocks->inherit_asnum();
2✔
1796
      }
1797

1798
      if(include_rdi) {
4✔
1799
         dyn_blocks->add_rdi(1500, 5000);
2✔
1800
         dyn_blocks->add_rdi(33000, 60000);
2✔
1801
      } else {
1802
         dyn_blocks->inherit_rdi();
2✔
1803
      }
1804

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

1807
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1808
      const Botan::X509_Certificate sub_cert =
4✔
1809
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1810

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

1814
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1815
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1816
   }
8✔
1817

1818
   result.end_timer();
1✔
1819
   return result;
2✔
1820
}
7✔
1821

1822
Test::Result test_x509_as_blocks_path_validation_success_ctor() {
1✔
1823
   Test::Result result("X509 AS Block path validation success (ctor)");
1✔
1824
   result.start_timer();
1✔
1825

1826
   using Botan::Cert_Extension::ASBlocks;
1✔
1827
   auto rng = Test::new_rng(__func__);
1✔
1828

1829
   /*
1830
   Creates a certificate chain of length 4.
1831
   Root: both asnum and rdi
1832
   Inherit: has both values as 'inherit'
1833
   Dynamic: has either both 'inherit', both with values, or just one with a value
1834
   Subject: both asnum and rdi as a subset of Root / Dynamic
1835
   */
1836

1837
   // Root Cert, both as and rdi
1838
   const ASBlocks::ASIdOrRange root_asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
1✔
1839
   const ASBlocks::ASIdOrRange root_asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
1✔
1840
   const ASBlocks::ASIdOrRange root_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
1✔
1841

1842
   const ASBlocks::ASIdOrRange root_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
1✔
1843
   const ASBlocks::ASIdOrRange root_rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
1✔
1844
   const ASBlocks::ASIdOrRange root_rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
1✔
1845

1846
   std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
1✔
1847
   root_as_ranges.push_back(root_asnum_id_or_range0);
1✔
1848
   root_as_ranges.push_back(root_asnum_id_or_range1);
1✔
1849
   root_as_ranges.push_back(root_asnum_id_or_range2);
1✔
1850

1851
   std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
1✔
1852
   root_rdi_ranges.push_back(root_rdi_id_or_range0);
1✔
1853
   root_rdi_ranges.push_back(root_rdi_id_or_range1);
1✔
1854
   root_rdi_ranges.push_back(root_rdi_id_or_range2);
1✔
1855

1856
   ASBlocks::ASIdentifierChoice root_asnum = ASBlocks::ASIdentifierChoice(root_as_ranges);
1✔
1857
   ASBlocks::ASIdentifierChoice root_rdi = ASBlocks::ASIdentifierChoice(root_rdi_ranges);
1✔
1858
   const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
4✔
1859
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
1✔
1860

1861
   // Inherit cert, both as 'inherit'
1862
   ASBlocks::ASIdentifierChoice inherit_asnum = ASBlocks::ASIdentifierChoice();
1✔
1863
   ASBlocks::ASIdentifierChoice inherit_rdi = ASBlocks::ASIdentifierChoice();
1✔
1864
   const ASBlocks::ASIdentifiers inherit_ident = ASBlocks::ASIdentifiers(inherit_asnum, inherit_rdi);
2✔
1865
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>(inherit_ident);
1✔
1866

1867
   // Dynamic cert
1868
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range0 = ASBlocks::ASIdOrRange(100, 600);
1✔
1869
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range1 = ASBlocks::ASIdOrRange(678);
1✔
1870
   const ASBlocks::ASIdOrRange dyn_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5042, 5101);
1✔
1871

1872
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 5000);
1✔
1873
   const ASBlocks::ASIdOrRange dyn_rdi_id_or_range1 = ASBlocks::ASIdOrRange(33000, 60000);
1✔
1874

1875
   std::vector<ASBlocks::ASIdOrRange> dyn_as_ranges;
1✔
1876
   dyn_as_ranges.push_back(dyn_asnum_id_or_range0);
1✔
1877
   dyn_as_ranges.push_back(dyn_asnum_id_or_range1);
1✔
1878
   dyn_as_ranges.push_back(dyn_asnum_id_or_range2);
1✔
1879

1880
   std::vector<ASBlocks::ASIdOrRange> dyn_rdi_ranges;
1✔
1881
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range0);
1✔
1882
   dyn_rdi_ranges.push_back(dyn_rdi_id_or_range1);
1✔
1883

1884
   // Subject cert
1885
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
1886
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
1887
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
1888
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
1889
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
1890

1891
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
1892
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
1893
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
1894
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
1895

1896
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
1897
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
1898
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
1899
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
1900
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
1901
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
1902

1903
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
1904
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
1905
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
1906
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
1907
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
1908

1909
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
1910
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
1911
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
1912
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
1913

1914
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1915
   root_opts.extensions.add(std::move(root_blocks));
1✔
1916
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1917
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1918
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1919
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1920

1921
   Botan::Certificate_Store_In_Memory trusted;
1✔
1922
   trusted.add_certificate(root_cert);
1✔
1923

1924
   for(size_t i = 0; i < 4; i++) {
5✔
1925
      const bool include_asnum = bit_set<0>(i);
4✔
1926
      const bool include_rdi = bit_set<1>(i);
4✔
1927

1928
      ASBlocks::ASIdentifierChoice dyn_asnum =
4✔
1929
         ASBlocks::ASIdentifierChoice(include_asnum ? std::optional(dyn_as_ranges) : std::nullopt);
6✔
1930
      ASBlocks::ASIdentifierChoice dyn_rdi =
4✔
1931
         ASBlocks::ASIdentifierChoice(include_rdi ? std::optional(dyn_rdi_ranges) : std::nullopt);
6✔
1932
      const ASBlocks::ASIdentifiers dyn_ident = ASBlocks::ASIdentifiers(dyn_asnum, dyn_rdi);
12✔
1933
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>(dyn_ident);
4✔
1934

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

1937
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
1938
      const Botan::X509_Certificate sub_cert =
4✔
1939
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
1940

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

1944
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1945
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1946
   }
12✔
1947

1948
   result.end_timer();
1✔
1949
   return result;
2✔
1950
}
11✔
1951

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

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

1959
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1960
   sub_blocks->add_asnum(120, 180);
1✔
1961
   sub_blocks->add_asnum(220, 224);
1✔
1962
   sub_blocks->add_asnum(260, 511);
1✔
1963
   sub_blocks->add_asnum(678);
1✔
1964
   sub_blocks->add_asnum(5043, 5100);
1✔
1965

1966
   sub_blocks->add_rdi(1500, 2300);
1✔
1967
   sub_blocks->add_rdi(2500, 4000);
1✔
1968
   sub_blocks->add_rdi(1567);
1✔
1969
   sub_blocks->add_rdi(33100, 40000);
1✔
1970

1971
   // create a root ca that does not have any extension
1972
   const Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1973
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1974
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1975
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1976
   const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
1977
   const Botan::X509_Certificate sub_cert =
1✔
1978
      root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1979

1980
   Botan::Certificate_Store_In_Memory trusted;
1✔
1981
   trusted.add_certificate(root_cert);
1✔
1982

1983
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1984
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1985

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

1989
   result.end_timer();
1✔
1990
   return result;
2✔
1991
}
3✔
1992

1993
Test::Result test_x509_as_blocks_path_validation_extension_not_present_ctor() {
1✔
1994
   Test::Result result("X509 AS Block path validation extension not present (ctor)");
1✔
1995
   result.start_timer();
1✔
1996

1997
   using Botan::Cert_Extension::ASBlocks;
1✔
1998
   auto rng = Test::new_rng(__func__);
1✔
1999

2000
   // Subject cert
2001
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
1✔
2002
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
1✔
2003
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
1✔
2004
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
1✔
2005
   const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
1✔
2006

2007
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
1✔
2008
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
1✔
2009
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
1✔
2010
   const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
1✔
2011

2012
   std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
1✔
2013
   sub_as_ranges.push_back(sub_asnum_id_or_range0);
1✔
2014
   sub_as_ranges.push_back(sub_asnum_id_or_range1);
1✔
2015
   sub_as_ranges.push_back(sub_asnum_id_or_range2);
1✔
2016
   sub_as_ranges.push_back(sub_asnum_id_or_range3);
1✔
2017
   sub_as_ranges.push_back(sub_asnum_id_or_range4);
1✔
2018

2019
   std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
1✔
2020
   sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
1✔
2021
   sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
1✔
2022
   sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
1✔
2023
   sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
1✔
2024

2025
   ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
1✔
2026
   ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
1✔
2027
   const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
4✔
2028
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
1✔
2029

2030
   // create a root ca that does not have any extension
2031
   const Botan::X509_Cert_Options root_opts = ca_opts();
1✔
2032
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
2033
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
2034
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
2035
   const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
2036
   const Botan::X509_Certificate sub_cert =
1✔
2037
      root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
2038

2039
   Botan::Certificate_Store_In_Memory trusted;
1✔
2040
   trusted.add_certificate(root_cert);
1✔
2041

2042
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
2043
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
2044

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

2048
   result.end_timer();
1✔
2049
   return result;
2✔
2050
}
5✔
2051

2052
Test::Result test_x509_as_blocks_path_validation_failure_builder() {
1✔
2053
   Test::Result result("X509 AS Block path validation failure (builder)");
1✔
2054
   result.start_timer();
1✔
2055

2056
   using Botan::Cert_Extension::ASBlocks;
1✔
2057
   auto rng = Test::new_rng(__func__);
1✔
2058

2059
   /*
2060
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2061

2062
   Each test is expected to fail and creates the following certificate chain:
2063
   Root -> Issuer -> Subject
2064

2065
   00: set all the asnum choices to 'inherit' for each cert
2066
   01: 00 but for rdis
2067
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
2068
   03: 02 but for rdis
2069
   04: both 02 and 03
2070
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
2071
   06: 05 but for rdis
2072
   07: both 05 and 06
2073
   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
2074
   09: 08 but for rdis
2075
   10: both 08 and 09
2076
   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)
2077
   12: 11 but for rdis
2078
   13: both 11 and 12
2079
   14: 08 but using the minimum instead of the maximum
2080
   15: 14 but for rdis
2081
   16: both 14 and 15
2082
   17: same as 11 but using the minimum instead of the maximum
2083
   18: 17 but for rdis
2084
   19: both 18 and 19
2085
   20: make the issuer ranges empty but have an entry in the subject ranges
2086
   */
2087
   for(size_t i = 0; i < 21; i++) {
22✔
2088
      // enable / disable all the different edge cases
2089
      const bool inherit_all_asnums = (i == 0);
21✔
2090
      const bool inherit_all_rdis = (i == 1);
21✔
2091
      const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
2092
      const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
2093
      const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
2094
      const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
2095
      const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
2096
      const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
2097
      const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
2098
      const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
2099
      const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
2100
      const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
2101
      const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
2102
      const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
2103
      const bool empty_issuer_non_empty_subject = (i == 20);
21✔
2104

2105
      // Root cert
2106
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
21✔
2107

2108
      if(!inherit_all_asnums) {
21✔
2109
         if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
20✔
2110
            // 100-200 for 02,03,04
2111
            root_blocks->add_asnum(100, 200);
4✔
2112
         } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
16✔
2113
            // 10-20,30-40,50-60 for 08,09,10
2114
            root_blocks->add_asnum(10, 20);
4✔
2115
            root_blocks->add_asnum(30, 40);
4✔
2116
            root_blocks->add_asnum(50, 60);
4✔
2117
         } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
12✔
2118
            // 10-20,30-50,60-70 for 11,12,13
2119
            root_blocks->add_asnum(10, 20);
4✔
2120
            root_blocks->add_asnum(30, 50);
4✔
2121
            root_blocks->add_asnum(60, 70);
4✔
2122
         }
2123
      } else {
2124
         root_blocks->inherit_asnum();
1✔
2125
      }
2126

2127
      // same values but for rdis
2128
      if(!inherit_all_rdis) {
21✔
2129
         if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
2130
            root_blocks->add_rdi(100, 200);
4✔
2131
         } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
2132
            root_blocks->add_rdi(10, 20);
4✔
2133
            root_blocks->add_rdi(30, 40);
4✔
2134
            root_blocks->add_rdi(50, 60);
4✔
2135
         } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
2136
            root_blocks->add_rdi(10, 20);
4✔
2137
            root_blocks->add_rdi(30, 50);
4✔
2138
            root_blocks->add_rdi(60, 70);
4✔
2139
         }
2140
      } else {
2141
         root_blocks->inherit_rdi();
1✔
2142
      }
2143

2144
      if(empty_issuer_non_empty_subject) {
21✔
2145
         root_blocks->restrict_asnum();
1✔
2146
         root_blocks->restrict_rdi();
1✔
2147
      }
2148

2149
      // Issuer cert
2150
      // the issuer cert has the same ranges as the root cert
2151
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2152
      auto issu_blocks = root_blocks->copy();
21✔
2153

2154
      // Subject cert
2155
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
21✔
2156
      if(!inherit_all_asnums) {
21✔
2157
         // assign the subject asnum ranges
2158
         if(push_asnum_min_edge_ranges) {
2159
            // 99-200 for 02 (so overlapping to the left)
2160
            sub_blocks->add_asnum(99, 200);
2✔
2161
         } else if(push_asnum_max_edge_ranges) {
2162
            // 100-201 for 03 (so overlapping to the right)
2163
            sub_blocks->add_asnum(100, 201);
2✔
2164
         } else if(push_asnum_max_middle_ranges) {
2165
            // same as root, but change the range in the middle to overlap to the right for 08
2166
            sub_blocks->add_asnum(10, 20);
2✔
2167
            sub_blocks->add_asnum(30, 41);
2✔
2168
            sub_blocks->add_asnum(50, 60);
2✔
2169
         } else if(push_asnum_max_split_ranges) {
2170
            // change the range in the middle to be cut at 45 for case 11
2171
            // the left range is 30-44
2172
            // the right range is 46-51 (overlapping the issuer range to the right)
2173
            sub_blocks->add_asnum(10, 20);
2✔
2174
            sub_blocks->add_asnum(30, 44);
2✔
2175
            sub_blocks->add_asnum(46, 51);
2✔
2176
            sub_blocks->add_asnum(60, 70);
2✔
2177
         } else if(push_asnum_min_middle_ranges) {
2178
            // just change the test in the middle to overlap to the left for case 14
2179
            sub_blocks->add_asnum(10, 20);
2✔
2180
            sub_blocks->add_asnum(29, 40);
2✔
2181
            sub_blocks->add_asnum(50, 60);
2✔
2182
         } else if(push_asnum_min_split_ranges) {
2183
            // again split the range in the middle at 45 for case 17
2184
            // creating two ranges 29-44 and 46-50 (so overlapping to the left)
2185
            sub_blocks->add_asnum(10, 20);
2✔
2186
            sub_blocks->add_asnum(29, 44);
2✔
2187
            sub_blocks->add_asnum(46, 50);
2✔
2188
            sub_blocks->add_asnum(60, 70);
2✔
2189
         } else if(empty_issuer_non_empty_subject) {
2190
            sub_blocks->add_asnum(50);
1✔
2191
         }
2192
      } else {
2193
         sub_blocks->inherit_asnum();
1✔
2194
      }
2195

2196
      if(!inherit_all_rdis) {
21✔
2197
         // same values but for rdis
2198
         if(push_rdi_min_edge_ranges) {
2199
            sub_blocks->add_rdi(99, 200);
2✔
2200
         } else if(push_rdi_max_edge_ranges) {
2201
            sub_blocks->add_rdi(100, 201);
2✔
2202
         } else if(push_rdi_max_middle_ranges) {
2203
            sub_blocks->add_rdi(10, 20);
2✔
2204
            sub_blocks->add_rdi(30, 41);
2✔
2205
            sub_blocks->add_rdi(50, 60);
2✔
2206
         } else if(push_rdi_max_split_ranges) {
2207
            sub_blocks->add_rdi(10, 20);
2✔
2208
            sub_blocks->add_rdi(30, 44);
2✔
2209
            sub_blocks->add_rdi(46, 51);
2✔
2210
            sub_blocks->add_rdi(60, 70);
2✔
2211
         } else if(push_rdi_min_middle_ranges) {
2212
            sub_blocks->add_rdi(10, 20);
2✔
2213
            sub_blocks->add_rdi(29, 40);
2✔
2214
            sub_blocks->add_rdi(50, 60);
2✔
2215
         } else if(push_rdi_min_split_ranges) {
2216
            sub_blocks->add_rdi(10, 20);
2✔
2217
            sub_blocks->add_rdi(29, 44);
2✔
2218
            sub_blocks->add_rdi(46, 50);
2✔
2219
            sub_blocks->add_rdi(60, 70);
2✔
2220
         }
2221
      } else {
2222
         sub_blocks->inherit_rdi();
1✔
2223
      }
2224

2225
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2226
      root_opts.extensions.add(std::move(root_blocks));
21✔
2227
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2228
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2229

2230
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2231
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2232
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2233
      const Botan::X509_Certificate sub_cert =
21✔
2234
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2235

2236
      Botan::Certificate_Store_In_Memory trusted;
21✔
2237
      trusted.add_certificate(root_cert);
21✔
2238

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

2242
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
21✔
2243
      // in all cases, the validation should fail, since we are creating invalid scenarios
2244
      result.test_is_true("path validation fails at iteration " + std::to_string(i),
42✔
2245
                          !path_result.successful_validation());
21✔
2246
   }
42✔
2247

2248
   result.end_timer();
1✔
2249
   return result;
1✔
2250
}
22✔
2251

2252
Test::Result test_x509_as_blocks_path_validation_failure_ctor() {
1✔
2253
   Test::Result result("X509 AS Block path validation failure (ctor)");
1✔
2254
   result.start_timer();
1✔
2255

2256
   using Botan::Cert_Extension::ASBlocks;
1✔
2257
   auto rng = Test::new_rng(__func__);
1✔
2258

2259
   /*
2260
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
2261

2262
   Each test is expected to fail and creates the following certificate chain:
2263
   Root -> Issuer -> Subject
2264

2265
   00: set all the asnum choices to 'inherit' for each cert
2266
   01: 00 but for rdis
2267
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
2268
   03: 02 but for rdis
2269
   04: both 02 and 03
2270
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
2271
   06: 05 but for rdis
2272
   07: both 05 and 06
2273
   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
2274
   09: 08 but for rdis
2275
   10: both 08 and 09
2276
   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)
2277
   12: 11 but for rdis
2278
   13: both 11 and 12
2279
   14: 08 but using the minimum instead of the maximum
2280
   15: 14 but for rdis
2281
   16: both 14 and 15
2282
   17: same as 11 but using the minimum instead of the maximum
2283
   18: 17 but for rdis
2284
   19: both 18 and 19
2285
   20: make the issuer ranges empty but have an entry in the subject ranges
2286
   */
2287
   for(size_t i = 0; i < 21; i++) {
22✔
2288
      // enable / disable all the different edge cases
2289
      const bool inherit_all_asnums = (i == 0);
21✔
2290
      const bool inherit_all_rdis = (i == 1);
21✔
2291
      const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
2292
      const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
2293
      const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
2294
      const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
2295
      const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
2296
      const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
2297
      const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
2298
      const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
2299
      const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
2300
      const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
2301
      const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
2302
      const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
2303
      const bool empty_issuer_non_empty_subject = (i == 20);
21✔
2304

2305
      // Root cert
2306
      std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
21✔
2307
      std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
21✔
2308

2309
      // assign the root ranges
2310
      if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
21✔
2311
         // 100-200 for 02,03,04
2312
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
2313
      } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
17✔
2314
         // 10-20,30-40,50-60 for 08,09,10
2315
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2316
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
2317
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
2318
      } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
13✔
2319
         // 10-20,30-50,60-70 for 11,12,13
2320
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2321
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
2322
         root_as_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
2323
      }
2324

2325
      // same values but for rdis
2326
      if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
21✔
2327
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
8✔
2328
      } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
2329
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2330
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
8✔
2331
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
8✔
2332
      } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
2333
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
8✔
2334
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
8✔
2335
         root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
8✔
2336
      }
2337

2338
      // Issuer cert
2339
      // the issuer cert has the same ranges as the root cert
2340
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
2341
      const std::vector<ASBlocks::ASIdOrRange> issu_as_ranges;
21✔
2342
      const std::vector<ASBlocks::ASIdOrRange> issu_rdi_ranges;
21✔
2343

2344
      // Subject cert
2345
      std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
21✔
2346
      std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
21✔
2347

2348
      // assign the subject asnum ranges
2349
      if(push_asnum_min_edge_ranges) {
21✔
2350
         // 99-200 for 02 (so overlapping to the left)
2351
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
4✔
2352
      } else if(push_asnum_max_edge_ranges) {
2353
         // 100-201 for 03 (so overlapping to the right)
2354
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
4✔
2355
      } else if(push_asnum_max_middle_ranges) {
2356
         // just change the range in the middle to overlap to the right for 08
2357
         sub_as_ranges = root_as_ranges;
2✔
2358
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
2✔
2359
      } else if(push_asnum_max_split_ranges) {
2360
         // change the range in the middle to be cut at 45 for case 11
2361
         // the left range is 30-44
2362
         // the right range is 46-51 (overlapping the issuer range to the right)
2363
         sub_as_ranges = root_as_ranges;
2✔
2364
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
2✔
2365
         // pushing the new range created by splitting to the back since they will be sorted anyway
2366
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
4✔
2367
      } else if(push_asnum_min_middle_ranges) {
2368
         // just change the test in the middle to overlap to the left for case 14
2369
         sub_as_ranges = root_as_ranges;
2✔
2370
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
2✔
2371
      } else if(push_asnum_min_split_ranges) {
2372
         // again split the range in the middle at 45 for case 17
2373
         // creating two ranges 29-44 and 46-50 (so overlapping to the left)
2374
         sub_as_ranges = root_as_ranges;
2✔
2375
         sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
2✔
2376
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
4✔
2377
      } else if(empty_issuer_non_empty_subject) {
2378
         sub_as_ranges.push_back(ASBlocks::ASIdOrRange(50));
1✔
2379
      }
2380

2381
      // same values but for rdis
2382
      if(push_rdi_min_edge_ranges) {
21✔
2383
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
4✔
2384
      } else if(push_rdi_max_edge_ranges) {
2385
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
4✔
2386
      } else if(push_rdi_max_middle_ranges) {
2387
         sub_rdi_ranges = root_rdi_ranges;
2✔
2388
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
2✔
2389
      } else if(push_rdi_max_split_ranges) {
2390
         sub_rdi_ranges = root_rdi_ranges;
2✔
2391
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
2✔
2392
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
4✔
2393
      } else if(push_rdi_min_middle_ranges) {
2394
         sub_rdi_ranges = root_rdi_ranges;
2✔
2395
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
2✔
2396
      } else if(push_rdi_min_split_ranges) {
2397
         sub_rdi_ranges = root_rdi_ranges;
2✔
2398
         sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
2✔
2399
         sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
4✔
2400
      }
2401

2402
      // for cases 00 and 01, set all certs to inherit (so std::nullopt)
2403
      // in all other cases use the ranges created beforehand
2404
      ASBlocks::ASIdentifierChoice root_asnum =
21✔
2405
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(root_as_ranges));
41✔
2406
      ASBlocks::ASIdentifierChoice root_rdi =
21✔
2407
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(root_rdi_ranges));
41✔
2408
      const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
82✔
2409
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
21✔
2410

2411
      const ASBlocks::ASIdentifiers& issu_ident = root_ident;
21✔
2412
      std::unique_ptr<ASBlocks> issu_blocks = std::make_unique<ASBlocks>(issu_ident);
21✔
2413

2414
      ASBlocks::ASIdentifierChoice sub_asnum =
21✔
2415
         ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(sub_as_ranges));
41✔
2416
      ASBlocks::ASIdentifierChoice sub_rdi =
21✔
2417
         ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(sub_rdi_ranges));
41✔
2418
      const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
82✔
2419
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
21✔
2420

2421
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
2422
      root_opts.extensions.add(std::move(root_blocks));
21✔
2423
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
2424
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
2425

2426
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
2427
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
2428
      const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
2429
      const Botan::X509_Certificate sub_cert =
21✔
2430
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
2431

2432
      Botan::Certificate_Store_In_Memory trusted;
21✔
2433
      trusted.add_certificate(root_cert);
21✔
2434

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

2438
      const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
21✔
2439
      // in all cases, the validation should fail, since we are creating invalid scenarios
2440
      result.test_is_true("path validation fails at iteration " + std::to_string(i),
42✔
2441
                          !path_result.successful_validation());
21✔
2442
   }
122✔
2443

2444
   result.end_timer();
1✔
2445
   return result;
1✔
2446
}
22✔
2447

2448
class X509_RPKI_Tests final : public Test {
1✔
2449
   public:
2450
      std::vector<Test::Result> run() override {
1✔
2451
         std::vector<Test::Result> results;
1✔
2452

2453
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
2454
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
2455
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
2456
   #endif
2457
         results.push_back(test_x509_as_blocks_extension_decode_malformed());
2✔
2458
         results.push_back(test_x509_ip_addr_blocks_rfc3779_example());
2✔
2459
         results.push_back(test_x509_ip_addr_blocks_encode_builder());
2✔
2460
         results.push_back(test_x509_ip_addr_blocks_extension_encode_ctor());
2✔
2461
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor());
2✔
2462
         results.push_back(test_x509_ip_addr_blocks_range_merge());
2✔
2463
         results.push_back(test_x509_ip_addr_blocks_family_merge());
2✔
2464
         results.push_back(test_x509_ip_addr_blocks_path_validation_success_builder());
2✔
2465
         results.push_back(test_x509_ip_addr_blocks_path_validation_success_ctor());
2✔
2466
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure_builder());
2✔
2467
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure_ctor());
2✔
2468
         results.push_back(test_x509_as_blocks_rfc3779_example());
2✔
2469
         results.push_back(test_x509_as_blocks_encode_builder());
2✔
2470
         results.push_back(test_x509_as_blocks_extension_encode_ctor());
2✔
2471
         results.push_back(test_x509_as_blocks_range_merge());
2✔
2472
         results.push_back(test_x509_as_blocks_path_validation_success_builder());
2✔
2473
         results.push_back(test_x509_as_blocks_path_validation_success_ctor());
2✔
2474
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present_builder());
2✔
2475
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present_ctor());
2✔
2476
         results.push_back(test_x509_as_blocks_path_validation_failure_builder());
2✔
2477
         results.push_back(test_x509_as_blocks_path_validation_failure_ctor());
2✔
2478
         return results;
1✔
2479
      }
×
2480
};
2481

2482
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
2483

2484
#endif
2485

2486
}  // namespace
2487

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