Skip to content

Commit

Permalink
Merge pull request #4367 from randombit/jack/refactor-speed-pk
Browse files Browse the repository at this point in the history
Refactor public key related performance test code
  • Loading branch information
randombit authored Oct 11, 2024
2 parents 4c29200 + fc2d731 commit 2cc573c
Show file tree
Hide file tree
Showing 10 changed files with 991 additions and 1,055 deletions.
2 changes: 2 additions & 0 deletions src/cli/perf.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class PerfConfig {

//virtual std::vector<std::string> providers() const = 0;

virtual const std::vector<std::string>& ecc_groups() const = 0;

virtual const std::vector<size_t>& buffer_sizes() const = 0;

virtual std::ostream& error_output() const = 0;
Expand Down
104 changes: 104 additions & 0 deletions src/cli/perf_ec.cpp
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
114 changes: 114 additions & 0 deletions src/cli/perf_pk_enc.cpp
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
147 changes: 147 additions & 0 deletions src/cli/perf_pk_ka.cpp
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
Loading

0 comments on commit 2cc573c

Please sign in to comment.