Skip to content

Commit

Permalink
Add DH_check support to perf tool (#982)
Browse files Browse the repository at this point in the history
Makes it possible to measure performance of the function DH_check().
  • Loading branch information
torben-hansen authored May 1, 2023
1 parent 1d7c26e commit 413bce5
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 24 deletions.
1 change: 1 addition & 0 deletions tool/bssl_bm.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "openssl/ctrdrbg.h"
#include <openssl/curve25519.h>
#include <openssl/crypto.h>
#include <openssl/dh.h>
#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/ec.h>
Expand Down
2 changes: 2 additions & 0 deletions tool/ossl_bm.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <openssl/aes.h>
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/dh.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/evp.h>
Expand Down Expand Up @@ -53,6 +54,7 @@ template <typename T> struct Deleter {
} // namespace internal
template <typename T> using UniquePtr = std::unique_ptr<T, internal::Deleter<T>>;

OSSL_MAKE_DELETER(DH, DH_free)
OSSL_MAKE_DELETER(RSA, RSA_free)
OSSL_MAKE_DELETER(BIGNUM, BN_free)
OSSL_MAKE_DELETER(EC_KEY, EC_KEY_free)
Expand Down
166 changes: 142 additions & 24 deletions tool/speed.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ static std::string ChunkLenSuffix(size_t chunk_len) {
return buf;
}

static std::string PrimeLenSuffix(size_t prime_length) {
char buf[32];
snprintf(buf, sizeof(buf), " (%zu bit%s)", prime_length,
prime_length != 1 ? "s" : "");
return buf;
}

// TimeResults represents the results of benchmarking a function.
struct TimeResults {
// num_calls is the number of function calls done in the time period.
Expand Down Expand Up @@ -113,6 +120,19 @@ struct TimeResults {
}
}

void PrintWithPrimes(const std::string &description,
size_t prime_size) const {
if (g_print_json) {
PrintJSON(description, "primeSizePerCall", prime_size);
} else {
printf(
"Did %" PRIu64 " %s operations in %" PRIu64
"us (%.3f ops/sec)\n",
num_calls, (description + PrimeLenSuffix(prime_size)).c_str(), us,
(static_cast<double>(num_calls) / static_cast<double>(us)) * 1000000);
}
}

private:
void PrintJSON(const std::string &description,
size_t bytes_per_call = 0) const {
Expand All @@ -132,6 +152,25 @@ struct TimeResults {
first_json_printed = true;
}

void PrintJSON(const std::string &description,
const std::string &size_label,
size_t size = 0) const {
if (first_json_printed) {
puts(",");
}

printf("{\"description\": \"%s\", \"numCalls\": %" PRIu64
", \"microseconds\": %" PRIu64,
description.c_str(), num_calls, us);

if (size > 0) {
printf(", \"%s\": %zu", size_label.c_str(), size);
}

printf("}");
first_json_printed = true;
}

// first_json_printed is true if |g_print_json| is true and the first item in
// the JSON results has been printed already. This is used to handle the
// commas between each item in the result list.
Expand Down Expand Up @@ -165,8 +204,10 @@ static uint64_t time_now() {
}
#endif

static uint64_t g_timeout_seconds = 1;
#define TIMEOUT_SECONDS_DEFAULT 1
static uint64_t g_timeout_seconds = TIMEOUT_SECONDS_DEFAULT;
static std::vector<size_t> g_chunk_lengths = {16, 256, 1350, 8192, 16384};
static std::vector<size_t> g_prime_bit_lengths = {2048, 3072};

static bool TimeFunction(TimeResults *results, std::function<bool()> func) {
// The first time |func| is called an expensive self check might run that
Expand Down Expand Up @@ -1944,6 +1985,56 @@ static bool SpeedJitter(std::string selected) {
}
#endif

static bool SpeedDHcheck(size_t prime_bit_length) {

TimeResults results;
BM_NAMESPACE::UniquePtr<DH> dh_params(DH_new());
if (dh_params == nullptr) {
return false;
}

// DH_generate_parameters_ex grows exponentially slower as prime length grows.
if (DH_generate_parameters_ex(dh_params.get(), prime_bit_length,
DH_GENERATOR_2, nullptr) != 1) {
return false;
}

if (!TimeFunction(&results, [&dh_params]() -> bool {
int result = 0;
if (DH_check(dh_params.get(), &result) != 1) {
return false;
}
return true;
})) {
return false;
}

results.PrintWithPrimes("DH check(s)", prime_bit_length);
return true;
}

static bool SpeedDHcheck(std::string selected) {
// Don't run this by default because it's so slow.
if (selected != "dhcheck") {
return true;
}

uint64_t maybe_reset_timeout = g_timeout_seconds;
if (g_timeout_seconds == TIMEOUT_SECONDS_DEFAULT) {
g_timeout_seconds = 10;
}

for (size_t prime_bit_length : g_prime_bit_lengths) {
if (!SpeedDHcheck(prime_bit_length)) {
return false;
}
}

g_timeout_seconds = maybe_reset_timeout;

return true;
}

#if !defined(OPENSSL_BENCHMARK) && !defined(BORINGSSL_BENCHMARK)
static bool SpeedPKCS8(const std::string &selected) {
if (!selected.empty() && selected.find("pkcs8") == std::string::npos) {
Expand Down Expand Up @@ -2056,6 +2147,12 @@ static const argument_t kArguments[] = {
"A comma-separated list of input sizes to run tests at (default is "
"16,256,1350,8192,16384)",
},
{
"-primes",
kOptionalArgument,
"A comma-separated list of prime input sizes (bits) to run tests at "
"(default is 2048,3072)",
},
{
"-json",
kBooleanArgument,
Expand All @@ -2073,6 +2170,38 @@ static const argument_t kArguments[] = {
},
};

// parseCommaArgumentToGlobalVector clears |vector| and parses comma-separated
// input for the argument |arg_name| in |args_map|.
static bool parseCommaArgumentToGlobalVector(std::vector<size_t> &vector,
std::map<std::string, std::string> &args_map, const std::string &arg_name) {

vector.clear();
const char *start = args_map[arg_name.c_str()].data();
const char *end = start + args_map[arg_name.c_str()].size();
while (start != end) {
errno = 0;
char *ptr;
unsigned long long val = strtoull(start, &ptr, 10);
if (ptr == start /* no numeric characters found */ ||
errno == ERANGE /* overflow */ ||
static_cast<size_t>(val) != val) {
fprintf(stderr, "Error parsing %s argument\n", arg_name.c_str());
return false;
}
vector.push_back(static_cast<size_t>(val));
start = ptr;
if (start != end) {
if (*start != ',') {
fprintf(stderr, "Error parsing %s argument\n", arg_name.c_str());
return false;
}
start++;
}
}

return true;
}

bool Speed(const std::vector<std::string> &args) {
#if defined(OPENSSL_IS_AWSLC)
// For mainline AWS-LC this is a no-op, however if speed.cc built with an old
Expand All @@ -2099,28 +2228,16 @@ bool Speed(const std::vector<std::string> &args) {
}

if (args_map.count("-chunks") != 0) {
g_chunk_lengths.clear();
const char *start = args_map["-chunks"].data();
const char *end = start + args_map["-chunks"].size();
while (start != end) {
errno = 0;
char *ptr;
unsigned long long val = strtoull(start, &ptr, 10);
if (ptr == start /* no numeric characters found */ ||
errno == ERANGE /* overflow */ ||
static_cast<size_t>(val) != val) {
fprintf(stderr, "Error parsing -chunks argument\n");
return false;
}
g_chunk_lengths.push_back(static_cast<size_t>(val));
start = ptr;
if (start != end) {
if (*start != ',') {
fprintf(stderr, "Error parsing -chunks argument\n");
return false;
}
start++;
}
if (!parseCommaArgumentToGlobalVector(g_chunk_lengths,
args_map, "-chunks")) {
return false;
}
}

if (args_map.count("-primes") != 0) {
if (!parseCommaArgumentToGlobalVector(g_prime_bit_lengths,
args_map, "-primes")) {
return false;
}
}

Expand Down Expand Up @@ -2187,7 +2304,8 @@ bool Speed(const std::vector<std::string> &args) {
!SpeedScrypt(selected) ||
#endif
!SpeedRSA(selected) ||
!SpeedRSAKeyGen(false, selected)
!SpeedRSAKeyGen(false, selected) ||
!SpeedDHcheck(selected)
#if !defined(OPENSSL_BENCHMARK)
||
!SpeedKEM(selected) ||
Expand Down

0 comments on commit 413bce5

Please sign in to comment.