Skip to content

Go bindings for tacet.

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)
}
}

Terminal window
go get github.com/agucova/tacet/crates/tacet-go
go 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.

PlatformArchitectureStatus
macOSARM64 (Apple Silicon)✅ Supported
macOSAMD64 (Intel)✅ Supported
LinuxARM64✅ Supported
LinuxAMD64✅ Supported

The library is statically linked, so binaries are self-contained with no runtime dependencies.


If you prefer to build the native library yourself instead of downloading pre-built binaries:

  • Rust toolchain (stable)
  • Go 1.21+ with CGo enabled
  • C compiler (clang or gcc)
Terminal window
# Clone the repository
git clone https://github.com/agucova/tacet
cd tacet
# Build the C library
cargo 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 directory
mkdir -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 works
cd crates/tacet-go
go test -v -short -run TestTimerWorks

To download a specific version of the library:

Terminal window
TIMING_ORACLE_VERSION=v0.1.0 go generate github.com/agucova/tacet/crates/tacet-go/...

func Test(gen Generator, op Operation, inputSize int, opts ...Option) (*Result, error)
ParameterTypeDescription
genGeneratorInput generator
opOperationOperation to test
inputSizeintInput buffer size
opts...OptionConfiguration options

func WithAttacker(model AttackerModel) Option
ModelThresholdUse Case
SharedHardware0.6 nsSGX, containers
PostQuantum3.3 nsPost-quantum crypto
AdjacentNetwork100 nsLAN, HTTP/2
RemoteNetwork50 μsInternet
Research0Detect any difference
func WithTimeBudget(d time.Duration) Option
func WithMaxSamples(n int) Option
OptionDefaultDescription
WithTimeBudget30sMaximum test time
WithMaxSamples100,000Max samples per class
func WithPassThreshold(p float64) Option // Default: 0.05
func WithFailThreshold(p float64) Option // Default: 0.95
func WithCustomThreshold(ns float64) Option

type Generator interface {
Generate(isBaseline bool, output []byte)
}
// Fixed zero bytes for baseline, random for sample
tacet.NewZeroGenerator(seed)
// Fixed value for baseline, random for sample
tacet.NewFixedGenerator(fixedValue, seed)
// Custom generator from function
tacet.FuncGenerator(func(isBaseline bool, output []byte) {
// ...
})
type Operation interface {
Execute(input []byte)
}
// From function
tacet.FuncOperation(func(input []byte) {
myCryptoFunction(input)
})

type Outcome int
const (
Pass Outcome = iota
Fail
Inconclusive
Unmeasurable
)
type Result struct {
Outcome Outcome
LeakProbability float64
Effect Effect
Quality Quality
Exploitability Exploitability
InconclusiveReason InconclusiveReason
SamplesUsed int
Recommendation string
}
type Effect struct {
ShiftNs float64
TailNs float64
CredibleInterval [2]float64
Pattern EffectPattern
}
func (e Effect) TotalNs() float64 // L2 norm of shift and tail
type Quality int
const (
Excellent Quality = iota // MDE < 5 ns
Good // MDE 5-20 ns
Poor // MDE 20-100 ns
TooNoisy // MDE > 100 ns
)
type Exploitability int
const (
SharedHardwareOnly Exploitability = iota // < 10 ns
Http2Multiplexing // 10-100 ns
StandardRemote // 100 ns - 10 us
ObviousLeak // > 10 us
)

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)
}
}