Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVP API ECDHE speed tool support #1080

Merged
merged 14 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions tool/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# Tools for AWS-LC
AWS-LC features enhanced benchmarking tools compatible with OpenSSL and BoringSSL in order to help facilitate 1-1 performance comparisons.

## Speed tool

torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
The speed subtool of `bssl` runs a performance test for a number of cryptographic operations (which can be implemented using different APIs). Each operation is mapped to a "filter name". Below we list the filter name, which operation it maps to and which API is used to implement it.

| Filter name | Description | Function family |
| ------------- | ------------- | -------------
| EVP ECDH {P-224, P-256, P-384, P-521, secp256k1, X25519} | ECDHE key agreement for one party | EVP |
| ECDH {P-224, P-256, P-384, P-521, secp256k1} | ECDHE key agreement for one party | EC |
| Generate {P-224, P-256, P-384, P-521, secp256k1} | Elliptic curve key generation | EVP |
| ECMUL {P-224, P-256, P-384, P-521, secp256k1} | Elliptic curve arbitrary scalar multiplication | EC |

## Benchmarking Tools
When compiled, AWS-LC will generate separate benchmarking tools when provided with corresponding compiler flags. These tools take the same arguments as `bssl speed` tool.

Expand Down
1 change: 1 addition & 0 deletions tool/ossl_bm.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ OSSL_MAKE_DELETER(EC_POINT, EC_POINT_free)
OSSL_MAKE_DELETER(BN_CTX, BN_CTX_free)
OSSL_MAKE_DELETER(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free)
OSSL_MAKE_DELETER(EVP_PKEY_CTX, EVP_PKEY_CTX_free)
OSSL_MAKE_DELETER(EVP_PKEY, EVP_PKEY_free)

// OpenSSL 1.0.x has different APIs for EVP_MD_CTX and HMAC
// We need to add more custom logic to HMAC to let it properly delete the
Expand Down
127 changes: 127 additions & 0 deletions tool/speed.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,130 @@ static bool SpeedECDSA(const std::string &selected) {
}

#if !defined(OPENSSL_1_0_BENCHMARK)
static EVP_PKEY * evp_generate_key(const int curve_nid) {

// P NIST curves are abstracted under the same virtual function table which
// is configured using |EVP_PKEY_EC|.
int local_nid = curve_nid;
if (curve_nid != NID_X25519) {
local_nid = EVP_PKEY_EC;
}

BM_NAMESPACE::UniquePtr<EVP_PKEY_CTX> evp_pkey_ctx(EVP_PKEY_CTX_new_id(local_nid, nullptr));

if (local_nid == EVP_PKEY_EC) {
// Since P NIST curves are abstracted under the same virtual function table,
// we haven't actually loaded the group yet. This must be done before we can
// generate the key.
EVP_PKEY *curve = nullptr;
if (!EVP_PKEY_paramgen_init(evp_pkey_ctx.get()) ||
!EVP_PKEY_CTX_set_ec_paramgen_curve_nid(evp_pkey_ctx.get(), curve_nid) ||
!EVP_PKEY_paramgen(evp_pkey_ctx.get(), &curve) ||
curve == nullptr) {
return nullptr;
}
BM_NAMESPACE::UniquePtr<EVP_PKEY> curve_uniqueptr(curve);
evp_pkey_ctx.reset(EVP_PKEY_CTX_new(curve_uniqueptr.get(), NULL));
if (evp_pkey_ctx == nullptr) {
return nullptr;
}
}

EVP_PKEY *key = nullptr;
if (!EVP_PKEY_keygen_init(evp_pkey_ctx.get()) ||
!EVP_PKEY_keygen(evp_pkey_ctx.get(), &key)) {
return nullptr;
}

return key;
}

// One could model serialisation as well using
// |EVP_PKEY_{get,set}1_tls_encodedpoint|. But that pair of functions only
// support a subset of curve types. |SpeedECDH| includes deserialisation of the
// peer key. Leaving this out doesn't bias measurements though.
static bool SpeedEvpEcdhCurve(const std::string &name, int nid,
const std::string &selected) {

if (!selected.empty() && name.find(selected) == std::string::npos) {
return true;
}

// First we need a peer key that we are going to re-use for all iterations.
BM_NAMESPACE::UniquePtr<EVP_PKEY> peer_key(evp_generate_key(nid));
if (peer_key == nullptr) {
return false;
}

if (nid != NID_X25519) {
// To model deriving an ECDHE shared secret, we need the peer key. But void
// the private part, to avoid biasing measurements. For example, when
// performing key validation. Currently, this is only a problem for the
// P NIST curve types.
BM_NAMESPACE::UniquePtr<EVP_PKEY> only_public_key_evp_pkey(EVP_PKEY_new());
BM_NAMESPACE::UniquePtr<EC_KEY> only_public_key_ec_key(EC_KEY_new_by_curve_name(nid));
if (only_public_key_ec_key == nullptr ||
only_public_key_evp_pkey == nullptr ||
!EC_KEY_set_public_key(only_public_key_ec_key.get(),
EC_KEY_get0_public_key(EVP_PKEY_get0_EC_KEY(peer_key.get()))) ||
!EVP_PKEY_assign_EC_KEY(only_public_key_evp_pkey.get(), only_public_key_ec_key.release())) {
return false;
}
peer_key.reset(only_public_key_evp_pkey.release());
}

TimeResults results;
if (!TimeFunction(&results, [nid, &peer_key]() -> bool {
BM_NAMESPACE::UniquePtr<EVP_PKEY> my_key(evp_generate_key(nid));
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved

#if defined(OPENSSL_BENCHMARK)
// For AWS-LC EVP_PKEY_derive() calls ECDH_compute_shared_secret() that
// performs the public key check.
if (nid != NID_X25519) {
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
// For the supported P NIST curves, the peer public key must be validated
// to ensure proper computation.
if (!EC_KEY_check_key(EVP_PKEY_get0_EC_KEY(peer_key.get()))) {
return false;
}
}
#endif

BM_NAMESPACE::UniquePtr<EVP_PKEY_CTX> derive_ctx(EVP_PKEY_CTX_new(my_key.get(), NULL));
if (derive_ctx == nullptr) {
return false;
}

size_t shared_secret_size = 0;
if (!EVP_PKEY_derive_init(derive_ctx.get()) ||
!EVP_PKEY_derive_set_peer(derive_ctx.get(), peer_key.get()) ||
!EVP_PKEY_derive(derive_ctx.get(), NULL, &shared_secret_size) ||
(shared_secret_size == 0)) {
return false;
}

std::unique_ptr<uint8_t[]> shared_secret(new uint8_t[shared_secret_size]);
if (!EVP_PKEY_derive(derive_ctx.get(), shared_secret.get(), &shared_secret_size)) {
return false;
}

return true;
})) {
return false;
}

results.Print(name);
return true;
}

static bool SpeedEvpEcdh(const std::string &selected) {
return SpeedEvpEcdhCurve("EVP ECDH P-224", NID_secp224r1, selected) &&
SpeedEvpEcdhCurve("EVP ECDH P-256", NID_X9_62_prime256v1, selected) &&
SpeedEvpEcdhCurve("EVP ECDH P-384", NID_secp384r1, selected) &&
SpeedEvpEcdhCurve("EVP ECDH P-521", NID_secp521r1, selected) &&
SpeedEvpEcdhCurve("EVP ECDH secp256k1", NID_secp256k1, selected) &&
SpeedEvpEcdhCurve("EVP ECDH X25519", NID_X25519, selected);
}

static bool SpeedECMULCurve(const std::string &name, int nid,
const std::string &selected) {
if (!selected.empty() && name.find(selected) == std::string::npos) {
Expand Down Expand Up @@ -2448,6 +2572,9 @@ bool Speed(const std::vector<std::string> &args) {
!SpeedECKeyGen(selected) ||
!SpeedECKeyGenerateKey(false, selected) ||
#if !defined(OPENSSL_1_0_BENCHMARK)
// OpenSSL 1.0.2 is missing functions e.g. |EVP_PKEY_get0_EC_KEY| and
// doesn't implement X255519 either.
!SpeedEvpEcdh(selected) ||
!SpeedECMUL(selected) ||
// OpenSSL 1.0 doesn't support Scrypt
!SpeedScrypt(selected) ||
Expand Down