-
Notifications
You must be signed in to change notification settings - Fork 564
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4367 from randombit/jack/refactor-speed-pk
Refactor public key related performance test code
- Loading branch information
Showing
10 changed files
with
991 additions
and
1,055 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <botan/ec_group.h> | ||
#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<Botan::BigInt> 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <botan/pk_algs.h> | ||
#include <botan/pubkey.h> | ||
#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<std::string> 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<uint8_t> 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<std::string> 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <botan/pk_algs.h> | ||
#include <botan/pubkey.h> | ||
#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<std::string> 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<const Botan::PK_Key_Agreement_Key&>(*key1); | ||
const Botan::PK_Key_Agreement_Key& ka_key2 = dynamic_cast<const Botan::PK_Key_Agreement_Key&>(*key2); | ||
|
||
Botan::PK_Key_Agreement ka1(ka_key1, rng, kdf, provider); | ||
Botan::PK_Key_Agreement ka2(ka_key2, rng, kdf, provider); | ||
|
||
const std::vector<uint8_t> ka1_pub = ka_key1.public_value(); | ||
const std::vector<uint8_t> 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<std::string> 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<std::string> 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 |
Oops, something went wrong.