diff --git a/src/cli/pubkey.cpp b/src/cli/pubkey.cpp index a088753006a..fd2dec727d7 100644 --- a/src/cli/pubkey.cpp +++ b/src/cli/pubkey.cpp @@ -97,25 +97,11 @@ BOTAN_REGISTER_COMMAND("keygen", PK_Keygen); namespace { -std::string choose_sig_padding(const std::string& key, const std::string& padding, const std::string& hash) { - if(key == "RSA") { - std::ostringstream oss; - if(padding.empty()) { - oss << "PSS"; - } else { - oss << padding; - } - - oss << "(" << hash << ")"; - return oss.str(); - } else if(padding.empty()) { - return hash; - } else if(hash.empty()) { - return padding; +std::string_view normalize_padding(const std::string& algo, const std::string& requested_padding) { + if(algo == "RSA" && requested_padding.empty()) { + return "PSS"; } else { - std::ostringstream oss; - oss << padding << "(" << hash << ")"; - return oss.str(); + return requested_padding; } } @@ -196,21 +182,17 @@ class PK_Sign final : public Command { throw CLI_Error_Unsupported("hashing", hash_fn); } - const std::string sig_padding = choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn); + auto signer_builder = key->signer() + .with_rng(rng()) + .with_hash(hash_fn) + .with_der_encoded_signature(flag_set("der-format")) + .with_provider(get_arg("provider")); - auto format = Botan::Signature_Format::Standard; - - if(flag_set("der-format")) { - if(key->message_parts() == 1) { - throw CLI_Usage_Error("Key type " + key->algo_name() + - " does not support DER formatting for signatures"); - } - format = Botan::Signature_Format::DerSequence; + if(const auto padding = normalize_padding(key->algo_name(), get_arg("padding")); !padding.empty()) { + signer_builder.with_padding(padding); } - const std::string provider = get_arg("provider"); - - Botan::PK_Signer signer(*key, rng(), sig_padding, format, provider); + auto signer = signer_builder.create(); auto onData = [&signer](const uint8_t b[], size_t l) { signer.update(b, l); }; Command::read_file(get_arg("file"), onData); @@ -254,18 +236,15 @@ class PK_Verify final : public Command { throw CLI_Error_Unsupported("hashing", hash_fn); } - const std::string sig_padding = choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn); + auto verifier_builder = + key->signature_verifier().with_hash(hash_fn).with_der_encoded_signature(flag_set("der-format")); - auto format = Botan::Signature_Format::Standard; - if(flag_set("der-format")) { - if(key->message_parts() == 1) { - throw CLI_Usage_Error("Key type " + key->algo_name() + - " does not support DER formatting for signatures"); - } - format = Botan::Signature_Format::DerSequence; + if(const auto padding = normalize_padding(key->algo_name(), get_arg("padding")); !padding.empty()) { + verifier_builder.with_padding(padding); } - Botan::PK_Verifier verifier(*key, sig_padding, format); + auto verifier = verifier_builder.create(); + 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/examples/ecdsa.cpp b/src/examples/ecdsa.cpp index 2dbdd3584a6..b1e0e260f30 100644 --- a/src/examples/ecdsa.cpp +++ b/src/examples/ecdsa.cpp @@ -15,13 +15,13 @@ int main() { const std::string message("This is a tasty burger!"); // sign data - Botan::PK_Signer signer(key, rng, "SHA-256"); + auto signer = key.signer().with_hash("SHA-256").with_rng(rng).create(); signer.update(message); std::vector signature = signer.signature(rng); std::cout << "Signature:\n" << Botan::hex_encode(signature); // now verify the signature - Botan::PK_Verifier verifier(key, "SHA-256"); + auto verifier = key.signature_verifier().with_hash("SHA-256").create(); verifier.update(message); std::cout << "\nis " << (verifier.check_signature(signature) ? "valid" : "invalid"); return 0; diff --git a/src/examples/pkcs11_ecdsa.cpp b/src/examples/pkcs11_ecdsa.cpp index 0c2add681e5..df23102fc20 100644 --- a/src/examples/pkcs11_ecdsa.cpp +++ b/src/examples/pkcs11_ecdsa.cpp @@ -95,10 +95,10 @@ int main() { std::vector plaintext(20, 0x01); - Botan::PK_Signer signer(key_pair.second, rng, "Raw", Botan::Signature_Format::Standard, "pkcs11"); + auto signer = key_pair.second.signer().with_rng(rng).with_hash("Raw").create(); auto signature = signer.sign_message(plaintext, rng); - Botan::PK_Verifier token_verifier(key_pair.first, "Raw", Botan::Signature_Format::Standard, "pkcs11"); + auto token_verifier = key_pair.first.signature_verifier().with_hash("Raw").create(); bool ecdsa_ok = token_verifier.verify_message(plaintext, signature); return ecdsa_ok ? 0 : 1; diff --git a/src/examples/pkcs11_rsa.cpp b/src/examples/pkcs11_rsa.cpp index 766c1eb8633..ffbbf3ca249 100644 --- a/src/examples/pkcs11_rsa.cpp +++ b/src/examples/pkcs11_rsa.cpp @@ -92,12 +92,12 @@ int main() { /************ RSA sign *************/ - Botan::PK_Signer signer(rsa_keypair.second, rng, "EMSA4(SHA-256)", Botan::Signature_Format::Standard); + auto signer = rsa_keypair.second.signer().with_rng(rng).with_hash("SHA-256").with_padding("PSS").create(); auto signature = signer.sign_message(plaintext, rng); /************ RSA verify *************/ - Botan::PK_Verifier verifier(rsa_keypair.first, "EMSA4(SHA-256)", Botan::Signature_Format::Standard); + auto verifier = rsa_keypair.first.signature_verifier().with_hash("SHA-256").with_padding("PSS").create(); auto ok = verifier.verify_message(plaintext, signature); return ok ? 0 : 1; diff --git a/src/examples/xmss.cpp b/src/examples/xmss.cpp index e3245f40cc4..c805d17e82f 100644 --- a/src/examples/xmss.cpp +++ b/src/examples/xmss.cpp @@ -4,7 +4,6 @@ #include #include -#include int main() { // Create a random number generator used for key generation. @@ -16,14 +15,14 @@ int main() { const Botan::XMSS_PublicKey& public_key(private_key); // create Public Key Signer using the private key. - Botan::PK_Signer signer(private_key, rng, ""); + auto signer = private_key.signer().with_rng(rng).create(); // create and sign a message using the Public Key Signer. Botan::secure_vector msg{0x01, 0x02, 0x03, 0x04}; auto sig = signer.sign_message(msg, rng); // create Public Key Verifier using the public key - Botan::PK_Verifier verifier(public_key, ""); + auto verifier = public_key.signature_verifier().create(); // verify the signature for the previously generated message. if(verifier.verify_message(msg, sig)) { diff --git a/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h b/src/lib/math/pcurves/pcurves_impl/pcurves_impl.h index 67d0556b351..df790eadb3d 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 d3d4146fee7..dacfb9bd9fd 100644 --- a/src/lib/pk_pad/emsa.cpp +++ b/src/lib/pk_pad/emsa.cpp @@ -1,5 +1,5 @@ /* -* (C) 2015 Jack Lloyd +* (C) 2015,2024 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,7 +8,7 @@ #include #include -#include +#include #if defined(BOTAN_HAS_EMSA_X931) #include @@ -32,108 +32,76 @@ namespace Botan { -std::unique_ptr EMSA::create(std::string_view algo_spec) { - SCAN_Name req(algo_spec); +std::unique_ptr EMSA::create_or_throw(PK_Signature_Options& options) { + const auto hash = options.hash_function().optional(); + const auto padding = options.padding().optional(); + const bool is_raw_hash = !hash.has_value() || hash.value() == "Raw"; + const bool is_raw_padding = !padding.has_value() || padding.value() == "Raw"; -#if defined(BOTAN_HAS_EMSA_PKCS1) - if(req.algo_name() == "EMSA_PKCS1" || req.algo_name() == "PKCS1v15" || req.algo_name() == "EMSA-PKCS1-v1_5" || - req.algo_name() == "EMSA3") { - if(req.arg_count() == 2 && req.arg(0) == "Raw") { - return std::make_unique(req.arg(1)); - } else if(req.arg_count() == 1) { - if(req.arg(0) == "Raw") { - return std::make_unique(); - } else { - if(auto hash = HashFunction::create(req.arg(0))) { - return std::make_unique(std::move(hash)); + 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(auto prehash = options.prehash().optional(); prehash.has_value() && prehash->has_value()) { + if(auto prehash_fn = HashFunction::create(prehash->value())) { + return std::make_unique(prehash_fn->output_length()); } + } else { + return std::make_unique(); } } - } #endif + } else { + // null if raw_hash + auto hash_fn = [&]() -> std::unique_ptr { + if(is_raw_hash) { + return nullptr; + } else { + return HashFunction::create(hash.value()); + } + }(); -#if defined(BOTAN_HAS_EMSA_PSSR) - if(req.algo_name() == "PSS_Raw" || req.algo_name() == "PSSR_Raw") { - if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { - if(auto hash = HashFunction::create(req.arg(0))) { - if(req.arg_count() == 3) { - const size_t salt_size = req.arg_as_integer(2, 0); - return std::make_unique(std::move(hash), salt_size); - } else { - return std::make_unique(std::move(hash)); - } +#if defined(BOTAN_HAS_EMSA_PKCS1) + if(padding == "PKCS1v15") { + if(is_raw_hash) { + return std::make_unique(options.prehash().or_default(std::nullopt)); + } else if(hash_fn) { + return std::make_unique(std::move(hash_fn)); } } - } +#endif - if(req.algo_name() == "PSS" || req.algo_name() == "PSSR" || req.algo_name() == "EMSA-PSS" || - req.algo_name() == "PSS-MGF1" || req.algo_name() == "EMSA4") { - if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { - if(auto hash = HashFunction::create(req.arg(0))) { - if(req.arg_count() == 3) { - const size_t salt_size = req.arg_as_integer(2, 0); - return std::make_unique(std::move(hash), salt_size); - } else { - return std::make_unique(std::move(hash)); - } - } +#if defined(BOTAN_HAS_EMSA_PSSR) + if(padding == "PSS_Raw" && hash_fn) { + return std::make_unique(std::move(hash_fn), options.salt_size().optional()); + } + + if(padding == "PSS" && hash_fn) { + return std::make_unique(std::move(hash_fn), options.salt_size().optional()); } - } #endif #if defined(BOTAN_HAS_ISO_9796) - if(req.algo_name() == "ISO_9796_DS2") { - if(req.arg_count_between(1, 3)) { - if(auto hash = HashFunction::create(req.arg(0))) { - const size_t salt_size = req.arg_as_integer(2, hash->output_length()); - const bool implicit = req.arg(1, "exp") == "imp"; - return std::make_unique(std::move(hash), implicit, salt_size); - } + if(padding == "ISO_9796_DS2" && hash_fn) { + return std::make_unique( + std::move(hash_fn), !options.using_explicit_trailer_field(), options.salt_size().optional()); } - } - //ISO-9796-2 DS 3 is deterministic and DS2 without a salt - if(req.algo_name() == "ISO_9796_DS3") { - if(req.arg_count_between(1, 2)) { - if(auto hash = HashFunction::create(req.arg(0))) { - const bool implicit = req.arg(1, "exp") == "imp"; - return std::make_unique(std::move(hash), implicit); - } + + //ISO-9796-2 DS 3 is deterministic and DS2 without a salt + 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(req.algo_name() == "EMSA_X931" || req.algo_name() == "EMSA2" || req.algo_name() == "X9.31") { - if(req.arg_count() == 1) { - if(auto hash = HashFunction::create(req.arg(0))) { - return std::make_unique(std::move(hash)); - } + if(padding == "X9.31" && hash_fn) { + return std::make_unique(std::move(hash_fn)); } - } #endif - -#if defined(BOTAN_HAS_EMSA_RAW) - if(req.algo_name() == "Raw") { - if(req.arg_count() == 0) { - return std::make_unique(); - } else { - auto hash = HashFunction::create(req.arg(0)); - if(hash) { - return std::make_unique(hash->output_length()); - } - } } -#endif - - return nullptr; -} -std::unique_ptr EMSA::create_or_throw(std::string_view algo_spec) { - auto emsa = EMSA::create(algo_spec); - if(emsa) { - return emsa; - } - throw Algorithm_Not_Found(algo_spec); + 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 dd253042b02..b8c5e5228e5 100644 --- a/src/lib/pk_pad/emsa.h +++ b/src/lib/pk_pad/emsa.h @@ -14,6 +14,7 @@ namespace Botan { class RandomNumberGenerator; +class PK_Signature_Options; /** * EMSA, from IEEE 1363s Encoding Method for Signatures, Appendix @@ -27,18 +28,10 @@ class BOTAN_TEST_API EMSA { /** * Factory method for EMSA (message-encoding methods for signatures * with appendix) objects - * @param algo_spec the name of the EMSA to create - * @return pointer to newly allocated object of that type, or nullptr - */ - static std::unique_ptr create(std::string_view algo_spec); - - /** - * Factory method for EMSA (message-encoding methods for signatures - * with appendix) objects - * @param algo_spec the name of the EMSA to create + * @param options the algorithm parameters * @return pointer to newly allocated object of that type, or throws */ - static std::unique_ptr create_or_throw(std::string_view algo_spec); + static std::unique_ptr create_or_throw(PK_Signature_Options& options); /** * Add more data to the signature computation diff --git a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp index cfc45ca365a..7d75149c4a3 100644 --- a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp +++ b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp @@ -73,16 +73,16 @@ EMSA_PKCS1v15::EMSA_PKCS1v15(std::unique_ptr hash) : m_hash(std::m m_hash_id = pkcs_hash_id(m_hash->name()); } -EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw() { - m_hash_output_len = 0; - // m_hash_id, m_hash_name left empty -} - -EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(std::string_view hash_algo) { - std::unique_ptr hash(HashFunction::create_or_throw(hash_algo)); - m_hash_id = pkcs_hash_id(hash_algo); - m_hash_name = hash->name(); - m_hash_output_len = hash->output_length(); +EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::optional& hash_algo) { + if(hash_algo) { + std::unique_ptr hash(HashFunction::create_or_throw(hash_algo.value())); + m_hash_id = pkcs_hash_id(hash->name()); + m_hash_name = hash->name(); + m_hash_output_len = hash->output_length(); + } else { + m_hash_output_len = 0; + // m_hash_id, m_hash_name left empty + } } void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length) { diff --git a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h index 80aa66ac666..51111319bc5 100644 --- a/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h +++ b/src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.h @@ -10,6 +10,7 @@ #include #include +#include namespace Botan { @@ -57,13 +58,11 @@ class EMSA_PKCS1v15_Raw final : public EMSA { bool verify(const std::vector&, const std::vector&, size_t) override; - EMSA_PKCS1v15_Raw(); - /** * @param hash_algo the digest id for that hash is included in * the signature. */ - EMSA_PKCS1v15_Raw(std::string_view hash_algo); + EMSA_PKCS1v15_Raw(const std::optional& hash_algo = std::nullopt); std::string hash_function() const override { return m_hash_name; } diff --git a/src/lib/pk_pad/emsa_pssr/pssr.cpp b/src/lib/pk_pad/emsa_pssr/pssr.cpp index a154182d2f8..2539ddf2a4f 100644 --- a/src/lib/pk_pad/emsa_pssr/pssr.cpp +++ b/src/lib/pk_pad/emsa_pssr/pssr.cpp @@ -144,11 +144,10 @@ bool pss_verify(HashFunction& hash, } // namespace -PSSR::PSSR(std::unique_ptr hash) : - m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {} - -PSSR::PSSR(std::unique_ptr hash, size_t salt_size) : - m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {} +PSSR::PSSR(std::unique_ptr hash, std::optional salt_size) : + m_hash(std::move(hash)), + m_salt_size(salt_size.value_or(m_hash->output_length())), + m_required_salt_len(salt_size.has_value()) {} /* * PSSR Update Operation @@ -189,11 +188,10 @@ std::string PSSR::name() const { return "EMSA4(" + m_hash->name() + ",MGF1," + std::to_string(m_salt_size) + ")"; } -PSSR_Raw::PSSR_Raw(std::unique_ptr hash) : - m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {} - -PSSR_Raw::PSSR_Raw(std::unique_ptr hash, size_t salt_size) : - m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {} +PSSR_Raw::PSSR_Raw(std::unique_ptr hash, std::optional salt_size) : + m_hash(std::move(hash)), + m_salt_size(salt_size.value_or(m_hash->output_length())), + m_required_salt_len(salt_size.has_value()) {} /* * PSSR_Raw Update Operation diff --git a/src/lib/pk_pad/emsa_pssr/pssr.h b/src/lib/pk_pad/emsa_pssr/pssr.h index a9980fda884..43060478184 100644 --- a/src/lib/pk_pad/emsa_pssr/pssr.h +++ b/src/lib/pk_pad/emsa_pssr/pssr.h @@ -10,6 +10,7 @@ #include #include +#include namespace Botan { @@ -18,16 +19,11 @@ namespace Botan { */ class PSSR final : public EMSA { public: - /** - * @param hash the hash function to use - */ - explicit PSSR(std::unique_ptr hash); - /** * @param hash the hash function to use * @param salt_size the size of the salt to use in bytes */ - PSSR(std::unique_ptr hash, size_t salt_size); + PSSR(std::unique_ptr hash, std::optional salt_size); std::string name() const override; @@ -55,16 +51,11 @@ class PSSR final : public EMSA { */ class PSSR_Raw final : public EMSA { public: - /** - * @param hash the hash function to use - */ - explicit PSSR_Raw(std::unique_ptr hash); - /** * @param hash the hash function to use * @param salt_size the size of the salt to use in bytes */ - PSSR_Raw(std::unique_ptr hash, size_t salt_size); + PSSR_Raw(std::unique_ptr hash, std::optional salt_size = std::nullopt); std::string hash_function() const override { return m_hash->name(); } diff --git a/src/lib/pk_pad/iso9796/iso9796.cpp b/src/lib/pk_pad/iso9796/iso9796.cpp index 737cef06f18..78bcec9f8b8 100644 --- a/src/lib/pk_pad/iso9796/iso9796.cpp +++ b/src/lib/pk_pad/iso9796/iso9796.cpp @@ -219,21 +219,21 @@ std::vector ISO_9796_DS2::raw_data() { std::vector ISO_9796_DS2::encoding_of(const std::vector& msg, size_t output_bits, RandomNumberGenerator& rng) { - return iso9796_encoding(msg, output_bits, m_hash, m_SALT_SIZE, m_implicit, rng); + return iso9796_encoding(msg, output_bits, m_hash, m_salt_size, m_implicit, rng); } /* * ISO-9796-2 scheme 2 verify operation */ bool ISO_9796_DS2::verify(const std::vector& const_coded, const std::vector& raw, size_t key_bits) { - return iso9796_verification(const_coded, raw, key_bits, m_hash, m_SALT_SIZE); + return iso9796_verification(const_coded, raw, key_bits, m_hash, m_salt_size); } /* * Return the SCAN name */ std::string ISO_9796_DS2::name() const { - return "ISO_9796_DS2(" + m_hash->name() + "," + (m_implicit ? "imp" : "exp") + "," + std::to_string(m_SALT_SIZE) + + return "ISO_9796_DS2(" + m_hash->name() + "," + (m_implicit ? "imp" : "exp") + "," + std::to_string(m_salt_size) + ")"; } diff --git a/src/lib/pk_pad/iso9796/iso9796.h b/src/lib/pk_pad/iso9796/iso9796.h index 852aeaab94b..d91e2c669ab 100644 --- a/src/lib/pk_pad/iso9796/iso9796.h +++ b/src/lib/pk_pad/iso9796/iso9796.h @@ -10,6 +10,7 @@ #include #include +#include namespace Botan { @@ -18,20 +19,13 @@ namespace Botan { */ class ISO_9796_DS2 final : public EMSA { public: - /** - * @param hash function to use - * @param implicit whether or not the trailer is implicit - */ - explicit ISO_9796_DS2(std::unique_ptr hash, bool implicit = false) : - m_hash(std::move(hash)), m_implicit(implicit), m_SALT_SIZE(hash->output_length()) {} - /** * @param hash function to use * @param implicit whether or not the trailer is implicit * @param salt_size size of the salt to use in bytes */ - ISO_9796_DS2(std::unique_ptr hash, bool implicit, size_t salt_size) : - m_hash(std::move(hash)), m_implicit(implicit), m_SALT_SIZE(salt_size) {} + ISO_9796_DS2(std::unique_ptr hash, bool implicit, std::optional salt_size) : + m_hash(std::move(hash)), m_implicit(implicit), m_salt_size(salt_size.value_or(m_hash->output_length())) {} std::string hash_function() const override { return m_hash->name(); } @@ -50,7 +44,7 @@ class ISO_9796_DS2 final : public EMSA { std::unique_ptr m_hash; bool m_implicit; - size_t m_SALT_SIZE; + size_t m_salt_size; std::vector m_msg_buffer; }; diff --git a/src/lib/prov/pkcs11/p11_ecdsa.cpp b/src/lib/prov/pkcs11/p11_ecdsa.cpp index 13e0559caee..528f786a64b 100644 --- a/src/lib/prov/pkcs11/p11_ecdsa.cpp +++ b/src/lib/prov/pkcs11/p11_ecdsa.cpp @@ -12,6 +12,7 @@ #include #include + #include #include #include @@ -48,7 +49,7 @@ std::unique_ptr PKCS11_ECDSA_PrivateKey::public_key() const { namespace { class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature { - public: + private: PKCS11_ECDSA_Signature_Operation(const PKCS11_ECDSA_PrivateKey& key, std::string_view hash) : PK_Ops::Signature(), m_key(key), @@ -56,6 +57,10 @@ class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature { 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().required()) {} + void update(std::span input) override { if(!m_initialized) { // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed @@ -110,13 +115,17 @@ AlgorithmIdentifier PKCS11_ECDSA_Signature_Operation::algorithm_identifier() con } class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification { - public: + 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(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().required()) {} + void update(std::span input) override { if(!m_initialized) { // first call to update: initialize and cache message because we can not determine yet whether a single- or multiple-part operation will be performed @@ -170,15 +179,15 @@ class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification { } // namespace -std::unique_ptr PKCS11_ECDSA_PublicKey::create_verification_op( - std::string_view params, std::string_view /*provider*/) const { - return std::make_unique(*this, params); +std::unique_ptr PKCS11_ECDSA_PublicKey::_create_verification_op( + PK_Signature_Options& options) const { + return std::make_unique(*this, options); } -std::unique_ptr PKCS11_ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view /*provider*/) const { - return std::make_unique(*this, params); +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); } PKCS11_ECDSA_KeyPair generate_ecdsa_keypair(Session& session, diff --git a/src/lib/prov/pkcs11/p11_ecdsa.h b/src/lib/prov/pkcs11/p11_ecdsa.h index 53680b3f567..9c0e1b22e63 100644 --- a/src/lib/prov/pkcs11/p11_ecdsa.h +++ b/src/lib/prov/pkcs11/p11_ecdsa.h @@ -58,8 +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(std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP @@ -118,9 +117,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 209e3eb984b..6c5ac2a1729 100644 --- a/src/lib/prov/pkcs11/p11_mechanism.cpp +++ b/src/lib/prov/pkcs11/p11_mechanism.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -85,39 +86,24 @@ class RSA_SignMechanism final : public MechanismData { const std::map SignMechanisms = { {"Raw", RSA_SignMechanism(MechanismType::RsaX509)}, - {"EMSA2(Raw)", RSA_SignMechanism(MechanismType::RsaX931)}, - {"EMSA2(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaX931)}, + {"X9.31(Raw)", RSA_SignMechanism(MechanismType::RsaX931)}, + {"X9.31(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaX931)}, // RSASSA PKCS#1 v1.5 - {"EMSA3(Raw)", RSA_SignMechanism(MechanismType::RsaPkcs)}, - {"EMSA3(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs)}, - {"EMSA3(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs)}, - {"EMSA3(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs)}, - {"EMSA3(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs)}, - {"EMSA3(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs)}, - - {"EMSA_PKCS1(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs)}, - {"EMSA_PKCS1(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs)}, - {"EMSA_PKCS1(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs)}, - {"EMSA_PKCS1(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs)}, - {"EMSA_PKCS1(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs)}, + {"PKCS1v15(Raw)", RSA_SignMechanism(MechanismType::RsaPkcs)}, + {"PKCS1v15(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcs)}, + {"PKCS1v15(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcs)}, + {"PKCS1v15(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcs)}, + {"PKCS1v15(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcs)}, + {"PKCS1v15(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcs)}, // RSASSA PKCS#1 PSS - {"EMSA4(Raw)", RSA_SignMechanism(MechanismType::RsaPkcsPss)}, - {"EMSA4(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcsPss)}, - {"EMSA4(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcsPss)}, - - {"EMSA4(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss)}, - {"EMSA4(SHA-256,MGF1,32)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss)}, - {"PSSR(SHA-256,MGF1,32)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss)}, - - {"EMSA4(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss)}, - {"EMSA4(SHA-384,MGF1,48)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss)}, - {"PSSR(SHA-384,MGF1,48)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss)}, - - {"EMSA4(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss)}, - {"EMSA4(SHA-512,MGF1,64)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss)}, - {"PSSR(SHA-512,MGF1,64)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss)}, + {"PSS(Raw)", RSA_SignMechanism(MechanismType::RsaPkcsPss)}, + {"PSS(SHA-1)", RSA_SignMechanism(MechanismType::Sha1RsaPkcsPss)}, + {"PSS(SHA-224)", RSA_SignMechanism(MechanismType::Sha224RsaPkcsPss)}, + {"PSS(SHA-256)", RSA_SignMechanism(MechanismType::Sha256RsaPkcsPss)}, + {"PSS(SHA-384)", RSA_SignMechanism(MechanismType::Sha384RsaPkcsPss)}, + {"PSS(SHA-512)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss)}, {"ISO9796", RSA_SignMechanism(MechanismType::Rsa9796)}}; @@ -204,12 +190,30 @@ MechanismWrapper MechanismWrapper::create_rsa_crypt_mechanism(std::string_view p return mech; } -MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(std::string_view padding_view) { - const std::string padding(padding_view); - auto mechanism_info_it = SignMechanisms.find(padding); +MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(PK_Signature_Options& options) { + const std::string mechanism_padding = [&]() { + const auto hash = options.hash_function().optional(); + const auto padding = options.padding().optional(); + + if(hash && padding) { + return fmt("{}({})", padding.value(), hash.value()); + } + + if(padding) { + return padding.value(); + } + + if(hash) { + return hash.value(); + } + + throw Invalid_Argument("RSA signature requires a padding scheme"); + }(); + + 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 EMSA " + padding); + throw Lookup_Error("PKCS#11 RSA sign/verify does not support padding with " + mechanism_padding); } RSA_SignMechanism mechanism_info = mechanism_info_it->second; diff --git a/src/lib/prov/pkcs11/p11_mechanism.h b/src/lib/prov/pkcs11/p11_mechanism.h index 4a9a436405a..b6200b7abb7 100644 --- a/src/lib/prov/pkcs11/p11_mechanism.h +++ b/src/lib/prov/pkcs11/p11_mechanism.h @@ -13,8 +13,13 @@ #include #include -#include -#include +#include + +namespace Botan { + +class PK_Signature_Options; + +} // namespace Botan namespace Botan::PKCS11 { @@ -36,10 +41,10 @@ class BOTAN_PUBLIC_API(3, 7) MechanismWrapper final { /** * Creates the CK_MECHANISM data for RSA signature/verification - * @param padding supported paddings are Raw (X.509), EMSA3 (PKCS#1 v1.5), EMSA4 (PKCS#1 PSS), + * @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(std::string_view padding); + 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 1c77ced553e..dd13889b6aa 100644 --- a/src/lib/prov/pkcs11/p11_rsa.cpp +++ b/src/lib/prov/pkcs11/p11_rsa.cpp @@ -211,8 +211,8 @@ 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, std::string_view padding) : - m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding)) {} + 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,8 +328,8 @@ 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, std::string_view padding) : - m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(padding)) {} + 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 { if(!m_initialized) { @@ -393,9 +393,9 @@ std::unique_ptr PKCS11_RSA_PublicKey::create_encryption_op(R return std::make_unique(*this, params); } -std::unique_ptr PKCS11_RSA_PublicKey::create_verification_op( - std::string_view params, std::string_view /*provider*/) const { - return std::make_unique(*this, params); +std::unique_ptr PKCS11_RSA_PublicKey::_create_verification_op( + PK_Signature_Options& options) const { + return std::make_unique(*this, options); } std::unique_ptr PKCS11_RSA_PrivateKey::create_decryption_op(RandomNumberGenerator& rng, @@ -408,10 +408,10 @@ std::unique_ptr PKCS11_RSA_PrivateKey::create_decryption_op( } } -std::unique_ptr PKCS11_RSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view /*provider*/) const { - return std::make_unique(*this, params); +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); } PKCS11_RSA_KeyPair generate_rsa_keypair(Session& session, diff --git a/src/lib/prov/pkcs11/p11_rsa.h b/src/lib/prov/pkcs11/p11_rsa.h index a9c81195b70..c9bff5549c6 100644 --- a/src/lib/prov/pkcs11/p11_rsa.h +++ b/src/lib/prov/pkcs11/p11_rsa.h @@ -85,8 +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(std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; }; /// Properties for importing a PKCS#11 RSA private key @@ -191,9 +190,8 @@ class BOTAN_PUBLIC_API(2, 0) PKCS11_RSA_PrivateKey final : public Object, std::string_view params, std::string_view provider) const override; - std::unique_ptr create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 5444fa5837b..e857154326f 100644 --- a/src/lib/prov/tpm/tpm.cpp +++ b/src/lib/prov/tpm/tpm.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -371,10 +372,13 @@ class TPM_Signing_Operation final : public PK_Ops::Signature { } // namespace -std::unique_ptr TPM_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view /*provider*/) const { - return std::make_unique(*this, params); +std::unique_ptr TPM_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + if(auto padding = options.padding().optional(); !padding.has_value() || padding != "PKCS1v15") { + throw Invalid_Argument("TPMv1 can only sign using PKCS1v15 padding"); + } + return std::make_unique(*this, options.hash_function().required()); } } // namespace Botan diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h index 6bc28c44b98..733a822b11c 100644 --- a/src/lib/prov/tpm/tpm.h +++ b/src/lib/prov/tpm/tpm.h @@ -172,9 +172,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const override; private: TPM_Context& m_ctx; diff --git a/src/lib/prov/tpm2/tpm2_algo_mappings.h b/src/lib/prov/tpm2/tpm2_algo_mappings.h index fcf1b40dcea..324141bede5 100644 --- a/src/lib/prov/tpm2/tpm2_algo_mappings.h +++ b/src/lib/prov/tpm2/tpm2_algo_mappings.h @@ -346,9 +346,11 @@ namespace Botan::TPM2 { return std::nullopt; } - if(scheme.value() == TPM2_ALG_RSAPSS && req.arg_count() != 1) { - // RSA signing using PSS with MGF1 - return std::nullopt; + if(scheme.value() == TPM2_ALG_RSAPSS) { + // RSA signing using PSS with MGF1 and salt size of 32 + if(req.arg(1, "MGF1") != "MGF1" || req.arg_as_integer(2, 32) != 32) { + return std::nullopt; + } } return TPMT_SIG_SCHEME{ diff --git a/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp b/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp index 04552a65257..44a49e689da 100644 --- a/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp +++ b/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp @@ -159,15 +159,16 @@ std::unique_ptr EC_PrivateKey::create_unrestricted_transient(c namespace { -SignatureAlgorithmSelection make_signature_scheme(std::string_view hash_name) { +SignatureAlgorithmSelection make_signature_scheme(PK_Signature_Options& options) { + auto hash_name = options.hash_function().required(); return { .signature_scheme = TPMT_SIG_SCHEME{ .scheme = TPM2_ALG_ECDSA, // Only support ECDSA .details = {.any = {.hashAlg = get_tpm2_hash_type(hash_name)}}, }, - .hash_name = std::string(hash_name), - .padding = std::nullopt, + .hash_name = std::move(hash_name), + .emsa = nullptr, // ECC does not use an EMSA }; } @@ -183,8 +184,8 @@ size_t signature_length_for_key_handle(const SessionBundle& sessions, const Obje class EC_Signature_Operation final : public Signature_Operation { public: - EC_Signature_Operation(const Object& object, const SessionBundle& sessions, std::string_view hash) : - Signature_Operation(object, sessions, make_signature_scheme(hash)) {} + EC_Signature_Operation(const Object& object, const SessionBundle& sessions, PK_Signature_Options& options) : + Signature_Operation(object, sessions, make_signature_scheme(options)) {} size_t signature_length() const override { return signature_length_for_key_handle(sessions(), key_handle()); } @@ -211,8 +212,8 @@ class EC_Signature_Operation final : public Signature_Operation { class EC_Verification_Operation final : public Verification_Operation { public: - EC_Verification_Operation(const Object& object, const SessionBundle& sessions, std::string_view hash) : - Verification_Operation(object, sessions, make_signature_scheme(hash)) {} + EC_Verification_Operation(const Object& object, const SessionBundle& sessions, PK_Signature_Options& options) : + Verification_Operation(object, sessions, make_signature_scheme(options)) {} private: TPMT_SIGNATURE unmarshal_signature(std::span sig_data) const override { @@ -239,17 +240,16 @@ class EC_Verification_Operation final : public Verification_Operation { } // namespace -std::unique_ptr EC_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - BOTAN_UNUSED(provider); - return std::make_unique(handles(), sessions(), params); +std::unique_ptr EC_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(handles(), sessions(), options); } -std::unique_ptr EC_PrivateKey::create_signature_op(Botan::RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { - BOTAN_UNUSED(rng, provider); - return std::make_unique(handles(), sessions(), params); +std::unique_ptr EC_PrivateKey::_create_signature_op(Botan::RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + return std::make_unique(handles(), sessions(), options); } } // namespace Botan::TPM2 diff --git a/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h b/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h index 6f9d166e080..03faf801741 100644 --- a/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h +++ b/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h @@ -37,8 +37,7 @@ class BOTAN_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::Pu return op == PublicKeyOperation::Signature; } - std::unique_ptr create_verification_op(std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; protected: friend class TPM2::PublicKey; @@ -96,9 +95,8 @@ class BOTAN_PUBLIC_API(3, 6) EC_PrivateKey final : public virtual Botan::TPM2::P bool supports_operation(PublicKeyOperation op) const override { return op == PublicKeyOperation::Signature; } - std::unique_ptr create_signature_op(Botan::RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(Botan::RandomNumberGenerator& rng, + PK_Signature_Options& options) const override; protected: friend class TPM2::PrivateKey; diff --git a/src/lib/prov/tpm2/tpm2_pkops.cpp b/src/lib/prov/tpm2/tpm2_pkops.cpp index bf1b9c48f5c..ff76d1b3b48 100644 --- a/src/lib/prov/tpm2/tpm2_pkops.cpp +++ b/src/lib/prov/tpm2/tpm2_pkops.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -44,9 +45,14 @@ std::unique_ptr create_hash_function(const Object& key_hand Signature_Operation::Signature_Operation(const Object& object, const SessionBundle& sessions, - const SignatureAlgorithmSelection& algorithms) : + SignatureAlgorithmSelection algorithms) : Botan::TPM2::Signature_Operation_Base( - object, sessions, algorithms, create_hash_function(object, sessions, algorithms.hash_name)) {} + object, + sessions, + { + create_hash_function(object, sessions, algorithms.hash_name), + std::move(algorithms), + }) {} std::vector Signature_Operation::sign(Botan::RandomNumberGenerator& rng) { BOTAN_UNUSED(rng); @@ -96,9 +102,13 @@ std::vector Signature_Operation::sign(Botan::RandomNumberGenerator& rng Verification_Operation::Verification_Operation(const Object& object, const SessionBundle& sessions, - const SignatureAlgorithmSelection& algorithms) : - Signature_Operation_Base( - object, sessions, algorithms, Botan::HashFunction::create_or_throw(algorithms.hash_name)) {} + SignatureAlgorithmSelection algorithms) : + Signature_Operation_Base(object, + sessions, + { + Botan::HashFunction::create_or_throw(algorithms.hash_name), + std::move(algorithms), + }) {} bool Verification_Operation::is_valid_signature(std::span sig_data) { TPM2B_DIGEST digest; diff --git a/src/lib/prov/tpm2/tpm2_pkops.h b/src/lib/prov/tpm2/tpm2_pkops.h index 52ca9bb3619..8785732bbff 100644 --- a/src/lib/prov/tpm2/tpm2_pkops.h +++ b/src/lib/prov/tpm2/tpm2_pkops.h @@ -12,6 +12,7 @@ #include #include +#include #include namespace Botan::TPM2 { @@ -19,21 +20,21 @@ namespace Botan::TPM2 { struct SignatureAlgorithmSelection { TPMT_SIG_SCHEME signature_scheme; std::string hash_name; - std::optional padding; + std::unique_ptr emsa; }; template class Signature_Operation_Base : public PKOpT { public: - Signature_Operation_Base(const Object& object, - const SessionBundle& sessions, - const SignatureAlgorithmSelection& algorithms, - std::unique_ptr hash) : + Signature_Operation_Base( + const Object& object, + const SessionBundle& sessions, + std::pair, SignatureAlgorithmSelection> algorithms) : m_key_handle(object), m_sessions(sessions), - m_scheme(algorithms.signature_scheme), - m_hash(std::move(hash)), - m_padding(algorithms.padding) { + m_hash(std::move(algorithms.first)), + m_scheme(algorithms.second.signature_scheme), + m_emsa(std::move(algorithms.second.emsa)) { BOTAN_ASSERT_NONNULL(m_hash); } @@ -51,14 +52,22 @@ class Signature_Operation_Base : public PKOpT { const TPMT_SIG_SCHEME& scheme() const { return m_scheme; } - std::optional padding() const { return m_padding; } + EMSA* emsa() const { + BOTAN_STATE_CHECK(m_emsa); + return m_emsa.get(); + } private: const Object& m_key_handle; const SessionBundle& m_sessions; - TPMT_SIG_SCHEME m_scheme; std::unique_ptr m_hash; - std::optional m_padding; + TPMT_SIG_SCHEME m_scheme; + + // This EMSA object actually isn't required, we just need it to + // conveniently parse the EMSA the user selected. + // + // TODO: This is a hack, and we should clean this up. + std::unique_ptr m_emsa; }; /** @@ -73,9 +82,7 @@ class Signature_Operation_Base : public PKOpT { */ class Signature_Operation : public Signature_Operation_Base { public: - Signature_Operation(const Object& object, - const SessionBundle& sessions, - const SignatureAlgorithmSelection& algorithms); + Signature_Operation(const Object& object, const SessionBundle& sessions, SignatureAlgorithmSelection algorithms); std::vector sign(Botan::RandomNumberGenerator& rng) override; @@ -91,7 +98,7 @@ class Verification_Operation : public Signature_Operation_Base sig_data) override; diff --git a/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp b/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp index 3f8a406f39b..4887f0a2b80 100644 --- a/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp +++ b/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp @@ -125,21 +125,24 @@ std::unique_ptr RSA_PrivateKey::create_unrestricted_transient( namespace { -SignatureAlgorithmSelection select_signature_algorithms(std::string_view padding) { - const SCAN_Name req(padding); +SignatureAlgorithmSelection select_signature_algorithms(PK_Signature_Options& options) { + auto emsa = EMSA::create_or_throw(options); + const auto emsa_name = emsa->name(); + + const SCAN_Name req(emsa_name); if(req.arg_count() == 0) { throw Invalid_Argument("RSA signing padding scheme must at least specify a hash function"); } - auto sig_scheme = rsa_signature_scheme_botan_to_tss2(padding); + auto sig_scheme = rsa_signature_scheme_botan_to_tss2(emsa_name); if(!sig_scheme) { - throw Not_Implemented(Botan::fmt("RSA signing with padding scheme {}", padding)); + throw Not_Implemented(Botan::fmt("RSA signing with padding scheme {}", emsa_name)); } return { .signature_scheme = sig_scheme.value(), - .hash_name = req.arg(0), - .padding = std::string(padding), + .hash_name = emsa->hash_function(), + .emsa = std::move(emsa), }; } @@ -149,8 +152,8 @@ size_t signature_length_for_key_handle(const SessionBundle& sessions, const Obje class RSA_Signature_Operation final : public Signature_Operation { public: - RSA_Signature_Operation(const Object& object, const SessionBundle& sessions, std::string_view padding) : - Signature_Operation(object, sessions, select_signature_algorithms(padding)) {} + RSA_Signature_Operation(const Object& object, const SessionBundle& sessions, PK_Signature_Options& options) : + Signature_Operation(object, sessions, select_signature_algorithms(options)) {} size_t signature_length() const override { return signature_length_for_key_handle(sessions(), key_handle()); } @@ -158,14 +161,7 @@ class RSA_Signature_Operation final : public Signature_Operation { // TODO: This is essentially a copy of the ::algorithm_identifier() // in `rsa.h`. We should probably refactor this into a common // function. - - // This EMSA object actually isn't required, we just need it to - // conveniently figure out the algorithm identifier. - // - // TODO: This is a hack, and we should clean this up. - BOTAN_STATE_CHECK(padding().has_value()); - const auto emsa = EMSA::create_or_throw(padding().value()); - const std::string emsa_name = emsa->name(); + const std::string emsa_name = emsa()->name(); try { const std::string full_name = "RSA/" + emsa_name; @@ -199,8 +195,8 @@ class RSA_Signature_Operation final : public Signature_Operation { class RSA_Verification_Operation final : public Verification_Operation { public: - RSA_Verification_Operation(const Object& object, const SessionBundle& sessions, std::string_view padding) : - Verification_Operation(object, sessions, select_signature_algorithms(padding)) {} + RSA_Verification_Operation(const Object& object, const SessionBundle& sessions, PK_Signature_Options& options) : + Verification_Operation(object, sessions, select_signature_algorithms(options)) {} private: TPMT_SIGNATURE unmarshal_signature(std::span signature) const override { @@ -373,17 +369,16 @@ class RSA_Decryption_Operation final : public PK_Ops::Decryption { } // namespace -std::unique_ptr RSA_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - BOTAN_UNUSED(provider); - return std::make_unique(handles(), sessions(), params); +std::unique_ptr RSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(handles(), sessions(), options); } -std::unique_ptr RSA_PrivateKey::create_signature_op(Botan::RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { - BOTAN_UNUSED(rng, provider); - return std::make_unique(handles(), sessions(), params); +std::unique_ptr RSA_PrivateKey::_create_signature_op(Botan::RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + return std::make_unique(handles(), sessions(), options); } std::unique_ptr RSA_PublicKey::create_encryption_op(Botan::RandomNumberGenerator& rng, diff --git a/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h b/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h index 32e33fbf7ce..a4e4c9749c1 100644 --- a/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h +++ b/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h @@ -30,8 +30,7 @@ class BOTAN_PUBLIC_API(3, 6) RSA_PublicKey final : public virtual Botan::TPM2::P return op == PublicKeyOperation::Encryption || op == PublicKeyOperation::Signature; } - std::unique_ptr create_verification_op(std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_verification_op(PK_Signature_Options& options) const override; std::unique_ptr create_encryption_op(Botan::RandomNumberGenerator& rng, std::string_view params, @@ -86,9 +85,8 @@ class BOTAN_PUBLIC_API(3, 6) RSA_PrivateKey final : public virtual Botan::TPM2:: return op == PublicKeyOperation::Encryption || op == PublicKeyOperation::Signature; } - std::unique_ptr create_signature_op(Botan::RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(Botan::RandomNumberGenerator& rng, + PK_Signature_Options& options) const override; std::unique_ptr create_decryption_op(Botan::RandomNumberGenerator& rng, std::string_view params, diff --git a/src/lib/pubkey/curve448/ed448/ed448.cpp b/src/lib/pubkey/curve448/ed448/ed448.cpp index 3b1d8cb4fe8..b434c2977bf 100644 --- a/src/lib/pubkey/curve448/ed448/ed448.cpp +++ b/src/lib/pubkey/curve448/ed448/ed448.cpp @@ -215,18 +215,14 @@ AlgorithmIdentifier Ed448_Sign_Operation::algorithm_identifier() const { } // namespace -std::unique_ptr Ed448_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - if(params.empty() || params == "Identity" || params == "Pure" || params == "Ed448") { - return std::make_unique(*this); - } else if(params == "Ed448ph") { - return std::make_unique(*this, "SHAKE-256(512)"); - } else { - return std::make_unique(*this, std::string(params)); - } +std::unique_ptr Ed448_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + + if(auto prehash = options.prehash().optional()) { + return std::make_unique(*this, prehash->value_or("SHAKE-256(512)")); + } else { + return std::make_unique(*this); } - throw Provider_Not_Found(algo_name(), provider); } std::unique_ptr Ed448_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, @@ -241,19 +237,16 @@ std::unique_ptr Ed448_PublicKey::create_x509_verification_ throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr Ed448_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - if(params.empty() || params == "Identity" || params == "Pure" || params == "Ed448") { - return std::make_unique(*this); - } else if(params == "Ed448ph") { - return std::make_unique(*this, "SHAKE-256(512)"); - } else { - return std::make_unique(*this, std::string(params)); - } +std::unique_ptr Ed448_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + + if(auto prehash = options.prehash().optional()) { + return std::make_unique(*this, prehash->value_or("SHAKE-256(512)")); + } else { + return std::make_unique(*this); } - throw Provider_Not_Found(algo_name(), provider); } } // namespace Botan diff --git a/src/lib/pubkey/curve448/ed448/ed448.h b/src/lib/pubkey/curve448/ed448/ed448.h index 44997e67351..dee4fa5a1f1 100644 --- a/src/lib/pubkey/curve448/ed448/ed448.h +++ b/src/lib/pubkey/curve448/ed448/ed448.h @@ -20,7 +20,7 @@ namespace Botan { * * By default, Ed448 without prehash is used (recommended). To use * Ed448ph, "Ed448ph" or a custom hash function identifier is passed - * as a parameter to the create_verification_op method. + * as a parameter to the _create_verification_op method. * * Note that contexts (i.e. Ed448ctx) are not supported by this interface. */ @@ -56,8 +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(std::string_view params, - std::string_view provider) 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; @@ -75,7 +74,7 @@ BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE * * By default, Ed448 without prehash is used (recommended). To use * Ed448ph, "Ed448ph" or a custom hash function identifier is passed - * as a parameter to the create_verification_op method. + * as a parameter to the _create_verification_op method. * * Note that contexts (i.e. Ed448ctx) are not supported by this interface. */ @@ -112,9 +111,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 857795b7d47..66b2f5b2b9f 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp @@ -126,16 +126,27 @@ bool DilithiumMode::is_available() const { return false; } +// 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(DilithiumInternalKeypair keypair, bool randomized) : + Dilithium_Signature_Operation(DilithiumInternalKeypair keypair, PK_Signature_Options& options) : m_keypair(std::move(keypair)), - m_randomized(randomized), + + // FIPS 204, Section 3.4 + // By default, this standard specifies the signing algorithm to use both + // types of randomness [fresh from the RNG and a value in the private key]. + // This is referred to as the “hedged” variant of the signing procedure. + m_randomized(!options.using_deterministic_signature()), m_h(m_keypair.second->mode().symmetric_primitives().get_message_hash(m_keypair.first->tr())), m_s1(ntt(m_keypair.second->s1().clone())), m_s2(ntt(m_keypair.second->s2().clone())), m_t0(ntt(m_keypair.second->t0().clone())), - m_A(Dilithium_Algos::expand_A(m_keypair.first->rho(), m_keypair.second->mode())) {} + m_A(Dilithium_Algos::expand_A(m_keypair.first->rho(), m_keypair.second->mode())) { + m_h->start(options.context().optional().value_or(std::vector())); + options.prehash().not_implemented("HashML-DSA currently not supported"); + } void update(std::span input) override { m_h->update(input); } @@ -248,11 +259,15 @@ class Dilithium_Signature_Operation final : public PK_Ops::Signature { class Dilithium_Verification_Operation final : public PK_Ops::Verification { public: - Dilithium_Verification_Operation(std::shared_ptr pubkey) : + Dilithium_Verification_Operation(std::shared_ptr pubkey, + PK_Signature_Options& options) : m_pub_key(std::move(pubkey)), m_A(Dilithium_Algos::expand_A(m_pub_key->rho(), m_pub_key->mode())), m_t1_ntt_shifted(ntt(m_pub_key->t1() << DilithiumConstants::D)), - m_h(m_pub_key->mode().symmetric_primitives().get_message_hash(m_pub_key->tr())) {} + m_h(m_pub_key->mode().symmetric_primitives().get_message_hash(m_pub_key->tr())) { + m_h->start(options.context().optional().value_or(std::vector())); + options.prehash().not_implemented("HashML-DSA currently not supported"); + } void update(std::span input) override { m_h->update(input); } @@ -373,24 +388,19 @@ std::unique_ptr Dilithium_PublicKey::generate_another(RandomNumberG return std::make_unique(rng, m_public->mode().mode()); } -std::unique_ptr Dilithium_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - BOTAN_ARG_CHECK(params.empty() || params == "Pure", "Unexpected parameters for verifying with Dilithium"); - if(provider.empty() || provider == "base") { - return std::make_unique(m_public); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr Dilithium_PublicKey::_create_verification_op( + PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(m_public, options); } std::unique_ptr Dilithium_PublicKey::create_x509_verification_op( const AlgorithmIdentifier& alg_id, std::string_view provider) const { - if(provider.empty() || provider == "base") { - if(alg_id != this->algorithm_identifier()) { - throw Decoding_Error("Unexpected AlgorithmIdentifier for Dilithium X.509 signature"); - } - return std::make_unique(m_public); + if(alg_id != this->algorithm_identifier()) { + throw Decoding_Error("Unexpected AlgorithmIdentifier for Dilithium X.509 signature"); } - throw Provider_Not_Found(algo_name(), provider); + auto options = PK_Verification_Options_Builder().with_provider(provider).commit(); + return _create_verification_op(options); } /** @@ -427,23 +437,11 @@ secure_vector Dilithium_PrivateKey::private_key_bits() const { return m_private->mode().keypair_codec().encode_keypair({m_public, m_private}); } -std::unique_ptr Dilithium_PrivateKey::create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { +std::unique_ptr Dilithium_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - - BOTAN_ARG_CHECK(params.empty() || params == "Deterministic" || params == "Randomized", - "Unexpected parameters for signing with ML-DSA/Dilithium"); - - // FIPS 204, Section 3.4 - // By default, this standard specifies the signing algorithm to use both - // types of randomness [fresh from the RNG and a value in the private key]. - // This is referred to as the “hedged” variant of the signing procedure. - const bool randomized = (params.empty() || params == "Randomized"); - if(provider.empty() || provider == "base") { - return std::make_unique(DilithiumInternalKeypair{m_public, m_private}, randomized); - } - throw Provider_Not_Found(algo_name(), provider); + options.exclude_provider(); + return std::make_unique(DilithiumInternalKeypair{m_public, 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 634abe37579..c6f7b330f42 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h @@ -94,8 +94,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(std::string_view params, - std::string_view provider) 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; @@ -136,14 +135,8 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PrivateKey final : public virtual Dilithi secure_vector raw_private_key_bits() const override; - /** - * Create a signature operation that produces a Dilithium signature either - * with "Randomized" or "Deterministic" rhoprime. Pass either of those - * strings as @p params. Default (i.e. empty @p params is "Randomized"). - */ - std::unique_ptr create_signature_op(RandomNumberGenerator&, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 66e444042c3..5104db55330 100644 --- a/src/lib/pubkey/dsa/dsa.cpp +++ b/src/lib/pubkey/dsa/dsa.cpp @@ -126,9 +126,9 @@ namespace { class DSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: DSA_Signature_Operation(const std::shared_ptr& key, - std::string_view emsa, + PK_Signature_Options& options, RandomNumberGenerator& rng) : - PK_Ops::Signature_with_Hash(emsa), m_key(key) { + PK_Ops::Signature_with_Hash(options), m_key(key) { m_b = BigInt::random_integer(rng, 2, m_key->group().get_q()); m_b_inv = m_key->group().inverse_mod_q(m_b); } @@ -204,8 +204,8 @@ 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, std::string_view emsa) : - PK_Ops::Verification_with_Hash(emsa), m_key(key) {} + 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) : PK_Ops::Verification_with_Hash(alg_id, "DSA"), m_key(key) {} @@ -251,12 +251,9 @@ bool DSA_Verification_Operation::verify(std::span input, std::spa } // namespace -std::unique_ptr DSA_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(this->m_public_key, params); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr DSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(this->m_public_key, options); } std::unique_ptr DSA_PublicKey::create_x509_verification_op( @@ -268,13 +265,10 @@ std::unique_ptr DSA_PublicKey::create_x509_verification_op throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr DSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(this->m_private_key, params, rng); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr DSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + options.exclude_provider(); + 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 fa241bc2646..8e97aeac602 100644 --- a/src/lib/pubkey/dsa/dsa.h +++ b/src/lib/pubkey/dsa/dsa.h @@ -59,8 +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(std::string_view params, - std::string_view provider) 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,9 +114,8 @@ class BOTAN_PUBLIC_API(2, 0) DSA_PrivateKey final : public DSA_PublicKey, const BigInt& get_int_field(std::string_view field) const override; secure_vector raw_private_key_bits() const override; - std::unique_ptr create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 665f1f27c0e..afd49f1ece5 100644 --- a/src/lib/pubkey/ecdsa/ecdsa.cpp +++ b/src/lib/pubkey/ecdsa/ecdsa.cpp @@ -121,8 +121,10 @@ namespace { */ class ECDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: - ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa, std::string_view padding, RandomNumberGenerator& rng) : - PK_Ops::Signature_with_Hash(padding), + ECDSA_Signature_Operation(const ECDSA_PrivateKey& ecdsa, + PK_Signature_Options& options, + RandomNumberGenerator& rng) : + PK_Ops::Signature_with_Hash(options), m_group(ecdsa.domain()), m_x(ecdsa._private_key()), m_b(EC_Scalar::random(m_group, rng)), @@ -196,8 +198,8 @@ std::vector ECDSA_Signature_Operation::raw_sign(std::span msg, std::spa } // namespace -std::unique_ptr ECDSA_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr ECDSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(*this, options); } std::unique_ptr ECDSA_PublicKey::create_x509_verification_op( @@ -248,14 +246,10 @@ std::unique_ptr ECDSA_PublicKey::create_x509_verification_ throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr ECDSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params, rng); - } - - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr ECDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + options.exclude_provider(); + 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 af3c7877a1b..28900a6ea11 100644 --- a/src/lib/pubkey/ecdsa/ecdsa.h +++ b/src/lib/pubkey/ecdsa/ecdsa.h @@ -71,8 +71,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(std::string_view params, - std::string_view provider) 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; @@ -127,9 +126,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 9bc9946ddab..513dbe7de01 100644 --- a/src/lib/pubkey/ecgdsa/ecgdsa.cpp +++ b/src/lib/pubkey/ecgdsa/ecgdsa.cpp @@ -36,8 +36,8 @@ namespace { */ class ECGDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: - ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa, std::string_view emsa) : - PK_Ops::Signature_with_Hash(emsa), m_group(ecgdsa.domain()), m_x(ecgdsa._private_key()) {} + 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,8 +79,8 @@ 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(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr ECGDSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(*this, options); } std::unique_ptr ECGDSA_PublicKey::create_x509_verification_op( @@ -134,13 +131,11 @@ std::unique_ptr ECGDSA_PublicKey::create_x509_verification throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr ECGDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr ECGDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + 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 111b56686a2..8a899554707 100644 --- a/src/lib/pubkey/ecgdsa/ecgdsa.h +++ b/src/lib/pubkey/ecgdsa/ecgdsa.h @@ -55,8 +55,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(std::string_view params, - std::string_view provider) 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,9 +111,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP diff --git a/src/lib/pubkey/ecies/ecies.h b/src/lib/pubkey/ecies/ecies.h index 36e0ad8dc17..870cfa88f07 100644 --- a/src/lib/pubkey/ecies/ecies.h +++ b/src/lib/pubkey/ecies/ecies.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.cpp b/src/lib/pubkey/eckcdsa/eckcdsa.cpp index 6607fd37f86..a825525f8ea 100644 --- a/src/lib/pubkey/eckcdsa/eckcdsa.cpp +++ b/src/lib/pubkey/eckcdsa/eckcdsa.cpp @@ -38,23 +38,14 @@ bool ECKCDSA_PrivateKey::check_key(RandomNumberGenerator& rng, bool strong) cons namespace { -std::unique_ptr eckcdsa_signature_hash(std::string_view padding) { - if(auto hash = HashFunction::create(padding)) { - return hash; - } - - SCAN_Name req(padding); - - if(req.algo_name() == "EMSA1" && req.arg_count() == 1) { - if(auto hash = HashFunction::create(req.arg(0))) { - return hash; - } - } +std::unique_ptr eckcdsa_signature_hash(PK_Signature_Options& options) { + // TODO: We could support prehashing, but it's not standard + options.prehash().not_implemented("non-standard prehashing is not supported"); - // intentionally not supporting Raw for ECKCDSA, we need to know + // intentionally not supporting Raw for ECKCDSA, since we need to know // the length in advance which complicates the logic for Raw - throw Algorithm_Not_Found(padding); + return HashFunction::create_or_throw(options.hash_function().required()); } std::unique_ptr eckcdsa_signature_hash(const AlgorithmIdentifier& alg_id) { @@ -115,10 +106,10 @@ 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& eckcdsa, std::string_view padding) : + ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& eckcdsa, PK_Signature_Options& options) : m_group(eckcdsa.domain()), m_x(eckcdsa._private_key()), - m_hash(eckcdsa_signature_hash(padding)), + m_hash(eckcdsa_signature_hash(options)), m_prefix(eckcdsa_prefix(eckcdsa._public_ec_point(), m_hash->hash_block_size())), m_prefix_used(false) {} @@ -187,10 +178,10 @@ std::vector ECKCDSA_Signature_Operation::raw_sign(std::spanhash_block_size())), m_prefix_used(false) {} @@ -264,12 +255,9 @@ std::unique_ptr ECKCDSA_PublicKey::generate_another(RandomNumberGen return std::make_unique(rng, domain()); } -std::unique_ptr ECKCDSA_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr ECKCDSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(*this, options); } std::unique_ptr ECKCDSA_PublicKey::create_x509_verification_op( @@ -281,13 +269,11 @@ std::unique_ptr ECKCDSA_PublicKey::create_x509_verificatio throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr ECKCDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr ECKCDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + 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 0c5a73f61e2..9510babe936 100644 --- a/src/lib/pubkey/eckcdsa/eckcdsa.h +++ b/src/lib/pubkey/eckcdsa/eckcdsa.h @@ -54,8 +54,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(std::string_view params, - std::string_view provider) 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; @@ -111,9 +110,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 73a12e51cae..fd8c8ad2cd6 100644 --- a/src/lib/pubkey/ed25519/ed25519.h +++ b/src/lib/pubkey/ed25519/ed25519.h @@ -48,8 +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(std::string_view params, - std::string_view provider) 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,9 +95,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 32858ff4bdc..cebb3d3261e 100644 --- a/src/lib/pubkey/ed25519/ed25519_key.cpp +++ b/src/lib/pubkey/ed25519/ed25519_key.cpp @@ -273,18 +273,15 @@ class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature { } // namespace -std::unique_ptr Ed25519_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - if(params.empty() || params == "Identity" || params == "Pure") { - return std::make_unique(*this); - } else if(params == "Ed25519ph") { - return std::make_unique(*this, "SHA-512", true); - } else { - return std::make_unique(*this, params, false); - } +std::unique_ptr Ed25519_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + + if(auto prehash = options.prehash().optional()) { + return std::make_unique( + *this, prehash->value_or("SHA-512"), !prehash->has_value()); + } else { + return std::make_unique(*this); } - throw Provider_Not_Found(algo_name(), provider); } std::unique_ptr Ed25519_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, @@ -299,19 +296,17 @@ std::unique_ptr Ed25519_PublicKey::create_x509_verificatio throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr Ed25519_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - if(params.empty() || params == "Identity" || params == "Pure") { - return std::make_unique(*this); - } else if(params == "Ed25519ph") { - return std::make_unique(*this, "SHA-512", true); - } else { - return std::make_unique(*this, params, false); - } +std::unique_ptr Ed25519_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + + if(auto prehash = options.prehash().optional()) { + return std::make_unique( + *this, prehash->value_or("SHA-512"), !prehash->has_value()); + } else { + return std::make_unique(*this); } - throw Provider_Not_Found(algo_name(), provider); } } // namespace Botan diff --git a/src/lib/pubkey/gost_3410/gost_3410.cpp b/src/lib/pubkey/gost_3410/gost_3410.cpp index e66d8655aa4..1666b1d16a0 100644 --- a/src/lib/pubkey/gost_3410/gost_3410.cpp +++ b/src/lib/pubkey/gost_3410/gost_3410.cpp @@ -124,8 +124,8 @@ 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, std::string_view emsa) : - PK_Ops::Signature_with_Hash(emsa), m_group(gost_3410.domain()), m_x(gost_3410._private_key()) {} + 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(); } @@ -176,26 +176,30 @@ std::vector GOST_3410_Signature_Operation::raw_sign(std::span std::optional { + if(oid_str == "GOST-34.10/GOST-R-34.11-94") { + return "GOST-R-34.11-94"; + } else if(oid_str == "GOST-34.10-2012-256/Streebog-256") { + return "Streebog-256"; + } else if(oid_str == "GOST-34.10-2012-512/Streebog-512") { + return "Streebog-512"; + } else if(oid_str == "GOST-34.10-2012-256/SHA-256") { + return "SHA-256"; + } else { + return std::nullopt; + } + }(alg_id.oid().to_formatted_string()); + + if(!hash.has_value()) { + throw Decoding_Error(fmt("Unknown OID ({}) for GOST 34.10 signatures", alg_id.oid())); } - throw Decoding_Error(fmt("Unknown OID ({}) for GOST 34.10 signatures", alg_id.oid())); + return PK_Verification_Options_Builder().with_hash(hash.value()).commit(); } /** @@ -203,13 +207,8 @@ std::string 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, std::string_view padding) : - PK_Ops::Verification_with_Hash(padding), m_group(gost.domain()), m_gy_mul(gost._public_ec_point()) {} - - GOST_3410_Verification_Operation(const GOST_3410_PublicKey& gost, const AlgorithmIdentifier& alg_id) : - PK_Ops::Verification_with_Hash(gost_hash_from_algid(alg_id)), - m_group(gost.domain()), - m_gy_mul(gost._public_ec_point()) {} + 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_ec_point()) {} bool verify(std::span msg, std::span sig) override; @@ -241,30 +240,27 @@ std::unique_ptr GOST_3410_PublicKey::generate_another(RandomNumberG return std::make_unique(rng, domain()); } -std::unique_ptr GOST_3410_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr GOST_3410_PublicKey::_create_verification_op( + PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(*this, options); } std::unique_ptr GOST_3410_PublicKey::create_x509_verification_op( const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const { if(provider == "base" || provider.empty()) { - return std::make_unique(*this, signature_algorithm); + auto opts = gost_hash_from_algid(signature_algorithm); + return std::make_unique(*this, opts); } throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr GOST_3410_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr GOST_3410_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + 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 fe79ab4465e..1da2870c53d 100644 --- a/src/lib/pubkey/gost_3410/gost_3410.h +++ b/src/lib/pubkey/gost_3410/gost_3410.h @@ -62,8 +62,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(std::string_view params, - std::string_view provider) 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; @@ -117,9 +116,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 43bfe7adc68..bda32b6685b 100644 --- a/src/lib/pubkey/hss_lms/hss_lms.cpp +++ b/src/lib/pubkey/hss_lms/hss_lms.cpp @@ -85,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(std::string_view /*params*/, - std::string_view provider) const { - if(provider.empty() || provider == "base") { - return std::make_unique(m_public); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr HSS_LMS_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + options.validate_for_hash_based_signature(); + return std::make_unique(m_public); } std::unique_ptr HSS_LMS_PublicKey::create_x509_verification_op( @@ -195,16 +193,14 @@ class HSS_LMS_Signature_Operation final : public PK_Ops::Signature { std::vector m_msg_buffer; }; -std::unique_ptr HSS_LMS_PrivateKey::create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { +std::unique_ptr HSS_LMS_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - BOTAN_ARG_CHECK(params.empty(), "Unexpected parameters for signing with HSS-LMS"); - if(provider.empty() || provider == "base") { - return std::make_unique(m_private, m_public); - } - throw Provider_Not_Found(algo_name(), provider); + options.exclude_provider(); + options.validate_for_hash_based_signature(); + + 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 8541cb3f250..78798f359ff 100644 --- a/src/lib/pubkey/hss_lms/hss_lms.h +++ b/src/lib/pubkey/hss_lms/hss_lms.h @@ -51,8 +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(std::string_view params, - std::string_view provider) 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,9 +145,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 bad0debd1d2..97cd2ffed0b 100644 --- a/src/lib/pubkey/info.txt +++ b/src/lib/pubkey/info.txt @@ -1,5 +1,5 @@ -PUBLIC_KEY_CRYPTO -> 20131128 +PUBLIC_KEY_CRYPTO -> 20240817 @@ -9,6 +9,8 @@ brief -> "Implementations of public key schemes" pk_algs.h +pk_enums.h +pk_options.h pk_keys.h pk_ops_fwd.h pk_ops.h diff --git a/src/lib/pubkey/pk_enums.h b/src/lib/pubkey/pk_enums.h new file mode 100644 index 00000000000..49ccd27c171 --- /dev/null +++ b/src/lib/pubkey/pk_enums.h @@ -0,0 +1,44 @@ +/* +* Public Key Interface +* (C) 1999-2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PK_ENUMS_H_ +#define BOTAN_PK_ENUMS_H_ + +#include + +namespace Botan { + +/** +* Enumeration specifying the signature format. +* +* This is mostly used for requesting DER encoding of ECDSA signatures; +* most other algorithms only support "standard". +*/ +enum class Signature_Format { + Standard, + DerSequence, + + IEEE_1363 BOTAN_DEPRECATED("Use Standard") = Standard, + DER_SEQUENCE BOTAN_DEPRECATED("Use DerSequence") = DerSequence, +}; + +/** +* Enumeration of possible operations a public key could be used for. +* +* It is possible to query if a key supports a particular operation +* type using Asymmetric_Key::supports_operation() +*/ +enum class PublicKeyOperation { + Encryption, + Signature, + KeyEncapsulation, + KeyAgreement, +}; + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/pk_keys.cpp b/src/lib/pubkey/pk_keys.cpp index 336be14255c..c270ae1df88 100644 --- a/src/lib/pubkey/pk_keys.cpp +++ b/src/lib/pubkey/pk_keys.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace Botan { @@ -19,6 +20,10 @@ const BigInt& Asymmetric_Key::get_int_field(std::string_view field) const { throw Unknown_PK_Field_Name(algo_name(), field); } +bool Asymmetric_Key::supports_context_data() const { + return false; +} + OID Asymmetric_Key::object_identifier() const { try { return OID::from_string(algo_name()); @@ -98,8 +103,8 @@ 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(std::string_view /*params*/, - std::string_view /*provider*/) 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())); } @@ -120,9 +125,9 @@ std::unique_ptr Private_Key::create_kem_decryption_op(Ra throw Lookup_Error(fmt("{} does not support KEM decryption", algo_name())); } -std::unique_ptr Private_Key::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view /*params*/, - std::string_view /*provider*/) const { +std::unique_ptr Private_Key::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng, options); throw Lookup_Error(fmt("{} does not support signatures", algo_name())); } @@ -132,4 +137,24 @@ std::unique_ptr Private_Key::create_key_agreement_op(Rand throw Lookup_Error(fmt("{} does not support key agreement", algo_name())); } +// Forwarding functions for compat + +std::unique_ptr Public_Key::create_verification_op(std::string_view params, + std::string_view provider) const { + auto opts = PK_Signature_Options::from_legacy( + *this /* won't be consumed */, params, Signature_Format::Standard /* won't be consumed */, 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 { + auto opts = PK_Signature_Options::from_legacy(*this /* won't be consumed */, + rng /* won't be consumed */, + params, + Signature_Format::Standard /* won't be consumed */, + 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 64844c8f2d3..4ba7f98cea1 100644 --- a/src/lib/pubkey/pk_keys.h +++ b/src/lib/pubkey/pk_keys.h @@ -9,7 +9,9 @@ #define BOTAN_PK_KEYS_H_ #include +#include #include +#include #include #include @@ -21,34 +23,7 @@ namespace Botan { class BigInt; class RandomNumberGenerator; - -/** -* Enumeration specifying the signature format. -* -* This is mostly used for requesting DER encoding of ECDSA signatures; -* most other algorithms only support "standard". -*/ -enum class Signature_Format { - Standard, - DerSequence, - - IEEE_1363 BOTAN_DEPRECATED("Use Standard") = Standard, - DER_SEQUENCE BOTAN_DEPRECATED("Use DerSequence") = DerSequence, -}; - -/** -* Enumeration of possible operations a public key could be used for. -* -* It is possible to query if a key supports a particular operation -* type using Asymmetric_Key::supports_operation() -*/ -enum class PublicKeyOperation { - Encryption, - Signature, - KeyEncapsulation, - KeyAgreement, -}; - +class PK_Signature_Options; class Private_Key; /** @@ -104,6 +79,23 @@ class BOTAN_PUBLIC_API(3, 0) Asymmetric_Key { */ virtual bool supports_operation(PublicKeyOperation op) const = 0; + /** + * Return true if this key supports contextual inputs during processing + * + * This is typically some protocol or user specific binding information + * which is included during cryptographic computations. + * + * This is only supported by a few algorithm types, so default + * implementation returns false. + * + * Note that support for contextual data may depend on both the algorithm + * and the library version. For example Ed25519 can support contextual + * data, using RFC 8032's Ed25519ctx construction, but this is not + * currently supported. In a future release, if Ed25519ctx was supported, + * then this function would start returning true for Ed25519 keys. + */ + virtual bool supports_context_data() const; + /** * Generate another (cryptographically independent) key pair using the * same algorithm parameters as this key. This is most useful for algorithms @@ -202,6 +194,24 @@ class BOTAN_PUBLIC_API(2, 0) Public_Key : public virtual Asymmetric_Key { return (this->message_parts() >= 2) ? Signature_Format::DerSequence : Signature_Format::Standard; } + /** + * Initiate the creation of a signature verification operation. + * This is a builder-style interface which allows setting various + * options for the operation. + * + * Typical usage: + * + * auto verifier = pub_key.signature_verifier() + * .with_padding("PSS(SHA-256)") + * .with_der_encoded_signature() + * .create(); + * + * @return a builder object for verification options + */ + PK_Verification_Options_Builder signature_verifier() const { + return PK_Verification_Options_Builder().with_public_key(*this); + } + /** * This is an internal library function exposed on key types. * In almost all cases applications should use wrappers in pubkey.h @@ -235,11 +245,24 @@ class BOTAN_PUBLIC_API(2, 0) Public_Key : public virtual Asymmetric_Key { * In all cases applications should use wrappers in pubkey.h * * Return a verification operation for this key/params or throw + * + * @param options which specify parameters of the signature beyond those + * implicit to the public key itself + */ + virtual std::unique_ptr _create_verification_op(PK_Signature_Options& options) const; + + /** + * This is an internal library function exposed on key types. + * In all cases applications should use wrappers in pubkey.h + * + * Return a verification operation for this key/params or throw + * * @param params additional parameters * @param provider the provider to use */ - virtual std::unique_ptr create_verification_op(std::string_view params, - std::string_view provider) const; + BOTAN_DEPRECATED("Use PK_Verifier") + std::unique_ptr create_verification_op(std::string_view params, + std::string_view provider) const; /** * This is an internal library function exposed on key types. @@ -315,6 +338,22 @@ class BOTAN_PUBLIC_API(2, 0) Private_Key : public virtual Public_Key { */ std::string fingerprint_private(std::string_view alg) const; + /** + * Initiate the creation of a signature creation operation. + * This is a builder-style interface which allows setting various + * options for the operation. + * + * Typical usage: + * + * auto signer = pub_key.signer() + * .with_padding("PSS(SHA-256)") + * .with_der_encoded_signature() + * .create(); + * + * @return a builder object for signing options + */ + PK_Signature_Options_Builder signer() const { return PK_Signature_Options_Builder().with_private_key(*this); } + /** * This is an internal library function exposed on key types. * In all cases applications should use wrappers in pubkey.h @@ -357,12 +396,29 @@ class BOTAN_PUBLIC_API(2, 0) Private_Key : public virtual Public_Key { * @param rng a random number generator. The PK_Op may maintain a * reference to the RNG and use it many times. The rng must outlive * any operations which reference it. + * + * @param options allow controlling behavior + */ + virtual std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const; + + /** + * This is an internal library function exposed on key types. + * In all cases applications should use wrappers in pubkey.h + * + * Return a signature operation for this key/params or throw + * + * @param rng a random number generator. The PK_Op may maintain a + * reference to the RNG and use it many times. The rng must outlive + * any operations which reference it. + * * @param params additional parameters * @param provider the provider to use */ - virtual std::unique_ptr create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const; + BOTAN_DEPRECATED("Use PK_Signer") + std::unique_ptr create_signature_op(RandomNumberGenerator& rng, + std::string_view params, + std::string_view provider) 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 3fa426e7ac7..4119556f11a 100644 --- a/src/lib/pubkey/pk_ops.cpp +++ b/src/lib/pubkey/pk_ops.cpp @@ -87,40 +87,40 @@ secure_vector PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len, namespace { -std::unique_ptr create_signature_hash(std::string_view padding) { - if(auto hash = HashFunction::create(padding)) { - return hash; - } - - SCAN_Name req(padding); +std::unique_ptr validate_options_returning_hash(PK_Signature_Options& options) { + const auto hash = options.hash_function().required(); - if(req.algo_name() == "EMSA1" && req.arg_count() == 1) { - if(auto hash = HashFunction::create(req.arg(0))) { - return hash; - } + /* + * 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(auto prehash = options.prehash().optional(); + prehash.has_value() && prehash->has_value() && prehash->value() != hash) { + throw Invalid_Argument("This algorithm does not support prehashing with a different hash"); } #if defined(BOTAN_HAS_RAW_HASH_FN) - if(req.algo_name() == "Raw") { - if(req.arg_count() == 0) { + if(hash.starts_with("Raw")) { + if(hash == "Raw") { return std::make_unique("Raw", 0); } + 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 - throw Algorithm_Not_Found(padding); + return HashFunction::create_or_throw(hash); } } // namespace -PK_Ops::Signature_with_Hash::Signature_with_Hash(std::string_view hash) : - Signature(), m_hash(create_signature_hash(hash)) {} +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) std::string PK_Ops::Signature_with_Hash::rfc6979_hash_function() const { @@ -141,8 +141,8 @@ std::vector PK_Ops::Signature_with_Hash::sign(RandomNumberGenerator& rn return raw_sign(msg, rng); } -PK_Ops::Verification_with_Hash::Verification_with_Hash(std::string_view padding) : - Verification(), m_hash(create_signature_hash(padding)) {} +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, std::string_view pk_algo, diff --git a/src/lib/pubkey/pk_ops_impl.h b/src/lib/pubkey/pk_ops_impl.h index f136245ecfc..33d59070257 100644 --- a/src/lib/pubkey/pk_ops_impl.h +++ b/src/lib/pubkey/pk_ops_impl.h @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace Botan::PK_Ops { @@ -57,7 +58,7 @@ class Verification_with_Hash : public Verification { std::string hash_function() const final { return m_hash->name(); } protected: - explicit Verification_with_Hash(std::string_view hash); + explicit Verification_with_Hash(PK_Signature_Options& options); explicit Verification_with_Hash(const AlgorithmIdentifier& alg_id, std::string_view pk_algo, @@ -65,10 +66,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; @@ -86,7 +85,7 @@ class Signature_with_Hash : public Signature { ~Signature_with_Hash() override = default; protected: - explicit Signature_with_Hash(std::string_view hash); + explicit Signature_with_Hash(PK_Signature_Options& 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 new file mode 100644 index 00000000000..c066e928ea7 --- /dev/null +++ b/src/lib/pubkey/pk_options.cpp @@ -0,0 +1,239 @@ +/* +* (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 + +namespace Botan { + +namespace { + +template +void retrofit_legacy_parameters(SigOptsBaseT& builder, + const Public_Key& key, + std::string_view params, + Signature_Format format, + 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. + */ + + const auto algo = key.algo_name(); + + builder.with_provider(provider); + + if(key.message_parts() != 1) { + builder.with_der_encoded_signature(format == Signature_Format::DerSequence); + } + + if(algo.starts_with("Dilithium") || algo == "SPHINCS+" || algo == "ML-DSA" || algo == "SLH-DSA") { + if(!params.empty() && params != "Randomized" && params != "Deterministic" && params != "Pure") { + throw Invalid_Argument(fmt("Unexpected parameters for signing with {}", algo)); + } + + if constexpr(std::is_same_v) { + if(params == "Deterministic") { + builder.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()) { + builder.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"}; + } + }(); + + builder.with_hash(hash); + if(hash != "Raw") { + builder.with_context(userid); + } + } + } else if(algo == "Ed25519") { + if(!params.empty() && params != "Identity" && params != "Pure") { + if(params == "Ed25519ph") { + builder.with_prehash(); + } else { + builder.with_prehash(std::string(params)); + } + } + } else if(algo == "Ed448") { + if(!params.empty() && params != "Identity" && params != "Pure" && params != "Ed448") { + if(params == "Ed448ph") { + builder.with_prehash(); + } else { + builder.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) { + builder.with_padding(padding); + } else if(req.arg_count() == 1) { + builder.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") { + builder.with_padding(padding).with_hash(req.arg(0)).with_prehash(req.arg(1)); + } else if(req.arg_count() == 1) { + builder.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") { + builder.with_padding(padding).with_hash(req.arg(0)); + + if(req.arg_count() == 3) { + builder.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"; + + builder.with_padding(padding).with_hash(req.arg(0)); + + if(req.arg_count() == 3) { + if(implicit) { + builder.with_salt_size(req.arg_as_integer(2)); + } else { + builder.with_salt_size(req.arg_as_integer(2)).with_explicit_trailer_field(); + } + } else if(!implicit) { + builder.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 + builder.with_padding(padding); + if(req.arg_count_between(1, 2)) { + builder.with_hash(req.arg(0)); + if(req.arg(1, "exp") != "imp") { + builder.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) { + builder.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); + } + }; + + builder.with_hash(hash()); + } + } +} + +} // namespace + +PK_Signature_Options PK_Signature_Options::from_legacy(const Private_Key& key, + RandomNumberGenerator& rng, + std::string_view params, + Signature_Format format, + std::string_view provider) { + auto builder = PK_Signature_Options_Builder().with_private_key(key).with_rng(rng); + retrofit_legacy_parameters(builder, key, params, format, provider); + return builder.commit(); +} + +PK_Signature_Options PK_Signature_Options::from_legacy(const Public_Key& key, + std::string_view params, + Signature_Format format, + std::string_view provider) { + auto builder = PK_Verification_Options_Builder().with_public_key(key); + retrofit_legacy_parameters(builder, key, params, format, provider); + return builder.commit(); +} + +PK_Signature_Options_Builder& PK_Signature_Options_Builder::with_private_key(const Private_Key& key) & { + with_product_name(key.algo_name()); + set_or_throw(options().private_key, std::cref(key)); + return *this; +} + +PK_Verification_Options_Builder& PK_Verification_Options_Builder::with_public_key(const Public_Key& key) & { + with_product_name(key.algo_name()); + set_or_throw(options().public_key, std::cref(key)); + return *this; +} + +void PK_Signature_Options::validate_for_hash_based_signature(std::optional acceptable_hash) { + if(auto hash = hash_function().optional()) { + if(!acceptable_hash.has_value()) { + throw Invalid_Argument(fmt("This {} key does not support explicit hash function choice", product_name())); + } + + if(hash != acceptable_hash.value()) { + throw Invalid_Argument( + fmt("This {} key can only be used with {}, not {}", product_name(), acceptable_hash.value(), hash.value())); + } + } +} + +} // namespace Botan diff --git a/src/lib/pubkey/pk_options.h b/src/lib/pubkey/pk_options.h new file mode 100644 index 00000000000..9c0e7d1c65c --- /dev/null +++ b/src/lib/pubkey/pk_options.h @@ -0,0 +1,459 @@ +/* +* (C) 2024 Jack Lloyd +* 2024 René Meusel - Rohde & Schwarz Cybersecurity GmbH +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PK_OPTIONS_H_ +#define BOTAN_PK_OPTIONS_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +class Public_Key; +class Private_Key; + +namespace detail { + +struct PK_Signature_Options_Container final { + // NOLINTBEGIN(misc-non-private-member-variables-in-classes) + + Option<"public key", std::reference_wrapper> public_key; + Option<"private key", std::reference_wrapper> private_key; + Option<"random number generator", std::reference_wrapper> rng; + Option<"hash", std::string> hash_fn; + Option<"prehash", std::optional> prehash; + Option<"padding", std::string> padding; + Option<"context", std::vector> context; + Option<"provider", std::string> provider; + Option<"salt size", size_t> salt_size; + Option<"use DER", bool> use_der; + Option<"deterministic", bool> deterministic_sig; + Option<"explicit trailer field", bool> explicit_trailer_field; + + // NOLINTEND(misc-non-private-member-variables-in-classes) + + auto all_options() const { + return std::tie(public_key, + private_key, + rng, + hash_fn, + prehash, + padding, + context, + provider, + salt_size, + use_der, + deterministic_sig, + explicit_trailer_field); + } +}; + +} // namespace detail + +class BOTAN_UNSTABLE_API PK_Signature_Options final : public Options { + public: + using Options::Options; + + public: + [[nodiscard]] auto public_key() { return take(options().public_key); } + + [[nodiscard]] auto private_key() { return take(options().private_key); } + + [[nodiscard]] auto rng() { return take(options().rng); } + + [[nodiscard]] auto hash_function() { return take(options().hash_fn); } + + [[nodiscard]] auto prehash() { return take(options().prehash); } + + [[nodiscard]] auto padding() { return take(options().padding); } + + [[nodiscard]] auto context() { return take(options().context); } + + [[nodiscard]] auto provider() { return take(options().provider); } + + [[nodiscard]] auto salt_size() { return take(options().salt_size); } + + [[nodiscard]] bool using_der_encoded_signature() { return take(options().use_der).or_default(false); } + + [[nodiscard]] bool using_deterministic_signature() { return take(options().deterministic_sig).or_default(false); } + + [[nodiscard]] bool using_explicit_trailer_field() { + return take(options().explicit_trailer_field).or_default(false); + } + + public: + /// It may be acceptable to provide a hash function, for hash-based + /// signatures (like LMS), but it is not required. + /// @throws Invalid_Argument if the provided hash is not acceptable + void validate_for_hash_based_signature(std::optional acceptable_hash = std::nullopt); + + /// 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() { + if(auto p = provider().optional()) { + throw Provider_Not_Found(product_name(), p.value()); + }; + } + + public: + /// Parse the legacy set of parameters that used to be passed to + /// PK_Signer. This should not be used by new code. + /// + /// @param key the private key to use + /// @param rng the rng to use + /// @param params the legacy parameters string + /// @param format the encoding format to use + /// @param provider the provider to use + static PK_Signature_Options from_legacy(const Private_Key& key, + RandomNumberGenerator& rng, + std::string_view params, + Signature_Format format, + std::string_view provider); + + /// Parse the legacy set of parameters that used to be passed to + /// PK_Verifier. This should not be used by new code. + /// + /// @param key the private key to use + /// @param params the legacy parameters string + /// @param format the encoding format to use + /// @param provider the provider to use + static PK_Signature_Options from_legacy(const Public_Key& key, + std::string_view params, + Signature_Format format, + std::string_view provider); +}; + +/** +* Signature generation/verification options +* +* The normal usage of this is in a builder style, eg. +* +* auto signer = private_key.signer() +* .with_hash("SHA-256") +* .with_der_encoded_signature() +* .with_context("Foo") +* .create(); +* +* This is a base class for the common options of 'signing' and 'verification'. +* We use CRTP to track the derived class type, so that the with_xxx functions +* can be chained and return the correct derived type. +* +* To properly handle method chaining even when the builder was transformed into +* an lvalue, we have to provide two overloads for each with_xxx function. The +* first overload is for lvalues and the second overload is for rvalues. +* Typically, the rvalue overload is implemented in terms of the lvalue overload. +* +* TODO: C++23: deducing-this will likely remove the need for CRTP and the +* lvalue/rvalue overloads. It allows the base class to know the +* derived type on which its methods were called. Along with the +* type, each method can also know whether it was called on an +* lvalue or rvalue and handle the return type accordingly. +*/ +template +class PK_Signature_Options_Builder_Base : public OptionsBuilder { + private: + using Self = DerivedT; + + DerivedT& self() { return static_cast(*this); } + + protected: + PK_Signature_Options_Builder_Base() = default; + + public: + /// Specify the hash function to use for signing/verification + /// + /// Most, but not all, schemes require specifying a hash function. + Self& with_hash(std::string_view hash) & { + set_or_throw(options().hash_fn, std::string(hash)); + return self(); + } + + /// Specify the hash function to use for signing/verification + /// + /// Most, but not all, schemes require specifying a hash function. + Self 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. + Self& with_padding(std::string_view padding) & { + set_or_throw(options().padding, std::string(padding)); + return self(); + } + + /// 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. + Self with_padding(std::string_view padding) && { return std::move(with_padding(padding)); } + + /// 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. + Self& with_prehash(std::optional prehash = std::nullopt) & { + set_or_throw(options().prehash, std::move(prehash)); + return self(); + } + + /// 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. + Self with_prehash(std::optional prehash = std::nullopt) && { + return std::move(with_prehash(std::move(prehash))); + } + + /// 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. + Self& with_context(std::span context) & { + set_or_throw(options().context, std::vector(context.begin(), context.end())); + return self(); + } + + /// 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. + Self 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. + Self& with_context(std::string_view context) & { + with_context(std::span{cast_char_ptr_to_uint8(context.data()), context.size()}); + return self(); + } + + /// 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. + Self 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. + Self& with_salt_size(size_t salt_size) & { + set_or_throw(options().salt_size, salt_size); + return self(); + } + + /// 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. + Self with_salt_size(size_t salt_size) && { return std::move(with_salt_size(salt_size)); } + + /// 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. + Self& with_der_encoded_signature(bool der = true) & { + set_or_throw(options().use_der, der); + return self(); + } + + /// 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. + Self with_der_encoded_signature(bool der = true) && { return std::move(with_der_encoded_signature(der)); } + + /// 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. + /// + Self& with_explicit_trailer_field(bool value = true) & { + set_or_throw(options().explicit_trailer_field, value); + return self(); + } + + /// 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. + /// + Self with_explicit_trailer_field(bool value = true) && { return std::move(with_explicit_trailer_field(value)); } + + /// Specify a provider that should be used + /// + /// The default provider used to be referred to as "base". There's no + /// need to specify the default provider explicitly and setting a + /// provider is rarely relevant in general. + Self& with_provider(std::string_view provider) & { + if(!provider.empty() && provider != "base") { + set_or_throw(options().provider, std::string(provider)); + } + return self(); + } + + /// Specify a provider that should be used + /// + /// This is rarely relevant + Self with_provider(std::string_view provider) && { return std::move(with_provider(provider)); } +}; + +class BOTAN_PUBLIC_API(3, 6) PK_Signature_Options_Builder final + : public PK_Signature_Options_Builder_Base { + public: + using Self = PK_Signature_Options_Builder; + + public: + /// Create a PK_Signature_Options_Builder with no options set + /// + /// This can be further parameterized by calling with_xxx functions + PK_Signature_Options_Builder() = default; + + /// Specify the private key to use for verification + Self& with_private_key(const Private_Key& key) &; + + /// Specify the private key to use for verification + Self with_private_key(const Private_Key& key) && { return std::move(with_private_key(key)); } + + /// Specify the random number generator to use + /// + /// If the signature scheme requires randomness, this RNG will be used. + /// Some schemes have a deterministic and a randomized mode, an RNG does + /// not need to be provided in the deterministic case. + Self& with_rng(RandomNumberGenerator& rng) & { + set_or_throw(options().rng, rng); + return *this; + } + + /// Specify the random number generator to use + /// + /// If the signature scheme requires randomness, this RNG will be used. + /// Some schemes have a deterministic and a randomized mode, an RNG does + /// not need to be provided in the deterministic case. + Self with_rng(RandomNumberGenerator& rng) && { return std::move(with_rng(rng)); } + + /// 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 + Self& with_deterministic_signature(bool value = true) & { + set_or_throw(options().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 + Self with_deterministic_signature(bool value = true) && { return std::move(with_deterministic_signature(value)); } + + /// Commit the options and create a PK_Signer object + PK_Signer create() { return PK_Signer(commit()); } +}; + +class BOTAN_PUBLIC_API(3, 6) PK_Verification_Options_Builder final + : public PK_Signature_Options_Builder_Base { + public: + using Self = PK_Verification_Options_Builder; + + public: + /// Create a PK_Verification_Options_Builder with no options set + /// + /// This can be further parameterized by calling with_xxx functions + PK_Verification_Options_Builder() = default; + + /// Specify the public key to use for verification + Self& with_public_key(const Public_Key& key) &; + + /// Specify the public key to use for verification + Self with_public_key(const Public_Key& key) && { return std::move(with_public_key(key)); } + + /// Commit the options and create a PK_Verifier object + PK_Verifier create() { return PK_Verifier(commit()); } +}; + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index a1aa5bb9920..0923e9bc9fa 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -1,5 +1,5 @@ /* -* (C) 1999-2010,2015,2018 Jack Lloyd +* (C) 1999-2010,2015,2018,2024 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -244,21 +243,36 @@ void check_der_format_supported(Signature_Format format, size_t parts) { } // namespace -PK_Signer::PK_Signer(const Private_Key& key, - RandomNumberGenerator& rng, - std::string_view emsa, - Signature_Format format, - std::string_view provider) { - m_op = key.create_signature_op(rng, emsa, provider); +PK_Signer::PK_Signer(PK_Signature_Options options) { + // Appease GCC warning about a possibly dangling reference because when using + // std::reference_wrapper's implicit conversion to the contained reference + // the wrapper does not outlive the reference which seems to confuse GCC. + const auto key_wrapped = options.private_key().required(); + const Private_Key& key = key_wrapped.get(); + + // TODO: The downstream algorithms should decide whether to require an RNG + // (e.g. explicitly 'deterministic' signatures should not require it). + RandomNumberGenerator& rng = options.rng().required(); + + 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())); } - m_sig_format = format; + m_sig_format = options.using_der_encoded_signature() ? Signature_Format::DerSequence : Signature_Format::Standard; m_parts = key.message_parts(); m_part_size = key.message_part_size(); - check_der_format_supported(format, m_parts); + check_der_format_supported(m_sig_format, m_parts); + + options.validate_option_consumption(); } +PK_Signer::PK_Signer(const Private_Key& key, + RandomNumberGenerator& rng, + std::string_view padding, + Signature_Format format, + std::string_view provider) : + PK_Signer(PK_Signature_Options::from_legacy(key, rng, padding, format, provider)) {} + AlgorithmIdentifier PK_Signer::algorithm_identifier() const { return m_op->algorithm_identifier(); } @@ -325,20 +339,30 @@ std::vector PK_Signer::signature(RandomNumberGenerator& rng) { } } -PK_Verifier::PK_Verifier(const Public_Key& key, - std::string_view emsa, - Signature_Format format, - std::string_view provider) { - m_op = key.create_verification_op(emsa, provider); +PK_Verifier::PK_Verifier(PK_Signature_Options options) { + // Appease GCC warning about a possibly dangling reference because when using + // std::reference_wrapper's implicit conversion to the contained reference + // the wrapper does not outlive the reference which seems to confuse GCC. + const auto key_wrapped = options.public_key().required(); + const Public_Key& key = key_wrapped.get(); + m_op = key._create_verification_op(options); if(!m_op) { throw Invalid_Argument(fmt("Key type {} does not support signature verification", key.algo_name())); } - m_sig_format = format; + m_sig_format = options.using_der_encoded_signature() ? Signature_Format::DerSequence : Signature_Format::Standard; m_parts = key.message_parts(); m_part_size = key.message_part_size(); - check_der_format_supported(format, m_parts); + check_der_format_supported(m_sig_format, m_parts); + + options.validate_option_consumption(); } +PK_Verifier::PK_Verifier(const Public_Key& pub_key, + std::string_view padding, + Signature_Format format, + std::string_view provider) : + PK_Verifier(PK_Signature_Options::from_legacy(pub_key, padding, format, provider)) {} + PK_Verifier::PK_Verifier(const Public_Key& key, const AlgorithmIdentifier& signature_algorithm, std::string_view provider) { diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index d7b551a9784..18f270c3bad 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -9,7 +9,7 @@ #define BOTAN_PUBKEY_H_ #include -#include +#include #include #include #include @@ -21,6 +21,11 @@ namespace Botan { class RandomNumberGenerator; +class Public_Key; +class Private_Key; + +class PK_Signature_Options; + /** * Public Key Encryptor * This is the primary interface for public key encryption @@ -154,6 +159,16 @@ class BOTAN_PUBLIC_API(2, 0) PK_Decryptor { */ class BOTAN_PUBLIC_API(2, 0) PK_Signer final { public: + /** + * Construct a PK signer + * + * @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. + */ + explicit PK_Signer(PK_Signature_Options options); + /** * Construct a PK Signer. * @param key the key to use inside this signer @@ -271,6 +286,12 @@ class BOTAN_PUBLIC_API(2, 0) PK_Signer final { */ class BOTAN_PUBLIC_API(2, 0) PK_Verifier final { public: + /** + * Construct a PK Verifier. + * @param options relating to the signature + */ + explicit PK_Verifier(PK_Signature_Options options); + /** * Construct a PK Verifier. * @param pub_key the public key to verify against diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp index ff62ac6d129..a0cbb4f83a4 100644 --- a/src/lib/pubkey/rsa/rsa.cpp +++ b/src/lib/pubkey/rsa/rsa.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #if defined(BOTAN_HAS_THREAD_UTILS) @@ -564,8 +565,10 @@ 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, std::string_view padding, RandomNumberGenerator& rng) : - RSA_Private_Operation(rsa, rng), m_emsa(EMSA::create_or_throw(padding)) {} + 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); + } private: std::unique_ptr m_emsa; @@ -671,8 +674,8 @@ 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, std::string_view padding) : - RSA_Public_Operation(rsa), m_emsa(EMSA::create_or_throw(padding)) {} + RSA_Verify_Operation(const RSA_PublicKey& rsa, PK_Signature_Options& options) : + RSA_Public_Operation(rsa), m_emsa(EMSA::create_or_throw(options)) {} std::string hash_function() const override { return m_emsa->hash_function(); } @@ -729,25 +732,23 @@ std::unique_ptr RSA_PublicKey::create_kem_encryption_op( throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr RSA_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); - } - - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr RSA_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(*this, options); } namespace { -std::string parse_rsa_signature_algorithm(const AlgorithmIdentifier& alg_id) { +PK_Signature_Options parse_rsa_signature_algorithm(const AlgorithmIdentifier& alg_id) { const auto sig_info = split_on(alg_id.oid().to_formatted_string(), '/'); if(sig_info.empty() || sig_info.size() != 2 || sig_info[0] != "RSA") { throw Decoding_Error("Unknown AlgorithmIdentifier for RSA X.509 signatures"); } - std::string padding = sig_info[1]; + const std::string& padding = sig_info[1]; + + PK_Verification_Options_Builder opts; if(padding == "EMSA4") { // "MUST contain RSASSA-PSS-params" @@ -780,10 +781,18 @@ std::string parse_rsa_signature_algorithm(const AlgorithmIdentifier& alg_id) { throw Decoding_Error("Unacceptable trailer field for PSS signatures"); } - padding += fmt("({},MGF1,{})", hash_algo, pss_params.salt_length()); + opts.with_padding("PSS").with_hash(hash_algo).with_salt_size(pss_params.salt_length()); + } else { + SCAN_Name scan(padding); + + if(scan.algo_name() != "EMSA3") { + throw Decoding_Error("Unexpected OID for RSA signatures"); + } + + opts.with_padding("PKCS1v15").with_hash(scan.arg(0)); } - return padding; + return opts.commit(); } } // namespace @@ -791,7 +800,8 @@ std::string parse_rsa_signature_algorithm(const AlgorithmIdentifier& alg_id) { std::unique_ptr RSA_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, std::string_view provider) const { if(provider == "base" || provider.empty()) { - return std::make_unique(*this, parse_rsa_signature_algorithm(alg_id)); + auto opts = parse_rsa_signature_algorithm(alg_id); + return std::make_unique(*this, opts); } throw Provider_Not_Found(algo_name(), provider); @@ -817,14 +827,10 @@ std::unique_ptr RSA_PrivateKey::create_kem_decryption_op throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr RSA_PrivateKey::create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params, rng); - } - - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr RSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + options.exclude_provider(); + 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 3e5fe49bee7..24a59f5e6cb 100644 --- a/src/lib/pubkey/rsa/rsa.h +++ b/src/lib/pubkey/rsa/rsa.h @@ -77,8 +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(std::string_view params, - std::string_view provider) 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,9 +173,8 @@ class BOTAN_PUBLIC_API(2, 0) RSA_PrivateKey final : public Private_Key, std::string_view params, std::string_view provider) const override; - std::unique_ptr create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 50275455ab0..e88452a3bfa 100644 --- a/src/lib/pubkey/sm2/sm2.cpp +++ b/src/lib/pubkey/sm2/sm2.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -66,8 +67,10 @@ SM2_PrivateKey::SM2_PrivateKey(RandomNumberGenerator& rng, EC_Group group, const m_da_inv((this->_private_key() + EC_Scalar::one(domain())).invert()), m_da_inv_legacy(m_da_inv.to_bigint()) {} +namespace { + std::vector sm2_compute_za(HashFunction& hash, - std::string_view user_id, + std::span user_id, const EC_Group& group, const EC_AffinePoint& pubkey) { if(user_id.size() >= 8192) { @@ -91,21 +94,42 @@ std::vector sm2_compute_za(HashFunction& hash, return hash.final>(); } +} // namespace + +std::vector sm2_compute_za(HashFunction& hash, + std::string_view user_id, + const EC_Group& group, + const EC_AffinePoint& pubkey) { + return sm2_compute_za(hash, {cast_char_ptr_to_uint8(user_id.data()), user_id.size()}, group, pubkey); +} + namespace { +// GM/T 0009-2012 specifies this as the default userid +// "1234567812345678"; +const std::vector sm2_default_userid = { + // clang-format off + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + // clang-format on +}; + /** * SM2 signature operation */ class SM2_Signature_Operation final : public PK_Ops::Signature { public: - SM2_Signature_Operation(const SM2_PrivateKey& sm2, std::string_view ident, std::string_view hash) : + 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()) { + const auto hash = options.hash_function().required(); if(hash == "Raw") { // m_hash is null, m_za is empty } else { + auto context = options.context().or_default(sm2_default_userid); + m_hash = HashFunction::create_or_throw(hash); // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA) - m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point()); + m_za = sm2_compute_za(*m_hash, context, m_group, sm2._public_ec_point()); m_hash->update(m_za); } } @@ -162,14 +186,17 @@ 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, std::string_view ident, std::string_view hash) : + SM2_Verification_Operation(const SM2_PublicKey& sm2, PK_Signature_Options& options) : m_group(sm2.domain()), m_gy_mul(sm2._public_ec_point()) { + const auto hash = options.hash_function().required(); if(hash == "Raw") { // m_hash is null, m_za is empty } else { + auto context = options.context().or_default(sm2_default_userid); + m_hash = HashFunction::create_or_throw(hash); // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA) - m_za = sm2_compute_za(*m_hash, ident, m_group, sm2._public_ec_point()); + m_za = sm2_compute_za(*m_hash, context, m_group, sm2._public_ec_point()); m_hash->update(m_za); } } @@ -222,56 +249,22 @@ bool SM2_Verification_Operation::is_valid_signature(std::span sig return false; } -void parse_sm2_param_string(std::string_view params, std::string& userid, std::string& hash) { - // GM/T 0009-2012 specifies this as the default userid - const std::string default_userid = "1234567812345678"; - - // defaults: - userid = default_userid; - hash = "SM3"; - - /* - * SM2 parameters have the following possible formats: - * Ident [since 2.2.0] - * Ident,Hash [since 2.3.0] - */ - - 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); - } -} - } // namespace std::unique_ptr SM2_PublicKey::generate_another(RandomNumberGenerator& rng) const { return std::make_unique(rng, domain()); } -std::unique_ptr SM2_PublicKey::create_verification_op(std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - std::string userid, hash; - parse_sm2_param_string(params, userid, hash); - return std::make_unique(*this, userid, hash); - } - - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr SM2_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(*this, options); } -std::unique_ptr SM2_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - std::string userid, hash; - parse_sm2_param_string(params, userid, hash); - return std::make_unique(*this, userid, hash); - } - - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr SM2_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + 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 cce8474ac80..703d9c6c6d2 100644 --- a/src/lib/pubkey/sm2/sm2.h +++ b/src/lib/pubkey/sm2/sm2.h @@ -55,10 +55,11 @@ class BOTAN_PUBLIC_API(2, 2) SM2_PublicKey : public virtual EC_PublicKey { return (op == PublicKeyOperation::Signature || op == PublicKeyOperation::Encryption); } + bool supports_context_data() const override { return true; } + size_t message_part_size() const override { return domain().get_order_bytes(); } - std::unique_ptr create_verification_op(std::string_view params, - std::string_view provider) 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, @@ -112,9 +113,8 @@ 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, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 3f05a0ae7fe..4002d5910fb 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp @@ -179,13 +179,15 @@ std::unique_ptr SphincsPlus_PublicKey::generate_another(RandomNumbe class SphincsPlus_Verification_Operation final : public PK_Ops::Verification { public: - SphincsPlus_Verification_Operation(std::shared_ptr pub_key) : + SphincsPlus_Verification_Operation(std::shared_ptr pub_key, + PK_Signature_Options& options) : m_public(std::move(pub_key)), m_hashes(Botan::Sphincs_Hash_Functions::create(m_public->parameters(), m_public->seed())), - m_context(/* TODO: Add API */ {}) { + m_context(options.context().optional().value_or(std::vector())) { BOTAN_ARG_CHECK(m_context.size() <= 255, "Context must not exceed 255 bytes"); BOTAN_ARG_CHECK(m_public->parameters().is_available(), "The selected SLH-DSA (or SPHINCS+) instance is not available in this build."); + options.prehash().not_implemented("HashSLH-DSA currently not supported"); } /** @@ -238,23 +240,19 @@ class SphincsPlus_Verification_Operation final : public PK_Ops::Verification { SphincsContext m_context; }; -std::unique_ptr SphincsPlus_PublicKey::create_verification_op(std::string_view /*params*/, - std::string_view provider) const { - if(provider.empty() || provider == "base") { - return std::make_unique(m_public); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr SphincsPlus_PublicKey::_create_verification_op( + PK_Signature_Options& options) const { + options.exclude_provider(); + return std::make_unique(m_public, options); } std::unique_ptr SphincsPlus_PublicKey::create_x509_verification_op( const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const { - if(provider.empty() || provider == "base") { - if(signature_algorithm != this->algorithm_identifier()) { - throw Decoding_Error("Unexpected AlgorithmIdentifier for SLH-DSA (or SPHINCS+) signature"); - } - return std::make_unique(m_public); + if(signature_algorithm != this->algorithm_identifier()) { + throw Decoding_Error("Unexpected AlgorithmIdentifier for SLH-DSA (or SPHINCS+) signature"); } - throw Provider_Not_Found(algo_name(), provider); + auto options = PK_Verification_Options_Builder().with_provider(provider).commit(); + return _create_verification_op(options); } bool SphincsPlus_PublicKey::supports_operation(PublicKeyOperation op) const { @@ -336,15 +334,16 @@ 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) : + PK_Signature_Options& options) : 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_context(/* TODO: add API for context */ {}) { + m_deterministic(options.using_deterministic_signature()), + m_context(options.context().optional().value_or(std::vector())) { BOTAN_ARG_CHECK(m_context.size() <= 255, "Context must not exceed 255 bytes"); BOTAN_ARG_CHECK(m_public->parameters().is_available(), "The selected SLH-DSA (or SPHINCS+) instance is not available in this build."); + options.prehash().not_implemented("HashSLH-DSA currently not supported"); } void update(std::span msg) override { @@ -354,7 +353,7 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { std::vector sign(RandomNumberGenerator& rng) override { std::optional addrnd = std::nullopt; - if(m_randomized) { + if(!m_deterministic) { addrnd = rng.random_vec(m_public->parameters().n()); } auto internal_msg = prepare_message(std::exchange(m_msg_buffer, {}), m_public->parameters(), m_context); @@ -417,25 +416,19 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { std::shared_ptr m_public; std::unique_ptr m_hashes; SphincsInputMessage m_msg_buffer; - bool m_randomized; + bool m_deterministic; SphincsContext m_context; }; -std::unique_ptr SphincsPlus_PrivateKey::create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const { +std::unique_ptr SphincsPlus_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { BOTAN_UNUSED(rng); - BOTAN_ARG_CHECK(params.empty() || params == "Deterministic" || params == "Randomized", - "Unexpected parameters for signing with SLH-DSA (or SPHINCS+)"); + options.exclude_provider(); // FIPS 205, Section 9.2 // The hedged variant is the default and should be used on platforms where // side-channel attacks are a concern. - const bool randomized = (params.empty() || params == "Randomized"); - if(provider.empty() || provider == "base") { - return std::make_unique(m_private, m_public, randomized); - } - throw Provider_Not_Found(algo_name(), provider); + return std::make_unique(m_private, m_public, options); } } // namespace Botan diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h index 906c6491063..069d318a5f2 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h @@ -49,8 +49,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(std::string_view params, - std::string_view provider) 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; @@ -102,9 +101,8 @@ class BOTAN_PUBLIC_API(3, 1) SphincsPlus_PrivateKey final : public virtual Sphin secure_vector raw_private_key_bits() const override; std::unique_ptr public_key() const override; - std::unique_ptr create_signature_op(RandomNumberGenerator& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 69b3849f845..7444756e7da 100644 --- a/src/lib/pubkey/xmss/xmss.h +++ b/src/lib/pubkey/xmss/xmss.h @@ -104,8 +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(std::string_view params, - std::string_view provider) 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,9 +231,8 @@ 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&, - std::string_view, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + 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 6cd572e19be..6b0059a29e2 100644 --- a/src/lib/pubkey/xmss/xmss_privatekey.cpp +++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -429,14 +430,12 @@ std::unique_ptr XMSS_PrivateKey::public_key() const { return std::make_unique(xmss_parameters().oid(), root(), public_seed()); } -std::unique_ptr XMSS_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view /*params*/, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this); - } - - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr XMSS_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + options.exclude_provider(); + options.validate_for_hash_based_signature(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 4a976b7d44e..377b1175c70 100644 --- a/src/lib/pubkey/xmss/xmss_publickey.cpp +++ b/src/lib/pubkey/xmss/xmss_publickey.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -100,12 +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(std::string_view /*params*/, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this); - } - throw Provider_Not_Found(algo_name(), provider); +std::unique_ptr XMSS_PublicKey::_create_verification_op(PK_Signature_Options& options) const { + options.exclude_provider(); + options.validate_for_hash_based_signature(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/tls/tls13_pqc/kex_to_kem_adapter.h b/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h index 9c57f24d343..4422431c0f7 100644 --- a/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h +++ b/src/lib/tls/tls13_pqc/kex_to_kem_adapter.h @@ -11,7 +11,7 @@ #ifndef BOTAN_TLS_13_KEX_TO_KEM_ADAPTER_H_ #define BOTAN_TLS_13_KEX_TO_KEM_ADAPTER_H_ -#include +#include #include diff --git a/src/lib/utils/info.txt b/src/lib/utils/info.txt index f62ce43fc82..3d9d5f5a948 100644 --- a/src/lib/utils/info.txt +++ b/src/lib/utils/info.txt @@ -19,6 +19,8 @@ database.h exceptn.h mem_ops.h mutex.h +options_builder.h +template_utils.h types.h strong_type.h version.h diff --git a/src/lib/utils/options_builder.cpp b/src/lib/utils/options_builder.cpp new file mode 100644 index 00000000000..4fe3e472905 --- /dev/null +++ b/src/lib/utils/options_builder.cpp @@ -0,0 +1,63 @@ +/* +* (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 + +#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 +} + +std::string to_string(std::span value) { + constexpr size_t max_printable_length = 64; + if(value.empty()) { + return "empty"; + } + + auto str = hex_encode(value.first(std::min(value.size(), max_printable_length))); + if(value.size() > max_printable_length) { + str += Botan::fmt("... ({} more bytes)", value.size() - max_printable_length); + } + + return str; +} + +} // namespace Botan::detail::BuilderOptionHelper diff --git a/src/lib/utils/options_builder.h b/src/lib/utils/options_builder.h new file mode 100644 index 00000000000..e735542aea1 --- /dev/null +++ b/src/lib/utils/options_builder.h @@ -0,0 +1,404 @@ +/* +* (C) 2024 Jack Lloyd +* (C) 2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_OPTIONS_BUILDER_H_ +#define BOTAN_OPTIONS_BUILDER_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +class HashFunction; +class MessageAuthenticationCode; + +template +class OptionsBuilder; + +template +class Options; + +template +class Option; + +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 BOTAN_UNSTABLE_API to_string(const std::unique_ptr& value); +std::string BOTAN_UNSTABLE_API to_string(const std::unique_ptr& value); +std::string BOTAN_UNSTABLE_API to_string(std::span value); + +template +std::string to_string(const std::vector& value) { + // This is needed to bind std::vector-style buffers to the span + // instead of to the catch-all template at the very top. + return to_string(std::span{value}); +} + +template +std::string to_string(const std::array& value) { + // This is needed to bind std::array-style buffers to the span + // instead of to the catch-all template at the very top. + return to_string(std::span{value}); +} + +} // namespace BuilderOptionHelper + +/** + * Return wrapper of an option value that allows for different ways to consume + * the value. Downstream code can choose to either require the option to be set + * or to handle it as an optional value. + */ +template +class OptionValue { + private: + std::optional take() { return std::exchange(m_value, {}); } + + public: + OptionValue(std::optional option, std::string_view option_name, std::string_view product_name) : + m_value(std::move(option)), m_option_name(option_name), m_product_name(product_name) {} + + /** + * @returns the option value or std::nullopt if it wasn't set. + */ + [[nodiscard]] std::optional optional() && { return take(); } + + /** + * @throws Invalid_Argument if the option wasn't set. + * @returns the option value or throws if it wasn't set. + */ + [[nodiscard]] T required() && { + auto value = take(); + if(!value.has_value()) { + throw Invalid_Argument("'" + m_product_name + "' requires the '" + std::string(m_option_name) + "' option"); + } + return std::move(value).value(); + } + + /** + * @returns the option value or the given @p default_value if it wasn't set. + */ + template U> + [[nodiscard]] T or_default(U&& default_value) && { + return take().value_or(std::forward(default_value)); + } + + /** + * Consumes the option value and throws if it was set. + * @throws Not_Implemented if the option was set, with given @p message. + */ + void not_implemented(std::string_view message) && { + if(take().has_value()) { + throw Not_Implemented("'" + m_product_name + "' currently does not implement the '" + + std::string(m_option_name) + "' option: " + std::string(message)); + } + } + + private: + std::optional m_value; + std::string_view m_option_name; + std::string m_product_name; +}; + +/// 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 +concept AllBuilderOptionsTuple = + requires(const T t) { std::apply([](const OptionTs&...) {}, t); }; + +template +concept OptionsContainer = std::is_default_constructible_v && std::is_move_assignable_v && + std::is_move_constructible_v && requires(const T opts) { + { opts.all_options() } -> AllBuilderOptionsTuple; + }; + +template +concept ConcreteOptions = + OptionsContainer && + requires(typename T::Container container, std::string_view name) { T(std::move(container), name); }; + +static constexpr auto unknown_product = "Unknown"; + +} // namespace detail + +/** + * Wraps a builder option value and provides a way to convert it to a string + * for debugging and error messages. + */ +template +class Option { + public: + using value_type = T; + + std::string_view name() const { return option_name.value; } + + private: + // Only the OptionsBuilder and Options base classes should be able to + // access the internals of any Option instance. + + template + friend class Botan::OptionsBuilder; + + template + friend class Options; + + private: + std::string to_string() const { + if(!m_value.has_value()) { + return ""; + } else { + return detail::BuilderOptionHelper::to_string(*m_value); + } + } + + bool has_value() const noexcept { return m_value.has_value(); } + + template U> + void set(U&& value) { + BOTAN_DEBUG_ASSERT(!m_value.has_value()); + m_value.emplace(std::forward(value)); + } + + std::optional take() { return std::exchange(m_value, {}); } + + private: + std::optional m_value; +}; + +/** + * Base class for all options builder helper classes. + * + * Concrete implementations of builders should derive from this class and + * pass their concrete implementation of the options consumer as the template, + * parameter. All available options must be wrapped in a default-constructible + * struct of `Option<>` instances that implements the `all_options` method. + * + * See the example at the end of this file for a full picture. + */ +template +class OptionsBuilder { + public: + static_assert(detail::ConcreteOptions); + using Container = typename OptionsT::Container; + + public: + OptionsT commit() { + return OptionsT(std::exchange(m_options, {}), std::exchange(m_product_name, detail::unknown_product)); + } + + protected: + Container& options() { return m_options; } + + template ValueT> + void set_or_throw(OptionT& option, ValueT&& value) { + if(option.has_value()) { + throw Invalid_State("'" + m_product_name + "' already set the '" + std::string(option.name()) + "' option"); + } + option.set(std::forward(value)); + } + + void with_product_name(std::string name) { m_product_name = std::move(name); } + + private: + Container m_options; + std::string m_product_name = detail::unknown_product; +}; + +/** + * Base class for all options consumer classes. + * + * Concrete implementations of options consumers should derive from this class + * and pass their concrete implementation of the options container as the + * template parameter. The options container must implement the `all_options` + * method that returns a tuple of all available options. + */ +template +class Options { + public: + static_assert(detail::OptionsContainer); + using Container = OptionsContainerT; + + public: + Options() = default; + + Options(Container options, std::string_view product_name) : + m_options(std::move(options)), m_product_name(product_name) {} + + [[nodiscard]] std::string to_string() const { + std::ostringstream oss; + foreach_option([&](const detail::BuilderOption auto& option) { + oss << option.name() << ": " << option.to_string() << '\n'; + }); + return oss.str(); + } + + void validate_option_consumption() { + std::vector disdained_options; + foreach_option([&](const detail::BuilderOption auto& option) { + if(option.has_value()) { + disdained_options.emplace_back(option.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 the options " + join(disdained_options)); + } + } + + protected: + Container& options() { return m_options; } + + std::string_view product_name() const { return m_product_name; } + + [[nodiscard]] auto take(detail::BuilderOption auto& o) { + return detail::OptionValue(o.take(), o.name(), m_product_name); + } + + template + void foreach_option(FnT&& fn) const { + std::apply([&](const OptionTs&... options) { (fn(options), ...); }, + m_options.all_options()); + } + + private: + Container m_options; + std::string m_product_name = detail::unknown_product; +}; + +} // namespace Botan + +/** + * Below is an example that sets up options for a hypothetical KDF. + * Note that the `with_*` methods are overloaded for lvalue and rvalue refs, to + * allow for properly chaining the calls. + * + * struct KDF_OptionsContainer { + * Option<"context", std::string> context; + * Option<"label", std::string> label; + * Option<"hash", std::unique_ptr> hash; + * + * auto all_options() { return std::tie(context, label, hash); } + * }; + * + * class KDF_Options : public Options { + * public: + * using Options::Options; + * + * public: + * /// Gets the context value or std::nullopt if it wasn't set + * [[nodiscard]] auto context() { return take(options().context); } + * + * /// Gets the label value or a default if it wasn't set + * [[nodiscard]] auto label() { return take(options().label); } + * + * /// Gets the hash function or throws if it wasn't set + * [[nodiscard]] auto hash() { return take(options().hash); } + * }; + * + * // 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 OptionsBuilder { + * public: + * KDF_Builder& with_context(std::string_view ctx) & { + * set_or_throw(options().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(options().label, 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(options().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(options().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() { + * with_product_name("KDF"); + * auto opts = this->commit(); + * KDF kdf(opts); + * opts.validate_option_consumption(); + * return kdf; + * } + * }; + */ + +#endif diff --git a/src/lib/utils/stl_util.h b/src/lib/utils/stl_util.h index cf0d31e66b3..03955092bb3 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 final { - 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..700f5c09e93 --- /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 final { + 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 diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp index 7b03877c5eb..6eb43d57df8 100644 --- a/src/lib/x509/ocsp.cpp +++ b/src/lib/x509/ocsp.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp index 8068dc8a40c..fed9fa912ba 100644 --- a/src/lib/x509/x509_obj.cpp +++ b/src/lib/x509/x509_obj.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include @@ -111,7 +111,7 @@ std::pair X509_Object::verify_signature(co } } catch(Decoding_Error&) { return std::make_pair(Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS, ""); - } catch(Algorithm_Not_Found&) { + } catch(Lookup_Error&) { return std::make_pair(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN, ""); } catch(...) { // This shouldn't happen, fallback to generic signature error diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py index 2312e5cee9c..47949f02b4b 100755 --- a/src/scripts/test_cli.py +++ b/src/scripts/test_cli.py @@ -585,13 +585,13 @@ def cli_xmss_sign_tests(tmp_dir): "6F:C4:08:CB:C3:61:CC:49:8A:25:90:3B:2F:D4:4D:B8:7F:2F:27:06:8C:8F:01:E0:01:DB:42:1F:B4:09:09:D9") # verify the key is updated after each signature: - test_cli("sign", [priv_key, msg, "--output=%s" % (sig1)], "") - test_cli("verify", [pub_key, msg, sig1], "Signature is valid") + test_cli("sign", [priv_key, "--hash=SHA-512", msg, "--output=%s" % (sig1)], "") + test_cli("verify", [pub_key, "--hash=SHA-512", msg, sig1], "Signature is valid") test_cli("hash", ["--no-fsname", sig1], "9DEBA79CE9FDC4966D7BA7B05ABEC54E3C11BB1C2C2732F7658820F2CAE47646") test_cli("hash", ["--no-fsname", priv_key], "A71507087530C85E9CF971CF3A305890B07B51519C405A2B3D0037C64D5802B1") - test_cli("sign", [priv_key, msg, "--output=%s" % (sig2)], "") - test_cli("verify", [pub_key, msg, sig2], "Signature is valid") + test_cli("sign", [priv_key, "--hash=SHA-512", msg, "--output=%s" % (sig2)], "") + test_cli("verify", [pub_key, "--hash=SHA-512", msg, sig2], "Signature is valid") test_cli("hash", ["--no-fsname", sig2], "803EC5D6BECDFB9DC676EE2EDFEFE3D71EE924343A2ED9D2D7BFF0A9D97D704E") test_cli("hash", ["--no-fsname", priv_key], "D581F5BFDA65669A825165C7A9CF17D6D5C5DF349004BCB7416DCD1A5C0349A0") diff --git a/src/tests/data/pubkey/api_sign.vec b/src/tests/data/pubkey/api_sign.vec index cd3d1f22acc..ecf07714ad1 100644 --- a/src/tests/data/pubkey/api_sign.vec +++ b/src/tests/data/pubkey/api_sign.vec @@ -62,4 +62,4 @@ SigParams = [XMSS] AlgoParams = XMSS-SHA2_10_256 -SigParams = SHA2_10_256 +SigParams = SHA-256 diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index 94e277f1f6d..6795b835ed2 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -63,7 +63,7 @@ class Dilithium_KAT_Tests : public Text_Based_Test { } Botan::Dilithium_PublicKey pub_key(priv_key.public_key_bits(), DerivedT::mode); - auto verifier = Botan::PK_Verifier(pub_key, ""); + auto verifier = pub_key.signature_verifier().create(); verifier.update(ref_msg.data(), ref_msg.size()); result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); @@ -145,7 +145,7 @@ class DilithiumRoundtripTests final : public Test { }; auto verify = [](const auto& public_key, const auto& msg, const auto& signature) { - auto verifier = Botan::PK_Verifier(public_key, ""); + auto verifier = public_key.signature_verifier().create(); verifier.update(msg); return verifier.check_signature(signature); }; diff --git a/src/tests/test_ecdsa.cpp b/src/tests/test_ecdsa.cpp index 04073fe85a1..a4bbf10b127 100644 --- a/src/tests/test_ecdsa.cpp +++ b/src/tests/test_ecdsa.cpp @@ -255,8 +255,8 @@ class ECDSA_AllGroups_Test : public Test { } try { - Botan::PK_Signer signer(priv, rng(), hash); - Botan::PK_Verifier verifier(*pub, hash); + auto signer = priv.signer().with_rng(rng()).with_hash(hash).create(); + auto verifier = pub->signature_verifier().with_hash(hash).create(); for(size_t i = 0; i != 16; ++i) { auto message = rng().random_vec(rng().next_byte()); diff --git a/src/tests/test_ed25519.cpp b/src/tests/test_ed25519.cpp index bc48d4eb68d..1aa4d94485f 100644 --- a/src/tests/test_ed25519.cpp +++ b/src/tests/test_ed25519.cpp @@ -90,11 +90,11 @@ class Ed25519_Curdle_Format_Tests final : public Test { auto pub_key = Botan::X509::load_key(pub_data); result.confirm("Public key loaded", pub_key != nullptr); - Botan::PK_Signer signer(*priv_key, this->rng(), "Pure"); + auto signer = priv_key->signer().with_rng(this->rng()).create(); signer.update("message"); std::vector sig = signer.signature(this->rng()); - Botan::PK_Verifier verifier(*pub_key, "Pure"); + auto verifier = pub_key->signature_verifier().create(); verifier.update("message"); result.confirm("Signature valid", verifier.check_signature(sig)); diff --git a/src/tests/test_hss_lms.cpp b/src/tests/test_hss_lms.cpp index 6c43c1d7a91..e7f8ee1f175 100644 --- a/src/tests/test_hss_lms.cpp +++ b/src/tests/test_hss_lms.cpp @@ -127,8 +127,8 @@ class HSS_LMS_Negative_Tests final : public Test { auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)"); - Botan::PK_Signer signer(*sk, Test::rng(), ""); - Botan::PK_Verifier verifier(*sk, ""); + auto signer = sk->signer().with_rng(Test::rng()).create(); + auto verifier = sk->signature_verifier().create(); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; @@ -154,8 +154,8 @@ class HSS_LMS_Negative_Tests final : public Test { auto sk = Botan::create_private_key("HSS-LMS", Test::rng(), "Truncated(SHA-256,192),HW(5,8)"); - Botan::PK_Signer signer(*sk, Test::rng(), ""); - Botan::PK_Verifier verifier(*sk, ""); + auto signer = sk->signer().with_rng(Test::rng()).create(); + auto verifier = sk->signature_verifier().create(); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; @@ -240,7 +240,7 @@ class HSS_LMS_Statefulness_Test final : public Test { Test::Result result("HSS-LMS"); auto sk = Botan::HSS_LMS_PrivateKey(Test::rng(), "Truncated(SHA-256,192),HW(5,8),HW(5,8)"); - Botan::PK_Signer signer(sk, Test::rng(), ""); + auto signer = sk.signer().with_rng(Test::rng()).create(); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; auto sk_bytes_begin = sk.private_key_bits(); @@ -275,7 +275,7 @@ class HSS_LMS_Statefulness_Test final : public Test { uint64_t total_sig_count = 32; auto sk = create_private_key_with_idx(total_sig_count - 1); - Botan::PK_Signer signer(sk, Test::rng(), ""); + auto signer = sk.signer().with_rng(Test::rng()).create(); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; auto sk_bytes_begin = sk.private_key_bits(); @@ -306,14 +306,14 @@ class HSS_LMS_Missing_API_Test final : public Test { 3 * sizeof(uint32_t) + Botan::LMS_IDENTIFIER_LEN); // HSS_LMS_Verification_Operation::hash_function() - Botan::PK_Verifier verifier(*sk, ""); + auto verifier = sk->signature_verifier().create(); result.test_eq("PK_Verifier should report the hash of the key", verifier.hash_function(), "SHA-256"); // HSS_LMS_PrivateKey::raw_private_key_bits() result.test_eq("Our BER and raw encoding is the same", sk->raw_private_key_bits(), sk->private_key_bits()); // HSS_LMS_Signature_Operation::algorithm_identifier() - Botan::PK_Signer signer(*sk, Test::rng(), ""); + auto signer = sk->signer().with_rng(Test::rng()).create(); result.test_is_eq(signer.algorithm_identifier(), sk->algorithm_identifier()); // HSS_LMS_Signature_Operation::hash_function() diff --git a/src/tests/test_options_builder.cpp b/src/tests/test_options_builder.cpp new file mode 100644 index 00000000000..76ca873fc93 --- /dev/null +++ b/src/tests/test_options_builder.cpp @@ -0,0 +1,316 @@ +/* +* (C) 2024 Jack Lloyd +* 2024 René Meusel - Rohde & Schwarz Cybersecurity +* +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" + +#include + +#include + +namespace Botan_Tests { + +namespace { + +struct Void {}; + +struct TestOptionsContainer { + // NOLINTBEGIN(misc-non-private-member-variables-in-classes) + + Botan::Option<"string", std::string> test_string; + Botan::Option<"pointer", std::unique_ptr> test_unique_ptr; + Botan::Option<"buffer", std::vector> test_buffer; + Botan::Option<"array", std::array> test_array; + Botan::Option<"bool", bool> test_bool; + + // NOLINTEND(misc-non-private-member-variables-in-classes) + + auto all_options() const { return std::tie(test_string, test_unique_ptr, test_buffer, test_array, test_bool); } +}; + +class TestOptions final : public Botan::Options { + public: + using Options::Options; + + public: + [[nodiscard]] auto get_string() { return take(options().test_string); } + + [[nodiscard]] auto get_unique_ptr() { return take(options().test_unique_ptr); } + + [[nodiscard]] auto get_buffer() { return take(options().test_buffer); } + + [[nodiscard]] auto get_array() { return take(options().test_array); } + + [[nodiscard]] auto get_bool() { return take(options().test_bool); } +}; + +class TestOptionsBuilder final : public Botan::OptionsBuilder { + public: + using OptionsBuilder::OptionsBuilder; + + public: + TestOptionsBuilder& with_string(std::string_view value) & { + set_or_throw(options().test_string, std::string(value)); + return *this; + } + + TestOptionsBuilder with_string(std::string_view value) && { return std::move(with_string(value)); } + + TestOptionsBuilder& with_unique_ptr(std::unique_ptr value) & { + set_or_throw(options().test_unique_ptr, std::move(value)); + return *this; + } + + TestOptionsBuilder with_unique_ptr(std::unique_ptr value) && { + return std::move(with_unique_ptr(std::move(value))); + } + + TestOptionsBuilder& with_buffer(std::vector value) & { + set_or_throw(options().test_buffer, std::move(value)); + return *this; + } + + TestOptionsBuilder with_buffer(std::vector value) && { return std::move(with_buffer(std::move(value))); } + + TestOptionsBuilder& with_array(std::array value) & { + set_or_throw(options().test_array, value); + return *this; + } + + TestOptionsBuilder with_array(std::array value) && { return std::move(with_array(value)); } + + TestOptionsBuilder& with_bool(bool value) & { + set_or_throw(options().test_bool, value); + return *this; + } + + TestOptionsBuilder with_bool(bool value) && { return std::move(with_bool(value)); } +}; + +size_t occurences(std::string_view haystack, std::string_view needle) { + size_t count = 0; + size_t pos = 0; + while((pos = haystack.find(needle, pos)) != std::string::npos) { + ++count; + pos += needle.size(); + } + return count; +} + +TestOptions all_set() { + return TestOptionsBuilder() + .with_string("hello") + .with_unique_ptr(std::make_unique()) + .with_buffer(std::vector{0, 1, 2}) + .with_array(std::array{7, 6, 5, 4, 3, 2, 1, 0}) + .with_bool(true) + .commit(); +}; + +std::vector test_default_options_builder() { + return { + CHECK("default builder creates empty options", + [](Test::Result& result) { + TestOptions options = TestOptionsBuilder().commit(); + result.confirm("string is empty", !options.get_string().optional().has_value()); + result.confirm("pointer is empty", !options.get_unique_ptr().optional().has_value()); + result.confirm("buffer is empty", !options.get_buffer().optional().has_value()); + result.confirm("array is empty", !options.get_array().optional().has_value()); + result.confirm("bool is empty", !options.get_bool().optional().has_value()); + }), + + CHECK("default builder does not contain anything", + [](Test::Result& result) { + TestOptions options = TestOptionsBuilder().commit(); + result.test_throws("string throws", + [&] { std::ignore = options.get_string().required(); }); + result.test_throws("pointer throws", + [&] { std::ignore = options.get_unique_ptr().required(); }); + result.test_throws("buffer throws", + [&] { std::ignore = options.get_buffer().required(); }); + result.test_throws("array throws", + [&] { std::ignore = options.get_array().required(); }); + result.test_throws("bool throws", + [&] { std::ignore = options.get_bool().required(); }); + }), + + CHECK("default builder passes on defaults", + [](Test::Result& result) { + TestOptions options = TestOptionsBuilder().commit(); + result.test_eq("string default", options.get_string().or_default("default"), "default"); + result.test_not_null("pointer default", options.get_unique_ptr().or_default(std::make_unique())); + result.test_eq("buffer default", + options.get_buffer().or_default(std::vector{0, 1, 2}), + std::vector{0, 1, 2}); + result.test_is_eq("array default", + options.get_array().or_default(std::array{0, 2, 4, 6, 8, 10, 12, 14}), + std::array{0, 2, 4, 6, 8, 10, 12, 14}); + result.test_eq("bool default", options.get_bool().or_default(true), true); + }), + + CHECK("default builder to_string", + [](Test::Result& result) { + TestOptions options = TestOptionsBuilder().commit(); + result.test_eq("5x ", occurences(options.to_string(), ""), 5); + result.test_eq("1x string", occurences(options.to_string(), "string"), 1); + result.test_eq("1x pointer", occurences(options.to_string(), "pointer"), 1); + result.test_eq("1x buffer", occurences(options.to_string(), "buffer"), 1); + result.test_eq("1x array", occurences(options.to_string(), "array"), 1); + result.test_eq("1x bool", occurences(options.to_string(), "bool"), 1); + }), + + CHECK("default builder is fully consumed", + [](Test::Result& result) { + TestOptions options = TestOptionsBuilder().commit(); + result.test_no_throw("consumption validation is successful", + [&] { options.validate_option_consumption(); }); + }), + }; +} + +std::vector test_validation_of_options() { + return { + CHECK( + "builder with all options set", + [&](Test::Result& result) { + TestOptions options = all_set(); + + result.test_throws("not fully consumed", + [&] { options.validate_option_consumption(); }); + + result.test_eq("string", options.get_string().required(), "hello"); + result.test_not_null("pointer", options.get_unique_ptr().required()); + result.test_eq("buffer", options.get_buffer().required(), std::vector{0, 1, 2}); + result.test_is_eq("array", options.get_array().required(), std::array{7, 6, 5, 4, 3, 2, 1, 0}); + result.test_eq("bool", options.get_bool().required(), true); + + result.test_no_throw("fully consumed", [&] { options.validate_option_consumption(); }); + }), + + CHECK("each option can be taken only once", + [&](Test::Result& result) { + TestOptions options = all_set(); + std::ignore = options.get_string().required(); + std::ignore = options.get_unique_ptr().required(); + std::ignore = options.get_buffer().required(); + std::ignore = options.get_array().required(); + std::ignore = options.get_bool().required(); + + result.test_no_throw("fully consumed", [&] { options.validate_option_consumption(); }); + + result.test_throws("string throws", + [&] { std::ignore = options.get_string().required(); }); + result.test_throws("pointer throws", + [&] { std::ignore = options.get_unique_ptr().required(); }); + result.test_throws("buffer throws", + [&] { std::ignore = options.get_buffer().required(); }); + result.test_throws("array throws", + [&] { std::ignore = options.get_array().required(); }); + result.test_throws("bool throws", + [&] { std::ignore = options.get_bool().required(); }); + }), + + CHECK("builder with all options set ignores all defaults", + [&](Test::Result& result) { + TestOptions options = all_set(); + result.test_eq("string", options.get_string().or_default("default"), "hello"); + result.test_not_null("pointer", options.get_unique_ptr().or_default(nullptr)); + result.test_eq("buffer", + options.get_buffer().or_default(std::vector{0, 8, 15}), + std::vector{0, 1, 2}); + result.test_is_eq("array", + options.get_array().or_default(std::array{0, 2, 4, 6, 8, 10, 12, 14}), + std::array{7, 6, 5, 4, 3, 2, 1, 0}); + result.test_eq("bool", options.get_bool().or_default(false), true); + + result.test_no_throw("fully consumed", [&] { options.validate_option_consumption(); }); + }), + + CHECK("builder with all options set throws for not_implemented", + [&](Test::Result& result) { + TestOptions options = all_set(); + result.test_throws("string throws", + [&] { options.get_string().not_implemented("reason"); }); + result.test_throws("pointer throws", + [&] { options.get_unique_ptr().not_implemented("reason"); }); + result.test_throws("buffer throws", + [&] { options.get_buffer().not_implemented("reason"); }); + result.test_throws("array throws", + [&] { options.get_array().not_implemented("reason"); }); + result.test_throws("bool throws", + [&] { options.get_bool().not_implemented("reason"); }); + + result.test_no_throw("fully consumed", [&] { options.validate_option_consumption(); }); + }), + + CHECK("consumption validation lists unused options", + [&](Test::Result& result) { + TestOptions options = all_set(); + + const auto error_message = [&]() -> std::string { + try { + options.validate_option_consumption(); + } catch(const Botan::Invalid_Argument& e) { + return e.what(); + } + + return "nothing thrown"; + }(); + + result.test_eq("string mentioned", occurences(error_message, "string"), 1); + result.test_eq("pointer mentioned", occurences(error_message, "pointer"), 1); + result.test_eq("buffer mentioned", occurences(error_message, "buffer"), 1); + result.test_eq("array mentioned", occurences(error_message, "array"), 1); + result.test_eq("bool mentioned", occurences(error_message, "bool"), 1); + }), + }; +} + +std::vector test_stringification_of_options() { + return { + CHECK("builder with all options set to_string", + [&](Test::Result& result) { + TestOptions options = all_set(); + result.test_eq("0x ", occurences(options.to_string(), ""), 0); + + result.test_eq("1x string", occurences(options.to_string(), "string"), 1); + result.test_eq("1x hello", occurences(options.to_string(), "hello"), 1); + + result.test_eq("1x pointer", occurences(options.to_string(), "pointer"), 1); + result.test_eq("1x unique_ptr", occurences(options.to_string(), "unique_ptr"), 1); + + result.test_eq("1x buffer", occurences(options.to_string(), "buffer"), 1); + result.test_eq("1x 000102", occurences(options.to_string(), "000102"), 1); + + result.test_eq("1x array", occurences(options.to_string(), "array"), 1); + result.test_eq("1x 0706050403020100", occurences(options.to_string(), "0706050403020100"), 1); + + result.test_eq("1x bool", occurences(options.to_string(), "bool"), 1); + result.test_eq("1x true", occurences(options.to_string(), "true"), 1); + + result.test_throws("to_string does not consume anything", + [&] { options.validate_option_consumption(); }); + }), + + CHECK("long buffers are truncated", + [](Test::Result& result) { + auto options = TestOptionsBuilder() + .with_buffer(Test::new_rng(__func__)->random_vec>(1024)) + .commit(); + result.test_eq("...", occurences(options.to_string(), "..."), 1); + result.test_eq("960 more bytes", occurences(options.to_string(), "960 more bytes"), 1); + }), + }; +}; + +} // namespace + +BOTAN_REGISTER_TEST_FN("utils", "default_options_builder", test_default_options_builder); +BOTAN_REGISTER_TEST_FN("utils", "validation_of_options", test_validation_of_options); +BOTAN_REGISTER_TEST_FN("utils", "options_to_string", test_stringification_of_options); + +} // namespace Botan_Tests diff --git a/src/tests/test_pk_pad.cpp b/src/tests/test_pk_pad.cpp index 3939ceb06dc..fb9bcf059a5 100644 --- a/src/tests/test_pk_pad.cpp +++ b/src/tests/test_pk_pad.cpp @@ -64,83 +64,4 @@ class EME_PKCS1v15_Decoding_Tests final : public Text_Based_Test { BOTAN_REGISTER_TEST("pubkey", "eme_pkcs1v15", EME_PKCS1v15_Decoding_Tests); #endif -#if defined(BOTAN_HAS_PK_PADDING) -class EMSA_unit_tests final : public Test { - public: - std::vector run() override { - Test::Result name_tests("EMSA_name_tests"); - - std::vector pads_need_hash = { - #if BOTAN_HAS_EMSA_X931 - "EMSA2", - #endif - #if BOTAN_HAS_EMSA_PKCS1 - "EMSA3", - #endif - #if BOTAN_HAS_EMSA_PSSR - "EMSA4", - "PSSR_Raw", - #endif - #if BOTAN_HAS_ISO_9796 - "ISO_9796_DS2", - "ISO_9796_DS3", - #endif - }; - - std::vector pads_no_hash = { - #if BOTAN_HAS_EMSA_RAW - "Raw", - #endif - #if BOTAN_HAS_EMSA_PKCS1 - "EMSA3(Raw)", - "EMSA3(Raw,SHA-512)", - #endif - }; - - for(const auto& pad : pads_need_hash) { - try { - const std::string hash_to_use = "SHA-256"; - auto emsa_1 = Botan::EMSA::create(Botan::fmt("{}({})", pad, hash_to_use)); - auto emsa_2 = Botan::EMSA::create(emsa_1->name()); - name_tests.test_eq("EMSA_name_test for " + pad, emsa_1->name(), emsa_2->name()); - } catch(Botan::Lookup_Error&) { - name_tests.test_note("Skipping test due to missing hash"); - } catch(const std::exception& e) { - name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what()); - } - } - - for(const auto& pad : pads_need_hash) { - std::string algo_name = pad + "(YYZ)"; - try { - auto emsa = Botan::EMSA::create_or_throw(algo_name); - name_tests.test_failure("EMSA_name_test for " + pad + ": " + "Could create EMSA with fantasy hash YYZ"); - } catch(Botan::Lookup_Error&) { - name_tests.test_note("Skipping test due to missing hash"); - } catch(const std::exception& e) { - name_tests.test_eq( - "EMSA_name_test for " + pad, e.what(), "Could not find any algorithm named \"" + algo_name + "\""); - } - } - - for(const auto& pad : pads_no_hash) { - try { - auto emsa_1 = Botan::EMSA::create(pad); - auto emsa_2 = Botan::EMSA::create(emsa_1->name()); - name_tests.test_eq("EMSA_name_test for " + pad, emsa_1->name(), emsa_2->name()); - } catch(Botan::Lookup_Error&) { - name_tests.test_note("Skipping test due to missing hash"); - } catch(const std::exception& e) { - name_tests.test_failure("EMSA_name_test for " + pad + ": " + e.what()); - } - } - - return {name_tests}; - } -}; - -BOTAN_REGISTER_TEST("pubkey", "pk_pad_emsa_unit", EMSA_unit_tests); - -#endif - } // namespace Botan_Tests diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index 938b68aa362..79edea422b0 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -106,7 +106,7 @@ Test::Result PK_Signature_Generation_Test::run_one_test(const std::string& pad_h if(vars.has_key("Group")) { test_name << "-" << vars.get_req_str("Group"); } - test_name << "/" << padding << " signature generation"; + test_name << "/" << printed_params(vars, padding) << " signature generation"; Test::Result result(test_name.str()); diff --git a/src/tests/test_pubkey.h b/src/tests/test_pubkey.h index 171cc314e13..375ca0a03dc 100644 --- a/src/tests/test_pubkey.h +++ b/src/tests/test_pubkey.h @@ -34,6 +34,8 @@ class PK_Test : public Text_Based_Test { throw Test_Error("No default padding scheme set for " + algo_name()); } + virtual std::string printed_params(const VarMap& /*vm*/, const std::string& padding) const { return padding; } + virtual std::string choose_padding(const VarMap& vars, const std::string& pad_hdr); private: diff --git a/src/tests/test_rsa.cpp b/src/tests/test_rsa.cpp index ad5b3fb298b..00ad0f2b7c8 100644 --- a/src/tests/test_rsa.cpp +++ b/src/tests/test_rsa.cpp @@ -203,9 +203,9 @@ class RSA_Blinding_Tests final : public Test { * are used as an additional test on the blinders. */ - Botan::PK_Signer signer( - rsa, this->rng(), "Raw", Botan::Signature_Format::Standard, "base"); // don't try this at home - Botan::PK_Verifier verifier(rsa, "Raw", Botan::Signature_Format::Standard, "base"); + // don't try this at home + auto signer = rsa.signer().with_rng(this->rng()).with_hash("Raw").create(); + auto verifier = rsa.signature_verifier().with_hash("Raw").create(); for(size_t i = 1; i <= BOTAN_BLINDING_REINIT_INTERVAL * 6; ++i) { std::vector input(16); diff --git a/src/tests/test_xmss.cpp b/src/tests/test_xmss.cpp index 7ff0d7afa9c..7a3d3a23ddc 100644 --- a/src/tests/test_xmss.cpp +++ b/src/tests/test_xmss.cpp @@ -43,7 +43,11 @@ class XMSS_Signature_Tests final : public PK_Signature_Generation_Test { return false; } - std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Params"); } + std::string default_padding(const VarMap& /*vars*/) const override { return ""; } + + std::string printed_params(const VarMap& vars, const std::string& /*padding*/) const override { + return vars.get_req_str("Params"); + } std::unique_ptr load_private_key(const VarMap& vars) override { const std::vector raw_key = vars.get_req_bin("PrivateKey"); @@ -58,7 +62,11 @@ class XMSS_Signature_Verify_Tests final : public PK_Signature_Verification_Test XMSS_Signature_Verify_Tests() : PK_Signature_Verification_Test("XMSS", "pubkey/xmss_verify.vec", "Params,Msg,PublicKey,Signature") {} - std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Params"); } + std::string default_padding(const VarMap& /*vars*/) const override { return ""; } + + std::string printed_params(const VarMap& vars, const std::string& /*padding*/) const override { + return vars.get_req_str("Params"); + } std::unique_ptr load_public_key(const VarMap& vars) override { const std::vector raw_key = vars.get_req_bin("PublicKey"); @@ -72,7 +80,11 @@ class XMSS_Signature_Verify_Invalid_Tests final : public PK_Signature_NonVerific PK_Signature_NonVerification_Test( "XMSS", "pubkey/xmss_invalid.vec", "Params,Msg,PublicKey,InvalidSignature") {} - std::string default_padding(const VarMap& vars) const override { return vars.get_req_str("Params"); } + std::string default_padding(const VarMap& /*vars*/) const override { return ""; } + + std::string printed_params(const VarMap& vars, const std::string& /*padding*/) const override { + return vars.get_req_str("Params"); + } std::unique_ptr load_public_key(const VarMap& vars) override { const std::vector raw_key = vars.get_req_bin("PublicKey"); @@ -157,8 +169,7 @@ std::vector xmss_statefulness() { auto sign_something = [&rng](auto& sk) { auto msg = Botan::hex_decode("deadbeef"); - Botan::PK_Signer signer(sk, *rng, "SHA2_10_256"); - signer.sign_message(msg, *rng); + sk.signer().with_rng(*rng).create().sign_message(msg, *rng); }; return {CHECK("signing alters state", @@ -279,34 +290,33 @@ std::vector xmss_legacy_private_key() { Botan::XMSS_PublicKey legacy_public_key = Botan::XMSS_PublicKey(legacy_xmss_public_key); const auto message = Botan::hex_decode("deadcafe"); - const auto algo_name = "SHA2_10_256"; auto rng = Test::new_rng(__func__); return { CHECK("Use a legacy private key to create a signature", [&](auto& result) { - Botan::PK_Signer signer(legacy_secret_key, *rng, algo_name); + auto signer = legacy_secret_key.signer().with_rng(*rng).create(); auto signature = signer.sign_message(message, *rng); - Botan::PK_Verifier verifier(public_key_from_secret_key, algo_name); + auto verifier = public_key_from_secret_key.signature_verifier().create(); result.confirm("legacy private key generates signatures that are still verifiable", verifier.verify_message(message, signature)); }), CHECK("Verify a legacy signature", [&](auto& result) { - Botan::PK_Verifier verifier(public_key_from_secret_key, algo_name); + auto verifier = public_key_from_secret_key.signature_verifier().create(); result.confirm("legacy private key generates signatures that are still verifiable", verifier.verify_message(message, legacy_signature)); }), CHECK("Verify a new signature by a legacy private key with a legacy public key", [&](auto& result) { - Botan::PK_Signer signer(legacy_secret_key, *rng, algo_name); + auto signer = legacy_secret_key.signer().with_rng(*rng).create(); auto signature = signer.sign_message(message, *rng); - Botan::PK_Verifier verifier(legacy_public_key, algo_name); + auto verifier = legacy_public_key.signature_verifier().create(); result.confirm("legacy private key generates signatures that are still verifiable", verifier.verify_message(message, legacy_signature)); }), diff --git a/src/tests/unit_ecdsa.cpp b/src/tests/unit_ecdsa.cpp index 8abfd20ca2a..2ab71ce4210 100644 --- a/src/tests/unit_ecdsa.cpp +++ b/src/tests/unit_ecdsa.cpp @@ -139,8 +139,8 @@ Test::Result test_read_pkcs8() { result.confirm("EC_Group is marked as explicit encoding", ecdsa_nodp->domain().used_explicit_encoding()); - Botan::PK_Signer signer(*ecdsa_nodp, *rng, "SHA-256"); - Botan::PK_Verifier verifier(*ecdsa_nodp, "SHA-256"); + auto signer = ecdsa_nodp->signer().with_rng(*rng).with_hash("SHA-256").create(); + auto verifier = ecdsa_nodp->signature_verifier().with_hash("SHA-256").create(); const auto msg = rng->random_vec(48);