Revision control

Copy as Markdown

Other Tools

/*
* (C) 2025 Jack Lloyd
* (C) 2025 Anton Einax, Dominik Schricker
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#include "tests.h"
#if defined(BOTAN_HAS_X509_CERTIFICATES)
#include <botan/certstor.h>
#include <botan/pk_algs.h>
#include <botan/pubkey.h>
#include <botan/rng.h>
#include <botan/x509_ca.h>
#include <botan/x509_ext.h>
#include <botan/x509path.h>
#include <botan/x509self.h>
#include <botan/internal/calendar.h>
#endif
namespace Botan_Tests {
namespace {
#if defined(BOTAN_HAS_X509_CERTIFICATES) && \
(defined(BOTAN_HAS_ECDSA) || defined(BOTAN_HAS_RSA) || defined(BOTAN_HAS_ED25519))
struct CA_Creation_Result {
Botan::X509_Certificate ca_cert;
Botan::X509_CA ca;
std::unique_ptr<Botan::Private_Key> sub_key;
std::string sig_algo;
std::string hash_fn;
};
Botan::X509_Time from_date(const int y, const int m, const int d) {
const size_t this_year = Botan::calendar_point(std::chrono::system_clock::now()).year();
const Botan::calendar_point t(static_cast<uint32_t>(this_year + y), m, d, 0, 0, 0);
return Botan::X509_Time(t.to_std_timepoint());
}
Botan::X509_Cert_Options ca_opts(const std::string& sig_padding = "") {
Botan::X509_Cert_Options opts("Test CA/US/Botan Project/Testing");
opts.dns = "botan.randombit.net";
opts.email = "testing@randombit.net";
opts.set_padding_scheme(sig_padding);
opts.CA_key(1);
return opts;
}
Botan::X509_Cert_Options req_opts(const std::string& algo, const std::string& sig_padding = "") {
Botan::X509_Cert_Options opts("Test User 1/US/Botan Project/Testing");
opts.dns = "botan.randombit.net";
opts.email = "testing@randombit.net";
opts.set_padding_scheme(sig_padding);
opts.not_before("160101200000Z");
opts.not_after("300101200000Z");
opts.challenge = "zoom";
if(algo == "RSA") {
opts.constraints = Botan::Key_Constraints::KeyEncipherment;
} else if(algo == "DSA" || algo == "ECDSA" || algo == "ECGDSA" || algo == "ECKCDSA") {
opts.constraints = Botan::Key_Constraints::DigitalSignature;
}
return opts;
}
std::tuple<std::string, std::string, std::string, std::unique_ptr<Botan::Private_Key>>
get_sig_algo_padding_and_generate_key(Botan::RandomNumberGenerator& rng) {
const auto generate_key = [&](const std::string& alg_name, const std::string& params = "") {
auto key = Botan::create_private_key(alg_name, rng, params);
if(!key) {
throw Test_Error("Could not generate key for algorithm " + alg_name);
}
return key;
};
#if defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_SHA2_32)
// Try to find a group that is supported in this build
const auto group_name = Test::supported_ec_group_name();
if(group_name) {
return {"ECDSA", "", "SHA-256", generate_key("ECDSA", *group_name)};
}
#endif
#if defined(BOTAN_HAS_ED25519)
return {"Ed25519", "", "SHA-512", generate_key("Ed25519")};
#elif defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
return {"RSA", "PKCS1v15(SHA-256)", "SHA-256", generate_key("RSA", "1536")};
#else
throw Test_Error("No suitable signature algorithm available in this build");
#endif
}
Botan::X509_Certificate make_self_signed(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
auto [sig_algo, padding_method, hash_fn, key] = get_sig_algo_padding_and_generate_key(*rng);
return Botan::X509::create_self_signed_cert(opts, *key, hash_fn, *rng);
}
CA_Creation_Result make_ca(std::unique_ptr<Botan::RandomNumberGenerator>& rng,
const Botan::X509_Cert_Options& opts = std::move(ca_opts())) {
auto [sig_algo, padding_method, hash_fn, ca_key] = get_sig_algo_padding_and_generate_key(*rng);
const auto ca_cert = Botan::X509::create_self_signed_cert(opts, *ca_key, hash_fn, *rng);
Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, padding_method, *rng);
auto sub_key = ca_key->generate_another(*rng);
return CA_Creation_Result{ca_cert, std::move(ca), std::move(sub_key), sig_algo, hash_fn};
}
std::pair<Botan::X509_Certificate, Botan::X509_CA> make_and_sign_ca(
std::unique_ptr<Botan::Certificate_Extension> ext,
Botan::X509_CA& parent_ca,
std::unique_ptr<Botan::RandomNumberGenerator>& rng) {
auto [sig_algo, padding_method, hash_fn, key] = get_sig_algo_padding_and_generate_key(*rng);
Botan::X509_Cert_Options opts = ca_opts();
opts.extensions.add(std::move(ext));
const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *key, hash_fn, *rng);
Botan::X509_Certificate cert = parent_ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
Botan::X509_CA ca(cert, *key, hash_fn, padding_method, *rng);
return std::make_pair(std::move(cert), std::move(ca));
}
constexpr auto IPv4 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv4;
constexpr auto IPv6 = Botan::Cert_Extension::IPAddressBlocks::Version::IPv6;
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
Test::Result test_x509_ip_addr_blocks_extension_decode() {
Test::Result result("X509 IP Address Block decode");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
{
const std::string filename("IPAddrBlocksAll.pem");
const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
const auto* ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
const auto& addr_blocks = ip_addr_blocks->addr_blocks();
result.test_is_true("cert has IPAddrBlocks extension", ip_addr_blocks != nullptr);
result.test_sz_eq("cert has two IpAddrBlocks", addr_blocks.size(), 2);
const auto& ipv4block = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[0].addr_choice());
const auto& ipv6block = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[1].addr_choice());
const auto& v4_blocks = ipv4block.ranges().value();
// cert contains (in this order)
// 192.168.0.0 - 192.168.127.255 (192.168.0.0/17)
// 193.168.0.0 - 193.169.255.255 (193.168.0.0/15)
// 194.168.0.0 - 195.175.1.2
// 196.168.0.1 - 196.168.0.1 (196.168.0.1/32)
result.test_bin_eq("ipv4 block 0 min", v4_blocks[0].min().value(), {192, 168, 0, 0});
result.test_bin_eq("ipv4 block 0 max", v4_blocks[0].max().value(), {192, 168, 127, 255});
result.test_bin_eq("ipv4 block 1 min", v4_blocks[1].min().value(), {193, 168, 0, 0});
result.test_bin_eq("ipv4 block 1 max", v4_blocks[1].max().value(), {193, 169, 255, 255});
result.test_bin_eq("ipv4 block 2 min", v4_blocks[2].min().value(), {194, 168, 0, 0});
result.test_bin_eq("ipv4 block 2 max", v4_blocks[2].max().value(), {195, 175, 1, 2});
result.test_bin_eq("ipv4 block 3 min", v4_blocks[3].min().value(), {196, 168, 0, 1});
result.test_bin_eq("ipv4 block 3 max", v4_blocks[3].max().value(), {196, 168, 0, 1});
const auto& v6_blocks = ipv6block.ranges().value();
// cert contains (in this order)
// fa80::/65
// fe20::/37
// 2003:0:6829:3435:420:10c5:0:c4/128
// ab01:0:0:0:0:0:0:1-cd02:0:0:0:0:0:0:2
result.test_bin_eq(
"ipv6 block 0 min",
v6_blocks[0].min().value(),
{0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4});
result.test_bin_eq(
"ipv6 block 0 max",
v6_blocks[0].max().value(),
{0x20, 0x03, 0x00, 0x00, 0x68, 0x29, 0x34, 0x35, 0x04, 0x20, 0x10, 0xc5, 0x00, 0x00, 0x00, 0xc4});
result.test_bin_eq(
"ipv6 block 1 min",
v6_blocks[1].min().value(),
{0xab, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01});
result.test_bin_eq(
"ipv6 block 1 max",
v6_blocks[1].max().value(),
{0xcd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02});
result.test_bin_eq(
"ipv6 block 2 min",
v6_blocks[2].min().value(),
{0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
result.test_bin_eq(
"ipv6 block 2 max",
v6_blocks[2].max().value(),
{0xfa, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
result.test_bin_eq(
"ipv6 block 3 min",
v6_blocks[3].min().value(),
{0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
result.test_bin_eq(
"ipv6 block 3 max",
v6_blocks[3].max().value(),
{0xfe, 0x20, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
}
{
const std::string filename("IPAddrBlocksUnsorted.pem");
const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
const auto* ip_addr_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
// cert contains (in this order)
// IPv6 (1) inherit
// IPv6 0xff....0xff
// IPv4 (2) inherit
// IPv4 (1) 192.168.0.0 - 192.168.2.1
// IPv4 (1) 192.168.2.2 - 200.0.0.0
// IPv4 inherit
// IPv4 ranges should be merged, IPv4 should come before IPv6, all should be sorted by safi
const auto& addr_blocks = ip_addr_blocks->addr_blocks();
result.test_sz_eq("cert has two IpAddrBlocks", addr_blocks.size(), 5);
result.test_opt_u8_eq("block 0 has no safi", addr_blocks[0].safi(), std::nullopt);
result.test_is_true(
"block 0 is inherited",
!std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[0].addr_choice()).ranges().has_value());
result.test_opt_u8_eq("block 1 has correct safi", addr_blocks[1].safi(), 1);
const auto& block_1 =
std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[1].addr_choice()).ranges().value();
result.test_sz_eq("block 1 has correct size", block_1.size(), 1);
result.test_bin_eq("block 1 min is correct", block_1[0].min().value(), {192, 168, 0, 0});
result.test_bin_eq("block 1 max is correct", block_1[0].max().value(), {200, 0, 0, 0});
result.test_opt_u8_eq("block 2 has correct safi", addr_blocks[2].safi(), 2);
result.test_is_true(
"block 2 is inherited",
!std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(addr_blocks[2].addr_choice()).ranges().has_value());
result.test_opt_u8_eq("block 3 has no safi", addr_blocks[3].safi(), std::nullopt);
const auto& block_3 =
std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[3].addr_choice()).ranges().value();
result.test_sz_eq("block 3 has correct size", block_3.size(), 1);
result.test_bin_eq(
"block 3 min is correct",
block_3[0].min().value(),
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
result.test_bin_eq(
"block 3 max is correct",
block_3[0].max().value(),
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
result.test_opt_u8_eq("block 24 has correct safi", addr_blocks[4].safi(), 1);
result.test_is_true(
"block 4 is inherited",
!std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(addr_blocks[4].addr_choice()).ranges().has_value());
}
{
const std::string filename("InvalidIPAddrBlocks.pem");
const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
// cert contains the 10.0.32.0/20 prefix, but with a 9 for the unused bits
result.test_is_true("extension is present", cert.v3_extensions().extension_set(IPAddressBlocks::static_oid()));
const auto* ext = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
result.test_is_true("extension is not decoded", ext == nullptr);
}
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_extension_decode() {
Test::Result result("X509 AS Block decode");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
{
const std::string filename("ASNumberCert.pem");
const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
const auto& identifier = as_blocks->as_identifiers();
result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
const auto& asnum = identifier.asnum().value().ranges().value();
const auto& rdi = identifier.rdi().value().ranges().value();
// cert contains asnum 0-999, 5042, 0-4294967295
result.test_u32_eq("asnum entry 0 min", asnum[0].min(), 0);
result.test_u32_eq("asnum entry 0 max", asnum[0].max(), 4294967295);
// and rdi 1234-5678, 32768, 0-4294967295
result.test_u32_eq("rdi entry 0 min", rdi[0].min(), 0);
result.test_u32_eq("rdi entry 0 max", rdi[0].max(), 4294967295);
}
{
const std::string filename("ASNumberOnly.pem");
const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
const auto& identifier = as_blocks->as_identifiers();
result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
const auto& asnum = identifier.asnum().value().ranges().value();
result.test_is_false("cert has no RDI entries", identifier.rdi().has_value());
// contains 0-999, 0-4294967295
result.test_u32_eq("asnum entry 0 min", asnum[0].min(), 0);
result.test_u32_eq("asnum entry 0 max", asnum[0].max(), 4294967295);
}
{
const std::string filename("ASRdiOnly.pem");
const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
const auto& identifier = as_blocks->as_identifiers();
result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
result.test_is_false("cert has no ASNUM entries", identifier.asnum().has_value());
const auto& rdi = identifier.rdi().value().ranges().value();
// contains 1234-5678, 0-4294967295
result.test_u32_eq("rdi entry 0 min", rdi[0].min(), 0);
result.test_u32_eq("rdi entry 0 max", rdi[0].max(), 4294967295);
}
{
const std::string filename("ASNumberInherit.pem");
const Botan::X509_Certificate cert(Test::data_file("x509/x509test/" + filename));
const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
const auto& identifier = as_blocks->as_identifiers();
result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
result.test_is_false("asnum has no entries", identifier.asnum().value().ranges().has_value());
const auto& rdi = identifier.rdi().value().ranges().value();
// contains 1234-5678, 0-4294967295
result.test_u32_eq("rdi entry 0 min", rdi[0].min(), 0);
result.test_u32_eq("rdi entry 0 max", rdi[0].max(), 4294967295);
}
result.end_timer();
return result;
}
#endif
Test::Result test_x509_ip_addr_blocks_rfc3779_example() {
Test::Result result("X509 IP Address Blocks rfc3779 example");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
std::unique_ptr<IPAddressBlocks> blocks_1 = std::make_unique<IPAddressBlocks>();
blocks_1->add_address<IPv4>({10, 0, 32, 0}, {10, 0, 47, 255}, 1);
blocks_1->add_address<IPv4>({10, 0, 64, 0}, {10, 0, 64, 255}, 1);
blocks_1->add_address<IPv4>({10, 1, 0, 0}, {10, 1, 255, 255}, 1);
blocks_1->add_address<IPv4>({10, 2, 48, 0}, {10, 2, 63, 255}, 1);
blocks_1->add_address<IPv4>({10, 2, 64, 0}, {10, 2, 64, 255}, 1);
blocks_1->add_address<IPv4>({10, 3, 0, 0}, {10, 3, 255, 255}, 1);
blocks_1->inherit<IPv6>();
Botan::X509_Cert_Options opts_1 = ca_opts();
opts_1.extensions.add(std::move(blocks_1));
auto cert_1 = make_self_signed(rng, opts_1);
auto bits_1 = cert_1.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
result.test_bin_eq(
"extension is encoded as specified",
bits_1,
"3035302B040300010130240304040A00200304000A00400303000A01300C0304040A02300304000A02400303000A033006040200020500");
const auto* ext_1 = cert_1.v3_extensions().get_extension_object_as<IPAddressBlocks>();
auto ext_1_addr_fam_1 = ext_1->addr_blocks()[0];
result.test_opt_u8_eq("extension 1 ipv4 safi", ext_1_addr_fam_1.safi(), 1);
auto ext_1_ranges =
std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_1_addr_fam_1.addr_choice()).ranges().value();
result.test_bin_eq("extension 1 range 1 min", ext_1_ranges[0].min().value(), {10, 0, 32, 0});
result.test_bin_eq("extension 1 range 1 max", ext_1_ranges[0].max().value(), {10, 0, 47, 255});
result.test_bin_eq("extension 1 range 2 min", ext_1_ranges[1].min().value(), {10, 0, 64, 0});
result.test_bin_eq("extension 1 range 2 max", ext_1_ranges[1].max().value(), {10, 0, 64, 255});
result.test_bin_eq("extension 1 range 3 min", ext_1_ranges[2].min().value(), {10, 1, 0, 0});
result.test_bin_eq("extension 1 range 3 max", ext_1_ranges[2].max().value(), {10, 1, 255, 255});
result.test_bin_eq("extension 1 range 4 min", ext_1_ranges[3].min().value(), {10, 2, 48, 0});
result.test_bin_eq("extension 1 range 4 max", ext_1_ranges[3].max().value(), {10, 2, 64, 255});
result.test_bin_eq("extension 1 range 5 min", ext_1_ranges[4].min().value(), {10, 3, 0, 0});
result.test_bin_eq("extension 1 range 5 max", ext_1_ranges[4].max().value(), {10, 3, 255, 255});
result.test_opt_u8_eq("extension 1 ipv6 safi", ext_1->addr_blocks()[1].safi(), std::nullopt);
result.test_is_true(
"extension 1 ipv6 inherited",
!std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_1->addr_blocks()[1].addr_choice()).ranges().has_value());
std::unique_ptr<IPAddressBlocks> blocks_2 = std::make_unique<IPAddressBlocks>();
blocks_2->add_address<IPv6>(
{0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
blocks_2->add_address<IPv4>({10, 0, 0, 0}, {10, 255, 255, 255}, 1);
blocks_2->add_address<IPv4>({172, 16, 0, 0}, {172, 31, 255, 255}, 1);
blocks_2->inherit<IPv4>(2);
Botan::X509_Cert_Options opts_2 = ca_opts();
opts_2.extensions.add(std::move(blocks_2));
auto cert_2 = make_self_signed(rng, opts_2);
auto bits_2 = cert_2.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
// see https://www.rfc-editor.org/errata/eid6792 as to why the B0 specified in the RFC is a AC here
result.test_bin_eq("extension is encoded as specified",
bits_2,
"302C3010040300010130090302000A030304AC10300704030001020500300F040200023009030700200100000002");
const auto* ext_2 = cert_2.v3_extensions().get_extension_object_as<IPAddressBlocks>();
auto ext_2_addr_fam_1 = ext_2->addr_blocks()[0];
result.test_opt_u8_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_1.safi(), 1);
auto ext_2_ranges_1 =
std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2_addr_fam_1.addr_choice()).ranges().value();
result.test_bin_eq("extension 2 fam 1 range 1 min", ext_2_ranges_1[0].min().value(), {10, 0, 0, 0});
result.test_bin_eq("extension 2 fam 1 range 1 max", ext_2_ranges_1[0].max().value(), {10, 255, 255, 255});
result.test_bin_eq("extension 2 fam 1 range 2 min", ext_2_ranges_1[1].min().value(), {172, 16, 0, 0});
result.test_bin_eq("extension 2 fam 1 range 2 max", ext_2_ranges_1[1].max().value(), {172, 31, 255, 255});
result.test_opt_u8_eq("extension 2 ipv4 2 safi", ext_2->addr_blocks()[1].safi(), 2);
result.test_is_true(
"extension 2 ipv4 2 inherited",
!std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(ext_2->addr_blocks()[1].addr_choice()).ranges().has_value());
auto ext_2_addr_fam_3 = ext_2->addr_blocks()[2];
result.test_opt_u8_eq("extension 2 ipv4 1 safi", ext_2_addr_fam_3.safi(), std::nullopt);
auto ext_2_ranges_3 =
std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(ext_2_addr_fam_3.addr_choice()).ranges().value();
result.test_bin_eq("extension 2 fam 3 range 1 min",
ext_2_ranges_3[0].min().value(),
{0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
result.test_bin_eq("extension 2 fam 3 range 1 max",
ext_2_ranges_3[0].max().value(),
{0x20, 0x01, 0x00, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff});
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_encode_builder() {
Test::Result result("X509 IP Address Blocks encode (builder)");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>();
// 64 - 127
blocks->add_address<IPv4>({192, 168, 0b01000000, 0}, {192, 168, 0b01111111, 255}, 2);
blocks->add_address<IPv4>({255, 255, 255, 255});
// encoded as prefix
blocks->add_address<IPv4>({190, 5, 0, 0}, {190, 5, 0b01111111, 255});
// encoded as min, max
blocks->add_address<IPv4>({127, 0, 0, 1}, {189, 5, 7, 255});
// full address range
blocks->add_address<IPv4>({0, 0, 0, 0}, {255, 255, 255, 255}, 1);
blocks->add_address<IPv4>({123, 123, 2, 1});
Botan::X509_Cert_Options opts = ca_opts();
opts.extensions.add(std::move(blocks));
auto cert = make_self_signed(rng, opts);
auto bits = cert.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid());
// hand validated with https://lapo.it/asn1js/
result.test_bin_eq(
"extension is encoded as specified",
bits,
"304630290402000130230305007B7B0201300D0305007F000001030403BD0500030407BE0500030500FFFFFFFF300A04030001013003030100300D04030001023006030406C0A840");
result.end_timer();
return result;
}
namespace {
template <size_t I>
bool bit_set(size_t v) {
if(((v >> I) & 1) == 1) {
return true;
} else {
return false;
}
}
} // namespace
Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() {
Test::Result result("X509 IP Address Block encode (ctor)");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
for(size_t i = 0; i < 64; i++) {
const bool push_ipv4_ranges = bit_set<0>(i);
const bool push_ipv6_ranges = bit_set<1>(i);
const bool inherit_ipv4 = bit_set<2>(i);
const bool inherit_ipv6 = bit_set<3>(i);
const bool push_ipv4_family = bit_set<4>(i);
const bool push_ipv6_family = bit_set<5>(i);
Botan::X509_Cert_Options opts = req_opts(sig_algo);
std::vector<uint8_t> a = {123, 123, 2, 1};
auto ipv4_1 = IPAddressBlocks::IPAddress<IPv4>(a);
a = {255, 255, 255, 255};
auto ipv4_2 = IPAddressBlocks::IPAddress<IPv4>(a);
// encoded as min, max
a = {127, 0, 0, 1};
auto ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {189, 5, 7, 255};
auto ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
// encoded as prefix
a = {190, 5, 0, 0};
auto ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {190, 5, 127, 255};
auto ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
a = {0xAB, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
auto ipv6_1 = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
auto ipv6_2 = IPAddressBlocks::IPAddress<IPv6>(a);
// encoded as min, max
a = {0xAF, 0x23, 0x34, 0x45, 0x67, 0x2A, 0x7A, 0xEF, 0x8C, 0x00, 0x00, 0x00, 0x66, 0x00, 0x52, 0x00};
auto ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0xAF, 0xCD, 0xDE, 0xF0, 0x00, 0x0F, 0xEE, 0x00, 0xBB, 0x4A, 0x9B, 0x00, 0x00, 0x4C, 0x00, 0xCC};
auto ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
// encoded as prefix
a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
auto ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0xBF, 0xCD, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
auto ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
auto ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_1);
auto ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_1_min, ipv4_range_1_max);
auto ipv4_range_3 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_range_2_min, ipv4_range_2_max);
auto ipv4_range_4 = IPAddressBlocks::IPAddressOrRange<IPv4>(ipv4_2);
auto ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_1);
auto ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_1_min, ipv6_range_1_max);
auto ipv6_range_3 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_range_2_min, ipv6_range_2_max);
auto ipv6_range_4 = IPAddressBlocks::IPAddressOrRange<IPv6>(ipv6_2);
std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv4_ranges;
if(push_ipv4_ranges) {
ipv4_ranges.push_back(ipv4_range_1);
ipv4_ranges.push_back(ipv4_range_2);
ipv4_ranges.push_back(ipv4_range_3);
ipv4_ranges.push_back(ipv4_range_4);
}
std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
if(push_ipv6_ranges) {
ipv6_ranges.push_back(ipv6_range_1);
ipv6_ranges.push_back(ipv6_range_2);
ipv6_ranges.push_back(ipv6_range_3);
ipv6_ranges.push_back(ipv6_range_4);
}
auto ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
if(!inherit_ipv4) {
ipv4_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv4_ranges);
}
auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
if(!inherit_ipv6) {
ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
}
auto ipv4_addr_family = IPAddressBlocks::IPAddressFamily(ipv4_addr_choice);
auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
if(push_ipv4_family) {
addr_blocks.push_back(ipv4_addr_family);
}
if(push_ipv6_family) {
addr_blocks.push_back(ipv6_addr_family);
}
std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
opts.extensions.add(std::move(blocks));
const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
{
const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
result.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr);
const auto& dec_addr_blocks = ip_blocks->addr_blocks();
if(!push_ipv4_family && !push_ipv6_family) {
result.test_is_true("no address family entries", dec_addr_blocks.empty());
continue;
}
if(push_ipv4_family) {
auto family = dec_addr_blocks[0];
result.test_u16_eq("ipv4 family afi", ipv4_addr_family.afi(), family.afi());
result.test_opt_u8_eq("ipv4 family safi", ipv4_addr_family.safi(), family.safi());
auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
if(!inherit_ipv4) {
auto ranges = choice.ranges().value();
if(push_ipv4_ranges) {
result.test_bin_eq("ipv4 entry 0 min", ranges[0].min().value(), ipv4_range_1.min().value());
result.test_bin_eq("ipv4 entry 0 max", ranges[0].max().value(), ipv4_range_1.max().value());
result.test_bin_eq("ipv4 entry 1 min", ranges[1].min().value(), ipv4_range_2.min().value());
result.test_bin_eq("ipv4 entry 1 max", ranges[1].max().value(), ipv4_range_2.max().value());
result.test_bin_eq("ipv4 entry 2 min", ranges[2].min().value(), ipv4_range_3.min().value());
result.test_bin_eq("ipv4 entry 2 max", ranges[2].max().value(), ipv4_range_3.max().value());
result.test_bin_eq("ipv4 entry 3 min", ranges[3].min().value(), ipv4_range_4.min().value());
result.test_bin_eq("ipv4 entry 3 max", ranges[3].max().value(), ipv4_range_4.max().value());
} else {
result.test_is_true("ipv4 range has no entries", ranges.empty());
}
} else {
result.test_is_false("ipv4 family inherit", choice.ranges().has_value());
}
}
if(push_ipv6_family) {
auto family = dec_addr_blocks[dec_addr_blocks.size() - 1];
result.test_u16_eq("ipv6 family afi", ipv6_addr_family.afi(), family.afi());
result.test_opt_u8_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
if(!inherit_ipv6) {
auto ranges = choice.ranges().value();
if(push_ipv6_ranges) {
result.test_bin_eq("ipv6 entry 0 min", ranges[0].min().value(), ipv6_range_1.min().value());
result.test_bin_eq("ipv6 entry 0 max", ranges[0].max().value(), ipv6_range_1.max().value());
result.test_bin_eq("ipv6 entry 1 min", ranges[1].min().value(), ipv6_range_2.min().value());
result.test_bin_eq("ipv6 entry 1 max", ranges[1].max().value(), ipv6_range_2.max().value());
result.test_bin_eq("ipv6 entry 2 min", ranges[2].min().value(), ipv6_range_3.min().value());
result.test_bin_eq("ipv6 entry 2 max", ranges[2].max().value(), ipv6_range_3.max().value());
result.test_bin_eq("ipv6 entry 3 min", ranges[3].min().value(), ipv6_range_4.min().value());
result.test_bin_eq("ipv6 entry 3 max", ranges[3].max().value(), ipv6_range_4.max().value());
} else {
result.test_is_true("ipv6 range has no entries", ranges.empty());
}
} else {
result.test_is_false("ipv6 family inherit", choice.ranges().has_value());
}
}
}
}
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() {
Test::Result result("X509 IP Address Block encode edge cases (ctor)");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
// trailing 0s, trailing 1s, and some arbitrary values
std::vector<uint8_t> edge_values = {0, 2, 4, 8, 16, 32, 64, 128, 1, 3, 7,
15, 31, 63, 127, 255, 12, 46, 123, 160, 234};
auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
for(size_t i = 0; i < edge_values.size(); i++) {
for(size_t j = 0; j < 4; j++) {
const bool modify_min = bit_set<0>(j);
const bool modify_max = bit_set<1>(j);
for(size_t k = 0; k < 18; k++) {
if(!modify_min && !modify_max && (k > 0 || i > 0)) {
// we don't modify anything, this is the extreme edge case of 0.0 ... - 255.255. ...
// so we only need to do this once
break;
}
Botan::X509_Cert_Options opts = req_opts(sig_algo);
std::vector<uint8_t> min_bytes(16, 0x00);
std::vector<uint8_t> max_bytes(16, 0xFF);
if(modify_min) {
min_bytes[15 - (k < 2 ? 0 : k - 2)] = edge_values[i];
}
if(modify_max) {
max_bytes[15 - (k > 15 ? 15 : k)] = edge_values[i];
}
auto address_min = IPAddressBlocks::IPAddress<IPv6>(min_bytes);
auto address_max = IPAddressBlocks::IPAddress<IPv6>(max_bytes);
auto ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(address_min, address_max);
std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> ipv6_ranges;
ipv6_ranges.push_back(ipv6_range);
auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv6>(ipv6_ranges);
auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
addr_blocks.push_back(ipv6_addr_family);
std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
opts.extensions.add(std::move(blocks));
const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate cert =
ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
{
const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
result.test_not_null("cert has IPAddrBlocks extension", ip_blocks);
const auto& dec_addr_blocks = ip_blocks->addr_blocks();
auto family = dec_addr_blocks[0];
result.test_u16_eq("ipv6 family afi", ipv6_addr_family.afi(), family.afi());
result.test_opt_u8_eq("ipv6 family safi", ipv6_addr_family.safi(), family.safi());
auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(family.addr_choice());
auto ranges = choice.ranges().value();
result.test_bin_eq("ipv6 edge case min", ranges[0].min().value(), ipv6_range.min().value());
result.test_bin_eq("ipv6 edge case max", ranges[0].max().value(), ipv6_range.max().value());
}
}
}
}
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_range_merge() {
Test::Result result("X509 IP Address Block range merge");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
Botan::X509_Cert_Options opts = req_opts(sig_algo);
const std::vector<std::vector<std::vector<uint8_t>>> addresses = {
{{11, 0, 0, 0}, {{11, 0, 0, 0}}},
{{123, 123, 123, 123}, {123, 123, 123, 123}},
{{10, 4, 5, 9}, {{10, 255, 0, 0}}},
{{12, 0, 0, 0}, {191, 0, 0, 1}},
{{190, 0, 0, 0}, {193, 0, 255, 255}},
{{10, 10, 10, 10}, {10, 20, 20, 20}},
{{5, 0, 0, 0}, {10, 255, 255, 255}},
{{192, 0, 0, 0}, {192, 255, 255, 255}},
{{11, 0, 0, 1}, {11, 255, 255, 255}},
};
std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> ipv6_ranges;
for(auto pair : addresses) {
auto address_min = IPAddressBlocks::IPAddress<IPv4>(pair[0]);
auto address_max = IPAddressBlocks::IPAddress<IPv4>(pair[1]);
auto range = IPAddressBlocks::IPAddressOrRange<IPv4>(address_min, address_max);
ipv6_ranges.push_back(range);
}
auto ipv6_addr_choice = IPAddressBlocks::IPAddressChoice<IPv4>(ipv6_ranges);
auto ipv6_addr_family = IPAddressBlocks::IPAddressFamily(ipv6_addr_choice);
std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
addr_blocks.push_back(ipv6_addr_family);
std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
opts.extensions.add(std::move(blocks));
const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
{
const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
result.test_not_null("cert has IPAddrBlocks extension", ip_blocks);
const auto& dec_addr_blocks = ip_blocks->addr_blocks();
auto family = dec_addr_blocks[0];
auto choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(family.addr_choice());
auto ranges = choice.ranges().value();
const std::array<uint8_t, 4> expected_min = {5, 0, 0, 0};
const std::array<uint8_t, 4> expected_max = {193, 0, 255, 255};
result.test_bin_eq("range expected min", ranges[0].min().value(), expected_min);
result.test_bin_eq("range expected max", ranges[0].max().value(), expected_max);
result.test_sz_eq("range length", ranges.size(), 1);
}
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_family_merge() {
Test::Result result("X509 IP Address Block family merge");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
Botan::X509_Cert_Options opts = req_opts(sig_algo);
std::vector<IPAddressBlocks::IPAddressFamily> addr_blocks;
IPAddressBlocks::IPAddressChoice<IPv4> v4_empty_choice;
IPAddressBlocks::IPAddressChoice<IPv6> v6_empty_choice;
const uint8_t v4_bytes_1[4] = {123, 123, 123, 123};
const IPAddressBlocks::IPAddress<IPv4> v4_addr_1(v4_bytes_1);
// create 2 prefixes from the v4 addresses -> they should be merged
std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> v4_choice_vec{
IPAddressBlocks::IPAddressOrRange<IPv4>(IPAddressBlocks::IPAddress<IPv4>({v4_addr_1}))};
IPAddressBlocks::IPAddressChoice<IPv4> v4_choice_dupl(v4_choice_vec);
result.test_sz_eq(
"IPAddressChoice v4 merges ranges already in constructor", v4_choice_dupl.ranges().value().size(), 1);
const IPAddressBlocks::IPAddressFamily v4_fam_dupl(v4_choice_dupl, 0);
const uint8_t v6_bytes_1[16] = {123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123};
const IPAddressBlocks::IPAddress<IPv6> v6_addr_1(v6_bytes_1);
std::vector<IPAddressBlocks::IPAddressOrRange<IPv6>> v6_choice_vec{
IPAddressBlocks::IPAddressOrRange<IPv6>(IPAddressBlocks::IPAddress<IPv6>({v6_addr_1}))};
IPAddressBlocks::IPAddressChoice<IPv6> v6_choice_dupl(v6_choice_vec);
result.test_sz_eq("IPAddressChoice v6 merges already in constructor", v6_choice_dupl.ranges().value().size(), 1);
const IPAddressBlocks::IPAddressFamily v6_fam_dupl(v6_choice_dupl, 0);
const IPAddressBlocks::IPAddressFamily v4_empty_fam(v4_empty_choice);
const IPAddressBlocks::IPAddressFamily v6_empty_fam(v6_empty_choice);
const IPAddressBlocks::IPAddressFamily v4_empty_fam_safi(v4_empty_choice, 2);
const IPAddressBlocks::IPAddressFamily v6_empty_fam_safi(v6_empty_choice, 2);
/*
considering the push order, the resulting order should be
[0] v4 no safi
[2] v4 safi
[1] v6 no safi
[3] v6 safi
*/
for(size_t i = 0; i < 3; i++) {
addr_blocks.push_back(v4_empty_fam_safi);
addr_blocks.push_back(v6_empty_fam);
addr_blocks.push_back(v4_fam_dupl);
addr_blocks.push_back(v6_empty_fam_safi);
addr_blocks.push_back(v6_fam_dupl);
addr_blocks.push_back(v4_empty_fam);
}
std::vector<IPAddressBlocks::IPAddressFamily> expected_blocks = {
v4_empty_fam, v4_fam_dupl, v4_empty_fam_safi, v6_empty_fam, v6_fam_dupl, v6_empty_fam_safi};
std::unique_ptr<IPAddressBlocks> blocks = std::make_unique<IPAddressBlocks>(addr_blocks);
opts.extensions.add(std::move(blocks));
const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
const auto* ip_blocks = cert.v3_extensions().get_extension_object_as<IPAddressBlocks>();
result.test_not_null("cert has IPAddrBlocks extension", ip_blocks);
const auto& dec_blocks = ip_blocks->addr_blocks();
result.test_sz_eq("blocks got merged lengthwise", dec_blocks.size(), expected_blocks.size());
bool sorted = true;
for(size_t i = 0; i < dec_blocks.size() - 1; i++) {
const IPAddressBlocks::IPAddressFamily& a = dec_blocks[i];
const IPAddressBlocks::IPAddressFamily& b = dec_blocks[i + 1];
uint32_t afam_a = a.afi();
if(a.safi().has_value()) {
afam_a = static_cast<uint32_t>(afam_a << 8) | a.safi().value();
} else {
afam_a = static_cast<uint32_t>(afam_a << 8);
}
uint32_t afam_b = b.afi();
if(b.safi().has_value()) {
afam_b = static_cast<uint32_t>(afam_b << 8) | b.safi().value();
} else {
afam_b = static_cast<uint32_t>(afam_b << 8);
}
if(afam_a > afam_b) {
sorted = false;
break;
}
}
result.test_is_true("blocks got sorted", sorted);
for(size_t i = 0; i < dec_blocks.size(); i++) {
const IPAddressBlocks::IPAddressFamily& dec = dec_blocks[i];
const IPAddressBlocks::IPAddressFamily& exp = expected_blocks[i];
result.test_u16_eq("blocks match push order by afi at index " + std::to_string(i), dec.afi(), exp.afi());
result.test_opt_u8_eq("blocks match push order by safi at index " + std::to_string(i), dec.safi(), exp.safi());
if((exp.afi() == 1) && (dec.afi() == 1)) {
auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(dec.addr_choice());
auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv4>>(exp.addr_choice());
if(!exp_choice.ranges().has_value()) {
result.test_is_false("block ranges should inherit at index " + std::to_string(i),
dec_choice.ranges().has_value());
} else {
result.test_is_true("block ranges should not inherit at index " + std::to_string(i),
dec_choice.ranges().has_value());
if(dec_choice.ranges().has_value() == false) {
continue;
}
auto dec_ranges = dec_choice.ranges().value();
auto exp_ranges = exp_choice.ranges().value();
result.test_sz_eq("block ranges got merged lengthwise at index " + std::to_string(i),
dec_ranges.size(),
exp_ranges.size());
if(dec_ranges.size() != exp_ranges.size()) {
continue;
}
for(size_t j = 0; j < exp_ranges.size(); j++) {
result.test_bin_eq(
"block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
exp_ranges[j].min().value(),
dec_ranges[j].min().value());
result.test_bin_eq(
"block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
exp_ranges[j].max().value(),
dec_ranges[j].max().value());
}
}
} else if((exp.afi() == 2) && (dec.afi() == 2)) {
auto dec_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(dec.addr_choice());
auto exp_choice = std::get<IPAddressBlocks::IPAddressChoice<IPv6>>(exp.addr_choice());
if(!exp_choice.ranges().has_value()) {
result.test_is_false("block ranges should inherit at index " + std::to_string(i),
dec_choice.ranges().has_value());
} else {
result.test_is_true("block ranges should not inherit at index " + std::to_string(i),
dec_choice.ranges().has_value());
if(dec_choice.ranges().has_value() == false) {
continue;
}
auto dec_ranges = dec_choice.ranges().value();
auto exp_ranges = exp_choice.ranges().value();
result.test_sz_eq("block ranges got merged lengthwise at index " + std::to_string(i),
dec_ranges.size(),
exp_ranges.size());
if(dec_ranges.size() != exp_ranges.size()) {
continue;
}
for(size_t j = 0; j < exp_ranges.size(); j++) {
result.test_bin_eq(
"block ranges min got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
exp_ranges[j].min().value(),
dec_ranges[j].min().value());
result.test_bin_eq(
"block ranges max got merged valuewise at indices " + std::to_string(i) + "," + std::to_string(j),
exp_ranges[j].max().value(),
dec_ranges[j].max().value());
}
}
}
}
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() {
Test::Result result("X509 IP Address Blocks path validation success (builder)");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
/*
Creates a certificate chain of length 4.
Root: ipv4 and ipv6
Inherit: has both values as 'inherit'
Dynamic: has either both 'inherit', both with values, or just one with a value
Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
*/
// Root cert
std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
root_blocks->add_address<IPv4>({10, 0, 0, 1}, {10, 255, 255, 255}, 42);
root_blocks->add_address<IPv6>(
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
root_blocks->add_address<IPv6>(
{0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
// Inherit cert
std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>();
inherit_blocks->inherit<IPv4>(42);
inherit_blocks->inherit<IPv6>();
// Subject cert
std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, 42);
sub_blocks->add_address<IPv4>({10, 0, 2, 1}, {10, 42, 0, 255}, 42);
sub_blocks->add_address<IPv6>(
{0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
Botan::X509_Cert_Options root_opts = ca_opts();
root_opts.extensions.add(std::move(root_blocks));
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
for(size_t i = 0; i < 4; i++) {
const bool include_v4 = bit_set<0>(i);
const bool include_v6 = bit_set<1>(i);
// Dynamic Cert
std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>();
if(include_v4) {
dyn_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
dyn_blocks->add_address<IPv4>({10, 0, 0, 255}, {10, 255, 0, 1}, 42);
} else {
dyn_blocks->inherit<IPv4>(42);
}
if(include_v6) {
dyn_blocks->add_address<IPv6>(
{0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
{0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
} else {
dyn_blocks->inherit<IPv6>();
}
auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation succeeds", path_result.successful_validation());
}
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() {
Test::Result result("X509 IP Address Block path validation success (ctor)");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
/*
Creates a certificate chain of length 4.
Root: ipv4 and ipv6
Inherit: has both values as 'inherit'
Dynamic: has either both 'inherit', both with values, or just one with a value
Subject: both ipv4 and ipv6 as a subset of Root / Dynamic
*/
// Root cert
std::vector<uint8_t> a = {120, 0, 0, 1};
auto root_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
a = {130, 140, 150, 160};
auto root_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
a = {10, 0, 0, 1};
auto root_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {10, 255, 255, 255};
auto root_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
a = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
auto root_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0xA0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
auto root_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
auto root_ipv6_range_2_min = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
auto root_ipv6_range_2_max = IPAddressBlocks::IPAddress<IPv6>(a);
auto root_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_1_min, root_ipv4_range_1_max);
auto root_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_ipv4_range_2_min, root_ipv4_range_2_max);
auto root_ipv6_range_1 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_1_min, root_ipv6_range_1_max);
auto root_ipv6_range_2 = IPAddressBlocks::IPAddressOrRange<IPv6>(root_ipv6_range_2_min, root_ipv6_range_2_max);
auto root_ipv4_ranges = {root_ipv4_range_1, root_ipv4_range_2};
auto root_ipv6_ranges = {root_ipv6_range_1, root_ipv6_range_2};
auto root_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(root_ipv4_ranges);
auto root_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(root_ipv6_ranges);
auto root_ipv4_family = IPAddressBlocks::IPAddressFamily(root_ipv4_choice, 42);
auto root_ipv6_family = IPAddressBlocks::IPAddressFamily(root_ipv6_choice);
auto root_addr_blocks = {root_ipv4_family, root_ipv6_family};
std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
// Inherit cert
auto inherit_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>();
auto inherit_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>();
auto inherit_ipv4_family = IPAddressBlocks::IPAddressFamily(inherit_ipv4_choice, 42);
auto inherit_ipv6_family = IPAddressBlocks::IPAddressFamily(inherit_ipv6_choice);
auto inherit_addr_blocks = {inherit_ipv4_family, inherit_ipv6_family};
std::unique_ptr<IPAddressBlocks> inherit_blocks = std::make_unique<IPAddressBlocks>(inherit_addr_blocks);
// Dynamic Cert
a = {122, 0, 0, 255};
auto dyn_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {128, 255, 255, 255};
auto dyn_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
a = {10, 0, 0, 255};
auto dyn_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {10, 255, 0, 1};
auto dyn_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
a = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
auto dyn_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
auto dyn_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
auto dyn_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_1_min, dyn_ipv4_range_1_max);
auto dyn_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(dyn_ipv4_range_2_min, dyn_ipv4_range_2_max);
auto dyn_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(dyn_ipv6_range_1_min, dyn_ipv6_range_1_max);
auto dyn_ipv4_ranges = {dyn_ipv4_range_1, dyn_ipv4_range_2};
auto dyn_ipv6_ranges = {dyn_ipv6_range};
// Subject cert
a = {124, 0, 255, 0};
auto sub_ipv4_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {126, 0, 0, 1};
auto sub_ipv4_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
a = {10, 0, 2, 1};
auto sub_ipv4_range_2_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {10, 42, 0, 255};
auto sub_ipv4_range_2_max = IPAddressBlocks::IPAddress<IPv4>(a);
a = {0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
auto sub_ipv6_range_1_min = IPAddressBlocks::IPAddress<IPv6>(a);
a = {0x0D, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
auto sub_ipv6_range_1_max = IPAddressBlocks::IPAddress<IPv6>(a);
auto sub_ipv4_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_1_min, sub_ipv4_range_1_max);
auto sub_ipv4_range_2 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_ipv4_range_2_min, sub_ipv4_range_2_max);
auto sub_ipv6_range = IPAddressBlocks::IPAddressOrRange<IPv6>(sub_ipv6_range_1_min, sub_ipv6_range_1_max);
auto sub_ipv4_ranges = {sub_ipv4_range_1, sub_ipv4_range_2};
auto sub_ipv6_ranges = {sub_ipv6_range};
auto sub_ipv4_choice = IPAddressBlocks::IPAddressChoice<IPv4>(sub_ipv4_ranges);
auto sub_ipv6_choice = IPAddressBlocks::IPAddressChoice<IPv6>(sub_ipv6_ranges);
auto sub_ipv4_family = IPAddressBlocks::IPAddressFamily(sub_ipv4_choice, 42);
auto sub_ipv6_family = IPAddressBlocks::IPAddressFamily(sub_ipv6_choice);
auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family};
std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
Botan::X509_Cert_Options root_opts = ca_opts();
root_opts.extensions.add(std::move(root_blocks));
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
for(size_t i = 0; i < 4; i++) {
const bool include_v4 = bit_set<0>(i);
const bool include_v6 = bit_set<1>(i);
auto dyn_ipv4_choice =
IPAddressBlocks::IPAddressChoice<IPv4>(include_v4 ? std::optional(dyn_ipv4_ranges) : std::nullopt);
auto dyn_ipv6_choice =
IPAddressBlocks::IPAddressChoice<IPv6>(include_v6 ? std::optional(dyn_ipv6_ranges) : std::nullopt);
auto dyn_ipv4_family = IPAddressBlocks::IPAddressFamily(dyn_ipv4_choice, 42);
auto dyn_ipv6_family = IPAddressBlocks::IPAddressFamily(dyn_ipv6_choice);
auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family};
std::unique_ptr<IPAddressBlocks> dyn_blocks = std::make_unique<IPAddressBlocks>(dyn_addr_blocks);
auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation succeeds", path_result.successful_validation());
}
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() {
Test::Result result("X509 IP Address Blocks path validation failure (builder)");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
for(size_t i = 0; i < 7; i++) {
const bool all_inherit = (i == 0);
const bool different_safi = (i == 1);
const bool too_small_subrange = (i == 2);
const bool too_large_subrange = (i == 3);
const bool no_more_issuer_ranges = (i == 4);
const bool empty_issuer_ranges = (i == 5);
const bool nullptr_extensions = (i == 6);
// Root cert
std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>();
if(!all_inherit) {
root_blocks->add_address<IPv4>({120, 0, 0, 1}, {130, 140, 150, 160}, 42);
} else {
root_blocks->inherit<IPv4>(42);
}
Botan::X509_Cert_Options root_opts = ca_opts();
if(!nullptr_extensions) {
root_opts.extensions.add(std::move(root_blocks));
}
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
// Issuer Cert
std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>();
if(!all_inherit) {
if(empty_issuer_ranges) {
iss_blocks->restrict<IPv4>(42);
} else {
iss_blocks->add_address<IPv4>({122, 0, 0, 255}, {128, 255, 255, 255}, 42);
}
} else {
iss_blocks->inherit<IPv4>(42);
}
auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
// Subject cert
std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>();
uint8_t safi = different_safi ? 41 : 42;
if(!all_inherit) {
if(too_small_subrange) {
sub_blocks->add_address<IPv4>({118, 0, 255, 0}, {126, 0, 0, 1}, safi);
} else if(too_large_subrange) {
sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {134, 0, 0, 1}, safi);
} else if(no_more_issuer_ranges) {
sub_blocks->add_address<IPv4>({140, 0, 0, 1}, {150, 0, 0, 1}, safi);
} else {
sub_blocks->add_address<IPv4>({124, 0, 255, 0}, {126, 0, 0, 1}, safi);
}
} else {
sub_blocks->inherit<IPv4>(safi);
}
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, iss_cert};
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation fails", !path_result.successful_validation());
}
result.end_timer();
return result;
}
Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() {
Test::Result result("X509 IP Address Block path validation failure (ctor)");
result.start_timer();
using Botan::Cert_Extension::IPAddressBlocks;
auto rng = Test::new_rng(__func__);
for(size_t i = 0; i < 7; i++) {
const bool all_inherit = (i == 0);
const bool different_safi = (i == 1);
const bool too_small_subrange = (i == 2);
const bool too_large_subrange = (i == 3);
const bool no_more_issuer_ranges = (i == 4);
const bool empty_issuer_ranges = (i == 5);
const bool nullptr_extensions = (i == 6);
// Root cert
std::vector<uint8_t> a = {120, 0, 0, 1};
auto root_range_1_min = IPAddressBlocks::IPAddress<IPv4>{a};
a = {130, 140, 150, 160};
auto root_range_1_max = IPAddressBlocks::IPAddress<IPv4>{a};
auto root_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(root_range_1_min, root_range_1_max);
auto root_ranges = {root_range_1};
auto root_choice =
IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(root_ranges));
auto root_family = IPAddressBlocks::IPAddressFamily(root_choice, 42);
auto root_addr_blocks = {root_family};
std::unique_ptr<IPAddressBlocks> root_blocks = std::make_unique<IPAddressBlocks>(root_addr_blocks);
Botan::X509_Cert_Options root_opts = ca_opts();
if(!nullptr_extensions) {
root_opts.extensions.add(std::move(root_blocks));
}
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
// Issuer Cert
a = {122, 0, 0, 255};
auto iss_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
a = {128, 255, 255, 255};
auto iss_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
auto iss_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(iss_range_1_min, iss_range_1_max);
std::vector<IPAddressBlocks::IPAddressOrRange<IPv4>> iss_ranges;
if(!empty_issuer_ranges) {
iss_ranges.push_back(iss_range_1);
}
auto iss_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(iss_ranges));
auto iss_family = IPAddressBlocks::IPAddressFamily(iss_choice, 42);
auto iss_addr_blocks = {iss_family};
std::unique_ptr<IPAddressBlocks> iss_blocks = std::make_unique<IPAddressBlocks>(iss_addr_blocks);
auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng);
// Subject cert
if(too_small_subrange) {
a = {118, 0, 255, 0};
} else if(no_more_issuer_ranges) {
a = {140, 0, 0, 1};
} else {
a = {124, 0, 255, 0};
}
auto sub_range_1_min = IPAddressBlocks::IPAddress<IPv4>(a);
if(too_large_subrange) {
a = {134, 0, 0, 1};
} else if(no_more_issuer_ranges) {
a = {150, 0, 0, 1};
} else {
a = {126, 0, 0, 1};
}
auto sub_range_1_max = IPAddressBlocks::IPAddress<IPv4>(a);
auto sub_range_1 = IPAddressBlocks::IPAddressOrRange<IPv4>(sub_range_1_min, sub_range_1_max);
auto sub_ranges = {sub_range_1};
auto sub_choice = IPAddressBlocks::IPAddressChoice<IPv4>(all_inherit ? std::nullopt : std::optional(sub_ranges));
auto sub_family = IPAddressBlocks::IPAddressFamily(sub_choice, different_safi ? 41 : 42);
auto sub_addr_blocks = {sub_family};
std::unique_ptr<IPAddressBlocks> sub_blocks = std::make_unique<IPAddressBlocks>(sub_addr_blocks);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
iss_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, iss_cert};
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation fails", !path_result.successful_validation());
}
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_rfc3779_example() {
Test::Result result("X509 AS Blocks rfc3779 example");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
blocks->add_asnum(135);
blocks->add_asnum(3000, 3999);
blocks->add_asnum(5001);
blocks->inherit_rdi();
Botan::X509_Cert_Options opts = ca_opts();
opts.extensions.add(std::move(blocks));
auto cert = make_self_signed(rng, opts);
auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
result.test_bin_eq(
"extension is encoded as specified", bits, "301AA014301202020087300802020BB802020F9F02021389A1020500");
auto as_idents = cert.v3_extensions().get_extension_object_as<ASBlocks>()->as_identifiers();
auto as_ids = as_idents.asnum().value().ranges().value();
result.test_u32_eq("extension has correct data", as_ids[0].min(), 135);
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_encode_builder() {
Test::Result result("X509 IP Address Blocks encode (builder)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>();
blocks->add_rdi(10);
blocks->add_rdi(20, 30);
blocks->add_rdi(42, 300);
blocks->add_rdi(9, 301);
blocks->inherit_asnum();
blocks->add_asnum(20);
// this overwrites the previous two
blocks->restrict_asnum();
Botan::X509_Cert_Options opts = ca_opts();
opts.extensions.add(std::move(blocks));
auto cert = make_self_signed(rng, opts);
auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid());
result.test_bin_eq("extension is encoded as specified", bits, "3011A0023000A10B300930070201090202012D");
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_extension_encode_ctor() {
Test::Result result("X509 AS Blocks encode (ctor)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
for(size_t i = 0; i < 16; i++) {
const bool push_asnum = bit_set<0>(i);
const bool push_rdi = bit_set<1>(i);
const bool include_asnum = bit_set<2>(i);
const bool include_rdi = bit_set<3>(i);
if(!include_asnum && !include_rdi) {
continue;
}
const ASBlocks::ASIdOrRange asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
const ASBlocks::ASIdOrRange asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
const ASBlocks::ASIdOrRange asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
const ASBlocks::ASIdOrRange rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
const ASBlocks::ASIdOrRange rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
const ASBlocks::ASIdOrRange rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
std::vector<ASBlocks::ASIdOrRange> as_ranges;
if(push_asnum) {
as_ranges.push_back(asnum_id_or_range0);
as_ranges.push_back(asnum_id_or_range1);
as_ranges.push_back(asnum_id_or_range2);
}
std::vector<ASBlocks::ASIdOrRange> rdi_ranges;
if(push_rdi) {
rdi_ranges.push_back(rdi_id_or_range0);
rdi_ranges.push_back(rdi_id_or_range1);
rdi_ranges.push_back(rdi_id_or_range2);
}
ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
ASBlocks::ASIdentifierChoice rdi = ASBlocks::ASIdentifierChoice(rdi_ranges);
const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(include_asnum ? std::optional(asnum) : std::nullopt,
include_rdi ? std::optional(rdi) : std::nullopt);
std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
Botan::X509_Cert_Options opts = req_opts(sig_algo);
opts.extensions.add(std::move(blocks));
const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
{
const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
const auto& identifier = as_blocks->as_identifiers();
if(include_asnum) {
const auto& asnum_entries = identifier.asnum().value().ranges().value();
if(push_asnum) {
result.test_u32_eq("asnum entry 0 min", asnum_entries[0].min(), 0);
result.test_u32_eq("asnum entry 0 max", asnum_entries[0].max(), 999);
result.test_u32_eq("asnum entry 1 min", asnum_entries[1].min(), 5042);
result.test_u32_eq("asnum entry 1 max", asnum_entries[1].max(), 4294967295);
} else {
result.test_is_true("asnum has no entries", asnum_entries.empty());
}
} else {
result.test_is_false("no asnum entry", identifier.asnum().has_value());
}
if(include_rdi) {
const auto& rdi_entries = identifier.rdi().value().ranges().value();
if(push_rdi) {
result.test_u32_eq("rdi entry 0 min", rdi_entries[0].min(), 1234);
result.test_u32_eq("rdi entry 0 max", rdi_entries[0].max(), 5678);
result.test_u32_eq("rdi entry 1 min", rdi_entries[1].min(), 32768);
result.test_u32_eq("rdi entry 1 max", rdi_entries[1].max(), 4294967295);
} else {
result.test_is_true("rdi has no entries", rdi_entries.empty());
}
} else {
result.test_is_false("rdi has no entry", identifier.rdi().has_value());
}
}
}
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_range_merge() {
Test::Result result("X509 AS Block range merge");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng);
Botan::X509_Cert_Options opts = req_opts(sig_algo);
const std::vector<std::vector<uint16_t>> ranges = {
{2005, 37005},
{60, 70},
{22, 50},
{35, 2000},
{2001, 2004},
{21, 21},
{0, 20},
};
std::vector<ASBlocks::ASIdOrRange> as_ranges;
as_ranges.reserve(ranges.size());
for(const auto& pair : ranges) {
as_ranges.emplace_back(pair[0], pair[1]);
}
ASBlocks::ASIdentifierChoice asnum = ASBlocks::ASIdentifierChoice(as_ranges);
const ASBlocks::ASIdentifiers ident = ASBlocks::ASIdentifiers(std::optional(asnum), std::nullopt);
std::unique_ptr<ASBlocks> blocks = std::make_unique<ASBlocks>(ident);
opts.extensions.add(std::move(blocks));
const Botan::PKCS10_Request req = Botan::X509::create_cert_req(opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate cert = ca.sign_request(req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
{
const auto* as_blocks = cert.v3_extensions().get_extension_object_as<ASBlocks>();
result.test_is_true("cert has ASBlock extension", as_blocks != nullptr);
const auto& identifier = as_blocks->as_identifiers();
const auto& asnum_entries = identifier.asnum().value().ranges().value();
result.test_u32_eq("asnum entry 0 min", asnum_entries[0].min(), 0);
result.test_u32_eq("asnum entry 0 max", asnum_entries[0].max(), 37005);
result.test_sz_eq("asnum length", asnum_entries.size(), 1);
}
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_path_validation_success_builder() {
Test::Result result("X509 AS Block path validation success (builder)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
/*
Creates a certificate chain of length 4.
Root: both asnum and rdi
Inherit: has both values as 'inherit'
Dynamic: has either both 'inherit', both with values, or just one with a value
Subject: both asnum and rdi as a subset of Root / Dynamic
*/
// Root Cert, both as and rdi
std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
root_blocks->add_asnum(0, 999);
root_blocks->add_asnum(5042);
root_blocks->add_asnum(5043, 4294967295);
root_blocks->add_rdi(1234, 5678);
root_blocks->add_rdi(32768);
root_blocks->add_rdi(32769, 4294967295);
// Inherit cert, both as 'inherit'
std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>();
inherit_blocks->inherit_asnum();
inherit_blocks->inherit_rdi();
// Subject cert
std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
sub_blocks->add_asnum(120, 180);
sub_blocks->add_asnum(220, 240);
sub_blocks->add_asnum(260, 511);
sub_blocks->add_asnum(678);
sub_blocks->add_asnum(5043, 5100);
sub_blocks->add_rdi(1500, 2300);
sub_blocks->add_rdi(2500, 4000);
sub_blocks->add_rdi(1567);
sub_blocks->add_rdi(33100, 40000);
Botan::X509_Cert_Options root_opts = ca_opts();
root_opts.extensions.add(std::move(root_blocks));
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
for(size_t i = 0; i < 4; i++) {
const bool include_asnum = bit_set<0>(i);
const bool include_rdi = bit_set<1>(i);
std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>();
if(include_asnum) {
dyn_blocks->add_asnum(100, 600);
dyn_blocks->add_asnum(678);
dyn_blocks->add_asnum(5042, 5101);
} else {
dyn_blocks->inherit_asnum();
}
if(include_rdi) {
dyn_blocks->add_rdi(1500, 5000);
dyn_blocks->add_rdi(33000, 60000);
} else {
dyn_blocks->inherit_rdi();
}
auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation succeeds", path_result.successful_validation());
}
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_path_validation_success_ctor() {
Test::Result result("X509 AS Block path validation success (ctor)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
/*
Creates a certificate chain of length 4.
Root: both asnum and rdi
Inherit: has both values as 'inherit'
Dynamic: has either both 'inherit', both with values, or just one with a value
Subject: both asnum and rdi as a subset of Root / Dynamic
*/
// Root Cert, both as and rdi
const ASBlocks::ASIdOrRange root_asnum_id_or_range0 = ASBlocks::ASIdOrRange(0, 999);
const ASBlocks::ASIdOrRange root_asnum_id_or_range1 = ASBlocks::ASIdOrRange(5042);
const ASBlocks::ASIdOrRange root_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5043, 4294967295);
const ASBlocks::ASIdOrRange root_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1234, 5678);
const ASBlocks::ASIdOrRange root_rdi_id_or_range1 = ASBlocks::ASIdOrRange(32768);
const ASBlocks::ASIdOrRange root_rdi_id_or_range2 = ASBlocks::ASIdOrRange(32769, 4294967295);
std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
root_as_ranges.push_back(root_asnum_id_or_range0);
root_as_ranges.push_back(root_asnum_id_or_range1);
root_as_ranges.push_back(root_asnum_id_or_range2);
std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
root_rdi_ranges.push_back(root_rdi_id_or_range0);
root_rdi_ranges.push_back(root_rdi_id_or_range1);
root_rdi_ranges.push_back(root_rdi_id_or_range2);
ASBlocks::ASIdentifierChoice root_asnum = ASBlocks::ASIdentifierChoice(root_as_ranges);
ASBlocks::ASIdentifierChoice root_rdi = ASBlocks::ASIdentifierChoice(root_rdi_ranges);
const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
// Inherit cert, both as 'inherit'
ASBlocks::ASIdentifierChoice inherit_asnum = ASBlocks::ASIdentifierChoice();
ASBlocks::ASIdentifierChoice inherit_rdi = ASBlocks::ASIdentifierChoice();
const ASBlocks::ASIdentifiers inherit_ident = ASBlocks::ASIdentifiers(inherit_asnum, inherit_rdi);
std::unique_ptr<ASBlocks> inherit_blocks = std::make_unique<ASBlocks>(inherit_ident);
// Dynamic cert
const ASBlocks::ASIdOrRange dyn_asnum_id_or_range0 = ASBlocks::ASIdOrRange(100, 600);
const ASBlocks::ASIdOrRange dyn_asnum_id_or_range1 = ASBlocks::ASIdOrRange(678);
const ASBlocks::ASIdOrRange dyn_asnum_id_or_range2 = ASBlocks::ASIdOrRange(5042, 5101);
const ASBlocks::ASIdOrRange dyn_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 5000);
const ASBlocks::ASIdOrRange dyn_rdi_id_or_range1 = ASBlocks::ASIdOrRange(33000, 60000);
std::vector<ASBlocks::ASIdOrRange> dyn_as_ranges;
dyn_as_ranges.push_back(dyn_asnum_id_or_range0);
dyn_as_ranges.push_back(dyn_asnum_id_or_range1);
dyn_as_ranges.push_back(dyn_asnum_id_or_range2);
std::vector<ASBlocks::ASIdOrRange> dyn_rdi_ranges;
dyn_rdi_ranges.push_back(dyn_rdi_id_or_range0);
dyn_rdi_ranges.push_back(dyn_rdi_id_or_range1);
// Subject cert
const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
sub_as_ranges.push_back(sub_asnum_id_or_range0);
sub_as_ranges.push_back(sub_asnum_id_or_range1);
sub_as_ranges.push_back(sub_asnum_id_or_range2);
sub_as_ranges.push_back(sub_asnum_id_or_range3);
sub_as_ranges.push_back(sub_asnum_id_or_range4);
std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
Botan::X509_Cert_Options root_opts = ca_opts();
root_opts.extensions.add(std::move(root_blocks));
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
auto [inherit_cert, inherit_ca] = make_and_sign_ca(std::move(inherit_blocks), root_ca, rng);
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
for(size_t i = 0; i < 4; i++) {
const bool include_asnum = bit_set<0>(i);
const bool include_rdi = bit_set<1>(i);
ASBlocks::ASIdentifierChoice dyn_asnum =
ASBlocks::ASIdentifierChoice(include_asnum ? std::optional(dyn_as_ranges) : std::nullopt);
ASBlocks::ASIdentifierChoice dyn_rdi =
ASBlocks::ASIdentifierChoice(include_rdi ? std::optional(dyn_rdi_ranges) : std::nullopt);
const ASBlocks::ASIdentifiers dyn_ident = ASBlocks::ASIdentifiers(dyn_asnum, dyn_rdi);
std::unique_ptr<ASBlocks> dyn_blocks = std::make_unique<ASBlocks>(dyn_ident);
auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng);
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
dyn_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, dyn_cert, inherit_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation succeeds", path_result.successful_validation());
}
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_path_validation_extension_not_present_builder() {
Test::Result result("X509 AS Block path validation extension not present (builder)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
sub_blocks->add_asnum(120, 180);
sub_blocks->add_asnum(220, 224);
sub_blocks->add_asnum(260, 511);
sub_blocks->add_asnum(678);
sub_blocks->add_asnum(5043, 5100);
sub_blocks->add_rdi(1500, 2300);
sub_blocks->add_rdi(2500, 4000);
sub_blocks->add_rdi(1567);
sub_blocks->add_rdi(33100, 40000);
// create a root ca that does not have any extension
const Botan::X509_Cert_Options root_opts = ca_opts();
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation fails", !path_result.successful_validation());
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_path_validation_extension_not_present_ctor() {
Test::Result result("X509 AS Block path validation extension not present (ctor)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
// Subject cert
const ASBlocks::ASIdOrRange sub_asnum_id_or_range0 = ASBlocks::ASIdOrRange(120, 180);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range1 = ASBlocks::ASIdOrRange(220, 240);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range2 = ASBlocks::ASIdOrRange(260, 511);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range3 = ASBlocks::ASIdOrRange(678);
const ASBlocks::ASIdOrRange sub_asnum_id_or_range4 = ASBlocks::ASIdOrRange(5043, 5100);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range0 = ASBlocks::ASIdOrRange(1500, 2300);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range1 = ASBlocks::ASIdOrRange(2500, 4000);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range2 = ASBlocks::ASIdOrRange(1567);
const ASBlocks::ASIdOrRange sub_rdi_id_or_range3 = ASBlocks::ASIdOrRange(33100, 40000);
std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
sub_as_ranges.push_back(sub_asnum_id_or_range0);
sub_as_ranges.push_back(sub_asnum_id_or_range1);
sub_as_ranges.push_back(sub_asnum_id_or_range2);
sub_as_ranges.push_back(sub_asnum_id_or_range3);
sub_as_ranges.push_back(sub_asnum_id_or_range4);
std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
sub_rdi_ranges.push_back(sub_rdi_id_or_range0);
sub_rdi_ranges.push_back(sub_rdi_id_or_range1);
sub_rdi_ranges.push_back(sub_rdi_id_or_range2);
sub_rdi_ranges.push_back(sub_rdi_id_or_range3);
ASBlocks::ASIdentifierChoice sub_asnum = ASBlocks::ASIdentifierChoice(sub_as_ranges);
ASBlocks::ASIdentifierChoice sub_rdi = ASBlocks::ASIdentifierChoice(sub_rdi_ranges);
const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
// create a root ca that does not have any extension
const Botan::X509_Cert_Options root_opts = ca_opts();
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
root_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
result.require("path validation fails", !path_result.successful_validation());
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_path_validation_failure_builder() {
Test::Result result("X509 AS Block path validation failure (builder)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
/*
This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
Each test is expected to fail and creates the following certificate chain:
Root -> Issuer -> Subject
00: set all the asnum choices to 'inherit' for each cert
01: 00 but for rdis
02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
03: 02 but for rdis
04: both 02 and 03
05: make largest max asnum of the subject larger than the largest max asnum of the issuer
06: 05 but for rdis
07: both 05 and 06
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
09: 08 but for rdis
10: both 08 and 09
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)
12: 11 but for rdis
13: both 11 and 12
14: 08 but using the minimum instead of the maximum
15: 14 but for rdis
16: both 14 and 15
17: same as 11 but using the minimum instead of the maximum
18: 17 but for rdis
19: both 18 and 19
20: make the issuer ranges empty but have an entry in the subject ranges
*/
for(size_t i = 0; i < 21; i++) {
// enable / disable all the different edge cases
const bool inherit_all_asnums = (i == 0);
const bool inherit_all_rdis = (i == 1);
const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
const bool empty_issuer_non_empty_subject = (i == 20);
// Root cert
std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>();
if(!inherit_all_asnums) {
if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
// 100-200 for 02,03,04
root_blocks->add_asnum(100, 200);
} else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
// 10-20,30-40,50-60 for 08,09,10
root_blocks->add_asnum(10, 20);
root_blocks->add_asnum(30, 40);
root_blocks->add_asnum(50, 60);
} else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
// 10-20,30-50,60-70 for 11,12,13
root_blocks->add_asnum(10, 20);
root_blocks->add_asnum(30, 50);
root_blocks->add_asnum(60, 70);
}
} else {
root_blocks->inherit_asnum();
}
// same values but for rdis
if(!inherit_all_rdis) {
if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
root_blocks->add_rdi(100, 200);
} else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
root_blocks->add_rdi(10, 20);
root_blocks->add_rdi(30, 40);
root_blocks->add_rdi(50, 60);
} else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
root_blocks->add_rdi(10, 20);
root_blocks->add_rdi(30, 50);
root_blocks->add_rdi(60, 70);
}
} else {
root_blocks->inherit_rdi();
}
if(empty_issuer_non_empty_subject) {
root_blocks->restrict_asnum();
root_blocks->restrict_rdi();
}
// Issuer cert
// the issuer cert has the same ranges as the root cert
// it is used to check that the 'inherit' check is bubbled up until the root cert is hit
auto issu_blocks = root_blocks->copy();
// Subject cert
std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>();
if(!inherit_all_asnums) {
// assign the subject asnum ranges
if(push_asnum_min_edge_ranges) {
// 99-200 for 02 (so overlapping to the left)
sub_blocks->add_asnum(99, 200);
} else if(push_asnum_max_edge_ranges) {
// 100-201 for 03 (so overlapping to the right)
sub_blocks->add_asnum(100, 201);
} else if(push_asnum_max_middle_ranges) {
// same as root, but change the range in the middle to overlap to the right for 08
sub_blocks->add_asnum(10, 20);
sub_blocks->add_asnum(30, 41);
sub_blocks->add_asnum(50, 60);
} else if(push_asnum_max_split_ranges) {
// change the range in the middle to be cut at 45 for case 11
// the left range is 30-44
// the right range is 46-51 (overlapping the issuer range to the right)
sub_blocks->add_asnum(10, 20);
sub_blocks->add_asnum(30, 44);
sub_blocks->add_asnum(46, 51);
sub_blocks->add_asnum(60, 70);
} else if(push_asnum_min_middle_ranges) {
// just change the test in the middle to overlap to the left for case 14
sub_blocks->add_asnum(10, 20);
sub_blocks->add_asnum(29, 40);
sub_blocks->add_asnum(50, 60);
} else if(push_asnum_min_split_ranges) {
// again split the range in the middle at 45 for case 17
// creating two ranges 29-44 and 46-50 (so overlapping to the left)
sub_blocks->add_asnum(10, 20);
sub_blocks->add_asnum(29, 44);
sub_blocks->add_asnum(46, 50);
sub_blocks->add_asnum(60, 70);
} else if(empty_issuer_non_empty_subject) {
sub_blocks->add_asnum(50);
}
} else {
sub_blocks->inherit_asnum();
}
if(!inherit_all_rdis) {
// same values but for rdis
if(push_rdi_min_edge_ranges) {
sub_blocks->add_rdi(99, 200);
} else if(push_rdi_max_edge_ranges) {
sub_blocks->add_rdi(100, 201);
} else if(push_rdi_max_middle_ranges) {
sub_blocks->add_rdi(10, 20);
sub_blocks->add_rdi(30, 41);
sub_blocks->add_rdi(50, 60);
} else if(push_rdi_max_split_ranges) {
sub_blocks->add_rdi(10, 20);
sub_blocks->add_rdi(30, 44);
sub_blocks->add_rdi(46, 51);
sub_blocks->add_rdi(60, 70);
} else if(push_rdi_min_middle_ranges) {
sub_blocks->add_rdi(10, 20);
sub_blocks->add_rdi(29, 40);
sub_blocks->add_rdi(50, 60);
} else if(push_rdi_min_split_ranges) {
sub_blocks->add_rdi(10, 20);
sub_blocks->add_rdi(29, 44);
sub_blocks->add_rdi(46, 50);
sub_blocks->add_rdi(60, 70);
}
} else {
sub_blocks->inherit_rdi();
}
Botan::X509_Cert_Options root_opts = ca_opts();
root_opts.extensions.add(std::move(root_blocks));
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, issu_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
// in all cases, the validation should fail, since we are creating invalid scenarios
result.test_is_true("path validation fails at iteration " + std::to_string(i),
!path_result.successful_validation());
}
result.end_timer();
return result;
}
Test::Result test_x509_as_blocks_path_validation_failure_ctor() {
Test::Result result("X509 AS Block path validation failure (ctor)");
result.start_timer();
using Botan::Cert_Extension::ASBlocks;
auto rng = Test::new_rng(__func__);
/*
This executes a few permutations, messing around with edge cases when it comes to constructing ranges.
Each test is expected to fail and creates the following certificate chain:
Root -> Issuer -> Subject
00: set all the asnum choices to 'inherit' for each cert
01: 00 but for rdis
02: make smallest min asnum of the subject smaller than the smallest min asnum of the issuer
03: 02 but for rdis
04: both 02 and 03
05: make largest max asnum of the subject larger than the largest max asnum of the issuer
06: 05 but for rdis
07: both 05 and 06
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
09: 08 but for rdis
10: both 08 and 09
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)
12: 11 but for rdis
13: both 11 and 12
14: 08 but using the minimum instead of the maximum
15: 14 but for rdis
16: both 14 and 15
17: same as 11 but using the minimum instead of the maximum
18: 17 but for rdis
19: both 18 and 19
20: make the issuer ranges empty but have an entry in the subject ranges
*/
for(size_t i = 0; i < 21; i++) {
// enable / disable all the different edge cases
const bool inherit_all_asnums = (i == 0);
const bool inherit_all_rdis = (i == 1);
const bool push_asnum_min_edge_ranges = (i == 2) || (i == 4);
const bool push_rdi_min_edge_ranges = (i == 3) || (i == 4);
const bool push_asnum_max_edge_ranges = (i == 5) || (i == 7);
const bool push_rdi_max_edge_ranges = (i == 6) || (i == 7);
const bool push_asnum_max_middle_ranges = (i == 8) || (i == 10);
const bool push_rdi_max_middle_ranges = (i == 9) || (i == 10);
const bool push_asnum_max_split_ranges = (i == 11) || (i == 13);
const bool push_rdi_max_split_ranges = (i == 12) || (i == 13);
const bool push_asnum_min_middle_ranges = (i == 14) || (i == 16);
const bool push_rdi_min_middle_ranges = (i == 15) || (i == 16);
const bool push_asnum_min_split_ranges = (i == 17) || (i == 19);
const bool push_rdi_min_split_ranges = (i == 18) || (i == 19);
const bool empty_issuer_non_empty_subject = (i == 20);
// Root cert
std::vector<ASBlocks::ASIdOrRange> root_as_ranges;
std::vector<ASBlocks::ASIdOrRange> root_rdi_ranges;
// assign the root ranges
if(push_asnum_min_edge_ranges || push_asnum_max_edge_ranges) {
// 100-200 for 02,03,04
root_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
} else if(push_asnum_max_middle_ranges || push_asnum_min_middle_ranges) {
// 10-20,30-40,50-60 for 08,09,10
root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
root_as_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
} else if(push_asnum_max_split_ranges || push_asnum_min_split_ranges) {
// 10-20,30-50,60-70 for 11,12,13
root_as_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
root_as_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
root_as_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
}
// same values but for rdis
if(push_rdi_min_edge_ranges || push_rdi_max_edge_ranges) {
root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 200));
} else if(push_rdi_max_middle_ranges || push_rdi_min_middle_ranges) {
root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 40));
root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(50, 60));
} else if(push_rdi_max_split_ranges || push_rdi_min_split_ranges) {
root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(10, 20));
root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(30, 50));
root_rdi_ranges.push_back(ASBlocks::ASIdOrRange(60, 70));
}
// Issuer cert
// the issuer cert has the same ranges as the root cert
// it is used to check that the 'inherit' check is bubbled up until the root cert is hit
const std::vector<ASBlocks::ASIdOrRange> issu_as_ranges;
const std::vector<ASBlocks::ASIdOrRange> issu_rdi_ranges;
// Subject cert
std::vector<ASBlocks::ASIdOrRange> sub_as_ranges;
std::vector<ASBlocks::ASIdOrRange> sub_rdi_ranges;
// assign the subject asnum ranges
if(push_asnum_min_edge_ranges) {
// 99-200 for 02 (so overlapping to the left)
sub_as_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
} else if(push_asnum_max_edge_ranges) {
// 100-201 for 03 (so overlapping to the right)
sub_as_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
} else if(push_asnum_max_middle_ranges) {
// just change the range in the middle to overlap to the right for 08
sub_as_ranges = root_as_ranges;
sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
} else if(push_asnum_max_split_ranges) {
// change the range in the middle to be cut at 45 for case 11
// the left range is 30-44
// the right range is 46-51 (overlapping the issuer range to the right)
sub_as_ranges = root_as_ranges;
sub_as_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
// pushing the new range created by splitting to the back since they will be sorted anyway
sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
} else if(push_asnum_min_middle_ranges) {
// just change the test in the middle to overlap to the left for case 14
sub_as_ranges = root_as_ranges;
sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
} else if(push_asnum_min_split_ranges) {
// again split the range in the middle at 45 for case 17
// creating two ranges 29-44 and 46-50 (so overlapping to the left)
sub_as_ranges = root_as_ranges;
sub_as_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
sub_as_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
} else if(empty_issuer_non_empty_subject) {
sub_as_ranges.push_back(ASBlocks::ASIdOrRange(50));
}
// same values but for rdis
if(push_rdi_min_edge_ranges) {
sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(99, 200));
} else if(push_rdi_max_edge_ranges) {
sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(100, 201));
} else if(push_rdi_max_middle_ranges) {
sub_rdi_ranges = root_rdi_ranges;
sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 41);
} else if(push_rdi_max_split_ranges) {
sub_rdi_ranges = root_rdi_ranges;
sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(30, 44);
sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 51));
} else if(push_rdi_min_middle_ranges) {
sub_rdi_ranges = root_rdi_ranges;
sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 40);
} else if(push_rdi_min_split_ranges) {
sub_rdi_ranges = root_rdi_ranges;
sub_rdi_ranges[1] = ASBlocks::ASIdOrRange(29, 44);
sub_rdi_ranges.push_back(ASBlocks::ASIdOrRange(46, 50));
}
// for cases 00 and 01, set all certs to inherit (so std::nullopt)
// in all other cases use the ranges created beforehand
ASBlocks::ASIdentifierChoice root_asnum =
ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(root_as_ranges));
ASBlocks::ASIdentifierChoice root_rdi =
ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(root_rdi_ranges));
const ASBlocks::ASIdentifiers root_ident = ASBlocks::ASIdentifiers(root_asnum, root_rdi);
std::unique_ptr<ASBlocks> root_blocks = std::make_unique<ASBlocks>(root_ident);
const ASBlocks::ASIdentifiers& issu_ident = root_ident;
std::unique_ptr<ASBlocks> issu_blocks = std::make_unique<ASBlocks>(issu_ident);
ASBlocks::ASIdentifierChoice sub_asnum =
ASBlocks::ASIdentifierChoice(inherit_all_asnums ? std::nullopt : std::optional(sub_as_ranges));
ASBlocks::ASIdentifierChoice sub_rdi =
ASBlocks::ASIdentifierChoice(inherit_all_rdis ? std::nullopt : std::optional(sub_rdi_ranges));
const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi);
std::unique_ptr<ASBlocks> sub_blocks = std::make_unique<ASBlocks>(sub_ident);
Botan::X509_Cert_Options root_opts = ca_opts();
root_opts.extensions.add(std::move(root_blocks));
auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts);
auto [issu_cert, issu_ca] = make_and_sign_ca(std::move(issu_blocks), root_ca, rng);
Botan::X509_Cert_Options sub_opts = req_opts(sig_algo);
sub_opts.extensions.add(std::move(sub_blocks));
const Botan::PKCS10_Request sub_req = Botan::X509::create_cert_req(sub_opts, *sub_key, hash_fn, *rng);
const Botan::X509_Certificate sub_cert =
issu_ca.sign_request(sub_req, *rng, from_date(-1, 01, 01), from_date(2, 01, 01));
Botan::Certificate_Store_In_Memory trusted;
trusted.add_certificate(root_cert);
const Botan::Path_Validation_Restrictions restrictions(false, 80);
const std::vector<Botan::X509_Certificate> certs = {sub_cert, issu_cert};
const Botan::Path_Validation_Result path_result = Botan::x509_path_validate(certs, restrictions, trusted);
// in all cases, the validation should fail, since we are creating invalid scenarios
result.test_is_true("path validation fails at iteration " + std::to_string(i),
!path_result.successful_validation());
}
result.end_timer();
return result;
}
class X509_RPKI_Tests final : public Test {
public:
std::vector<Test::Result> run() override {
std::vector<Test::Result> results;
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
results.push_back(test_x509_ip_addr_blocks_extension_decode());
results.push_back(test_x509_as_blocks_extension_decode());
#endif
results.push_back(test_x509_ip_addr_blocks_rfc3779_example());
results.push_back(test_x509_ip_addr_blocks_encode_builder());
results.push_back(test_x509_ip_addr_blocks_extension_encode_ctor());
results.push_back(test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor());
results.push_back(test_x509_ip_addr_blocks_range_merge());
results.push_back(test_x509_ip_addr_blocks_family_merge());
results.push_back(test_x509_ip_addr_blocks_path_validation_success_builder());
results.push_back(test_x509_ip_addr_blocks_path_validation_success_ctor());
results.push_back(test_x509_ip_addr_blocks_path_validation_failure_builder());
results.push_back(test_x509_ip_addr_blocks_path_validation_failure_ctor());
results.push_back(test_x509_as_blocks_rfc3779_example());
results.push_back(test_x509_as_blocks_encode_builder());
results.push_back(test_x509_as_blocks_extension_encode_ctor());
results.push_back(test_x509_as_blocks_range_merge());
results.push_back(test_x509_as_blocks_path_validation_success_builder());
results.push_back(test_x509_as_blocks_path_validation_success_ctor());
results.push_back(test_x509_as_blocks_path_validation_extension_not_present_builder());
results.push_back(test_x509_as_blocks_path_validation_extension_not_present_ctor());
results.push_back(test_x509_as_blocks_path_validation_failure_builder());
results.push_back(test_x509_as_blocks_path_validation_failure_ctor());
return results;
}
};
BOTAN_REGISTER_TEST("x509", "x509_rpki", X509_RPKI_Tests);
#endif
} // namespace
} // namespace Botan_Tests