Skip to content

Instantly share code, notes, and snippets.

@schwabe
Created November 6, 2021 18:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save schwabe/029dc5e5a690df8e2e3f774a13ec7bce to your computer and use it in GitHub Desktop.
Save schwabe/029dc5e5a690df8e2e3f774a13ec7bce to your computer and use it in GitHub Desktop.
// c++ -O2 -std=c++14 -g -I/usr/local/opt/openssl@3/include -L/usr/local/opt/openssl@3/lib -lcrypto -lssl -lbenchmark scratch.cpp && ./a.out
#include <benchmark/benchmark.h>
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <iostream>
static uint8_t *nonce_data = NULL; /* GLOBAL */
const EVP_MD *nonce_md = NULL;
int nonce_secret_len = 16;
#define PRNG_NONCE_RESET_BYTES 8192
#define md_kt_size EVP_MD_size
/* Reset the nonce value, also done periodically to refresh entropy */
static void
prng_reset_nonce(void)
{
const int size = md_kt_size(nonce_md) + nonce_secret_len;
if (!RAND_bytes(nonce_data, size))
{
std::cerr << "ERROR: Random number generator cannot obtain entropy for PRNG" << std::endl;
}
}
int
md_full(const EVP_MD *kt, const uint8_t *src, int src_len, uint8_t *dst)
{
unsigned int in_md_len = 0;
return EVP_Digest(src, src_len, dst, &in_md_len, kt, NULL);
}
void
prng_init()
{
nonce_md = ::EVP_sha1();
if (nonce_md)
{
{
const int size = md_kt_size(nonce_md) + nonce_secret_len;
nonce_data = (uint8_t *) malloc(size);
//check_malloc_return(nonce_data);
prng_reset_nonce();
}
}
}
int
prng_bytes(uint8_t *output, int len)
{
static size_t processed = 0;
if (nonce_md)
{
const int md_size = md_kt_size(nonce_md);
while (len > 0)
{
const int blen = std::min(len, md_size);
md_full(nonce_md, nonce_data, md_size + nonce_secret_len, nonce_data);
memcpy(output, nonce_data, blen);
output += blen;
len -= blen;
/* Ensure that random data is reset regularly */
processed += blen;
if (processed > PRNG_NONCE_RESET_BYTES)
{
prng_reset_nonce();
processed = 0;
}
}
}
return 1;
}
int
dummy_prng(uint8_t *output, int len)
{
*output = 42;
return 1;
}
static void BM_Encrypt_AES_CBC(benchmark::State &state, decltype(&prng_bytes) rnd)
{
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
uint8_t key[32];
uint8_t iv[32];
RAND_bytes(key, 32);
uint8_t input[1400];
uint8_t output[1500];
RAND_bytes(input, 140);
int ivlen=EVP_CIPHER_iv_length(EVP_aes_128_cbc());
assert(EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv));
for (auto _: state)
{
rnd(iv, ivlen);
assert(EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv));
int len;
/* The EVP_EncryptFinal method will write to the dst+len pointer even
* though there is nothing to encrypt anymore, provide space for that to
* not overflow the stack */
assert(EVP_EncryptUpdate(ctx, output, &len, input, sizeof(input)));
assert(EVP_EncryptFinal(ctx, output + len, &len));
}
EVP_CIPHER_CTX_free(ctx);
}
static void BM_Encrypt_AES_CBC_dummy(benchmark::State &state)
{
BM_Encrypt_AES_CBC(state, dummy_prng);
}
static void BM_Encrypt_AES_CBC_RAND_bytes(benchmark::State &state)
{
BM_Encrypt_AES_CBC(state, RAND_bytes);
}
static void BM_Encrypt_AES_CBC_prng_bytes(benchmark::State &state)
{
prng_init();
BM_Encrypt_AES_CBC(state, prng_bytes);
}
static void BM_OpenVPN_RAND(benchmark::State &state)
{
prng_init();
for (auto _: state)
{
// This code gets timed
uint8_t out[16];
prng_bytes(out, 16);
}
}
static void BM_OpenSSL_RAND(benchmark::State &state)
{
// Perform setup here
for (auto _: state)
{
// This code gets timed
uint8_t out[16];
RAND_bytes(out, 16);
}
}
// Register the function as a benchmark
BENCHMARK(BM_OpenSSL_RAND);
BENCHMARK(BM_OpenVPN_RAND);
BENCHMARK(BM_Encrypt_AES_CBC_dummy);
BENCHMARK(BM_Encrypt_AES_CBC_RAND_bytes);
BENCHMARK(BM_Encrypt_AES_CBC_prng_bytes);
// Run the benchmark
BENCHMARK_MAIN();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment