From c43bb8cae218c807e14ba193d1e66d6a7d9c49f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Tue, 10 Sep 2024 08:36:20 +0200 Subject: [PATCH] Introduce a base builder as discussed here: https://github.com/randombit/botan/pull/4318#issuecomment-2298475650 --- src/cli/pubkey.cpp | 13 +- .../math/pcurves/pcurves_impl/pcurves_impl.h | 1 + src/lib/pk_pad/emsa.cpp | 49 +-- src/lib/pk_pad/emsa.h | 2 +- src/lib/prov/pkcs11/p11_ecdsa.cpp | 30 +- src/lib/prov/pkcs11/p11_ecdsa.h | 4 +- src/lib/prov/pkcs11/p11_mechanism.cpp | 21 +- src/lib/prov/pkcs11/p11_mechanism.h | 2 +- src/lib/prov/pkcs11/p11_rsa.cpp | 10 +- src/lib/prov/pkcs11/p11_rsa.h | 4 +- src/lib/prov/tpm/tpm.cpp | 6 +- src/lib/prov/tpm/tpm.h | 2 +- src/lib/pubkey/curve448/ed448/ed448.cpp | 35 +-- src/lib/pubkey/curve448/ed448/ed448.h | 4 +- .../dilithium/dilithium_common/dilithium.cpp | 45 +-- .../dilithium/dilithium_common/dilithium.h | 4 +- src/lib/pubkey/dsa/dsa.cpp | 21 +- src/lib/pubkey/dsa/dsa.h | 4 +- src/lib/pubkey/ecdsa/ecdsa.cpp | 22 +- src/lib/pubkey/ecdsa/ecdsa.h | 4 +- src/lib/pubkey/ecgdsa/ecgdsa.cpp | 21 +- src/lib/pubkey/ecgdsa/ecgdsa.h | 4 +- src/lib/pubkey/eckcdsa/eckcdsa.cpp | 32 +- src/lib/pubkey/eckcdsa/eckcdsa.h | 4 +- src/lib/pubkey/ed25519/ed25519.h | 4 +- src/lib/pubkey/ed25519/ed25519_key.cpp | 47 +-- src/lib/pubkey/gost_3410/gost_3410.cpp | 23 +- src/lib/pubkey/gost_3410/gost_3410.h | 4 +- src/lib/pubkey/hss_lms/hss_lms.cpp | 23 +- src/lib/pubkey/hss_lms/hss_lms.h | 4 +- src/lib/pubkey/info.txt | 1 - src/lib/pubkey/pk_keys.cpp | 12 +- src/lib/pubkey/pk_keys.h | 4 +- src/lib/pubkey/pk_ops.cpp | 26 +- src/lib/pubkey/pk_ops_impl.h | 12 +- src/lib/pubkey/pk_options.cpp | 271 +++++++++++------ src/lib/pubkey/pk_options.h | 278 ++++++++++++++---- src/lib/pubkey/pk_options_impl.cpp | 210 ------------- src/lib/pubkey/pk_options_impl.h | 25 -- src/lib/pubkey/pubkey.cpp | 22 +- src/lib/pubkey/pubkey.h | 26 +- src/lib/pubkey/rsa/rsa.cpp | 27 +- src/lib/pubkey/rsa/rsa.h | 4 +- src/lib/pubkey/sm2/sm2.cpp | 32 +- src/lib/pubkey/sm2/sm2.h | 4 +- .../sphincsplus_common/sphincsplus.cpp | 37 +-- .../sphincsplus_common/sphincsplus.h | 4 +- src/lib/pubkey/xmss/xmss.h | 4 +- src/lib/pubkey/xmss/xmss_privatekey.cpp | 14 +- src/lib/pubkey/xmss/xmss_publickey.cpp | 14 +- src/lib/utils/base_builder.cpp | 46 +++ src/lib/utils/base_builder.h | 252 ++++++++++++++++ src/lib/utils/info.txt | 2 + src/lib/utils/stl_util.h | 11 - src/lib/utils/template_utils.h | 28 ++ 55 files changed, 1017 insertions(+), 798 deletions(-) delete mode 100644 src/lib/pubkey/pk_options_impl.cpp delete mode 100644 src/lib/pubkey/pk_options_impl.h create mode 100644 src/lib/utils/base_builder.cpp create mode 100644 src/lib/utils/base_builder.h create mode 100644 src/lib/utils/template_utils.h diff --git a/src/cli/pubkey.cpp b/src/cli/pubkey.cpp index 9a41345a442..ba788d360d3 100644 --- a/src/cli/pubkey.cpp +++ b/src/cli/pubkey.cpp @@ -187,10 +187,10 @@ class PK_Sign final : public Command { throw CLI_Error_Unsupported("hashing", hash_fn); } - const auto options = - sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), get_arg("provider")); - - Botan::PK_Signer signer(*key, rng(), options); + Botan::PK_Signer signer( + *key, + rng(), + sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), get_arg("provider"))); auto onData = [&signer](const uint8_t b[], size_t l) { signer.update(b, l); }; Command::read_file(get_arg("file"), onData); @@ -234,9 +234,8 @@ class PK_Verify final : public Command { throw CLI_Error_Unsupported("hashing", hash_fn); } - const auto options = sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), ""); - - Botan::PK_Verifier verifier(*key, options); + Botan::PK_Verifier verifier( + *key, sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), "")); auto onData = [&verifier](const uint8_t b[], size_t l) { verifier.update(b, l); }; Command::read_file(get_arg("file"), onData); diff --git a/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h b/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h index 1181994e5e7..c583fbc582a 100644 --- a/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h +++ b/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h @@ -8,6 +8,7 @@ #define BOTAN_PCURVES_IMPL_H_ #include +#include #include #include #include diff --git a/src/lib/pk_pad/emsa.cpp b/src/lib/pk_pad/emsa.cpp index 46ee3c31125..f4649c2181f 100644 --- a/src/lib/pk_pad/emsa.cpp +++ b/src/lib/pk_pad/emsa.cpp @@ -32,17 +32,20 @@ namespace Botan { -std::unique_ptr EMSA::create_or_throw(const PK_Signature_Options& options) { - const bool is_raw_hash = !options.using_hash() || options.hash_function_name() == "Raw"; +std::unique_ptr EMSA::create_or_throw(PK_Signature_Options& options) { + const auto hash = options.maybe_hash_function(); + const auto padding = options.padding(); + const bool is_raw_hash = !hash.has_value() || hash.value() == "Raw"; + const bool is_raw_padding = !padding.has_value() || padding.value() == "Raw"; - if(!options.using_padding() || options.padding() == "Raw") { + if(is_raw_padding) { // Only valid possibility for empty padding is no hash / "Raw" hash #if defined(BOTAN_HAS_EMSA_RAW) if(is_raw_hash) { - if(options.using_prehash()) { - if(auto hash = HashFunction::create(options.prehash_fn().value())) { - return std::make_unique(hash->output_length()); + if(auto [using_prehash, prehash_fn] = options.prehash(); using_prehash && prehash_fn.has_value()) { + if(auto prehash = HashFunction::create(prehash_fn.value())) { + return std::make_unique(prehash->output_length()); } } else { return std::make_unique(); @@ -50,57 +53,55 @@ std::unique_ptr EMSA::create_or_throw(const PK_Signature_Options& options) } #endif } else { - const std::string padding = options.padding().value(); - // null if raw_hash - auto hash = [&]() -> std::unique_ptr { + auto hash_fn = [&]() -> std::unique_ptr { if(is_raw_hash) { return nullptr; } else { - return HashFunction::create(options.hash_function_name()); + return HashFunction::create(hash.value()); } }(); #if defined(BOTAN_HAS_EMSA_PKCS1) if(padding == "PKCS1v15") { if(is_raw_hash) { - return std::make_unique(options.prehash_fn()); - } else if(hash) { - return std::make_unique(std::move(hash)); + return std::make_unique(options.prehash().second); + } else if(hash_fn) { + return std::make_unique(std::move(hash_fn)); } } #endif #if defined(BOTAN_HAS_EMSA_PSSR) - if(padding == "PSS_Raw" && hash) { - return std::make_unique(std::move(hash), options.salt_size()); + if(padding == "PSS_Raw" && hash_fn) { + return std::make_unique(std::move(hash_fn), options.salt_size()); } - if(padding == "PSS" && hash) { - return std::make_unique(std::move(hash), options.salt_size()); + if(padding == "PSS" && hash_fn) { + return std::make_unique(std::move(hash_fn), options.salt_size()); } #endif #if defined(BOTAN_HAS_ISO_9796) - if(padding == "ISO_9796_DS2" && hash) { + if(padding == "ISO_9796_DS2" && hash_fn) { return std::make_unique( - std::move(hash), !options.using_explicit_trailer_field(), options.salt_size()); + std::move(hash_fn), !options.using_explicit_trailer_field(), options.salt_size()); } //ISO-9796-2 DS 3 is deterministic and DS2 without a salt - if(padding == "ISO_9796_DS3" && hash) { - return std::make_unique(std::move(hash), !options.using_explicit_trailer_field()); + if(padding == "ISO_9796_DS3" && hash_fn) { + return std::make_unique(std::move(hash_fn), !options.using_explicit_trailer_field()); } #endif #if defined(BOTAN_HAS_EMSA_X931) - if(padding == "X9.31" && hash) { - return std::make_unique(std::move(hash)); + if(padding == "X9.31" && hash_fn) { + return std::make_unique(std::move(hash_fn)); } #endif } - throw Lookup_Error("Invalid or unavailable signature padding scheme " + options.to_string()); + throw Lookup_Error("Invalid or unavailable signature padding scheme\n" + options.to_string()); } } // namespace Botan diff --git a/src/lib/pk_pad/emsa.h b/src/lib/pk_pad/emsa.h index 435e316b2db..b8c5e5228e5 100644 --- a/src/lib/pk_pad/emsa.h +++ b/src/lib/pk_pad/emsa.h @@ -31,7 +31,7 @@ class BOTAN_TEST_API EMSA { * @param options the algorithm parameters * @return pointer to newly allocated object of that type, or throws */ - static std::unique_ptr create_or_throw(const PK_Signature_Options& options); + static std::unique_ptr create_or_throw(PK_Signature_Options& options); /** * Add more data to the signature computation diff --git a/src/lib/prov/pkcs11/p11_ecdsa.cpp b/src/lib/prov/pkcs11/p11_ecdsa.cpp index 2395448e5d0..b8d1c401bf7 100644 --- a/src/lib/prov/pkcs11/p11_ecdsa.cpp +++ b/src/lib/prov/pkcs11/p11_ecdsa.cpp @@ -53,13 +53,17 @@ std::unique_ptr PKCS11_ECDSA_PrivateKey::public_key() const { namespace { class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature { - public: - PKCS11_ECDSA_Signature_Operation(const PKCS11_ECDSA_PrivateKey& key, const PK_Signature_Options& options) : + private: + PKCS11_ECDSA_Signature_Operation(const PKCS11_ECDSA_PrivateKey& key, std::string_view hash) : PK_Ops::Signature(), m_key(key), m_order_bytes(key.domain().get_order_bytes()), - m_mechanism(MechanismWrapper::create_ecdsa_mechanism(options.hash_function_name())), - m_hash(options.hash_function_name()) {} + m_mechanism(MechanismWrapper::create_ecdsa_mechanism(hash)), + m_hash(hash) {} + + public: + PKCS11_ECDSA_Signature_Operation(const PKCS11_ECDSA_PrivateKey& key, PK_Signature_Options& options) : + PKCS11_ECDSA_Signature_Operation(key, options.hash_function()) {} void update(std::span input) override { if(!m_initialized) { @@ -115,12 +119,16 @@ AlgorithmIdentifier PKCS11_ECDSA_Signature_Operation::algorithm_identifier() con } class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification { - public: - PKCS11_ECDSA_Verification_Operation(const PKCS11_ECDSA_PublicKey& key, const PK_Signature_Options& options) : + private: + PKCS11_ECDSA_Verification_Operation(const PKCS11_ECDSA_PublicKey& key, std::string_view hash) : PK_Ops::Verification(), m_key(key), - m_mechanism(MechanismWrapper::create_ecdsa_mechanism(options.hash_function_name())), - m_hash(options.hash_function_name()) {} + m_mechanism(MechanismWrapper::create_ecdsa_mechanism(hash)), + m_hash(hash) {} + + public: + PKCS11_ECDSA_Verification_Operation(const PKCS11_ECDSA_PublicKey& key, PK_Signature_Options& options) : + PKCS11_ECDSA_Verification_Operation(key, options.hash_function()) {} void update(std::span input) override { if(!m_initialized) { @@ -176,12 +184,12 @@ class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification { } // namespace std::unique_ptr PKCS11_ECDSA_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { return std::make_unique(*this, options); } -std::unique_ptr PKCS11_ECDSA_PrivateKey::_create_signature_op( - RandomNumberGenerator& rng, const PK_Signature_Options& options) const { +std::unique_ptr PKCS11_ECDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); return std::make_unique(*this, options); } diff --git a/src/lib/prov/pkcs11/p11_ecdsa.h b/src/lib/prov/pkcs11/p11_ecdsa.h index d486951ee8f..a1d6f40f4d4 100644 --- a/src/lib/prov/pkcs11/p11_ecdsa.h +++ b/src/lib/prov/pkcs11/p11_ecdsa.h @@ -58,7 +58,7 @@ class BOTAN_PUBLIC_API(2, 0) PKCS11_ECDSA_PublicKey final : public PKCS11_EC_Pub throw Not_Implemented("Cannot generate a new PKCS#11 ECDSA keypair from this public key"); } - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP @@ -118,7 +118,7 @@ class BOTAN_PUBLIC_API(2, 0) PKCS11_ECDSA_PrivateKey final : public PKCS11_EC_Pr bool check_key(RandomNumberGenerator&, bool) const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; }; using PKCS11_ECDSA_KeyPair = std::pair; diff --git a/src/lib/prov/pkcs11/p11_mechanism.cpp b/src/lib/prov/pkcs11/p11_mechanism.cpp index b7707d5581b..176e6fb153d 100644 --- a/src/lib/prov/pkcs11/p11_mechanism.cpp +++ b/src/lib/prov/pkcs11/p11_mechanism.cpp @@ -190,24 +190,27 @@ MechanismWrapper MechanismWrapper::create_rsa_crypt_mechanism(std::string_view p return mech; } -MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(const PK_Signature_Options& options) { - const std::string padding = [&]() { - if(options.using_hash() && options.using_padding()) { - return fmt("{}({})", options.padding().value(), options.hash_function_name()); +MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(PK_Signature_Options& options) { + const std::string mechanism_padding = [&]() { + const auto hash = options.maybe_hash_function(); + const auto padding = options.padding(); + + if(hash && padding) { + return fmt("{}({})", padding.value(), hash.value()); } - if(options.using_padding()) { - return options.padding().value(); + if(padding) { + return padding.value(); } - if(options.using_hash()) { - return options.hash_function_name(); + if(hash) { + return hash.value(); } throw Invalid_Argument("RSA signature requires a padding scheme"); }(); - auto mechanism_info_it = SignMechanisms.find(padding); + auto mechanism_info_it = SignMechanisms.find(mechanism_padding); if(mechanism_info_it == SignMechanisms.end()) { // at this point it would be possible to support additional configurations that are not predefined above by parsing `padding` throw Lookup_Error("PKCS#11 RSA sign/verify does not support padding with " + padding); diff --git a/src/lib/prov/pkcs11/p11_mechanism.h b/src/lib/prov/pkcs11/p11_mechanism.h index 528354d3855..43250ba5307 100644 --- a/src/lib/prov/pkcs11/p11_mechanism.h +++ b/src/lib/prov/pkcs11/p11_mechanism.h @@ -44,7 +44,7 @@ class MechanismWrapper final { * @param options supported paddings are Raw (X.509), EMSA3 (PKCS#1 v1.5), EMSA4 (PKCS#1 PSS), * EMSA2 (ANSI X9.31) and ISO9796 (ISO/IEC 9796) */ - static MechanismWrapper create_rsa_sign_mechanism(const PK_Signature_Options& options); + static MechanismWrapper create_rsa_sign_mechanism(PK_Signature_Options& options); /** * Creates the CK_MECHANISM data for ECDSA signature/verification diff --git a/src/lib/prov/pkcs11/p11_rsa.cpp b/src/lib/prov/pkcs11/p11_rsa.cpp index 1de1a416b55..745eeb77213 100644 --- a/src/lib/prov/pkcs11/p11_rsa.cpp +++ b/src/lib/prov/pkcs11/p11_rsa.cpp @@ -211,7 +211,7 @@ class PKCS11_RSA_Encryption_Operation final : public PK_Ops::Encryption { class PKCS11_RSA_Signature_Operation final : public PK_Ops::Signature { public: - PKCS11_RSA_Signature_Operation(const PKCS11_RSA_PrivateKey& key, const PK_Signature_Options& options) : + PKCS11_RSA_Signature_Operation(const PKCS11_RSA_PrivateKey& key, PK_Signature_Options& options) : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(options)) {} size_t signature_length() const override { return m_key.get_n().bytes(); } @@ -328,7 +328,7 @@ AlgorithmIdentifier PKCS11_RSA_Signature_Operation::algorithm_identifier() const class PKCS11_RSA_Verification_Operation final : public PK_Ops::Verification { public: - PKCS11_RSA_Verification_Operation(const PKCS11_RSA_PublicKey& key, const PK_Signature_Options& options) : + PKCS11_RSA_Verification_Operation(const PKCS11_RSA_PublicKey& key, PK_Signature_Options& options) : m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(options)) {} void update(std::span input) override { @@ -394,7 +394,7 @@ std::unique_ptr PKCS11_RSA_PublicKey::create_encryption_op(R } std::unique_ptr PKCS11_RSA_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { return std::make_unique(*this, options); } @@ -408,8 +408,8 @@ std::unique_ptr PKCS11_RSA_PrivateKey::create_decryption_op( } } -std::unique_ptr PKCS11_RSA_PrivateKey::_create_signature_op( - RandomNumberGenerator& rng, const PK_Signature_Options& options) const { +std::unique_ptr PKCS11_RSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); return std::make_unique(*this, options); } diff --git a/src/lib/prov/pkcs11/p11_rsa.h b/src/lib/prov/pkcs11/p11_rsa.h index 849014f74e3..ffdd0febfd0 100644 --- a/src/lib/prov/pkcs11/p11_rsa.h +++ b/src/lib/prov/pkcs11/p11_rsa.h @@ -85,7 +85,7 @@ class BOTAN_PUBLIC_API(2, 0) PKCS11_RSA_PublicKey : public Object, std::string_view params, std::string_view provider) const override; - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; }; /// Properties for importing a PKCS#11 RSA private key @@ -191,7 +191,7 @@ class BOTAN_PUBLIC_API(2, 0) PKCS11_RSA_PrivateKey final : public Object, std::string_view provider) const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: bool m_use_software_padding = false; diff --git a/src/lib/prov/tpm/tpm.cpp b/src/lib/prov/tpm/tpm.cpp index a0f739e6bf1..0827e83ac7f 100644 --- a/src/lib/prov/tpm/tpm.cpp +++ b/src/lib/prov/tpm/tpm.cpp @@ -373,12 +373,12 @@ class TPM_Signing_Operation final : public PK_Ops::Signature { } // namespace std::unique_ptr TPM_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - if(!options.using_padding() || options.padding().value() != "PKCS1v15") { + if(auto padding = options.padding(); padding && padding.value() != "PKCS1v15") { throw Invalid_Argument("TPMv1 can only sign using PKCS1v15 padding"); } - return std::make_unique(*this, options.hash_function_name()); + return std::make_unique(*this, options.hash_function()); } } // namespace Botan diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h index 3e99b29ad55..733a822b11c 100644 --- a/src/lib/prov/tpm/tpm.h +++ b/src/lib/prov/tpm/tpm.h @@ -173,7 +173,7 @@ class BOTAN_PUBLIC_API(2, 0) TPM_PrivateKey final : public Private_Key { bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: TPM_Context& m_ctx; diff --git a/src/lib/pubkey/curve448/ed448/ed448.cpp b/src/lib/pubkey/curve448/ed448/ed448.cpp index b6bb8b6e5cf..f8041f5b1b1 100644 --- a/src/lib/pubkey/curve448/ed448/ed448.cpp +++ b/src/lib/pubkey/curve448/ed448/ed448.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include @@ -216,19 +215,14 @@ AlgorithmIdentifier Ed448_Sign_Operation::algorithm_identifier() const { } // namespace -std::unique_ptr Ed448_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - BOTAN_ARG_CHECK(!options.using_padding(), "Ed448 does not support padding"); +std::unique_ptr Ed448_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); - if(!options.using_provider()) { - if(options.using_prehash()) { - return std::make_unique(*this, options.prehash_fn().value_or("SHAKE-256(512)")); - } else { - return std::make_unique(*this); - } + if(const auto [uses_prehash, prehash_fn] = options.prehash(); uses_prehash) { + return std::make_unique(*this, prehash_fn.value_or("SHAKE-256(512)")); + } else { + return std::make_unique(*this); } - - throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr Ed448_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, @@ -244,20 +238,15 @@ std::unique_ptr Ed448_PublicKey::create_x509_verification_ } std::unique_ptr Ed448_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); + options.exclude_provider_for_algorithm(algo_name()); - BOTAN_ARG_CHECK(!options.using_padding(), "Ed448 does not support padding"); - BOTAN_ARG_CHECK(!options.using_salt_size(), "Ed448 does not support a salt"); - - if(!options.using_provider()) { - if(options.using_prehash()) { - return std::make_unique(*this, options.prehash_fn().value_or("SHAKE-256(512)")); - } else { - return std::make_unique(*this); - } + if(const auto [uses_prehash, prehash_fn] = options.prehash(); uses_prehash) { + return std::make_unique(*this, prehash_fn.value_or("SHAKE-256(512)")); + } else { + return std::make_unique(*this); } - throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/curve448/ed448/ed448.h b/src/lib/pubkey/curve448/ed448/ed448.h index c3eef005216..dee4fa5a1f1 100644 --- a/src/lib/pubkey/curve448/ed448/ed448.h +++ b/src/lib/pubkey/curve448/ed448/ed448.h @@ -56,7 +56,7 @@ class BOTAN_PUBLIC_API(3, 4) Ed448_PublicKey : public virtual Public_Key { */ Ed448_PublicKey(std::span key_bits); - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -112,7 +112,7 @@ class BOTAN_PUBLIC_API(3, 4) Ed448_PrivateKey final : public Ed448_PublicKey, bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: secure_vector m_private; diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp index e2507925421..21abb663e8a 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp @@ -182,41 +182,19 @@ class Dilithium_PrivateKeyInternal { DilithiumPolyVec m_s2; }; -namespace { - -// Return true if randomized signatures were requested -void validate_dilithium_options(const PK_Signature_Options& options) { - BOTAN_ARG_CHECK(!options.using_hash(), "Dilithium does not allow specifying the hash function"); - BOTAN_ARG_CHECK(!options.using_padding(), "Dilithium does not support padding"); - - if(options.using_salt_size()) { - BOTAN_ARG_CHECK(!options.using_deterministic_signature(), - "Dilithium cannot support a salt while also being deterministic"); - BOTAN_ARG_CHECK(options.salt_size().value() == 32, "Dilithium can only be used with a 32 byte salt"); - } - - // This is available in ML-DSA and might be supported in the future - BOTAN_ARG_CHECK(!options.using_prehash(), "Dilithium does not support prehashing"); -} - -} // namespace - // The signature and verification operations should be in an anonymous namespace // as well, but cannot due to an apparent bug in MSVC class Dilithium_Signature_Operation final : public PK_Ops::Signature { public: - Dilithium_Signature_Operation(std::shared_ptr sk, - const PK_Signature_Options& options) : + Dilithium_Signature_Operation(std::shared_ptr sk, PK_Signature_Options& options) : m_priv_key(std::move(sk)), m_randomized(!options.using_deterministic_signature()), m_h(m_priv_key->mode().symmetric_primitives().get_message_hash(m_priv_key->tr())), m_s1(ntt(m_priv_key->s1().clone())), m_s2(ntt(m_priv_key->s2().clone())), m_t0(ntt(m_priv_key->t0().clone())), - m_A(Dilithium_Algos::expand_A(m_priv_key->rho(), m_priv_key->mode())) { - validate_dilithium_options(options); - } + m_A(Dilithium_Algos::expand_A(m_priv_key->rho(), m_priv_key->mode())) {} void update(std::span input) override { m_h.update(input); } @@ -431,11 +409,9 @@ std::unique_ptr Dilithium_PublicKey::generate_another(RandomNumberG } std::unique_ptr Dilithium_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(m_public); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(m_public); } std::unique_ptr Dilithium_PublicKey::create_x509_verification_op( @@ -529,14 +505,11 @@ secure_vector Dilithium_PrivateKey::private_key_bits() const { return std::move(m_private->raw_sk().get()); } -std::unique_ptr Dilithium_PrivateKey::_create_signature_op( - RandomNumberGenerator& rng, const PK_Signature_Options& options) const { +std::unique_ptr Dilithium_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - - if(!options.using_provider()) { - return std::make_unique(m_private, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(m_private, options); } std::unique_ptr Dilithium_PrivateKey::public_key() const { diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h index 5b012262093..2dc7e9bdbc5 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h @@ -82,7 +82,7 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PublicKey : public virtual Public_Key { Dilithium_PublicKey(std::span pk, DilithiumMode mode); - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -124,7 +124,7 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PrivateKey final : public virtual Dilithi secure_vector raw_private_key_bits() const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: friend class Dilithium_Signature_Operation; diff --git a/src/lib/pubkey/dsa/dsa.cpp b/src/lib/pubkey/dsa/dsa.cpp index cc12b77045f..b75454c4229 100644 --- a/src/lib/pubkey/dsa/dsa.cpp +++ b/src/lib/pubkey/dsa/dsa.cpp @@ -126,7 +126,7 @@ namespace { class DSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: DSA_Signature_Operation(const std::shared_ptr& key, - const PK_Signature_Options& options, + PK_Signature_Options& options, RandomNumberGenerator& rng) : PK_Ops::Signature_with_Hash(options), m_key(key) { m_b = BigInt::random_integer(rng, 2, m_key->group().get_q()); @@ -204,7 +204,7 @@ std::vector DSA_Signature_Operation::raw_sign(std::span */ class DSA_Verification_Operation final : public PK_Ops::Verification_with_Hash { public: - DSA_Verification_Operation(const std::shared_ptr& key, const PK_Signature_Options& options) : + DSA_Verification_Operation(const std::shared_ptr& key, PK_Signature_Options& options) : PK_Ops::Verification_with_Hash(options), m_key(key) {} DSA_Verification_Operation(const std::shared_ptr& key, const AlgorithmIdentifier& alg_id) : @@ -251,12 +251,9 @@ bool DSA_Verification_Operation::verify(std::span input, std::spa } // namespace -std::unique_ptr DSA_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(this->m_public_key, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr DSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(this->m_public_key, options); } std::unique_ptr DSA_PublicKey::create_x509_verification_op( @@ -269,11 +266,9 @@ std::unique_ptr DSA_PublicKey::create_x509_verification_op } std::unique_ptr DSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(this->m_private_key, options, rng); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(this->m_private_key, options, rng); } } // namespace Botan diff --git a/src/lib/pubkey/dsa/dsa.h b/src/lib/pubkey/dsa/dsa.h index 10f9abcd083..8e97aeac602 100644 --- a/src/lib/pubkey/dsa/dsa.h +++ b/src/lib/pubkey/dsa/dsa.h @@ -59,7 +59,7 @@ class BOTAN_PUBLIC_API(2, 0) DSA_PublicKey : public virtual Public_Key { const BigInt& get_int_field(std::string_view field) const override; - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -115,7 +115,7 @@ class BOTAN_PUBLIC_API(2, 0) DSA_PrivateKey final : public DSA_PublicKey, secure_vector raw_private_key_bits() const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: std::shared_ptr m_private_key; diff --git a/src/lib/pubkey/ecdsa/ecdsa.cpp b/src/lib/pubkey/ecdsa/ecdsa.cpp index 44eb6199f60..5d889dd028e 100644 --- a/src/lib/pubkey/ecdsa/ecdsa.cpp +++ b/src/lib/pubkey/ecdsa/ecdsa.cpp @@ -120,7 +120,7 @@ namespace { class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa, - const PK_Signature_Options& options, + PK_Signature_Options& options, RandomNumberGenerator& rng) : PK_Ops::Signature_with_Hash(options), m_group(ecdsa.domain()), @@ -196,7 +196,7 @@ std::vector ECDSA_Signature_Operation::raw_sign(std::span msg, std::spa } // namespace -std::unique_ptr ECDSA_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr ECDSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } std::unique_ptr ECDSA_PublicKey::create_x509_verification_op( @@ -248,12 +245,9 @@ std::unique_ptr ECDSA_PublicKey::create_x509_verification_ } std::unique_ptr ECDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options, rng); - } - - throw Provider_Not_Found(algo_name(), options.provider().value()); + PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options, rng); } } // namespace Botan diff --git a/src/lib/pubkey/ecdsa/ecdsa.h b/src/lib/pubkey/ecdsa/ecdsa.h index 1c886d5480f..47e9e27caf9 100644 --- a/src/lib/pubkey/ecdsa/ecdsa.h +++ b/src/lib/pubkey/ecdsa/ecdsa.h @@ -62,7 +62,7 @@ class BOTAN_PUBLIC_API(2, 0) ECDSA_PublicKey : public virtual EC_PublicKey { uint8_t recovery_param(const std::vector& msg, const BigInt& r, const BigInt& s) const; - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -103,7 +103,7 @@ class BOTAN_PUBLIC_API(2, 0) ECDSA_PrivateKey final : public ECDSA_PublicKey, std::unique_ptr public_key() const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP diff --git a/src/lib/pubkey/ecgdsa/ecgdsa.cpp b/src/lib/pubkey/ecgdsa/ecgdsa.cpp index 1ea2daba9c7..c1129544dda 100644 --- a/src/lib/pubkey/ecgdsa/ecgdsa.cpp +++ b/src/lib/pubkey/ecgdsa/ecgdsa.cpp @@ -36,7 +36,7 @@ namespace { */ class ECGDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: - ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa, const PK_Signature_Options& options) : + ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa, PK_Signature_Options& options) : PK_Ops::Signature_with_Hash(options), m_group(ecgdsa.domain()), m_x(ecgdsa._private_key()) {} std::vector raw_sign(std::span msg, RandomNumberGenerator& rng) override; @@ -79,7 +79,7 @@ std::vector ECGDSA_Signature_Operation::raw_sign(std::span ECGDSA_PublicKey::generate_another(RandomNumberGene return std::make_unique(rng, domain()); } -std::unique_ptr ECGDSA_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr ECGDSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } std::unique_ptr ECGDSA_PublicKey::create_x509_verification_op( @@ -135,12 +132,10 @@ std::unique_ptr ECGDSA_PublicKey::create_x509_verification } std::unique_ptr ECGDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } } // namespace Botan diff --git a/src/lib/pubkey/ecgdsa/ecgdsa.h b/src/lib/pubkey/ecgdsa/ecgdsa.h index b1e9d803e8f..279a953f751 100644 --- a/src/lib/pubkey/ecgdsa/ecgdsa.h +++ b/src/lib/pubkey/ecgdsa/ecgdsa.h @@ -46,7 +46,7 @@ class BOTAN_PUBLIC_API(2, 0) ECGDSA_PublicKey : public virtual EC_PublicKey { bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -87,7 +87,7 @@ class BOTAN_PUBLIC_API(2, 0) ECGDSA_PrivateKey final : public ECGDSA_PublicKey, bool check_key(RandomNumberGenerator& rng, bool) const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.cpp b/src/lib/pubkey/eckcdsa/eckcdsa.cpp index 87e50b095df..948aabd3359 100644 --- a/src/lib/pubkey/eckcdsa/eckcdsa.cpp +++ b/src/lib/pubkey/eckcdsa/eckcdsa.cpp @@ -38,17 +38,13 @@ bool ECKCDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) cons namespace { -std::unique_ptr eckcdsa_signature_hash(const PK_Signature_Options& options) { - BOTAN_ARG_CHECK(!options.using_padding(), "ECKCDSA does not support padding modes"); - BOTAN_ARG_CHECK(!options.using_salt_size(), "ECKCDSA does not support a salt"); - - // We could support this, but it's not standard - BOTAN_ARG_CHECK(!options.using_prehash(), "ECKCDSA does not support prehashing"); +std::unique_ptr eckcdsa_signature_hash(PK_Signature_Options& options) { + // TODO: We could support prehashing, but it's not standard // intentionally not supporting Raw for ECKCDSA, since we need to know // the length in advance which complicates the logic for Raw - return HashFunction::create_or_throw(options.hash_function_name()); + return HashFunction::create_or_throw(options.hash_function()); } std::unique_ptr eckcdsa_signature_hash(const AlgorithmIdentifier& alg_id) { @@ -109,7 +105,7 @@ void truncate_hash_if_needed(std::vector& digest, size_t group_order_by */ class ECKCDSA_Signature_Operation final : public PK_Ops::Signature { public: - ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& key, const PK_Signature_Options& options) : + ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& key, PK_Signature_Options& options) : m_group(key.domain()), m_x(key._private_key()), m_hash(eckcdsa_signature_hash(options)), @@ -179,7 +175,7 @@ std::vector ECKCDSA_Signature_Operation::raw_sign(std::span ECKCDSA_PublicKey::generate_another(RandomNumberGen return std::make_unique(rng, domain()); } -std::unique_ptr ECKCDSA_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr ECKCDSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } std::unique_ptr ECKCDSA_PublicKey::create_x509_verification_op( @@ -274,13 +267,10 @@ std::unique_ptr ECKCDSA_PublicKey::create_x509_verificatio } std::unique_ptr ECKCDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } } // namespace Botan diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.h b/src/lib/pubkey/eckcdsa/eckcdsa.h index 95f14e97d4d..08afbbee984 100644 --- a/src/lib/pubkey/eckcdsa/eckcdsa.h +++ b/src/lib/pubkey/eckcdsa/eckcdsa.h @@ -45,7 +45,7 @@ class BOTAN_PUBLIC_API(2, 0) ECKCDSA_PublicKey : public virtual EC_PublicKey { bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -86,7 +86,7 @@ class BOTAN_PUBLIC_API(2, 0) ECKCDSA_PrivateKey final : public ECKCDSA_PublicKey std::unique_ptr public_key() const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP diff --git a/src/lib/pubkey/ed25519/ed25519.h b/src/lib/pubkey/ed25519/ed25519.h index 3fba1920a66..fd8c8ad2cd6 100644 --- a/src/lib/pubkey/ed25519/ed25519.h +++ b/src/lib/pubkey/ed25519/ed25519.h @@ -48,7 +48,7 @@ class BOTAN_PUBLIC_API(2, 2) Ed25519_PublicKey : public virtual Public_Key { Ed25519_PublicKey(const uint8_t pub_key[], size_t len); - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -96,7 +96,7 @@ class BOTAN_PUBLIC_API(2, 2) Ed25519_PrivateKey final : public Ed25519_PublicKey bool check_key(RandomNumberGenerator& rng, bool strong) const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& option) const override; + PK_Signature_Options& option) const override; private: secure_vector m_private; diff --git a/src/lib/pubkey/ed25519/ed25519_key.cpp b/src/lib/pubkey/ed25519/ed25519_key.cpp index 355eec19a94..c1d514c32a5 100644 --- a/src/lib/pubkey/ed25519/ed25519_key.cpp +++ b/src/lib/pubkey/ed25519/ed25519_key.cpp @@ -273,23 +273,15 @@ class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature { } // namespace -std::unique_ptr Ed25519_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - BOTAN_ARG_CHECK(!options.using_padding(), "Ed25519 does not support padding"); - - if(!options.using_provider()) { - if(options.using_prehash()) { - if(options.prehash_fn().has_value()) { - return std::make_unique(*this, options.prehash_fn().value(), false); - } else { - return std::make_unique(*this, "SHA-512", true); - } - } else { - return std::make_unique(*this); - } - } +std::unique_ptr Ed25519_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); - throw Provider_Not_Found(algo_name(), options.provider().value()); + if(auto [uses_prehash, prehash_fn] = options.prehash(); uses_prehash) { + return std::make_unique( + *this, prehash_fn.value_or("SHA-512"), !prehash_fn.has_value()); + } else { + return std::make_unique(*this); + } } std::unique_ptr Ed25519_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, @@ -305,25 +297,16 @@ std::unique_ptr Ed25519_PublicKey::create_x509_verificatio } std::unique_ptr Ed25519_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); + options.exclude_provider_for_algorithm(algo_name()); - BOTAN_ARG_CHECK(!options.using_padding(), "Ed25519 does not support padding"); - BOTAN_ARG_CHECK(!options.using_salt_size(), "Ed25519 does not support a salt"); - - if(!options.using_provider()) { - if(options.using_prehash()) { - if(options.prehash_fn().has_value()) { - return std::make_unique(*this, options.prehash_fn().value(), false); - } else { - return std::make_unique(*this, "SHA-512", true); - } - } else { - return std::make_unique(*this); - } + if(auto [uses_prehash, prehash_fn] = options.prehash(); uses_prehash) { + return std::make_unique( + *this, prehash_fn.value_or("SHA-512"), !prehash_fn.has_value()); + } else { + return std::make_unique(*this); } - - throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/gost_3410/gost_3410.cpp b/src/lib/pubkey/gost_3410/gost_3410.cpp index 8842af520f6..3acfad77a9d 100644 --- a/src/lib/pubkey/gost_3410/gost_3410.cpp +++ b/src/lib/pubkey/gost_3410/gost_3410.cpp @@ -116,7 +116,7 @@ EC_Scalar gost_msg_to_scalar(const EC_Group& group, std::span msg */ class GOST_3410_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: - GOST_3410_Signature_Operation(const GOST_3410_PrivateKey& gost_3410, const PK_Signature_Options& options) : + GOST_3410_Signature_Operation(const GOST_3410_PrivateKey& gost_3410, PK_Signature_Options& options) : PK_Ops::Signature_with_Hash(options), m_group(gost_3410.domain()), m_x(gost_3410._private_key()) {} size_t signature_length() const override { return 2 * m_group.get_order_bytes(); } @@ -195,7 +195,7 @@ PK_Signature_Options gost_hash_from_algid(const AlgorithmIdentifier& alg_id) { */ class GOST_3410_Verification_Operation final : public PK_Ops::Verification_with_Hash { public: - GOST_3410_Verification_Operation(const GOST_3410_PublicKey& gost, const PK_Signature_Options& options) : + GOST_3410_Verification_Operation(const GOST_3410_PublicKey& gost, PK_Signature_Options& options) : PK_Ops::Verification_with_Hash(options), m_group(gost.domain()), m_gy_mul(gost._public_key()) {} GOST_3410_Verification_Operation(const GOST_3410_PublicKey& gost, const AlgorithmIdentifier& alg_id) : @@ -234,11 +234,9 @@ std::unique_ptr GOST_3410_PublicKey::generate_another(RandomNumberG } std::unique_ptr GOST_3410_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } std::unique_ptr GOST_3410_PublicKey::create_x509_verification_op( @@ -250,14 +248,11 @@ std::unique_ptr GOST_3410_PublicKey::create_x509_verificat throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr GOST_3410_PrivateKey::_create_signature_op( - RandomNumberGenerator& rng, const PK_Signature_Options& options) const { +std::unique_ptr GOST_3410_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } } // namespace Botan diff --git a/src/lib/pubkey/gost_3410/gost_3410.h b/src/lib/pubkey/gost_3410/gost_3410.h index ee35471139f..de5ad22d499 100644 --- a/src/lib/pubkey/gost_3410/gost_3410.h +++ b/src/lib/pubkey/gost_3410/gost_3410.h @@ -54,7 +54,7 @@ class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual EC_PublicKey { bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -94,7 +94,7 @@ class BOTAN_PUBLIC_API(2, 0) GOST_3410_PrivateKey final : public GOST_3410_Publi AlgorithmIdentifier pkcs8_algorithm_identifier() const override { return EC_PublicKey::algorithm_identifier(); } std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP diff --git a/src/lib/pubkey/hss_lms/hss_lms.cpp b/src/lib/pubkey/hss_lms/hss_lms.cpp index 616156dc94f..c896fbef936 100644 --- a/src/lib/pubkey/hss_lms/hss_lms.cpp +++ b/src/lib/pubkey/hss_lms/hss_lms.cpp @@ -11,7 +11,6 @@ #include #include #include -#include namespace Botan { @@ -86,12 +85,10 @@ class HSS_LMS_Verification_Operation final : public PK_Ops::Verification { std::vector m_msg_buffer; }; -std::unique_ptr HSS_LMS_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(m_public); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr HSS_LMS_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + options.validate_for_hash_based_signature_algorithm(algo_name()); + return std::make_unique(m_public); } std::unique_ptr HSS_LMS_PublicKey::create_x509_verification_op( @@ -197,17 +194,13 @@ class HSS_LMS_Signature_Operation final : public PK_Ops::Signature { }; std::unique_ptr HSS_LMS_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - validate_for_hash_based_signature(options, "HSS-LMS", ""); + options.exclude_provider_for_algorithm(algo_name()); + options.validate_for_hash_based_signature_algorithm(algo_name()); - BOTAN_ARG_CHECK(!options.using_hash(), "Unexpected parameters for signing with HSS-LMS"); - - if(!options.using_provider()) { - return std::make_unique(m_private, m_public); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + return std::make_unique(m_private, m_public); } } // namespace Botan diff --git a/src/lib/pubkey/hss_lms/hss_lms.h b/src/lib/pubkey/hss_lms/hss_lms.h index 1a46a653737..78798f359ff 100644 --- a/src/lib/pubkey/hss_lms/hss_lms.h +++ b/src/lib/pubkey/hss_lms/hss_lms.h @@ -51,7 +51,7 @@ class BOTAN_PUBLIC_API(3, 5) HSS_LMS_PublicKey : public virtual Public_Key { std::vector raw_public_key_bits() const override; std::vector public_key_bits() const override; - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -146,7 +146,7 @@ class BOTAN_PUBLIC_API(3, 5) HSS_LMS_PrivateKey final : public virtual HSS_LMS_P std::unique_ptr generate_another(RandomNumberGenerator& rng) const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: HSS_LMS_PrivateKey(std::shared_ptr sk); diff --git a/src/lib/pubkey/info.txt b/src/lib/pubkey/info.txt index dc3843fa45b..292d73a2f0d 100644 --- a/src/lib/pubkey/info.txt +++ b/src/lib/pubkey/info.txt @@ -21,7 +21,6 @@ x509_key.h blinding.h pk_ops_impl.h -pk_options_impl.h workfactor.h diff --git a/src/lib/pubkey/pk_keys.cpp b/src/lib/pubkey/pk_keys.cpp index 28c20916bf0..e46adb62c6b 100644 --- a/src/lib/pubkey/pk_keys.cpp +++ b/src/lib/pubkey/pk_keys.cpp @@ -11,8 +11,8 @@ #include #include #include +#include #include -#include namespace Botan { @@ -103,7 +103,7 @@ std::unique_ptr Public_Key::create_kem_encryption_op(std throw Lookup_Error(fmt("{} does not support KEM encryption", algo_name())); } -std::unique_ptr Public_Key::_create_verification_op(const PK_Signature_Options& options) const { +std::unique_ptr Public_Key::_create_verification_op(PK_Signature_Options& options) const { BOTAN_UNUSED(options); throw Lookup_Error(fmt("{} does not support verification", algo_name())); } @@ -126,7 +126,7 @@ std::unique_ptr Private_Key::create_kem_decryption_op(Ra } std::unique_ptr Private_Key::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng, options); throw Lookup_Error(fmt("{} does not support signatures", algo_name())); } @@ -141,13 +141,15 @@ std::unique_ptr Private_Key::create_key_agreement_op(Rand std::unique_ptr Public_Key::create_verification_op(std::string_view params, std::string_view provider) const { - return this->_create_verification_op(parse_legacy_sig_options(*this, params).with_provider(provider)); + PK_Signature_Options opts(algo_name(), params, provider); + return this->_create_verification_op(opts); } std::unique_ptr Private_Key::create_signature_op(RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { - return this->_create_signature_op(rng, parse_legacy_sig_options(*this, params).with_provider(provider)); + PK_Signature_Options opts(algo_name(), params, provider); + return this->_create_signature_op(rng, opts); } } // namespace Botan diff --git a/src/lib/pubkey/pk_keys.h b/src/lib/pubkey/pk_keys.h index d135e9dd264..8d18448186c 100644 --- a/src/lib/pubkey/pk_keys.h +++ b/src/lib/pubkey/pk_keys.h @@ -257,7 +257,7 @@ class BOTAN_PUBLIC_API(2, 0) Public_Key : public virtual Asymmetric_Key { * @param options which specify parameters of the signature beyond those * implicit to the public key itself */ - virtual std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const; + virtual std::unique_ptr _create_verification_op(PK_Signature_Options& options) const; /** * This is an internal library function exposed on key types. @@ -392,7 +392,7 @@ class BOTAN_PUBLIC_API(2, 0) Private_Key : public virtual Public_Key { * @param options allow controlling behavior */ virtual std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const; + PK_Signature_Options& options) const; /** * This is an internal library function exposed on key types. diff --git a/src/lib/pubkey/pk_ops.cpp b/src/lib/pubkey/pk_ops.cpp index e13f48b96c3..b7a5a0842d4 100644 --- a/src/lib/pubkey/pk_ops.cpp +++ b/src/lib/pubkey/pk_ops.cpp @@ -84,42 +84,40 @@ secure_vector PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len, namespace { -std::unique_ptr validate_options_returning_hash(const PK_Signature_Options& options) { - BOTAN_ARG_CHECK(!options.hash_function_name().empty(), "This algorithm requires a hash function for signing"); - BOTAN_ARG_CHECK(!options.using_padding(), "This algorithm does not support padding modes"); - BOTAN_ARG_CHECK(!options.using_salt_size(), "This algorithm does not support a salt"); +std::unique_ptr validate_options_returning_hash(PK_Signature_Options& options) { + const auto hash = options.hash_function(); /* * In a sense ECDSA/DSA are *always* in prehashing mode, so we accept the case * where prehashing is requested as long as the prehash hash matches the signature hash. */ - if(options.prehash_fn().has_value()) { - if(options.prehash_fn().value() != options.hash_function_name()) { + if(auto [uses_prehash, prehash_fn] = options.prehash(); uses_prehash) { + if(prehash_fn.has_value() && prehash_fn.value() != hash) { throw Invalid_Argument("This algorithm does not support prehashing with a different hash"); } } #if defined(BOTAN_HAS_RAW_HASH_FN) - if(options.hash_function_name().starts_with("Raw")) { - if(options.hash_function_name() == "Raw") { + if(hash.starts_with("Raw")) { + if(hash == "Raw") { return std::make_unique("Raw", 0); } - SCAN_Name req(options.hash_function_name()); + SCAN_Name req(hash); if(req.arg_count() == 1) { - if(auto hash = HashFunction::create(req.arg(0))) { - return std::make_unique(std::move(hash)); + if(auto hash_object = HashFunction::create(req.arg(0))) { + return std::make_unique(std::move(hash_object)); } } } #endif - return HashFunction::create_or_throw(options.hash_function_name()); + return HashFunction::create_or_throw(hash); } } // namespace -PK_Ops::Signature_with_Hash::Signature_with_Hash(const PK_Signature_Options& options) : +PK_Ops::Signature_with_Hash::Signature_with_Hash(PK_Signature_Options& options) : Signature(), m_hash(validate_options_returning_hash(options)) {} #if defined(BOTAN_HAS_RFC6979_GENERATOR) @@ -141,7 +139,7 @@ std::vector PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rn return raw_sign(msg, rng); } -PK_Ops::Verification_with_Hash::Verification_with_Hash(const PK_Signature_Options& options) : +PK_Ops::Verification_with_Hash::Verification_with_Hash(PK_Signature_Options& options) : Verification(), m_hash(validate_options_returning_hash(options)) {} PK_Ops::Verification_with_Hash::Verification_with_Hash(const AlgorithmIdentifier& alg_id, diff --git a/src/lib/pubkey/pk_ops_impl.h b/src/lib/pubkey/pk_ops_impl.h index a6c05968e39..51318d46593 100644 --- a/src/lib/pubkey/pk_ops_impl.h +++ b/src/lib/pubkey/pk_ops_impl.h @@ -58,7 +58,9 @@ class Verification_with_Hash : public Verification { std::string hash_function() const final { return m_hash->name(); } protected: - explicit Verification_with_Hash(const PK_Signature_Options& options); + explicit Verification_with_Hash(PK_Signature_Options& options); + + explicit Verification_with_Hash(PK_Signature_Options&& options) : Verification_with_Hash(options) {} explicit Verification_with_Hash(const AlgorithmIdentifier& alg_id, std::string_view pk_algo, @@ -66,10 +68,8 @@ class Verification_with_Hash : public Verification { /* * Perform a signature check operation - * @param msg the message - * @param msg_len the length of msg in bytes + * @param input the message * @param sig the signature - * @param sig_len the length of sig in bytes * @returns if signature is a valid one for message */ virtual bool verify(std::span input, std::span sig) = 0; @@ -87,7 +87,9 @@ class Signature_with_Hash : public Signature { ~Signature_with_Hash() override = default; protected: - explicit Signature_with_Hash(const PK_Signature_Options& options); + explicit Signature_with_Hash(PK_Signature_Options& options); + + explicit Signature_with_Hash(PK_Signature_Options&& options) : Signature_with_Hash(options) {} std::string hash_function() const final { return m_hash->name(); } diff --git a/src/lib/pubkey/pk_options.cpp b/src/lib/pubkey/pk_options.cpp index 34d44fb0372..9bacc38f0a3 100644 --- a/src/lib/pubkey/pk_options.cpp +++ b/src/lib/pubkey/pk_options.cpp @@ -1,123 +1,200 @@ /* * (C) 2024 Jack Lloyd +* (C) 2024 René Meusel - Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ #include -#include -#include -#include #include -#include +#include namespace Botan { -PK_Signature_Options::~PK_Signature_Options() = default; +PK_Signature_Options::PK_Signature_Options(std::string_view algo, std::string_view params, std::string_view provider) { + /* + * This is a convoluted mess because we must handle dispatch for every algorithm + * specific detail of how padding strings were formatted in versions prior to 3.6. + * + * This will all go away once the deprecated constructors of PK_Signer and PK_Verifier + * are removed in Botan4. + */ -PK_Signature_Options PK_Signature_Options::with_hash(std::string_view hash) && { - BOTAN_STATE_CHECK_MSG(!using_hash(), "PK_Signature_Options::with_hash cannot specify hash twice"); - if(!hash.empty()) { - this->m_hash_fn = hash; - } - return std::move(*this); -} + auto options = PK_Signature_Options(); -PK_Signature_Options PK_Signature_Options::with_padding(std::string_view padding) && { - BOTAN_STATE_CHECK_MSG(!using_padding(), "PK_Signature_Options::with_padding cannot specify padding twice"); - if(!padding.empty()) { - this->m_padding = padding; + if(!provider.empty() && provider != "base") { + with_provider(provider); } - return std::move(*this); -} - -PK_Signature_Options PK_Signature_Options::with_prehash(std::optional prehash_fn) && { - BOTAN_STATE_CHECK_MSG(!using_prehash(), "PK_Signature_Options::with_prehash cannot specify prehash twice"); - this->m_using_prehash = true; - this->m_prehash = std::move(prehash_fn); - return std::move(*this); -} - -PK_Signature_Options PK_Signature_Options::with_provider(std::string_view provider) && { - if(!provider.empty()) { - BOTAN_STATE_CHECK_MSG(!using_provider(), "PK_Signature_Options::with_provider cannot specify provider twice"); - this->m_provider = provider; - } - return std::move(*this); -} - -PK_Signature_Options PK_Signature_Options::with_context(std::span context) && { - BOTAN_STATE_CHECK_MSG(!using_context(), "PK_Signature_Options::with_context cannot specify context twice"); - this->m_context = std::vector(context.begin(), context.end()); - return std::move(*this); -} - -PK_Signature_Options PK_Signature_Options::with_context(std::string_view context) && { - BOTAN_STATE_CHECK_MSG(!using_context(), "PK_Signature_Options::with_context cannot specify context twice"); - const uint8_t* ptr = cast_char_ptr_to_uint8(context.data()); - this->m_context = std::vector(ptr, ptr + context.size()); - return std::move(*this); -} - -PK_Signature_Options PK_Signature_Options::with_salt_size(size_t salt_size) && { - BOTAN_STATE_CHECK_MSG(!using_salt_size(), "PK_Signature_Options::with_salt_size cannot specify salt size twice"); - this->m_salt_size = salt_size; - return std::move(*this); -} - -PK_Signature_Options PK_Signature_Options::with_deterministic_signature() && { - this->m_deterministic_sig = true; - return std::move(*this); -} - -PK_Signature_Options PK_Signature_Options::with_der_encoded_signature(bool der) && { - this->m_use_der = der; - return std::move(*this); -} -PK_Signature_Options PK_Signature_Options::with_explicit_trailer_field() && { - this->m_explicit_trailer_field = true; - return std::move(*this); -} + if(algo.starts_with("Dilithium") || algo == "SPHINCS+") { + if(!params.empty() && params != "Randomized" && params != "Deterministic") { + throw Invalid_Argument(fmt("Unexpected parameters for signing with {}", algo)); + } -std::string PK_Signature_Options::hash_function_name() const { - if(m_hash_fn.has_value()) { - return m_hash_fn.value(); + if(params == "Deterministic") { + with_deterministic_signature(); + } + } else if(algo == "SM2") { + /* + * SM2 parameters have the following possible formats: + * Ident [since 2.2.0] + * Ident,Hash [since 2.3.0] + */ + if(params.empty()) { + with_hash("SM3"); + } else { + const auto [userid, hash] = [&]() -> std::pair { + if(const auto comma = params.find(','); comma != std::string::npos) { + return {params.substr(0, comma), params.substr(comma + 1)}; + } else { + return {params, "SM3"}; + } + }(); + + with_context(userid); + with_hash(hash); + } + } else if(algo == "Ed25519") { + if(!params.empty() && params != "Identity" && params != "Pure") { + if(params == "Ed25519ph") { + with_prehash(); + } else { + with_prehash(std::string(params)); + } + } + } else if(algo == "Ed448") { + if(!params.empty() && params != "Identity" && params != "Pure" && params != "Ed448") { + if(params == "Ed448ph") { + with_prehash(); + } else { + with_prehash(std::string(params)); + } + } + } else if(algo == "RSA") { + SCAN_Name req(params); + + // handling various deprecated aliases that have accumulated over the years ... + auto padding = [](std::string_view alg) -> std::string_view { + if(alg == "EMSA_PKCS1" || alg == "EMSA-PKCS1-v1_5" || alg == "EMSA3") { + return "PKCS1v15"; + } + + if(alg == "PSSR_Raw") { + return "PSS_Raw"; + } + + if(alg == "PSSR" || alg == "EMSA-PSS" || alg == "PSS-MGF1" || alg == "EMSA4") { + return "PSS"; + } + + if(alg == "EMSA_X931" || alg == "EMSA2" || alg == "X9.31") { + return "X9.31"; + } + + return alg; + }(req.algo_name()); + + if(padding == "Raw") { + if(req.arg_count() == 0) { + with_padding(padding); + } else if(req.arg_count() == 1) { + with_padding(padding).with_prehash(req.arg(0)); + } else { + throw Invalid_Argument("Raw padding with more than one parameter"); + } + } else if(padding == "PKCS1v15") { + if(req.arg_count() == 2 && req.arg(0) == "Raw") { + with_padding(padding); + with_hash(req.arg(0)); + with_prehash(req.arg(1)); + } else if(req.arg_count() == 1) { + with_padding(padding); + with_hash(req.arg(0)); + } else { + throw Lookup_Error("PKCS1v15 padding with unexpected parameters"); + } + } else if(padding == "PSS_Raw" || padding == "PSS") { + if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { + with_padding(padding); + with_hash(req.arg(0)); + + if(req.arg_count() == 3) { + with_salt_size(req.arg_as_integer(2)); + } + } else { + throw Lookup_Error("PSS padding with unexpected parameters"); + } + } else if(padding == "ISO_9796_DS2") { + if(req.arg_count_between(1, 3)) { + // FIXME + const bool implicit = req.arg(1, "exp") == "imp"; + + with_padding(padding); + with_hash(req.arg(0)); + + if(req.arg_count() == 3) { + if(implicit) { + with_salt_size(req.arg_as_integer(2)); + } else { + with_salt_size(req.arg_as_integer(2)); + with_explicit_trailer_field(); + } + } else if(!implicit) { + with_explicit_trailer_field(); + } + } else { + throw Lookup_Error("ISO-9796-2 DS2 padding with unexpected parameters"); + } + } else if(padding == "ISO_9796_DS3") { + //ISO-9796-2 DS 3 is deterministic and DS2 without a salt + with_padding(padding); + if(req.arg_count_between(1, 2)) { + with_hash(req.arg(0)); + if(req.arg(1, "exp") != "imp") { + with_explicit_trailer_field(); + } + } else { + throw Lookup_Error("ISO-9796-2 DS3 padding with unexpected parameters"); + } + } else if(padding == "X9.31") { + if(req.arg_count() == 1) { + with_padding(padding); + with_hash(req.arg(0)); + } else { + throw Lookup_Error("X9.31 padding with unexpected parameters"); + } + } + // end of RSA block + } else { + if(!params.empty()) { + // ECDSA/DSA/ECKCDSA/etc + auto hash = [&]() { + if(params.starts_with("EMSA1")) { + SCAN_Name req(params); + return req.arg(0); + } else { + return std::string(params); + } + }; + + with_hash(hash()); + } } - - throw Invalid_State("This signature scheme requires specifying a hash function"); } -std::string PK_Signature_Options::to_string() const { - std::ostringstream out; - - auto print_str = [&](std::string_view name, std::optional val) { - if(val.has_value()) { - out << name << "='" << val.value() << "' "; +void PK_Signature_Options::validate_for_hash_based_signature_algorithm( + std::string_view algo_name, std::optional acceptable_hash) { + if(auto hash = take(m_hash_fn)) { + if(!acceptable_hash.has_value()) { + throw Invalid_Argument(fmt("This {} key does not support explicit hash function choice", algo_name)); } - }; - - print_str("Hash", this->hash_function()); - print_str("Padding", this->padding()); - print_str("Prehash", this->prehash_fn()); - print_str("Provider", this->provider()); - - if(auto context = this->context()) { - out << "Context=" << hex_encode(*context) << " "; - } - if(auto salt = this->salt_size()) { - out << "SaltLen=" << *salt << " "; - } - if(this->using_der_encoded_signature()) { - out << "DerSignature "; - } - if(this->using_deterministic_signature()) { - out << "Deterministic "; + if(hash != acceptable_hash.value()) { + throw Invalid_Argument( + fmt("This {} key can only be used with {}, not {}", algo_name, acceptable_hash.value(), hash.value())); + } } - - return out.str(); } } // namespace Botan diff --git a/src/lib/pubkey/pk_options.h b/src/lib/pubkey/pk_options.h index 092eeb2524d..04234e0e7ad 100644 --- a/src/lib/pubkey/pk_options.h +++ b/src/lib/pubkey/pk_options.h @@ -1,5 +1,6 @@ /* * (C) 2024 Jack Lloyd +* 2024 René Meusel - Rohde & Schwarz Cybersecurity GmbH * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -7,6 +8,8 @@ #ifndef BOTAN_PK_OPTIONS_H_ #define BOTAN_PK_OPTIONS_H_ +#include +#include #include #include #include @@ -26,37 +29,51 @@ namespace Botan { * .with_der_encoded_signature() * .with_context("Foo") */ -class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options { +class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options : public Builder { public: - /// Create an empty PK_Signature_Options + /// Create a PK_Signature_Options with no options set /// /// This can be further parameterized by calling with_xxx functions PK_Signature_Options() = default; - PK_Signature_Options(PK_Signature_Options&& other) = default; - PK_Signature_Options& operator=(PK_Signature_Options&& other) = default; - - PK_Signature_Options(const PK_Signature_Options&) = delete; - PK_Signature_Options& operator=(const PK_Signature_Options& other) = delete; - ~PK_Signature_Options(); - - /// Format this PK_Signature_Options as a string - /// - /// This is primarily intended for debugging and error messages; - /// the format is not fixed - std::string to_string() const; - /// Create a PK_Signature_Options specifying the hash to use /// - /// Most but not all signture schemes require specifying the hash + /// Most but not all signature schemes require specifying the hash /// /// This can be further parameterized by calling with_xxx functions - PK_Signature_Options(std::string_view hash_fn) : m_hash_fn(hash_fn) {} + PK_Signature_Options(std::string_view hash_fn) { with_hash(hash_fn); } + + /// Parse the legacy set of parameters that used to be passed to + /// PK_Signer/PK_Verifier. This should not be used by new code. + /// + /// @param algo the public key algorithm name + /// @param params the legacy parameters string + /// @param provider the provider to use + PK_Signature_Options(std::string_view algo, std::string_view params, std::string_view provider); /// Specify the hash function to use for signing/verification /// /// Most, but not all, schemes require specifying a hash function. - PK_Signature_Options with_hash(std::string_view hash) &&; + PK_Signature_Options& with_hash(std::string_view hash) & { + set_or_throw(m_hash_fn, std::string(hash)); + return *this; + } + + /// Specify the hash function to use for signing/verification + /// + /// Most, but not all, schemes require specifying a hash function. + PK_Signature_Options with_hash(std::string_view hash) && { return std::move(with_hash(hash)); } + + /// Specify a padding scheme + /// + /// This is mostly/only used for RSA + /// + /// If the scheme does not support a padding option, it will throw an + /// exception when presented with such an option. + PK_Signature_Options& with_padding(std::string_view padding) & { + set_or_throw(m_padding, std::string(padding)); + return *this; + } /// Specify a padding scheme /// @@ -64,7 +81,7 @@ class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options { /// /// If the scheme does not support a padding option, it will throw an /// exception when presented with such an option. - PK_Signature_Options with_padding(std::string_view padding) &&; + PK_Signature_Options with_padding(std::string_view padding) && { return std::move(with_padding(padding)); } /// Specify the signature is prehashed /// @@ -79,7 +96,27 @@ class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options { /// /// If the scheme does not support prehashing, it will throw an /// exception when presented with such an option. - PK_Signature_Options with_prehash(std::optional prehash = std::nullopt) &&; + PK_Signature_Options& with_prehash(std::optional prehash = std::nullopt) & { + set_or_throw(m_prehash, std::move(prehash)); + return *this; + } + + /// Specify the signature is prehashed + /// + /// Some signature schemes, such as Ed25519, normally sign the + /// entire message along with some context data. However such + /// schemes also sometimes offer a prehashing variant where the + /// message is hashed on its own, then the hash is signed. + /// + /// If given this specifies what hash function to use for prehashing. + /// If prehash is nullopt, this requests prehashing using an algorithm + /// specific default function + /// + /// If the scheme does not support prehashing, it will throw an + /// exception when presented with such an option. + PK_Signature_Options with_prehash(std::optional prehash = std::nullopt) && { + return std::move(with_prehash(std::move(prehash))); + } /// Specify a context /// @@ -90,19 +127,63 @@ class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options { /// /// If the scheme does not support contextual identifiers, then an exception /// will be thrown. - PK_Signature_Options with_context(std::span context) &&; + PK_Signature_Options& with_context(std::span context) & { + set_or_throw(m_context, std::vector(context.begin(), context.end())); + return *this; + } - /// Specify a context as a string + /// Specify a context /// - /// Equivalent to the version taking a span above; just uses the bytes - /// of the string instead. - PK_Signature_Options with_context(std::string_view context) &&; + /// Some signature schemes allow specifying a context with the signature. + /// This is typically a fixed string that identifies a protocol or peer. + /// + /// For SM2 this context is the user identifier + /// + /// If the scheme does not support contextual identifiers, then an exception + /// will be thrown. + PK_Signature_Options with_context(std::span context) && { + return std::move(with_context(context)); + } + + /// Specify a context + /// + /// Some signature schemes allow specifying a context with the signature. + /// This is typically a fixed string that identifies a protocol or peer. + /// + /// For SM2 this context is the user identifier + /// + /// If the scheme does not support contextual identifiers, then an exception + /// will be thrown. + PK_Signature_Options& with_context(std::string_view context) & { + with_context(std::span{cast_char_ptr_to_uint8(context.data()), context.size()}); + return *this; + } + + /// Specify a context + /// + /// Some signature schemes allow specifying a context with the signature. + /// This is typically a fixed string that identifies a protocol or peer. + /// + /// For SM2 this context is the user identifier + /// + /// If the scheme does not support contextual identifiers, then an exception + /// will be thrown. + PK_Signature_Options with_context(std::string_view context) && { return std::move(with_context(context)); } + + /// Specify the size of salt to be used + /// + /// A small number of padding schemes (most importantly RSA-PSS) use a randomized + /// salt. This allows controlling the size of the salt that is used. + PK_Signature_Options& with_salt_size(size_t salt_size) & { + set_or_throw(m_salt_size, salt_size); + return *this; + } /// Specify the size of salt to be used /// /// A small number of padding schemes (most importantly RSA-PSS) use a randomized /// salt. This allows controlling the size of the salt that is used. - PK_Signature_Options with_salt_size(size_t salt_size) &&; + PK_Signature_Options with_salt_size(size_t salt_size) && { return std::move(with_salt_size(salt_size)); } /// Request producing a deterministic signature /// @@ -112,7 +193,34 @@ class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options { /// always randomized, this option has no effect. /// /// This option is ignored for verification - PK_Signature_Options with_deterministic_signature() &&; + PK_Signature_Options& with_deterministic_signature(bool value = true) & { + set_or_throw(m_deterministic_sig, value); + return *this; + } + + /// Request producing a deterministic signature + /// + /// Some signature schemes are always deterministic, or always randomized. + /// Others support both randomized or deterministic options. This allows + /// requesting this. For signatures which are always deterministic or + /// always randomized, this option has no effect. + /// + /// This option is ignored for verification + PK_Signature_Options with_deterministic_signature(bool value = true) && { + return std::move(with_deterministic_signature(value)); + } + + /// Specify producing or expecting a DER encoded signature + /// + /// This is mostly used with ECDSA + /// + /// For schemes that do not support such formatting (such as RSA + /// or post-quantum schemes), an exception will be thrown when the + /// PK_Signer or PK_Verifier is created. + PK_Signature_Options& with_der_encoded_signature(bool der = true) & { + set_or_throw(m_use_der, der); + return *this; + } /// Specify producing or expecting a DER encoded signature /// @@ -121,7 +229,9 @@ class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options { /// For schemes that do not support such formatting (such as RSA /// or post-quantum schemes), an exception will be thrown when the /// PK_Signer or PK_Verifier is created. - PK_Signature_Options with_der_encoded_signature(bool der = true) &&; + PK_Signature_Options with_der_encoded_signature(bool der = true) && { + return std::move(with_der_encoded_signature(der)); + } /// Specify producing or expecting an explicit trailer field /// @@ -135,61 +245,113 @@ class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options { /// 4055) prohibit using explicit trailers for PSS, and it is not /// currently supported. /// - PK_Signature_Options with_explicit_trailer_field() &&; + PK_Signature_Options& with_explicit_trailer_field(bool value = true) & { + set_or_throw(m_explicit_trailer_field, value); + return *this; + } + + /// Specify producing or expecting an explicit trailer field + /// + /// Certain RSA padding schemes, such as PSS and ISO-9796, support two + /// different trailer fields. One is an "implicit" trailer, which does not + /// directly identify the hash. The other is an "explicit" trailer, which + /// does. + /// + /// Note that currently this option is only supported by ISO-9796. While + /// some standards allow PSS to use a trailer field, others (such as RFC + /// 4055) prohibit using explicit trailers for PSS, and it is not + /// currently supported. + /// + PK_Signature_Options with_explicit_trailer_field(bool value = true) && { + return std::move(with_explicit_trailer_field(value)); + } + + /// Specify a provider that should be used + /// + /// This is rarely relevant + PK_Signature_Options& with_provider(std::string_view provider) & { + set_or_throw(m_provider, std::string(provider)); + return *this; + } /// Specify a provider that should be used /// /// This is rarely relevant - PK_Signature_Options with_provider(std::string_view provider) &&; + PK_Signature_Options with_provider(std::string_view provider) && { return std::move(with_provider(provider)); } /// Return the name of the hash function to use /// /// This will throw an exception if no hash function was configured - std::string hash_function_name() const; + // std::string hash_function_name() const; // Getters; these are mostly for internal use - const std::optional& hash_function() const { return m_hash_fn; } + [[nodiscard]] std::string hash_function() { return require(m_hash_fn); } - const std::optional& prehash_fn() const { return m_prehash; } + [[nodiscard]] std::optional maybe_hash_function() { return take(m_hash_fn); } - const std::optional& padding() const { return m_padding; } + /// It may be acceptable to provide a hash function, for hash-based + /// signatures (like SLH-DSA or LMS), but it is not required. + /// @throws Invalid_Argument if the provided hash is not acceptable + void validate_for_hash_based_signature_algorithm(std::string_view algo_name, + std::optional acceptable_hash = std::nullopt); - const std::optional>& context() const { return m_context; } + [[nodiscard]] std::pair> prehash() { + if(auto prehash = take(m_prehash)) { + return {true, std::move(prehash.value())}; + } else { + return {false, std::nullopt}; + } + } - const std::optional& provider() const { return m_provider; } + [[nodiscard]] std::optional padding() { return take(m_padding); } - const std::optional& salt_size() const { return m_salt_size; } + [[nodiscard]] std::optional> context() { return take(m_context); } - bool using_der_encoded_signature() const { return m_use_der; } + [[nodiscard]] std::optional provider() { return take(m_provider); } - bool using_deterministic_signature() const { return m_deterministic_sig; } + /// This is a convenience helper for algorithms that do not support + /// specifying a provider. + /// @throws Provider_Not_Found if a provider is set + void exclude_provider_for_algorithm(std::string_view algo_name) { + if(auto p = provider()) { + throw Provider_Not_Found(algo_name, p.value()); + }; + } - bool using_explicit_trailer_field() const { return m_explicit_trailer_field; } + [[nodiscard]] std::optional salt_size() { return take(m_salt_size); } - bool using_hash() const { return hash_function().has_value(); } + [[nodiscard]] bool using_der_encoded_signature() { return take(m_use_der).value_or(false); } - bool using_context() const { return context().has_value(); } + [[nodiscard]] bool using_deterministic_signature() { return take(m_deterministic_sig).value_or(false); } - bool using_prehash() const { return m_using_prehash; } + [[nodiscard]] bool using_explicit_trailer_field() { return take(m_explicit_trailer_field).value_or(false); } - bool using_padding() const { return padding().has_value(); } - - bool using_salt_size() const { return salt_size().has_value(); } - - bool using_provider() const { return provider().has_value() && provider().value() != "base"; } + private: + friend class Builder; + + auto all_options() const { + return std::tie(m_hash_fn, + m_prehash, + m_padding, + m_context, + m_provider, + m_salt_size, + m_use_der, + m_deterministic_sig, + m_explicit_trailer_field); + } private: - std::optional m_hash_fn; - std::optional m_prehash; - std::optional m_padding; - std::optional> m_context; - std::optional m_provider; - std::optional m_salt_size; - bool m_using_prehash = false; - bool m_use_der = false; - bool m_deterministic_sig = false; - bool m_explicit_trailer_field = false; + detail::Option<"hash", std::string> m_hash_fn; + detail::Option<"prehash", std::optional> m_prehash; + detail::Option<"padding", std::string> m_padding; + detail::Option<"context", std::vector> m_context; + detail::Option<"provider", std::string> m_provider; + detail::Option<"salt size", size_t> m_salt_size; + detail::Option<"use DER", bool> m_use_der; + detail::Option<"deterministic", bool> m_deterministic_sig; + detail::Option<"explicit trailer field", bool> m_explicit_trailer_field; }; } // namespace Botan diff --git a/src/lib/pubkey/pk_options_impl.cpp b/src/lib/pubkey/pk_options_impl.cpp deleted file mode 100644 index 42d96d31f05..00000000000 --- a/src/lib/pubkey/pk_options_impl.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* -* (C) 2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include - -#include -#include -#include -#include - -namespace Botan { - -PK_Signature_Options parse_legacy_sig_options(const Public_Key& key, std::string_view params) { - /* - * This is a convoluted mess because we must handle dispatch for every algorithm - * specific detail of how padding strings were formatted in versions prior to 3.6. - * - * This will all go away once the deprecated constructors of PK_Signer and PK_Verifier - * are removed in Botan4. - */ - - const std::string algo = key.algo_name(); - - if(algo.starts_with("Dilithium") || algo == "SPHINCS+") { - if(!params.empty() && params != "Randomized" && params != "Deterministic") { - throw Invalid_Argument(fmt("Unexpected parameters for signing with {}", algo)); - } - - if(params == "Deterministic") { - return PK_Signature_Options().with_deterministic_signature(); - } else { - return PK_Signature_Options(); - } - } - - if(algo == "SM2") { - /* - * SM2 parameters have the following possible formats: - * Ident [since 2.2.0] - * Ident,Hash [since 2.3.0] - */ - if(params.empty()) { - return PK_Signature_Options().with_hash("SM3"); - } else { - std::string userid; - std::string hash = "SM3"; - auto comma = params.find(','); - if(comma == std::string::npos) { - userid = params; - } else { - userid = params.substr(0, comma); - hash = params.substr(comma + 1, std::string::npos); - } - return PK_Signature_Options(hash).with_context(userid); - } - } - - if(algo == "Ed25519") { - if(params.empty() || params == "Identity" || params == "Pure") { - return PK_Signature_Options(); - } else if(params == "Ed25519ph") { - return PK_Signature_Options().with_prehash(); - } else { - return PK_Signature_Options().with_prehash(std::string(params)); - } - } - - if(algo == "Ed448") { - if(params.empty() || params == "Identity" || params == "Pure" || params == "Ed448") { - return PK_Signature_Options(); - } else if(params == "Ed448ph") { - return PK_Signature_Options().with_prehash(); - } else { - return PK_Signature_Options().with_prehash(std::string(params)); - } - } - - if(algo == "RSA") { - SCAN_Name req(params); - - // handling various deprecated aliases that have accumulated over the years ... - auto padding = [](std::string_view alg) -> std::string_view { - if(alg == "EMSA_PKCS1" || alg == "EMSA-PKCS1-v1_5" || alg == "EMSA3") { - return "PKCS1v15"; - } - - if(alg == "PSSR_Raw") { - return "PSS_Raw"; - } - - if(alg == "PSSR" || alg == "EMSA-PSS" || alg == "PSS-MGF1" || alg == "EMSA4") { - return "PSS"; - } - - if(alg == "EMSA_X931" || alg == "EMSA2" || alg == "X9.31") { - return "X9.31"; - } - - return alg; - }(req.algo_name()); - - if(padding == "Raw") { - if(req.arg_count() == 0) { - return PK_Signature_Options().with_padding(padding); - } else if(req.arg_count() == 1) { - return PK_Signature_Options().with_padding(padding).with_prehash(req.arg(0)); - } - } - - if(padding == "PKCS1v15") { - if(req.arg_count() == 2 && req.arg(0) == "Raw") { - return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)).with_prehash(req.arg(1)); - } else if(req.arg_count() == 1) { - return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); - } - } - - if(padding == "PSS_Raw" || padding == "PSS") { - if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { - auto pss_opt = PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); - - if(req.arg_count() == 3) { - return std::move(pss_opt).with_salt_size(req.arg_as_integer(2)); - } else { - return pss_opt; - } - } - } - - if(padding == "ISO_9796_DS2") { - if(req.arg_count_between(1, 3)) { - // FIXME - const bool implicit = req.arg(1, "exp") == "imp"; - - auto opt = PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); - - if(req.arg_count() == 3) { - if(implicit) { - return std::move(opt).with_salt_size(req.arg_as_integer(2)); - } else { - return std::move(opt).with_salt_size(req.arg_as_integer(2)).with_explicit_trailer_field(); - } - } else { - if(implicit) { - return opt; - } else { - return std::move(opt).with_explicit_trailer_field(); - } - } - } - } - - //ISO-9796-2 DS 3 is deterministic and DS2 without a salt - if(padding == "ISO_9796_DS3") { - if(req.arg_count_between(1, 2)) { - if(req.arg(1, "exp") == "imp") { - return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); - } else { - return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)).with_explicit_trailer_field(); - } - } - } - - if(padding == "X9.31" && req.arg_count() == 1) { - return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); - } - } // RSA block - - if(params.empty()) { - return PK_Signature_Options(); - } - - // ECDSA/DSA/ECKCDSA/etc - auto hash = [&]() { - if(params.starts_with("EMSA1")) { - SCAN_Name req(params); - return req.arg(0); - } else { - return std::string(params); - } - }(); - - return PK_Signature_Options().with_hash(hash); -} - -void validate_for_hash_based_signature(const PK_Signature_Options& options, - std::string_view algo_name, - std::string_view hash_fn) { - if(options.using_hash()) { - if(hash_fn.empty()) { - throw Invalid_Argument(fmt("This {} key does not support explicit hash function choice", algo_name)); - } else if(options.hash_function_name() != hash_fn) { - throw Invalid_Argument( - fmt("This {} key can only be used with {}, not {}", algo_name, hash_fn, options.hash_function_name())); - } - } - - if(options.using_padding()) { - throw Invalid_Argument(fmt("{} does not support padding modes", algo_name)); - } - - if(options.using_prehash()) { - throw Invalid_Argument(fmt("{} does not support prehashing")); - } -} - -} // namespace Botan diff --git a/src/lib/pubkey/pk_options_impl.h b/src/lib/pubkey/pk_options_impl.h deleted file mode 100644 index dd9905bac72..00000000000 --- a/src/lib/pubkey/pk_options_impl.h +++ /dev/null @@ -1,25 +0,0 @@ -/* -* (C) 2024 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_PK_OPTIONS_IMPL_H_ -#define BOTAN_PK_OPTIONS_IMPL_H_ - -#include -#include - -namespace Botan { - -class Public_Key; - -PK_Signature_Options parse_legacy_sig_options(const Public_Key& key, std::string_view params); - -void validate_for_hash_based_signature(const PK_Signature_Options& options, - std::string_view algo_name, - std::string_view hash_fn = ""); - -} // namespace Botan - -#endif diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index a69030f0d01..ab599b8d41e 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace Botan { @@ -248,18 +247,10 @@ PK_Signer::PK_Signer(const Private_Key& key, std::string_view provider) : PK_Signer(key, rng, - parse_legacy_sig_options(key, padding) - .with_der_encoded_signature(format == Signature_Format::DerSequence) - .with_provider(provider)) {} - -PK_Signer::PK_Signer(const Private_Key& key, RandomNumberGenerator& rng, const PK_Signature_Options& options) { - if(options.using_context() && !key.supports_context_data()) { - throw Invalid_Argument(fmt("Key type {} does not support context information", key.algo_name())); - } - if(options.using_der_encoded_signature() && key.message_parts() == 1) { - throw Invalid_Argument(fmt("Key type {} does not support DER encoded signatures", key.algo_name())); - } + PK_Signature_Options(key.algo_name(), padding, provider) + .with_der_encoded_signature(format == Signature_Format::DerSequence)) {} +PK_Signer::PK_Signer(const Private_Key& key, RandomNumberGenerator& rng, PK_Signature_Options& options) { m_op = key._create_signature_op(rng, options); if(!m_op) { throw Invalid_Argument(fmt("Key type {} does not support signature generation", key.algo_name())); @@ -341,11 +332,10 @@ PK_Verifier::PK_Verifier(const Public_Key& pub_key, Signature_Format format, std::string_view provider) : PK_Verifier(pub_key, - parse_legacy_sig_options(pub_key, padding) - .with_der_encoded_signature(format == Signature_Format::DerSequence) - .with_provider(provider)) {} + PK_Signature_Options(pub_key.algo_name(), padding, provider) + .with_der_encoded_signature(format == Signature_Format::DerSequence)) {} -PK_Verifier::PK_Verifier(const Public_Key& key, const PK_Signature_Options& options) { +PK_Verifier::PK_Verifier(const Public_Key& key, PK_Signature_Options& options) { m_op = key._create_verification_op(options); if(!m_op) { throw Invalid_Argument(fmt("Key type {} does not support signature verification", key.algo_name())); diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index 14193bcb76b..74f89447fa9 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -165,9 +165,20 @@ class BOTAN_PUBLIC_API(2, 0) PK_Signer final { * Note that most common algorithms (eg RSA or ECDSA) require an options * parameter to specify at least which hash function to use. */ - PK_Signer(const Private_Key& key, - RandomNumberGenerator& rng, - const PK_Signature_Options& options = PK_Signature_Options()); + PK_Signer(const Private_Key& key, RandomNumberGenerator& rng, PK_Signature_Options& options); + + /** + * Construct a PK signer + * + * @param key the key to use to generate signatures + * @param rng the random generator to use + * @param options controls the behavior of the signature generation, eg which hash function to use + * + * Note that most common algorithms (eg RSA or ECDSA) require an options + * parameter to specify at least which hash function to use. + */ + PK_Signer(const Private_Key& key, RandomNumberGenerator& rng, PK_Signature_Options&& options = {}) : + PK_Signer(key, rng, options) {} /** * Construct a PK Signer. @@ -291,7 +302,14 @@ class BOTAN_PUBLIC_API(2, 0) PK_Verifier final { * @param pub_key the public key to verify against * @param options relating to the signature */ - PK_Verifier(const Public_Key& pub_key, const PK_Signature_Options& options = PK_Signature_Options()); + PK_Verifier(const Public_Key& pub_key, PK_Signature_Options& options); + + /** + * Construct a PK Verifier. + * @param pub_key the public key to verify against + * @param options relating to the signature + */ + PK_Verifier(const Public_Key& pub_key, PK_Signature_Options&& options = {}) : PK_Verifier(pub_key, options) {} /** * Construct a PK Verifier. diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp index 21b36738c32..ecee6328651 100644 --- a/src/lib/pubkey/rsa/rsa.cpp +++ b/src/lib/pubkey/rsa/rsa.cpp @@ -565,9 +565,7 @@ class RSA_Signature_Operation final : public PK_Ops::Signature, std::string hash_function() const override { return m_emsa->hash_function(); } - RSA_Signature_Operation(const RSA_PrivateKey& rsa, - const PK_Signature_Options& options, - RandomNumberGenerator& rng) : + RSA_Signature_Operation(const RSA_PrivateKey& rsa, PK_Signature_Options& options, RandomNumberGenerator& rng) : RSA_Private_Operation(rsa, rng) { m_emsa = EMSA::create_or_throw(options); } @@ -676,9 +674,12 @@ class RSA_Verify_Operation final : public PK_Ops::Verification, return m_emsa->verify(message_repr, msg, public_modulus_bits() - 1); } - RSA_Verify_Operation(const RSA_PublicKey& rsa, const PK_Signature_Options& options) : + RSA_Verify_Operation(const RSA_PublicKey& rsa, PK_Signature_Options& options) : RSA_Public_Operation(rsa), m_emsa(EMSA::create_or_throw(options)) {} + RSA_Verify_Operation(const RSA_PublicKey& rsa, PK_Signature_Options&& options) : + RSA_Verify_Operation(rsa, options) {} + std::string hash_function() const override { return m_emsa->hash_function(); } private: @@ -734,12 +735,9 @@ std::unique_ptr RSA_PublicKey::create_kem_encryption_op( throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr RSA_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr RSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } namespace { @@ -828,12 +826,9 @@ std::unique_ptr RSA_PrivateKey::create_kem_decryption_op } std::unique_ptr RSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options, rng); - } - - throw Provider_Not_Found(algo_name(), options.provider().value()); + PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options, rng); } } // namespace Botan diff --git a/src/lib/pubkey/rsa/rsa.h b/src/lib/pubkey/rsa/rsa.h index 1386eae7d9d..24a59f5e6cb 100644 --- a/src/lib/pubkey/rsa/rsa.h +++ b/src/lib/pubkey/rsa/rsa.h @@ -77,7 +77,7 @@ class BOTAN_PUBLIC_API(2, 0) RSA_PublicKey : public virtual Public_Key { std::unique_ptr create_kem_encryption_op(std::string_view params, std::string_view provider) const override; - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& alg_id, std::string_view provider) const override; @@ -174,7 +174,7 @@ class BOTAN_PUBLIC_API(2, 0) RSA_PrivateKey final : public Private_Key, std::string_view provider) const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: void init(BigInt&& d, BigInt&& p, BigInt&& q, BigInt&& d1, BigInt&& d2, BigInt&& c); diff --git a/src/lib/pubkey/sm2/sm2.cpp b/src/lib/pubkey/sm2/sm2.cpp index a3312ae31bf..56c21da0da7 100644 --- a/src/lib/pubkey/sm2/sm2.cpp +++ b/src/lib/pubkey/sm2/sm2.cpp @@ -102,14 +102,15 @@ const std::vector sm2_default_userid = { */ class SM2_Signature_Operation final : public PK_Ops::Signature { public: - SM2_Signature_Operation(const SM2_PrivateKey& sm2, const PK_Signature_Options& options) : + SM2_Signature_Operation(const SM2_PrivateKey& sm2, PK_Signature_Options& options) : m_group(sm2.domain()), m_x(sm2._private_key()), m_da_inv(sm2._get_da_inv()) { - if(options.hash_function_name() == "Raw") { + const auto hash = options.hash_function(); + if(hash == "Raw") { // m_hash is null, m_za is empty } else { auto context = options.context().value_or(sm2_default_userid); - m_hash = HashFunction::create_or_throw(options.hash_function_name()); + m_hash = HashFunction::create_or_throw(hash); // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA) m_za = sm2_compute_za(*m_hash, context, m_group, sm2._public_key()); m_hash->update(m_za); @@ -168,14 +169,15 @@ std::vector SM2_Signature_Operation::sign(RandomNumberGenerator& rng) { */ class SM2_Verification_Operation final : public PK_Ops::Verification { public: - SM2_Verification_Operation(const SM2_PublicKey& sm2, const PK_Signature_Options& options) : + SM2_Verification_Operation(const SM2_PublicKey& sm2, PK_Signature_Options& options) : m_group(sm2.domain()), m_gy_mul(sm2._public_key()) { - if(options.hash_function_name() == "Raw") { + const auto hash = options.hash_function(); + if(hash == "Raw") { // m_hash is null, m_za is empty } else { auto context = options.context().value_or(sm2_default_userid); - m_hash = HashFunction::create_or_throw(options.hash_function_name()); + m_hash = HashFunction::create_or_throw(hash); // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA) m_za = sm2_compute_za(*m_hash, context, m_group, sm2._public_key()); m_hash->update(m_za); @@ -245,22 +247,16 @@ std::vector sm2_compute_za(HashFunction& hash, return sm2_compute_za(hash, ident_bytes, group, apoint); } -std::unique_ptr SM2_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr SM2_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } std::unique_ptr SM2_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - - if(!options.using_provider()) { - return std::make_unique(*this, options); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + options.exclude_provider_for_algorithm(algo_name()); + return std::make_unique(*this, options); } } // namespace Botan diff --git a/src/lib/pubkey/sm2/sm2.h b/src/lib/pubkey/sm2/sm2.h index 1ee7b7e536f..e0529935a24 100644 --- a/src/lib/pubkey/sm2/sm2.h +++ b/src/lib/pubkey/sm2/sm2.h @@ -50,7 +50,7 @@ class BOTAN_PUBLIC_API(2, 2) SM2_PublicKey : public virtual EC_PublicKey { size_t message_part_size() const override { return domain().get_order_bytes(); } - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_encryption_op(RandomNumberGenerator& rng, std::string_view params, @@ -90,7 +90,7 @@ class BOTAN_PUBLIC_API(2, 2) SM2_PrivateKey final : public SM2_PublicKey, std::unique_ptr public_key() const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; std::unique_ptr create_decryption_op(RandomNumberGenerator& rng, std::string_view params, diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp index dd77ca18df6..733c2ecfef6 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -189,14 +188,10 @@ class SphincsPlus_Verification_Operation final : public PK_Ops::Verification { }; std::unique_ptr SphincsPlus_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - validate_for_hash_based_signature(options, "SPHINCS+", m_public->parameters().hash_name()); - - if(!options.using_provider()) { - return std::make_unique(m_public); - } - - throw Provider_Not_Found(algo_name(), options.provider().value()); + PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + options.validate_for_hash_based_signature_algorithm(algo_name(), m_public->parameters().hash_name()); + return std::make_unique(m_public); } std::unique_ptr SphincsPlus_PublicKey::create_x509_verification_op( @@ -284,11 +279,11 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { public: SphincsPlus_Signature_Operation(std::shared_ptr private_key, std::shared_ptr public_key, - bool randomized) : + bool deterministic) : m_private(std::move(private_key)), m_public(std::move(public_key)), m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())), - m_randomized(randomized) {} + m_deterministic(deterministic) {} void update(std::span msg) override { m_msg_buffer.insert(m_msg_buffer.end(), msg.begin(), msg.end()); @@ -302,7 +297,7 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { // Compute and append the digest randomization value (R of spec). SphincsOptionalRandomness opt_rand(m_public->seed()); - if(m_randomized) { + if(!m_deterministic) { opt_rand = rng.random_vec(p.n()); } auto msg_random_s = sphincs_sig.next(p.n()); @@ -350,20 +345,16 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { std::shared_ptr m_public; std::unique_ptr m_hashes; std::vector m_msg_buffer; - bool m_randomized; + bool m_deterministic; }; -std::unique_ptr SphincsPlus_PrivateKey::_create_signature_op( - RandomNumberGenerator& rng, const PK_Signature_Options& options) const { +std::unique_ptr SphincsPlus_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - - validate_for_hash_based_signature(options, "SPHINCS+", m_public->parameters().hash_name()); - - const bool randomized = !options.using_deterministic_signature(); - if(!options.using_provider()) { - return std::make_unique(m_private, m_public, randomized); - } - throw Provider_Not_Found(algo_name(), options.provider().value()); + options.exclude_provider_for_algorithm(algo_name()); + options.validate_for_hash_based_signature_algorithm(algo_name(), m_public->parameters().hash_name()); + return std::make_unique( + m_private, m_public, options.using_deterministic_signature()); } } // namespace Botan diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h index a743f5e2ce1..9c536ac647e 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h @@ -51,7 +51,7 @@ class BOTAN_PUBLIC_API(3, 1) SphincsPlus_PublicKey : public virtual Public_Key { std::unique_ptr generate_another(RandomNumberGenerator& rng) const final; - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -83,7 +83,7 @@ class BOTAN_PUBLIC_API(3, 1) SphincsPlus_PrivateKey final : public virtual Sphin std::unique_ptr public_key() const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; private: std::shared_ptr m_private; diff --git a/src/lib/pubkey/xmss/xmss.h b/src/lib/pubkey/xmss/xmss.h index fdaf7102af1..7444756e7da 100644 --- a/src/lib/pubkey/xmss/xmss.h +++ b/src/lib/pubkey/xmss/xmss.h @@ -104,7 +104,7 @@ class BOTAN_PUBLIC_API(2, 0) XMSS_PublicKey : public virtual Public_Key { bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::Signature); } - std::unique_ptr _create_verification_op(const PK_Signature_Options& options) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& alg_id, std::string_view provider) const override; @@ -232,7 +232,7 @@ class BOTAN_PUBLIC_API(2, 0) XMSS_PrivateKey final : public virtual XMSS_PublicK std::optional remaining_operations() const override; std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const override; + PK_Signature_Options& options) const override; secure_vector private_key_bits() const override; diff --git a/src/lib/pubkey/xmss/xmss_privatekey.cpp b/src/lib/pubkey/xmss/xmss_privatekey.cpp index 43bacc5945a..33d97f38ea8 100644 --- a/src/lib/pubkey/xmss/xmss_privatekey.cpp +++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -432,16 +431,11 @@ std::unique_ptr XMSS_PrivateKey::public_key() const { } std::unique_ptr XMSS_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, - const PK_Signature_Options& options) const { + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - - validate_for_hash_based_signature(options, "XMSS", this->m_private->hash().hash_function()); - - if(!options.using_provider()) { - return std::make_unique(*this); - } - - throw Provider_Not_Found(algo_name(), options.provider().value()); + options.exclude_provider_for_algorithm(algo_name()); + options.validate_for_hash_based_signature_algorithm(algo_name(), m_private->hash().hash_function()); + return std::make_unique(*this); } } // namespace Botan diff --git a/src/lib/pubkey/xmss/xmss_publickey.cpp b/src/lib/pubkey/xmss/xmss_publickey.cpp index 93a248ae0b7..5ed06fc182b 100644 --- a/src/lib/pubkey/xmss/xmss_publickey.cpp +++ b/src/lib/pubkey/xmss/xmss_publickey.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -102,15 +101,10 @@ XMSS_PublicKey::XMSS_PublicKey(XMSS_Parameters::xmss_algorithm_t xmss_oid, BOTAN_ARG_CHECK(m_public_seed.size() == m_xmss_params.element_size(), "XMSS: unexpected byte length of public seed"); } -std::unique_ptr XMSS_PublicKey::_create_verification_op( - const PK_Signature_Options& options) const { - validate_for_hash_based_signature(options, "XMSS", this->m_xmss_params.hash_function_name()); - - if(!options.using_provider()) { - return std::make_unique(*this); - } - - throw Provider_Not_Found(algo_name(), options.provider().value()); +std::unique_ptr XMSS_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider_for_algorithm(algo_name()); + options.validate_for_hash_based_signature_algorithm(algo_name(), m_xmss_params.hash_function_name()); + return std::make_unique(*this); } std::unique_ptr XMSS_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, diff --git a/src/lib/utils/base_builder.cpp b/src/lib/utils/base_builder.cpp new file mode 100644 index 00000000000..3baf5ce28ea --- /dev/null +++ b/src/lib/utils/base_builder.cpp @@ -0,0 +1,46 @@ +/* +* (C) 2024 Jack Lloyd +* (C) 2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#if defined(BOTAN_HAS_HASH) + #include +#endif + +#if defined(BOTAN_HAS_MAC) + #include +#endif + +namespace Botan::detail::BuilderOptionHelper { + +std::string to_string(const std::unique_ptr& value) { +#if defined(BOTAN_HAS_HASH) + if(value) { + return value->name(); + } else { + return "nullptr"; + } +#else + BOTAN_UNUSED(value); + return "hash function not available"; +#endif +} + +std::string to_string(const std::unique_ptr& value) { +#if defined(BOTAN_HAS_MAC) + if(value) { + return value->name(); + } else { + return "nullptr"; + } +#else + BOTAN_UNUSED(value); + return "MAC not available"; +#endif +} + +} // namespace Botan::detail::BuilderOptionHelper diff --git a/src/lib/utils/base_builder.h b/src/lib/utils/base_builder.h new file mode 100644 index 00000000000..d035dba47b5 --- /dev/null +++ b/src/lib/utils/base_builder.h @@ -0,0 +1,252 @@ +/* +* (C) 2024 Jack Lloyd +* (C) 2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_BASE_BUILDER_H_ +#define BOTAN_BASE_BUILDER_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +class HashFunction; +class MessageAuthenticationCode; + +template +class Builder; + +namespace detail { + +namespace BuilderOptionHelper { + +template +std::string to_string(const U&) { + return std::string("'; +} + +template U> +std::string to_string(const U& value) { + return std::string(value); +} + +template +std::string to_string(const std::optional& value) { + if(!value.has_value()) { + return "explicitly not set"; + } + return to_string(value.value()); +} + +inline std::string to_string(bool value) { + return value ? "true" : "false"; +} + +std::string to_string(const std::unique_ptr& value); +std::string to_string(const std::unique_ptr& value); + +} // namespace BuilderOptionHelper + +/** + * Wraps a builder option value and provides a way to convert it to a string + * for debugging and error messages. + */ +template +class Option { + public: + constexpr static std::string_view name = option_name.value; + using value_type = T; + + std::string to_string() const { + if(!value.has_value()) { + return ""; + } else { + return BuilderOptionHelper::to_string(*value); + } + } + + private: + // Only the abstract builder may access the wrapped value + template + friend class Botan::Builder; + + std::optional value; +}; + +/// Concept to check whether T is a BuilderOption +template +struct is_builder_option : std::false_type {}; + +template +struct is_builder_option> : std::true_type {}; + +template +concept BuilderOption = is_builder_option::value; + +template +consteval bool all_builder_options(std::tuple) { + return (BuilderOption && ... && true); +} + +} // namespace detail + +/** + * Base class for all builder helper classes + * + * Concrete implementations of builders should derive from this class, wrap all + * its options in `Option` instances and implement the `all_options` method. + * + * Below is an example that sets up a hypothetical key derivation function. + * Note that the `with_*` methods are overloaded for lvalue and rvalue refs, to + * allow for properly chaining the calls. + * + * TODO: C++23: Using "deducing-this" we will be able to remove the CRTP and + * remove the overloads for lvalue and rvalue refs. + * + * class KDF_Builder : public Builder { + * private: + * detail::Option<"context", std::string> m_context; + * detail::Option<"label", std::string> m_label; + * detail::Option<"hash", std::unique_ptr> m_hash; + * + * friend class Builder; + * + * /// Returns a tuple of all options (needed for the base implementation) + * auto all_options() const { return std::tie(m_context, m_hash); } + * + * public: + * KDF_Builder& with_context(std::string_view ctx) & { + * set_or_throw(m_context, std::string(ctx)); + * return *this; + * } + * + * KDF_Builder with_context(std::string_view ctx) && { + * return std::move(with_context(ctx)); + * } + * + * KDF_Builder& with_label(std::string_view label) & { + * set_or_throw(m_context, std::string(label)); + * return *this; + * } + * + * KDF_Builder with_label(std::string_view label) && { + * return std::move(with_label(label)); + * } + * + * KDF_Builder& with_hash(std::string_view hash) & { + * set_or_throw(m_hash, Botan::HashFunction::create_or_throw(hash)); + * return *this; + * } + * + * KDF_Builder with_hash(std::string_view hash) && { + * return std::move(with_hash(hash)); + * } + * + * KDF_Builder& with_hash(std::unique_ptr hash) & { + * set_or_throw(m_hash, std::move(hash)); + * return *this; + * } + * + * KDF_Builder with_hash(std::unique_ptr hash) && { + * return std::move(with_hash(std::move(hash))); + * } + * + * /// Creates a new KDF instance with the current options and validates + * /// that all options have been consumed by the new KDF instance. + * KDF create() { + * KDF kdf(*this); + * validate_option_consumption(); + * return kdf; + * } + * + * /// Gets the context value or std::nullopt if it wasn't set + * std::optional context() { return take(context); } + * + * /// Gets the label value or a default if it wasn't set + * std::string label() { return take(label).value_or("default"); } + * + * /// Gets the hash function or throws if it wasn't set + * std::unique_ptr hash() { return require(hash); } + * }; + */ +template +class Builder { + public: + [[nodiscard]] std::string to_string() const { + std::ostringstream oss; + foreach_option([&](const OptionT& option) { + oss << OptionT::name << ": " << option.to_string() << '\n'; + }); + return oss.str(); + } + + protected: + void set_product_name(std::string_view name) { m_product_name = std::string(name); } + + [[nodiscard]] static auto take(detail::BuilderOption auto& o) noexcept { + return std::exchange(o.value, std::nullopt); + } + + template + [[nodiscard]] auto require(OptionT& o) { + if(!o.value.has_value()) { + throw Invalid_Argument("'" + m_product_name + "' requires the '" + std::string(OptionT::name) + "' option"); + } + return take(o).value(); + } + + template ValueT> + void set_or_throw(OptionT& option, ValueT&& value) { + if(option.value.has_value()) { + throw Invalid_State("'" + m_product_name + "' already set the '" + std::string(OptionT::name) + "' option"); + } + option.value.emplace(std::forward(value)); + } + + template + void foreach_option(FnT&& fn) const { + // TODO: C++23: using deducing-this we can remove the CRTP and simply + // deduce the DerivedT from the explicit object parameter. + std::apply([&](const OptionTs&... options) { (fn(options), ...); }, + static_cast(*this).all_options()); + } + + void validate_option_consumption() { + std::vector disdained_options; + foreach_option([&](const OptionT& option) { + if(option.value.has_value()) { + disdained_options.push_back(OptionT::name); + } + }); + + auto join = [](const std::vector& v) { + // C++23: std::format can print ranges out-of-the-box + return std::accumulate( + v.begin(), v.end(), std::string{}, [](const std::string& a, std::string_view b) -> std::string { + return a.empty() ? std::string(b) : a + ", " + std::string(b); + }); + }; + + if(!disdained_options.empty()) { + throw Invalid_Argument("'" + m_product_name + "' failed to use some options: " + join(disdained_options)); + } + } + + private: + std::string m_product_name = "Unknown"; +}; + +} // namespace Botan + +#endif diff --git a/src/lib/utils/info.txt b/src/lib/utils/info.txt index 4c4fcc733f5..38a1cb74ad3 100644 --- a/src/lib/utils/info.txt +++ b/src/lib/utils/info.txt @@ -12,6 +12,7 @@ load_on always assert.h allocator.h +base_builder.h compiler.h concepts.h data_src.h @@ -19,6 +20,7 @@ database.h exceptn.h mem_ops.h mutex.h +template_utils.h types.h strong_type.h version.h diff --git a/src/lib/utils/stl_util.h b/src/lib/utils/stl_util.h index 9356b2db5a3..cef382f6b86 100644 --- a/src/lib/utils/stl_util.h +++ b/src/lib/utils/stl_util.h @@ -397,17 +397,6 @@ T assert_is_some(std::optional v, const char* expr, const char* func, const c #define BOTAN_ASSERT_IS_SOME(v) assert_is_some(v, #v, __func__, __FILE__, __LINE__) -/* - * @brief Helper class to pass literal strings to C++ templates - */ -template -class StringLiteral { - public: - constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } - - char value[N]; -}; - // TODO: C++23: replace with std::to_underlying template requires std::is_enum_v diff --git a/src/lib/utils/template_utils.h b/src/lib/utils/template_utils.h new file mode 100644 index 00000000000..bb021559fc6 --- /dev/null +++ b/src/lib/utils/template_utils.h @@ -0,0 +1,28 @@ +/* +* STL Utility Functions +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_TEMPLATE_UTILS_H_ +#define BOTAN_TEMPLATE_UTILS_H_ + +#include + +namespace Botan { + +/* + * @brief Helper class to pass literal strings to C++ templates + */ +template +class StringLiteral { + public: + constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); } + + char value[N]; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +} // namespace Botan + +#endif