Skip to content
Security
Skill

/constant-time-analysis

Detects timing side-channel vulnerabilities in cryptographic code. Use when implementing or reviewing crypto code, encountering division on secrets, secret-dependent branches, or constant-time programming questions in C, C++, Go, Rust, Swift, Java, Kotlin, C#, PHP, JavaScript,

From plugin
trailofbits-skills
7.1k81 skills30 agents8 commands2 MCP
Install
$ npx -y skills add trailofbits/skills --skill constant-time-analysis --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/constant-time-analysis

Context preview

The summary Claude sees to decide when to auto-load this skill.

Detects timing side-channel vulnerabilities in cryptographic code. Use when implementing or reviewing crypto code, encountering division on secrets, secret-dependent branches, or constant-time programming questions in C, C++, Go, Rust, Swift, Java, Kotlin, C#, PHP, JavaScript,

SKILL.md

constant-time-analysis.SKILL.md
name: constant-time-analysis
description: Detects timing side-channel vulnerabilities in cryptographic code. Use when implementing or reviewing crypto code, encountering division on secrets, secret-dependent branches, or constant-time programming questions in C, C++, Go, Rust, Swift, Java, Kotlin, C#, PHP, JavaScript, TypeScript, Python, or Ruby.
allowed-tools: Bash Read Grep Glob
effort: medium

Constant-Time Analysis

Compile the code, inspect the emitted assembly or bytecode for variable-time instructions, then decide which of the flagged operations actually touch secrets. The compilation step is mechanical; the triage step is the work.

When to Use

  • Implementing or reviewing a signature, encryption, KEM, or key derivation routine
  • Code applies `/` or `%` to a value derived from a key, plaintext, nonce, or token
  • The user mentions "constant-time", "timing attack", "side-channel", or "KyberSlash"
  • Reviewing functions named `sign`, `verify`, `encrypt`, `decrypt`, `derive_key`

When NOT to Use

  • **Measuring** timing variance on a running binary — use the `constant-time-testing` skill from the `testing-handbook-skills` plugin, which covers dudect and statistical approaches and may not be installed. This skill inspects compiler output statically and never executes the code under test.
  • Non-cryptographic code, or crypto code where every input is public
  • High-level API usage where a vetted library owns the constant-time guarantees
  • Cache and other microarchitectural side channels — the assembly view cannot see them

Language Routing

Read the guide for the target language before interpreting any findings; each one lists that language's dangerous instructions and the idiomatic constant-time replacements.

| Guide | Languages | | ----- | --------- | | [references/compiled.md](references/compiled.md) | C, C++, Go, Rust | | [references/swift.md](references/swift.md) | Swift | | [references/vm-compiled.md](references/vm-compiled.md) | Java, C# | | [references/kotlin.md](references/kotlin.md) | Kotlin | | [references/php.md](references/php.md) | PHP | | [references/javascript.md](references/javascript.md) | JavaScript, TypeScript | | [references/python.md](references/python.md) | Python | | [references/ruby.md](references/ruby.md) | Ruby |

Running the Analyzer

The analyzer takes one file and detects the language from its extension. **Always pass `--warnings`:**

uv run {baseDir}/ct_analyzer/analyzer.py --warnings <source_file>

Without it the analyzer reports only error-severity findings, which means division, modulo and weak RNG. Four detector families are warning severity and stay silent: secret-dependent branches, early-exit comparison (`memcmp`, `strcmp`, `.equals`, `==`), table lookups indexed by a secret, and variable-time encoding. Early-exit comparison of an authentication tag is the most common timing bug in real code — Lucky Thirteen was exactly that — so a default run is quiet about the finding you are most likely to have.

| Flag | Effect | | ---- | ------ | | `--warnings` | Add the four warning-severity families above. Pass it every time | | `--func <regex>` | Restrict output to function names matching the regex | | `--json` | Machine-readable output | | `--github` | GitHub Actions annotations | | `--arch <target>` | Target architecture (`x86_64`, `arm64`, `riscv64`, ...) — native languages only | | `--opt-level <level>` | Optimization level (`O0` through `O3`, `Os`, `Oz`) — native languages only | | `--compiler <name>` | Override compiler choice (`gcc`, `clang`, `go`, `rustc`, `swiftc`) |

Narrow a large file to the routines that handle secrets with a regex, for example `--func 'sign|verify'`.

**Run natively compiled code (C, C++, Go, Rust, Swift) at more than one `--arch` and `--opt-level`.** Division timing and branch lowering are architecture- and optimization-dependent: x86_64 `IDIV` and arm64 `SDIV` differ, and a `cmov` at `-O2` can become a branch at `-O0`. A single clean run proves one configuration safe, not the code.

**How `--arch` crosses depends on the toolchain.** clang crosses with `--target` and needs no second compiler, but any source that includes libc headers also needs that target's C library headers — `libc6-dev-riscv64-cross` and friends — or it fails with `bits/libc-header-start.h file not found`. Go cross-builds through `GOARCH`, though `go tool objdump` has no riscv64 disassembler. A GNU cross toolchain is a *separate binary*, so gcc needs it named explicitly — `--compiler x86_64-linux-gnu-gcc`, `--compiler riscv64-linux-gnu-gcc` — and nothing is substituted for you, so the report always names the binary that ran. rustc needs the target's standard library (`rustup target add`), and Swift on Linux targets only the host. Compare against the toolchain that builds your product, not whichever cross build a distribution packages.

**Re-run the whole sweep on the fix, across compilers, targets and every level including `Os` and `Oz`.** Any fix that works by handing the compiler a constant divisor to strength-reduce is a fix only where the compiler chooses to cooperate, and that choice varies more than it looks. Replacing `key_coef / (2 * gamma2)` with a `#define`d divisor still emits a real divide here:

| Toolchain | Levels that emit a division | | --------- | --------------------------- | | gcc riscv64 | `O0` through `Oz` — every level | | gcc arm64, gcc x86_64 | `Os`, `Oz` | | clang arm64 | `O0`, `Oz` |

Strength reduction is an optimizer courtesy, not a language guarantee. Prefer an explicit multiply-shift, and verify it against the original expression over the full input range rather than on sampled values — an off-by-a-power-of-two reciprocal matches for millions of inputs before it diverges.

Java, Kotlin, and C# compile to JVM/CIL bytecode. The analyzer reads that bytecode, so `--arch` and `--opt-level` do not apply and the JIT may still introduce variable-time native code the analyzer cannot see.

Per-languag

Read more
Ships withtrailofbits-skills

A Claude Code plugin marketplace from Trail of Bits providing skills to enhance AI-assisted security analysis, testing, and development workflows. Codex can load this marketplace through its Claude marketplace compatibility.

Get the whole plugin

Other skills on trailofbits-skills.