Constant Time Analysis
Trail of BitsOfficialPublished by Trail of Bits
- Skill
- Review and audit
- Security
- CC-BY-SA-4.0
- Low risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Constant-Time Analysis is a static analysis tool designed to identify potential timing side-channel vulnerabilities in cryptographic implementations. By inspecting compiled assembly or bytecode, it flags operations that may exhibit variable-time execution, such as secret-dependent branches, divisions, or early-exit comparisons. The tool is intended for developers and security reviewers working on sensitive routines like encryption, signing, or key derivation across a wide range of languages including C, Rust, Go, Java, and JavaScript. It emphasizes a triage-based workflow where users must verify whether flagged instructions actually process secret data, distinguishing between true vulnerabilities and false positives.
What it does
- Scans compiled assembly or bytecode for variable-time instructions like division and modulo.
- Detects secret-dependent branches and early-exit comparisons that leak information.
- Provides a triage framework to help users verify if flagged operations involve secret data.
- Supports multiple languages including C, C++, Go, Rust, Swift, Java, Kotlin, C#, PHP, JS, TS, Python, and Ruby.
- Allows filtering by function name and provides machine-readable output for CI integration.
- Offers configuration options for different architectures and optimization levels to ensure comprehensive coverage.
When to use it
- When implementing or reviewing cryptographic primitives like encryption, signing, or KEMs.
- When code performs division or modulo operations on values derived from secrets.
- When you need to ensure code is resistant to timing attacks.
- When reviewing functions that handle sensitive data like keys, nonces, or authentication tags.
- When you want to verify that comparisons (e.g., MAC verification) are constant-time.
Advantages
- Enables proactive detection of timing vulnerabilities before deployment.
- Provides a structured approach to triaging findings to minimize false positives.
- Supports cross-architecture and cross-optimization level testing to catch configuration-specific bugs.
- Integrates into CI pipelines for automated security checks.
- Helps developers identify idiomatic constant-time replacements for dangerous operations.
Usage examples
- “Analyze my C implementation of a signature routine for timing leaks.”
- The tool will compile the code and report any variable-time instructions found in the signature function, which you then triage to see if they touch secret key material.
- “Check my TypeScript crypto code for early-exit comparisons.”
- The tool will analyze the transpiled bytecode to identify potential early-exit patterns in comparisons, prompting you to replace them with constant-time alternatives.
- “Run a constant-time analysis on my Go crypto package across multiple architectures.”
- The tool will perform the analysis for each specified architecture, helping you identify if compiler optimizations introduce timing variances on specific platforms.
- “How do I filter the analysis to only check my verify function?”
- You can use the --func flag to restrict the output to only the 'verify' function, reducing noise in large files.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/trailofbits/constant-time-analysis/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip constant-time-analysis.zip -d .agents/skills
Documentation by Trail of Bits
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 tokenThe 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-testingskill from thetesting-handbook-skillsplugin, 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 | C, C++, Go, Rust |
| references/swift.md | Swift |
| references/vm-compiled.md | Java, C# |
| references/kotlin.md | Kotlin |
| references/php.md | PHP |
| references/javascript.md | JavaScript, TypeScript |
| references/python.md | Python |
| 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 #defined 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-language coverage limits
Coverage is not uniform, and the gaps change what a clean report means:
| Language | What the report does not cover |
|---|---|
| Go | Only symbols from the analyzed file. go build links the runtime in, and its divisions — all on public data — would otherwise dominate the findings |
| JavaScript, TypeScript | Bytecode findings are restricted to functions the file declares by name, because V8 dumps node's internals the same way it dumps yours. Anonymous callbacks fall to the source scan. For TypeScript, bytecode findings name the function but carry no line, since V8's positions index the transpiled output |
| Python, Ruby, PHP | Bytecode reflects the interpreter that ran, not a JIT'd or alternative runtime |
| Rust | Analyzed as a library unless the file declares fn main; private functions with no caller may be optimized away before analysis |
| Swift | Targets the host platform on Linux; iOS and macOS triples need an Apple toolchain |
Since findings and silence both depend on the configuration, say which compiler, architecture, and optimization level produced a result when reporting it.
To sweep a directory, loop in the shell — the analyzer is a deterministic script, one invocation per file:
for f in src/crypto/*.c; do uv run {baseDir}/ct_analyzer/analyzer.py --warnings --json "$f"; donePrerequisites
| Language | Requirement |
|---|---|
| C, C++, Go, Rust | gcc/clang, go, rustc in PATH |
| Swift | Xcode or Swift toolchain (swiftc) |
| Java / Kotlin | JDK (javac, javap); Kotlin also needs kotlinc |
| C# | .NET SDK plus ilspycmd (dotnet tool install -g ilspycmd) |
| PHP | PHP with the VLD extension or OPcache |
| JavaScript / TypeScript | Node.js |
| Python | Python 3.x |
| Ruby | Ruby with --dump=insns support |
On a "toolchain not found" error, see references/vm-compiled.md for JVM and .NET installation, macOS keg-only PATH configuration, and troubleshooting.
Interpreting Results
PASSED — no error-severity finding for the configuration you ran. Warnings do not affect it, so Result: PASSED alongside Warnings: 6 is normal and is not a clean result. Read the warning list before concluding anything.
FAILED — dangerous instructions found, reported per function:
[ERROR] SDIV
Function: decompose_vulnerable
Reason: SDIV has early termination optimization; execution time depends on operand valuesTriaging Findings
The analyzer has no data flow analysis. It flags every dangerous instruction regardless of whether a secret reaches it, so a FAILED report is a worklist, not a verdict. Reporting the raw output as a set of vulnerabilities is the primary failure mode of this skill.
For each flagged instruction, read the source and answer one question: does an operand depend on secret data? Trace from the instruction's function back to the caller's inputs, then classify:
// FALSE POSITIVE: operands are a buffer length, already public from the ciphertext size
int num_blocks = data_len / 16;
// TRUE POSITIVE: dividend is a private-key coefficient; IDIV/SDIV leaks its magnitude
int32_t q = secret_coef / GAMMA2;| Question | If yes |
|---|---|
| Is the operand a compile-time constant? | Likely false positive |
| Is the operand a public parameter — length, count, index bound? | Likely false positive |
| Is the operand derived from a key, plaintext, nonce, or token? | True positive |
| Can an attacker influence the operand's value? | True positive |
State the verdict and the data flow that justifies it for every flagged item. A finding you cannot trace to a secret is not a finding; say so explicitly rather than dropping it silently.
{baseDir}/ct_analyzer/tests/triage_samples/ holds a known-answer case per language: each fixture pairs a true positive with a false positive that the analyzer reports identically, and expectations.json records which is which and why. triage_c.c is the shortest example — the analyzer flags the division in both ct_high_bits and ct_block_count, and correct triage confirms the first and clears the second.
Weak-RNG and encoding findings ask a different question. For Math.random, mt_rand, random.randint, System.Random and base64_encode, no operand is secret, so "does an operand depend on a secret?" does not resolve them. Ask instead what the result is used for: seeding a nonce or key is a true positive, jittering a retry delay is not. These are reported by a regex scan over the source rather than from bytecode, so they are attributed to <source> with a line number instead of to the enclosing function — except in PHP, where they carry the function.
Comparison and lookup findings have their own question, and their own fix. For an early-exit comparison, ask whether either side is secret: comparing an authentication tag, MAC, or password hash is a true positive, comparing a public protocol header is not. For a table lookup, ask whether the index is secret — the array's contents do not matter, only what selects the element. Both are exploitable as written, so a confirmed one needs the language's constant-time primitive rather than a rewrite of the loop:
| Language | Constant-time comparison |
|---|---|
| C, C++ | CRYPTO_memcmp (OpenSSL) or sodium_memcmp |
| Go | crypto/subtle.ConstantTimeCompare |
| Rust | the subtle crate's ConstantTimeEq |
| Java, Kotlin | MessageDigest.isEqual |
| C# | CryptographicOperations.FixedTimeEquals |
| PHP | hash_equals |
| Python | hmac.compare_digest |
| Ruby | OpenSSL.secure_compare |
| JavaScript, TypeScript | crypto.timingSafeEqual |
A secret-indexed lookup has no drop-in replacement: it needs a bit-sliced or arithmetic formulation that touches every element, which is why AES S-box tables are the classic case. Encoding a secret through a table — base64_encode, bin2hex, chr/ord — is the same problem in a library, and paragonie/constant_time_encoding is the reference fix for PHP.
Limitations
Static only — reads assembly and bytecode, never runtime behavior. Cache timing and other microarchitectural channels are invisible.
No data flow analysis — see triage above.
Configuration-specific — a different compiler, optimization level, architecture, or runtime version can emit different instructions from identical source.
Real-World Impact
KyberSlash (2023) — division instructions in ML-KEM implementations allowed key recovery
Lucky Thirteen (2013) — timing differences in CBC padding validation enabled plaintext recovery
RSA timing attacks — early implementations leaked private key bits through division timing
References
Cryptocoding Guidelines — defensive coding for crypto
KyberSlash — division timing in post-quantum crypto
BearSSL Constant-Time — practical constant-time techniques
Files
- ATTRIBUTION.mdAdded by AutoGineer960 B
- LICENSELicence file added by AutoGineer19.7 KiB
- README.md4.8 KiB
- SKILL.md12.9 KiB
- agents/openai.yaml240 B
- assets/trail-of-bits-mark.svg3.0 KiB
- references/compiled.md4.0 KiB
- references/javascript.md4.3 KiB
- references/kotlin.md6.6 KiB
- references/php.md5.1 KiB
- references/python.md5.1 KiB
- references/ruby.md5.3 KiB
- references/swift.md8.0 KiB
- references/vm-compiled.md11.3 KiB
references/vm-compiled.md
Constant-Time Analysis: VM-Compiled Languages
Analysis guidance for Java and C#. These languages compile to bytecode (JVM bytecode / CIL) that runs on a virtual machine with Just-In-Time (JIT) compilation to native code.
Understanding VM-Compiled Languages
Unlike native-compiled languages (C, Rust, Go), Java and C# add an intermediate layer:
Source Code (.java/.cs)
|
v
Compiler (javac/csc)
|
v
Bytecode (.class/.dll)
|
v
JIT Compiler (HotSpot/RyuJIT)
|
v
Native Code (at runtime)Security implications:
Bytecode is deterministic - Same source always produces same bytecode
JIT is non-deterministic - Native code varies by runtime, version, and warmup state
Analysis target - We analyze bytecode since JIT output is impractical to capture
Limitations:
JIT may introduce timing variations not visible in bytecode
Runtime optimizations can convert safe bytecode to unsafe native code
Different JVM/CLR implementations may behave differently
Running the Analyzer
# Java
uv run {baseDir}/ct_analyzer/analyzer.py CryptoUtils.java
# C#
uv run {baseDir}/ct_analyzer/analyzer.py CryptoUtils.cs
# Include conditional branch warnings
uv run {baseDir}/ct_analyzer/analyzer.py --warnings CryptoUtils.java
# Filter to specific methods
uv run {baseDir}/ct_analyzer/analyzer.py --func 'sign|verify' CryptoUtils.java
# CI-friendly JSON output
uv run {baseDir}/ct_analyzer/analyzer.py --json CryptoUtils.javaNote: The --arch and --opt-level flags do not apply to VM-compiled languages.
Dangerous Bytecode Instructions
JVM Bytecode
| Category | Instructions | Risk |
|---|---|---|
| Integer Division | idiv, ldiv, irem, lrem | Variable-time based on operand values |
| Floating Division | fdiv, ddiv, frem, drem | Variable latency |
| Conditional Branches | ifeq, ifne, iflt, ifge, ifgt, ifle, if_icmp*, if_acmp* | Timing leak if condition depends on secrets |
| Table Lookups | *aload, *astore, tableswitch, lookupswitch | Cache timing if index depends on secrets |
CIL (C# / .NET)
| Category | Instructions | Risk |
|---|---|---|
| Integer Division | div, div.un, rem, rem.un | Variable-time based on operand values |
| Floating Division | (uses same div/rem opcodes) | Variable latency |
| Conditional Branches | beq, bne, blt, bgt, ble, bge, brfalse, brtrue | Timing leak if condition depends on secrets |
| Table Lookups | ldelem.*, stelem.*, switch | Cache timing if index depends on secrets |
Constant-Time Patterns
Java
Replace Division
// VULNERABLE: Division instruction emitted
int q = secretValue / divisor;
// SAFE: Barrett reduction (for fixed divisor)
// Precompute: mu = (1L << 32) / divisor
long mu = 0x100000000L / divisor;
int q = (int) ((secretValue * mu) >>> 32);Replace Branches
// VULNERABLE: Branch timing reveals secret
int result;
if (secret != 0) {
result = a;
} else {
result = b;
}
// SAFE: Constant-time selection using bitwise ops
int mask = -(secret != 0 ? 1 : 0); // All 1s if true, all 0s if false
// Better: compute mask without branch
int mask = (secret | -secret) >> 31; // -1 if secret != 0, else 0
int result = (a & mask) | (b & ~mask);Replace Comparisons
// VULNERABLE: Arrays.equals() may early-terminate
if (Arrays.equals(computed, expected)) { ... }
// SAFE: Use MessageDigest.isEqual() for constant-time comparison
import java.security.MessageDigest;
if (MessageDigest.isEqual(computed, expected)) { ... }Secure Random
// VULNERABLE: Predictable PRNG
Random rand = new Random();
int value = rand.nextInt();
// SAFE: Cryptographically secure
SecureRandom secureRand = new SecureRandom();
int value = secureRand.nextInt();C# / .NET
Replace Division
// VULNERABLE: Division instruction emitted
int q = secretValue / divisor;
// SAFE: Barrett reduction (for fixed divisor)
// Precompute: mu = (1UL << 32) / divisor
ulong mu = 0x100000000UL / (ulong)divisor;
int q = (int)((secretValue * mu) >> 32);Replace Branches
// VULNERABLE: Branch timing reveals secret
int result = secret != 0 ? a : b;
// SAFE: Constant-time selection
int mask = -(secret != 0 ? 1 : 0);
int result = (a & mask) | (b & ~mask);
// Or use Vector<T> for SIMD constant-time ops (.NET 7+)Replace Comparisons
// VULNERABLE: SequenceEqual may early-terminate
if (computed.SequenceEqual(expected)) { ... }
// SAFE: Use CryptographicOperations.FixedTimeEquals (.NET Core 2.1+)
using System.Security.Cryptography;
if (CryptographicOperations.FixedTimeEquals(computed, expected)) { ... }Secure Random
// VULNERABLE: Predictable PRNG
Random rand = new Random();
int value = rand.Next();
// SAFE: Cryptographically secure
using System.Security.Cryptography;
int value = RandomNumberGenerator.GetInt32(int.MaxValue);
// Or for bytes:
byte[] bytes = RandomNumberGenerator.GetBytes(32);Platform-Specific Considerations
Java
Bouncy Castle: Use
org.bouncycastle.util.Arrays.constantTimeAreEqual()for constant-time comparisonJEP 329 (Java 12+): ChaCha20 and Poly1305 implementations are designed to be constant-time
BigInteger: Operations like
modPow()may have timing leaks; consider using Bouncy Castle's constant-time implementations
C# / .NET
Span<T>: Use
CryptographicOperations.FixedTimeEquals(ReadOnlySpan<byte>, ReadOnlySpan<byte>)for best performanceNSec: Consider using NSec library for constant-time cryptographic primitives
BigInteger: .NET's BigInteger has potential timing leaks; use specialized crypto libraries
JIT Compiler Caveats
Even if bytecode appears safe, JIT compilers can introduce timing vulnerabilities:
Speculative optimization - JIT may convert constant-time bytecode to branching native code
Escape analysis - May inline and optimize in ways that introduce timing
Tiered compilation - Code behavior may change as it "warms up"
Mitigations:
Test with production JVM/CLR versions
Consider ahead-of-time (AOT) compilation (GraalVM Native Image, .NET Native AOT)
For critical code, verify native code output with JIT logging:
# Java: Print JIT compilation
java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly MyClass
# .NET: Enable tiered compilation diagnostics
DOTNET_TieredCompilation=0 dotnet run # Disable tiered compilation for consistent behaviorSetup Requirements
Java
Required: JDK 8+ with javac and javap available.
Installation:
# macOS (Homebrew)
brew install openjdk@21
# Ubuntu/Debian
sudo apt install openjdk-21-jdk
# Windows (via winget)
winget install Microsoft.OpenJDK.21PATH Configuration (macOS):
On macOS, Homebrew installs OpenJDK as "keg-only" (not linked to /usr/local/bin). You must add it to your PATH:
# Add to ~/.zshrc or ~/.bashrc
export PATH="/opt/homebrew/opt/openjdk@21/bin:$PATH" # Apple Silicon
# or
export PATH="/usr/local/opt/openjdk@21/bin:$PATH" # Intel MacVerification:
javac --version # Should show: javac 21.x.x
javap -version # Should show version infoCommon Issues:
"Unable to locate a Java Runtime" on macOS: The system
/usr/bin/javacis a stub that requires a real JDK. Install OpenJDK via Homebrew.Wrong Java version: If you have multiple JDKs, use
JAVA_HOMEor ensure the correct one is first in PATH.
C#
Required: .NET SDK 8.0+ with dotnet available, plus ilspycmd for IL disassembly.
Installation:
# macOS (Homebrew)
brew install dotnet-sdk
# Ubuntu/Debian
sudo apt install dotnet-sdk-8.0
# Windows
winget install Microsoft.DotNet.SDK.8Install IL Disassembler:
dotnet tool install -g ilspycmdPATH Configuration:
Ensure the .NET tools directory is in your PATH:
# Add to ~/.zshrc or ~/.bashrc
export PATH="$HOME/.dotnet/tools:$PATH"Verification:
dotnet --version # Should show: 8.x.x or higher
ilspycmd --version # Should show: ilspycmd: 9.x.xCommon Issues:
"ilspycmd requires .NET 8.0 but you have .NET 10.0": This happens when ilspycmd targets an older .NET version than your installed SDK. ilspycmd installs framework-dependent, so the version you have decides which runtime it needs — ilspycmd 8.x needs .NET 8, 9.x needs .NET 9. Do not assume .NET 8; check what is actually in the tool store:
# Prints the target framework the installed ilspycmd was built for, e.g. net9.0 find ~/.dotnet/tools/.store/ilspycmd -maxdepth 6 -type d -name 'net*'findrather thanlson a glob, becausels ~/.dotnet/.../tools/*aborts under zsh when nothing matches. Iffinditself reports "No such file or directory", the store does not exist andilspycmdwas never installed withdotnet tool install— that is the answer, not an error to work around.The analyzer resolves this itself, preferring a Homebrew keg of that exact major and otherwise running the tool on whatever runtime it can find with
DOTNET_ROLL_FORWARD=Major. Installing the matching keg is the more reliable path:# macOS — substitute the major from the command above (dotnet@8, dotnet@9, …) brew install dotnet@9 # Other platforms: install that .NET runtime alongside your SDKNote that homebrew-core does not carry a keg for every major —
dotnet@6,dotnet@8anddotnet@9exist,dotnet@7was dropped at end of life. When there is no keg for the major you need, the roll-forward path above is what runs, and no install is required."IL disassembly tools not found": Ensure
ilspycmdis installed globally and~/.dotnet/toolsis in your PATH.Source-only fallback: If IL disassembly fails, the analyzer falls back to source-level analysis. This still detects division operators and dangerous function calls but misses bytecode-level issues.
Alternative: Mono (Linux/macOS)
For environments without .NET SDK, you can use Mono:
# macOS
brew install mono
# Ubuntu/Debian
sudo apt install mono-complete
# Verify
mcs --version
monodis --helpNote: Mono's monodis produces different IL output than ilspycmd. The analyzer supports both formats.
Common Mistakes
Trusting high-level APIs -
Arrays.equals()in Java andSequenceEqual()in C# are NOT constant-timeIgnoring JIT behavior - Bytecode analysis is necessary but not sufficient; JIT can introduce leaks
BigInteger operations - Both platforms' BigInteger implementations may leak timing; use crypto libraries
String comparisons - Never compare secrets as strings; use byte arrays with constant-time comparison
Exception timing - Try/catch blocks around secret operations may leak timing through exception handling
Further Reading
Bouncy Castle Java - Constant-time crypto primitives
NSec - Modern cryptographic library for .NET
Requirements and compatibility
Written for: Claude Code
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter, Needs Python, Needs Node.js
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Trail of Bits
- Publisher
- Trail of Bits (Official)
- Source
- trailofbits/skills · plugins/constant-time-analysis/skills/constant-time-analysis · commit e4dd0bb
- Licence
- Creative Commons Attribution-ShareAlike 4.0 International (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Trail of Bits.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download constant-time-analysis.zip