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

randombit / botan / 15280799449

27 May 2025 04:36PM UTC coverage: 90.972% (-0.003%) from 90.975%
15280799449

Pull #4890

github

web-flow
Merge 831bec7f1 into 5fbcc7daa
Pull Request #4890: Improve RFC 3779 extension api

97996 of 107721 relevant lines covered (90.97%)

12566792.91 hits per line

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

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

8
#include "tests.h"
9

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

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

25
namespace Botan_Tests {
26

27
namespace {
28

29
#if defined(BOTAN_HAS_X509_CERTIFICATES)
30

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

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

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

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

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

62
   return Botan::create_private_key(algo, rng, params);
112✔
63
}
112✔
64

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

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

73
   opts.CA_key(1);
75✔
74

75
   return opts;
75✔
76
}
×
77

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

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

86
   opts.not_before("160101200000Z");
1,245✔
87
   opts.not_after("300101200000Z");
1,245✔
88

89
   opts.challenge = "zoom";
1,245✔
90

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

97
   return opts;
1,245✔
98
}
×
99

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

115
   return std::make_tuple(sig_algo, padding_method, hash_fn);
150✔
116
}
75✔
117

118
CA_Creation_Result make_ca(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
37✔
119
                           const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
120
   auto [sig_algo, padding_method, hash_fn] = get_sig_algo_padding();
37✔
121
   auto ca_key = generate_key(sig_algo, *rng);
37✔
122
   const auto ca_cert = Botan::X509::create_self_signed_cert(opts, *ca_key, hash_fn, *rng);
37✔
123
   Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
37✔
124
   auto sub_key = generate_key(sig_algo, *rng);
37✔
125

126
   return CA_Creation_Result{ca_cert, std::move(ca), std::move(sub_key), sig_algo, hash_fn};
111✔
127
}
74✔
128

129
std::pair<Botan::X509_Certificate, Botan::X509_CA> make_and_sign_ca(
38✔
130
   std::unique_ptr<Botan::Certificate_Extension> ext,
131
   Botan::X509_CA& parent_ca,
132
   std::unique_ptr<Botan::RandomNumberGenerator>& rng) {
133
   auto [sig_algo, padding_method, hash_fn] = get_sig_algo_padding();
38✔
134

135
   Botan::X509_Cert_Options opts = ca_opts();
38✔
136
   opts.extensions.add(std::move(ext));
38✔
137

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

140
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, hash_fn, *rng);
38✔
141
   Botan::X509_Certificate cert = parent_ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
38✔
142
   Botan::X509_CA ca(cert, *key, hash_fn, padding_method, *rng);
38✔
143

144
   return std::make_pair(std::move(cert), std::move(ca));
38✔
145
}
76✔
146

147
constexpr auto IPv4 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv4;
148
constexpr auto IPv6 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv6;
149

150
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
151

152
Test::Result test_x509_ip_addr_blocks_extension_decode() {
1✔
153
   Test::Result result("X509 IP Address Block decode");
1✔
154
   result.start_timer();
1✔
155
   const std::string filename("IPAddrBlocksAll.pem");
1✔
156

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

159
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
160

161
   auto ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
162

163
   const auto& addr_blocks = ip_addr_blocks->addr_blocks();
1✔
164
   result.confirm("cert has IPAddrBlocks extension", ip_addr_blocks != nullptr, true);
2✔
165
   result.test_eq("cert has two IpAddrBlocks", addr_blocks.size(), 2);
1✔
166

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

170
   auto& v4_blocks = ipv4block.ranges().value();
1✔
171

172
   // 192.168.0.0
173
   result.test_eq("ipv4 block 0 min", v4_blocks[0].min().value(), "C0A80000");
1✔
174
   // 192.168.127.255
175
   result.test_eq("ipv4 block 0 max", v4_blocks[0].max().value(), "C0A87FFF");
1✔
176

177
   // 193.168.0.0
178
   result.test_eq("ipv4 block 1 min", v4_blocks[1].min().value(), "C1A80000");
1✔
179
   // 193.169.255.255
180
   result.test_eq("ipv4 block 1 max", v4_blocks[1].max().value(), "C1A9FFFF");
1✔
181

182
   // 194.168.0.0
183
   result.test_eq("ipv4 block 2 min", v4_blocks[2].min().value(), "C2A80000");
1✔
184
   // 195.175.1.2
185
   result.test_eq("ipv4 block 2 max", v4_blocks[2].max().value(), "C3AF0102");
1✔
186

187
   // 196.168.0.1
188
   result.test_eq("ipv4 block 3 min", v4_blocks[3].min().value(), "C4A80001");
1✔
189
   // 196.168.0.1
190
   result.test_eq("ipv4 block 3 max", v4_blocks[3].max().value(), "C4A80001");
1✔
191

192
   auto& v6_blocks = ipv6block.ranges().value();
1✔
193

194
   result.test_eq("ipv6 block 0 min", v6_blocks[0].min().value(), "FA800000000000000000000000000000");
1✔
195
   result.test_eq("ipv6 block 0 max", v6_blocks[0].max().value(), "FA800000000000007FFFFFFFFFFFFFFF");
1✔
196
   result.test_eq("ipv6 block 1 min", v6_blocks[1].min().value(), "FE200000000000000000000000000000");
1✔
197
   result.test_eq("ipv6 block 1 max", v6_blocks[1].max().value(), "FE20000007FFFFFFFFFFFFFFFFFFFFFF");
1✔
198
   result.test_eq("ipv6 block 2 min", v6_blocks[2].min().value(), "2003000068293435042010C5000000C4");
1✔
199
   result.test_eq("ipv6 block 2 max", v6_blocks[2].max().value(), "2003000068293435042010C5000000C4");
1✔
200
   result.test_eq("ipv6 block 3 min", v6_blocks[3].min().value(), "AB010000000000000000000000000001");
1✔
201
   result.test_eq("ipv6 block 3 max", v6_blocks[3].max().value(), "CD020000000000000000000000000002");
1✔
202

203
   result.end_timer();
1✔
204
   return result;
2✔
205
}
1✔
206

207
Test::Result test_x509_as_blocks_extension_decode() {
1✔
208
   Test::Result result("X509 AS Block decode");
1✔
209
   result.start_timer();
1✔
210
   using Botan::Cert_Extension::ASBlocks;
1✔
211

212
   {
1✔
213
      const std::string filename("ASNumberCert.pem");
1✔
214
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
215

216
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
217

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

221
      const auto& asnum = identifier.asnum().value().ranges().value();
1✔
222
      const auto& rdi = identifier.rdi().value().ranges().value();
1✔
223

224
      // cert contains asnum 0-999, 5042, 0-4294967295
225
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
226
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
227

228
      // and rdi 1234-5678, 32768, 0-4294967295
229
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
230
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
231
   }
1✔
232
   {
1✔
233
      const std::string filename("ASNumberOnly.pem");
1✔
234
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
235

236
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
237

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

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

244
      // contains 0-999, 0-4294967295
245
      result.confirm("asnum entry 0 min", asnum[0].min() == 0, true);
2✔
246
      result.confirm("asnum entry 0 max", asnum[0].max() == 4294967295, true);
2✔
247
   }
1✔
248
   {
1✔
249
      const std::string filename("ASRdiOnly.pem");
1✔
250
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
251

252
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
253

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

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

260
      // contains 1234-5678, 0-4294967295
261
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
262
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
263
   }
1✔
264
   {
1✔
265
      const std::string filename("ASNumberInherit.pem");
1✔
266
      Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
2✔
267

268
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
269

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

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

276
      // contains 1234-5678, 0-4294967295
277
      result.confirm("rdi entry 0 min", rdi[0].min() == 0, true);
2✔
278
      result.confirm("rdi entry 0 max", rdi[0].max() == 4294967295, true);
2✔
279
   }
1✔
280

281
   result.end_timer();
1✔
282
   return result;
1✔
283
}
×
284

285
   #endif
286

287
Test::Result test_x509_ip_addr_blocks_extension_encode() {
1✔
288
   Test::Result result("X509 IP Address Block encode");
1✔
289
   result.start_timer();
1✔
290

291
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
292

293
   auto rng = Test::new_rng(__func__);
1✔
294

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

297
   for(size_t i = 0; i < 64; i++) {
65✔
298
      bool push_ipv4_ranges = i & 1;
64✔
299
      bool push_ipv6_ranges = i >> 1 & 1;
64✔
300
      bool inherit_ipv4 = i >> 2 & 1;
64✔
301
      bool inherit_ipv6 = i >> 3 & 1;
64✔
302
      bool push_ipv4_family = i >> 4 & 1;
64✔
303
      bool push_ipv6_family = i >> 5 & 1;
64✔
304

305
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
64✔
306

307
      std::vector<uint8_t> a = {123, 123, 2, 1};
64✔
308
      auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
309
      a = {255, 255, 255, 255};
64✔
310
      auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
311

312
      // encoded as min, max
313
      a = {127, 0, 0, 1};
64✔
314
      auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
315
      a = {189, 5, 7, 255};
64✔
316
      auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
317

318
      // encoded as prefix
319
      a = {190, 5, 0, 0};
64✔
320
      auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
321
      a = {190, 5, 127, 255};
64✔
322
      auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
64✔
323

324
      a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
325
      auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
326
      a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
327
      auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
328

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

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

336
      // encoded as prefix
337
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
64✔
338
      auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
339
      a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
64✔
340
      auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
64✔
341

342
      auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
64✔
343
      auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
64✔
344
      auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
64✔
345
      auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
64✔
346

347
      auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
64✔
348
      auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
64✔
349
      auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
64✔
350
      auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
64✔
351

352
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
64✔
353
      if(push_ipv4_ranges) {
64✔
354
         ipv4_ranges.push_back(ipv4_range_1);
32✔
355
         ipv4_ranges.push_back(ipv4_range_2);
32✔
356
         ipv4_ranges.push_back(ipv4_range_3);
32✔
357
         ipv4_ranges.push_back(ipv4_range_4);
32✔
358
      }
359

360
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
64✔
361
      if(push_ipv6_ranges) {
64✔
362
         ipv6_ranges.push_back(ipv6_range_1);
32✔
363
         ipv6_ranges.push_back(ipv6_range_2);
32✔
364
         ipv6_ranges.push_back(ipv6_range_3);
32✔
365
         ipv6_ranges.push_back(ipv6_range_4);
32✔
366
      }
367

368
      auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
64✔
369
      if(!inherit_ipv4) {
64✔
370
         ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
96✔
371
      }
372

373
      auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
64✔
374
      if(!inherit_ipv6) {
64✔
375
         ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
96✔
376
      }
377

378
      auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
160✔
379
      auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
160✔
380

381
      std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
64✔
382
      if(push_ipv4_family) {
64✔
383
         addr_blocks.push_back(ipv4_addr_family);
32✔
384
      }
385
      if(push_ipv6_family) {
64✔
386
         addr_blocks.push_back(ipv6_addr_family);
32✔
387
      }
388

389
      std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
64✔
390

391
      opts.extensions.add(std::move(blocks));
64✔
392

393
      Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
64✔
394
      Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
64✔
395
      {
64✔
396
         auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
64✔
397
         result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
128✔
398

399
         const auto& dec_addr_blocks = ip_blocks->addr_blocks();
64✔
400
         if(!push_ipv4_family && !push_ipv6_family) {
64✔
401
            result.confirm("no address family entries", dec_addr_blocks.empty(), true);
32✔
402
            continue;
16✔
403
         }
404

405
         if(push_ipv4_family) {
48✔
406
            auto family = dec_addr_blocks[0];
32✔
407
            result.confirm("ipv4 family afi", ipv4_addr_family.afi() == family.afi(), true);
64✔
408
            result.test_eq("ipv4 family safi", ipv4_addr_family.safi(), family.safi());
32✔
409
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
32✔
410

411
            if(!inherit_ipv4) {
32✔
412
               auto ranges = choice.ranges().value();
16✔
413
               if(push_ipv4_ranges) {
16✔
414
                  result.test_eq("ipv4 entry 0 min", ranges[0].min().value(), ipv4_range_1.min().value());
16✔
415
                  result.test_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
16✔
416
                  result.test_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
16✔
417
                  result.test_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
16✔
418
                  result.test_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
16✔
419
                  result.test_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
16✔
420
                  result.test_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
16✔
421
                  result.test_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
16✔
422
               } else {
423
                  result.confirm("ipv4 range has no entries", ranges.empty(), true);
16✔
424
               }
425
            } else {
16✔
426
               result.confirm("ipv4 family inherit", choice.ranges().has_value(), false);
32✔
427
            }
428
         }
64✔
429

430
         if(push_ipv6_family) {
48✔
431
            auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
32✔
432
            result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
64✔
433
            result.test_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
32✔
434
            auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
32✔
435
            if(!inherit_ipv6) {
32✔
436
               auto ranges = choice.ranges().value();
16✔
437
               if(push_ipv6_ranges) {
16✔
438
                  result.test_eq("ipv6 entry 0 min", ranges[0].min().value(), ipv6_range_1.min().value());
16✔
439
                  result.test_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
16✔
440
                  result.test_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
16✔
441
                  result.test_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
16✔
442
                  result.test_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
16✔
443
                  result.test_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
16✔
444
                  result.test_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
16✔
445
                  result.test_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
16✔
446
               } else {
447
                  result.confirm("ipv6 range has no entries", ranges.empty(), true);
16✔
448
               }
449
            } else {
16✔
450
               result.confirm("ipv6 family inherit", choice.ranges().has_value(), false);
32✔
451
            }
452
         }
64✔
453
      }
454
   }
320✔
455

456
   result.end_timer();
1✔
457
   return result;
2✔
458
}
2✔
459

460
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases() {
1✔
461
   Test::Result result("X509 IP Address Block encode edge cases");
1✔
462
   result.start_timer();
1✔
463

464
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
465

466
   auto rng = Test::new_rng(__func__);
1✔
467

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

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

474
   for(size_t i = 0; i < edge_values.size(); i++) {
22✔
475
      for(size_t j = 0; j < 4; j++) {
105✔
476
         bool modify_min = j & 1;
84✔
477
         bool modify_max = (j >> 1) & 1;
84✔
478

479
         for(size_t k = 0; k < 18; k++) {
1,219✔
480
            if(!modify_min && !modify_max && (k > 0 || i > 0)) {
1,156✔
481
               // we don't modify anything, this is the extreme edge case of 0.0 ... - 255.255. ...
482
               // so we only need to do this once
483
               break;
484
            }
485

486
            Botan::X509_Cert_Options opts = req_opts(sig_algo);
1,135✔
487

488
            std::vector<uint8_t> min_bytes(16, 0x00);
1,135✔
489
            std::vector<uint8_t> max_bytes(16, 0xFF);
1,135✔
490

491
            if(modify_min) {
1,135✔
492
               min_bytes[15 - (k < 2 ? 0 : k - 2)] = edge_values[i];
756✔
493
            }
494
            if(modify_max) {
1,135✔
495
               max_bytes[15 - (k > 15 ? 15 : k)] = edge_values[i];
756✔
496
            }
497

498
            auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
1,135✔
499
            auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
1,135✔
500

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

503
            std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
1,135✔
504
            ipv6_ranges.push_back(ipv6_range);
1,135✔
505

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

508
            auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3,405✔
509

510
            std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1,135✔
511
            addr_blocks.push_back(ipv6_addr_family);
1,135✔
512

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

515
            opts.extensions.add(std::move(blocks));
1,135✔
516

517
            Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1,135✔
518
            Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1,135✔
519
            {
1,135✔
520
               auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1,135✔
521
               result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2,270✔
522
               const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1,135✔
523
               auto family = dec_addr_blocks[0];
1,135✔
524
               result.confirm("ipv6 family afi", ipv6_addr_family.afi() == family.afi(), true);
2,270✔
525
               result.test_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
1,135✔
526
               auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
1,135✔
527
               auto ranges = choice.ranges().value();
1,135✔
528

529
               result.test_eq("ipv6 edge case min", ranges[0].min().value(), ipv6_range.min().value());
2,270✔
530
               result.test_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
2,270✔
531
            }
2,270✔
532
         }
5,675✔
533
      }
534
   }
535
   result.end_timer();
1✔
536
   return result;
2✔
537
}
3✔
538

539
Test::Result test_x509_ip_addr_blocks_range_merge() {
1✔
540
   Test::Result result("X509 IP Address Block range merge");
1✔
541
   result.start_timer();
1✔
542

543
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
544

545
   auto rng = Test::new_rng(__func__);
1✔
546

547
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
548
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
549

550
   std::vector<std::vector<std::vector<uint8_t>>> addresses = {
1✔
551
      {{11, 0, 0, 0}, {{11, 0, 0, 0}}},
552
      {{123, 123, 123, 123}, {123, 123, 123, 123}},
553
      {{10, 4, 5, 9}, {{10, 255, 0, 0}}},
554
      {{12, 0, 0, 0}, {191, 0, 0, 1}},
555
      {{190, 0, 0, 0}, {193, 0, 255, 255}},
556
      {{10, 10, 10, 10}, {10, 20, 20, 20}},
557
      {{5, 0, 0, 0}, {10, 255, 255, 255}},
558
      {{192, 0, 0, 0}, {192, 255, 255, 255}},
559
      {{11, 0, 0, 1}, {11, 255, 255, 255}},
560
   };
46✔
561

562
   std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv6_ranges;
1✔
563
   for(auto pair : addresses) {
10✔
564
      auto address_min = IPAddressBlocks::IPAddress<IPv4>(pair[0]);
9✔
565
      auto address_max = IPAddressBlocks::IPAddress<IPv4>(pair[1]);
9✔
566
      auto range = IPAddressBlocks::IPAddressOrRange<IPv4>(address_min, address_max);
9✔
567
      ipv6_ranges.push_back(range);
9✔
568
   }
9✔
569

570
   auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
1✔
571
   auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
3✔
572

573
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
574
   addr_blocks.push_back(ipv6_addr_family);
1✔
575

576
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
577

578
   opts.extensions.add(std::move(blocks));
1✔
579

580
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
581
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
582
   {
1✔
583
      auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
584
      result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
585
      const auto& dec_addr_blocks = ip_blocks->addr_blocks();
1✔
586
      auto family = dec_addr_blocks[0];
1✔
587
      auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
1✔
588
      auto ranges = choice.ranges().value();
1✔
589

590
      std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
1✔
591
      std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
1✔
592

593
      result.test_eq("range expected min", ranges[0].min().value(), expected_min);
2✔
594
      result.test_eq("range expected max", ranges[0].max().value(), expected_max);
2✔
595
      result.test_eq("range length", ranges.size(), 1);
1✔
596
   }
2✔
597

598
   result.end_timer();
1✔
599
   return result;
2✔
600
}
24✔
601

602
Test::Result test_x509_ip_addr_blocks_family_merge() {
1✔
603
   Test::Result result("X509 IP Address Block family merge");
1✔
604
   result.start_timer();
1✔
605

606
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
607

608
   auto rng = Test::new_rng(__func__);
1✔
609

610
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
611
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
612

613
   std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
1✔
614

615
   IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
1✔
616
   IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
1✔
617

618
   uint8_t v4_bytes_1[4] = {123, 123, 123, 123};
1✔
619
   IPAddressBlocks::IPAddress<IPv4> v4_addr_1(v4_bytes_1);
1✔
620
   // create 2 prefixes from the v4 addresses -> they should be merged
621
   IPAddressBlocks::IPAddressChoice<IPv4> v4_choice_dupl({{{{v4_addr_1}, {v4_addr_1}}}});
×
622
   result.confirm(
2✔
623
      "IPAddressChoice v4 merges ranges already in constructor", v4_choice_dupl.ranges().value().size() == 1, true);
1✔
624
   IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
3✔
625

626
   uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
1✔
627
   IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
1✔
628
   IPAddressBlocks::IPAddressChoice<IPv6> v6_choice_dupl({{{{v6_addr_1}, {v6_addr_1}}}});
×
629
   result.confirm(
2✔
630
      "IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size() == 1, true);
1✔
631
   IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
3✔
632

633
   IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
2✔
634
   IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
2✔
635

636
   IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
2✔
637
   IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
2✔
638

639
   /*
640
   considering the push order, the resulting order should be
641
   [0] v4 no safi
642
   [1] v6 no safi
643
   [2] v4 safi
644
   [3] v6 safi
645
   */
646
   for(size_t i = 0; i < 3; i++) {
4✔
647
      addr_blocks.push_back(v4_empty_fam_safi);
3✔
648
      addr_blocks.push_back(v6_empty_fam);
3✔
649
      addr_blocks.push_back(v4_fam_dupl);
3✔
650
      addr_blocks.push_back(v6_empty_fam_safi);
3✔
651
      addr_blocks.push_back(v6_fam_dupl);
3✔
652
      addr_blocks.push_back(v4_empty_fam);
3✔
653
   }
654

655
   std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
1✔
656
      v4_empty_fam, v6_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_fam_dupl, v6_empty_fam_safi};
7✔
657

658
   std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
1✔
659

660
   opts.extensions.add(std::move(blocks));
1✔
661

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

665
   auto ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
1✔
666
   result.confirm("cert has IPAddrBlocks extension", ip_blocks != nullptr, true);
2✔
667
   const auto& dec_blocks = ip_blocks->addr_blocks();
1✔
668

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

671
   bool sorted = true;
1✔
672
   for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
6✔
673
      const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
5✔
674
      const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
5✔
675

676
      uint32_t afam_a = a.afi();
5✔
677
      if(a.safi().has_value()) {
5✔
678
         afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
3✔
679
      }
680

681
      uint32_t afam_b = b.afi();
5✔
682
      if(b.safi().has_value()) {
5✔
683
         afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
4✔
684
      }
685

686
      if(afam_a > afam_b) {
5✔
687
         sorted = false;
688
         break;
689
      }
690
   }
691

692
   result.confirm("blocks got sorted", sorted, true);
2✔
693

694
   for(size_t i = 0; i < dec_blocks.size(); i++) {
7✔
695
      const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
6✔
696
      const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
6✔
697

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

701
      if((exp.afi() == 1) && (dec.afi() == 1)) {
6✔
702
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
3✔
703
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
3✔
704

705
         if(!exp_choice.ranges().has_value()) {
3✔
706
            result.confirm(
2✔
707
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
708
         } else {
709
            result.confirm(
1✔
710
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
711

712
            if(dec_choice.ranges().has_value() == false) {
1✔
713
               continue;
×
714
            }
715

716
            auto dec_ranges = dec_choice.ranges().value();
1✔
717
            auto exp_ranges = exp_choice.ranges().value();
1✔
718
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
719
                           dec_ranges.size() == exp_ranges.size(),
1✔
720
                           true);
721

722
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
723
               continue;
×
724
            }
725

726
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
727
               result.test_eq(
2✔
728
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
729
                  exp_ranges[j].min().value(),
1✔
730
                  dec_ranges[j].min().value());
1✔
731
               result.test_eq(
2✔
732
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
733
                  exp_ranges[j].max().value(),
1✔
734
                  dec_ranges[j].max().value());
2✔
735
            }
736
         }
1✔
737
      } else if((exp.afi() == 2) && (dec.afi() == 2)) {
7✔
738
         auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(dec.addr_choice());
3✔
739
         auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(exp.addr_choice());
3✔
740

741
         if(!exp_choice.ranges().has_value()) {
3✔
742
            result.confirm(
2✔
743
               "block ranges should inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), false);
6✔
744
         } else {
745
            result.confirm(
1✔
746
               "block ranges should not inherit at index " + std::to_string(i), dec_choice.ranges().has_value(), true);
3✔
747

748
            if(dec_choice.ranges().has_value() == false) {
1✔
749
               continue;
×
750
            }
751

752
            auto dec_ranges = dec_choice.ranges().value();
1✔
753
            auto exp_ranges = exp_choice.ranges().value();
1✔
754
            result.confirm("block ranges got merged lengthwise at index " + std::to_string(i),
2✔
755
                           dec_ranges.size() == exp_ranges.size(),
1✔
756
                           true);
757

758
            if(dec_ranges.size() != exp_ranges.size()) {
1✔
759
               continue;
×
760
            }
761

762
            for(size_t j = 0; j < exp_ranges.size(); j++) {
2✔
763
               result.test_eq(
2✔
764
                  "block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
765
                  exp_ranges[j].min().value(),
1✔
766
                  dec_ranges[j].min().value());
1✔
767
               result.test_eq(
2✔
768
                  "block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
4✔
769
                  exp_ranges[j].max().value(),
1✔
770
                  dec_ranges[j].max().value());
2✔
771
            }
772
         }
1✔
773
      }
4✔
774
   }
775

776
   result.end_timer();
1✔
777
   return result;
2✔
778
}
19✔
779

780
Test::Result test_x509_ip_addr_blocks_path_validation_success() {
1✔
781
   Test::Result result("X509 IP Address Block path validation success");
1✔
782
   result.start_timer();
1✔
783

784
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
785
   auto rng = Test::new_rng(__func__);
1✔
786

787
   /*
788
   Creates a certificate chain of length 4.
789
   Root: ipv4 and ipv6
790
   Inherit: has both values as 'inherit'
791
   Dynamic: has either both 'inherit', both with values, or just one with a value
792
   Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
793
   */
794

795
   // Root cert
796
   std::vector<uint8_t> a = {120, 0, 0, 1};
1✔
797
   auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
798
   a = {130, 140, 150, 160};
1✔
799
   auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
1✔
800

801
   a = {10, 0, 0, 1};
1✔
802
   auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
803
   a = {10, 255, 255, 255};
1✔
804
   auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
805

806
   a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
807
   auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
808
   a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
809
   auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
810

811
   a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
812
   auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
813
   a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
814
   auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
815

816
   auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
1✔
817
   auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
1✔
818
   auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
1✔
819
   auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
1✔
820

821
   auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
1✔
822
   auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
1✔
823

824
   auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
1✔
825
   auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
1✔
826

827
   auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
3✔
828
   auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
3✔
829

830
   auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
6✔
831
   std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
1✔
832

833
   // Inherit cert
834
   auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
1✔
835
   auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
1✔
836

837
   auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
2✔
838
   auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
2✔
839

840
   auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
6✔
841
   std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
1✔
842

843
   // Dynamic Cert
844
   a = {122, 0, 0, 255};
1✔
845
   auto dyn_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
846
   a = {128, 255, 255, 255};
1✔
847
   auto dyn_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
848
   a = {10, 0, 0, 255};
1✔
849
   auto dyn_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
850
   a = {10, 255, 0, 1};
1✔
851
   auto dyn_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
852

853
   a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1✔
854
   auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
855
   a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
856
   auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
857

858
   auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
1✔
859
   auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
1✔
860
   auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
1✔
861

862
   auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
1✔
863
   auto dyn_ipv6_ranges = {dyn_ipv6_range};
1✔
864

865
   // Subject cert
866
   a = {124, 0, 255, 0};
1✔
867
   auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
868
   a = {126, 0, 0, 1};
1✔
869
   auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
870

871
   a = {10, 0, 2, 1};
1✔
872
   auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
873
   a = {10, 42, 0, 255};
1✔
874
   auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
1✔
875

876
   a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
877
   auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
878
   a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1✔
879
   auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
1✔
880

881
   auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
1✔
882
   auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
1✔
883
   auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
1✔
884

885
   auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
1✔
886
   auto sub_ipv6_ranges = {sub_ipv6_range};
1✔
887

888
   auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
1✔
889
   auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
1✔
890

891
   auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
3✔
892
   auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
3✔
893

894
   auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
6✔
895
   std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
1✔
896

897
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
898
   root_opts.extensions.add(std::move(root_blocks));
1✔
899
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
900
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
901
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
902
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
903

904
   Botan::Certificate_Store_In_Memory trusted;
1✔
905
   trusted.add_certificate(root_cert);
1✔
906

907
   for(size_t i = 0; i < 4; i++) {
5✔
908
      bool include_v4 = i & 1;
4✔
909
      bool include_v6 = (i >> 1) & 1;
4✔
910

911
      auto dyn_ipv4_choice =
4✔
912
         IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
8✔
913
      auto dyn_ipv6_choice =
4✔
914
         IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
8✔
915

916
      auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
10✔
917
      auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
10✔
918

919
      auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
24✔
920
      std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
4✔
921

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

924
      Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
4✔
925
      Botan::X509_Certificate sub_cert =
4✔
926
         dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
4✔
927

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

931
      Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
932
      result.require("path validation succeeds", path_result.successful_validation());
4✔
933
   }
28✔
934

935
   result.end_timer();
1✔
936
   return result;
2✔
937
}
24✔
938

939
Test::Result test_x509_ip_addr_blocks_path_validation_failure() {
1✔
940
   Test::Result result("X509 IP Address Block path validation failure");
1✔
941
   result.start_timer();
1✔
942

943
   using Botan::Cert_Extension::IPAddressBlocks;
1✔
944
   auto rng = Test::new_rng(__func__);
1✔
945

946
   for(size_t i = 0; i < 7; i++) {
8✔
947
      bool all_inherit = (i == 0);
7✔
948
      bool different_safi = (i == 1);
7✔
949
      bool too_small_subrange = (i == 2);
7✔
950
      bool too_large_subrange = (i == 3);
7✔
951
      bool no_more_issuer_ranges = (i == 4);
7✔
952
      bool empty_issuer_ranges = (i == 5);
7✔
953
      bool nullptr_extensions = (i == 6);
7✔
954

955
      // Root cert
956
      std::vector<uint8_t> a = {120, 0, 0, 1};
7✔
957
      auto root_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
958
      a = {130, 140, 150, 160};
7✔
959
      auto root_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
7✔
960

961
      auto root_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_range_1_min, root_range_1_max);
7✔
962
      auto root_ranges = {root_range_1};
7✔
963
      auto root_choice =
7✔
964
         IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(root_ranges));
14✔
965
      auto root_family = IPAddressBlocks::IPAddressFamily(root_choice, 42);
20✔
966
      auto root_addr_blocks = {root_family};
21✔
967
      std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
7✔
968

969
      Botan::X509_Cert_Options root_opts = ca_opts();
7✔
970
      if(!nullptr_extensions) {
7✔
971
         root_opts.extensions.add(std::move(root_blocks));
12✔
972
      }
973
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
7✔
974

975
      // Issuer Cert
976
      a = {122, 0, 0, 255};
7✔
977
      auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
978
      a = {128, 255, 255, 255};
7✔
979
      auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
980
      auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
7✔
981

982
      std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
7✔
983

984
      if(!empty_issuer_ranges) {
7✔
985
         iss_ranges.push_back(iss_range_1);
6✔
986
      }
987

988
      auto iss_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(iss_ranges));
19✔
989
      auto iss_family = IPAddressBlocks::IPAddressFamily(iss_choice, 42);
20✔
990
      auto iss_addr_blocks = {iss_family};
21✔
991
      std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>(iss_addr_blocks);
7✔
992
      auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
14✔
993

994
      // Subject cert
995
      if(too_small_subrange) {
7✔
996
         a = {118, 0, 255, 0};
2✔
997
      } else if(no_more_issuer_ranges) {
6✔
998
         a = {140, 0, 0, 1};
2✔
999
      } else {
1000
         a = {124, 0, 255, 0};
10✔
1001
      }
1002

1003
      auto sub_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1004
      if(too_large_subrange) {
7✔
1005
         a = {134, 0, 0, 1};
2✔
1006
      } else if(no_more_issuer_ranges) {
6✔
1007
         a = {150, 0, 0, 1};
2✔
1008
      } else {
1009
         a = {126, 0, 0, 1};
10✔
1010
      }
1011
      auto sub_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
7✔
1012

1013
      auto sub_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_range_1_min, sub_range_1_max);
7✔
1014
      auto sub_ranges = {sub_range_1};
7✔
1015
      auto sub_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(sub_ranges));
14✔
1016
      auto sub_family = IPAddressBlocks::IPAddressFamily(sub_choice, different_safi ? 41 : 42);
26✔
1017

1018
      auto sub_addr_blocks = {sub_family};
21✔
1019
      std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
7✔
1020

1021
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
7✔
1022
      sub_opts.extensions.add(std::move(sub_blocks));
7✔
1023

1024
      Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
7✔
1025
      Botan::X509_Certificate sub_cert =
7✔
1026
         iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
7✔
1027

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

1031
      Botan::Certificate_Store_In_Memory trusted;
7✔
1032
      trusted.add_certificate(root_cert);
7✔
1033

1034
      Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
7✔
1035
      result.require("path validation fails", !path_result.successful_validation());
7✔
1036
   }
82✔
1037

1038
   result.end_timer();
1✔
1039
   return result;
1✔
1040
}
8✔
1041

1042
Test::Result test_x509_as_blocks_extension_encode() {
1✔
1043
   Test::Result result("X509 AS Blocks encode");
1✔
1044
   result.start_timer();
1✔
1045

1046
   using Botan::Cert_Extension::ASBlocks;
1✔
1047

1048
   auto rng = Test::new_rng(__func__);
1✔
1049

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

1052
   for(size_t i = 0; i < 16; i++) {
17✔
1053
      bool push_asnum = i & 1;
16✔
1054
      bool push_rdi = (i >> 1) & 1;
16✔
1055
      bool include_asnum = (i >> 2) & 1;
16✔
1056
      bool include_rdi = (i >> 3) & 1;
16✔
1057
      if(!include_asnum && !include_rdi) {
16✔
1058
         continue;
4✔
1059
      }
1060

1061
      std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
12✔
1062

1063
      if(include_asnum) {
12✔
1064
         if(push_asnum) {
8✔
1065
            blocks->add_asnum(0, 999);
4✔
1066
            blocks->add_asnum(5042);
4✔
1067
            blocks->add_asnum(5043, 4294967295);
4✔
1068
         } else {
1069
            blocks->restrict_asnum();
4✔
1070
         }
1071
      } else {
1072
         blocks->inherit_asnum();
4✔
1073
      }
1074

1075
      if(include_rdi) {
12✔
1076
         if(push_rdi) {
8✔
1077
            blocks->add_rdi(1234, 5678);
4✔
1078
            blocks->add_rdi(32768);
4✔
1079
            blocks->add_rdi(32769, 4294967295);
4✔
1080
         } else {
1081
            blocks->restrict_rdi();
4✔
1082
         }
1083
      } else {
1084
         blocks->inherit_rdi();
4✔
1085
      }
1086

1087
      Botan::X509_Cert_Options opts = req_opts(sig_algo);
12✔
1088
      opts.extensions.add(std::move(blocks));
12✔
1089

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

1093
      {
12✔
1094
         auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
12✔
1095
         result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
24✔
1096

1097
         const auto& identifier = as_blocks->as_identifiers();
12✔
1098

1099
         if(include_asnum) {
12✔
1100
            const auto& asnum_entries = identifier.asnum().value().ranges().value();
8✔
1101

1102
            if(push_asnum) {
8✔
1103
               result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
8✔
1104
               result.confirm("asnum entry 0 max", asnum_entries[0].max() == 999, true);
8✔
1105

1106
               result.confirm("asnum entry 1 min", asnum_entries[1].min() == 5042, true);
8✔
1107
               result.confirm("asnum entry 1 max", asnum_entries[1].max() == 4294967295, true);
8✔
1108
            } else {
1109
               result.confirm("asnum has no entries", asnum_entries.empty(), true);
8✔
1110
            }
1111
         } else {
1112
            result.confirm("asnum has no entry", identifier.asnum().value().ranges().has_value(), false);
8✔
1113
         }
1114

1115
         if(include_rdi) {
12✔
1116
            const auto& rdi_entries = identifier.rdi().value().ranges().value();
8✔
1117

1118
            if(push_rdi) {
8✔
1119
               result.confirm("rdi entry 0 min", rdi_entries[0].min() == 1234, true);
8✔
1120
               result.confirm("rdi entry 0 max", rdi_entries[0].max() == 5678, true);
8✔
1121

1122
               result.confirm("rdi entry 1 min", rdi_entries[1].min() == 32768, true);
8✔
1123
               result.confirm("rdi entry 1 max", rdi_entries[1].max() == 4294967295, true);
8✔
1124
            } else {
1125
               result.confirm("rdi has no entries", rdi_entries.empty(), true);
8✔
1126
            }
1127
         } else {
1128
            result.confirm("rdi has no entry", identifier.rdi().value().ranges().has_value(), false);
8✔
1129
         }
1130
      }
1131
   }
12✔
1132

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

1137
Test::Result test_x509_as_blocks_range_merge() {
1✔
1138
   Test::Result result("X509 AS Block range merge");
1✔
1139
   result.start_timer();
1✔
1140

1141
   using Botan::Cert_Extension::ASBlocks;
1✔
1142

1143
   auto rng = Test::new_rng(__func__);
1✔
1144

1145
   auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
1✔
1146
   Botan::X509_Cert_Options opts = req_opts(sig_algo);
1✔
1147

1148
   std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
1✔
1149

1150
   blocks->add_asnum(2005, 37005);
1✔
1151
   blocks->add_asnum(60, 70);
1✔
1152
   blocks->add_asnum(22, 50);
1✔
1153
   blocks->add_asnum(35, 2000);
1✔
1154
   blocks->add_asnum(2001, 2004);
1✔
1155
   blocks->add_asnum(21);
1✔
1156
   blocks->add_asnum(0, 20);
1✔
1157

1158
   opts.extensions.add(std::move(blocks));
1✔
1159

1160
   Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
1✔
1161
   Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1162
   {
1✔
1163
      auto as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
1✔
1164
      result.confirm("cert has ASBlock extension", as_blocks != nullptr, true);
2✔
1165

1166
      const auto& identifier = as_blocks->as_identifiers();
1✔
1167

1168
      const auto& asnum_entries = identifier.asnum().value().ranges().value();
1✔
1169

1170
      result.confirm("asnum entry 0 min", asnum_entries[0].min() == 0, true);
2✔
1171
      result.confirm("asnum entry 0 max", asnum_entries[0].max() == 37005, true);
2✔
1172
      result.confirm("asnum length", asnum_entries.size() == 1, true);
2✔
1173
   }
1174

1175
   result.end_timer();
1✔
1176
   return result;
2✔
1177
}
2✔
1178

1179
Test::Result test_x509_as_blocks_path_validation_success() {
1✔
1180
   Test::Result result("X509 AS Block path validation success");
1✔
1181
   result.start_timer();
1✔
1182

1183
   using Botan::Cert_Extension::ASBlocks;
1✔
1184
   auto rng = Test::new_rng(__func__);
1✔
1185

1186
   /*
1187
   Creates a certificate chain of length 4.
1188
   Root: both asnum and rdi
1189
   Inherit: has both values as 'inherit'
1190
   Dynamic: has either both 'inherit', both with values, or just one with a value
1191
   Subject: both asnum and rdi as a subset of Root / Dynamic
1192
   */
1193

1194
   // Root Cert, both as and rdi
1195

1196
   std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
1✔
1197

1198
   root_blocks->add_asnum(0, 999);
1✔
1199
   root_blocks->add_asnum(5042);
1✔
1200
   root_blocks->add_asnum(5043, 4294967295);
1✔
1201

1202
   root_blocks->add_rdi(1234, 5678);
1✔
1203
   root_blocks->add_rdi(32768);
1✔
1204
   root_blocks->add_rdi(32769, 4294967295);
1✔
1205

1206
   // Inherit cert, both as 'inherit'
1207
   std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>();
1✔
1208
   inherit_blocks->inherit_asnum();
1✔
1209
   inherit_blocks->inherit_rdi();
1✔
1210

1211
   // Subject cert
1212

1213
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1214

1215
   sub_blocks->add_asnum(120, 180);
1✔
1216
   sub_blocks->add_asnum(220, 240);
1✔
1217
   sub_blocks->add_asnum(260, 511);
1✔
1218
   sub_blocks->add_asnum(678);
1✔
1219
   sub_blocks->add_asnum(5043, 5100);
1✔
1220

1221
   sub_blocks->add_rdi(1500, 2300);
1✔
1222
   sub_blocks->add_rdi(2500, 4000);
1✔
1223
   sub_blocks->add_rdi(1567);
1✔
1224
   sub_blocks->add_rdi(33100, 40000);
1✔
1225

1226
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1227
   root_opts.extensions.add(std::move(root_blocks));
1✔
1228
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1229
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1230
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1231
   auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
2✔
1232

1233
   Botan::Certificate_Store_In_Memory trusted;
1✔
1234
   trusted.add_certificate(root_cert);
1✔
1235

1236
   for(size_t i = 0; i < 4; i++) {
5✔
1237
      bool include_asnum = i & 1;
4✔
1238
      bool include_rdi = (i >> 1) & 1;
4✔
1239

1240
      std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>();
4✔
1241
      if(include_asnum) {
4✔
1242
         dyn_blocks->add_asnum(100, 600);
2✔
1243
         dyn_blocks->add_asnum(678);
2✔
1244
         dyn_blocks->add_asnum(5042, 5101);
2✔
1245
      } else {
1246
         dyn_blocks->inherit_asnum();
2✔
1247
      }
1248

1249
      if(include_rdi) {
4✔
1250
         dyn_blocks->add_rdi(1500, 5000);
2✔
1251
         dyn_blocks->add_rdi(33000, 60000);
2✔
1252
      } else {
1253
         dyn_blocks->inherit_rdi();
2✔
1254
      }
1255

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

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

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

1265
      Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
4✔
1266
      result.require("path validation succeeds", path_result.successful_validation());
4✔
1267
   }
8✔
1268

1269
   result.end_timer();
1✔
1270
   return result;
2✔
1271
}
7✔
1272

1273
Test::Result test_x509_as_blocks_path_validation_extension_not_present() {
1✔
1274
   Test::Result result("X509 AS Block path validation extension not present");
1✔
1275
   result.start_timer();
1✔
1276

1277
   using Botan::Cert_Extension::ASBlocks;
1✔
1278
   auto rng = Test::new_rng(__func__);
1✔
1279

1280
   std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
1✔
1281
   sub_blocks->add_asnum(120, 180);
1✔
1282
   sub_blocks->add_asnum(220, 224);
1✔
1283
   sub_blocks->add_asnum(260, 511);
1✔
1284
   sub_blocks->add_asnum(678);
1✔
1285
   sub_blocks->add_asnum(5043, 5100);
1✔
1286

1287
   sub_blocks->add_rdi(1500, 2300);
1✔
1288
   sub_blocks->add_rdi(2500, 4000);
1✔
1289
   sub_blocks->add_rdi(1567);
1✔
1290
   sub_blocks->add_rdi(33100, 40000);
1✔
1291

1292
   // create a root ca that does not have any extension
1293
   Botan::X509_Cert_Options root_opts = ca_opts();
1✔
1294
   auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
1✔
1295
   Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
1✔
1296
   sub_opts.extensions.add(std::move(sub_blocks));
1✔
1297
   Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
1✔
1298
   Botan::X509_Certificate sub_cert = root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
1✔
1299

1300
   Botan::Certificate_Store_In_Memory trusted;
1✔
1301
   trusted.add_certificate(root_cert);
1✔
1302

1303
   const Botan::Path_Validation_Restrictions restrictions(false, 80);
2✔
1304
   const std::vector<Botan::X509_Certificate> certs = {sub_cert};
2✔
1305

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

1309
   result.end_timer();
1✔
1310
   return result;
2✔
1311
}
3✔
1312

1313
Test::Result test_x509_as_blocks_path_validation_failure() {
1✔
1314
   Test::Result result("X509 AS Block path validation failure");
1✔
1315
   result.start_timer();
1✔
1316

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

1320
   /*
1321
   This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
1322

1323
   Each test is expected to fail and creates the following certificate chain:
1324
   Root -> Issuer -> Subject
1325

1326
   00: set all the asnum choices to 'inherit' for each cert
1327
   01: 00 but for rdis
1328
   02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
1329
   03: 02 but for rdis
1330
   04: both 02 and 03
1331
   05: make largest max asnum of the subject larger than the largest max asnum of the issuer
1332
   06: 05 but for rdis
1333
   07: both 05 and 06
1334
   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
1335
   09: 08 but for rdis
1336
   10: both 08 and 09
1337
   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)
1338
   12: 11 but for rdis
1339
   13: both 11 and 12
1340
   14: 08 but using the minimum instead of the maximum
1341
   15: 14 but for rdis
1342
   16: both 14 and 15
1343
   17: same as 11 but using the minimum instead of the maximum
1344
   18: 17 but for rdis
1345
   19: both 18 and 19
1346
   20: make the issuer ranges empty but have an entry in the subject ranges
1347
   */
1348
   for(size_t i = 0; i < 21; i++) {
22✔
1349
      // enable / disable all the different edge cases
1350
      bool inherit_all_asnums = (i == 0);
21✔
1351
      bool inherit_all_rdis = (i == 1);
21✔
1352
      bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
21✔
1353
      bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
21✔
1354
      bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
21✔
1355
      bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
21✔
1356
      bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
21✔
1357
      bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
21✔
1358
      bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
21✔
1359
      bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
21✔
1360
      bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
21✔
1361
      bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
21✔
1362
      bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
21✔
1363
      bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
21✔
1364
      bool empty_issuer_non_empty_subject = (i == 20);
21✔
1365

1366
      // Root cert
1367
      std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
21✔
1368

1369
      if(!inherit_all_asnums) {
21✔
1370
         if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
20✔
1371
            // 100-200 for 02,03,04
1372
            root_blocks->add_asnum(100, 200);
4✔
1373
         } else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
16✔
1374
            // 10-20,30-40,50-60 for 08,09,10
1375
            root_blocks->add_asnum(10, 20);
4✔
1376
            root_blocks->add_asnum(30, 40);
4✔
1377
            root_blocks->add_asnum(50, 60);
4✔
1378
         } else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
12✔
1379
            // 10-20,30-50,60-70 for 11,12,13
1380
            root_blocks->add_asnum(10, 20);
4✔
1381
            root_blocks->add_asnum(30, 50);
4✔
1382
            root_blocks->add_asnum(60, 70);
4✔
1383
         }
1384
      } else {
1385
         root_blocks->inherit_asnum();
1✔
1386
      }
1387

1388
      // same values but for rdis
1389
      if(!inherit_all_rdis) {
21✔
1390
         if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
1391
            root_blocks->add_rdi(100, 200);
4✔
1392
         } else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
1393
            root_blocks->add_rdi(10, 20);
4✔
1394
            root_blocks->add_rdi(30, 40);
4✔
1395
            root_blocks->add_rdi(50, 60);
4✔
1396
         } else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
1397
            root_blocks->add_rdi(10, 20);
4✔
1398
            root_blocks->add_rdi(30, 50);
4✔
1399
            root_blocks->add_rdi(60, 70);
4✔
1400
         }
1401
      } else {
1402
         root_blocks->inherit_rdi();
1✔
1403
      }
1404

1405
      if(empty_issuer_non_empty_subject) {
21✔
1406
         root_blocks->restrict_asnum();
1✔
1407
         root_blocks->restrict_rdi();
1✔
1408
      }
1409

1410
      // Issuer cert
1411
      // the issuer cert has the same ranges as the root cert
1412
      // it is used to check that the 'inherit' check is bubbled up until the root cert is hit
1413
      auto issu_blocks = root_blocks->copy();
21✔
1414

1415
      // Subject cert
1416
      std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
21✔
1417

1418
      std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
21✔
1419
      std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
21✔
1420

1421
      if(!inherit_all_asnums) {
21✔
1422
         // assign the subject asnum ranges
1423
         if(push_asnum_min_edge_ranges) {
1424
            // 99-200 for 02 (so overlapping to the left)
1425
            sub_blocks->add_asnum(99, 200);
2✔
1426
         } else if(push_asnum_max_edge_ranges) {
1427
            // 100-201 for 03 (so overlapping to the right)
1428
            sub_blocks->add_asnum(100, 201);
2✔
1429
         } else if(push_asnum_max_middle_ranges) {
1430
            // same as root, but change the range in the middle to overlap to the right for 08
1431
            sub_blocks->add_asnum(10, 20);
2✔
1432
            sub_blocks->add_asnum(30, 41);
2✔
1433
            sub_blocks->add_asnum(50, 60);
2✔
1434
         } else if(push_asnum_max_split_ranges) {
1435
            // change the range in the middle to be cut at 45 for case 11
1436
            // the left range is 30-44
1437
            // the right range is 46-51 (overlapping the issuer range to the right)
1438
            sub_blocks->add_asnum(10, 20);
2✔
1439
            sub_blocks->add_asnum(30, 44);
2✔
1440
            sub_blocks->add_asnum(46, 51);
2✔
1441
            sub_blocks->add_asnum(60, 70);
2✔
1442
         } else if(push_asnum_min_middle_ranges) {
1443
            // just change the test in the middle to overlap to the left for case 14
1444
            sub_blocks->add_asnum(10, 20);
2✔
1445
            sub_blocks->add_asnum(29, 40);
2✔
1446
            sub_blocks->add_asnum(50, 60);
2✔
1447
         } else if(push_asnum_min_split_ranges) {
1448
            // again split the range in the middle at 45 for case 17
1449
            // creating two ranges 29-44 and 46-50 (so overlapping to the left)
1450
            sub_blocks->add_asnum(10, 20);
2✔
1451
            sub_blocks->add_asnum(29, 44);
2✔
1452
            sub_blocks->add_asnum(46, 50);
2✔
1453
            sub_blocks->add_asnum(60, 70);
2✔
1454
         } else if(empty_issuer_non_empty_subject) {
1455
            sub_blocks->add_asnum(50);
1✔
1456
         }
1457
      } else {
1458
         sub_blocks->inherit_asnum();
1✔
1459
      }
1460

1461
      if(!inherit_all_rdis) {
21✔
1462
         // same values but for rdis
1463
         if(push_rdi_min_edge_ranges) {
1464
            sub_blocks->add_rdi(99, 200);
2✔
1465
         } else if(push_rdi_max_edge_ranges) {
1466
            sub_blocks->add_rdi(100, 201);
2✔
1467
         } else if(push_rdi_max_middle_ranges) {
1468
            sub_blocks->add_rdi(10, 20);
2✔
1469
            sub_blocks->add_rdi(30, 41);
2✔
1470
            sub_blocks->add_rdi(50, 60);
2✔
1471
         } else if(push_rdi_max_split_ranges) {
1472
            sub_blocks->add_rdi(10, 20);
2✔
1473
            sub_blocks->add_rdi(30, 44);
2✔
1474
            sub_blocks->add_rdi(46, 51);
2✔
1475
            sub_blocks->add_rdi(60, 70);
2✔
1476
         } else if(push_rdi_min_middle_ranges) {
1477
            sub_blocks->add_rdi(10, 20);
2✔
1478
            sub_blocks->add_rdi(29, 40);
2✔
1479
            sub_blocks->add_rdi(50, 60);
2✔
1480
         } else if(push_rdi_min_split_ranges) {
1481
            sub_blocks->add_rdi(10, 20);
2✔
1482
            sub_blocks->add_rdi(29, 44);
2✔
1483
            sub_blocks->add_rdi(46, 50);
2✔
1484
            sub_blocks->add_rdi(60, 70);
2✔
1485
         }
1486
      } else {
1487
         sub_blocks->inherit_rdi();
1✔
1488
      }
1489

1490
      Botan::X509_Cert_Options root_opts = ca_opts();
21✔
1491
      root_opts.extensions.add(std::move(root_blocks));
21✔
1492
      auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
21✔
1493
      auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
42✔
1494

1495
      Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
21✔
1496
      sub_opts.extensions.add(std::move(sub_blocks));
21✔
1497
      Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
21✔
1498
      Botan::X509_Certificate sub_cert =
21✔
1499
         issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
21✔
1500

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

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

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

1512
   result.end_timer();
1✔
1513
   return result;
1✔
1514
}
22✔
1515

1516
class X509_RPKI_Tests final : public Test {
×
1517
   public:
1518
      std::vector<Test::Result> run() override {
1✔
1519
         std::vector<Test::Result> results;
1✔
1520

1521
   #if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
1522
         results.push_back(test_x509_ip_addr_blocks_extension_decode());
2✔
1523
         results.push_back(test_x509_as_blocks_extension_decode());
2✔
1524
   #endif
1525

1526
         results.push_back(test_x509_ip_addr_blocks_extension_encode());
2✔
1527
         results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases());
2✔
1528
         results.push_back(test_x509_ip_addr_blocks_range_merge());
2✔
1529
         results.push_back(test_x509_ip_addr_blocks_family_merge());
2✔
1530
         results.push_back(test_x509_ip_addr_blocks_path_validation_success());
2✔
1531
         results.push_back(test_x509_ip_addr_blocks_path_validation_failure());
2✔
1532
         results.push_back(test_x509_as_blocks_extension_encode());
2✔
1533
         results.push_back(test_x509_as_blocks_range_merge());
2✔
1534
         results.push_back(test_x509_as_blocks_path_validation_success());
2✔
1535
         results.push_back(test_x509_as_blocks_path_validation_extension_not_present());
2✔
1536
         results.push_back(test_x509_as_blocks_path_validation_failure());
2✔
1537
         return results;
1✔
1538
      }
×
1539
};
1540

1541
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
1542

1543
#endif
1544

1545
}  // namespace
1546

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