From 07298307754f61dcfdb0f92b6814545bf1fac205 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sat, 17 Aug 2024 10:43:57 -0400 Subject: [PATCH] Add PK_Signature_Options This allows controlling all details of how signatures are created, without having to stuff values into the single parameters string which was previously available. --- src/cli/pubkey.cpp | 61 ++--- src/examples/pkcs10_csr_on_tpm2.cpp | 2 +- .../emsa_pkcs1/pkcs1_sig_padding.cpp | 15 +- .../emsa_pkcs1/pkcs1_sig_padding.h | 4 +- src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp | 18 +- src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h | 15 +- .../pk_pad/sig_padding/iso9796/iso9796.cpp | 3 + src/lib/pk_pad/sig_padding/iso9796/iso9796.h | 4 +- src/lib/pk_pad/sig_padding/sig_padding.cpp | 132 ++++------- src/lib/pk_pad/sig_padding/sig_padding.h | 17 +- src/lib/prov/pkcs11/p11_ecdsa.cpp | 28 +-- src/lib/prov/pkcs11/p11_ecdsa.h | 8 +- src/lib/prov/pkcs11/p11_mechanism.cpp | 19 +- src/lib/prov/pkcs11/p11_mechanism.h | 13 +- src/lib/prov/pkcs11/p11_rsa.cpp | 22 +- src/lib/prov/pkcs11/p11_rsa.h | 8 +- src/lib/prov/tpm/tpm.cpp | 12 +- src/lib/prov/tpm/tpm.h | 5 +- src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp | 29 +-- src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.h | 8 +- src/lib/prov/tpm2/tpm2_rsa/info.txt | 3 + src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp | 78 +++---- src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h | 9 +- src/lib/pubkey/curve448/ed448/ed448.cpp | 42 ++-- src/lib/pubkey/curve448/ed448/ed448.h | 12 +- .../dilithium/dilithium_common/dilithium.cpp | 63 ++++-- .../dilithium/dilithium_common/dilithium.h | 13 +- src/lib/pubkey/dsa/dsa.cpp | 29 ++- src/lib/pubkey/dsa/dsa.h | 8 +- src/lib/pubkey/ecdsa/ecdsa.cpp | 32 +-- src/lib/pubkey/ecdsa/ecdsa.h | 8 +- src/lib/pubkey/ecgdsa/ecgdsa.cpp | 31 +-- src/lib/pubkey/ecgdsa/ecgdsa.h | 8 +- src/lib/pubkey/eckcdsa/eckcdsa.cpp | 51 ++--- src/lib/pubkey/eckcdsa/eckcdsa.h | 8 +- src/lib/pubkey/ed25519/ed25519.h | 8 +- src/lib/pubkey/ed25519/ed25519_key.cpp | 50 ++-- src/lib/pubkey/gost_3410/gost_3410.cpp | 42 ++-- src/lib/pubkey/gost_3410/gost_3410.h | 8 +- src/lib/pubkey/hss_lms/hss_lms.cpp | 23 +- src/lib/pubkey/hss_lms/hss_lms.h | 8 +- src/lib/pubkey/info.txt | 6 +- src/lib/pubkey/pk_keys.cpp | 29 ++- src/lib/pubkey/pk_keys.h | 58 ++++- src/lib/pubkey/pk_ops.cpp | 35 +-- src/lib/pubkey/pk_ops_impl.h | 6 +- src/lib/pubkey/pk_options.cpp | 142 ++++++++++++ src/lib/pubkey/pk_options.h | 197 ++++++++++++++++ src/lib/pubkey/pk_options_impl.cpp | 214 ++++++++++++++++++ src/lib/pubkey/pk_options_impl.h | 25 ++ src/lib/pubkey/pubkey.cpp | 44 +++- src/lib/pubkey/pubkey.h | 28 ++- src/lib/pubkey/rsa/rsa.cpp | 49 ++-- src/lib/pubkey/rsa/rsa.h | 8 +- src/lib/pubkey/sm2/sm2.cpp | 99 ++++---- src/lib/pubkey/sm2/sm2.h | 10 +- .../sphincsplus_common/sphincsplus.cpp | 27 ++- .../sphincsplus_common/sphincsplus.h | 8 +- src/lib/pubkey/xmss/xmss.h | 8 +- src/lib/pubkey/xmss/xmss_privatekey.cpp | 15 +- src/lib/pubkey/xmss/xmss_publickey.cpp | 13 +- src/lib/utils/assert.h | 7 + src/lib/x509/x509_obj.cpp | 2 +- src/scripts/test_cli.py | 8 +- src/tests/data/pubkey/api_sign.vec | 2 +- src/tests/test_dilithium.cpp | 5 +- src/tests/test_ecdsa.cpp | 5 +- src/tests/test_ed25519.cpp | 5 +- src/tests/test_hss_lms.cpp | 17 +- src/tests/test_pk_pad.cpp | 81 ------- src/tests/test_pubkey.cpp | 2 +- src/tests/test_pubkey.h | 2 + src/tests/test_rsa.cpp | 7 +- src/tests/test_xmss.cpp | 34 ++- 74 files changed, 1388 insertions(+), 767 deletions(-) create mode 100644 src/lib/pubkey/pk_options.cpp create mode 100644 src/lib/pubkey/pk_options.h create mode 100644 src/lib/pubkey/pk_options_impl.cpp create mode 100644 src/lib/pubkey/pk_options_impl.h diff --git a/src/cli/pubkey.cpp b/src/cli/pubkey.cpp index 80b56647d7a..33dda27037f 100644 --- a/src/cli/pubkey.cpp +++ b/src/cli/pubkey.cpp @@ -21,10 +21,10 @@ #include #include #include + #include #include #include - #include #if defined(BOTAN_HAS_DL_GROUP) #include @@ -97,26 +97,17 @@ 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; - } else { - std::ostringstream oss; - oss << padding << "(" << hash << ")"; - return oss.str(); +Botan::PK_Signature_Options sig_options( + std::string_view key, std::string_view padding, std::string_view hash, bool use_der, std::string_view provider) { + if(key == "RSA" && padding.empty()) { + return sig_options(key, "PSS", hash, use_der, provider); } + + return Botan::PK_Signature_Options() + .with_hash(hash) + .with_padding(padding) + .with_der_encoded_signature(use_der) + .with_provider(provider); } } // namespace @@ -196,21 +187,14 @@ 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 format = Botan::Signature_Format::Standard; - - if(flag_set("der-format")) { - if(!key->_signature_element_size_for_DER_encoding()) { - throw CLI_Usage_Error("Key type " + key->algo_name() + - " does not support DER formatting for signatures"); - } - format = Botan::Signature_Format::DerSequence; + if(flag_set("der-format") && !key->_signature_element_size_for_DER_encoding()) { + throw CLI_Usage_Error("Key type " + key->algo_name() + " does not support DER formatting for signatures"); } - const std::string provider = get_arg("provider"); + const auto options = + sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), get_arg("provider")); - Botan::PK_Signer signer(*key, rng(), sig_padding, format, provider); + Botan::PK_Signer signer(*key, rng(), options); auto onData = [&signer](const uint8_t b[], size_t l) { signer.update(b, l); }; Command::read_file(get_arg("file"), onData); @@ -254,18 +238,9 @@ 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 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; - } + const auto options = sig_options(key->algo_name(), get_arg("padding"), hash_fn, flag_set("der-format"), ""); - Botan::PK_Verifier verifier(*key, sig_padding, format); + Botan::PK_Verifier verifier(*key, options); 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/pkcs10_csr_on_tpm2.cpp b/src/examples/pkcs10_csr_on_tpm2.cpp index b0479c12af6..3d251ae1348 100644 --- a/src/examples/pkcs10_csr_on_tpm2.cpp +++ b/src/examples/pkcs10_csr_on_tpm2.cpp @@ -2,7 +2,7 @@ #include -#if defined(BOTAN_HAS_TPM2) +#if defined(BOTAN_HAS_TPM2) && defined(BOTAN_HAS_TPM2_RSA) #include #include diff --git a/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp b/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp index abae897c02a..03a5efe988c 100644 --- a/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp +++ b/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.cpp @@ -100,11 +100,16 @@ PKCS1v15_Raw_SignaturePaddingScheme::PKCS1v15_Raw_SignaturePaddingScheme() : m_h // m_hash_id, m_hash_name left empty } -PKCS1v15_Raw_SignaturePaddingScheme::PKCS1v15_Raw_SignaturePaddingScheme(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(); +PKCS1v15_Raw_SignaturePaddingScheme::PKCS1v15_Raw_SignaturePaddingScheme(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 PKCS1v15_Raw_SignaturePaddingScheme::update(const uint8_t input[], size_t length) { diff --git a/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h b/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h index d8b3d82c683..4ed58f2c0d0 100644 --- a/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h +++ b/src/lib/pk_pad/sig_padding/emsa_pkcs1/pkcs1_sig_padding.h @@ -11,8 +11,8 @@ #include #include +#include #include -#include #include namespace Botan { @@ -73,7 +73,7 @@ class PKCS1v15_Raw_SignaturePaddingScheme final : public SignaturePaddingScheme * @param hash_algo the digest id for that hash is included in * the signature. */ - explicit PKCS1v15_Raw_SignaturePaddingScheme(std::string_view hash_algo); + explicit PKCS1v15_Raw_SignaturePaddingScheme(const std::optional& hash_algo = std::nullopt); std::string hash_function() const override { return m_hash_name; } diff --git a/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp b/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp index 5590043141d..29a42f33d29 100644 --- a/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp +++ b/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.cpp @@ -145,11 +145,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 @@ -192,11 +191,10 @@ std::string PSSR::name() const { return fmt("PSS({},MGF1,{})", m_hash->name(), m_salt_size); } -PSS_Raw::PSS_Raw(std::unique_ptr hash) : - m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {} - -PSS_Raw::PSS_Raw(std::unique_ptr hash, size_t salt_size) : - m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {} +PSS_Raw::PSS_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()) {} /* * PSS_Raw Update Operation diff --git a/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h b/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h index c59ee4df0d9..c21994fa427 100644 --- a/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h +++ b/src/lib/pk_pad/sig_padding/emsa_pssr/pssr.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -23,16 +24,11 @@ class HashFunction; */ class PSSR final : public SignaturePaddingScheme { 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; @@ -60,16 +56,11 @@ class PSSR final : public SignaturePaddingScheme { */ class PSS_Raw final : public SignaturePaddingScheme { public: - /** - * @param hash the hash function to use - */ - explicit PSS_Raw(std::unique_ptr hash); - /** * @param hash the hash function to use * @param salt_size the size of the salt to use in bytes */ - PSS_Raw(std::unique_ptr hash, size_t salt_size); + explicit PSS_Raw(std::unique_ptr hash, std::optional salt_size = std::nullopt); std::string hash_function() const override; diff --git a/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp b/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp index 54e6ce1812f..5d5419e08e4 100644 --- a/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp +++ b/src/lib/pk_pad/sig_padding/iso9796/iso9796.cpp @@ -209,6 +209,9 @@ bool iso9796_verification(std::span repr, } // namespace +ISO_9796_DS2::ISO_9796_DS2(std::unique_ptr hash, bool implicit, std::optional salt_size) : + m_hash(std::move(hash)), m_implicit(implicit), m_salt_len(salt_size.value_or(m_hash->output_length())) {} + /* * ISO-9796-2 signature scheme 2 * DS 2 is probabilistic diff --git a/src/lib/pk_pad/sig_padding/iso9796/iso9796.h b/src/lib/pk_pad/sig_padding/iso9796/iso9796.h index 12c58712ee1..7519185d5df 100644 --- a/src/lib/pk_pad/sig_padding/iso9796/iso9796.h +++ b/src/lib/pk_pad/sig_padding/iso9796/iso9796.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -27,8 +28,7 @@ class ISO_9796_DS2 final : public SignaturePaddingScheme { * @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_len(salt_size) {} + ISO_9796_DS2(std::unique_ptr hash, bool implicit, std::optional salt_size); std::string hash_function() const override; diff --git a/src/lib/pk_pad/sig_padding/sig_padding.cpp b/src/lib/pk_pad/sig_padding/sig_padding.cpp index 4e339c8515a..e03f8466758 100644 --- a/src/lib/pk_pad/sig_padding/sig_padding.cpp +++ b/src/lib/pk_pad/sig_padding/sig_padding.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #if defined(BOTAN_HAS_X931_SIGNATURE_PADDING) @@ -32,112 +33,75 @@ namespace Botan { -std::unique_ptr SignaturePaddingScheme::create(std::string_view algo_spec) { - SCAN_Name req(algo_spec); +std::unique_ptr SignaturePaddingScheme::create_or_throw(const PK_Signature_Options& options) { + const bool is_raw_hash = !options.using_hash() || options.hash_function_name() == "Raw"; -#if defined(BOTAN_HAS_EMSA_PKCS1) - // TODO(Botan4) Remove all but "PKCS1v15" - 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(!options.using_padding() || options.padding() == "Raw") { + // Only valid possibility for empty padding is no hash / "Raw" hash + +#if defined(BOTAN_HAS_EMSA_RAW) + if(is_raw_hash) { + if(options.using_prehash()) { + if(auto hash = HashFunction::create(options.prehash_fn().value())) { + return std::make_unique(hash->output_length()); } + } else { + return std::make_unique(); } } - } #endif + } else { + const std::string padding = options.padding().value(); -#if defined(BOTAN_HAS_EMSA_PSSR) - // TODO(Botan4) Remove all but "PSS_Raw" - 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)); - } + // null if raw_hash + auto hash = [&]() -> std::unique_ptr { + if(is_raw_hash) { + return nullptr; + } else { + return HashFunction::create(options.hash_function_name()); } - } - } + }(); - // TODO(Botan4) Remove all but "PSS" - 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_PKCS1) + if(padding == "PKCS1v15") { + if(is_raw_hash) { + return std::make_unique(options.prehash_fn()); + } else if(hash) { + return std::make_unique(std::move(hash)); } } - } #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 defined(BOTAN_HAS_PSS) + if(padding == "PSS_Raw" && hash) { + return std::make_unique(std::move(hash), options.salt_size()); } - } - //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); - } + + if(padding == "PSS" && hash) { + return std::make_unique(std::move(hash), options.salt_size()); } - } #endif -#if defined(BOTAN_HAS_X931_SIGNATURE_PADDING) - // TODO(Botan4) Remove all but "X9.31" - 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 defined(BOTAN_HAS_ISO_9796) + if(padding == "ISO_9796_DS2" && hash) { + return std::make_unique( + std::move(hash), !options.using_explicit_trailer_field(), options.salt_size()); } - } -#endif -#if defined(BOTAN_HAS_RAW_SIGNATURE_PADDING) - 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()); - } + //ISO-9796-2 DS 3 is deterministic and DS2 without a salt + if(padding == "ISO_9796_DS3" && hash) { + return std::make_unique(std::move(hash), !options.using_explicit_trailer_field()); } - } #endif - return nullptr; -} - -std::unique_ptr SignaturePaddingScheme::create_or_throw(std::string_view algo_spec) { - auto emsa = SignaturePaddingScheme::create(algo_spec); - if(emsa) { - return emsa; +#if defined(BOTAN_HAS_EMSA_X931) + if(padding == "X9.31" && hash) { + return std::make_unique(std::move(hash)); + } +#endif } - throw Algorithm_Not_Found(algo_spec); + + throw Lookup_Error("Invalid or unavailable signature padding scheme " + options.to_string()); } } // namespace Botan diff --git a/src/lib/pk_pad/sig_padding/sig_padding.h b/src/lib/pk_pad/sig_padding/sig_padding.h index c0837a4ecb5..41c48b1d550 100644 --- a/src/lib/pk_pad/sig_padding/sig_padding.h +++ b/src/lib/pk_pad/sig_padding/sig_padding.h @@ -16,6 +16,7 @@ namespace Botan { class RandomNumberGenerator; +class PK_Signature_Options; /** * RSA Signature Padding Scheme @@ -27,20 +28,12 @@ class BOTAN_TEST_API SignaturePaddingScheme /* NOLINT(*-special-member-functions virtual ~SignaturePaddingScheme() = default; /** - * Factory method for SignaturePaddingScheme (message-encoding methods for signatures - * with appendix) objects - * @param algo_spec the name of the SignaturePaddingScheme 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 SignaturePaddingScheme (message-encoding methods for signatures - * with appendix) objects - * @param algo_spec the name of the SignaturePaddingScheme to create + * Factory method for SignaturePaddingScheme objects + * + * @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(const PK_Signature_Options& options); /** * Add more data to the signature computation diff --git a/src/lib/prov/pkcs11/p11_ecdsa.cpp b/src/lib/prov/pkcs11/p11_ecdsa.cpp index 13e0559caee..93e17fbc2b3 100644 --- a/src/lib/prov/pkcs11/p11_ecdsa.cpp +++ b/src/lib/prov/pkcs11/p11_ecdsa.cpp @@ -10,10 +10,12 @@ #if defined(BOTAN_HAS_ECDSA) + #include #include #include #include #include + #include namespace Botan::PKCS11 { @@ -49,12 +51,12 @@ namespace { class PKCS11_ECDSA_Signature_Operation final : public PK_Ops::Signature { public: - PKCS11_ECDSA_Signature_Operation(const PKCS11_ECDSA_PrivateKey& key, std::string_view hash) : + PKCS11_ECDSA_Signature_Operation(const PKCS11_ECDSA_PrivateKey& key, const PK_Signature_Options& options) : PK_Ops::Signature(), m_key(key), m_order_bytes(key.domain().get_order_bytes()), - m_mechanism(MechanismWrapper::create_ecdsa_mechanism(hash)), - m_hash(hash) {} + m_mechanism(MechanismWrapper::create_ecdsa_mechanism(options.hash_function_name())), + m_hash(options.hash_function_name()) {} void update(std::span input) override { if(!m_initialized) { @@ -111,11 +113,11 @@ AlgorithmIdentifier PKCS11_ECDSA_Signature_Operation::algorithm_identifier() con class PKCS11_ECDSA_Verification_Operation final : public PK_Ops::Verification { public: - PKCS11_ECDSA_Verification_Operation(const PKCS11_ECDSA_PublicKey& key, std::string_view hash) : + PKCS11_ECDSA_Verification_Operation(const PKCS11_ECDSA_PublicKey& key, const PK_Signature_Options& options) : PK_Ops::Verification(), m_key(key), - m_mechanism(MechanismWrapper::create_ecdsa_mechanism(hash)), - m_hash(hash) {} + m_mechanism(MechanismWrapper::create_ecdsa_mechanism(options.hash_function_name())), + m_hash(options.hash_function_name()) {} void update(std::span input) override { if(!m_initialized) { @@ -170,15 +172,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( + const 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, const 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 3b7a9f27759..0952c7d7f5a 100644 --- a/src/lib/prov/pkcs11/p11_ecdsa.h +++ b/src/lib/prov/pkcs11/p11_ecdsa.h @@ -57,8 +57,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(const PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP @@ -117,9 +116,8 @@ class BOTAN_PUBLIC_API(2, 0) PKCS11_ECDSA_PrivateKey final : public PKCS11_EC_Pr 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, + const 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 ba539b1859d..60e8a308525 100644 --- a/src/lib/prov/pkcs11/p11_mechanism.cpp +++ b/src/lib/prov/pkcs11/p11_mechanism.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -154,7 +155,7 @@ MechanismWrapper MechanismWrapper::create_rsa_crypt_mechanism(std::string_view p return mech; } -MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(std::string_view padding) { +MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(const PK_Signature_Options& options) { // note: when updating this map, update the documentation for `MechanismWrapper::create_rsa_sign_mechanism` static const std::map SignMechanisms = { {"Raw", RSA_SignMechanism(MechanismType::RsaX509)}, @@ -227,6 +228,22 @@ MechanismWrapper MechanismWrapper::create_rsa_sign_mechanism(std::string_view pa {"PSSR(SHA-512,MGF1,64)", RSA_SignMechanism(MechanismType::Sha512RsaPkcsPss)}, }; + const std::string padding = [&]() { + if(options.using_hash() && options.using_padding()) { + return fmt("{}({})", options.padding().value(), options.hash_function_name()); + } + + if(options.using_padding()) { + return options.padding().value(); + } + + if(options.using_hash()) { + return options.hash_function_name(); + } + + throw Invalid_Argument("RSA signature requires a padding scheme"); + }(); + auto mechanism_info_it = SignMechanisms.find(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` diff --git a/src/lib/prov/pkcs11/p11_mechanism.h b/src/lib/prov/pkcs11/p11_mechanism.h index 6c742367404..d5f4b872422 100644 --- a/src/lib/prov/pkcs11/p11_mechanism.h +++ b/src/lib/prov/pkcs11/p11_mechanism.h @@ -12,8 +12,13 @@ #include #include -#include -#include +#include + +namespace Botan { + +class PK_Signature_Options; + +} // namespace Botan namespace Botan::PKCS11 { @@ -35,10 +40,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(const 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 111721b48d4..fb83452c709 100644 --- a/src/lib/prov/pkcs11/p11_rsa.cpp +++ b/src/lib/prov/pkcs11/p11_rsa.cpp @@ -222,8 +222,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, const 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(); } @@ -339,8 +339,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, const PK_Signature_Options& options) : + m_key(key), m_mechanism(MechanismWrapper::create_rsa_sign_mechanism(options)) {} void update(std::span input) override { if(!m_initialized) { @@ -404,9 +404,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( + const PK_Signature_Options& options) const { + return std::make_unique(*this, options); } std::unique_ptr PKCS11_RSA_PrivateKey::create_decryption_op(RandomNumberGenerator& rng, @@ -419,10 +419,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, const 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 0d1a5cd0e28..0d6c9c2034c 100644 --- a/src/lib/prov/pkcs11/p11_rsa.h +++ b/src/lib/prov/pkcs11/p11_rsa.h @@ -81,8 +81,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(const PK_Signature_Options& options) const override; }; /// Properties for importing a PKCS#11 RSA private key @@ -183,9 +182,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, + const 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 6cd32101572..9f006c1182a 100644 --- a/src/lib/prov/tpm/tpm.cpp +++ b/src/lib/prov/tpm/tpm.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -372,10 +373,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, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + if(!options.using_padding() || options.padding().value() != "PKCS1v15") { + throw Invalid_Argument("TPMv1 can only sign using PKCS1v15 padding"); + } + return std::make_unique(*this, options.hash_function_name()); } } // namespace Botan diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h index 6bc28c44b98..3e99b29ad55 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, + const PK_Signature_Options& options) const override; private: TPM_Context& m_ctx; diff --git a/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp b/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp index a763cd95c55..544768b005a 100644 --- a/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp +++ b/src/lib/prov/tpm2/tpm2_ecc/tpm2_ecc.cpp @@ -8,6 +8,7 @@ #include +#include #include #include #include @@ -159,7 +160,8 @@ std::unique_ptr EC_PrivateKey::create_unrestricted_transient(c namespace { -SignatureAlgorithmSelection make_signature_scheme(std::string_view hash_name) { +SignatureAlgorithmSelection make_signature_scheme(const PK_Signature_Options& options) { + const std::string hash_name = options.hash_function_name(); return { .signature_scheme = TPMT_SIG_SCHEME{ @@ -183,8 +185,8 @@ size_t signature_length_for_ecdsa_key_handle(const SessionBundle& sessions, cons 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, const PK_Signature_Options& options) : + Signature_Operation(object, sessions, make_signature_scheme(options)) {} size_t signature_length() const override { return signature_length_for_ecdsa_key_handle(sessions(), key_handle()); @@ -213,8 +215,10 @@ 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, + const PK_Signature_Options& options) : + Verification_Operation(object, sessions, make_signature_scheme(options)) {} private: TPMT_SIGNATURE unmarshal_signature(std::span sig_data) const override { @@ -241,17 +245,14 @@ 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(const PK_Signature_Options& options) const { + 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, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + 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 15cf7379385..80c1af062bc 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(const 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, + const PK_Signature_Options& options) const override; protected: friend class TPM2::PrivateKey; diff --git a/src/lib/prov/tpm2/tpm2_rsa/info.txt b/src/lib/prov/tpm2/tpm2_rsa/info.txt index d06a86edba9..7e7134fba4e 100644 --- a/src/lib/prov/tpm2/tpm2_rsa/info.txt +++ b/src/lib/prov/tpm2/tpm2_rsa/info.txt @@ -2,6 +2,9 @@ TPM2_RSA_ADAPTER -> 20240819 +# TODO needs to be fixed before merge +load_on never + name -> "TPM2 RSA Adapter" brief -> "Support for RSA key pairs hosted on TPM 2.0" diff --git a/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp b/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp index 58802cb01aa..4073bec80bb 100644 --- a/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp +++ b/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -142,22 +143,23 @@ std::unique_ptr RSA_PrivateKey::create_unrestricted_transient( namespace { -SignatureAlgorithmSelection select_signature_algorithms(std::string_view padding) { - const SCAN_Name req(padding); - if(req.arg_count() == 0) { - throw Invalid_Argument("RSA signing padding scheme must at least specify a hash function"); - } +SignatureAlgorithmSelection select_signature_algorithms(const PK_Signature_Options& options) { + const std::string hash_name = options.hash_function_name(); - auto sig_scheme = rsa_signature_scheme_botan_to_tss2(padding); - if(!sig_scheme) { - throw Not_Implemented(Botan::fmt("RSA signing with padding scheme {}", padding)); - } + if(auto padding = options.padding()) { + auto sig_scheme = rsa_signature_scheme_botan_to_tss2(*padding); + if(!sig_scheme) { + throw Not_Implemented(Botan::fmt("RSA signing with padding scheme {}", *padding)); + } - return { - .signature_scheme = sig_scheme.value(), - .hash_name = req.arg(0), - .padding = std::string(padding), - }; + return { + .signature_scheme = sig_scheme.value(), + .hash_name = hash_name, + .padding = std::string(*padding), + }; + } else { + throw Not_Implemented("TPM2 RSA signing without padding is not supported"); + } } size_t signature_length_for_rsa_key_handle(const SessionBundle& sessions, const Object& key_handle) { @@ -166,32 +168,24 @@ size_t signature_length_for_rsa_key_handle(const SessionBundle& sessions, const 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, + const PK_Signature_Options& options) : + Signature_Operation(object, sessions, select_signature_algorithms(options)) {} size_t signature_length() const override { return signature_length_for_rsa_key_handle(sessions(), key_handle()); } AlgorithmIdentifier algorithm_identifier() const override { - // 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 std::string padding_name = SignaturePaddingScheme::create_or_throw(padding().value())->name(); - - try { + if(scheme == TPM2_ALG_RSASSA) { const std::string full_name = "RSA/" + padding_name; const OID oid = OID::from_string(full_name); return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM); - } catch(Lookup_Error&) {} - - if(padding_name.starts_with("PSS(")) { + } else if(scheme == TPM2_ALG_RSAPSS) { auto parameters = PSS_Params::from_padding_name(padding_name).serialize(); return AlgorithmIdentifier("RSA/PSS", parameters); + } else { + throw Internal_Error( + fmt("Unexpected signature scheme {} used for RSA signatures", static_cast(scheme))); } throw Invalid_Argument(fmt("Signatures using RSA/{} are not supported", padding_name)); @@ -215,8 +209,10 @@ 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, + const PK_Signature_Options& options) : + Verification_Operation(object, sessions, select_signature_algorithms(options)) {} private: TPMT_SIGNATURE unmarshal_signature(std::span signature) const override { @@ -389,17 +385,15 @@ 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( + const PK_Signature_Options& options) const { + 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, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + 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 c19a684b6c7..0b87b9a0d84 100644 --- a/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h +++ b/src/lib/prov/tpm2/tpm2_rsa/tpm2_rsa.h @@ -8,6 +8,7 @@ #ifndef BOTAN_TPM2_RSA_H_ #define BOTAN_TPM2_RSA_H_ +#include "botan/pk_keys.h" #include #include @@ -30,8 +31,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(const PK_Signature_Options& options) const override; std::unique_ptr create_encryption_op(Botan::RandomNumberGenerator& rng, std::string_view params, @@ -95,9 +95,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, + const 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 6eb14beb502..60d73e88c4c 100644 --- a/src/lib/pubkey/curve448/ed448/ed448.cpp +++ b/src/lib/pubkey/curve448/ed448/ed448.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -213,18 +214,19 @@ 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)"); +std::unique_ptr Ed448_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + BOTAN_ARG_CHECK(!options.using_padding(), "Ed448 does not support padding"); + + if(!options.using_provider()) { + if(options.using_prehash()) { + return std::make_unique(*this, options.prehash_fn().value_or("SHAKE-256(512)")); } else { - return std::make_unique(*this, std::string(params)); + return std::make_unique(*this); } } - throw Provider_Not_Found(algo_name(), provider); + + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr Ed448_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, @@ -239,19 +241,21 @@ 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)"); +std::unique_ptr Ed448_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + + BOTAN_ARG_CHECK(!options.using_padding(), "Ed448 does not support padding"); + BOTAN_ARG_CHECK(!options.using_salt_size(), "Ed448 does not support a salt"); + + if(!options.using_provider()) { + if(options.using_prehash()) { + return std::make_unique(*this, options.prehash_fn().value_or("SHAKE-256(512)")); } else { - return std::make_unique(*this, std::string(params)); + return std::make_unique(*this); } } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/curve448/ed448/ed448.h b/src/lib/pubkey/curve448/ed448/ed448.h index c9082897e33..3d9c3f834b8 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 { */ BOTAN_FUTURE_EXPLICIT 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(const 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, + const 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 4e3f744c803..78daf0789e8 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp @@ -126,16 +126,45 @@ bool DilithiumMode::is_available() const { return false; } +namespace { + +// Return true if randomized signatures were requested +void validate_dilithium_options(const PK_Signature_Options& options) { + BOTAN_ARG_CHECK(!options.using_hash(), "Dilithium does not allow specifying the hash function"); + BOTAN_ARG_CHECK(!options.using_padding(), "Dilithium does not support padding"); + + if(options.using_salt_size()) { + BOTAN_ARG_CHECK(!options.using_deterministic_signature(), + "Dilithium cannot support a salt while also being deterministic"); + BOTAN_ARG_CHECK(options.salt_size().value() == 32, "Dilithium can only be used with a 32 byte salt"); + } + + // This is available in ML-DSA and might be supported in the future + BOTAN_ARG_CHECK(!options.using_prehash(), "Dilithium does not support prehashing"); +} + +} // namespace + +// The signature and verification operations should be in an anonymous namespace +// as well, but cannot due to an apparent bug in MSVC + class Dilithium_Signature_Operation final : public PK_Ops::Signature { public: - Dilithium_Signature_Operation(DilithiumInternalKeypair keypair, bool randomized) : + Dilithium_Signature_Operation(DilithiumInternalKeypair keypair, const 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())) { + validate_dilithium_options(options); + } void update(std::span input) override { m_h->update(input); } @@ -373,13 +402,12 @@ 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") { +std::unique_ptr Dilithium_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { return std::make_unique(m_public); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr Dilithium_PublicKey::create_x509_verification_op( @@ -427,23 +455,14 @@ 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, const 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); + if(!options.using_provider()) { + return std::make_unique(DilithiumInternalKeypair{m_public, m_private}, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } 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 9f27b42aafa..255f6901bb7 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h @@ -91,8 +91,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(const PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -133,14 +132,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& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + const 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 ef78c1b2ac7..c10d30b09eb 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 hash_fn, + const PK_Signature_Options& options, RandomNumberGenerator& rng) : - PK_Ops::Signature_with_Hash(hash_fn), m_key(key) { + PK_Ops::Signature_with_Hash(options), m_key(key) { m_b = BigInt::random_integer(rng, BigInt::from_s32(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 hash_fn) : - PK_Ops::Verification_with_Hash(hash_fn), m_key(key) {} + DSA_Verification_Operation(const std::shared_ptr& key, const 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,12 @@ 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); +std::unique_ptr DSA_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(this->m_public_key, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr DSA_PublicKey::create_x509_verification_op( @@ -268,13 +268,12 @@ 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); +std::unique_ptr DSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(this->m_private_key, options, rng); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/dsa/dsa.h b/src/lib/pubkey/dsa/dsa.h index 7f7ae4be6c0..bea341adca8 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(const 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, + const 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 c948267c9c4..29bb9c99750 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, + const 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)), @@ -194,8 +196,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); +std::unique_ptr ECDSA_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options); } - - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr ECDSA_PublicKey::create_x509_verification_op( @@ -246,14 +247,13 @@ 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); +std::unique_ptr ECDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options, rng); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/ecdsa/ecdsa.h b/src/lib/pubkey/ecdsa/ecdsa.h index ed639f75035..69f005d7296 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(const 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, + const 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 fe7241175dc..4956b1baac0 100644 --- a/src/lib/pubkey/ecgdsa/ecgdsa.cpp +++ b/src/lib/pubkey/ecgdsa/ecgdsa.cpp @@ -8,6 +8,7 @@ #include +#include #include #include @@ -36,8 +37,8 @@ namespace { */ class ECGDSA_Signature_Operation final : public PK_Ops::Signature_with_Hash { public: - ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa, std::string_view hash_fn) : - PK_Ops::Signature_with_Hash(hash_fn), m_group(ecgdsa.domain()), m_x(ecgdsa._private_key()) {} + ECGDSA_Signature_Operation(const ECGDSA_PrivateKey& ecgdsa, const 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; @@ -78,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); +std::unique_ptr ECGDSA_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr ECGDSA_PublicKey::create_x509_verification_op( @@ -133,13 +134,13 @@ 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); +std::unique_ptr ECGDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + if(!options.using_provider()) { + return std::make_unique(*this, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/ecgdsa/ecgdsa.h b/src/lib/pubkey/ecgdsa/ecgdsa.h index 1f3fe162390..bbd336b9b07 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(const 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) ECGDSA_PrivateKey final : public ECGDSA_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, + const PK_Signature_Options& options) const override; }; BOTAN_DIAGNOSTIC_POP diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.cpp b/src/lib/pubkey/eckcdsa/eckcdsa.cpp index f2ec611dfbf..d7f143d6677 100644 --- a/src/lib/pubkey/eckcdsa/eckcdsa.cpp +++ b/src/lib/pubkey/eckcdsa/eckcdsa.cpp @@ -39,23 +39,17 @@ 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; - } +std::unique_ptr eckcdsa_signature_hash(const PK_Signature_Options& options) { + BOTAN_ARG_CHECK(!options.using_padding(), "ECKCDSA does not support padding modes"); + BOTAN_ARG_CHECK(!options.using_salt_size(), "ECKCDSA does not support a salt"); - SCAN_Name req(padding); + // We could support this, but it's not standard + BOTAN_ARG_CHECK(!options.using_prehash(), "ECKCDSA does not support prehashing"); - if(req.algo_name() == "EMSA1" && req.arg_count() == 1) { - if(auto hash = HashFunction::create(req.arg(0))) { - return hash; - } - } - - // 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_name()); } std::unique_ptr eckcdsa_signature_hash(const AlgorithmIdentifier& alg_id) { @@ -116,10 +110,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, const 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 +181,10 @@ std::vector ECKCDSA_Signature_Operation::raw_sign(std::spanhash_block_size())), m_prefix_used(false) {} @@ -264,12 +258,12 @@ 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); +std::unique_ptr ECKCDSA_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr ECKCDSA_PublicKey::create_x509_verification_op( @@ -281,13 +275,14 @@ 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); +std::unique_ptr ECKCDSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + + if(!options.using_provider()) { + return std::make_unique(*this, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/eckcdsa/eckcdsa.h b/src/lib/pubkey/eckcdsa/eckcdsa.h index e89b1a09e2f..ed4adbc384d 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(const PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -110,9 +109,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, + const 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 b8e65f00f27..cab887c0792 100644 --- a/src/lib/pubkey/ed25519/ed25519.h +++ b/src/lib/pubkey/ed25519/ed25519.h @@ -50,8 +50,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(const PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -123,9 +122,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, + const 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 3083780a770..a8dbfde7043 100644 --- a/src/lib/pubkey/ed25519/ed25519_key.cpp +++ b/src/lib/pubkey/ed25519/ed25519_key.cpp @@ -272,18 +272,23 @@ 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); +std::unique_ptr Ed25519_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + BOTAN_ARG_CHECK(!options.using_padding(), "Ed25519 does not support padding"); + + if(!options.using_provider()) { + if(options.using_prehash()) { + if(options.prehash_fn().has_value()) { + return std::make_unique(*this, options.prehash_fn().value(), false); + } else { + return std::make_unique(*this, "SHA-512", true); + } } else { - return std::make_unique(*this, params, false); + return std::make_unique(*this); } } - throw Provider_Not_Found(algo_name(), provider); + + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr Ed25519_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, @@ -298,19 +303,26 @@ 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); +std::unique_ptr Ed25519_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + + BOTAN_ARG_CHECK(!options.using_padding(), "Ed25519 does not support padding"); + BOTAN_ARG_CHECK(!options.using_salt_size(), "Ed25519 does not support a salt"); + + if(!options.using_provider()) { + if(options.using_prehash()) { + if(options.prehash_fn().has_value()) { + return std::make_unique(*this, options.prehash_fn().value(), false); + } else { + return std::make_unique(*this, "SHA-512", true); + } } else { - return std::make_unique(*this, params, false); + return std::make_unique(*this); } } - throw Provider_Not_Found(algo_name(), provider); + + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/gost_3410/gost_3410.cpp b/src/lib/pubkey/gost_3410/gost_3410.cpp index 7d4b27d21b9..9d38343535d 100644 --- a/src/lib/pubkey/gost_3410/gost_3410.cpp +++ b/src/lib/pubkey/gost_3410/gost_3410.cpp @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -124,8 +125,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 hash_fn) : - PK_Ops::Signature_with_Hash(hash_fn), m_group(gost_3410.domain()), m_x(gost_3410._private_key()) {} + GOST_3410_Signature_Operation(const GOST_3410_PrivateKey& gost_3410, const 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(); } @@ -175,23 +176,23 @@ std::vector GOST_3410_Signature_Operation::raw_sign(std::span 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); +std::unique_ptr GOST_3410_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr GOST_3410_PublicKey::create_x509_verification_op( @@ -257,13 +258,14 @@ std::unique_ptr GOST_3410_PublicKey::create_x509_verificat throw Provider_Not_Found(algo_name(), provider); } -std::unique_ptr GOST_3410_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, - std::string_view params, - std::string_view provider) const { - if(provider == "base" || provider.empty()) { - return std::make_unique(*this, params); +std::unique_ptr GOST_3410_PrivateKey::_create_signature_op( + RandomNumberGenerator& rng, const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + + if(!options.using_provider()) { + return std::make_unique(*this, options); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/gost_3410/gost_3410.h b/src/lib/pubkey/gost_3410/gost_3410.h index 5fbc0a8c6b1..793a9c39832 100644 --- a/src/lib/pubkey/gost_3410/gost_3410.h +++ b/src/lib/pubkey/gost_3410/gost_3410.h @@ -64,8 +64,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(const PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -121,9 +120,8 @@ class BOTAN_PUBLIC_API(2, 0) GOST_3410_PrivateKey final : public GOST_3410_Publi return EC_PublicKey::algorithm_identifier(); // NOLINT(bugprone-parent-virtual-call) } - 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, + const 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 250df36d5d4..3e041407fb0 100644 --- a/src/lib/pubkey/hss_lms/hss_lms.cpp +++ b/src/lib/pubkey/hss_lms/hss_lms.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace Botan { @@ -85,12 +86,12 @@ 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") { +std::unique_ptr HSS_LMS_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { return std::make_unique(m_public); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr HSS_LMS_PublicKey::create_x509_verification_op( @@ -195,16 +196,18 @@ 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, + const 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") { + validate_for_hash_based_signature(options, "HSS-LMS", ""); + + BOTAN_ARG_CHECK(!options.using_hash(), "Unexpected parameters for signing with HSS-LMS"); + + if(!options.using_provider()) { return std::make_unique(m_private, m_public); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/hss_lms/hss_lms.h b/src/lib/pubkey/hss_lms/hss_lms.h index 6d794a068be..360c24dc76d 100644 --- a/src/lib/pubkey/hss_lms/hss_lms.h +++ b/src/lib/pubkey/hss_lms/hss_lms.h @@ -55,8 +55,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(const PK_Signature_Options& options) const override; std::unique_ptr create_x509_verification_op(const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const override; @@ -154,9 +153,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, + const PK_Signature_Options& options) const override; private: explicit HSS_LMS_PrivateKey(std::shared_ptr sk); diff --git a/src/lib/pubkey/info.txt b/src/lib/pubkey/info.txt index 877e86e2e16..4ac3c9c0ce1 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 @@ -18,7 +18,11 @@ x509_key.h +# Will be public eventually +pk_options.h + pk_ops_impl.h +pk_options_impl.h workfactor.h diff --git a/src/lib/pubkey/pk_keys.cpp b/src/lib/pubkey/pk_keys.cpp index 7dc0309efe4..c158a303c72 100644 --- a/src/lib/pubkey/pk_keys.cpp +++ b/src/lib/pubkey/pk_keys.cpp @@ -7,11 +7,13 @@ #include +#include #include #include #include #include #include +#include namespace Botan { @@ -19,6 +21,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()); @@ -106,8 +112,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(const PK_Signature_Options& options) const { + BOTAN_UNUSED(options); throw Lookup_Error(fmt("{} does not support verification", algo_name())); } @@ -128,9 +134,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, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng, options); throw Lookup_Error(fmt("{} does not support signatures", algo_name())); } @@ -140,4 +146,17 @@ 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 { + return this->_create_verification_op(parse_legacy_sig_options(*this, params).with_provider(provider)); +} + +std::unique_ptr Private_Key::create_signature_op(RandomNumberGenerator& rng, + std::string_view params, + std::string_view provider) const { + return this->_create_signature_op(rng, parse_legacy_sig_options(*this, params).with_provider(provider)); +} + } // namespace Botan diff --git a/src/lib/pubkey/pk_keys.h b/src/lib/pubkey/pk_keys.h index 2dc29e1ed8d..f43e55fe15e 100644 --- a/src/lib/pubkey/pk_keys.h +++ b/src/lib/pubkey/pk_keys.h @@ -22,6 +22,7 @@ namespace Botan { class BigInt; class RandomNumberGenerator; +class PK_Signature_Options; /** * Enumeration specifying the signature format. @@ -107,6 +108,23 @@ class BOTAN_PUBLIC_API(3, 0) Asymmetric_Key /* NOLINT(*special-member-functions) */ 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 @@ -263,11 +281,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(const 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. @@ -385,12 +416,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, + const 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 72421c6d164..22e6989ac6d 100644 --- a/src/lib/pubkey/pk_ops.cpp +++ b/src/lib/pubkey/pk_ops.cpp @@ -94,25 +94,30 @@ 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; - } +std::unique_ptr validate_options_returning_hash(const PK_Signature_Options& options) { + BOTAN_ARG_CHECK(!options.hash_function_name().empty(), "This algorithm requires a hash function for signing"); + BOTAN_ARG_CHECK(!options.using_padding(), "This algorithm does not support padding modes"); + BOTAN_ARG_CHECK(!options.using_salt_size(), "This algorithm does not support a salt"); - SCAN_Name req(padding); + BOTAN_ARG_CHECK(!options.using_explicit_trailer_field(), "This algorithm does not support a padding trailer field"); - 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(options.prehash_fn().has_value()) { + if(options.prehash_fn().value() != options.hash_function_name()) { + 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(options.hash_function_name().starts_with("Raw")) { + if(options.hash_function_name() == "Raw") { return std::make_unique("Raw", 0); } + SCAN_Name req(options.hash_function_name()); if(req.arg_count() == 1) { if(auto hash = HashFunction::create(req.arg(0))) { return std::make_unique(std::move(hash)); @@ -121,13 +126,13 @@ std::unique_ptr create_signature_hash(std::string_view padding) { } #endif - throw Algorithm_Not_Found(padding); + return HashFunction::create_or_throw(options.hash_function_name()); } } // 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(const PK_Signature_Options& options) : + Signature(), m_hash(validate_options_returning_hash(options)) {} PK_Ops::Signature_with_Hash::~Signature_with_Hash() = default; @@ -154,8 +159,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(const PK_Signature_Options& options) : + Verification(), m_hash(validate_options_returning_hash(options)) {} PK_Ops::Verification_with_Hash::~Verification_with_Hash() = default; diff --git a/src/lib/pubkey/pk_ops_impl.h b/src/lib/pubkey/pk_ops_impl.h index 6afc68ceea8..91225131fdc 100644 --- a/src/lib/pubkey/pk_ops_impl.h +++ b/src/lib/pubkey/pk_ops_impl.h @@ -10,6 +10,8 @@ #include +#include + namespace Botan { class HashFunction; @@ -64,7 +66,7 @@ class Verification_with_Hash : public Verification { std::string hash_function() const final; protected: - explicit Verification_with_Hash(std::string_view hash); + explicit Verification_with_Hash(const PK_Signature_Options& options); explicit Verification_with_Hash(const AlgorithmIdentifier& alg_id, std::string_view pk_algo, @@ -91,7 +93,7 @@ class Signature_with_Hash : public Signature { ~Signature_with_Hash() override; protected: - explicit Signature_with_Hash(std::string_view hash); + explicit Signature_with_Hash(const PK_Signature_Options& options); std::string hash_function() const final; diff --git a/src/lib/pubkey/pk_options.cpp b/src/lib/pubkey/pk_options.cpp new file mode 100644 index 00000000000..a645eaf0abe --- /dev/null +++ b/src/lib/pubkey/pk_options.cpp @@ -0,0 +1,142 @@ +/* +* (C) 2024,2025 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +namespace Botan { + +PK_Signature_Options::~PK_Signature_Options() = default; + +PK_Signature_Options PK_Signature_Options::with_hash(std::string_view hash) { + BOTAN_STATE_CHECK_MSG(!using_hash(), "PK_Signature_Options::with_hash cannot specify hash twice"); + auto next = (*this); + if(!hash.empty()) { + next.m_hash_fn = hash; + } + return next; +} + +PK_Signature_Options PK_Signature_Options::with_padding(std::string_view padding) { + BOTAN_STATE_CHECK_MSG(!using_padding(), "PK_Signature_Options::with_padding cannot specify padding twice"); + auto next = (*this); + if(!padding.empty()) { + next.m_padding = padding; + } + return next; +} + +PK_Signature_Options PK_Signature_Options::with_prehash(std::optional prehash_fn) { + BOTAN_STATE_CHECK_MSG(!using_prehash(), "PK_Signature_Options::with_prehash cannot specify prehash twice"); + auto next = (*this); + next.m_using_prehash = true; + next.m_prehash = std::move(prehash_fn); + return next; +} + +PK_Signature_Options PK_Signature_Options::with_provider(std::string_view provider) { + BOTAN_STATE_CHECK_MSG(provider.empty() || !using_provider(), + "PK_Signature_Options::with_provider cannot specify provider twice"); + auto next = (*this); + if(!provider.empty()) { + next.m_provider = provider; + } + return next; +} + +PK_Signature_Options PK_Signature_Options::with_context(std::span context) { + BOTAN_STATE_CHECK_MSG(!using_context(), "PK_Signature_Options::with_context cannot specify context twice"); + auto next = (*this); + next.m_context = std::vector(context.begin(), context.end()); + return next; +} + +PK_Signature_Options PK_Signature_Options::with_context(std::string_view context) { + BOTAN_STATE_CHECK_MSG(!using_context(), "PK_Signature_Options::with_context cannot specify context twice"); + auto next = (*this); + auto contextb = as_span_of_bytes(context); + next.m_context = std::vector(contextb.begin(), contextb.end()); + return next; +} + +PK_Signature_Options PK_Signature_Options::with_salt_size(size_t salt_size) { + BOTAN_STATE_CHECK_MSG(!using_salt_size(), "PK_Signature_Options::with_salt_size cannot specify salt size twice"); + auto next = (*this); + next.m_salt_size = salt_size; + return next; +} + +PK_Signature_Options PK_Signature_Options::with_deterministic_signature() { + auto next = (*this); + next.m_deterministic_sig = true; + return next; +} + +PK_Signature_Options PK_Signature_Options::with_der_encoded_signature(bool der) { + auto next = (*this); + next.m_use_der = der; + return next; +} + +PK_Signature_Options PK_Signature_Options::with_explicit_trailer_field() { + auto next = (*this); + next.m_explicit_trailer_field = true; + return next; +} + +bool PK_Signature_Options::using_provider() const { + if(auto prov = provider()) { + return !prov->empty() && *prov != "base"; + } + return false; +} + +std::string PK_Signature_Options::hash_function_name() const { + if(m_hash_fn.has_value()) { + return m_hash_fn.value(); + } + + throw Invalid_State("This signature scheme requires specifying a hash function"); +} + +std::string PK_Signature_Options::to_string() const { + std::ostringstream out; + + auto print_str = [&](std::string_view name, std::optional val) { + if(val.has_value()) { + out << name << "='" << val.value() << "' "; + } + }; + + print_str("Hash", this->hash_function()); + print_str("Padding", this->padding()); + print_str("Prehash", this->prehash_fn()); + print_str("Provider", this->provider()); + + if(auto context = this->context()) { + out << "Context=" << hex_encode(*context) << " "; + } + + if(auto salt = this->salt_size()) { + out << "SaltLen=" << *salt << " "; + } + if(this->using_der_encoded_signature()) { + out << "DerSignature "; + } + if(this->using_deterministic_signature()) { + out << "Deterministic "; + } + + return out.str(); +} + +} // namespace Botan diff --git a/src/lib/pubkey/pk_options.h b/src/lib/pubkey/pk_options.h new file mode 100644 index 00000000000..96d0be3129f --- /dev/null +++ b/src/lib/pubkey/pk_options.h @@ -0,0 +1,197 @@ +/* +* (C) 2024,2025 Jack Lloyd +* +* 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 + +namespace Botan { + +/** +* Signature generation/verification options +* +* The normal usage of this is in a builder style, eg +* +* PK_Signature_Options() +* .with_hash("SHA-256") +* .with_der_encoded_signature() +* .with_context("Foo") +*/ +class BOTAN_TEST_API PK_Signature_Options { + public: + /// Create an empty PK_Signature_Options + /// + /// This can be further parameterized by calling with_xxx functions + PK_Signature_Options() = default; + + PK_Signature_Options(PK_Signature_Options&& other) = default; + PK_Signature_Options& operator=(PK_Signature_Options&& other) = default; + + PK_Signature_Options(const PK_Signature_Options&) = default; + PK_Signature_Options& operator=(const PK_Signature_Options& other) = default; + ~PK_Signature_Options(); + + /// Format this PK_Signature_Options as a string + /// + /// This is primarily intended for debugging and error messages; + /// the format is not fixed + std::string to_string() const; + + /// Create a PK_Signature_Options specifying the hash to use + /// + /// Most but not all signture schemes require specifying the hash + /// + /// This can be further parameterized by calling with_xxx functions + explicit PK_Signature_Options(std::string_view hash_fn) : m_hash_fn(hash_fn) {} + + /// Specify the hash function to use for signing/verification + /// + /// Most, but not all, schemes require specifying a hash function. + PK_Signature_Options with_hash(std::string_view hash); + + /// Specify a padding scheme + /// + /// This is mostly/only used for RSA + /// + /// If the scheme does not support a padding option, it will throw an + /// exception when presented with such an option. + PK_Signature_Options with_padding(std::string_view padding); + + /// Specify the signature is prehashed + /// + /// Some signature schemes, such as Ed25519, normally sign the + /// entire message along with some context data. However such + /// schemes also sometimes offer a prehashing variant where the + /// message is hashed on its own, then the hash is signed. + /// + /// If given this specifies what hash function to use for prehashing. + /// If prehash is nullopt, this requests prehashing using an algorithm + /// specific default function + /// + /// If the scheme does not support prehashing, it will throw an + /// exception when presented with such an option. + PK_Signature_Options with_prehash(std::optional prehash = std::nullopt); + + /// Specify a context + /// + /// Some signature schemes allow specifying a context with the signature. + /// This is typically a fixed string that identifies a protocol or peer. + /// + /// For SM2 this context is the user identifier + /// + /// If the scheme does not support contextual identifiers, then an exception + /// will be thrown. + PK_Signature_Options with_context(std::span context); + + /// Specify a context as a string + /// + /// Equivalent to the version taking a span above; just uses the bytes + /// of the string instead. + PK_Signature_Options with_context(std::string_view context); + + /// Specify the size of salt to be used + /// + /// A small number of padding schemes (most importantly RSA-PSS) use a randomized + /// salt. This allows controlling the size of the salt that is used. + PK_Signature_Options with_salt_size(size_t salt_size); + + /// Request producing a deterministic signature + /// + /// Some signature schemes are always deterministic, or always randomized. + /// Others support both randomized or deterministic options. This allows + /// requesting this. For signatures which are always deterministic or + /// always randomized, this option has no effect. + /// + /// This option is ignored for verification + PK_Signature_Options with_deterministic_signature(); + + /// Specify producing or expecting a DER encoded signature + /// + /// This is mostly used with ECDSA + /// + /// For schemes that do not support such formatting (such as RSA + /// or post-quantum schemes), an exception will be thrown when the + /// PK_Signer or PK_Verifier is created. + PK_Signature_Options with_der_encoded_signature(bool der = true); + + /// Specify producing or expecting an explicit trailer field + /// + /// Certain RSA padding schemes, such as PSS and ISO-9796, support two + /// different trailer fields. One is an "implicit" trailer, which does not + /// directly identify the hash. The other is an "explicit" trailer, which + /// does. + /// + /// Note that currently this option is only supported by ISO-9796. While + /// some standards allow PSS to use a trailer field, others (such as RFC + /// 4055) prohibit using explicit trailers for PSS, and it is not + /// currently supported. + /// + PK_Signature_Options with_explicit_trailer_field(); + + /// Specify a provider that should be used + /// + /// This is rarely relevant + PK_Signature_Options with_provider(std::string_view provider); + + /// Return the name of the hash function to use + /// + /// This will throw an exception if no hash function was configured + std::string hash_function_name() const; + + // Getters; these are mostly for internal use + + const std::optional& hash_function() const { return m_hash_fn; } + + const std::optional& prehash_fn() const { return m_prehash; } + + const std::optional& padding() const { return m_padding; } + + const std::optional>& context() const { return m_context; } + + const std::optional& provider() const { return m_provider; } + + const std::optional& salt_size() const { return m_salt_size; } + + bool using_der_encoded_signature() const { return m_use_der; } + + bool using_deterministic_signature() const { return m_deterministic_sig; } + + bool using_explicit_trailer_field() const { return m_explicit_trailer_field; } + + bool using_hash() const { return hash_function().has_value(); } + + bool using_context() const { return context().has_value(); } + + bool using_prehash() const { return m_using_prehash; } + + bool using_padding() const { return padding().has_value(); } + + bool using_salt_size() const { return salt_size().has_value(); } + + bool using_provider() const; + + private: + std::optional m_hash_fn; + std::optional m_prehash; + std::optional m_padding; + std::optional> m_context; + std::optional m_provider; + std::optional m_salt_size; + bool m_using_prehash = false; + bool m_use_der = false; + bool m_deterministic_sig = false; + bool m_explicit_trailer_field = false; +}; + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/pk_options_impl.cpp b/src/lib/pubkey/pk_options_impl.cpp new file mode 100644 index 00000000000..a74be0b1718 --- /dev/null +++ b/src/lib/pubkey/pk_options_impl.cpp @@ -0,0 +1,214 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include + +#include +#include +#include +#include + +namespace Botan { + +PK_Signature_Options parse_legacy_sig_options(const Public_Key& key, std::string_view params) { + /* + * This is a convoluted mess because we must handle dispatch for every algorithm + * specific detail of how padding strings were formatted in versions prior to 3.6. + * + * This will all go away once the deprecated constructors of PK_Signer and PK_Verifier + * are removed in Botan4. + */ + + const std::string algo = key.algo_name(); + + if(algo.starts_with("Dilithium") || algo.starts_with("ML-DSA") || algo == "SLH-DSA" || algo == "SPHINCS+") { + if(!params.empty() && params != "Randomized" && params != "Deterministic" && params != "Pure") { + throw Invalid_Argument(fmt("Unexpected parameters '{}' for signing with {}", params, algo)); + } + + if(params == "Deterministic" || params == "Pure") { + return PK_Signature_Options().with_deterministic_signature(); + } else { + return PK_Signature_Options(); + } + } + + if(algo == "SM2") { + /* + * SM2 parameters have the following possible formats: + * Ident [since 2.2.0] + * Ident,Hash [since 2.3.0] + */ + if(params.empty()) { + return PK_Signature_Options().with_hash("SM3"); + } else { + std::string userid; + std::string hash = "SM3"; + auto comma = params.find(','); + if(comma == std::string::npos) { + userid = params; + } else { + userid = params.substr(0, comma); + hash = params.substr(comma + 1, std::string::npos); + } + return PK_Signature_Options(hash).with_context(userid); + } + } + + if(algo == "Ed25519") { + if(params.empty() || params == "Identity" || params == "Pure") { + return PK_Signature_Options(); + } else if(params == "Ed25519ph") { + return PK_Signature_Options().with_prehash(); + } else { + return PK_Signature_Options().with_prehash(std::string(params)); + } + } + + if(algo == "Ed448") { + if(params.empty() || params == "Identity" || params == "Pure" || params == "Ed448") { + return PK_Signature_Options(); + } else if(params == "Ed448ph") { + return PK_Signature_Options().with_prehash(); + } else { + return PK_Signature_Options().with_prehash(std::string(params)); + } + } + + if(algo == "RSA") { + SCAN_Name req(params); + + // handling various deprecated aliases that have accumulated over the years ... + auto padding = [](std::string_view alg) -> std::string_view { + // TODO(Botan4) Remove all but "PKCSv15" + if(alg == "EMSA_PKCS1" || alg == "EMSA-PKCS1-v1_5" || alg == "EMSA3") { + return "PKCS1v15"; + } + + // TODO(Botan4) Remove this alias + if(alg == "PSSR_Raw") { + return "PSS_Raw"; + } + + // TODO(Botan4) Remove all but "PSS" + if(alg == "PSSR" || alg == "EMSA-PSS" || alg == "PSS-MGF1" || alg == "EMSA4") { + return "PSS"; + } + + // TODO(Botan4) Remove all but "X9.31" + if(alg == "EMSA_X931" || alg == "EMSA2" || alg == "X9.31") { + return "X9.31"; + } + + return alg; + }(req.algo_name()); + + if(padding == "Raw") { + if(req.arg_count() == 0) { + return PK_Signature_Options().with_padding(padding); + } else if(req.arg_count() == 1) { + return PK_Signature_Options().with_padding(padding).with_prehash(req.arg(0)); + } + } + + if(padding == "PKCS1v15") { + if(req.arg_count() == 2 && req.arg(0) == "Raw") { + return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)).with_prehash(req.arg(1)); + } else if(req.arg_count() == 1) { + return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); + } + } + + if(padding == "PSS_Raw" || padding == "PSS") { + if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") { + auto pss_opt = PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); + + if(req.arg_count() == 3) { + return std::move(pss_opt).with_salt_size(req.arg_as_integer(2)); + } else { + return pss_opt; + } + } + } + + if(padding == "ISO_9796_DS2") { + if(req.arg_count_between(1, 3)) { + // FIXME + const bool implicit = req.arg(1, "exp") == "imp"; + + auto opt = PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); + + if(req.arg_count() == 3) { + if(implicit) { + return std::move(opt).with_salt_size(req.arg_as_integer(2)); + } else { + return std::move(opt).with_salt_size(req.arg_as_integer(2)).with_explicit_trailer_field(); + } + } else { + if(implicit) { + return opt; + } else { + return std::move(opt).with_explicit_trailer_field(); + } + } + } + } + + //ISO-9796-2 DS 3 is deterministic and DS2 without a salt + if(padding == "ISO_9796_DS3") { + if(req.arg_count_between(1, 2)) { + if(req.arg(1, "exp") == "imp") { + return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); + } else { + return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)).with_explicit_trailer_field(); + } + } + } + + if(padding == "X9.31" && req.arg_count() == 1) { + return PK_Signature_Options().with_padding(padding).with_hash(req.arg(0)); + } + } // RSA block + + if(params.empty()) { + return PK_Signature_Options(); + } + + // ECDSA/DSA/ECKCDSA/etc + auto hash = [&]() { + if(params.starts_with("EMSA1")) { + SCAN_Name req(params); + return req.arg(0); + } else { + return std::string(params); + } + }(); + + return PK_Signature_Options().with_hash(hash); +} + +void validate_for_hash_based_signature(const PK_Signature_Options& options, + std::string_view algo_name, + std::string_view hash_fn) { + if(options.using_hash()) { + if(hash_fn.empty()) { + throw Invalid_Argument(fmt("This {} key does not support explicit hash function choice", algo_name)); + } else if(options.hash_function_name() != hash_fn) { + throw Invalid_Argument( + fmt("This {} key can only be used with {}, not {}", algo_name, hash_fn, options.hash_function_name())); + } + } + + if(options.using_padding()) { + throw Invalid_Argument(fmt("{} does not support padding modes", algo_name)); + } + + if(options.using_prehash()) { + throw Invalid_Argument(fmt("{} does not support prehashing")); + } +} + +} // namespace Botan diff --git a/src/lib/pubkey/pk_options_impl.h b/src/lib/pubkey/pk_options_impl.h new file mode 100644 index 00000000000..be19da4b63f --- /dev/null +++ b/src/lib/pubkey/pk_options_impl.h @@ -0,0 +1,25 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PK_OPTIONS_IMPL_H_ +#define BOTAN_PK_OPTIONS_IMPL_H_ + +#include +#include + +namespace Botan { + +class Public_Key; + +PK_Signature_Options parse_legacy_sig_options(const Public_Key& key, std::string_view params); + +void validate_for_hash_based_signature(const PK_Signature_Options& options, + std::string_view algo_name, + std::string_view hash_fn = ""); + +} // namespace Botan + +#endif diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index edd4506f31f..53e40baa869 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) */ @@ -17,6 +17,8 @@ #include #include #include +#include +#include namespace Botan { @@ -255,15 +257,28 @@ PK_Signer::PK_Signer(const Private_Key& key, std::string_view padding, Signature_Format format, std::string_view provider) : - m_sig_format(format), m_sig_element_size(key._signature_element_size_for_DER_encoding()) { - if(m_sig_format == Signature_Format::DerSequence) { - BOTAN_ARG_CHECK(m_sig_element_size.has_value(), "This key does not support DER signatures"); + PK_Signer(key, + rng, + parse_legacy_sig_options(key, padding) + .with_der_encoded_signature(format == Signature_Format::DerSequence) + .with_provider(provider)) {} + +PK_Signer::PK_Signer(const Private_Key& key, RandomNumberGenerator& rng, const PK_Signature_Options& options) { + if(options.using_context() && !key.supports_context_data()) { + throw Invalid_Argument(fmt("Key type {} does not support context information", key.algo_name())); } - m_op = key.create_signature_op(rng, padding, provider); + 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 = options.using_der_encoded_signature() ? Signature_Format::DerSequence : Signature_Format::Standard; + m_sig_element_size = key._signature_element_size_for_DER_encoding(); + + if(m_sig_format == Signature_Format::DerSequence && !m_sig_element_size.has_value()) { + throw Invalid_Argument(fmt("Key type {} does not support DER encoded signatures", key.algo_name())); + } } AlgorithmIdentifier PK_Signer::algorithm_identifier() const { @@ -365,20 +380,27 @@ std::vector PK_Signer::signature(RandomNumberGenerator& rng) { } } -PK_Verifier::PK_Verifier(const Public_Key& key, +PK_Verifier::PK_Verifier(const Public_Key& pub_key, std::string_view padding, Signature_Format format, - std::string_view provider) { - m_op = key.create_verification_op(padding, provider); + std::string_view provider) : + PK_Verifier(pub_key, + parse_legacy_sig_options(pub_key, padding) + .with_der_encoded_signature(format == Signature_Format::DerSequence) + .with_provider(provider)) {} + +PK_Verifier::PK_Verifier(const Public_Key& key, const PK_Signature_Options& options) { + m_op = key._create_verification_op(options); + if(!m_op) { throw Invalid_Argument(fmt("Key type {} does not support signature verification", key.algo_name())); } - m_sig_format = format; m_sig_element_size = key._signature_element_size_for_DER_encoding(); + m_sig_format = options.using_der_encoded_signature() ? Signature_Format::DerSequence : Signature_Format::Standard; - if(m_sig_format == Signature_Format::DerSequence) { - BOTAN_ARG_CHECK(m_sig_element_size.has_value(), "This key does not support DER signatures"); + if(m_sig_format == Signature_Format::DerSequence && !m_sig_element_size.has_value()) { + throw Invalid_Argument(fmt("Key type {} does not support DER encoded signatures", key.algo_name())); } } diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index 52207fbdc23..e66fc9c9425 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -19,6 +19,7 @@ namespace Botan { +class PK_Signature_Options; class RandomNumberGenerator; /** @@ -168,6 +169,21 @@ class BOTAN_PUBLIC_API(2, 0) PK_Signer final { Signature_Format format = Signature_Format::Standard, std::string_view provider = ""); + /** + * Construct a PK signer + * + * @param key the key to use to generate signatures + * @param rng the random generator to use + * @param options controls the behavior of the signature generation, eg which hash function to use + * + * Note that most common algorithms (eg RSA or ECDSA) require an options + * parameter to specify at least which hash function to use. + * + * @note This function is not currently usable by applications as + * PK_Signature_Options is not a public type + */ + PK_Signer(const Private_Key& key, RandomNumberGenerator& rng, const PK_Signature_Options& options); + ~PK_Signer(); PK_Signer(const PK_Signer&) = delete; @@ -274,7 +290,7 @@ class BOTAN_PUBLIC_API(2, 0) PK_Verifier final { /** * Construct a PK Verifier. * @param pub_key the public key to verify against - * @param padding the padding/hash to use (eg "SHA-512" or "PSS(SHA-256)") + * @param padding the padding/hash to use (eg "EMSA_PKCS1(SHA-256)") * @param format the signature format to use * @param provider the provider to use */ @@ -283,6 +299,16 @@ class BOTAN_PUBLIC_API(2, 0) PK_Verifier final { Signature_Format format = Signature_Format::Standard, std::string_view provider = ""); + /** + * Construct a PK Verifier. + * @param pub_key the public key to verify against + * @param options relating to the signature + * + * @note This function is not currently usable by applications as + * PK_Signature_Options is not a public type + */ + PK_Verifier(const Public_Key& pub_key, const PK_Signature_Options& options); + /** * Construct a PK Verifier (X.509 specific) * diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp index 181f318c174..4afbc312ea4 100644 --- a/src/lib/pubkey/rsa/rsa.cpp +++ b/src/lib/pubkey/rsa/rsa.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -630,8 +631,10 @@ class RSA_Signature_Operation final : public PK_Ops::Signature, std::string hash_function() const override { return m_padding->hash_function(); } - RSA_Signature_Operation(const RSA_PrivateKey& rsa, std::string_view padding, RandomNumberGenerator& rng) : - RSA_Private_Operation(rsa, rng), m_padding(SignaturePaddingScheme::create_or_throw(padding)) {} + RSA_Signature_Operation(const RSA_PrivateKey& rsa, + const PK_Signature_Options& options, + RandomNumberGenerator& rng) : + RSA_Private_Operation(rsa, rng), m_padding(SignaturePaddingScheme::create_or_throw(options)) {} private: std::unique_ptr m_padding; @@ -737,8 +740,8 @@ class RSA_Verify_Operation final : public PK_Ops::Verification, return m_padding->verify(message_repr, msg, public_modulus_bits() - 1); } - RSA_Verify_Operation(const RSA_PublicKey& rsa, std::string_view padding) : - RSA_Public_Operation(rsa), m_padding(SignaturePaddingScheme::create_or_throw(padding)) {} + RSA_Verify_Operation(const RSA_PublicKey& rsa, const PK_Signature_Options& options) : + RSA_Public_Operation(rsa), m_padding(SignaturePaddingScheme::create_or_throw(options)) {} std::string hash_function() const override { return m_padding->hash_function(); } @@ -795,25 +798,24 @@ 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); +std::unique_ptr RSA_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options); } - - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } 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]; if(padding == "PSS") { // "MUST contain RSASSA-PSS-params" @@ -846,10 +848,16 @@ 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()); - } + return PK_Signature_Options().with_padding("PSS").with_hash(hash_algo).with_salt_size(pss_params.salt_length()); + } else { + SCAN_Name scan(padding); - return padding; + if(scan.algo_name() != "PKCS1v15") { + throw Decoding_Error("Unexpected OID for RSA signatures"); + } + + return PK_Signature_Options().with_padding("PKCS1v15").with_hash(scan.arg(0)); + } } } // namespace @@ -883,14 +891,13 @@ 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); +std::unique_ptr RSA_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options, rng); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/rsa/rsa.h b/src/lib/pubkey/rsa/rsa.h index 947759bf221..95a6df913cb 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(const 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, + const 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 14a51130247..901c77ea211 100644 --- a/src/lib/pubkey/sm2/sm2.cpp +++ b/src/lib/pubkey/sm2/sm2.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -65,8 +66,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) { @@ -90,21 +93,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, as_span_of_bytes(user_id), group, pubkey); +} + namespace { +// GM/T 0009-2012 specifies this as the default userid +// "1234567812345678"; +const std::array 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, const PK_Signature_Options& options) : m_group(sm2.domain()), m_x(sm2._private_key()), m_da_inv(sm2._get_da_inv()) { - if(hash == "Raw") { + if(options.hash_function_name() == "Raw") { // m_hash is null, m_za is empty } else { - m_hash = HashFunction::create_or_throw(hash); + auto context = + options.context().value_or(std::vector(sm2_default_userid.begin(), sm2_default_userid.end())); + + m_hash = HashFunction::create_or_throw(options.hash_function_name()); // 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); } } @@ -160,14 +184,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, const PK_Signature_Options& options) : m_group(sm2.domain()), m_gy_mul(sm2._public_ec_point()) { - if(hash == "Raw") { + if(options.hash_function_name() == "Raw") { // m_hash is null, m_za is empty } else { - m_hash = HashFunction::create_or_throw(hash); + auto context = + options.context().value_or(std::vector(sm2_default_userid.begin(), sm2_default_userid.end())); + + m_hash = HashFunction::create_or_throw(options.hash_function_name()); // 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); } } @@ -220,58 +247,28 @@ 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; - std::string hash; - parse_sm2_param_string(params, userid, hash); - return std::make_unique(*this, userid, hash); +std::unique_ptr SM2_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + if(!options.using_provider()) { + return std::make_unique(*this, options); } - - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } -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; - std::string hash; - parse_sm2_param_string(params, userid, hash); - return std::make_unique(*this, userid, hash); - } +std::unique_ptr SM2_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); - throw Provider_Not_Found(algo_name(), provider); + if(!options.using_provider()) { + return std::make_unique(*this, options); + } + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/sm2/sm2.h b/src/lib/pubkey/sm2/sm2.h index be7b78c22fc..043ecc0fc08 100644 --- a/src/lib/pubkey/sm2/sm2.h +++ b/src/lib/pubkey/sm2/sm2.h @@ -53,12 +53,13 @@ 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; } + std::optional _signature_element_size_for_DER_encoding() 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(const 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, + const 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 bca26a9f13e..3a40ffcb016 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -241,12 +242,15 @@ 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") { +std::unique_ptr SphincsPlus_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + validate_for_hash_based_signature(options, "SPHINCS+", m_public->parameters().hash_name()); + + if(!options.using_provider()) { return std::make_unique(m_public); } - throw Provider_Not_Found(algo_name(), provider); + + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr SphincsPlus_PublicKey::create_x509_verification_op( @@ -427,21 +431,20 @@ class SphincsPlus_Signature_Operation final : public PK_Ops::Signature { 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, const 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+)"); + + validate_for_hash_based_signature(options, "SPHINCS+", m_public->parameters().hash_name()); // 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") { + const bool randomized = !options.using_deterministic_signature(); + if(!options.using_provider()) { return std::make_unique(m_private, m_public, randomized); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h index def09d21ac0..31f85de9a1e 100644 --- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h +++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sphincsplus.h @@ -54,8 +54,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(const 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(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, + const 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 d8ce1633fd8..72f02d211fb 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(const 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& rng, - std::string_view params, - std::string_view provider) const override; + std::unique_ptr _create_signature_op(RandomNumberGenerator& rng, + const 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 fb219950040..3d5d103e337 100644 --- a/src/lib/pubkey/xmss/xmss_privatekey.cpp +++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -430,14 +432,17 @@ 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()) { +std::unique_ptr XMSS_PrivateKey::_create_signature_op(RandomNumberGenerator& rng, + const PK_Signature_Options& options) const { + BOTAN_UNUSED(rng); + + validate_for_hash_based_signature(options, "XMSS", this->m_private->hash().hash_function()); + + if(!options.using_provider()) { return std::make_unique(*this); } - throw Provider_Not_Found(algo_name(), provider); + throw Provider_Not_Found(algo_name(), options.provider().value()); } } // namespace Botan diff --git a/src/lib/pubkey/xmss/xmss_publickey.cpp b/src/lib/pubkey/xmss/xmss_publickey.cpp index 4a976b7d44e..315b82296b3 100644 --- a/src/lib/pubkey/xmss/xmss_publickey.cpp +++ b/src/lib/pubkey/xmss/xmss_publickey.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -100,12 +102,15 @@ 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()) { +std::unique_ptr XMSS_PublicKey::_create_verification_op( + const PK_Signature_Options& options) const { + validate_for_hash_based_signature(options, "XMSS", this->m_xmss_params.hash_function_name()); + + if(!options.using_provider()) { return std::make_unique(*this); } - throw Provider_Not_Found(algo_name(), provider); + + throw Provider_Not_Found(algo_name(), options.provider().value()); } std::unique_ptr XMSS_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id, diff --git a/src/lib/utils/assert.h b/src/lib/utils/assert.h index a698983414f..63d2823c415 100644 --- a/src/lib/utils/assert.h +++ b/src/lib/utils/assert.h @@ -54,6 +54,13 @@ namespace Botan { } \ } while(0) +#define BOTAN_STATE_CHECK_MSG(expr, msg) \ + /* NOLINTNEXTLINE(*-avoid-do-while) */ \ + do { \ + if(!(expr)) \ + Botan::throw_invalid_state(msg, __func__, __FILE__); \ + } while(0) + /** * Make an assertion */ diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp index 4847438112b..f7bd3d7b0e4 100644 --- a/src/lib/x509/x509_obj.cpp +++ b/src/lib/x509/x509_obj.cpp @@ -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 f6d780cffa4..3297b9c1c70 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 43d55ae7d49..2848c007cb5 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 ca86f48a5f3..89aeee600fb 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -18,6 +18,7 @@ #include #include #include + #include #include #include "test_pubkey.h" @@ -65,7 +66,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 = Botan::PK_Verifier(pub_key, Botan::PK_Signature_Options()); verifier.update(ref_msg.data(), ref_msg.size()); result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); @@ -147,7 +148,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 = Botan::PK_Verifier(public_key, Botan::PK_Signature_Options()); verifier.update(msg); return verifier.check_signature(signature); }; diff --git a/src/tests/test_ecdsa.cpp b/src/tests/test_ecdsa.cpp index 3b418dc292b..c8aac1da7ac 100644 --- a/src/tests/test_ecdsa.cpp +++ b/src/tests/test_ecdsa.cpp @@ -15,6 +15,7 @@ #include #include #include + #include #endif namespace Botan_Tests { @@ -280,8 +281,8 @@ class ECDSA_AllGroups_Test : public Test { } try { - Botan::PK_Signer signer(priv, rng(), hash); - Botan::PK_Verifier verifier(*pub, hash); + Botan::PK_Signer signer(priv, rng(), Botan::PK_Signature_Options().with_hash(hash)); + Botan::PK_Verifier verifier(*pub, Botan::PK_Signature_Options().with_hash(hash)); 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 4d931423425..77991a1ca6b 100644 --- a/src/tests/test_ed25519.cpp +++ b/src/tests/test_ed25519.cpp @@ -12,6 +12,7 @@ #include #include #include + #include #endif namespace Botan_Tests { @@ -88,11 +89,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"); + Botan::PK_Signer signer(*priv_key, this->rng(), Botan::PK_Signature_Options()); signer.update("message"); std::vector sig = signer.signature(this->rng()); - Botan::PK_Verifier verifier(*pub_key, "Pure"); + Botan::PK_Verifier verifier(*pub_key, Botan::PK_Signature_Options()); 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 08cd58325ea..4c60bb86141 100644 --- a/src/tests/test_hss_lms.cpp +++ b/src/tests/test_hss_lms.cpp @@ -15,6 +15,7 @@ #include #include #include + #include namespace Botan_Tests { @@ -127,8 +128,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, ""); + Botan::PK_Signer signer(*sk, Test::rng(), Botan::PK_Signature_Options()); + Botan::PK_Verifier verifier(*sk, Botan::PK_Signature_Options()); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; @@ -154,8 +155,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, ""); + Botan::PK_Signer signer(*sk, Test::rng(), Botan::PK_Signature_Options()); + Botan::PK_Verifier verifier(*sk, Botan::PK_Signature_Options()); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; @@ -240,7 +241,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(), ""); + Botan::PK_Signer signer(sk, Test::rng(), Botan::PK_Signature_Options()); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; auto sk_bytes_begin = sk.private_key_bits(); @@ -275,7 +276,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(), ""); + Botan::PK_Signer signer(sk, Test::rng(), Botan::PK_Signature_Options()); std::vector mes = {0xde, 0xad, 0xbe, 0xef}; auto sk_bytes_begin = sk.private_key_bits(); @@ -306,14 +307,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, ""); + Botan::PK_Verifier verifier(*sk, Botan::PK_Signature_Options()); 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(), ""); + Botan::PK_Signer signer(*sk, Test::rng(), Botan::PK_Signature_Options()); result.test_is_eq(signer.algorithm_identifier(), sk->algorithm_identifier()); // HSS_LMS_Signature_Operation::hash_function() diff --git a/src/tests/test_pk_pad.cpp b/src/tests/test_pk_pad.cpp index e54b41d4316..eec5e7ef301 100644 --- a/src/tests/test_pk_pad.cpp +++ b/src/tests/test_pk_pad.cpp @@ -67,85 +67,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_RSA_SIGNATURE_PADDING) -class SignaturePaddingSchemeNameTests final : public Test { - public: - std::vector run() override { - Test::Result result("SignaturePaddingScheme::name"); - - std::vector pads_need_hash = { - #if BOTAN_HAS_EMSA_X931 - "X9.31", - #endif - #if BOTAN_HAS_EMSA_PKCS1 - "PKCS1v15", - #endif - #if BOTAN_HAS_EMSA_PSSR - "PSS", - "PSS_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 - "PKCS1v15(Raw)", - "PKCS1v15(Raw,SHA-512)", - #endif - }; - - for(const auto& pad : pads_need_hash) { - try { - const std::string hash_to_use = "SHA-256"; - auto padding = Botan::SignaturePaddingScheme::create_or_throw(Botan::fmt("{}({})", pad, hash_to_use)); - auto padding_copy = Botan::SignaturePaddingScheme::create(padding->name()); - result.test_eq("SignaturePaddingScheme::name for " + pad, padding->name(), padding_copy->name()); - } catch(Botan::Lookup_Error&) { - result.test_note("Skipping test due to missing hash"); - } catch(const std::exception& e) { - result.test_failure("SignaturePaddingScheme::name for " + pad + ": " + e.what()); - } - } - - for(const auto& pad : pads_need_hash) { - std::string algo_name = pad + "(YYZ)"; - try { - auto padding = Botan::SignaturePaddingScheme::create_or_throw(algo_name); - result.test_failure("SignaturePaddingScheme::name for " + pad + ": " + - "Could create SignaturePaddingScheme with fantasy hash YYZ"); - } catch(Botan::Lookup_Error&) { - result.test_note("Skipping test due to missing hash"); - } catch(const std::exception& e) { - result.test_eq("SignaturePaddingScheme::name for " + pad, - e.what(), - "Could not find any algorithm named \"" + algo_name + "\""); - } - } - - for(const auto& pad : pads_no_hash) { - try { - auto padding = Botan::SignaturePaddingScheme::create(pad); - auto padding_copy = Botan::SignaturePaddingScheme::create(padding->name()); - result.test_eq("SignaturePaddingScheme::name for " + pad, padding->name(), padding_copy->name()); - } catch(Botan::Lookup_Error&) { - result.test_note("Skipping test due to missing hash"); - } catch(const std::exception& e) { - result.test_failure("SignaturePaddingScheme::name for " + pad + ": " + e.what()); - } - } - - return {result}; - } -}; - -BOTAN_REGISTER_TEST("pubkey", "sig_padding_name", SignaturePaddingSchemeNameTests); - -#endif - } // namespace Botan_Tests diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp index fc7db02063d..58256260418 100644 --- a/src/tests/test_pubkey.cpp +++ b/src/tests/test_pubkey.cpp @@ -107,7 +107,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 0237d1f2a47..31100694b1b 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 8a3ffc22301..73f8020c49e 100644 --- a/src/tests/test_rsa.cpp +++ b/src/tests/test_rsa.cpp @@ -12,6 +12,7 @@ #include #include #include + #include #endif namespace Botan_Tests { @@ -205,9 +206,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 + Botan::PK_Signer signer(rsa, this->rng(), Botan::PK_Signature_Options().with_hash("Raw")); + Botan::PK_Verifier verifier(rsa, Botan::PK_Signature_Options().with_hash("Raw")); for(size_t i = 1; i <= Botan::Blinder::ReinitInterval * 6; ++i) { std::vector input(16); diff --git a/src/tests/test_xmss.cpp b/src/tests/test_xmss.cpp index 58ed48e382d..5e406a8cde7 100644 --- a/src/tests/test_xmss.cpp +++ b/src/tests/test_xmss.cpp @@ -15,6 +15,7 @@ #include #include #include + #include #include #endif @@ -43,7 +44,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 +63,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 +81,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"); @@ -145,7 +158,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"); + Botan::PK_Signer signer(sk, *rng, Botan::PK_Signature_Options()); signer.sign_message(msg, *rng); }; @@ -267,34 +280,35 @@ 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* const 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); + Botan::PK_Signer signer(legacy_secret_key, *rng, Botan::PK_Signature_Options()); auto signature = signer.sign_message(message, *rng); - Botan::PK_Verifier verifier(*public_key_from_secret_key, algo_name); + Botan::PK_Verifier verifier(*public_key_from_secret_key, Botan::PK_Signature_Options()); + 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); + Botan::PK_Verifier verifier(*public_key_from_secret_key, Botan::PK_Signature_Options()); + 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); + Botan::PK_Signer signer(legacy_secret_key, *rng, Botan::PK_Signature_Options()); auto signature = signer.sign_message(message, *rng); - Botan::PK_Verifier verifier(legacy_public_key, algo_name); + Botan::PK_Verifier verifier(legacy_public_key, Botan::PK_Signature_Options()); result.confirm("legacy private key generates signatures that are still verifiable", verifier.verify_message(message, legacy_signature)); }),