Source
tests/benchmarks/integrity_bench.cpp
1
// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
// Micro-benchmarks for the per-load cost of each module-integrity tier4
// (runtime/integrity.cpp). Module verification is paid exactly ONCE, when the runtime5
// loads a module just before dlopen — never during the program's execution. So this6
// measures verify_module() directly (no process spawn, no program run) to isolate the7
// pure added work of each tier, at a couple of representative module sizes.8
//9
// Off no sidecars — the baseline: the module isn't even read.10
// Checksum + <mod>.sha512 — one SHA-512 pass over the module bytes.11
// Signed + <mod>.sig (strict) — SHA-512 checksum + one Ed25519 verification.12
// Full + <mod>.rt/.rt.sig — all of the above + parse/compare the runtime13
// manifest and verify its (separately-keyed) signature.14
//15
// Build with the `release` preset; run e.g.16
// ./build/release/bin/cheatah_benchmarks --benchmark_filter=Integrity17
#include <benchmark/benchmark.h>19
#include <cstdint>20
#include <filesystem>21
#include <fstream>22
#include <map>23
#include <string>24
#include <vector>26
#include "build_fingerprint.hpp" // build_runtime_manifest() — the host's own manifest27
#include "ed25519.hpp"28
#include "hashlib.hpp"29
#include "integrity.hpp"31
namespace {32
namespace fs = std::filesystem;33
namespace ig = cheatah::integrity;35
std::string base_name(const std::string& p) {36
const std::size_t s = p.find_last_of('/');37
return s == std::string::npos ? p : p.substr(s + 1);38
}39
void write_file(const std::string& path, const std::string& data) {40
std::ofstream f(path, std::ios::binary | std::ios::trunc);41
f << data;42
}43
std::string sig_sidecar(std::string_view secret, const std::string& message) {44
const std::string pub = cheatah::ed25519::public_key(secret);45
const std::string sig = cheatah::ed25519::sign(secret, message);46
return "cheatah-sig v1\npubkey " + pub + "\nsig " + sig + "\n";47
}49
// A fake module of `size` bytes on disk, plus the sidecars for tiers up to `tier`:50
// 0 none · 1 +.sha512 · 2 +.sig · 3 +.rt/.rt.sig. The .rt is the HOST's own manifest, so51
// the compatibility check passes and we time the success path, not an early rejection.52
struct Fixture {53
std::string path, pubkey, rt_pubkey;54
};55
Fixture make_fixture(std::size_t size, int tier, const std::string& tag) {56
const fs::path dir = fs::temp_directory_path() / "cheatah_integrity_bench";57
fs::create_directories(dir);58
Fixture fx;59
fx.path = (dir / ("mod_" + tag + ".bin")).string();61
std::string bytes(size, '\0'); // deterministic pseudo-random fill (no real .so needed)62
std::uint32_t s = 0x9e3779b9u;63
for (std::size_t i = 0; i < size; ++i) {64
s = s * 1103515245u + 12345u;65
bytes[i] = static_cast<char>(s >> 16);66
}67
write_file(fx.path, bytes);69
if (tier >= 1) {70
const std::string hex = cheatah::hashlib::sha512(bytes);71
write_file(fx.path + ".sha512", hex + " " + base_name(fx.path) + "\n");72
}73
if (tier >= 2) {74
const std::string secret = cheatah::ed25519::generate();75
fx.pubkey = cheatah::ed25519::public_key(secret);76
write_file(fx.path + ".sig", sig_sidecar(secret, bytes));77
}78
if (tier >= 3) {79
const std::string manifest = cheatah::build_runtime_manifest();80
write_file(fx.path + ".rt", manifest);81
const std::string rt_secret = cheatah::ed25519::generate();82
fx.rt_pubkey = cheatah::ed25519::public_key(rt_secret);83
write_file(fx.path + ".rt.sig", sig_sidecar(rt_secret, manifest));84
}85
return fx;86
}88
void BM_Integrity(benchmark::State& state, std::size_t size, int tier) {89
// Build the fixture once (outside the timed loop) and reuse it across iterations.90
static std::map<std::string, Fixture> cache;91
const std::string tag = std::to_string(size) + "_" + std::to_string(tier);92
auto it = cache.find(tag);93
if (it == cache.end()) it = cache.emplace(tag, make_fixture(size, tier, tag)).first;94
const Fixture& fx = it->second;96
const ig::Policy policy = (tier >= 2) ? ig::Policy::Strict : ig::Policy::Off;97
const std::vector<std::string> keys = (tier >= 2) ? std::vector<std::string>{fx.pubkey}98
: std::vector<std::string>{};99
const std::vector<std::string> rt_keys =100
(tier >= 3) ? std::vector<std::string>{fx.rt_pubkey} : std::vector<std::string>{};102
for (auto _ : state) {103
ig::Result r = ig::verify_module(fx.path, policy, keys, rt_keys);104
if (!r.ok) {105
state.SkipWithError(("verify_module failed: " + r.error).c_str());106
break;107
}108
ig::release(r); // close the fd verify_module opened, so we don't leak descriptors109
benchmark::DoNotOptimize(r.fd);110
}111
state.SetBytesProcessed(static_cast<std::int64_t>(state.iterations()) *112
static_cast<std::int64_t>(size));113
}114
} // namespace116
// 64 KiB ≈ a small program module; 1 MiB ≈ a larger one with several imported modules.117
BENCHMARK_CAPTURE(BM_Integrity, Off/64K, 64u * 1024, 0);118
BENCHMARK_CAPTURE(BM_Integrity, Checksum/64K, 64u * 1024, 1);119
BENCHMARK_CAPTURE(BM_Integrity, Signed/64K, 64u * 1024, 2);120
BENCHMARK_CAPTURE(BM_Integrity, Full/64K, 64u * 1024, 3);121
BENCHMARK_CAPTURE(BM_Integrity, Off/1M, 1024u * 1024, 0);122
BENCHMARK_CAPTURE(BM_Integrity, Checksum/1M, 1024u * 1024, 1);123
BENCHMARK_CAPTURE(BM_Integrity, Signed/1M, 1024u * 1024, 2);124
BENCHMARK_CAPTURE(BM_Integrity, Full/1M, 1024u * 1024, 3);