Rust API
Complete API documentation for the Rust implementation of tacet.
Quick Reference
Section titled “Quick Reference”use tacet::{TimingOracle, AttackerModel, Outcome, helpers::InputPair};
// Create inputs using closureslet inputs = InputPair::new( || [0u8; 32], // baseline: returns constant value || rand::random(), // sample: generates varied values);
// Run test with attacker modellet outcome = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork) .test(inputs, |data| my_function(data));
// Simple: Display prints formatted output with colorsprintln!("{outcome}");assert!(outcome.passed(), "Timing leak detected");
// Or access detailed fields when neededif outcome.failed() { let effect = outcome.effect().unwrap(); eprintln!("Effect: {:.1}ns ({})", effect.max_effect_ns, effect.tail_diagnostics.pattern_label);}TimingOracle Builder
Section titled “TimingOracle Builder”Attacker Model Presets
Section titled “Attacker Model Presets”use tacet::{TimingOracle, AttackerModel};
// Shared hardware: SGX, cross-VM, containers (~0.6ns / ~2 cycles)TimingOracle::for_attacker(AttackerModel::SharedHardware)
// Post-quantum sentinel: Catch KyberSlash-class leaks (~3.3ns / ~10 cycles)TimingOracle::for_attacker(AttackerModel::PostQuantumSentinel)
// Adjacent network: LAN or HTTP/2 endpoints (100ns) - DEFAULTTimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
// Remote network: Public APIs, general internet (50us)TimingOracle::for_attacker(AttackerModel::RemoteNetwork)
// Research: Detect any difference (not for CI)TimingOracle::for_attacker(AttackerModel::Research)
// Custom threshold in nanosecondsTimingOracle::for_attacker(AttackerModel::Custom { threshold_ns: 500.0 })| Preset | Threshold | Use case |
|---|---|---|
SharedHardware | ~0.6 ns | SGX, cross-VM, containers |
PostQuantumSentinel | ~3.3 ns | Post-quantum crypto |
AdjacentNetwork | 100 ns | LAN, HTTP/2 |
RemoteNetwork | 50 us | Public APIs |
Research | 0 | Academic analysis |
Configuration Methods
Section titled “Configuration Methods”use tacet::{TimingOracle, AttackerModel, TimerSpec};use std::time::Duration;
let oracle = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork) // Decision thresholds .pass_threshold(0.05) // Below this = Pass (default: 0.05) .fail_threshold(0.95) // Above this = Fail (default: 0.95)
// Resource limits .time_budget(Duration::from_secs(30)) // Max time (default: 60s) .max_samples(100_000) // Hard cap (default: 1M)
// Advanced tuning .batch_size(1_000) // Samples per batch .calibration_samples(5_000) // Calibration samples .prior_no_leak(0.75) // Prior P(no leak) .warmup(1_000) // Warmup iterations
// Timer configuration .timer_spec(TimerSpec::Auto) // Auto-detect (default) .timer_spec(TimerSpec::CyclePrecision) // Require cycle-accurate timer .standard_timer() // Force standard timer
// Reproducibility .seed(42); // Deterministic seedOutcome Enum
Section titled “Outcome Enum”pub enum Outcome { Pass { leak_probability, effect, samples_used, quality, diagnostics }, Fail { leak_probability, effect, exploitability, samples_used, quality, diagnostics }, Inconclusive { reason, leak_probability, effect, samples_used, quality, diagnostics }, Unmeasurable { operation_ns, threshold_ns, platform, recommendation },}Outcome implements Display for formatted terminal output with colors:
println!("{outcome}"); // Prints formatted resultOutcome Methods
Section titled “Outcome Methods”impl Outcome { /// Check if test passed fn passed(&self) -> bool;
/// Check if test failed fn failed(&self) -> bool;
/// Check if result is conclusive (Pass or Fail) fn is_conclusive(&self) -> bool;
/// Check if operation was measurable fn is_measurable(&self) -> bool;
/// Check if measurement is reliable fn is_reliable(&self) -> bool;
/// Get leak probability if available fn leak_probability(&self) -> Option<f64>;
/// Get effect estimate if available fn effect(&self) -> Option<&EffectEstimate>;}Supporting Types
Section titled “Supporting Types”EffectEstimate
Section titled “EffectEstimate”pub struct EffectEstimate { pub max_effect_ns: f64, // L∞ norm of W₁ distance pub credible_interval_ns: (f64, f64), // 95% CI pub tail_diagnostics: TailDiagnostics, // Shift-vs-tail decomposition}The tail_diagnostics field decomposes the timing effect into uniform shift and tail components:
pub struct TailDiagnostics { pub shift_ns: f64, // Median of rank-matched differences pub tail_ns: f64, // Mean absolute deviation from shift pub tail_share: f64, // Fraction of effect from tail (0.0-1.0) pub tail_slow_share: f64, // Fraction of tail mass that's positive pub quantile_shifts: QuantileShifts, // Per-quantile timing differences pub pattern_label: EffectPattern, // Automatic classification}
pub struct QuantileShifts { pub p50_ns: f64, // Median shift pub p90_ns: f64, // 90th percentile shift pub p95_ns: f64, // 95th percentile shift pub p99_ns: f64, // 99th percentile shift}
pub enum EffectPattern { TailEffect, // >50% of effect in slow outliers UniformShift, // <30% of effect in tail, consistent across distribution Mixed, // 30-50% in tail, both components present Negligible, // Total effect below measurement floor}Understanding the decomposition:
- shift_ns: Represents the median timing difference across all observations (aligned by rank). A non-zero shift indicates consistent timing differences across the entire distribution.
- tail_ns: Measures how much individual measurements deviate from the uniform shift. High values indicate some operations are disproportionately slow.
- tail_share: The fraction of the total W₁ distance coming from tail deviations. Values above 0.5 indicate tail-dominated effects.
Example: Accessing effect details
if outcome.failed() { let effect = outcome.effect().unwrap(); let tail = &effect.tail_diagnostics;
println!("Max effect: {:.1}ns", effect.max_effect_ns); println!("Pattern: {}", tail.pattern_label); println!("Shift: {:.1}ns, Tail: {:.1}ns", tail.shift_ns, tail.tail_ns);
// Interpret the effect pattern match tail.pattern_label { EffectPattern::TailEffect => { println!("Tail-heavy leak: {:.0}% from slow outliers", tail.tail_share * 100.0); println!("p99: {:.1}ns vs median: {:.1}ns", tail.quantile_shifts.p99_ns, tail.quantile_shifts.p50_ns); } EffectPattern::UniformShift => { println!("Uniform timing shift: {:.1}ns across distribution", tail.shift_ns); } EffectPattern::Mixed => { println!("Mixed effect: {:.1}ns shift + {:.1}ns tail", tail.shift_ns, tail.tail_ns); } EffectPattern::Negligible => { println!("Effect below measurement floor"); } }}Exploitability
Section titled “Exploitability”pub enum Exploitability { SharedHardwareOnly, // < 10 ns Http2Multiplexing, // 10-100 ns StandardRemote, // 100 ns - 10 us ObviousLeak, // > 10 us}MeasurementQuality
Section titled “MeasurementQuality”pub enum MeasurementQuality { Excellent, // MDE < 5 ns Good, // MDE 5-20 ns Poor, // MDE 20-100 ns TooNoisy, // MDE > 100 ns}InconclusiveReason
Section titled “InconclusiveReason”pub enum InconclusiveReason { DataTooNoisy, NotLearning, WouldTakeTooLong, TimeBudgetExceeded, SampleBudgetExceeded, ConditionsChanged,}Helper Types
Section titled “Helper Types”InputPair
Section titled “InputPair”use tacet::helpers::InputPair;
// Create input pair with closureslet inputs = InputPair::new( || [0u8; 32], // baseline generator || rand::random(), // sample generator);
// Or from iteratorslet inputs = InputPair::from_iters( std::iter::repeat([0u8; 32]), (0..).map(|i| [i as u8; 32]),);Macro API
Section titled “Macro API”With the macros feature:
use tacet::{timing_test_checked, TimingOracle, AttackerModel};
let outcome = timing_test_checked! { oracle: TimingOracle::for_attacker(AttackerModel::AdjacentNetwork) .time_budget(Duration::from_secs(30)), baseline: || [0u8; 32], sample: || rand::random::<[u8; 32]>(), measure: |input| my_function(&input),};Configuration Defaults
Section titled “Configuration Defaults”| Parameter | Default | Description |
|---|---|---|
pass_threshold | 0.05 | P(leak) below this → Pass |
fail_threshold | 0.95 | P(leak) above this → Fail |
time_budget | 60 s | Maximum analysis time |
max_samples | 1,000,000 | Maximum samples per class |
batch_size | 1,000 | Samples per adaptive batch |
calibration_samples | 5,000 | Samples for calibration |
prior_no_leak | 0.75 | Prior probability of no leak |
warmup | 1,000 | Warmup iterations |
outlier_percentile | 0.9999 | Winsorization threshold |