From b79a5577137cb6499b080ddd932f63e42b92860a Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 10 Oct 2024 08:36:33 -0400 Subject: [PATCH 1/7] Refactor performance test of PK signature algorithms --- src/cli/perf.h | 2 + src/cli/perf_pk_sig.cpp | 381 ++++++++++++++++++++++++++++++++++++++++ src/cli/speed.cpp | 365 +------------------------------------- 3 files changed, 391 insertions(+), 357 deletions(-) create mode 100644 src/cli/perf_pk_sig.cpp diff --git a/src/cli/perf.h b/src/cli/perf.h index 0da322b623..6d7a158a90 100644 --- a/src/cli/perf.h +++ b/src/cli/perf.h @@ -26,6 +26,8 @@ class PerfConfig { //virtual std::vector providers() const = 0; + virtual const std::vector& ecc_groups() const = 0; + virtual const std::vector& buffer_sizes() const = 0; virtual std::ostream& error_output() const = 0; diff --git a/src/cli/perf_pk_sig.cpp b/src/cli/perf_pk_sig.cpp new file mode 100644 index 0000000000..800f6a3cd0 --- /dev/null +++ b/src/cli/perf_pk_sig.cpp @@ -0,0 +1,381 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include + #include +#endif + +namespace Botan_CLI { + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + +class PerfTest_PKSig : public PerfTest { + public: + virtual std::string algo() const = 0; + + virtual std::string hash() const { return "SHA-256"; } + + virtual std::vector keygen_params(const PerfConfig& config) const { + BOTAN_UNUSED(config); + return {""}; + } + + virtual std::string format_name(const std::string& alg, const std::string& param) const { + return param.empty() ? alg : Botan::fmt("{}-{}", alg, param); + } + + void go(const PerfConfig& config) override { + const std::string alg = this->algo(); + const std::string padding = this->hash(); + + const auto params = this->keygen_params(config); + + for(const auto& param : params) { + const std::string nm = this->format_name(alg, param); + bench_pk_sig(config, nm, alg, param, padding); + } + } + + void bench_pk_sig(const PerfConfig& config, + const std::string& nm, + const std::string& alg, + const std::string& param, + const std::string& padding, + const std::string& provider = "") { + auto& rng = config.rng(); + const auto msec = config.runtime(); + + auto keygen_timer = config.make_timer(nm, 1, "keygen"); + + auto key = keygen_timer->run([&] { return Botan::create_private_key(alg, rng, param); }); + ; + + if(key != nullptr) { + config.record_result(*keygen_timer); + + std::vector message, signature, bad_signature; + + Botan::PK_Signer sig(*key, rng, padding, Botan::Signature_Format::Standard, provider); + Botan::PK_Verifier ver(*key, padding, Botan::Signature_Format::Standard, provider); + + auto sig_timer = config.make_timer(nm, 1, "sign"); + auto ver_timer = config.make_timer(nm, 1, "verify"); + + size_t invalid_sigs = 0; + + while(ver_timer->under(msec) || sig_timer->under(msec)) { + if(signature.empty() || sig_timer->under(msec)) { + /* + Length here is kind of arbitrary, but 48 bytes fits into a single + hash block so minimizes hashing overhead versus the PK op itself. + */ + rng.random_vec(message, 48); + + signature = sig_timer->run([&]() { return sig.sign_message(message, rng); }); + + bad_signature = signature; + bad_signature[rng.next_byte() % bad_signature.size()] ^= rng.next_nonzero_byte(); + } + + if(ver_timer->under(msec)) { + const bool verified = ver_timer->run([&] { return ver.verify_message(message, signature); }); + + if(!verified) { + invalid_sigs += 1; + } + + const bool verified_bad = ver_timer->run([&] { return ver.verify_message(message, bad_signature); }); + + if(verified_bad) { + config.error_output() << "Bad signature accepted in " << nm << " signature bench\n"; + } + } + } + + if(invalid_sigs > 0) { + config.error_output() << invalid_sigs << " generated signatures rejected in " << nm + << " signature bench\n"; + } + + config.record_result(*sig_timer); + config.record_result(*ver_timer); + } + } +}; + +#endif + +#if defined(BOTAN_HAS_DSA) + +class PerfTest_DSA final : public PerfTest_PKSig { + public: + std::string algo() const override { return "DSA"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return {"dsa/jce/1024", "dsa/botan/2048", "dsa/botan/3072"}; + } + + std::string format_name(const std::string& alg, const std::string& param) const override { + return Botan::fmt("{}-{}", alg, param.substr(param.find_last_of('/') + 1)); + } +}; + +BOTAN_REGISTER_PERF_TEST("DSA", PerfTest_DSA); + +#endif + +#if defined(BOTAN_HAS_RSA) + +class PerfTest_RSA final : public PerfTest_PKSig { + public: + std::string algo() const override { return "RSA"; } + + std::string hash() const override { return "PKCS1v15(SHA-256)"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return {"1024", "2048", "3072", "4096"}; + } +}; + +BOTAN_REGISTER_PERF_TEST("RSA", PerfTest_RSA); + +#endif + +#if defined(BOTAN_HAS_ECDSA) + +class PerfTest_ECDSA final : public PerfTest_PKSig { + public: + std::string algo() const override { return "ECDSA"; } + + std::vector keygen_params(const PerfConfig& config) const override { return config.ecc_groups(); } +}; + +BOTAN_REGISTER_PERF_TEST("ECDSA", PerfTest_ECDSA); + +#endif + +#if defined(BOTAN_HAS_ECKCDSA) + +class PerfTest_ECKCDSA final : public PerfTest_PKSig { + public: + std::string algo() const override { return "ECKCDSA"; } + + std::vector keygen_params(const PerfConfig& config) const override { return config.ecc_groups(); } +}; + +BOTAN_REGISTER_PERF_TEST("ECKCDSA", PerfTest_ECKCDSA); + +#endif + +#if defined(BOTAN_HAS_ECGDSA) + +class PerfTest_ECGDSA final : public PerfTest_PKSig { + public: + std::string algo() const override { return "ECGDSA"; } + + std::vector keygen_params(const PerfConfig& config) const override { return config.ecc_groups(); } +}; + +BOTAN_REGISTER_PERF_TEST("ECGDSA", PerfTest_ECGDSA); + +#endif + +#if defined(BOTAN_HAS_GOST_34_10_2001) && defined(BOTAN_HAS_GOST_34_11) + +class PerfTest_Gost3410 final : public PerfTest_PKSig { + public: + std::string algo() const override { return "GOST-34.10"; } + + std::string hash() const override { return "GOST-34.11"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return {"gost_256A"}; + } +}; + +BOTAN_REGISTER_PERF_TEST("GOST-34.10", PerfTest_Gost3410); + +#endif + +#if defined(BOTAN_HAS_SM2) && defined(BOTAN_HAS_SM3) + +class PerfTest_SM2 final : public PerfTest_PKSig { + public: + std::string algo() const override { return "SM2"; } + + std::string hash() const override { return "SM3"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return {"sm2p256v1"}; + } +}; + +BOTAN_REGISTER_PERF_TEST("SM2", PerfTest_SM2); + +#endif + +#if defined(BOTAN_HAS_ED25519) + +class PerfTest_Ed25519 final : public PerfTest_PKSig { + public: + std::string algo() const override { return "Ed25519"; } + + std::string hash() const override { return "Pure"; } +}; + +BOTAN_REGISTER_PERF_TEST("Ed25519", PerfTest_Ed25519); + +#endif + +#if defined(BOTAN_HAS_ED448) + +class PerfTest_Ed448 final : public PerfTest_PKSig { + public: + std::string algo() const override { return "Ed448"; } + + std::string hash() const override { return "Pure"; } +}; + +BOTAN_REGISTER_PERF_TEST("Ed448", PerfTest_Ed448); + +#endif + +#if defined(BOTAN_HAS_XMSS_RFC8391) + +class PerfTest_XMSS final : public PerfTest_PKSig { + public: + std::string algo() const override { return "XMSS"; } + + std::string hash() const override { return ""; } + + std::string format_name(const std::string& alg, const std::string& param) const override { + BOTAN_UNUSED(alg); + return param; // Param already has XMSS in the name... + } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + + /* + We only test H10 signatures here since already they are quite slow (a + few seconds per signature). On a fast machine, H16 signatures take 1-2 + minutes to generate and H20 signatures take 5-10 minutes to generate + */ + return { + "XMSS-SHA2_10_256", + "XMSS-SHAKE_10_256", + "XMSS-SHA2_10_512", + "XMSS-SHAKE_10_512", + }; + } +}; + +BOTAN_REGISTER_PERF_TEST("XMSS", PerfTest_XMSS); + +#endif + +#if defined(BOTAN_HAS_HSS_LMS) + +class PerfTest_HSS_LMS final : public PerfTest_PKSig { + public: + std::string algo() const override { return "HSS-LMS"; } + + std::string hash() const override { return ""; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + + // At first we compare instances with multiple hash functions. LMS trees with + // height 10 are suitable, since they can be used for enough signatures and are + // fast enough for speed testing. + // Afterward, setups with multiple HSS layers are tested + return {"SHA-256,HW(10,1)", + "SHAKE-256(256),HW(10,1)", + "SHAKE-256(192),HW(10,1)", + "Truncated(SHA-256,192),HW(10,1)", + "SHA-256,HW(10,1),HW(10,1)", + "SHA-256,HW(10,1),HW(10,1),HW(10,1)"}; + } +}; + +BOTAN_REGISTER_PERF_TEST("HSS-LMS", PerfTest_HSS_LMS); + +#endif + +#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) + +class PerfTest_SPHINCSp final : public PerfTest_PKSig { + public: + std::string algo() const override { return "SPHINCS+"; } + + std::string hash() const override { return ""; } + + std::string format_name(const std::string& alg, const std::string& param) const override { + BOTAN_UNUSED(alg); + return param; // Param already has algo in the string + } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + + return {"SphincsPlus-sha2-128s-r3.1", + "SphincsPlus-sha2-128f-r3.1", + "SphincsPlus-sha2-192s-r3.1", + "SphincsPlus-sha2-192f-r3.1", + "SphincsPlus-sha2-256s-r3.1", + "SphincsPlus-sha2-256f-r3.1", + "SphincsPlus-shake-128s-r3.1", + "SphincsPlus-shake-128f-r3.1", + "SphincsPlus-shake-192s-r3.1", + "SphincsPlus-shake-192f-r3.1", + "SphincsPlus-shake-256s-r3.1", + "SphincsPlus-shake-256f-r3.1"}; + } +}; + +BOTAN_REGISTER_PERF_TEST("SPHINCS+", PerfTest_SPHINCSp); + +#endif + +#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES) + +class PerfTest_Dilithium final : public PerfTest_PKSig { + public: + std::string algo() const override { return "Dilithium"; } + + std::string hash() const override { return ""; } + + std::string format_name(const std::string& alg, const std::string& param) const override { + BOTAN_UNUSED(alg); + return param; // Param already has algo in the string + } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + + return { + "Dilithium-4x4-r3", + "Dilithium-4x4-AES-r3", + "Dilithium-6x5-r3", + "Dilithium-6x5-AES-r3", + "Dilithium-8x7-r3", + "Dilithium-8x7-AES-r3", + }; + } +}; + +BOTAN_REGISTER_PERF_TEST("Dilithium", PerfTest_Dilithium); + +#endif + +} // namespace Botan_CLI diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 518214cedb..dd3928396d 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -72,18 +72,6 @@ #include #endif -#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES) - #include -#endif - -#if defined(BOTAN_HAS_HSS_LMS) - #include -#endif - -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) - #include -#endif - #if defined(BOTAN_HAS_FRODOKEM) #include #endif @@ -434,12 +422,15 @@ class Speed final : public Command { class PerfConfig_Cli final : public PerfConfig { public: PerfConfig_Cli(std::chrono::milliseconds runtime, + const std::vector& ecc_groups, const std::vector& buffer_sizes, Speed* speed) : - m_runtime(runtime), m_buffer_sizes(buffer_sizes), m_speed(speed) {} + m_runtime(runtime), m_ecc_groups(ecc_groups), m_buffer_sizes(buffer_sizes), m_speed(speed) {} const std::vector& buffer_sizes() const override { return m_buffer_sizes; } + const std::vector& ecc_groups() const override { return m_ecc_groups; } + std::chrono::milliseconds runtime() const override { return m_runtime; } std::ostream& error_output() const override { return m_speed->error_output(); } @@ -458,11 +449,12 @@ class Speed final : public Command { private: std::chrono::milliseconds m_runtime; + std::vector m_ecc_groups; std::vector m_buffer_sizes; Speed* m_speed; }; - PerfConfig_Cli perf_config(msec, buf_sizes, this); + PerfConfig_Cli perf_config(msec, ecc_groups, buf_sizes, this); for(const auto& algo : algos) { using namespace std::placeholders; @@ -507,9 +499,7 @@ class Speed final : public Command { } #endif #if defined(BOTAN_HAS_RSA) - else if(algo == "RSA") { - bench_rsa(provider, msec); - } else if(algo == "RSA_keygen") { + else if(algo == "RSA_keygen") { bench_rsa_keygen(provider, msec); } #endif @@ -525,52 +515,15 @@ class Speed final : public Command { #endif #if defined(BOTAN_HAS_ECDSA) - else if(algo == "ECDSA") { - bench_ecdsa(ecc_groups, provider, msec); - } else if(algo == "ecdsa_recovery") { + else if(algo == "ecdsa_recovery") { bench_ecdsa_recovery(ecc_groups, provider, msec); } #endif -#if defined(BOTAN_HAS_SM2) - else if(algo == "SM2") { - bench_sm2(ecc_groups, provider, msec); - } -#endif -#if defined(BOTAN_HAS_ECKCDSA) - else if(algo == "ECKCDSA") { - bench_eckcdsa(ecc_groups, provider, msec); - } -#endif -#if defined(BOTAN_HAS_GOST_34_10_2001) - else if(algo == "GOST-34.10") { - bench_gost_3410(provider, msec); - } -#endif -#if defined(BOTAN_HAS_ECGDSA) - else if(algo == "ECGDSA") { - bench_ecgdsa(ecc_groups, provider, msec); - } -#endif -#if defined(BOTAN_HAS_ED25519) - else if(algo == "Ed25519") { - bench_ed25519(provider, msec); - } -#endif -#if defined(BOTAN_HAS_ED448) - else if(algo == "Ed448") { - bench_ed448(provider, msec); - } -#endif #if defined(BOTAN_HAS_DIFFIE_HELLMAN) else if(algo == "DH") { bench_dh(provider, msec); } #endif -#if defined(BOTAN_HAS_DSA) - else if(algo == "DSA") { - bench_dsa(provider, msec); - } -#endif #if defined(BOTAN_HAS_ELGAMAL) else if(algo == "ElGamal") { bench_elgamal(provider, msec); @@ -601,26 +554,6 @@ class Speed final : public Command { bench_kyber(provider, msec); } #endif -#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES) - else if(algo == "Dilithium") { - bench_dilithium(provider, msec); - } -#endif -#if defined(BOTAN_HAS_XMSS_RFC8391) - else if(algo == "XMSS") { - bench_xmss(provider, msec); - } -#endif -#if defined(BOTAN_HAS_HSS_LMS) - else if(algo == "HSS-LMS") { - bench_hss_lms(provider, msec); - } -#endif -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) - else if(algo == "SPHINCS+") { - bench_sphincs_plus(provider, msec); - } -#endif #if defined(BOTAN_HAS_FRODOKEM) else if(algo == "FrodoKEM") { bench_frodokem(provider, msec); @@ -1316,79 +1249,6 @@ class Speed final : public Command { record_result(kem_dec_timer); } - void bench_pk_sig_ecc(const std::string& algo, - const std::string& emsa, - const std::string& provider, - const std::vector& params, - std::chrono::milliseconds msec) { - for(std::string grp : params) { - const std::string nm = grp.empty() ? algo : Botan::fmt("{}-{}", algo, grp); - - auto keygen_timer = make_timer(nm, provider, "keygen"); - - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), grp); })); - - record_result(keygen_timer); - bench_pk_sig(*key, nm, provider, emsa, msec); - } - } - - size_t bench_pk_sig(const Botan::Private_Key& key, - const std::string& nm, - const std::string& provider, - const std::string& padding, - std::chrono::milliseconds msec) { - std::vector message, signature, bad_signature; - - Botan::PK_Signer sig(key, rng(), padding, Botan::Signature_Format::Standard, provider); - Botan::PK_Verifier ver(key, padding, Botan::Signature_Format::Standard, provider); - - auto sig_timer = make_timer(nm + " " + padding, provider, "sign"); - auto ver_timer = make_timer(nm + " " + padding, provider, "verify"); - - size_t invalid_sigs = 0; - - while(ver_timer->under(msec) || sig_timer->under(msec)) { - if(signature.empty() || sig_timer->under(msec)) { - /* - Length here is kind of arbitrary, but 48 bytes fits into a single - hash block so minimizes hashing overhead versus the PK op itself. - */ - rng().random_vec(message, 48); - - signature = sig_timer->run([&]() { return sig.sign_message(message, rng()); }); - - bad_signature = signature; - bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte(); - } - - if(ver_timer->under(msec)) { - const bool verified = ver_timer->run([&] { return ver.verify_message(message, signature); }); - - if(!verified) { - invalid_sigs += 1; - } - - const bool verified_bad = ver_timer->run([&] { return ver.verify_message(message, bad_signature); }); - - if(verified_bad) { - error_output() << "Bad signature accepted in " << nm << " signature bench\n"; - } - } - } - - if(invalid_sigs > 0) { - error_output() << invalid_sigs << " generated signatures rejected in " << nm << " signature bench\n"; - } - - const size_t events = static_cast(std::min(sig_timer->events(), ver_timer->events())); - - record_result(sig_timer); - record_result(ver_timer); - - return events; - } #endif #if defined(BOTAN_HAS_RSA) @@ -1407,35 +1267,9 @@ class Speed final : public Command { record_result(keygen_timer); } } - - void bench_rsa(const std::string& provider, std::chrono::milliseconds msec) { - for(size_t keylen : {1024, 2048, 3072, 4096}) { - const std::string nm = "RSA-" + std::to_string(keylen); - - auto keygen_timer = make_timer(nm, provider, "keygen"); - - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key("RSA", rng(), std::to_string(keylen)); })); - - record_result(keygen_timer); - - // Using PKCS #1 padding so OpenSSL provider can play along - bench_pk_sig(*key, nm, provider, "EMSA-PKCS1-v1_5(SHA-256)", msec); - - //bench_pk_sig(*key, nm, provider, "PSSR(SHA-256)", msec); - //bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec); - //bench_pk_enc(*key, nm, provider, "OAEP(SHA-1)", msec); - } - } #endif #if defined(BOTAN_HAS_ECDSA) - void bench_ecdsa(const std::vector& groups, - const std::string& provider, - std::chrono::milliseconds msec) { - return bench_pk_sig_ecc("ECDSA", "SHA-256", provider, groups, msec); - } - void bench_ecdsa_recovery(const std::vector& groups, const std::string& /*unused*/, std::chrono::milliseconds msec) { @@ -1474,48 +1308,6 @@ class Speed final : public Command { #endif -#if defined(BOTAN_HAS_ECKCDSA) - void bench_eckcdsa(const std::vector& groups, - const std::string& provider, - std::chrono::milliseconds msec) { - return bench_pk_sig_ecc("ECKCDSA", "SHA-256", provider, groups, msec); - } -#endif - -#if defined(BOTAN_HAS_GOST_34_10_2001) - void bench_gost_3410(const std::string& provider, std::chrono::milliseconds msec) { - return bench_pk_sig_ecc("GOST-34.10", "GOST-34.11", provider, {"gost_256A"}, msec); - } -#endif - -#if defined(BOTAN_HAS_SM2) - void bench_sm2(const std::vector& groups, - const std::string& provider, - std::chrono::milliseconds msec) { - return bench_pk_sig_ecc("SM2_Sig", "SM3", provider, groups, msec); - } -#endif - -#if defined(BOTAN_HAS_ECGDSA) - void bench_ecgdsa(const std::vector& groups, - const std::string& provider, - std::chrono::milliseconds msec) { - return bench_pk_sig_ecc("ECGDSA", "SHA-256", provider, groups, msec); - } -#endif - -#if defined(BOTAN_HAS_ED25519) - void bench_ed25519(const std::string& provider, std::chrono::milliseconds msec) { - return bench_pk_sig_ecc("Ed25519", "Pure", provider, std::vector{""}, msec); - } -#endif - -#if defined(BOTAN_HAS_ED448) - void bench_ed448(const std::string& provider, std::chrono::milliseconds msec) { - return bench_pk_sig_ecc("Ed448", "Pure", provider, std::vector{""}, msec); - } -#endif - #if defined(BOTAN_HAS_DIFFIE_HELLMAN) void bench_dh(const std::string& provider, std::chrono::milliseconds msec) { for(size_t bits : {2048, 3072, 4096, 6144, 8192}) { @@ -1524,25 +1316,6 @@ class Speed final : public Command { } #endif -#if defined(BOTAN_HAS_DSA) - void bench_dsa(const std::string& provider, std::chrono::milliseconds msec) { - for(size_t bits : {1024, 2048, 3072}) { - const std::string nm = "DSA-" + std::to_string(bits); - - const std::string params = (bits == 1024) ? "dsa/jce/1024" : ("dsa/botan/" + std::to_string(bits)); - - auto keygen_timer = make_timer(nm, provider, "keygen"); - - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key("DSA", rng(), params); })); - - record_result(keygen_timer); - - bench_pk_sig(*key, nm, provider, "SHA-256", msec); - } - } -#endif - #if defined(BOTAN_HAS_ELGAMAL) void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec) { for(size_t keylen : {1024, 2048, 3072, 4096}) { @@ -1651,73 +1424,6 @@ class Speed final : public Command { } #endif -#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES) - void bench_dilithium(const std::string& provider, std::chrono::milliseconds msec) { - const Botan::DilithiumMode::Mode all_modes[] = {Botan::DilithiumMode::Dilithium4x4, - Botan::DilithiumMode::Dilithium4x4_AES, - Botan::DilithiumMode::Dilithium6x5, - Botan::DilithiumMode::Dilithium6x5_AES, - Botan::DilithiumMode::Dilithium8x7, - Botan::DilithiumMode::Dilithium8x7_AES}; - - for(auto modet : all_modes) { - Botan::DilithiumMode mode(modet); - - #if !defined(BOTAN_HAS_DILITHIUM) - if(mode.is_modern()) - continue; - #endif - - #if !defined(BOTAN_HAS_DILITHIUM_AES) - if(mode.is_aes()) - continue; - #endif - - auto keygen_timer = make_timer(mode.to_string(), provider, "keygen"); - - auto key = keygen_timer->run([&] { return Botan::Dilithium_PrivateKey(rng(), mode); }); - - record_result(keygen_timer); - - bench_pk_sig(key, mode.to_string(), provider, "", msec); - } - } -#endif - -#if defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHA2) || defined(BOTAN_HAS_SPHINCS_PLUS_WITH_SHAKE) - void bench_sphincs_plus(const std::string& provider, std::chrono::milliseconds msec) { - // Sphincs_Parameter_Set set, Sphincs_Hash_Type hash - std::vector sphincs_params{"SphincsPlus-sha2-128s-r3.1", - "SphincsPlus-sha2-128f-r3.1", - "SphincsPlus-sha2-192s-r3.1", - "SphincsPlus-sha2-192f-r3.1", - "SphincsPlus-sha2-256s-r3.1", - "SphincsPlus-sha2-256f-r3.1", - "SphincsPlus-shake-128s-r3.1", - "SphincsPlus-shake-128f-r3.1", - "SphincsPlus-shake-192s-r3.1", - "SphincsPlus-shake-192f-r3.1", - "SphincsPlus-shake-256s-r3.1", - "SphincsPlus-shake-256f-r3.1"}; - - for(auto params : sphincs_params) { - try { - auto keygen_timer = make_timer(params, provider, "keygen"); - - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key("SPHINCS+", rng(), params); })); - - record_result(keygen_timer); - if(bench_pk_sig(*key, params, provider, "", msec) == 1) { - break; - } - } catch(Botan::Not_Implemented&) { - continue; - } - } - } -#endif - #if defined(BOTAN_HAS_FRODOKEM) void bench_frodokem(const std::string& provider, std::chrono::milliseconds msec) { std::vector frodo_modes{ @@ -1752,61 +1458,6 @@ class Speed final : public Command { } } #endif - -#if defined(BOTAN_HAS_XMSS_RFC8391) - void bench_xmss(const std::string& provider, std::chrono::milliseconds msec) { - /* - We only test H10 signatures here since already they are quite slow (a - few seconds per signature). On a fast machine, H16 signatures take 1-2 - minutes to generate and H20 signatures take 5-10 minutes to generate - */ - std::vector xmss_params{ - "XMSS-SHA2_10_256", - "XMSS-SHAKE_10_256", - "XMSS-SHA2_10_512", - "XMSS-SHAKE_10_512", - }; - - for(std::string params : xmss_params) { - auto keygen_timer = make_timer(params, provider, "keygen"); - - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key("XMSS", rng(), params); })); - - record_result(keygen_timer); - if(bench_pk_sig(*key, params, provider, "", msec) == 1) { - break; - } - } - } -#endif - -#if defined(BOTAN_HAS_HSS_LMS) - void bench_hss_lms(const std::string& provider, std::chrono::milliseconds msec) { - // At first we compare instances with multiple hash functions. LMS trees with - // height 10 are suitable, since they can be used for enough signatures and are - // fast enough for speed testing. - // Afterward, setups with multiple HSS layers are tested - std::vector hss_lms_instances{"SHA-256,HW(10,1)", - "SHAKE-256(256),HW(10,1)", - "SHAKE-256(192),HW(10,1)", - "Truncated(SHA-256,192),HW(10,1)", - "SHA-256,HW(10,1),HW(10,1)", - "SHA-256,HW(10,1),HW(10,1),HW(10,1)"}; - - for(const auto& params : hss_lms_instances) { - auto keygen_timer = make_timer(params, provider, "keygen"); - - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key("HSS-LMS", rng(), params); })); - - record_result(keygen_timer); - if(bench_pk_sig(*key, params, provider, "", msec) == 1) { - break; - } - } - } -#endif }; BOTAN_REGISTER_COMMAND("speed", Speed); From 2e83143e6b1df1b75efa413dff4427247f5dbc32 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 10 Oct 2024 12:29:24 -0400 Subject: [PATCH 2/7] Refactor performance test of PK key agreement schemes --- src/cli/perf_pk_ka.cpp | 147 +++++++++++++++++++++++++++++++++++++++++ src/cli/speed.cpp | 89 ------------------------- 2 files changed, 147 insertions(+), 89 deletions(-) create mode 100644 src/cli/perf_pk_ka.cpp diff --git a/src/cli/perf_pk_ka.cpp b/src/cli/perf_pk_ka.cpp new file mode 100644 index 0000000000..444b073c2b --- /dev/null +++ b/src/cli/perf_pk_ka.cpp @@ -0,0 +1,147 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include + #include +#endif + +namespace Botan_CLI { + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + +class PerfTest_PKKa : public PerfTest { + public: + virtual std::string algo() const = 0; + + virtual std::vector keygen_params(const PerfConfig& config) const { + BOTAN_UNUSED(config); + return {""}; + } + + virtual std::string format_name(const std::string& alg, const std::string& param) const { + return param.empty() ? alg : Botan::fmt("{}-{}", alg, param); + } + + void go(const PerfConfig& config) override { + const std::string alg = this->algo(); + + const auto params = this->keygen_params(config); + + for(const auto& param : params) { + const std::string nm = this->format_name(alg, param); + bench_pk_ka(config, nm, alg, param); + } + } + + void bench_pk_ka(const PerfConfig& config, + const std::string& nm, + const std::string& algo, + const std::string& params, + const std::string& provider = "") { + const auto msec = config.runtime(); + + const std::string kdf = "KDF2(SHA-256)"; // arbitrary choice + + auto keygen_timer = config.make_timer(nm, 1, "keygen"); + + auto& rng = config.rng(); + + auto key1 = keygen_timer->run([&] { return Botan::create_private_key(algo, rng, params); }); + auto key2 = keygen_timer->run([&] { return Botan::create_private_key(algo, rng, params); }); + + config.record_result(*keygen_timer); + + const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast(*key1); + const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast(*key2); + + Botan::PK_Key_Agreement ka1(ka_key1, rng, kdf, provider); + Botan::PK_Key_Agreement ka2(ka_key2, rng, kdf, provider); + + const std::vector ka1_pub = ka_key1.public_value(); + const std::vector ka2_pub = ka_key2.public_value(); + + auto ka_timer = config.make_timer(nm, 1, "key agreements"); + + while(ka_timer->under(msec)) { + auto k1 = ka_timer->run([&]() { return ka1.derive_key(32, ka2_pub); }); + auto k2 = ka_timer->run([&]() { return ka2.derive_key(32, ka1_pub); }); + + if(k1 != k2) { + config.error_output() << "Key agreement mismatch in PK bench\n"; + } + } + + config.record_result(*ka_timer); + } +}; + +#endif + +#if defined(BOTAN_HAS_DIFFIE_HELLMAN) + +class PerfTest_DH final : public PerfTest_PKKa { + public: + std::string algo() const override { return "DH"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return { + "ffdhe/ietf/2048", + "ffdhe/ietf/3072", + "ffdhe/ietf/4096", + "ffdhe/ietf/6144", + "ffdhe/ietf/8192", + }; + } + + std::string format_name(const std::string& alg, const std::string& param) const override { + return Botan::fmt("{}-{}", alg, param.substr(param.find_last_of('/') + 1)); + } +}; + +BOTAN_REGISTER_PERF_TEST("DH", PerfTest_DH); + +#endif + +#if defined(BOTAN_HAS_ECDH) + +class PerfTest_ECDH final : public PerfTest_PKKa { + public: + std::string algo() const override { return "ECDH"; } + + std::vector keygen_params(const PerfConfig& config) const override { return config.ecc_groups(); } +}; + +BOTAN_REGISTER_PERF_TEST("ECDH", PerfTest_ECDH); + +#endif + +#if defined(BOTAN_HAS_X25519) + +class PerfTest_X25519 final : public PerfTest_PKKa { + public: + std::string algo() const override { return "X25519"; } +}; + +BOTAN_REGISTER_PERF_TEST("X25519", PerfTest_X25519); + +#endif + +#if defined(BOTAN_HAS_X448) + +class PerfTest_X448 final : public PerfTest_PKKa { + public: + std::string algo() const override { return "X448"; } +}; + +BOTAN_REGISTER_PERF_TEST("X448", PerfTest_X448); + +#endif + +} // namespace Botan_CLI diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index dd3928396d..564a4137e4 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -519,31 +519,11 @@ class Speed final : public Command { bench_ecdsa_recovery(ecc_groups, provider, msec); } #endif -#if defined(BOTAN_HAS_DIFFIE_HELLMAN) - else if(algo == "DH") { - bench_dh(provider, msec); - } -#endif #if defined(BOTAN_HAS_ELGAMAL) else if(algo == "ElGamal") { bench_elgamal(provider, msec); } #endif -#if defined(BOTAN_HAS_ECDH) - else if(algo == "ECDH") { - bench_ecdh(ecc_groups, provider, msec); - } -#endif -#if defined(BOTAN_HAS_X25519) - else if(algo == "X25519") { - bench_x25519(provider, msec); - } -#endif -#if defined(BOTAN_HAS_X448) - else if(algo == "X448") { - bench_x448(provider, msec); - } -#endif #if defined(BOTAN_HAS_MCELIECE) else if(algo == "McEliece") { bench_mceliece(provider, msec); @@ -1179,45 +1159,6 @@ class Speed final : public Command { record_result(dec_timer); } - void bench_pk_ka(const std::string& algo, - const std::string& nm, - const std::string& params, - const std::string& provider, - std::chrono::milliseconds msec) { - const std::string kdf = "KDF2(SHA-256)"; // arbitrary choice - - auto keygen_timer = make_timer(nm, provider, "keygen"); - - std::unique_ptr key1( - keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); })); - std::unique_ptr key2( - keygen_timer->run([&] { return Botan::create_private_key(algo, rng(), params); })); - - record_result(keygen_timer); - - const Botan::PK_Key_Agreement_Key& ka_key1 = dynamic_cast(*key1); - const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast(*key2); - - Botan::PK_Key_Agreement ka1(ka_key1, rng(), kdf, provider); - Botan::PK_Key_Agreement ka2(ka_key2, rng(), kdf, provider); - - const std::vector ka1_pub = ka_key1.public_value(); - const std::vector ka2_pub = ka_key2.public_value(); - - auto ka_timer = make_timer(nm, provider, "key agreements"); - - while(ka_timer->under(msec)) { - Botan::SymmetricKey symkey1 = ka_timer->run([&]() { return ka1.derive_key(32, ka2_pub); }); - Botan::SymmetricKey symkey2 = ka_timer->run([&]() { return ka2.derive_key(32, ka1_pub); }); - - if(symkey1 != symkey2) { - error_output() << "Key agreement mismatch in PK bench\n"; - } - } - - record_result(ka_timer); - } - void bench_pk_kem(const Botan::Private_Key& key, const std::string& nm, const std::string& provider, @@ -1308,14 +1249,6 @@ class Speed final : public Command { #endif -#if defined(BOTAN_HAS_DIFFIE_HELLMAN) - void bench_dh(const std::string& provider, std::chrono::milliseconds msec) { - for(size_t bits : {2048, 3072, 4096, 6144, 8192}) { - bench_pk_ka("DH", "DH-" + std::to_string(bits), "ffdhe/ietf/" + std::to_string(bits), provider, msec); - } - } -#endif - #if defined(BOTAN_HAS_ELGAMAL) void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec) { for(size_t keylen : {1024, 2048, 3072, 4096}) { @@ -1335,28 +1268,6 @@ class Speed final : public Command { } #endif -#if defined(BOTAN_HAS_ECDH) - void bench_ecdh(const std::vector& groups, - const std::string& provider, - std::chrono::milliseconds msec) { - for(const std::string& grp : groups) { - bench_pk_ka("ECDH", "ECDH-" + grp, grp, provider, msec); - } - } -#endif - -#if defined(BOTAN_HAS_X25519) - void bench_x25519(const std::string& provider, std::chrono::milliseconds msec) { - bench_pk_ka("X25519", "X25519", "", provider, msec); - } -#endif - -#if defined(BOTAN_HAS_X448) - void bench_x448(const std::string& provider, std::chrono::milliseconds msec) { - bench_pk_ka("X448", "X448", "", provider, msec); - } -#endif - #if defined(BOTAN_HAS_MCELIECE) void bench_mceliece(const std::string& provider, std::chrono::milliseconds msec) { /* From 2f82c262bb57577b20a1a750315789b94040c500 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 10 Oct 2024 13:21:27 -0400 Subject: [PATCH 3/7] Refactor performance test of PK KEM schemes --- src/cli/perf_pk_kem.cpp | 137 ++++++++++++++++++++++++++++++++++ src/cli/speed.cpp | 161 ---------------------------------------- src/scripts/test_cli.py | 2 +- 3 files changed, 138 insertions(+), 162 deletions(-) create mode 100644 src/cli/perf_pk_kem.cpp diff --git a/src/cli/perf_pk_kem.cpp b/src/cli/perf_pk_kem.cpp new file mode 100644 index 0000000000..b16ebb358d --- /dev/null +++ b/src/cli/perf_pk_kem.cpp @@ -0,0 +1,137 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include + #include +#endif + +namespace Botan_CLI { + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + +class PerfTest_PK_KEM : public PerfTest { + public: + virtual std::string algo() const = 0; + + virtual std::vector keygen_params(const PerfConfig& config) const { + BOTAN_UNUSED(config); + return {""}; + } + + virtual std::string format_name(const std::string& alg, const std::string& param) const { + return param.empty() ? alg : Botan::fmt("{}-{}", alg, param); + } + + void go(const PerfConfig& config) override { + const std::string alg = this->algo(); + + const auto params = this->keygen_params(config); + + for(const auto& param : params) { + const std::string nm = this->format_name(alg, param); + bench_pk_kem(config, nm, alg, param); + } + } + + void bench_pk_kem(const PerfConfig& config, + const std::string& nm, + const std::string& algo, + const std::string& params, + const std::string& provider = "") { + const auto msec = config.runtime(); + auto& rng = config.rng(); + + const std::string kdf = "KDF2(SHA-256)"; // arbitrary choice + + auto keygen_timer = config.make_timer(nm, 1, "keygen"); + + auto key = keygen_timer->run([&] { return Botan::create_private_key(algo, rng, params); }); + + Botan::PK_KEM_Decryptor dec(*key, rng, kdf, provider); + Botan::PK_KEM_Encryptor enc(*key, kdf, provider); + + auto kem_enc_timer = config.make_timer(nm, 1, "KEM encrypt"); + auto kem_dec_timer = config.make_timer(nm, 1, "KEM decrypt"); + + while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec)) { + Botan::secure_vector salt = rng.random_vec(16); + + kem_enc_timer->start(); + const auto kem_result = enc.encrypt(rng, 64, salt); + kem_enc_timer->stop(); + + kem_dec_timer->start(); + Botan::secure_vector dec_shared_key = dec.decrypt(kem_result.encapsulated_shared_key(), 64, salt); + kem_dec_timer->stop(); + + if(kem_result.shared_key() != dec_shared_key) { + config.error_output() << "KEM mismatch in PK bench\n"; + } + } + + config.record_result(*kem_enc_timer); + config.record_result(*kem_dec_timer); + } +}; + +#endif + +#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S) + +class PerfTest_Kyber final : public PerfTest_PK_KEM { + public: + std::string algo() const override { return "Kyber"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return { + "Kyber-512-r3", + "Kyber-512-90s-r3", + "Kyber-768-r3", + "Kyber-768-90s-r3", + "Kyber-1024-r3", + "Kyber-1024-90s-r3", + }; + } +}; + +BOTAN_REGISTER_PERF_TEST("Kyber", PerfTest_Kyber); + +#endif + +#if defined(BOTAN_HAS_FRODOKEM) + +class PerfTest_FrodoKEM final : public PerfTest_PK_KEM { + public: + std::string algo() const override { return "FrodoKEM"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return { + "FrodoKEM-640-SHAKE", + "FrodoKEM-640-AES", + "eFrodoKEM-640-SHAKE", + "eFrodoKEM-640-AES", + "FrodoKEM-976-SHAKE", + "FrodoKEM-976-AES", + "eFrodoKEM-976-SHAKE", + "eFrodoKEM-976-AES", + "FrodoKEM-1344-SHAKE", + "FrodoKEM-1344-AES", + "eFrodoKEM-1344-SHAKE", + "eFrodoKEM-1344-AES", + }; + } +}; + +BOTAN_REGISTER_PERF_TEST("FrodoKEM", PerfTest_FrodoKEM); + +#endif + +} // namespace Botan_CLI diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 564a4137e4..7b816deac0 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -64,18 +64,6 @@ #include #endif -#if defined(BOTAN_HAS_MCELIECE) - #include -#endif - -#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S) - #include -#endif - -#if defined(BOTAN_HAS_FRODOKEM) - #include -#endif - #if defined(BOTAN_HAS_ECDSA) #include #endif @@ -336,7 +324,6 @@ class Speed final : public Command { "Ed448", "X25519", "X448", - "McEliece", "Kyber", "SPHINCS+", "FrodoKEM", @@ -524,21 +511,6 @@ class Speed final : public Command { bench_elgamal(provider, msec); } #endif -#if defined(BOTAN_HAS_MCELIECE) - else if(algo == "McEliece") { - bench_mceliece(provider, msec); - } -#endif -#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S) - else if(algo == "Kyber") { - bench_kyber(provider, msec); - } -#endif -#if defined(BOTAN_HAS_FRODOKEM) - else if(algo == "FrodoKEM") { - bench_frodokem(provider, msec); - } -#endif #if defined(BOTAN_HAS_ECC_GROUP) else if(algo == "ecc_mult") { @@ -1159,37 +1131,6 @@ class Speed final : public Command { record_result(dec_timer); } - void bench_pk_kem(const Botan::Private_Key& key, - const std::string& nm, - const std::string& provider, - const std::string& kdf, - std::chrono::milliseconds msec) { - Botan::PK_KEM_Decryptor dec(key, rng(), kdf, provider); - Botan::PK_KEM_Encryptor enc(key, kdf, provider); - - auto kem_enc_timer = make_timer(nm, provider, "KEM encrypt"); - auto kem_dec_timer = make_timer(nm, provider, "KEM decrypt"); - - while(kem_enc_timer->under(msec) && kem_dec_timer->under(msec)) { - Botan::secure_vector salt = rng().random_vec(16); - - kem_enc_timer->start(); - const auto kem_result = enc.encrypt(rng(), 64, salt); - kem_enc_timer->stop(); - - kem_dec_timer->start(); - Botan::secure_vector dec_shared_key = dec.decrypt(kem_result.encapsulated_shared_key(), 64, salt); - kem_dec_timer->stop(); - - if(kem_result.shared_key() != dec_shared_key) { - error_output() << "KEM mismatch in PK bench\n"; - } - } - - record_result(kem_enc_timer); - record_result(kem_dec_timer); - } - #endif #if defined(BOTAN_HAS_RSA) @@ -1267,108 +1208,6 @@ class Speed final : public Command { } } #endif - -#if defined(BOTAN_HAS_MCELIECE) - void bench_mceliece(const std::string& provider, std::chrono::milliseconds msec) { - /* - SL=80 n=1632 t=33 - 59 KB pubkey 140 KB privkey - SL=107 n=2480 t=45 - 128 KB pubkey 300 KB privkey - SL=128 n=2960 t=57 - 195 KB pubkey 459 KB privkey - SL=147 n=3408 t=67 - 265 KB pubkey 622 KB privkey - SL=191 n=4624 t=95 - 516 KB pubkey 1234 KB privkey - SL=256 n=6624 t=115 - 942 KB pubkey 2184 KB privkey - */ - - const std::vector> mce_params = { - {2480, 45}, {2960, 57}, {3408, 67}, {4624, 95}, {6624, 115}}; - - for(auto params : mce_params) { - size_t n = params.first; - size_t t = params.second; - - const std::string nm = "McEliece-" + std::to_string(n) + "," + std::to_string(t) + - " (WF=" + std::to_string(Botan::mceliece_work_factor(n, t)) + ")"; - - auto keygen_timer = make_timer(nm, provider, "keygen"); - - std::unique_ptr key = - keygen_timer->run([&] { return std::make_unique(rng(), n, t); }); - - record_result(keygen_timer); - bench_pk_kem(*key, nm, provider, "KDF2(SHA-256)", msec); - } - } -#endif - -#if defined(BOTAN_HAS_KYBER) || defined(BOTAN_HAS_KYBER_90S) - void bench_kyber(const std::string& provider, std::chrono::milliseconds msec) { - const Botan::KyberMode::Mode all_modes[] = { - Botan::KyberMode::Kyber512_R3, - Botan::KyberMode::Kyber512_90s, - Botan::KyberMode::Kyber768_R3, - Botan::KyberMode::Kyber768_90s, - Botan::KyberMode::Kyber1024_R3, - Botan::KyberMode::Kyber1024_90s, - }; - - for(auto modet : all_modes) { - Botan::KyberMode mode(modet); - - #if !defined(BOTAN_HAS_KYBER) - if(mode.is_modern()) - continue; - #endif - - #if !defined(BOTAN_HAS_KYBER_90S) - if(mode.is_90s()) - continue; - #endif - - auto keygen_timer = make_timer(mode.to_string(), provider, "keygen"); - - auto key = keygen_timer->run([&] { return Botan::Kyber_PrivateKey(rng(), mode); }); - - record_result(keygen_timer); - - bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec); - } - } -#endif - -#if defined(BOTAN_HAS_FRODOKEM) - void bench_frodokem(const std::string& provider, std::chrono::milliseconds msec) { - std::vector frodo_modes{ - Botan::FrodoKEMMode::FrodoKEM640_SHAKE, - Botan::FrodoKEMMode::FrodoKEM976_SHAKE, - Botan::FrodoKEMMode::FrodoKEM1344_SHAKE, - Botan::FrodoKEMMode::eFrodoKEM640_SHAKE, - Botan::FrodoKEMMode::eFrodoKEM976_SHAKE, - Botan::FrodoKEMMode::eFrodoKEM1344_SHAKE, - Botan::FrodoKEMMode::FrodoKEM640_AES, - Botan::FrodoKEMMode::FrodoKEM976_AES, - Botan::FrodoKEMMode::FrodoKEM1344_AES, - Botan::FrodoKEMMode::eFrodoKEM640_AES, - Botan::FrodoKEMMode::eFrodoKEM976_AES, - Botan::FrodoKEMMode::eFrodoKEM1344_AES, - }; - - for(auto modet : frodo_modes) { - if(!modet.is_available()) { - continue; - } - - Botan::FrodoKEMMode mode(modet); - - auto keygen_timer = make_timer(mode.to_string(), provider, "keygen"); - - auto key = keygen_timer->run([&] { return Botan::FrodoKEM_PrivateKey(rng(), mode); }); - - record_result(keygen_timer); - - bench_pk_kem(key, mode.to_string(), provider, "KDF2(SHA-256)", msec); - } - } -#endif }; BOTAN_REGISTER_COMMAND("speed", Speed); diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py index 48ad55650e..da2f6fe378 100755 --- a/src/scripts/test_cli.py +++ b/src/scripts/test_cli.py @@ -1622,7 +1622,7 @@ def cli_speed_pk_tests(_tmp_dir): msec = 1 pk_algos = ["ECDSA", "ECDH", "SM2", "ECKCDSA", "ECGDSA", "GOST-34.10", - "DH", "DSA", "ElGamal", "Ed25519", "Ed448", "X25519", "X448", "McEliece", + "DH", "DSA", "ElGamal", "Ed25519", "Ed448", "X25519", "X448", "RSA", "RSA_keygen", "XMSS", "ec_h2c", "Kyber", "Dilithium", "SPHINCS+"] From 910b92c09750541a2b61802e2304e83fd4604932 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 10 Oct 2024 13:59:05 -0400 Subject: [PATCH 4/7] Refactor performance test of PK encryption schemes --- src/cli/perf_pk_enc.cpp | 114 ++++++++++++++++++++++++++++++++++++++++ src/cli/speed.cpp | 61 --------------------- 2 files changed, 114 insertions(+), 61 deletions(-) create mode 100644 src/cli/perf_pk_enc.cpp diff --git a/src/cli/perf_pk_enc.cpp b/src/cli/perf_pk_enc.cpp new file mode 100644 index 0000000000..fa13dc788b --- /dev/null +++ b/src/cli/perf_pk_enc.cpp @@ -0,0 +1,114 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include + #include +#endif + +namespace Botan_CLI { + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + +class PerfTest_PKEnc : public PerfTest { + public: + virtual std::string algo() const = 0; + + virtual std::vector keygen_params(const PerfConfig& config) const { + BOTAN_UNUSED(config); + return {""}; + } + + virtual std::string format_name(const std::string& alg, const std::string& param) const { + return param.empty() ? alg : Botan::fmt("{}-{}", alg, param); + } + + void go(const PerfConfig& config) override { + const std::string alg = this->algo(); + + const auto params = this->keygen_params(config); + + for(const auto& param : params) { + const std::string nm = this->format_name(alg, param); + bench_pk_ka(config, nm, alg, param); + } + } + + void bench_pk_ka(const PerfConfig& config, + const std::string& nm, + const std::string& algo, + const std::string& params, + const std::string& provider = "") { + auto& rng = config.rng(); + const auto msec = config.runtime(); + + std::vector plaintext, ciphertext; + + auto keygen_timer = config.make_timer(nm, 1, "keygen"); + + auto key = keygen_timer->run([&] { return Botan::create_private_key(algo, rng, params); }); + + // TODO this would have to be generalized for anything but RSA/ElGamal + const std::string padding = "PKCS1v15"; + + Botan::PK_Encryptor_EME enc(*key, rng, padding, provider); + Botan::PK_Decryptor_EME dec(*key, rng, padding, provider); + + auto enc_timer = config.make_timer(nm + " " + padding, 1, "encrypt"); + auto dec_timer = config.make_timer(nm + " " + padding, 1, "decrypt"); + + while(enc_timer->under(msec) || dec_timer->under(msec)) { + // Generate a new random ciphertext to decrypt + if(ciphertext.empty() || enc_timer->under(msec)) { + rng.random_vec(plaintext, enc.maximum_input_size()); + ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng); }); + } + + if(dec_timer->under(msec)) { + const auto dec_pt = dec_timer->run([&]() { return dec.decrypt(ciphertext); }); + + // sanity check + if(!(Botan::unlock(dec_pt) == plaintext)) { + config.error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n"; + } + } + } + + config.record_result(*enc_timer); + config.record_result(*dec_timer); + } +}; + +#endif + +#if defined(BOTAN_HAS_ELGAMAL) + +class PerfTest_ElGamal final : public PerfTest_PKEnc { + public: + std::string algo() const override { return "ElGamal"; } + + std::vector keygen_params(const PerfConfig& config) const override { + BOTAN_UNUSED(config); + return { + "modp/ietf/1024", + "modp/ietf/2048", + "modp/ietf/3072", + "modp/ietf/4096", + }; + } + + std::string format_name(const std::string& alg, const std::string& param) const override { + return Botan::fmt("{}-{}", alg, param.substr(param.find_last_of('/') + 1)); + } +}; + +BOTAN_REGISTER_PERF_TEST("ElGamal", PerfTest_ElGamal); + +#endif + +} // namespace Botan_CLI diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 7b816deac0..1e9be50ae7 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -506,11 +506,6 @@ class Speed final : public Command { bench_ecdsa_recovery(ecc_groups, provider, msec); } #endif -#if defined(BOTAN_HAS_ELGAMAL) - else if(algo == "ElGamal") { - bench_elgamal(provider, msec); - } -#endif #if defined(BOTAN_HAS_ECC_GROUP) else if(algo == "ecc_mult") { @@ -1096,43 +1091,6 @@ class Speed final : public Command { #endif -#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) - void bench_pk_enc(const Botan::Private_Key& key, - const std::string& nm, - const std::string& provider, - const std::string& padding, - std::chrono::milliseconds msec) { - std::vector plaintext, ciphertext; - - Botan::PK_Encryptor_EME enc(key, rng(), padding, provider); - Botan::PK_Decryptor_EME dec(key, rng(), padding, provider); - - auto enc_timer = make_timer(nm + " " + padding, provider, "encrypt"); - auto dec_timer = make_timer(nm + " " + padding, provider, "decrypt"); - - while(enc_timer->under(msec) || dec_timer->under(msec)) { - // Generate a new random ciphertext to decrypt - if(ciphertext.empty() || enc_timer->under(msec)) { - rng().random_vec(plaintext, enc.maximum_input_size()); - ciphertext = enc_timer->run([&]() { return enc.encrypt(plaintext, rng()); }); - } - - if(dec_timer->under(msec)) { - const auto dec_pt = dec_timer->run([&]() { return dec.decrypt(ciphertext); }); - - if(!(Botan::unlock(dec_pt) == plaintext)) // sanity check - { - error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n"; - } - } - } - - record_result(enc_timer); - record_result(dec_timer); - } - -#endif - #if defined(BOTAN_HAS_RSA) void bench_rsa_keygen(const std::string& provider, std::chrono::milliseconds msec) { for(size_t keylen : {1024, 2048, 3072, 4096}) { @@ -1189,25 +1147,6 @@ class Speed final : public Command { } #endif - -#if defined(BOTAN_HAS_ELGAMAL) - void bench_elgamal(const std::string& provider, std::chrono::milliseconds msec) { - for(size_t keylen : {1024, 2048, 3072, 4096}) { - const std::string nm = "ElGamal-" + std::to_string(keylen); - - const std::string params = "modp/ietf/" + std::to_string(keylen); - - auto keygen_timer = make_timer(nm, provider, "keygen"); - - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key("ElGamal", rng(), params); })); - - record_result(keygen_timer); - - bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec); - } - } -#endif }; BOTAN_REGISTER_COMMAND("speed", Speed); From 3530fb04a431bc4e78e91970a192f49ed7be64e3 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 10 Oct 2024 14:05:58 -0400 Subject: [PATCH 5/7] Refactor performance test of PK misc operations --- src/cli/perf_pk_misc.cpp | 94 ++++++++++++++++++++++++++++++++++++++++ src/cli/speed.cpp | 80 ---------------------------------- 2 files changed, 94 insertions(+), 80 deletions(-) create mode 100644 src/cli/perf_pk_misc.cpp diff --git a/src/cli/perf_pk_misc.cpp b/src/cli/perf_pk_misc.cpp new file mode 100644 index 0000000000..12089d9f02 --- /dev/null +++ b/src/cli/perf_pk_misc.cpp @@ -0,0 +1,94 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" + +#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) + #include +#endif + +#if defined(BOTAN_HAS_ECDSA) + #include + #include +#endif + +namespace Botan_CLI { + +#if defined(BOTAN_HAS_RSA) + +class PerfTest_RSAKeyGen final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto runtime = config.runtime(); + auto& rng = config.rng(); + + for(size_t keylen : {1024, 2048, 3072, 4096}) { + const std::string nm = Botan::fmt("RSA-{}", keylen); + auto keygen_timer = config.make_timer(nm, 1, "keygen"); + + while(keygen_timer->under(runtime)) { + auto key = + keygen_timer->run([&] { return Botan::create_private_key("RSA", rng, std::to_string(keylen)); }); + + BOTAN_ASSERT(key->check_key(rng, true), "Key is ok"); + } + + config.record_result(*keygen_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("RSA_keygen", PerfTest_RSAKeyGen); + +#endif + +#if defined(BOTAN_HAS_ECDSA) + +class PerfTest_ECDSAKeyRec final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto runtime = config.runtime(); + auto& rng = config.rng(); + + for(const std::string& group_name : config.ecc_groups()) { + const auto group = Botan::EC_Group::from_name(group_name); + auto recovery_timer = config.make_timer("ECDSA recovery " + group_name); + + while(recovery_timer->under(runtime)) { + Botan::ECDSA_PrivateKey key(rng, group); + + std::vector message(group.get_order_bits() / 8); + rng.randomize(message.data(), message.size()); + + Botan::PK_Signer signer(key, rng, "Raw"); + signer.update(message); + std::vector signature = signer.signature(rng); + + Botan::PK_Verifier verifier(key, "Raw", Botan::Signature_Format::Standard, "base"); + verifier.update(message); + BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature"); + + Botan::BigInt r(signature.data(), signature.size() / 2); + Botan::BigInt s(signature.data() + signature.size() / 2, signature.size() / 2); + + const uint8_t v = key.recovery_param(message, r, s); + + recovery_timer->run([&]() { + Botan::ECDSA_PublicKey pubkey(group, message, r, s, v); + BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key"); + }); + } + + config.record_result(*recovery_timer); + } + } +}; + +BOTAN_REGISTER_PERF_TEST("ecdsa_recovery", PerfTest_ECDSAKeyRec); + +#endif + +} // namespace Botan_CLI diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 1e9be50ae7..16cca96d50 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -17,7 +17,6 @@ #include // Always available: -#include #include #include #include @@ -49,13 +48,6 @@ #include #endif -#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) - #include - #include - #include - #include -#endif - #if defined(BOTAN_HAS_ECC_GROUP) #include #endif @@ -64,10 +56,6 @@ #include #endif -#if defined(BOTAN_HAS_ECDSA) - #include -#endif - namespace Botan_CLI { using Botan::Timer; @@ -485,11 +473,6 @@ class Speed final : public Command { algo, provider, msec, buf_sizes, std::bind(&Speed::bench_mac, this, _1, _2, _3, _4)); } #endif -#if defined(BOTAN_HAS_RSA) - else if(algo == "RSA_keygen") { - bench_rsa_keygen(provider, msec); - } -#endif #if defined(BOTAN_HAS_PCURVES) else if(algo == "ECDSA-pcurves") { @@ -501,12 +484,6 @@ class Speed final : public Command { } #endif -#if defined(BOTAN_HAS_ECDSA) - else if(algo == "ecdsa_recovery") { - bench_ecdsa_recovery(ecc_groups, provider, msec); - } -#endif - #if defined(BOTAN_HAS_ECC_GROUP) else if(algo == "ecc_mult") { bench_ecc_mult(ecc_groups, msec); @@ -1089,63 +1066,6 @@ class Speed final : public Command { } } -#endif - -#if defined(BOTAN_HAS_RSA) - void bench_rsa_keygen(const std::string& provider, std::chrono::milliseconds msec) { - for(size_t keylen : {1024, 2048, 3072, 4096}) { - const std::string nm = "RSA-" + std::to_string(keylen); - auto keygen_timer = make_timer(nm, provider, "keygen"); - - while(keygen_timer->under(msec)) { - std::unique_ptr key( - keygen_timer->run([&] { return Botan::create_private_key("RSA", rng(), std::to_string(keylen)); })); - - BOTAN_ASSERT(key->check_key(rng(), true), "Key is ok"); - } - - record_result(keygen_timer); - } - } -#endif - -#if defined(BOTAN_HAS_ECDSA) - void bench_ecdsa_recovery(const std::vector& groups, - const std::string& /*unused*/, - std::chrono::milliseconds msec) { - for(const std::string& group_name : groups) { - const auto group = Botan::EC_Group::from_name(group_name); - auto recovery_timer = make_timer("ECDSA recovery " + group_name); - - while(recovery_timer->under(msec)) { - Botan::ECDSA_PrivateKey key(rng(), group); - - std::vector message(group.get_order_bits() / 8); - rng().randomize(message.data(), message.size()); - - Botan::PK_Signer signer(key, rng(), "Raw"); - signer.update(message); - std::vector signature = signer.signature(rng()); - - Botan::PK_Verifier verifier(key, "Raw", Botan::Signature_Format::Standard, "base"); - verifier.update(message); - BOTAN_ASSERT(verifier.check_signature(signature), "Valid signature"); - - Botan::BigInt r(signature.data(), signature.size() / 2); - Botan::BigInt s(signature.data() + signature.size() / 2, signature.size() / 2); - - const uint8_t v = key.recovery_param(message, r, s); - - recovery_timer->run([&]() { - Botan::ECDSA_PublicKey pubkey(group, message, r, s, v); - BOTAN_ASSERT(pubkey.public_point() == key.public_point(), "Recovered public key"); - }); - } - - record_result(recovery_timer); - } - } - #endif }; From 81bb3ab5634564669085e01229a2552130b10999 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 10 Oct 2024 15:04:32 -0400 Subject: [PATCH 6/7] Consolidate the various EC related benchmarks Also remove the pcurves ECDH/ECDSA benchmarks which are not relevant anymore, now that EC_Group/EC_Scalar/EC_AffinePoint use pcurves internally. --- src/cli/perf_ec.cpp | 104 ++++++++++++++ src/cli/speed.cpp | 304 +--------------------------------------- src/scripts/test_cli.py | 7 +- 3 files changed, 108 insertions(+), 307 deletions(-) create mode 100644 src/cli/perf_ec.cpp diff --git a/src/cli/perf_ec.cpp b/src/cli/perf_ec.cpp new file mode 100644 index 0000000000..81b23b959f --- /dev/null +++ b/src/cli/perf_ec.cpp @@ -0,0 +1,104 @@ +/* +* (C) 2024 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "perf.h" + +#if defined(BOTAN_HAS_ECC_GROUP) + #include +#endif + +namespace Botan_CLI { + +#if defined(BOTAN_HAS_ECC_GROUP) + +class PerfTest_EllipticCurve final : public PerfTest { + public: + void go(const PerfConfig& config) override { + const auto run = config.runtime(); + auto& rng = config.rng(); + + for(const auto& group_name : config.ecc_groups()) { + auto init_timer = config.make_timer(group_name + " initialization"); + + while(init_timer->under(run)) { + Botan::EC_Group::clear_registered_curve_data(); + init_timer->run([&]() { Botan::EC_Group::from_name(group_name); }); + } + + config.record_result(*init_timer); + + const auto group = Botan::EC_Group::from_name(group_name); + + auto bp_timer = config.make_timer(group_name + " base point mul"); + auto vp_timer = config.make_timer(group_name + " variable point mul"); + auto der_uc_timer = config.make_timer(group_name + " point deserialize (uncompressed)"); + auto der_c_timer = config.make_timer(group_name + " point deserialize (compressed)"); + auto mul2_setup_timer = config.make_timer(group_name + " mul2 setup"); + auto mul2_timer = config.make_timer(group_name + " mul2"); + auto scalar_inv_timer = config.make_timer(group_name + " scalar inversion"); + auto h2c_nu_timer = config.make_timer(group_name + " hash to curve (NU)"); + auto h2c_ro_timer = config.make_timer(group_name + " hash to curve (RO)"); + + std::vector ws; + + auto g = Botan::EC_AffinePoint::generator(group); + + const bool h2c_supported = [&]() { + try { + Botan::EC_AffinePoint::hash_to_curve_nu(group, "SHA-256", {}, {}); + } catch(Botan::Not_Implemented&) { + return false; + } + return true; + }(); + + while(bp_timer->under(run) && vp_timer->under(run)) { + const auto k = Botan::EC_Scalar::random(group, rng); + const auto r1 = bp_timer->run([&]() { return Botan::EC_AffinePoint::g_mul(k, rng, ws); }); + const auto r2 = vp_timer->run([&]() { return g.mul(k, rng, ws); }); + + const auto r1_bytes = r1.serialize_uncompressed(); + const auto r2_bytes = r2.serialize_uncompressed(); + BOTAN_ASSERT_EQUAL(r1_bytes, r2_bytes, "Same result for multiplication"); + + der_uc_timer->run([&]() { Botan::EC_AffinePoint::deserialize(group, r1_bytes); }); + + const auto r1_cbytes = r1.serialize_compressed(); + der_c_timer->run([&]() { Botan::EC_AffinePoint::deserialize(group, r1_cbytes); }); + + auto mul2 = mul2_setup_timer->run([&]() { return Botan::EC_Group::Mul2Table(r1); }); + + auto k_inv = scalar_inv_timer->run([&]() { return k.invert(); }); + + auto pt = mul2_timer->run([&]() { return mul2.mul2_vartime(k, k_inv); }); + + if(h2c_supported) { + h2c_nu_timer->run([&]() { Botan::EC_AffinePoint::hash_to_curve_nu(group, "SHA-256", r1_bytes, {}); }); + h2c_ro_timer->run([&]() { Botan::EC_AffinePoint::hash_to_curve_ro(group, "SHA-256", r1_bytes, {}); }); + } + } + + config.record_result(*bp_timer); + config.record_result(*vp_timer); + config.record_result(*mul2_setup_timer); + config.record_result(*mul2_timer); + config.record_result(*scalar_inv_timer); + config.record_result(*der_uc_timer); + config.record_result(*der_c_timer); + + if(h2c_supported) { + config.record_result(*h2c_nu_timer); + config.record_result(*h2c_ro_timer); + } + } + } +}; + +BOTAN_REGISTER_PERF_TEST("ecc", PerfTest_EllipticCurve); + +#endif + +} // namespace Botan_CLI diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 16cca96d50..e2e195d586 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -1,5 +1,5 @@ /* -* (C) 2009,2010,2014,2015,2017,2018 Jack Lloyd +* (C) 2009,2010,2014,2015,2017,2018,2024 Jack Lloyd * (C) 2015 Simon Warta (Kullo GmbH) * * Botan is released under the Simplified BSD License (see license.txt) @@ -52,10 +52,6 @@ #include #endif -#if defined(BOTAN_HAS_PCURVES) - #include -#endif - namespace Botan_CLI { using Botan::Timer; @@ -473,31 +469,6 @@ class Speed final : public Command { algo, provider, msec, buf_sizes, std::bind(&Speed::bench_mac, this, _1, _2, _3, _4)); } #endif - -#if defined(BOTAN_HAS_PCURVES) - else if(algo == "ECDSA-pcurves") { - bench_pcurve_ecdsa(ecc_groups, msec); - } else if(algo == "ECDH-pcurves") { - bench_pcurve_ecdh(ecc_groups, msec); - } else if(algo == "pcurves") { - bench_pcurves(ecc_groups, msec); - } -#endif - -#if defined(BOTAN_HAS_ECC_GROUP) - else if(algo == "ecc_mult") { - bench_ecc_mult(ecc_groups, msec); - } else if(algo == "ecc_init") { - bench_ecc_init(ecc_groups, msec); - } else if(algo == "os2ecp") { - bench_os2ecp(ecc_groups, msec); - } -#endif -#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) - else if(algo == "ec_h2c") { - bench_ec_h2c(msec); - } -#endif else { if(verbose() || !using_defaults) { error_output() << "Unknown algorithm '" << algo << "'\n"; @@ -794,279 +765,6 @@ class Speed final : public Command { } } #endif - -#if defined(BOTAN_HAS_ECC_GROUP) - void bench_ecc_init(const std::vector& groups, const std::chrono::milliseconds runtime) { - for(std::string group_name : groups) { - auto timer = make_timer(group_name + " initialization"); - - while(timer->under(runtime)) { - Botan::EC_Group::clear_registered_curve_data(); - timer->run([&]() { Botan::EC_Group::from_name(group_name); }); - } - - record_result(timer); - } - } - - void bench_ecc_mult(const std::vector& groups, const std::chrono::milliseconds runtime) { - for(const std::string& group_name : groups) { - const auto group = Botan::EC_Group::from_name(group_name); - - auto bp_timer = make_timer(group_name + " base point"); - auto vp_timer = make_timer(group_name + " variable point"); - - std::vector ws; - - auto g = Botan::EC_AffinePoint::generator(group); - - while(bp_timer->under(runtime) && vp_timer->under(runtime)) { - const auto k = Botan::EC_Scalar::random(group, rng()); - - const auto r1 = bp_timer->run([&]() { return Botan::EC_AffinePoint::g_mul(k, rng(), ws); }); - - const auto r2 = vp_timer->run([&]() { return g.mul(k, rng(), ws); }); - - BOTAN_ASSERT_EQUAL( - r1.serialize_uncompressed(), r2.serialize_uncompressed(), "Same result for multiplication"); - } - - record_result(bp_timer); - record_result(vp_timer); - } - } - - void bench_os2ecp(const std::vector& groups, const std::chrono::milliseconds runtime) { - for(const std::string& group_name : groups) { - auto uncmp_timer = make_timer("OS2ECP uncompressed " + group_name); - auto cmp_timer = make_timer("OS2ECP compressed " + group_name); - - const auto ec_group = Botan::EC_Group::from_name(group_name); - - while(uncmp_timer->under(runtime) && cmp_timer->under(runtime)) { - const Botan::BigInt k(rng(), 256); - const Botan::EC_Point p = ec_group.get_base_point() * k; - const std::vector os_cmp = p.encode(Botan::EC_Point_Format::Compressed); - const std::vector os_uncmp = p.encode(Botan::EC_Point_Format::Uncompressed); - - uncmp_timer->run([&]() { ec_group.OS2ECP(os_uncmp); }); - cmp_timer->run([&]() { ec_group.OS2ECP(os_cmp); }); - } - - record_result(uncmp_timer); - record_result(cmp_timer); - } - } - -#endif - -#if defined(BOTAN_HAS_EC_HASH_TO_CURVE) - void bench_ec_h2c(const std::chrono::milliseconds runtime) { - for(std::string group_name : {"secp256r1", "secp384r1", "secp521r1"}) { - auto h2c_ro_timer = make_timer(group_name + "-RO", "", "hash to curve"); - auto h2c_nu_timer = make_timer(group_name + "-NU", "", "hash to curve"); - - const auto group = Botan::EC_Group::from_name(group_name); - - const std::string hash_fn = "SHA-256"; - - while(h2c_ro_timer->under(runtime)) { - const auto input = rng().random_array<32>(); - const auto domain_sep = rng().random_array<32>(); - - h2c_ro_timer->run( - [&]() { return Botan::EC_AffinePoint::hash_to_curve_ro(group, hash_fn, input, domain_sep); }); - - h2c_nu_timer->run( - [&]() { return Botan::EC_AffinePoint::hash_to_curve_nu(group, hash_fn, input, domain_sep); }); - } - - record_result(h2c_ro_timer); - record_result(h2c_nu_timer); - } - } -#endif - -#if defined(BOTAN_HAS_PCURVES) - - void bench_pcurves(const std::vector& groups, const std::chrono::milliseconds runtime) { - for(const auto& group_name : groups) { - if(auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name)) { - auto base_timer = make_timer(group_name + " pcurve base mul"); - auto var_timer = make_timer(group_name + " pcurve var mul"); - auto mul2_setup_timer = make_timer(group_name + " pcurve mul2 setup"); - auto mul2_timer = make_timer(group_name + " pcurve mul2"); - - auto scalar_invert = make_timer(group_name + " pcurve scalar invert"); - auto to_affine = make_timer(group_name + " pcurve proj->affine"); - - auto g = curve->generator(); - auto h = curve->mul(g, curve->random_scalar(rng()), rng()).to_affine(); - auto gh_tab = curve->mul2_setup(g, h); - - while(base_timer->under(runtime)) { - const auto scalar = curve->random_scalar(rng()); - base_timer->run([&]() { return curve->mul_by_g(scalar, rng()).to_affine(); }); - } - - while(var_timer->under(runtime)) { - const auto scalar = curve->random_scalar(rng()); - var_timer->run([&]() { return curve->mul(h, scalar, rng()).to_affine(); }); - } - - while(mul2_setup_timer->under(runtime)) { - mul2_setup_timer->run([&]() { return curve->mul2_setup(g, h); }); - } - - while(mul2_timer->under(runtime)) { - const auto scalar = curve->random_scalar(rng()); - const auto scalar2 = curve->random_scalar(rng()); - mul2_timer->run([&]() -> std::optional { - if(auto pt = curve->mul2_vartime(*gh_tab, scalar, scalar2)) { - return pt->to_affine(); - } else { - return {}; - } - }); - } - - auto pt = curve->mul(g, curve->random_scalar(rng()), rng()); - to_affine->run_until_elapsed(runtime, [&]() { pt.to_affine(); }); - - while(scalar_invert->under(runtime)) { - const auto scalar = curve->random_scalar(rng()); - scalar_invert->run([&]() { scalar.invert(); }); - } - - record_result(base_timer); - record_result(var_timer); - record_result(mul2_setup_timer); - record_result(mul2_timer); - record_result(to_affine); - record_result(scalar_invert); - } - } - } - - void bench_pcurve_ecdsa(const std::vector& groups, const std::chrono::milliseconds runtime) { - for(const auto& group_name : groups) { - auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name); - if(!curve) { - continue; - } - - // Setup (not timed) - const auto g = curve->generator(); - const auto x = curve->random_scalar(rng()); - const auto y = curve->mul_by_g(x, rng()).to_affine(); - const auto e = curve->random_scalar(rng()); - - const auto gy_tab = curve->mul2_setup(g, y); - - auto b = curve->random_scalar(rng()); - auto b_inv = b.invert(); - - auto sign_timer = make_timer("ECDSA sign pcurves " + group_name); - auto verify_timer = make_timer("ECDSA verify pcurves " + group_name); - - while(sign_timer->under(runtime)) { - sign_timer->start(); - - const auto signature = [&]() { - const auto k = curve->random_scalar(rng()); - const auto r = curve->base_point_mul_x_mod_order(k, rng()); - const auto k_inv = (b * k).invert() * b; - b = b.square(); - b_inv = b_inv.square(); - const auto be = b * e; - const auto bx = b * x; - const auto bxr_e = (bx * r) + be; - const auto s = (k_inv * bxr_e) * b_inv; - - return Botan::concat(r.serialize(), s.serialize()); - }(); - - sign_timer->stop(); - - verify_timer->start(); - - auto result = [&](std::span sig) { - const size_t scalar_bytes = curve->scalar_bytes(); - if(sig.size() != 2 * scalar_bytes) { - return false; - } - - const auto r = curve->deserialize_scalar(sig.first(scalar_bytes)); - const auto s = curve->deserialize_scalar(sig.last(scalar_bytes)); - - if(r && s) { - if(r->is_zero() || s->is_zero()) { - return false; - } - - auto w = s->invert(); - - auto u1 = e * w; - auto u2 = *r * w; - - return curve->mul2_vartime_x_mod_order_eq(*gy_tab, *r, u1, u2); - } - - return false; - }(signature); - - BOTAN_ASSERT(result, "ECDSA-pcurves signature ok"); - - verify_timer->stop(); - } - - record_result(sign_timer); - record_result(verify_timer); - } - } - - void bench_pcurve_ecdh(const std::vector& groups, const std::chrono::milliseconds runtime) { - for(const auto& group_name : groups) { - auto curve = Botan::PCurve::PrimeOrderCurve::from_name(group_name); - if(!curve) { - continue; - } - - auto ka_timer = make_timer("ECDH agree pcurves " + group_name); - - auto agree = [&](const Botan::PCurve::PrimeOrderCurve::Scalar& sk, std::span pt_bytes) { - const auto pt = curve->deserialize_point(pt_bytes); - if(pt) { - return curve->mul(*pt, sk, rng()).to_affine().serialize(); - } else { - return std::vector(); - } - }; - - while(ka_timer->under(runtime)) { - const auto g = curve->generator(); - const auto x1 = curve->random_scalar(rng()); - const auto x2 = curve->random_scalar(rng()); - - const auto y1 = curve->mul_by_g(x1, rng()).to_affine().serialize(); - const auto y2 = curve->mul_by_g(x2, rng()).to_affine().serialize(); - - ka_timer->start(); - const auto ss1 = agree(x1, y2); - ka_timer->stop(); - - ka_timer->start(); - const auto ss2 = agree(x1, y2); - ka_timer->stop(); - - BOTAN_ASSERT(ss1 == ss2, "Key agreement worked"); - } - - record_result(ka_timer); - } - } - -#endif }; BOTAN_REGISTER_COMMAND("speed", Speed); diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py index da2f6fe378..ca060cf509 100755 --- a/src/scripts/test_cli.py +++ b/src/scripts/test_cli.py @@ -1623,8 +1623,7 @@ def cli_speed_pk_tests(_tmp_dir): pk_algos = ["ECDSA", "ECDH", "SM2", "ECKCDSA", "ECGDSA", "GOST-34.10", "DH", "DSA", "ElGamal", "Ed25519", "Ed448", "X25519", "X448", - "RSA", "RSA_keygen", "XMSS", "ec_h2c", "Kyber", "Dilithium", - "SPHINCS+"] + "RSA", "RSA_keygen", "XMSS", "Kyber", "Dilithium", "SPHINCS+"] output = test_cli("speed", ["--msec=%d" % (msec)] + pk_algos, None).split('\n') @@ -1705,8 +1704,8 @@ def cli_speed_math_tests(_tmp_dir): msec = 1 # these all have a common output format math_ops = ['mp_mul', 'mp_div', 'mp_div10', 'modexp', 'random_prime', 'inverse_mod', - 'rfc3394', 'fpe_fe1', 'ecdsa_recovery', 'ecc_init', - 'bn_redc', 'nistp_redc', 'ecc_mult', 'os2ecp', 'primality_test'] + 'rfc3394', 'fpe_fe1', 'ecdsa_recovery', 'ecc', + 'bn_redc', 'nistp_redc', 'primality_test'] format_re = re.compile(r'^.* [0-9]+ /sec; [0-9]+\.[0-9]+ ms/op .*\([0-9]+ (op|ops) in [0-9]+(\.[0-9]+)? ms\)') for op in math_ops: From fc2d731478a5ffc5f686abbd3d05437daf752ab3 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 10 Oct 2024 15:27:53 -0400 Subject: [PATCH 7/7] Add explicit visibility annotation for Mul2Table This is apparently necessary for MSVC to export it --- src/lib/pubkey/ec_group/ec_group.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/pubkey/ec_group/ec_group.h b/src/lib/pubkey/ec_group/ec_group.h index 188285e2cc..a6029fb387 100644 --- a/src/lib/pubkey/ec_group/ec_group.h +++ b/src/lib/pubkey/ec_group/ec_group.h @@ -252,7 +252,7 @@ class BOTAN_PUBLIC_API(2, 0) EC_Group final { bool verify_public_element(const EC_Point& y) const; /// Table for computing g*x + h*y - class Mul2Table final { + class BOTAN_PUBLIC_API(3, 6) Mul2Table final { public: /** * Create a table for computing g*x + h*y