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\n", result.Effect.TotalNs(), 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 generate github.com/agucova/tacet/crates/tacet-go/...The go generate command downloads the pre-built static library for your platform (~12MB). This only needs to be run once.
Requirements: Go 1.21+ with CGo enabled.
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.21+ 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 download a specific version of the library:
TIMING_ORACLE_VERSION=v0.1.0 go generate github.com/agucova/tacet/crates/tacet-go/...Test 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 { ShiftNs float64 TailNs float64 CredibleInterval [2]float64 Pattern EffectPattern}
func (e Effect) TotalNs() float64 // L2 norm of shift and tailQuality
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\n", result.Effect.TotalNs()) default: fmt.Printf("Test incomplete: %v\n", result.Outcome) }}