Skip to content

This guide walks through writing and running your first timing test. You’ve seen the basic pattern on the landing page; now let’s create a complete, runnable test.

Terminal window
cargo add tacet --dev
cargo add rand --dev # For random input generation
cargo add subtle --dev # For the constant-time comparison example

Create a test file (e.g., tests/timing.rs):

use tacet::{TimingOracle, AttackerModel, helpers::InputPair};
use std::time::Duration;
#[test]
fn test_constant_time_compare() {
let secret = [0u8; 32];
// Baseline matches secret (slow path if leaky)
// Sample is random (fast path if leaky)
let inputs = InputPair::new(
|| [0u8; 32],
|| rand::random::<[u8; 32]>(),
);
let outcome = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
.time_budget(Duration::from_secs(30))
.test(inputs, |input| {
// Test your comparison function
constant_time_eq(&secret, &input);
});
// Display prints nicely formatted output with colors
println!("{outcome}");
// Assert the test passed (panics on Fail, warns on Inconclusive/Unmeasurable)
assert!(outcome.passed(), "Timing leak detected");
}
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
// Your constant-time comparison implementation
subtle::ConstantTimeEq::ct_eq(a, b).into()
}
Terminal window
cargo test --test timing -- --test-threads=1 --nocapture

You should see output like:

[test_constant_time_compare]
tacet
──────────────────────────────────────────────────────────────
Samples: 6000 per class
Quality: Good
✓ No timing leak detected
Probability of leak: 0.0%
95% CI: 0.0–12.5 ns
──────────────────────────────────────────────────────────────

The library ran your function thousands of times with two types of inputs (baseline zeros and random samples), measured execution times, and compared the timing distributions using Bayesian statistics. The result tells you whether there’s a security-relevant timing difference above your chosen threshold.

For details on the statistical methodology, see How It Works.