Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/errorcodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const std::string errorcode(benchmark_result::operation_outcome_t outcome) {
overloaded {
[&](benchmark_result::Ok) -> std::string { return "CKR_OK"; },
[&](benchmark_result::NotFound const& nf) -> std::string { return nf.what(); },
[&](benchmark_result::AmbiguousResult const& ar) -> std::string { return ar.what(); },
[&](benchmark_result::PayloadSizeNotSupported const& psns) -> std::string { return psns.what(); },
[&](benchmark_result::ApiErr const& apiErr) -> std::string { return _errorcode(apiErr); }
}, outcome );
}
Expand Down
5 changes: 5 additions & 0 deletions src/p11aescbc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ inline P11AESCBCBenchmark *P11AESCBCBenchmark::clone() const {
return new P11AESCBCBenchmark{*this};
}

bool P11AESCBCBenchmark::is_payload_supported(size_t payload_size)
{
// AES CBC requires payload to be multiple of block size (16 bytes)
return (payload_size % 16) == 0;
}

void P11AESCBCBenchmark::prepare(Session &session, Object &obj, std::optional<size_t> threadindex)
{
Expand Down
33 changes: 33 additions & 0 deletions src/p11aescbc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,38 @@

#include "p11benchmark.hpp"

// ============================================================================
// TEST CASE: AES-CBC Encryption
// ============================================================================
//
// DESCRIPTION:
// This test case measures the performance of AES encryption using Cipher
// Block Chaining (CBC) mode. It encrypts variable-size payloads using the
// CKM_AES_CBC mechanism with a randomly generated initialization vector.
//
// PAYLOAD:
// The payload consists of random data of configurable size. The test
// supports any payload size that is a multiple of the AES block size
// (16 bytes). The payload size is specified via command-line options.
//
// KEY REQUIREMENTS:
// - Key type: CKK_AES (secret key)
// - Key sizes: 128, 192, or 256 bits
// - The key must be extractable and support encryption operations
// - Key attributes: CKA_ENCRYPT must be set to CK_TRUE
//
// OPTIONS:
// --keysize <bits> : Specifies the AES key size (128, 192, or 256)
// --payload <bytes> : Size of data to encrypt (must be multiple of 16)
//
// TESTING APPROACH:
// The test performs encryption operations in a tight loop, measuring the
// throughput. Each iteration encrypts the same payload using the same key
// but with the same IV (set during prepare phase). The performance metric
// is the number of encryption operations per second and data throughput.
//
// ============================================================================

class P11AESCBCBenchmark : public P11Benchmark
{
Byte m_iv[16];
Expand All @@ -31,6 +63,7 @@ class P11AESCBCBenchmark : public P11Benchmark
virtual void prepare(Session &session, Object &obj, std::optional<size_t> threadindex) override;
virtual void crashtestdummy( Session &session) override;
virtual P11AESCBCBenchmark *clone() const override;
virtual bool is_payload_supported(size_t payload_size) override;

public:

Expand Down
5 changes: 5 additions & 0 deletions src/p11aesecb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ inline P11AESECBBenchmark *P11AESECBBenchmark::clone() const {
return new P11AESECBBenchmark{*this};
}

bool P11AESECBBenchmark::is_payload_supported(size_t payload_size)
{
// AES ECB requires payload to be multiple of block size (16 bytes)
return (payload_size % 16) == 0;
}

void P11AESECBBenchmark::prepare(Session &session, Object &obj, std::optional<size_t> threadindex)
{
Expand Down
34 changes: 34 additions & 0 deletions src/p11aesecb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,39 @@

#include "p11benchmark.hpp"

// ============================================================================
// TEST CASE: AES-ECB Encryption
// ============================================================================
//
// DESCRIPTION:
// This test case measures the performance of AES encryption using Electronic
// Codebook (ECB) mode. It encrypts variable-size payloads using the
// CKM_AES_ECB mechanism without an initialization vector.
//
// PAYLOAD:
// The payload consists of random data of configurable size. The test
// supports any payload size that is a multiple of the AES block size
// (16 bytes). The payload size is specified via command-line options.
//
// KEY REQUIREMENTS:
// - Key type: CKK_AES (secret key)
// - Key sizes: 128, 192, or 256 bits
// - The key must be extractable and support encryption operations
// - Key attributes: CKA_ENCRYPT must be set to CK_TRUE
//
// OPTIONS:
// --keysize <bits> : Specifies the AES key size (128, 192, or 256)
// --payload <bytes> : Size of data to encrypt (must be multiple of 16)
//
// TESTING APPROACH:
// The test performs encryption operations in a tight loop, measuring the
// throughput. ECB mode processes each block independently, making it
// suitable for performance benchmarking but not recommended for production
// use due to security considerations. The performance metric is the number
// of encryption operations per second and data throughput.
//
// ============================================================================

class P11AESECBBenchmark : public P11Benchmark
{
Mechanism m_mech_aesecb { CKM_AES_ECB, nullptr, 0 };
Expand All @@ -30,6 +63,7 @@ class P11AESECBBenchmark : public P11Benchmark
virtual void prepare(Session &session, Object &obj, std::optional<size_t> threadindex) override;
virtual void crashtestdummy(Session &session) override;
virtual P11AESECBBenchmark *clone() const override;
virtual bool is_payload_supported(size_t payload_size) override;

public:

Expand Down
34 changes: 34 additions & 0 deletions src/p11aesgcm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,40 @@

#include "p11benchmark.hpp"

// ============================================================================
// TEST CASE: AES-GCM Encryption
// ============================================================================
//
// DESCRIPTION:
// This test case measures the performance of AES encryption using Galois/
// Counter Mode (GCM), an authenticated encryption mode. It encrypts
// variable-size payloads using the CKM_AES_GCM mechanism with a randomly
// generated initialization vector and produces an authentication tag.
//
// PAYLOAD:
// The payload consists of random data of configurable size. Unlike ECB and
// CBC modes, GCM does not require the payload to be a multiple of the block
// size. The payload size is specified via command-line options.
//
// KEY REQUIREMENTS:
// - Key type: CKK_AES (secret key)
// - Key sizes: 128, 192, or 256 bits
// - The key must support encryption operations
// - Key attributes: CKA_ENCRYPT must be set to CK_TRUE
//
// OPTIONS:
// --keysize <bits> : Specifies the AES key size (128, 192, or 256)
// --payload <bytes> : Size of data to encrypt (any size supported)
//
// TESTING APPROACH:
// The test performs authenticated encryption operations in a tight loop.
// GCM mode provides both confidentiality and authenticity with a 128-bit
// authentication tag. The IV is randomly generated during the prepare phase.
// The performance metric is the number of encryption operations per second
// and data throughput, including the authentication overhead.
//
// ============================================================================

class P11AESGCMBenchmark : public P11Benchmark
{
std::vector<uint8_t> m_iv;
Expand Down
136 changes: 76 additions & 60 deletions src/p11benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ std::string P11Benchmark::build_threaded_label(std::optional<size_t> threadindex
// if threadindex has a value, it means we have generated session keys (one per thread)
// in which case we need to recreate the thread-specific key label
if(threadindex) {
std::stringstream thread_specific_label;
thread_specific_label << this->label() << "-th-" << std::setw(5) << std::setfill('0') << threadindex.value();
label = thread_specific_label.str();
std::stringstream thread_specific_label;
thread_specific_label << this->label() << "-th-" << std::setw(5) << std::setfill('0') << threadindex.value();
label = thread_specific_label.str();
} else { // else we have std::nullopt, and no session key has been generated, we don't need to transform the name
label = this->label();
label = this->label();
}

return label;
Expand Down Expand Up @@ -106,66 +106,82 @@ benchmark_result::benchmark_result_t P11Benchmark::execute(Session *session, con
benchmark_result::operation_outcome_t return_code = benchmark_result::Ok{};
std::vector<milliseconds_double_t> records(iterations);

// a small lambda to handle exceptions in a uniform way
auto handle_benchmark_exception = [&](auto const& exc) {
{
std::lock_guard<std::mutex> lg{display_mtx};
std::cerr << "ERROR: " << exc.what() << std::endl;
}
using Exc = std::decay_t<decltype(exc)>;
return_code = Exc{exc};
};

try {
auto label = build_threaded_label(threadindex); // build threaded label (if needed)

m_payload = payload; // remember the payload

AttributeContainer search_template;
search_template.add_string( AttributeType::Label, label );
search_template.add_class( m_objectclass );

auto found_objs = Object::search<Object>( *session, search_template.attributes() );

if( found_objs.size()==0 ) {
std::cerr << "Error: no object found for label '" << label << "'" << std::endl;
} else if( found_objs.size()>1 ) {
std::cerr << "Error: more than one object found for label '" << label << "'" << std::endl;
} else {
for (auto &obj: found_objs) {

prepare(*session, obj, threadindex);

// wait for green light - all threads are starting together
{
std::unique_lock<std::mutex> greenlight_lck(greenlight_mtx);
greenlight_cond.wait(greenlight_lck,[]{ return greenlight; });
}

// ok go now!

// first run iterations that are skipped, i.e. not taken into account for stats
for (size_t i=0; i<skipiterations; i++) {
crashtestdummy(*session);
cleanup(*session); // cleanup any created object (e.g. unwrapped or derived keys)
}
for (size_t i=0; i<iterations; i++) {
reset_timer();
crashtestdummy(*session);
suspend_timer();
cleanup(*session); // cleanup any created object (e.g. unwrapped or derived keys)
records.at(i) = elapsed();
}
teardown(*session, obj, threadindex); // perform any needed teardown
}
}
auto label = build_threaded_label(threadindex); // build threaded label (if needed)

m_payload = payload; // remember the payload

AttributeContainer search_template;
search_template.add_string( AttributeType::Label, label );
search_template.add_class( m_objectclass );

auto found_objs = Object::search<Object>( *session, search_template.attributes() );

if( found_objs.size()==0 ) {
throw benchmark_result::NotFound(label);
} else if( found_objs.size()>1 ) {
throw benchmark_result::AmbiguousResult(label);
} else {
for (auto &obj: found_objs) {

prepare(*session, obj, threadindex);

// check payload size support
if( !is_payload_supported( m_payload.size() ) ) {
throw benchmark_result::PayloadSizeNotSupported(m_payload.size());
}

// wait for green light - all threads are starting together
{
std::unique_lock<std::mutex> greenlight_lck(greenlight_mtx);
greenlight_cond.wait(greenlight_lck,[]{ return greenlight; });
}

// ok go now!

// first run iterations that are skipped, i.e. not taken into account for stats
for (size_t i=0; i<skipiterations; i++) {
crashtestdummy(*session);
cleanup(*session); // cleanup any created object (e.g. unwrapped or derived keys)
}
for (size_t i=0; i<iterations; i++) {
reset_timer();
crashtestdummy(*session);
suspend_timer();
cleanup(*session); // cleanup any created object (e.g. unwrapped or derived keys)
records.at(i) = elapsed();
}
teardown(*session, obj, threadindex); // perform any needed teardown
}
}
} catch (benchmark_result::PayloadSizeNotSupported &psns) {
handle_benchmark_exception(psns);
} catch (benchmark_result::NotFound &nfe) {
// we print the exception, and move on
std::lock_guard<std::mutex> lg{display_mtx};
std::cerr << "ERROR: " << nfe.what() << std::endl;
return_code = benchmark_result::NotFound();
handle_benchmark_exception(nfe);
} catch (benchmark_result::AmbiguousResult &are) {
handle_benchmark_exception(are);
} catch (Botan::PKCS11::PKCS11_ReturnError &bexc) {
// we print the exception, and move on
std::lock_guard<std::mutex> lg{display_mtx};
std::cerr << "ERROR:: " << bexc.what()
<< " (" << errorcode(bexc.error_code()) << ")"
<< std::endl;
return_code = benchmark_result::ApiErr{bexc.error_code()};
// we print the exception, and move on
std::lock_guard<std::mutex> lg{display_mtx};
std::cerr << "ERROR:: " << bexc.what()
<< " (" << errorcode(bexc.error_code()) << ")"
<< std::endl;
return_code = benchmark_result::ApiErr{bexc.error_code()};
} catch (...) {
std::lock_guard<std::mutex> lg{display_mtx};
std::cerr << "ERROR: caught an unmanaged exception" << std::endl;
// rethrow
throw;
std::lock_guard<std::mutex> lg{display_mtx};
std::cerr << "ERROR: caught an unmanaged exception" << std::endl;
// rethrow
throw;
}

return std::make_pair( std::move(records), return_code );
Expand Down
Loading