# C API

<ApiLanguageSwitcher currentLanguage="c" />

C bindings for tacet, providing low-overhead timing side-channel detection.

## Quick Reference

```c
#include <tacet.h>

// Callback for sample collection
void collect(uint64_t* baseline, uint64_t* sample, size_t count, void* ctx) {
    for (size_t i = 0; i < count; i++) {
        baseline[i] = measure(generate_baseline());
        sample[i] = measure(generate_sample());
    }
}

int main(void) {
    // Configure based on attacker model
    ToConfig cfg = to_config_default(AdjacentNetwork);
    cfg.time_budget_secs = 30.0;
    cfg.max_samples = 100000;
    cfg = to_config_from_env(cfg);  // CI can set TO_TIME_BUDGET_SECS, etc.

    ToResult result;
    to_test(&cfg, collect, NULL, &result);

    switch (result.outcome) {
        case Pass:
            printf("No leak: P(leak)=%.1f%%\n", result.leak_probability * 100.0);
            break;
        case Fail:
            printf("Leak: %.1f ns (%s)\n",
                   result.effect.max_effect_ns,
                   to_pattern_string(result.effect.tail_diagnostics.pattern));
            break;
        case Inconclusive:
            printf("Inconclusive\n");
            break;
        case Unmeasurable:
            printf("Too fast to measure\n");
            break;
    }

    return result.outcome == Fail ? 1 : 0;
}
```

---

## Installation

**Quick Install (Recommended):**

```bash
# Install script (Linux/macOS)
curl -fsSL https://raw.githubusercontent.com/agucova/tacet/main/install.sh | bash

# Or Homebrew (macOS)
brew install https://raw.githubusercontent.com/agucova/tacet/main/homebrew/Formula/tacet-c.rb

# Verify
pkg-config --modversion tacet
```

**Build from Source:**

```bash
git clone https://github.com/agucova/tacet
cd tacet
cargo build --release -p tacet-c

# Artifacts:
# - Header: crates/tacet-c/include/tacet.h
# - Library: target/release/libtacet_c.a
# - pkg-config: target/release/tacet.pc
```

See [Installation Guide](https://tacet.sh/getting-started/installation) for more details.

---

## Configuration

### Creating a Configuration

Start by creating a configuration for your attacker model, then customize as needed:

```c
// Create config for your attacker model
ToConfig cfg = to_config_default(AdjacentNetwork);

// Or use convenience functions
ToConfig cfg = to_config_adjacent_network();  // 100 ns threshold
ToConfig cfg = to_config_shared_hardware();   // ~0.6 ns threshold
ToConfig cfg = to_config_remote_network();    // 50 μs threshold
```

### Customizing Configuration

```c
ToConfig cfg = to_config_default(AdjacentNetwork);
cfg.time_budget_secs = 60.0;
cfg.max_samples = 50000;
cfg.pass_threshold = 0.01;   // More confident pass
cfg.fail_threshold = 0.99;   // More confident fail
cfg.seed = 12345;            // Reproducibility
cfg.custom_threshold_ns = 500.0;  // Override threshold
```

### Environment Variable Override

Allow CI systems to override configuration without recompiling:

```c
ToConfig cfg = to_config_default(AdjacentNetwork);
cfg.time_budget_secs = 30.0;
cfg.max_samples = 100000;
cfg = to_config_from_env(cfg);  // Merges TO_* env vars
```

Supported environment variables:
- `TO_TIME_BUDGET_SECS` - Time budget in seconds (float)
- `TO_MAX_SAMPLES` - Maximum samples per class (integer)
- `TO_PASS_THRESHOLD` - Pass threshold for P(leak) (float, e.g., 0.05)
- `TO_FAIL_THRESHOLD` - Fail threshold for P(leak) (float, e.g., 0.95)
- `TO_SEED` - Random seed for reproducibility (integer)
- `TO_THRESHOLD_NS` - Custom threshold in nanoseconds (float)

### Attacker Models

| Enum Value | Threshold | Use Case |
|------------|-----------|----------|
| `SharedHardware` | ~0.6 ns | SGX, containers, hyperthreading |
| `PostQuantum` | ~3.3 ns | Post-quantum crypto |
| `AdjacentNetwork` | 100 ns | LAN, HTTP/2 |
| `RemoteNetwork` | 50 μs | Public internet |
| `Research` | ~0 | Detect any difference |

---

## Callback-Based API (Recommended)

The `to_test()` function handles the full adaptive sampling loop internally.

### Collect Callback

```c
typedef void (*ToCollectFn)(
    uint64_t* baseline_out,  // Fill with baseline timing samples
    uint64_t* sample_out,    // Fill with sample timing samples
    size_t count,            // Number of samples to collect per class
    void* user_ctx           // User context pointer
);
```

The callback is invoked multiple times. Each invocation should:
1. Collect `count` baseline timing measurements into `baseline_out`
2. Collect `count` sample timing measurements into `sample_out`
3. Use interleaved sampling for best statistical properties

### Running a Test

```c
ToError to_test(
    const ToConfig* config,
    ToCollectFn collect_fn,
    void* user_ctx,
    ToResult* result_out
);
```

**Example:**

```c
void collect(uint64_t* baseline, uint64_t* sample, size_t count, void* ctx) {
    for (size_t i = 0; i < count; i++) {
        // Baseline: all-zero input
        uint8_t input[32] = {0};
        uint64_t start = read_timer();
        my_crypto_op(input, 32);
        uint64_t end = read_timer();
        baseline[i] = end - start;

        // Sample: random input
        arc4random_buf(input, 32);
        start = read_timer();
        my_crypto_op(input, 32);
        end = read_timer();
        sample[i] = end - start;
    }
}

int main(void) {
    ToConfig cfg = to_config_default(AdjacentNetwork);
    cfg.time_budget_secs = 30.0;
    cfg.max_samples = 100000;
    ToResult result;

    ToError err = to_test(&cfg, collect, NULL, &result);
    if (err != Ok) {
        fprintf(stderr, "Test failed: %d\n", err);
        return 1;
    }

    // Handle result...
    return result.outcome == Fail ? 1 : 0;
}
```

---

## Low-Level Adaptive API

For more control, use the calibrate/step API:

```c
// 1. Collect calibration samples
uint64_t baseline[5000], sample[5000];
collect_samples(baseline, sample, 5000);

// 2. Calibrate
ToError err;
ToCalibration* cal = to_calibrate(baseline, sample, 5000, &config, &err);

// 3. Create state
ToState* state = to_state_new();

// 4. Adaptive loop
double start_time = get_time();
while (1) {
    uint64_t batch_b[1000], batch_s[1000];
    collect_samples(batch_b, batch_s, 1000);

    ToStepResult step;
    to_step(cal, state, batch_b, batch_s, 1000, &config,
            get_time() - start_time, &step);

    if (step.has_decision) {
        // step.result contains the final ToResult
        break;
    }
}

// 5. Clean up
to_state_free(state);
to_calibration_free(cal);
```

---

## One-Shot Analysis

For pre-collected data:

```c
uint64_t baseline[10000], sample[10000];
// ... collect all samples ...

ToResult result;
ToError err = to_analyze(baseline, sample, 10000, &config, &result);
```

---

## Result Types

### Outcome Enum

```c
typedef enum {
    Pass = 0,        // No leak detected
    Fail = 1,        // Leak confirmed
    Inconclusive = 2,// Cannot decide
    Unmeasurable = 3 // Operation too fast
} ToOutcome;
```

### Result Structure

```c
typedef struct {
    ToOutcome outcome;
    double leak_probability;      // P(effect > threshold | data)
    ToEffect effect;              // Effect size estimate
    ToMeasurementQuality quality; // Measurement precision
    uint64_t samples_used;        // Samples per class
    double elapsed_secs;          // Test duration
    ToExploitability exploitability;  // Exploitability assessment
    ToInconclusiveReason inconclusive_reason;
    double mde_shift_ns;          // Minimum detectable shift
    double mde_tail_ns;           // Minimum detectable tail
    double theta_user_ns;         // User's requested threshold
    double theta_eff_ns;          // Effective threshold used
    double theta_floor_ns;        // Measurement floor
    double timer_resolution_ns;   // Timer resolution
    ToDiagnostics diagnostics;    // Detailed diagnostics
} ToResult;
```

### Effect Structure

```c
typedef struct {
    double max_effect_ns;  // L∞ norm of W₁ distance
    double ci_low_ns;      // 95% credible interval lower
    double ci_high_ns;     // 95% credible interval upper
    ToTailDiagnostics tail_diagnostics;  // Shift-vs-tail decomposition
} ToEffect;
```

### Tail Diagnostics Structure

The `ToTailDiagnostics` structure decomposes the timing effect into uniform shift and tail components:

```c
typedef struct {
    double shift_ns;           // Median of rank-matched differences
    double tail_ns;            // Mean absolute deviation from shift
    double tail_share;         // Fraction of effect from tail (0.0-1.0)
    double tail_slow_share;    // Fraction of tail mass that's positive
    ToQuantileShifts quantile_shifts;  // Per-quantile timing differences
    ToEffectPattern pattern;   // Automatic classification
} ToTailDiagnostics;

typedef struct {
    double p50_ns;  // Median shift
    double p90_ns;  // 90th percentile shift
    double p95_ns;  // 95th percentile shift
    double p99_ns;  // 99th percentile shift
} ToQuantileShifts;

typedef enum {
    TailEffect = 0,    // >50% of effect in slow outliers
    UniformShift = 1,  // <30% of effect in tail
    Mixed = 2,         // 30-50% in tail
    Negligible = 3     // Total effect below measurement floor
} ToEffectPattern;
```

**Understanding the decomposition:**
- **shift_ns**: Median timing difference across all observations (aligned by rank). Non-zero values indicate 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**: Fraction of total W₁ distance from tail deviations. Values above 0.5 indicate tail-dominated effects.

**Helper Function:**

```c
const char* to_pattern_string(ToEffectPattern pattern);
// Returns: "tail effect", "uniform shift", "mixed", or "negligible"
```

**Example: Accessing effect details**

```c
if (result.outcome == Fail) {
    printf("Max effect: %.1f ns\n", result.effect.max_effect_ns);
    printf("Pattern: %s\n", to_pattern_string(result.effect.tail_diagnostics.pattern));

    ToTailDiagnostics* tail = &result.effect.tail_diagnostics;
    printf("Shift: %.1f ns, Tail: %.1f ns\n", tail->shift_ns, tail->tail_ns);

    // Interpret the effect pattern
    switch (tail->pattern) {
        case TailEffect:
            printf("Tail-heavy leak: %.0f%% from slow outliers\n", tail->tail_share * 100.0);
            printf("p99: %.1f ns vs median: %.1f ns\n",
                   tail->quantile_shifts.p99_ns,
                   tail->quantile_shifts.p50_ns);
            break;
        case UniformShift:
            printf("Uniform timing shift: %.1f ns across distribution\n", tail->shift_ns);
            break;
        case Mixed:
            printf("Mixed effect: %.1f ns shift + %.1f ns tail\n",
                   tail->shift_ns, tail->tail_ns);
            break;
        case Negligible:
            printf("Effect below measurement floor\n");
            break;
    }
}
```

### Exploitability

```c
typedef enum {
    SharedHardwareOnly = 0,  // < 10 ns
    Http2Multiplexing = 1,   // 10-100 ns
    StandardRemote = 2,      // 100 ns - 10 μs
    ObviousLeak = 3          // > 10 μs
} ToExploitability;
```

### Measurement Quality

```c
typedef enum {
    Excellent = 0,  // MDE < 5 ns
    Good = 1,       // MDE 5-20 ns
    Poor = 2,       // MDE 20-100 ns
    TooNoisy = 3    // MDE > 100 ns
} ToMeasurementQuality;
```

---

## Error Handling

```c
typedef enum {
    Ok = 0,
    NullPointer = 1,
    InvalidConfig = 2,
    CalibrationFailed = 3,
    AnalysisFailed = 4,
    NotEnoughSamples = 5
} ToError;
```

---

## Complete Example

```c
#include <tacet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

// Platform-specific timer
#if defined(__x86_64__)
static inline uint64_t read_timer(void) {
    uint32_t lo, hi;
    __asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
    return ((uint64_t)hi << 32) | lo;
}
#elif defined(__aarch64__)
static inline uint64_t read_timer(void) {
    uint64_t val;
    __asm__ volatile("mrs %0, cntvct_el0" : "=r"(val));
    return val;
}
#endif

#define DO_NOT_OPTIMIZE(x) __asm__ volatile("" : : "r,m"(x) : "memory")

static uint8_t secret[32];

// Leaky comparison (for testing)
static int leaky_compare(const uint8_t* a, const uint8_t* b, size_t n) {
    for (size_t i = 0; i < n; i++) {
        if (a[i] != b[i]) return 0;  // Early exit!
    }
    return 1;
}

void collect(uint64_t* baseline, uint64_t* sample, size_t count, void* ctx) {
    uint8_t input[32];

    for (size_t i = 0; i < count; i++) {
        // Baseline: all zeros
        memset(input, 0, 32);
        uint64_t start = read_timer();
        int result = leaky_compare(input, secret, 32);
        uint64_t end = read_timer();
        DO_NOT_OPTIMIZE(result);
        baseline[i] = end - start;

        // Sample: random
        arc4random_buf(input, 32);
        start = read_timer();
        result = leaky_compare(input, secret, 32);
        end = read_timer();
        DO_NOT_OPTIMIZE(result);
        sample[i] = end - start;
    }
}

int main(void) {
    arc4random_buf(secret, sizeof(secret));

    ToConfig cfg = to_config_default(AdjacentNetwork);
    cfg.time_budget_secs = 30.0;
    cfg.max_samples = 100000;
    cfg = to_config_from_env(cfg);

    printf("Running timing test...\n");

    ToResult result;
    ToError err = to_test(&cfg, collect, NULL, &result);

    if (err != Ok) {
        fprintf(stderr, "Test failed: %d\n", err);
        return 1;
    }

    printf("Outcome: %s\n",
           result.outcome == Pass ? "PASS" :
           result.outcome == Fail ? "FAIL" :
           result.outcome == Inconclusive ? "INCONCLUSIVE" : "UNMEASURABLE");
    printf("Leak probability: %.1f%%\n", result.leak_probability * 100.0);
    printf("Effect: %.2f ns (%s)\n",
           result.effect.max_effect_ns,
           to_pattern_string(result.effect.tail_diagnostics.pattern));
    printf("Samples: %llu, Time: %.2fs\n",
           (unsigned long long)result.samples_used, result.elapsed_secs);

    return result.outcome == Fail ? 1 : 0;
}
```

### Compilation

**With pkg-config (recommended):**

```bash
cc -O3 -Wall -std=c11 timing_test.c $(pkg-config --cflags --libs tacet) -o timing_test
```

**Manual linking:**

```bash
# macOS
cc -O3 -Wall -std=c11 timing_test.c -o timing_test \
    -I/usr/local/include/tacet \
    -L/usr/local/lib -ltacet_c \
    -framework Security -framework CoreFoundation

# Linux
cc -O3 -Wall -std=c11 timing_test.c -o timing_test \
    -I/usr/local/include/tacet \
    -L/usr/local/lib -ltacet_c \
    -lpthread -ldl -lm
```