Go API
Go bindings for tacet.
Quick Reference
Section titled “Quick Reference”package main
import ( "fmt" "log" "time"
tacet "github.com/agucova/tacet/crates/tacet-go")
func main() { result, err := tacet.Test( tacet.NewZeroGenerator(0), // Baseline: all zeros tacet.FuncOperation(func(input []byte) { myCryptoFunction(input) }), 32, // Input size in bytes tacet.WithAttacker(tacet.AdjacentNetwork), tacet.WithTimeBudget(30 * time.Second), ) if err != nil { log.Fatal(err) }
switch result.Outcome { case tacet.Pass: fmt.Printf("No leak: P(leak)=%.1f%%\n", result.LeakProbability*100) case tacet.Fail: fmt.Printf("Leak: %.1f ns (%s), %s\n", result.Effect.MaxEffectNs, result.Effect.TailDiagnostics.PatternLabel, result.Exploitability) case tacet.Inconclusive: fmt.Printf("Inconclusive: %s\n", result.InconclusiveReason) case tacet.Unmeasurable: fmt.Printf("Too fast: %s\n", result.Recommendation) }}Installation
Section titled “Installation”go get github.com/agucova/tacet/crates/tacet-gogo run github.com/agucova/tacet/crates/tacet-go/cmd/tacet-install@latestThe install command downloads the pre-built static library for your platform (~12MB) and places it in the module cache where CGo can find it. This only needs to be run once.
Requirements: Go 1.22+ with CGo enabled.
Troubleshooting: If you see linker errors about missing tacet symbols, run the install command above. The native library must be installed before building.
Platform Support
Section titled “Platform Support”| Platform | Architecture | Status |
|---|---|---|
| macOS | ARM64 (Apple Silicon) | ✅ Supported |
| macOS | AMD64 (Intel) | ✅ Supported |
| Linux | ARM64 | ✅ Supported |
| Linux | AMD64 | ✅ Supported |
The library is statically linked, so binaries are self-contained with no runtime dependencies.
Building from Source
Section titled “Building from Source”If you prefer to build the native library yourself instead of downloading pre-built binaries:
Prerequisites
Section titled “Prerequisites”- Rust toolchain (stable)
- Go 1.22+ with CGo enabled
- C compiler (clang or gcc)
Build Steps
Section titled “Build Steps”# Clone the repositorygit clone https://github.com/agucova/tacetcd tacet
# Build the C librarycargo build -p tacet-c --release
# Strip debug symbols (reduces size from ~26MB to ~12MB)strip -S target/release/libtacet_c.a # macOS# or: strip --strip-debug target/release/libtacet_c.a # Linux
# Copy to the appropriate platform directorymkdir -p crates/tacet-go/internal/ffi/lib/$(go env GOOS)_$(go env GOARCH)cp target/release/libtacet_c.a \ crates/tacet-go/internal/ffi/lib/$(go env GOOS)_$(go env GOARCH)/
# Verify it workscd crates/tacet-gogo test -v -short -run TestTimerWorksSpecifying a Version
Section titled “Specifying a Version”To install a specific version of the library:
go run github.com/agucova/tacet/crates/tacet-go/cmd/tacet-install@latest -version=v0.2.3Test Function
Section titled “Test Function”func Test(gen Generator, op Operation, inputSize int, opts ...Option) (*Result, error)| Parameter | Type | Description |
|---|---|---|
gen | Generator | Input generator |
op | Operation | Operation to test |
inputSize | int | Input buffer size |
opts | ...Option | Configuration options |
Configuration Options
Section titled “Configuration Options”Attacker Models
Section titled “Attacker Models”func WithAttacker(model AttackerModel) Option| Model | Threshold | Use Case |
|---|---|---|
SharedHardware | 0.6 ns | SGX, containers |
PostQuantum | 3.3 ns | Post-quantum crypto |
AdjacentNetwork | 100 ns | LAN, HTTP/2 |
RemoteNetwork | 50 μs | Internet |
Research | 0 | Detect any difference |
Budget Options
Section titled “Budget Options”func WithTimeBudget(d time.Duration) Optionfunc WithMaxSamples(n int) Option| Option | Default | Description |
|---|---|---|
WithTimeBudget | 30s | Maximum test time |
WithMaxSamples | 100,000 | Max samples per class |
Threshold Options
Section titled “Threshold Options”func WithPassThreshold(p float64) Option // Default: 0.05func WithFailThreshold(p float64) Option // Default: 0.95func WithCustomThreshold(ns float64) OptionGenerators and Operations
Section titled “Generators and Operations”Generator Interface
Section titled “Generator Interface”type Generator interface { Generate(isBaseline bool, output []byte)}Built-in Generators
Section titled “Built-in Generators”// Fixed zero bytes for baseline, random for sampletacet.NewZeroGenerator(seed)
// Fixed value for baseline, random for sampletacet.NewFixedGenerator(fixedValue, seed)
// Custom generator from functiontacet.FuncGenerator(func(isBaseline bool, output []byte) { // ...})Operation Interface
Section titled “Operation Interface”type Operation interface { Execute(input []byte)}// From functiontacet.FuncOperation(func(input []byte) { myCryptoFunction(input)})Result Types
Section titled “Result Types”Outcome
Section titled “Outcome”type Outcome int
const ( Pass Outcome = iota Fail Inconclusive Unmeasurable)Result
Section titled “Result”type Result struct { Outcome Outcome LeakProbability float64 Effect Effect Quality Quality Exploitability Exploitability InconclusiveReason InconclusiveReason SamplesUsed int Recommendation string}Effect
Section titled “Effect”type Effect struct { MaxEffectNs float64 // L∞ norm of W₁ distance CredibleInterval [2]float64 // 95% credible interval TailDiagnostics TailDiagnostics // Shift-vs-tail decomposition}TailDiagnostics
Section titled “TailDiagnostics”The TailDiagnostics structure decomposes the timing effect into uniform shift and tail components:
type TailDiagnostics struct { ShiftNs float64 // Median of rank-matched differences TailNs float64 // Mean absolute deviation from shift TailShare float64 // Fraction of effect from tail (0.0-1.0) TailSlowShare float64 // Fraction of tail mass that's positive QuantileShifts QuantileShifts // Per-quantile timing differences PatternLabel EffectPattern // Automatic classification}
type QuantileShifts struct { P50Ns float64 // Median shift P90Ns float64 // 90th percentile shift P95Ns float64 // 95th percentile shift P99Ns float64 // 99th percentile shift}
type EffectPattern int
const ( TailEffect EffectPattern = iota // >50% of effect in slow outliers UniformShift // <30% of effect in tail Mixed // 30-50% in tail Negligible // Total effect below measurement floor)
func (p EffectPattern) String() string // "tail effect", "uniform shift", "mixed", "negligible"Understanding the decomposition:
- ShiftNs: Median timing difference across all observations (aligned by rank). Non-zero values indicate consistent timing differences across the entire distribution.
- TailNs: Measures how much individual measurements deviate from the uniform shift. High values indicate some operations are disproportionately slow.
- TailShare: Fraction of total W₁ distance from tail deviations. Values above 0.5 indicate tail-dominated effects.
Example: Accessing effect details
if result.Outcome == tacet.Fail { fmt.Printf("Max effect: %.1f ns\n", result.Effect.MaxEffectNs) fmt.Printf("Pattern: %s\n", result.Effect.TailDiagnostics.PatternLabel)
tail := result.Effect.TailDiagnostics fmt.Printf("Shift: %.1f ns, Tail: %.1f ns\n", tail.ShiftNs, tail.TailNs)
// Interpret the effect pattern switch tail.PatternLabel { case tacet.TailEffect: fmt.Printf("Tail-heavy leak: %.0f%% from slow outliers\n", tail.TailShare*100) fmt.Printf("p99: %.1f ns vs median: %.1f ns\n", tail.QuantileShifts.P99Ns, tail.QuantileShifts.P50Ns) case tacet.UniformShift: fmt.Printf("Uniform timing shift: %.1f ns across distribution\n", tail.ShiftNs) case tacet.Mixed: fmt.Printf("Mixed effect: %.1f ns shift + %.1f ns tail\n", tail.ShiftNs, tail.TailNs) case tacet.Negligible: fmt.Println("Effect below measurement floor") }}Quality
Section titled “Quality”type Quality int
const ( Excellent Quality = iota // MDE < 5 ns Good // MDE 5-20 ns Poor // MDE 20-100 ns TooNoisy // MDE > 100 ns)Exploitability
Section titled “Exploitability”type Exploitability int
const ( SharedHardwareOnly Exploitability = iota // < 10 ns Http2Multiplexing // 10-100 ns StandardRemote // 100 ns - 10 us ObviousLeak // > 10 us)Complete Example
Section titled “Complete Example”package main
import ( "crypto/subtle" "fmt" "time"
"github.com/agucova/tacet/crates/tacet-go")
func main() { secret := make([]byte, 32) copy(secret, []byte("my-secret-key-here-32-bytes-long"))
result, _ := tacet.Test( tacet.NewFixedGenerator(secret, 42), tacet.FuncOperation(func(input []byte) { subtle.ConstantTimeCompare(secret, input) }), 32, tacet.WithAttacker(tacet.AdjacentNetwork), tacet.WithTimeBudget(30*time.Second), )
switch result.Outcome { case tacet.Pass: fmt.Println("Constant-time comparison verified") case tacet.Fail: fmt.Printf("Timing leak detected! Effect: %.1f ns (%s)\n", result.Effect.MaxEffectNs, result.Effect.TailDiagnostics.PatternLabel) default: fmt.Printf("Test incomplete: %v\n", result.Outcome) }}