diff --git a/src/cli/perf_x509.cpp b/src/cli/perf_x509.cpp index 7656de98e95..17491cf35ef 100644 --- a/src/cli/perf_x509.cpp +++ b/src/cli/perf_x509.cpp @@ -15,12 +15,14 @@ #include #include #include + #include + #include #include #include #include + #include #include #include - #include #endif namespace Botan_CLI { @@ -44,14 +46,23 @@ class PerfTest_ASN1_Parsing final : public PerfTest { } static CA create_ca(Botan::RandomNumberGenerator& rng) { - auto root_cert_options = Botan::X509_Cert_Options("Benchmark Root/DE/RS/CS"); - root_cert_options.dns = "unobtainium.example.com"; - root_cert_options.email = "idont@exist.com"; - root_cert_options.is_CA = true; - auto root_key = create_private_key(rng); BOTAN_ASSERT_NONNULL(root_key); - auto root_cert = Botan::X509::create_self_signed_cert(root_cert_options, *root_key, get_hash_function(), rng); + + Botan::CertificateParametersBuilder root_cert_params; + root_cert_params.add_common_name("Benchmark Root") + .add_country("DE") + .add_organization("RS") + .add_organizational_unit("CS") + .add_dns(Botan::DNSName::from_string("unobtainium.example.com").value()) + .add_email(Botan::EmailAddress::from_string("idont@exist.com").value()) + .set_as_ca_certificate(); + + const auto not_before = std::chrono::system_clock::now(); + const auto not_after = not_before + std::chrono::seconds(86400); + + auto root_cert = + root_cert_params.into_self_signed_cert(not_before, not_after, *root_key, rng, get_hash_function()); auto ca = Botan::X509_CA(root_cert, *root_key, get_hash_function(), rng); return CA{ diff --git a/src/lib/utils/types.h b/src/lib/utils/types.h index b76399e21ce..9872d823625 100644 --- a/src/lib/utils/types.h +++ b/src/lib/utils/types.h @@ -86,7 +86,7 @@ namespace Botan { * TLS::Client, TLS::Server, TLS::Policy, TLS::Protocol_Version, TLS::Callbacks, TLS::Ciphersuite, * TLS::Session, TLS::Session_Summary, TLS::Session_Manager, Credentials_Manager *
X.509
-* X509_Certificate, X509_CRL, X509_CA, Certificate_Extension, PKCS10_Request, X509_Cert_Options, +* X509_Certificate, X509_CRL, X509_CA, Certificate_Extension, PKCS10_Request, CertificateParametersBuilder, * Certificate_Store, Certificate_Store_In_SQL, Certificate_Store_In_SQLite *
eXtendable Output Functions
* @ref Ascon_XOF128 "Ascon-XOF128", @ref SHAKE_XOF "SHAKE" diff --git a/src/lib/x509/info.txt b/src/lib/x509/info.txt index 0cc33ae80ff..f937764771d 100644 --- a/src/lib/x509/info.txt +++ b/src/lib/x509/info.txt @@ -26,6 +26,7 @@ ocsp.h pkcs10.h pkix_enums.h pkix_types.h +x509_builder.h x509_ca.h x509_crl.h x509_ext.h diff --git a/src/lib/x509/pkix_enums.h b/src/lib/x509/pkix_enums.h index 689b24a6cb9..fdd7bdf6dbb 100644 --- a/src/lib/x509/pkix_enums.h +++ b/src/lib/x509/pkix_enums.h @@ -180,6 +180,8 @@ class BOTAN_PUBLIC_API(3, 0) Key_Constraints final { void operator|=(Key_Constraints::Bits other) { m_value |= other; } + void operator|=(Key_Constraints other) { m_value |= other.m_value; } + // Return true if all bits in mask are set bool includes(Key_Constraints::Bits other) const { return (m_value & other) == other; } diff --git a/src/lib/x509/x509_builder.cpp b/src/lib/x509/x509_builder.cpp new file mode 100644 index 00000000000..6c06c3b1239 --- /dev/null +++ b/src/lib/x509/x509_builder.cpp @@ -0,0 +1,304 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +class CertificateParametersBuilder::State final { + public: + const X509_DN& subject_dn() const { return m_subject_dn; } + + Extensions finalize_extensions(const Public_Key& key) const { + auto extensions = m_extensions; + + extensions.replace(setup_alt_name(extensions)); + + extensions.add_new( + std::make_unique(m_is_ca_request, m_path_limit.value_or(0)), true); + + const Key_Constraints usage = this->usage(); + + if(!usage.empty()) { + if(!usage.compatible_with(key)) { + throw Invalid_Argument("The requested key usage is incompatible with the algorithm"); + } + + extensions.add_new(std::make_unique(usage), true); + } + + if(!m_ext_usage.empty()) { + extensions.add_new(std::make_unique(m_ext_usage)); + } + + return extensions; + } + + Key_Constraints usage() const { + if(m_is_ca_request) { + return Key_Constraints::ca_constraints(); + } else { + return m_usage; + } + } + + void add_common_name(std::string_view cn) { add_subject_dn("common_name", "X520.CommonName", cn); } + + void add_country(std::string_view country) { add_subject_dn("country", "X520.Country", country); } + + void add_state(std::string_view state) { add_subject_dn("state", "X520.State", state); } + + void add_locality(std::string_view locality) { add_subject_dn("locality", "X520.Locality", locality); } + + void add_serial_number(std::string_view sn) { add_subject_dn("serial_number", "X520.SerialNumber", sn); } + + void add_organization(std::string_view org) { add_subject_dn("organization", "X520.Organization", org); } + + void add_organizational_unit(std::string_view org_unit) { + add_subject_dn("organizational_unit", "X520.OrganizationalUnit", org_unit); + } + + void add_extension(std::unique_ptr extn, bool is_critical) { + if(!m_extensions.add_new(std::move(extn), is_critical)) { + throw Invalid_Argument("CertificateParametersBuilder::add_extension: cannot add same extension twice"); + } + } + + void add_email(const EmailAddress& email) { m_email.push_back(email); } + + void add_dns(const DNSName& dns) { m_dns.push_back(dns); } + + void add_uri(const URI& uri) { m_uri.push_back(uri); } + + void add_xmpp(std::string_view xmpp) { m_xmpp.emplace_back(xmpp); } + + void add_ipv4(const IPv4Address& ipv4) { m_ipv4.push_back(ipv4); } + + void add_ipv6(const IPv6Address& ipv6) { m_ipv6.push_back(ipv6); } + + void add_allowed_usage(Key_Constraints usage) { m_usage |= usage; } + + void add_allowed_extended_usage(const OID& usage) { m_ext_usage.push_back(usage); } + + void set_as_ca_certificate(std::optional path_limit) { + if(m_is_ca_request) { + throw Invalid_State("CertificateParametersBuilder::set_as_ca_certificate cannot be called twice"); + } else { + m_is_ca_request = true; + m_path_limit = path_limit; + } + } + + private: + void add_subject_dn(std::string_view fn_suffix, std::string_view attr, std::string_view value) { + const auto oid = OID::from_string(attr); + const size_t ub = X509_DN::lookup_ub(oid); + + if(value.empty()) { + throw Invalid_Argument(fmt("CertificateParametersBuilder::add_{}: empty name is prohibited", fn_suffix)); + } + + if(value.size() > ub) { + throw Invalid_Argument( + fmt("CertificateParametersBuilder::add_{}: name exceeds maximum allowed length ({}) for this type", + fn_suffix, + ub)); + } + m_subject_dn.add_attribute(oid, value); + } + + std::unique_ptr setup_alt_name(const Extensions& extensions) const { + AlternativeName subject_alt; + + /* + If the extension was already created in extensions we need to merge the + values provided with the extension value + */ + if(const auto* ext = extensions.get_extension_object_as()) { + subject_alt = ext->get_alt_name(); + } + + for(const auto& dns : m_dns) { + subject_alt.add_dns(dns.name()); + } + for(const auto& uri : m_uri) { + subject_alt.add_uri(uri.original_input()); + } + for(const auto& email : m_email) { + subject_alt.add_email(email.to_string()); + } + for(const auto& xmpp : m_xmpp) { + subject_alt.add_other_name(OID::from_string("PKIX.XMPPAddr"), ASN1_String(xmpp, ASN1_Type::Utf8String)); + } + for(const auto& ipv4 : m_ipv4) { + subject_alt.add_ipv4_address(ipv4); + } + for(const auto& ipv6 : m_ipv6) { + subject_alt.add_ipv6_address(ipv6); + } + + return std::make_unique(subject_alt); + } + + X509_DN m_subject_dn; + Extensions m_extensions; + Key_Constraints m_usage; + std::vector m_email; + std::vector m_dns; + std::vector m_uri; + std::vector m_xmpp; + std::vector m_ipv4; + std::vector m_ipv6; + std::vector m_ext_usage; + bool m_is_ca_request = false; + std::optional m_path_limit; +}; + +CertificateParametersBuilder::CertificateParametersBuilder() : + m_state(std::make_unique()) {} + +CertificateParametersBuilder::CertificateParametersBuilder(CertificateParametersBuilder&& other) noexcept = default; + +CertificateParametersBuilder::~CertificateParametersBuilder() = default; + +X509_Certificate CertificateParametersBuilder::into_self_signed_cert(std::chrono::system_clock::time_point not_before, + std::chrono::system_clock::time_point not_after, + const Private_Key& key, + RandomNumberGenerator& rng, + std::optional hash_fn, + std::optional padding) const { + auto signer_p = X509_Object::choose_sig_format(key, rng, hash_fn.value_or(""), padding.value_or("")); + auto& signer = *signer_p; + + const AlgorithmIdentifier sig_algo = signer.algorithm_identifier(); + BOTAN_ASSERT_NOMSG(sig_algo.oid().has_value()); + + Extensions extensions = m_state->finalize_extensions(key); + + const std::vector pub_key = key.subject_public_key(); + auto skid = std::make_unique(pub_key, signer.hash_function()); + + extensions.add_new(std::make_unique(skid->get_key_id())); + extensions.add_new(std::move(skid)); + + const auto& subject_dn = m_state->subject_dn(); + + return X509_CA::make_cert( + signer, rng, sig_algo, pub_key, X509_Time(not_before), X509_Time(not_after), subject_dn, subject_dn, extensions); +} + +PKCS10_Request CertificateParametersBuilder::into_pkcs10_request( + const Private_Key& key, + RandomNumberGenerator& rng, + std::optional hash_fn, + std::optional padding, + std::optional challenge_password) const { + const auto& subject_dn = m_state->subject_dn(); + + const auto extensions = m_state->finalize_extensions(key); + + return PKCS10_Request::create( + key, subject_dn, extensions, hash_fn.value_or(""), rng, padding.value_or(""), challenge_password.value_or("")); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_common_name(std::string_view cn) { + m_state->add_common_name(cn); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_country(std::string_view country) { + m_state->add_country(country); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_organization(std::string_view org) { + m_state->add_organization(org); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_organizational_unit(std::string_view org_unit) { + m_state->add_organizational_unit(org_unit); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_locality(std::string_view locality) { + m_state->add_locality(locality); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_state(std::string_view state) { + m_state->add_state(state); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_serial_number(std::string_view serial) { + m_state->add_serial_number(serial); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_email(const EmailAddress& email) { + m_state->add_email(email); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_uri(const URI& uri) { + m_state->add_uri(uri); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_dns(const DNSName& dns) { + m_state->add_dns(dns); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_ipv4(const IPv4Address& ipv4) { + m_state->add_ipv4(ipv4); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_ipv6(const IPv6Address& ipv6) { + m_state->add_ipv6(ipv6); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_xmpp(std::string_view xmpp) { + m_state->add_xmpp(xmpp); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_allowed_usage(Key_Constraints kc) { + m_state->add_allowed_usage(kc); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_allowed_extended_usage(const OID& usage) { + m_state->add_allowed_extended_usage(usage); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::add_extension(std::unique_ptr extn, + bool is_critical) { + m_state->add_extension(std::move(extn), is_critical); + return (*this); +} + +CertificateParametersBuilder& CertificateParametersBuilder::set_as_ca_certificate(std::optional path_limit) { + m_state->set_as_ca_certificate(path_limit); + return (*this); +} + +} // namespace Botan diff --git a/src/lib/x509/x509_builder.h b/src/lib/x509/x509_builder.h new file mode 100644 index 00000000000..57f809de3c6 --- /dev/null +++ b/src/lib/x509/x509_builder.h @@ -0,0 +1,195 @@ +/* +* (C) 2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_X509_CERT_PARAM_BUILDER_H_ +#define BOTAN_X509_CERT_PARAM_BUILDER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +class Certificate_Extension; +class DNSName; +class EmailAddress; +class IPv4Address; +class IPv6Address; +class OID; +class Private_Key; +class RandomNumberGenerator; +class URI; + +/** +* Certificate Metadata Builder +* +* Allows incrementally forming metadata included in a certificate or PKCS10 request. +* +* The various add_ methods each return a reference to this, allowing for convenient method chaining. +*/ +class BOTAN_PUBLIC_API(3, 9) CertificateParametersBuilder final { + public: + CertificateParametersBuilder(); + + CertificateParametersBuilder(const CertificateParametersBuilder& other) = delete; + CertificateParametersBuilder& operator=(const CertificateParametersBuilder& other) = delete; + + CertificateParametersBuilder(CertificateParametersBuilder&& other) noexcept; + CertificateParametersBuilder& operator=(CertificateParametersBuilder&& other) = delete; + ~CertificateParametersBuilder(); + + /** + * Create a self-signed X.509 certificate from these parameters. + * + * @param not_before the initial validity time of the generated certificate + * @param not_after the final validity time of the generated certificate + * @param key the private key + * @param rng the rng to use + * @param hash_fn the hash function to use; if unset a reasonable default is used + * @param padding optional padding scheme (for specifying RSA-PSS vs RSA-PKCS1, + * otherwise not necessary) + * + * @return newly created self-signed certificate + */ + X509_Certificate into_self_signed_cert(std::chrono::system_clock::time_point not_before, + std::chrono::system_clock::time_point not_after, + const Private_Key& key, + RandomNumberGenerator& rng, + std::optional hash_fn = {}, + std::optional padding = {}) const; + + /** + * Create a PKCS10 request + * + * @param key the private key + * @param rng the rng to use + * @param hash_fn the hash function to use; if unset a reasonable default is used + * @param padding optional padding scheme (for specifying RSA-PSS vs RSA-PKCS1, + * otherwise not necessary) + * @param challenge_password an optional challenge password included in the PKCS10 request + * (originally intended as a preshared key to allow authenticating a + * later revocation request, but now obsolete in most contexts) + * + * @return newly created self-signed certificate + */ + PKCS10_Request into_pkcs10_request(const Private_Key& key, + RandomNumberGenerator& rng, + std::optional hash_fn = {}, + std::optional padding = {}, + std::optional challenge_password = {}) const; + + /** + * Add an additional common name to the certificate metadata + */ + CertificateParametersBuilder& add_common_name(std::string_view cn); + + /** + * Add an additional country name to the certificate metadata + */ + CertificateParametersBuilder& add_country(std::string_view country); + + /** + * Add an additional organization name to the certificate metadata + */ + CertificateParametersBuilder& add_organization(std::string_view org); + + /** + * Add an additional organization unit name to the certificate metadata + */ + CertificateParametersBuilder& add_organizational_unit(std::string_view org_unit); + + /** + * Add an additional locality name to the certificate metadata + */ + CertificateParametersBuilder& add_locality(std::string_view locality); + + /** + * Add an additional state name to the certificate metadata + */ + CertificateParametersBuilder& add_state(std::string_view state); + + /** + * Add an additional serial number to the certificate metadata + * + * Note this is the X.520 serial number included in the DN, and has nothing + * to do with the serial number of the issued certificate. + */ + CertificateParametersBuilder& add_serial_number(std::string_view serial); + + /** + * Add an additional email address to the certificate metadata + */ + CertificateParametersBuilder& add_email(const EmailAddress& email); + + /** + * Add an additional URI to the certificate metadata + */ + CertificateParametersBuilder& add_uri(const URI& uri); + + /** + * Add an additional DNS name to the certificate metadata + */ + CertificateParametersBuilder& add_dns(const DNSName& dns); + + /** + * Add an additional IPv4 address to the certificate metadata + */ + CertificateParametersBuilder& add_ipv4(const IPv4Address& ipv4); + + /** + * Add an additional IPv6 address to the certificate metadata + */ + CertificateParametersBuilder& add_ipv6(const IPv6Address& ipv6); + + /** + * Add a XMPP name to the certificate metadata + */ + CertificateParametersBuilder& add_xmpp(std::string_view xmpp); + + /** + * Add another allowed usage to the KeyUsage extension + * + * This is cummulative; all usages indicated are ORed together + */ + CertificateParametersBuilder& add_allowed_usage(Key_Constraints kc); + + /** + * Add another allowed usage to the ExtendedKeyUsage extension + */ + CertificateParametersBuilder& add_allowed_extended_usage(const OID& usage); + + /** + * Add an arbitrary certificate extension + * + * An extension can be added at most once. If the same extension is added a second + * time (even with identical contents) an exception will be thrown. + */ + CertificateParametersBuilder& add_extension(std::unique_ptr extn, + bool is_critical = false); + + /** + * Set the parameters as being for a CA certificate + * + * If the path limit is specified then the BasicConstraints extension will include + * that value as the maximum chain length issuable by the resulting cert. + * + * It is not allowed to call set_as_ca_certificate more than once; further calls will + * result in an Invalid_State exception. + */ + CertificateParametersBuilder& set_as_ca_certificate(std::optional path_limit = {}); + + private: + class State; + std::unique_ptr m_state; +}; + +} // namespace Botan + +#endif diff --git a/src/lib/x509/x509opt.cpp b/src/lib/x509/x509opt.cpp index f045c5cfd22..1e7e74debf3 100644 --- a/src/lib/x509/x509opt.cpp +++ b/src/lib/x509/x509opt.cpp @@ -7,6 +7,11 @@ #include +#include +#include +#include +#include +#include #include #include @@ -92,4 +97,90 @@ X509_Cert_Options::X509_Cert_Options(std::string_view initial_opts, uint32_t exp } } +CertificateParametersBuilder X509_Cert_Options::into_builder() const { + auto builder = CertificateParametersBuilder(); + if(!this->common_name.empty()) { + builder.add_common_name(this->common_name); + } + if(!this->country.empty()) { + builder.add_country(this->country); + } + if(!this->organization.empty()) { + builder.add_organization(this->organization); + } + if(!this->org_unit.empty()) { + builder.add_organizational_unit(this->org_unit); + } + for(const auto& ou : this->more_org_units) { + if(!ou.empty()) { + builder.add_organizational_unit(ou); + } + } + if(!this->locality.empty()) { + builder.add_locality(this->locality); + } + if(!this->state.empty()) { + builder.add_state(this->state); + } + if(!this->serial_number.empty()) { + builder.add_serial_number(this->serial_number); + } + if(!this->email.empty()) { + if(auto parsed = EmailAddress::from_string(this->email)) { + builder.add_email(*parsed); + } else { + throw Invalid_Argument(fmt("Invalid email address '{}'", this->email)); + } + } + if(!this->uri.empty()) { + if(auto parsed = URI::parse(this->uri)) { + builder.add_uri(*parsed); + } else { + throw Invalid_Argument(fmt("Invalid URI '{}'", this->uri)); + } + } + if(!this->ip.empty()) { + if(auto ipv4 = IPv4Address::from_string(this->ip)) { + builder.add_ipv4(*ipv4); + } else { + throw Invalid_Argument(fmt("Invalid IPv4 address '{}'", this->ip)); + } + } + + auto add_dns = [&](std::string_view nm) { + if(auto parsed = DNSName::from_san_string(nm)) { + builder.add_dns(*parsed); + } else { + throw Invalid_Argument(fmt("Invalid DNS name '{}'", nm)); + } + }; + + if(!this->dns.empty()) { + add_dns(this->dns); + } + for(const auto& nm : this->more_dns) { + if(!nm.empty()) { + add_dns(nm); + } + } + if(!this->xmpp.empty()) { + builder.add_xmpp(this->xmpp); + } + if(this->is_CA) { + builder.set_as_ca_certificate(this->path_limit); + } + if(!this->constraints.empty()) { + builder.add_allowed_usage(this->constraints); + } + for(const OID& usage : this->ex_constraints) { + builder.add_allowed_extended_usage(usage); + } + + for(auto& [extn, is_critical] : this->extensions.extensions()) { + builder.add_extension(std::move(extn), is_critical); + } + + return builder; +} + } // namespace Botan diff --git a/src/lib/x509/x509self.cpp b/src/lib/x509/x509self.cpp index 6e60e1fbd37..7533a3c15d1 100644 --- a/src/lib/x509/x509self.cpp +++ b/src/lib/x509/x509self.cpp @@ -7,73 +7,7 @@ #include -#include -#include -#include -#include -#include -#include - -namespace Botan { - -namespace { - -/* -* Load information from the X509_Cert_Options -*/ -X509_DN load_dn_info(const X509_Cert_Options& opts) { - X509_DN subject_dn; - - subject_dn.add_attribute("X520.CommonName", opts.common_name); - subject_dn.add_attribute("X520.Country", opts.country); - subject_dn.add_attribute("X520.State", opts.state); - subject_dn.add_attribute("X520.Locality", opts.locality); - subject_dn.add_attribute("X520.Organization", opts.organization); - subject_dn.add_attribute("X520.OrganizationalUnit", opts.org_unit); - subject_dn.add_attribute("X520.SerialNumber", opts.serial_number); - - for(const auto& extra_ou : opts.more_org_units) { - subject_dn.add_attribute("X520.OrganizationalUnit", extra_ou); - } - - return subject_dn; -} - -auto create_alt_name_ext(const X509_Cert_Options& opts, const Extensions& extensions) { - AlternativeName subject_alt; - - /* - If the extension was already created in opts.extension we need to - merge the values provided in opts with the values set in the extension. - */ - if(const auto* ext = extensions.get_extension_object_as()) { - subject_alt = ext->get_alt_name(); - } - - subject_alt.add_dns(opts.dns); - for(const auto& nm : opts.more_dns) { - subject_alt.add_dns(nm); - } - subject_alt.add_uri(opts.uri); - subject_alt.add_email(opts.email); - if(!opts.ip.empty()) { - if(auto ipv4 = IPv4Address::from_string(opts.ip)) { - subject_alt.add_ipv4_address(*ipv4); - } else { - throw Invalid_Argument(fmt("Invalid IPv4 address '{}'", opts.ip)); - } - } - - if(!opts.xmpp.empty()) { - subject_alt.add_other_name(OID::from_string("PKIX.XMPPAddr"), ASN1_String(opts.xmpp, ASN1_Type::Utf8String)); - } - - return std::make_unique(subject_alt); -} - -} // namespace - -namespace X509 { +namespace Botan::X509 { /* * Create a new self-signed X.509 certificate @@ -82,37 +16,13 @@ X509_Certificate create_self_signed_cert(const X509_Cert_Options& opts, const Private_Key& key, std::string_view hash_fn, RandomNumberGenerator& rng) { - const std::vector pub_key = X509::BER_encode(key); - auto signer = X509_Object::choose_sig_format(key, rng, hash_fn, opts.padding_scheme); - const AlgorithmIdentifier sig_algo = signer->algorithm_identifier(); - BOTAN_ASSERT_NOMSG(sig_algo.oid().has_value()); + auto not_before = opts.start.to_std_timepoint(); + auto not_after = opts.end.to_std_timepoint(); - const auto subject_dn = load_dn_info(opts); + const std::optional padding = + (opts.padding_scheme.empty()) ? std::nullopt : std::optional(opts.padding_scheme); - Extensions extensions = opts.extensions; - - const auto constraints = opts.is_CA ? Key_Constraints::ca_constraints() : opts.constraints; - - if(!constraints.compatible_with(key)) { - throw Invalid_Argument("The requested key constraints are incompatible with the algorithm"); - } - - extensions.add_new(std::make_unique(opts.is_CA, opts.path_limit), true); - - if(!constraints.empty()) { - extensions.add_new(std::make_unique(constraints), true); - } - - auto skid = std::make_unique(pub_key, signer->hash_function()); - - extensions.add_new(std::make_unique(skid->get_key_id())); - extensions.add_new(std::move(skid)); - - extensions.replace(create_alt_name_ext(opts, extensions)); - - extensions.add_new(std::make_unique(opts.ex_constraints)); - - return X509_CA::make_cert(*signer, rng, sig_algo, pub_key, opts.start, opts.end, subject_dn, subject_dn, extensions); + return opts.into_builder().into_self_signed_cert(not_before, not_after, key, rng, hash_fn, padding); } /* @@ -122,31 +32,14 @@ PKCS10_Request create_cert_req(const X509_Cert_Options& opts, const Private_Key& key, std::string_view hash_fn, RandomNumberGenerator& rng) { - const auto subject_dn = load_dn_info(opts); - - const auto constraints = opts.is_CA ? Key_Constraints::ca_constraints() : opts.constraints; - - if(!constraints.compatible_with(key)) { - throw Invalid_Argument("The requested key constraints are incompatible with the algorithm"); - } - - Extensions extensions = opts.extensions; + const std::optional challenge_password = + (opts.challenge.empty()) ? std::nullopt : std::optional(opts.challenge); + const std::optional padding = + (opts.padding_scheme.empty()) ? std::nullopt : std::optional(opts.padding_scheme); - extensions.add_new(std::make_unique(opts.is_CA, opts.path_limit)); + // opts.start and opts.end are ignored here - if(!constraints.empty()) { - extensions.add_new(std::make_unique(constraints)); - } - - extensions.replace(create_alt_name_ext(opts, extensions)); - - if(!opts.ex_constraints.empty()) { - extensions.add_new(std::make_unique(opts.ex_constraints)); - } - - return PKCS10_Request::create(key, subject_dn, extensions, hash_fn, rng, opts.padding_scheme, opts.challenge); + return opts.into_builder().into_pkcs10_request(key, rng, hash_fn, padding, challenge_password); } -} // namespace X509 - -} // namespace Botan +} // namespace Botan::X509 diff --git a/src/lib/x509/x509self.h b/src/lib/x509/x509self.h index 0754956c147..d1e8b065e57 100644 --- a/src/lib/x509/x509self.h +++ b/src/lib/x509/x509self.h @@ -12,13 +12,18 @@ #include #include #include +#include #include +#include +#include namespace Botan { class RandomNumberGenerator; class Private_Key; +// Older interface for creating PKCS10 requests and self-signed certificates follows + /** * Options for X.509 certificates. */ @@ -179,6 +184,8 @@ class BOTAN_PUBLIC_API(2, 0) X509_Cert_Options final { */ void add_ex_constraint(std::string_view name); + CertificateParametersBuilder into_builder() const; + /** * Construct a new options object * @param opts define the common name of this object. An example for this diff --git a/src/tests/test_x509_rpki.cpp b/src/tests/test_x509_rpki.cpp index 80dfd43245c..79383ea9a56 100644 --- a/src/tests/test_x509_rpki.cpp +++ b/src/tests/test_x509_rpki.cpp @@ -10,14 +10,18 @@ #if defined(BOTAN_HAS_X509_CERTIFICATES) #include #include + #include #include #include #include + #include #include #include #include - #include #include + #if defined(BOTAN_HAS_ECC_GROUP) + #include + #endif #endif namespace Botan_Tests { @@ -42,96 +46,109 @@ Botan::X509_Time from_date(const int y, const int m, const int d) { 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.uri = "https://botan.randombit.net"; - opts.dns = "botan.randombit.net"; - opts.email = "testing@randombit.net"; - opts.set_padding_scheme(sig_padding); +std::unique_ptr generate_key(const std::string& algo, Botan::RandomNumberGenerator& rng) { + std::string params; + if(algo == "ECDSA") { + params = "secp256r1"; - opts.CA_key(1); + #if defined(BOTAN_HAS_ECC_GROUP) + if(Botan::EC_Group::supports_named_group("secp192r1")) { + params = "secp192r1"; + } + #endif + } else if(algo == "Ed25519") { + params = ""; + } else if(algo == "RSA") { + params = "1024"; + } - return opts; + return Botan::create_private_key(algo, rng, params); } -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"); +Botan::CertificateParametersBuilder ca_params() { + Botan::CertificateParametersBuilder builder; - opts.uri = "https://botan.randombit.net"; - opts.dns = "botan.randombit.net"; - opts.email = "testing@randombit.net"; - opts.set_padding_scheme(sig_padding); + builder.add_common_name("RPKI Test CA") + .add_country("US") + .add_organization("Botan") + .add_dns(Botan::DNSName::from_string("botan.randombit.net").value()) + .set_as_ca_certificate(1); - opts.not_before("160101200000Z"); - opts.not_after("300101200000Z"); + return builder; +} - opts.challenge = "zoom"; +Botan::CertificateParametersBuilder user_params() { + Botan::CertificateParametersBuilder builder; - 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; - } + builder.add_common_name("RPKI Test User") + .add_country("US") + .add_organization("Botan") + .add_allowed_usage(Botan::Key_Constraints::DigitalSignature); - return opts; + return builder; } -std::tuple> -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)}; - } +std::pair get_sig_algo() { + #if defined(BOTAN_HAS_ECDSA) + const std::string sig_algo{"ECDSA"}; + const std::string hash_fn{"SHA-256"}; + #elif defined(BOTAN_HAS_ED25519) + const std::string sig_algo{"Ed25519"}; + const std::string hash_fn{"SHA-512"}; + #elif defined(BOTAN_HAS_RSA) + const std::string sig_algo{"RSA"}; + const std::string hash_fn{"SHA-256"}; #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 + return std::make_pair(sig_algo, hash_fn); } -Botan::X509_Certificate make_self_signed(std::unique_ptr& 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); +Botan::X509_Certificate make_self_signed(Botan::RandomNumberGenerator& rng, + const Botan::CertificateParametersBuilder& params) { + auto [sig_algo, hash_fn] = get_sig_algo(); + auto key = generate_key(sig_algo, rng); + + const auto now = std::chrono::system_clock::now(); + const auto not_before = now - std::chrono::seconds(180); + const auto not_after = now + std::chrono::seconds(86400); + + return params.into_self_signed_cert(not_before, not_after, *key, rng, hash_fn); } -CA_Creation_Result make_ca(std::unique_ptr& 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); +CA_Creation_Result make_ca(Botan::RandomNumberGenerator& rng, const Botan::CertificateParametersBuilder& params) { + auto [sig_algo, hash_fn] = get_sig_algo(); + auto ca_key = generate_key(sig_algo, rng); + + const auto now = std::chrono::system_clock::now(); + const auto not_before = now - std::chrono::seconds(180); + const auto not_after = now + std::chrono::seconds(86400); + + const auto ca_cert = params.into_self_signed_cert(not_before, not_after, *ca_key, rng, hash_fn); + + Botan::X509_CA ca(ca_cert, *ca_key, hash_fn, rng); + auto sub_key = generate_key(sig_algo, rng); return CA_Creation_Result{ca_cert, std::move(ca), std::move(sub_key), sig_algo, hash_fn}; } -std::pair make_and_sign_ca( - std::unique_ptr ext, - Botan::X509_CA& parent_ca, - std::unique_ptr& rng) { - auto [sig_algo, padding_method, hash_fn, key] = get_sig_algo_padding_and_generate_key(*rng); +CA_Creation_Result make_ca(Botan::RandomNumberGenerator& rng) { + return make_ca(rng, ca_params()); +} + +std::pair make_and_sign_ca(std::unique_ptr ext, + Botan::X509_CA& parent_ca, + Botan::RandomNumberGenerator& rng) { + auto [sig_algo, hash_fn] = get_sig_algo(); + + auto params = ca_params(); + params.add_extension(std::move(ext)); - Botan::X509_Cert_Options opts = ca_opts(); - opts.extensions.add(std::move(ext)); + std::unique_ptr key = generate_key(sig_algo, rng); - 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); + Botan::PKCS10_Request req = params.into_pkcs10_request(*key, rng, hash_fn); + + 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, rng); return std::make_pair(std::move(cert), std::move(ca)); } @@ -425,10 +442,7 @@ Test::Result test_x509_ip_addr_blocks_rfc3779_example() { blocks_1->add_address({10, 3, 0, 0}, {10, 3, 255, 255}, 1); blocks_1->inherit(); - 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 cert_1 = make_self_signed(*rng, ca_params().add_extension(std::move(blocks_1))); auto bits_1 = cert_1.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid()); @@ -472,10 +486,7 @@ Test::Result test_x509_ip_addr_blocks_rfc3779_example() { blocks_2->add_address({172, 16, 0, 0}, {172, 31, 255, 255}, 1); blocks_2->inherit(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 cert_2 = make_self_signed(*rng, ca_params().add_extension(std::move(blocks_2))); auto bits_2 = cert_2.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid()); @@ -539,10 +550,7 @@ Test::Result test_x509_ip_addr_blocks_encode_builder() { blocks->add_address({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 cert = make_self_signed(*rng, ca_params().add_extension(std::move(blocks))); auto bits = cert.v3_extensions().get_extension_bits(IPAddressBlocks::static_oid()); // hand validated with https://lapo.it/asn1js/ @@ -576,7 +584,7 @@ Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() { auto rng = Test::new_rng(__func__); - auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng); + 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); @@ -586,8 +594,6 @@ Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() { 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 a = {123, 123, 2, 1}; auto ipv4_1 = IPAddressBlocks::IPAddress(a); a = {255, 255, 255, 255}; @@ -672,10 +678,8 @@ Test::Result test_x509_ip_addr_blocks_extension_encode_ctor() { std::unique_ptr blocks = std::make_unique(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)); + auto req = user_params().add_extension(std::move(blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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(); result.test_is_true("cert has IPAddrBlocks extension", ip_blocks != nullptr); @@ -753,7 +757,7 @@ Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() { std::vector 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); + 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++) { @@ -767,8 +771,6 @@ Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() { break; } - Botan::X509_Cert_Options opts = req_opts(sig_algo); - std::vector min_bytes(16, 0x00); std::vector max_bytes(16, 0xFF); @@ -796,11 +798,8 @@ Test::Result test_x509_ip_addr_blocks_extension_encode_edge_cases_ctor() { std::unique_ptr blocks = std::make_unique(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)); + auto req = user_params().add_extension(std::move(blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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(); result.test_not_null("cert has IPAddrBlocks extension", ip_blocks); @@ -829,8 +828,7 @@ Test::Result test_x509_ip_addr_blocks_range_merge() { 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); + auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(*rng); const std::vector>> addresses = { {{11, 0, 0, 0}, {{11, 0, 0, 0}}}, @@ -860,10 +858,8 @@ Test::Result test_x509_ip_addr_blocks_range_merge() { std::unique_ptr blocks = std::make_unique(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)); + auto req = user_params().add_extension(std::move(blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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(); result.test_not_null("cert has IPAddrBlocks extension", ip_blocks); @@ -892,8 +888,7 @@ Test::Result test_x509_ip_addr_blocks_family_merge() { 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); + auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(*rng); std::vector addr_blocks; @@ -947,10 +942,8 @@ Test::Result test_x509_ip_addr_blocks_family_merge() { std::unique_ptr blocks = std::make_unique(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)); + auto req = user_params().add_extension(std::move(blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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(); result.test_not_null("cert has IPAddrBlocks extension", ip_blocks); @@ -1115,12 +1108,9 @@ Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() { {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); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = + make_ca(*rng, ca_params().add_extension(std::move(root_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); @@ -1146,11 +1136,11 @@ Test::Result test_x509_ip_addr_blocks_path_validation_success_builder() { dyn_blocks->inherit(); } - auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng); + 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 auto sub_req = user_params().add_extension(sub_blocks->copy()).into_pkcs10_request(*sub_key, *rng, hash_fn); + + const auto 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 certs = {sub_cert, dyn_cert, inherit_cert}; @@ -1280,12 +1270,9 @@ Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() { auto sub_addr_blocks = {sub_ipv4_family, sub_ipv6_family}; std::unique_ptr sub_blocks = std::make_unique(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); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = + make_ca(*rng, ca_params().add_extension(std::move(root_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); @@ -1305,11 +1292,10 @@ Test::Result test_x509_ip_addr_blocks_path_validation_success_ctor() { auto dyn_addr_blocks = {dyn_ipv4_family, dyn_ipv6_family}; std::unique_ptr dyn_blocks = std::make_unique(dyn_addr_blocks); - auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng); + 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 auto sub_req = user_params().add_extension(sub_blocks->copy()).into_pkcs10_request(*sub_key, *rng, hash_fn); + const auto 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 certs = {sub_cert, dyn_cert, inherit_cert}; @@ -1346,11 +1332,11 @@ Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() { root_blocks->inherit(42); } - Botan::X509_Cert_Options root_opts = ca_opts(); + auto root_params = ca_params(); if(!nullptr_extensions) { - root_opts.extensions.add(std::move(root_blocks)); + root_params.add_extension(std::move(root_blocks)); } - auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(*rng, root_params); // Issuer Cert std::unique_ptr iss_blocks = std::make_unique(); @@ -1364,7 +1350,7 @@ Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() { iss_blocks->inherit(42); } - auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng); + auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, *rng); // Subject cert std::unique_ptr sub_blocks = std::make_unique(); @@ -1385,11 +1371,8 @@ Test::Result test_x509_ip_addr_blocks_path_validation_failure_builder() { sub_blocks->inherit(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 = + auto sub_req = user_params().add_extension(std::move(sub_blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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); @@ -1436,11 +1419,11 @@ Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() { auto root_addr_blocks = {root_family}; std::unique_ptr root_blocks = std::make_unique(root_addr_blocks); - Botan::X509_Cert_Options root_opts = ca_opts(); + auto root_params = ca_params(); if(!nullptr_extensions) { - root_opts.extensions.add(std::move(root_blocks)); + root_params.add_extension(std::move(root_blocks)); } - auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(rng, root_opts); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(*rng, root_params); // Issuer Cert a = {122, 0, 0, 255}; @@ -1459,7 +1442,7 @@ Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() { auto iss_family = IPAddressBlocks::IPAddressFamily(iss_choice, 42); auto iss_addr_blocks = {iss_family}; std::unique_ptr iss_blocks = std::make_unique(iss_addr_blocks); - auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, rng); + auto [iss_cert, iss_ca] = make_and_sign_ca(std::move(iss_blocks), root_ca, *rng); // Subject cert if(too_small_subrange) { @@ -1488,11 +1471,8 @@ Test::Result test_x509_ip_addr_blocks_path_validation_failure_ctor() { auto sub_addr_blocks = {sub_family}; std::unique_ptr sub_blocks = std::make_unique(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 = + auto sub_req = user_params().add_extension(std::move(sub_blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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); @@ -1523,10 +1503,7 @@ Test::Result test_x509_as_blocks_rfc3779_example() { 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 cert = make_self_signed(*rng, ca_params().add_extension(std::move(blocks))); auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid()); result.test_bin_eq( @@ -1560,10 +1537,7 @@ Test::Result test_x509_as_blocks_encode_builder() { // 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 cert = make_self_signed(*rng, ca_params().add_extension(std::move(blocks))); auto bits = cert.v3_extensions().get_extension_bits(ASBlocks::static_oid()); result.test_bin_eq("extension is encoded as specified", bits, "3011A0023000A10B300930070201090202012D"); @@ -1580,7 +1554,7 @@ Test::Result test_x509_as_blocks_extension_encode_ctor() { auto rng = Test::new_rng(__func__); - auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(rng); + 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); @@ -1621,11 +1595,8 @@ Test::Result test_x509_as_blocks_extension_encode_ctor() { std::unique_ptr blocks = std::make_unique(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)); + auto req = user_params().add_extension(std::move(blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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(); @@ -1679,8 +1650,7 @@ Test::Result test_x509_as_blocks_range_merge() { 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); + auto [ca_cert, ca, sub_key, sig_algo, hash_fn] = make_ca(*rng); const std::vector> ranges = { {2005, 37005}, @@ -1704,10 +1674,8 @@ Test::Result test_x509_as_blocks_range_merge() { std::unique_ptr blocks = std::make_unique(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 req = user_params().add_extension(std::move(blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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(); result.test_is_true("cert has ASBlock extension", as_blocks != nullptr); @@ -1772,12 +1740,9 @@ Test::Result test_x509_as_blocks_path_validation_success_builder() { 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); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = + make_ca(*rng, ca_params().add_extension(std::move(root_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); @@ -1802,10 +1767,10 @@ Test::Result test_x509_as_blocks_path_validation_success_builder() { dyn_blocks->inherit_rdi(); } - auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng); + 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 = + const auto sub_req = user_params().add_extension(sub_blocks->copy()).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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); @@ -1911,12 +1876,9 @@ Test::Result test_x509_as_blocks_path_validation_success_ctor() { const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi); std::unique_ptr sub_blocks = std::make_unique(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); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = + make_ca(*rng, ca_params().add_extension(std::move(root_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); @@ -1932,10 +1894,10 @@ Test::Result test_x509_as_blocks_path_validation_success_ctor() { const ASBlocks::ASIdentifiers dyn_ident = ASBlocks::ASIdentifiers(dyn_asnum, dyn_rdi); std::unique_ptr dyn_blocks = std::make_unique(dyn_ident); - auto [dyn_cert, dyn_ca] = make_and_sign_ca(std::move(dyn_blocks), inherit_ca, rng); + 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 = + const auto sub_req = user_params().add_extension(sub_blocks->copy()).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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); @@ -1969,13 +1931,9 @@ Test::Result test_x509_as_blocks_path_validation_extension_not_present_builder() 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)); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(*rng, ca_params()); + auto sub_req = user_params().add_extension(std::move(sub_blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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); @@ -2028,13 +1986,9 @@ Test::Result test_x509_as_blocks_path_validation_extension_not_present_ctor() { std::unique_ptr sub_blocks = std::make_unique(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)); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = make_ca(*rng, ca_params()); + const auto sub_req = user_params().add_extension(std::move(sub_blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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); @@ -2222,15 +2176,13 @@ Test::Result test_x509_as_blocks_path_validation_failure_builder() { 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); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = + make_ca(*rng, ca_params().add_extension(std::move(root_blocks))); + 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 = + const auto sub_req = + user_params().add_extension(std::move(sub_blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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; @@ -2418,15 +2370,13 @@ Test::Result test_x509_as_blocks_path_validation_failure_ctor() { const ASBlocks::ASIdentifiers sub_ident = ASBlocks::ASIdentifiers(sub_asnum, sub_rdi); std::unique_ptr sub_blocks = std::make_unique(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); + auto [root_cert, root_ca, sub_key, sig_algo, hash_fn] = + make_ca(*rng, ca_params().add_extension(std::move(root_blocks))); + 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 = + const auto sub_req = + user_params().add_extension(std::move(sub_blocks)).into_pkcs10_request(*sub_key, *rng, hash_fn); + 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;