Installation
tacet is available for multiple languages. Choose your platform:
Add tacet as a dev dependency:
cargo add tacet --devcargo add rand --dev # For random input generationOr add to your Cargo.toml:
[dev-dependencies]tacet = "0.1"rand = "0.8"Feature flags
Section titled “Feature flags”Default features (recommended):
parallel- Rayon-based parallel bootstrap (4-8x speedup)kperf(macOS ARM64) - Cycle-accurate timing via kperf (opt-in, requires sudo)perf(Linux) - Cycle-accurate timing via perf_event (opt-in, requires sudo)
Optional features:
macros- Proc macros (timing_test!,timing_test_checked!)
Minimal build:
[dev-dependencies]tacet = { version = "0.1", default-features = false }bun add tacetnpm i tacetpnpm add tacetyarn add tacetNative bindings via NAPI-RS, supporting Node.js and Bun on:
- Linux (x86_64, aarch64)
- macOS (x86_64, aarch64)
- Windows (x86_64)
import { TimingOracle, AttackerModel } from 'tacet';
const outcome = TimingOracle.forAttacker(AttackerModel.AdjacentNetwork) .test( () => new Uint8Array(32).fill(0), // Baseline () => crypto.getRandomValues(new Uint8Array(32)), // Sample (data) => myFunction(data) );Build the C bindings from source:
git clone https://github.com/agucova/tacetcd tacetcargo build --release -p tacet-c
# Headers:# C: crates/tacet-c/include/tacet.h# C++: bindings/cpp/tacet.hpp# Library:# target/release/libtacet_c.{a,so,dylib}C usage:
#include <tacet.h>
tacet_t* oracle = tacet_for_attacker(ATTACKER_ADJACENT_NETWORK);tacet_outcome_t outcome;tacet_test(oracle, &inputs, measure_fn, &outcome);C++ usage:
#include <tacet.hpp>using namespace tacet;
auto outcome = TimingOracle::forAttacker(AttackerModel::AdjacentNetwork) .test(inputs, [](auto& data) { myFunction(data); });go get github.com/agucova/tacet/bindings/goimport tacet "github.com/agucova/tacet/bindings/go"
inputs := tacet.NewInputPair( func() []byte { return make([]byte, 32) }, func() []byte { b := make([]byte, 32); rand.Read(b); return b },)
outcome := tacet.ForAttacker(tacet.AdjacentNetwork). Test(inputs, func(data []byte) { myFunction(data) })The Go bindings use cgo to link against the C library.
Platform support
Section titled “Platform support”| Platform | Timer | Resolution | Notes |
|---|---|---|---|
| Linux x86_64 | rdtsc | ~0.3ns | Best precision |
| Linux aarch64 | cntvct_el0 | 1-10ns | Good precision |
| macOS x86_64 | rdtsc | ~0.3ns | Best precision |
| macOS ARM64 | cntvct_el0 | ~42ns | Uses adaptive batching |
| Windows x86_64 | rdtsc | ~0.3ns | Best precision |
On platforms with coarse timer resolution (macOS ARM64), the library automatically uses adaptive batching to compensate.
Enabling cycle-accurate timers
Section titled “Enabling cycle-accurate timers”For cycle-accurate measurements on ARM64, use TimerSpec::CyclePrecision:
use tacet::{TimingOracle, AttackerModel, TimerSpec};
TimingOracle::for_attacker(AttackerModel::SharedHardware) .timer_spec(TimerSpec::CyclePrecision) // Request cycle-accurate timerThis requires elevated privileges on ARM64:
# kperf requires BOTH sudo AND single-threaded executionsudo -E cargo test --test my_test -- --test-threads=1# Option 1: Run as rootsudo cargo test --test my_test -- --test-threads=1
# Option 2: Grant CAP_PERFMON (persistent)sudo setcap cap_perfmon+ep target/debug/deps/my_test-*cargo test --test my_test -- --test-threads=1Next steps
Section titled “Next steps”- Quick Start: Write your first timing test
- The Two-Class Pattern: Understanding input class selection
- Attacker Models: Choosing the right threat model