Claude 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, Ty

LLM Mart · 0 points · 11 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download trailofbits-skills-plugins_constant-time-analysis_skills_constant-time-analysis-123037e.zip · 29 KB
trailofbits/skills 7234 616 forks CC-BY-SA-4.0 Updated 11h ago
Part of trailofbits/skills — 100 skills

Install

skills CLI npx skills add https://github.com/trailofbits/skills/tree/main/plugins/constant-time-analysis/skills/constant-time-analysis
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install trailofbits-skills@llmmart
Git git clone https://github.com/trailofbits/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole trailofbits/skills collection as a plugin from our marketplace. Git is the plain clone.

README

Constant-Time Analysis Skill

A Claude Code skill that detects timing side-channel vulnerabilities in cryptographic code by analyzing assembly or bytecode output for dangerous instructions.

What This Skill Does

When activated, this skill helps Claude:

  • Detect timing vulnerabilities - Identifies variable-time instructions (division, floating-point) that leak secrets through execution timing
  • Analyze across architectures - Tests compiled output for x86_64, ARM64, RISC-V, and other targets
  • Support scripting languages - Analyzes PHP, JavaScript/TypeScript, Python, and Ruby via bytecode
  • Guide constant-time fixes - Provides patterns for Barrett reduction, constant-time selection, and safe comparisons
  • Integrate with CI - Produces JSON output suitable for automated pipelines

Supported Languages

Language Analysis Method Reference Guide
C/C++ Assembly (gcc/clang) references/compiled.md
Go Assembly (go) references/compiled.md
Rust Assembly (rustc) references/compiled.md
Swift Assembly (swiftc) references/swift.md
Java JVM bytecode (javap) references/vm-compiled.md
Kotlin JVM bytecode (kotlinc + javap) references/kotlin.md
C# CIL (ilspycmd) references/vm-compiled.md
PHP Zend opcodes (VLD/OPcache) references/php.md
JavaScript V8 bytecode (Node.js) references/javascript.md
TypeScript V8 bytecode (tsc + Node.js) references/javascript.md
Python CPython bytecode (dis) references/python.md
Ruby YARV bytecode references/ruby.md

Supported Architectures (Compiled Languages)

Architecture Division Instructions Common Use
x86_64 DIV, IDIV Servers, desktops
ARM64 UDIV, SDIV Mobile, Apple Silicon
ARM UDIV, SDIV Embedded
RISC-V DIV, DIVU, REM Emerging platforms
PowerPC DIVW, DIVD Legacy servers
s390x D, DR, DL Mainframes
i386 DIV, IDIV Legacy

File Structure

skills/constant-time-analysis/
├── SKILL.md              # Entry point - routing, analyzer usage, triage
├── README.md             # This file
└── references/
    ├── compiled.md       # C, C++, Go, Rust analysis
    ├── swift.md          # Swift analysis
    ├── vm-compiled.md    # Java and C# bytecode, JVM/.NET setup
    ├── kotlin.md         # Kotlin analysis (Android/JVM)
    ├── php.md            # PHP analysis (VLD installation, opcodes)
    ├── javascript.md     # JavaScript/TypeScript analysis
    ├── python.md         # Python analysis (dis module)
    └── ruby.md           # Ruby analysis (YARV)

The analyzer tool is located at ct_analyzer/analyzer.py in the plugin root. Its test suite and samples live in ct_analyzer/tests/:

  • test_samples/ — vulnerable and constant-time inputs for detector tests
  • triage_samples/ — one known-answer fixture per supported language, each pairing true positives with false positives the analyzer cannot distinguish. expectations.json records the verdict and rationale for every case; TestTriageMatrix asserts the analyzer still reports both members of each pair, and fails rather than skipping if no fixture could be exercised.

Usage

The skill activates automatically when Claude detects:

  • Cryptographic code implementation (encryption, signing, key derivation)
  • Questions about timing attacks or constant-time programming
  • Code handling secret keys, tokens, or cryptographic material
  • Functions with division/modulo operations on potentially secret data

You can also invoke it explicitly by asking Claude to check code for timing vulnerabilities.

Example Prompts

"Check this crypto function for timing vulnerabilities"
"Is this signature verification constant-time?"
"Help me replace this division with Barrett reduction"
"Analyze this ML-KEM implementation for KyberSlash-style issues"
"What constant-time patterns should I use here?"

Quick Reference

Vulnerability Detection Fix
Secret division DIV, IDIV, SDIV, UDIV Barrett reduction
Secret branches JE, JNE, BEQ, BNE Bit masking, cmov
Secret comparison Early-exit memcmp crypto/subtle
Variable-time FP FDIV, FSQRT Avoid in crypto

Real-World Attacks

  • KyberSlash (2023) - Division in ML-KEM leaked keys
  • Lucky Thirteen (2013) - Padding timing in TLS
  • Timing attacks on RSA - Division in modular exponentiation

Skill manifest

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 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"; done

Prerequisites

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 values

Triaging 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

  1. Static only — reads assembly and bytecode, never runtime behavior. Cache timing and other microarchitectural channels are invisible.
  2. No data flow analysis — see triage above.
  3. 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

Files (skills)
  • agents
    • openai.yaml 240 B
      interface:
        display_name: "Constant-Time Analysis"
        short_description: "Find timing side channels in cryptographic code"
        icon_small: "assets/trail-of-bits-mark.svg"
        icon_large: "assets/trail-of-bits-mark.svg"
        brand_color: "#D83A34"
      
  • assets
    • trail-of-bits-mark.svg 3 KB · in bundle
  • references
    • compiled.md 4 KB
      # Constant-Time Analysis: Compiled Languages
      
      Analysis guidance for C, C++, Go, and Rust. These languages compile to native assembly, where timing side-channels are detected by scanning for variable-time CPU instructions.
      
      ## Running the Analyzer
      
      ```bash
      # C/C++ (default: clang, native architecture)
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.c
      
      # Go
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.go
      
      # Rust
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.rs
      
      # Cross-architecture testing (RECOMMENDED)
      uv run {baseDir}/ct_analyzer/analyzer.py --arch x86_64 crypto.c
      uv run {baseDir}/ct_analyzer/analyzer.py --arch arm64 crypto.c
      
      # Multiple optimization levels
      uv run {baseDir}/ct_analyzer/analyzer.py --opt-level O0 crypto.c
      uv run {baseDir}/ct_analyzer/analyzer.py --opt-level O3 crypto.c
      
      # Include conditional branch warnings
      uv run {baseDir}/ct_analyzer/analyzer.py --warnings crypto.c
      
      # Filter to specific functions
      uv run {baseDir}/ct_analyzer/analyzer.py --func 'sign|verify|decrypt' crypto.c
      
      # CI-friendly JSON output
      uv run {baseDir}/ct_analyzer/analyzer.py --json crypto.c
      ```
      
      ## Supported Compilers
      
      | Language | Compiler | Flag |
      |----------|----------|------|
      | C/C++ | gcc | `--compiler gcc` |
      | C/C++ | clang (default) | `--compiler clang` |
      | Go | go | `--compiler go` |
      | Rust | rustc | `--compiler rustc` |
      
      ## Supported Architectures
      
      x86_64, arm64, arm, riscv64, ppc64le, s390x, i386
      
      ## Dangerous Instructions by Architecture
      
      | Architecture | Division | Floating-Point |
      |-------------|----------|----------------|
      | x86_64 | DIV, IDIV, DIVQ, IDIVQ | DIVSS, DIVSD, SQRTSS, SQRTSD |
      | ARM64 | UDIV, SDIV | FDIV, FSQRT |
      | ARM | UDIV, SDIV | VDIV, VSQRT |
      | RISC-V | DIV, DIVU, REM, REMU | FDIV.S, FDIV.D, FSQRT |
      | PowerPC | DIVW, DIVD | FDIV, FSQRT |
      | s390x | D, DR, DL, DLG, DSG | DDB, SQDB |
      
      ## Constant-Time Patterns
      
      ### Replace Division
      
      ```c
      // VULNERABLE: Compiler emits DIV instruction
      int32_t q = a / divisor;
      
      // SAFE: Barrett reduction (precompute mu = ceil(2^32 / divisor))
      uint32_t q = (uint32_t)(((uint64_t)a * mu) >> 32);
      ```
      
      ### Replace Branches
      
      ```c
      // VULNERABLE: Branch timing reveals secret
      if (secret) { result = a; } else { result = b; }
      
      // SAFE: Constant-time selection
      uint32_t mask = -(uint32_t)(secret != 0);
      result = (a & mask) | (b & ~mask);
      ```
      
      ### Replace Comparisons
      
      ```c
      // VULNERABLE: memcmp returns early on mismatch
      if (memcmp(a, b, len) == 0) { ... }
      
      // SAFE: Constant-time comparison
      if (CRYPTO_memcmp(a, b, len) == 0) { ... }  // OpenSSL
      if (subtle.ConstantTimeCompare(a, b) == 1) { ... }  // Go
      ```
      
      ## Common Mistakes
      
      1. **Testing only one optimization level** - Compilers make different decisions at O0 vs O3. A clean O2 build may have divisions at O0.
      
      2. **Testing only one architecture** - ARM and x86 have different division behavior. Test your deployment targets.
      
      3. **Ignoring warnings** - Conditional branches on secrets are exploitable. Use `--warnings` and review each branch.
      
      4. **Assuming the tool catches everything** - This tool detects instruction-level issues only. It cannot detect:
         - Cache timing from memory access patterns
         - Microarchitectural attacks (Spectre, etc.)
         - Whether flagged code actually processes secrets
      
      5. **Fixing symptoms, not causes** - If compiler introduces division, understand why. Sometimes the algorithm itself needs redesign.
      
      ## Go-Specific Notes
      
      Go compiles to native code, so the analyzer builds a binary and disassembles it using `go tool objdump`. The analyzer:
      - Sets `CGO_ENABLED=0` for pure Go analysis
      - Supports cross-compilation via `GOARCH` environment variable
      - Uses `-N -l` gcflags for O0 (disable optimizations)
      
      ## Rust-Specific Notes
      
      Rust uses `rustc --emit=asm` for assembly generation. The analyzer:
      - Maps optimization levels to rustc's `-C opt-level` flag
      - Supports cross-compilation via `--target` flag
      - Analyzes the emitted assembly for timing-unsafe instructions
      
      ## CI Integration
      
      ```yaml
      - name: Check constant-time properties
        run: |
          uv run ct_analyzer/analyzer.py --json src/crypto/*.c
          # Exit code 1 = violations found
      ```
      
    • javascript.md 4.3 KB
      # Constant-Time Analysis: JavaScript and TypeScript
      
      Analysis guidance for JavaScript and TypeScript. Uses V8 bytecode output from Node.js to detect timing-unsafe operations.
      
      ## Prerequisites
      
      - **Node.js** (v14+) - for JavaScript analysis
      - **TypeScript compiler** (tsc) - for TypeScript files (optional, uses npx fallback)
      
      ## Running the Analyzer
      
      ```bash
      # Analyze JavaScript
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.js
      
      # Analyze TypeScript (transpiles first)
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.ts
      
      # Include warning-level violations
      uv run {baseDir}/ct_analyzer/analyzer.py --warnings crypto.js
      
      # Filter to specific functions
      uv run {baseDir}/ct_analyzer/analyzer.py --func 'encrypt|sign' crypto.js
      
      # JSON output for CI
      uv run {baseDir}/ct_analyzer/analyzer.py --json crypto.js
      ```
      
      ## Dangerous Operations
      
      ### Bytecodes (Errors)
      
      | Bytecode | Issue |
      |----------|-------|
      | Div | Variable-time execution based on operand values |
      | Mod | Variable-time execution based on operand values |
      | DivSmi | Division by small integer has variable-time execution |
      | ModSmi | Modulo by small integer has variable-time execution |
      
      ### Functions (Errors)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `Math.sqrt()` | Variable latency based on operand values | Avoid in crypto |
      | `Math.pow()` | Variable latency based on operand values | Avoid in crypto |
      | `Math.random()` | Predictable | `crypto.getRandomValues()` |
      | `eval()` | Unpredictable timing | Avoid entirely |
      
      ### Functions (Warnings)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `===` (strings) | Early-terminating | `crypto.timingSafeEqual()` |
      | `indexOf()` | Early-terminating | Constant-time search |
      | `includes()` | Early-terminating | Constant-time search |
      | `startsWith()` | Early-terminating | `crypto.timingSafeEqual()` on prefix |
      | `endsWith()` | Early-terminating | `crypto.timingSafeEqual()` on suffix |
      | `JSON.stringify()` | Variable-length output | Fixed-length padding |
      | `JSON.parse()` | Variable-time based on input | Fixed-length input |
      | `btoa()` / `atob()` | Variable-length output | Fixed-length padding |
      
      ## Safe Patterns
      
      ### String Comparison (Node.js)
      
      ```javascript
      // VULNERABLE: Early exit on mismatch
      if (userToken === storedToken) { ... }
      
      // SAFE: Constant-time comparison (Node.js)
      const crypto = require('crypto');
      if (crypto.timingSafeEqual(Buffer.from(userToken), Buffer.from(storedToken))) { ... }
      ```
      
      ### Random Number Generation
      
      ```javascript
      // VULNERABLE: Predictable
      const token = Math.random().toString(36);
      
      // SAFE: Cryptographically secure (Node.js)
      const crypto = require('crypto');
      const token = crypto.randomBytes(16).toString('hex');
      
      // SAFE: Browser
      const array = new Uint8Array(16);
      crypto.getRandomValues(array);
      ```
      
      ### Division Operations
      
      ```javascript
      // VULNERABLE: Division has variable timing
      const quotient = secret / divisor;
      
      // SAFE: Use multiplication by inverse (if divisor is constant)
      // Precompute: inverse = 1/divisor as fixed-point
      const quotient = Math.floor(secret * inverse);
      ```
      
      ## TypeScript Notes
      
      The analyzer:
      1. Looks for `tsconfig.json` in parent directories
      2. Transpiles TypeScript to JavaScript in a temp directory
      3. Analyzes the transpiled JavaScript
      4. Reports violations against the original TypeScript file
      
      If tsc is not installed, the analyzer tries `npx tsc` as a fallback.
      
      ## Limitations
      
      ### V8 Bytecode Analysis
      
      The analyzer uses `node --print-bytecode` to get V8 bytecode. This has limitations:
      
      1. **JIT Compilation**: V8 may JIT-compile hot functions to native code with different timing characteristics
      2. **Function Inlining**: Inlined functions may not appear in bytecode
      3. **Deoptimization**: Code can be deoptimized back to bytecode
      
      ### Source-Level Detection
      
      The analyzer also performs source-level pattern matching to detect:
      - Division (`/`) and modulo (`%`) operators
      - Dangerous function calls (`Math.random()`, etc.)
      
      This catches issues that bytecode analysis might miss due to parsing limitations.
      
      ## Browser Considerations
      
      The analyzer targets Node.js V8 bytecode. Browser JavaScript engines (SpiderMonkey, JavaScriptCore) have different bytecode formats and timing characteristics.
      
      For browser-targeted code:
      - The V8 analysis is still valuable as a baseline
      - Consider additional testing in target browsers
      - Use Web Crypto API for cryptographic operations
      
    • kotlin.md 6.6 KB
      # Constant-Time Analysis: Kotlin
      
      Analysis guidance for Kotlin targeting Android and JVM platforms. Kotlin compiles to JVM bytecode, sharing the same runtime characteristics as Java.
      
      ## Understanding Kotlin Compilation
      
      Kotlin compiles to JVM bytecode that runs on the same virtual machine as Java:
      
      ```text
      Source Code (.kt/.kts)
              |
              v
          kotlinc (Kotlin Compiler)
              |
              v
      Bytecode (.class files)
              |
              v
          JIT Compiler (HotSpot/ART)
              |
              v
         Native Code (at runtime)
      ```
      
      **Key implications for Android:**
      
      1. **Android Runtime (ART)** - Android uses ART instead of HotSpot JVM
      2. **AOT compilation** - ART compiles bytecode to native code at install time
      3. **Same bytecode vulnerabilities** - Division/branch timing issues persist regardless of runtime
      
      ## Running the Analyzer
      
      ```bash
      # Analyze Kotlin source
      uv run {baseDir}/ct_analyzer/analyzer.py CryptoUtils.kt
      
      # Include conditional branch warnings
      uv run {baseDir}/ct_analyzer/analyzer.py --warnings CryptoUtils.kt
      
      # Filter to specific functions
      uv run {baseDir}/ct_analyzer/analyzer.py --func 'sign|verify' CryptoUtils.kt
      
      # CI-friendly JSON output
      uv run {baseDir}/ct_analyzer/analyzer.py --json CryptoUtils.kt
      ```
      
      Note: The `--arch` and `--opt-level` flags do not apply to Kotlin as it compiles to JVM bytecode.
      
      ## Dangerous Bytecode Instructions
      
      Kotlin compiles to the same JVM bytecode as Java:
      
      | 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*` | Timing leak if condition depends on secrets |
      | Table Lookups | `*aload`, `*astore`, `tableswitch`, `lookupswitch` | Cache timing if index depends on secrets |
      
      ## Constant-Time Patterns
      
      ### Replace Division
      
      ```kotlin
      // VULNERABLE: Division instruction emitted
      val q = secretValue / divisor
      
      // SAFE: Barrett reduction (for fixed divisor)
      // Precompute: mu = (1L shl 32) / divisor
      val mu = (1L shl 32) / divisor
      val q = ((secretValue.toLong() * mu) ushr 32).toInt()
      ```
      
      ### Replace Branches
      
      ```kotlin
      // VULNERABLE: Branch timing reveals secret
      val result = if (secret != 0) a else b
      
      // SAFE: Constant-time selection using bitwise ops
      val mask = -(if (secret != 0) 1 else 0)
      // Better: compute mask without branch
      val mask = (secret or -secret) shr 31  // -1 if secret != 0, else 0
      val result = (a and mask) or (b and mask.inv())
      ```
      
      ### Replace Comparisons
      
      ```kotlin
      // VULNERABLE: contentEquals() may early-terminate
      if (computed.contentEquals(expected)) { ... }
      
      // SAFE: Use MessageDigest.isEqual() for constant-time comparison
      import java.security.MessageDigest
      if (MessageDigest.isEqual(computed, expected)) { ... }
      ```
      
      ### Secure Random
      
      ```kotlin
      // VULNERABLE: kotlin.random.Random is predictable
      import kotlin.random.Random
      val value = Random.nextInt()
      
      // SAFE: Cryptographically secure
      import java.security.SecureRandom
      val secureRand = SecureRandom()
      val value = secureRand.nextInt()
      
      // Or use Kotlin's secure wrapper (requires kotlin-stdlib-jdk8)
      import kotlin.random.asKotlinRandom
      val secureKotlinRandom = SecureRandom().asKotlinRandom()
      ```
      
      ## Android-Specific Considerations
      
      ### Keystore Operations
      
      ```kotlin
      // Use Android Keystore for cryptographic key storage
      import android.security.keystore.KeyGenParameterSpec
      import android.security.keystore.KeyProperties
      
      val keyGenerator = KeyGenerator.getInstance(
          KeyProperties.KEY_ALGORITHM_AES,
          "AndroidKeyStore"
      )
      keyGenerator.init(
          KeyGenParameterSpec.Builder(
              "my_key",
              KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
          )
          .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
          .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
          .build()
      )
      ```
      
      ### Constant-Time Comparison on Android
      
      ```kotlin
      // Android provides MessageDigest.isEqual()
      import java.security.MessageDigest
      
      fun constantTimeEquals(a: ByteArray, b: ByteArray): Boolean {
          return MessageDigest.isEqual(a, b)
      }
      ```
      
      ### Secure Random on Android
      
      ```kotlin
      // SecureRandom works the same on Android
      import java.security.SecureRandom
      
      fun generateSecureToken(length: Int): ByteArray {
          val random = SecureRandom()
          val token = ByteArray(length)
          random.nextBytes(token)
          return token
      }
      ```
      
      ## Kotlin-Specific Pitfalls
      
      ### Extension Functions on Primitives
      
      ```kotlin
      // DANGEROUS: Division in extension function
      fun Int.divideBy(divisor: Int) = this / divisor  // Emits IDIV
      
      // The inline modifier doesn't change bytecode behavior
      inline fun Int.divideByInline(divisor: Int) = this / divisor  // Still IDIV
      ```
      
      ### When Expressions
      
      ```kotlin
      // VULNERABLE: when compiles to tableswitch/lookupswitch
      when (secretValue) {
          0 -> handleZero()
          1 -> handleOne()
          else -> handleOther()
      }
      
      // Consider constant-time alternatives for secret-dependent dispatch
      ```
      
      ### Null Safety Checks
      
      ```kotlin
      // Nullable operations may introduce branches
      val result = secretNullable?.process()  // Introduces null check branch
      
      // Be aware of null-check timing when handling secrets
      ```
      
      ## Setup Requirements
      
      ### Kotlin Compiler
      
      **macOS:**
      ```bash
      brew install kotlin
      ```
      
      **Ubuntu/Debian:**
      ```bash
      sudo snap install kotlin --classic
      ```
      
      **Windows:**
      ```bash
      scoop install kotlin
      # or
      choco install kotlinc
      ```
      
      ### Android Development
      
      For Android projects, the Kotlin compiler is typically bundled with Android Studio. Ensure your project's Kotlin version is up to date in `build.gradle.kts`:
      
      ```kotlin
      plugins {
          kotlin("jvm") version "1.9.0"
      }
      ```
      
      ### Verification
      
      ```bash
      kotlinc -version  # Should show: kotlinc-jvm X.X.X
      javap -version    # Required for bytecode disassembly
      ```
      
      ## Common Mistakes
      
      1. **Using kotlin.random.Random** - The default Random is not cryptographically secure; use `java.security.SecureRandom`
      
      2. **Relying on == for byte arrays** - `==` compares references in Kotlin; use `contentEquals()` for value comparison, but neither is constant-time
      
      3. **Infix functions for crypto** - Custom operators don't change timing characteristics of underlying operations
      
      4. **Coroutines timing** - Suspending functions add scheduling overhead that may mask or introduce timing variations
      
      5. **Sealed classes for dispatch** - Pattern matching on sealed classes compiles to switches that may leak timing
      
      ## Further Reading
      
      - [Kotlin/JVM Interoperability](https://kotlinlang.org/docs/java-interop.html)
      - [Android Keystore System](https://developer.android.com/training/articles/keystore)
      - [Bouncy Castle for Kotlin](https://www.bouncycastle.org/java.html) - Constant-time crypto primitives
      
    • php.md 5.1 KB
      # Constant-Time Analysis: PHP
      
      Analysis guidance for PHP scripts. Uses the VLD extension or OPcache debug output to analyze Zend opcodes.
      
      ## Prerequisites
      
      ### Installing VLD Extension
      
      The VLD (Vulcan Logic Dumper) extension is required for detailed opcode analysis. OPcache fallback is available but provides less detail.
      
      **Option 1: PECL Install (Recommended)**
      
      ```bash
      # Query latest version from PECL. POSIX ERE, not `grep -P`: PCRE mode is a GNU
      # extension and stock macOS grep exits 2 on it, which would silently leave
      # VLD_VERSION empty and install a package named `vld-` with no version.
      VLD_VERSION=$(curl -fsS https://pecl.php.net/package/vld |
        grep -oE 'vld-[0-9]+(\.[0-9]+)*\.tgz' | head -1 | sed -E 's/^vld-//; s/\.tgz$//')
      
      if [ -z "$VLD_VERSION" ]; then
        echo "Could not read the latest VLD version from PECL." >&2
        echo "Check https://pecl.php.net/package/vld and set VLD_VERSION by hand." >&2
      else
        echo "Latest VLD version: $VLD_VERSION"
      
        # Install via PECL channel URL (avoids version detection issues)
        pecl install channel://pecl.php.net/vld-${VLD_VERSION}
      
        # Or if above fails, install with explicit channel:
        pecl install https://pecl.php.net/get/vld-${VLD_VERSION}.tgz
      fi
      ```
      
      **Option 2: Build from Source**
      
      ```bash
      # Clone and build from GitHub
      git clone https://github.com/derickr/vld.git
      cd vld
      phpize
      ./configure
      make
      sudo make install
      
      # Add to php.ini
      echo "extension=vld.so" | sudo tee -a $(php --ini | grep "Loaded Configuration" | cut -d: -f2 | tr -d ' ')
      ```
      
      **Verify Installation**
      
      ```bash
      php -m | grep -i vld
      # Should output: vld
      ```
      
      ### macOS with Homebrew PHP
      
      ```bash
      # Homebrew PHP may need manual extension directory setup
      PHP_EXT_DIR=$(php -i | grep extension_dir | awk '{print $3}')
      echo "PHP extension directory: $PHP_EXT_DIR"
      
      # After building VLD, copy the extension
      sudo cp modules/vld.so "$PHP_EXT_DIR/"
      ```
      
      ## Running the Analyzer
      
      ```bash
      # Analyze PHP file
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.php
      
      # Include warning-level violations
      uv run {baseDir}/ct_analyzer/analyzer.py --warnings crypto.php
      
      # Filter to specific functions
      uv run {baseDir}/ct_analyzer/analyzer.py --func 'encrypt|decrypt' crypto.php
      
      # JSON output for CI
      uv run {baseDir}/ct_analyzer/analyzer.py --json crypto.php
      ```
      
      ## Dangerous Operations
      
      ### Opcodes (Errors)
      
      | Opcode | Issue |
      |--------|-------|
      | DIV | Variable-time execution based on operand values |
      | MOD | Variable-time execution based on operand values |
      | POW | Variable-time execution |
      
      ### Functions (Errors)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `chr()` | Table lookup indexed by secret data | `pack('C', $int)` |
      | `ord()` | Table lookup indexed by secret data | `unpack('C', $char)[1]` |
      | `bin2hex()` | Table lookups indexed on secret data | Custom constant-time implementation |
      | `hex2bin()` | Table lookups indexed on secret data | Custom constant-time implementation |
      | `base64_encode()` | Table lookups indexed on secret data | Custom constant-time implementation |
      | `base64_decode()` | Table lookups indexed on secret data | Custom constant-time implementation |
      | `rand()` | Predictable | `random_int()` |
      | `mt_rand()` | Predictable | `random_int()` |
      | `array_rand()` | Uses mt_rand internally | `random_int()` |
      | `uniqid()` | Predictable | `random_bytes()` |
      | `shuffle()` | Uses mt_rand internally | Fisher-Yates with `random_int()` |
      
      ### Functions (Warnings)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `strcmp()` | Variable-time | `hash_equals()` |
      | `strcasecmp()` | Variable-time | `hash_equals()` |
      | `strncmp()` | Variable-time | `hash_equals()` |
      | `substr_compare()` | Variable-time | `hash_equals()` |
      | `serialize()` | Variable-length output | Fixed-length output |
      | `json_encode()` | Variable-length output | Fixed-length output |
      
      ## Safe Patterns
      
      ### String Comparison
      
      ```php
      // VULNERABLE: Early exit on mismatch
      if ($user_token === $stored_token) { ... }
      
      // SAFE: Constant-time comparison
      if (hash_equals($stored_token, $user_token)) { ... }
      ```
      
      ### Random Number Generation
      
      ```php
      // VULNERABLE: Predictable
      $token = bin2hex(random_bytes(16));  // OK - random_bytes is secure
      $index = mt_rand(0, count($array) - 1);  // VULNERABLE
      
      // SAFE: Cryptographically secure
      $token = bin2hex(random_bytes(16));
      $index = random_int(0, count($array) - 1);
      ```
      
      ### Character Operations
      
      ```php
      // VULNERABLE: Table lookup timing
      $byte = ord($secret_char);
      $char = chr($secret_byte);
      
      // SAFE: No table lookup
      $byte = unpack('C', $secret_char)[1];
      $char = pack('C', $secret_byte);
      ```
      
      ## Troubleshooting
      
      ### VLD Not Loading
      
      ```bash
      # Check if extension is enabled
      php -i | grep vld
      
      # Check for loading errors
      php -d display_errors=1 -d vld.active=1 -r "echo 'test';" 2>&1
      
      # Common issue: wrong extension directory
      php -i | grep extension_dir
      ls $(php -r "echo ini_get('extension_dir');") | grep vld
      ```
      
      ### OPcache Fallback
      
      If VLD is unavailable, the analyzer falls back to OPcache debug output:
      
      ```bash
      # Manually test OPcache output
      php -d opcache.enable_cli=1 -d opcache.opt_debug_level=0x10000 crypto.php 2>&1
      ```
      
      OPcache provides less detailed output than VLD but still detects division/modulo opcodes.
      
    • python.md 5.1 KB
      # Constant-Time Analysis: Python
      
      Analysis guidance for Python scripts. Uses the `dis` module to analyze CPython bytecode for timing-unsafe operations.
      
      ## Prerequisites
      
      - Python 3.10+ (bytecode format varies by version)
      
      ## Running the Analyzer
      
      ```bash
      # Analyze Python file
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.py
      
      # Include warning-level violations
      uv run {baseDir}/ct_analyzer/analyzer.py --warnings crypto.py
      
      # Filter to specific functions
      uv run {baseDir}/ct_analyzer/analyzer.py --func 'encrypt|sign' crypto.py
      
      # JSON output for CI
      uv run {baseDir}/ct_analyzer/analyzer.py --json crypto.py
      ```
      
      ## Dangerous Operations
      
      ### Bytecodes (Errors)
      
      **Python < 3.11:**
      
      | Bytecode | Issue |
      |----------|-------|
      | BINARY_TRUE_DIVIDE | Variable-time execution |
      | BINARY_FLOOR_DIVIDE | Variable-time execution |
      | BINARY_MODULO | Variable-time execution |
      | INPLACE_TRUE_DIVIDE | Variable-time execution |
      | INPLACE_FLOOR_DIVIDE | Variable-time execution |
      | INPLACE_MODULO | Variable-time execution |
      
      **Python 3.11+:**
      
      | BINARY_OP Oparg | Operation | Issue |
      |-----------------|-----------|-------|
      | 11 | `/` | Variable-time execution |
      | 12 | `//` | Variable-time execution |
      | 6 | `%` | Variable-time execution |
      | 24 | `/=` | Variable-time execution |
      | 25 | `//=` | Variable-time execution |
      | 19 | `%=` | Variable-time execution |
      
      ### Functions (Errors)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `random.random()` | Predictable | `secrets.token_bytes()` |
      | `random.randint()` | Predictable | `secrets.randbelow()` |
      | `random.randrange()` | Predictable | `secrets.randbelow()` |
      | `random.choice()` | Predictable | `secrets.choice()` |
      | `random.shuffle()` | Predictable | Custom with `secrets` |
      | `random.sample()` | Predictable | Custom with `secrets` |
      | `math.sqrt()` | Variable latency | Avoid in crypto |
      | `math.pow()` | Variable latency | Avoid in crypto |
      | `eval()` | Unpredictable timing | Avoid entirely |
      | `exec()` | Unpredictable timing | Avoid entirely |
      
      ### Functions (Warnings)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `str.find()` | Early-terminating | Constant-time search |
      | `str.index()` | Early-terminating | Constant-time search |
      | `str.startswith()` | Early-terminating | `hmac.compare_digest()` |
      | `str.endswith()` | Early-terminating | `hmac.compare_digest()` |
      | `in` (strings) | Early-terminating | Constant-time search |
      | `json.dumps()` | Variable-length output | Fixed-length padding |
      | `json.loads()` | Variable-time | Fixed-length input |
      | `base64.b64encode()` | Variable-length output | Fixed-length padding |
      | `pickle.dumps()` | Variable-length output | Avoid for secrets |
      | `pickle.loads()` | Variable-time, security risk | Avoid for secrets |
      
      ## Safe Patterns
      
      ### String Comparison
      
      ```python
      # VULNERABLE: Early exit on mismatch
      if user_token == stored_token:
          ...
      
      # SAFE: Constant-time comparison
      import hmac
      if hmac.compare_digest(user_token, stored_token):
          ...
      
      # SAFE: For bytes
      import secrets
      if secrets.compare_digest(user_bytes, stored_bytes):
          ...
      ```
      
      ### Random Number Generation
      
      ```python
      # VULNERABLE: Predictable
      import random
      token = random.randint(0, 2**128)
      
      # SAFE: Cryptographically secure
      import secrets
      token = secrets.token_bytes(16)
      token_int = secrets.randbits(128)
      random_index = secrets.randbelow(len(items))
      ```
      
      ### Division Operations
      
      ```python
      # VULNERABLE: Division has variable timing
      quotient = secret // divisor
      
      # SAFE: Barrett reduction for constant divisors
      # Precompute: mu = (1 << (2 * BITS)) // divisor
      def barrett_reduce(value: int, divisor: int, mu: int, bits: int) -> int:
          q = (value * mu) >> (2 * bits)
          r = value - q * divisor
          # Constant-time correction
          mask = -(r >= divisor)
          return r - (divisor & mask)
      ```
      
      ## Python Version Notes
      
      ### Python 3.11+ Changes
      
      Python 3.11 introduced the `BINARY_OP` bytecode that replaces individual binary operation bytecodes. The analyzer detects division/modulo by checking the oparg:
      
      ```
      BINARY_OP               11 (/)    # True division
      BINARY_OP               12 (//)   # Floor division
      BINARY_OP                6 (%)    # Modulo
      ```
      
      ### Python 3.10 and Earlier
      
      Uses separate bytecodes:
      ```
      BINARY_TRUE_DIVIDE
      BINARY_FLOOR_DIVIDE
      BINARY_MODULO
      ```
      
      ## Cryptography Library Considerations
      
      When using the `cryptography` library:
      
      ```python
      # The cryptography library handles constant-time internally
      from cryptography.hazmat.primitives.ciphers.aead import AESGCM
      
      # SAFE: Library handles timing protection
      aesgcm = AESGCM(key)
      ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
      ```
      
      For custom cryptographic code, ensure you:
      1. Use `hmac.compare_digest()` for comparisons
      2. Use `secrets` module for randomness
      3. Avoid division/modulo on secret-derived values
      4. Use fixed-length data representations
      
      ## Limitations
      
      ### CPython Bytecode Only
      
      The analyzer targets CPython bytecode. Alternative implementations (PyPy, Jython, etc.) have different bytecode formats and timing characteristics.
      
      ### JIT Compilation
      
      PyPy and Numba can JIT-compile Python to native code with potentially different timing behavior. Consider additional analysis for JIT-compiled code paths.
      
    • ruby.md 5.3 KB
      # Constant-Time Analysis: Ruby
      
      Analysis guidance for Ruby scripts. Uses YARV (Yet Another Ruby VM) instruction sequence dump to analyze bytecode for timing-unsafe operations.
      
      ## Prerequisites
      
      - Ruby 2.0+ (uses `ruby --dump=insns`)
      
      ## Running the Analyzer
      
      ```bash
      # Analyze Ruby file
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.rb
      
      # Include warning-level violations
      uv run {baseDir}/ct_analyzer/analyzer.py --warnings crypto.rb
      
      # Filter to specific functions
      uv run {baseDir}/ct_analyzer/analyzer.py --func 'encrypt|sign' crypto.rb
      
      # JSON output for CI
      uv run {baseDir}/ct_analyzer/analyzer.py --json crypto.rb
      ```
      
      ## Dangerous Operations
      
      ### Bytecodes (Errors)
      
      | Bytecode | Issue |
      |----------|-------|
      | opt_div | Variable-time execution based on operand values |
      | opt_mod | Variable-time execution based on operand values |
      
      ### Bytecodes (Warnings)
      
      | Bytecode | Issue |
      |----------|-------|
      | opt_eq | May early-terminate on secret data |
      | opt_neq | May early-terminate on secret data |
      | opt_lt, opt_le, opt_gt, opt_ge | Comparison may leak timing |
      | branchif, branchunless | Conditional branch on secrets |
      | opt_aref | Array access may leak timing via cache |
      | opt_aset | Array store may leak timing via cache |
      | opt_lshift, opt_rshift | Bit shift timing may vary |
      
      ### Functions (Errors)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `rand()` | Predictable | `SecureRandom.random_bytes()` |
      | `Random.new` | Predictable | `SecureRandom` |
      | `srand()` | Sets predictable seed | `SecureRandom` |
      | `Math.sqrt()` | Variable latency | Avoid in crypto |
      
      ### Functions (Warnings)
      
      | Function | Issue | Safe Alternative |
      |----------|-------|------------------|
      | `include?()` | Early-terminating | Constant-time search |
      | `index()` | Early-terminating | Constant-time search |
      | `start_with?()` | Early-terminating | `Rack::Utils.secure_compare()` |
      | `end_with?()` | Early-terminating | `Rack::Utils.secure_compare()` |
      | `match()` | Variable-time | Avoid on secrets |
      | `=~` | Variable-time regex | Avoid on secrets |
      | `to_json()` | Variable-length output | Fixed-length padding |
      | `Marshal.dump()` | Variable-length output | Avoid for secrets |
      | `Marshal.load()` | Variable-time, security risk | Avoid for secrets |
      
      ## Safe Patterns
      
      ### String Comparison
      
      ```ruby
      # VULNERABLE: Early exit on mismatch
      if user_token == stored_token
        # ...
      end
      
      # SAFE: Constant-time comparison (Rails/Rack)
      require 'rack/utils'
      if Rack::Utils.secure_compare(user_token, stored_token)
        # ...
      end
      
      # SAFE: ActiveSupport (Rails)
      require 'active_support/security_utils'
      if ActiveSupport::SecurityUtils.secure_compare(user_token, stored_token)
        # ...
      end
      
      # SAFE: OpenSSL (stdlib)
      require 'openssl'
      if OpenSSL.secure_compare(user_token, stored_token)
        # ...
      end
      ```
      
      ### Random Number Generation
      
      ```ruby
      # VULNERABLE: Predictable
      token = rand(2**128)
      random_bytes = Random.new.bytes(16)
      
      # SAFE: Cryptographically secure
      require 'securerandom'
      token = SecureRandom.random_bytes(16)
      token_hex = SecureRandom.hex(16)
      token_base64 = SecureRandom.base64(16)
      random_number = SecureRandom.random_number(2**128)
      ```
      
      ### Division Operations
      
      ```ruby
      # VULNERABLE: Division has variable timing
      quotient = secret / divisor
      
      # SAFE: Barrett reduction for constant divisors
      def barrett_reduce(value, divisor, mu, bits)
        q = (value * mu) >> (2 * bits)
        r = value - q * divisor
        # Constant-time correction using bitwise operations
        mask = -(r >= divisor ? 1 : 0)
        r - (divisor & mask)
      end
      ```
      
      ## Rails/Rack Integration
      
      ### Secure Compare
      
      Rails and Rack provide constant-time comparison:
      
      ```ruby
      # Rack (standalone)
      Rack::Utils.secure_compare(a, b)
      
      # Rails/ActiveSupport
      ActiveSupport::SecurityUtils.secure_compare(a, b)
      
      # OpenSSL (Ruby 2.5+)
      OpenSSL.secure_compare(a, b)
      ```
      
      ### CSRF Token Comparison
      
      ```ruby
      # Rails automatically uses secure_compare for CSRF tokens
      # For custom token validation:
      class ApplicationController < ActionController::Base
        def verify_api_token
          provided = request.headers['X-API-Token']
          expected = current_user.api_token
      
          # SAFE: Constant-time comparison
          unless ActiveSupport::SecurityUtils.secure_compare(provided, expected)
            head :unauthorized
          end
        end
      end
      ```
      
      ## YARV Bytecode Notes
      
      The analyzer uses `ruby --dump=insns` to get YARV instruction sequences. Example output:
      
      ```
      == disasm: #<ISeq:vulnerable_function@test.rb:1 (1,0)-(5,3)>
      local table (size: 2, argc: 2)
      [ 2] value@0    [ 1] modulus@1
      0000 getlocal_WC_0     value@0
      0002 getlocal_WC_0     modulus@1
      0004 opt_div           <calldata!mid:/, argc:1>
      0006 leave
      ```
      
      The `opt_div` instruction at offset 0004 is flagged as a timing vulnerability.
      
      ## Limitations
      
      ### MRI Ruby Only
      
      The analyzer targets MRI (Matz's Ruby Interpreter) YARV bytecode. Alternative implementations (JRuby, TruffleRuby) have different bytecode formats:
      
      - **JRuby**: Compiles to JVM bytecode
      - **TruffleRuby**: Uses GraalVM intermediate representation
      
      ### Method Caching
      
      Ruby's method dispatch involves caching that can affect timing. Even with constant-time operations, method lookup timing may leak information about code paths.
      
      ### Gem Dependencies
      
      When auditing gems:
      1. Check if the gem uses `SecureRandom` instead of `rand`
      2. Verify string comparisons use `secure_compare`
      3. Look for division/modulo operations on sensitive data
      
    • swift.md 8 KB
      # Constant-Time Analysis: Swift
      
      Analysis guidance for Swift targeting iOS, macOS, watchOS, and tvOS. Swift compiles to native code, making it subject to the same CPU-level timing side-channels as C, C++, Go, and Rust.
      
      ## Understanding Swift Compilation
      
      Swift compiles directly to native machine code:
      
      ```text
      Source Code (.swift)
              |
              v
          swiftc (Swift Compiler / LLVM)
              |
              v
         Native Assembly
              |
              v
         Machine Code (binary)
      ```
      
      **Key implications:**
      
      1. **Same vulnerabilities as C** - Division, branches, and table lookups have data-dependent timing
      2. **LLVM backend** - Swift uses LLVM, so analysis is similar to clang-compiled code
      3. **Architecture matters** - x86_64 (Mac) and arm64 (iOS devices, Apple Silicon) have different instruction sets
      
      ## Running the Analyzer
      
      ```bash
      # Analyze Swift for native architecture
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.swift
      
      # Analyze for iOS device (arm64)
      uv run {baseDir}/ct_analyzer/analyzer.py --arch arm64 crypto.swift
      
      # Analyze for Intel Mac
      uv run {baseDir}/ct_analyzer/analyzer.py --arch x86_64 crypto.swift
      
      # Test multiple optimization levels (RECOMMENDED)
      uv run {baseDir}/ct_analyzer/analyzer.py --opt-level O0 crypto.swift
      uv run {baseDir}/ct_analyzer/analyzer.py --opt-level O2 crypto.swift
      
      # Include conditional branch warnings
      uv run {baseDir}/ct_analyzer/analyzer.py --warnings crypto.swift
      
      # CI-friendly JSON output
      uv run {baseDir}/ct_analyzer/analyzer.py --json crypto.swift
      ```
      
      ## Dangerous Instructions by Architecture
      
      ### ARM64 (iOS devices, Apple Silicon Macs)
      
      | Category | Instructions | Risk |
      |----------|--------------|------|
      | Division | `UDIV`, `SDIV` | Early termination optimization; variable-time |
      | Floating-Point | `FDIV`, `FSQRT` | Variable latency based on operand values |
      | Conditional Branches | `B.EQ`, `B.NE`, `CBZ`, `CBNZ`, etc. | Timing leak if condition depends on secrets |
      
      ### x86_64 (Intel Macs)
      
      | Category | Instructions | Risk |
      |----------|--------------|------|
      | Division | `DIV`, `IDIV`, `DIVQ`, `IDIVQ` | Data-dependent timing |
      | Floating-Point | `DIVSS`, `DIVSD`, `SQRTSS`, `SQRTSD` | Variable latency |
      | Conditional Branches | `JE`, `JNE`, `JZ`, `JNZ`, etc. | Timing leak if condition depends on secrets |
      
      ## Constant-Time Patterns
      
      ### Replace Division
      
      ```swift
      // VULNERABLE: Division instruction emitted
      let q = secretValue / divisor
      
      // SAFE: Barrett reduction (for fixed divisor)
      // Precompute: mu = (1 << 32) / divisor
      let mu: UInt64 = (1 << 32) / UInt64(divisor)
      let q = Int32((UInt64(secretValue) &* mu) >> 32)
      ```
      
      ### Replace Branches
      
      ```swift
      // VULNERABLE: Branch timing reveals secret
      let result = secret != 0 ? a : b
      
      // SAFE: Constant-time selection using bitwise ops
      let mask = Int32(bitPattern: UInt32(bitPattern: -Int32(secret != 0 ? 1 : 0)))
      // Better approach with no branch:
      let nonZero = (secret | -secret) >> 31  // -1 if secret != 0, else 0
      let result = (a & nonZero) | (b & ~nonZero)
      ```
      
      ### Replace Comparisons
      
      ```swift
      // VULNERABLE: Standard equality may early-terminate
      if computed == expected { ... }
      
      // SAFE: Constant-time comparison
      import CryptoKit  // Available on iOS 13+, macOS 10.15+
      
      // Use Data's built-in constant-time comparison for crypto
      if computed.withUnsafeBytes({ cPtr in
          expected.withUnsafeBytes { ePtr in
              timingSafeCompare(cPtr, ePtr)
          }
      }) { ... }
      
      // Manual constant-time comparison
      func constantTimeCompare(_ a: [UInt8], _ b: [UInt8]) -> Bool {
          guard a.count == b.count else { return false }
          var result: UInt8 = 0
          for i in 0..<a.count {
              result |= a[i] ^ b[i]
          }
          return result == 0
      }
      ```
      
      ### Secure Random
      
      ```swift
      // VULNERABLE: Don't use for cryptographic purposes
      import Foundation
      let value = Int.random(in: 0..<100)  // Uses arc4random, generally OK but not verified
      
      // SAFE: Use CryptoKit (iOS 13+, macOS 10.15+)
      import CryptoKit
      
      // Generate secure random bytes
      var randomBytes = [UInt8](repeating: 0, count: 32)
      let status = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)
      guard status == errSecSuccess else { /* handle error */ }
      
      // Or use SymmetricKey for key generation
      let key = SymmetricKey(size: .bits256)
      ```
      
      ## Apple Platform Considerations
      
      ### Using CryptoKit (Recommended)
      
      CryptoKit provides constant-time implementations for common operations:
      
      ```swift
      import CryptoKit
      
      // HMAC (constant-time internally)
      let key = SymmetricKey(size: .bits256)
      let signature = HMAC<SHA256>.authenticationCode(for: data, using: key)
      
      // AES-GCM encryption
      let sealedBox = try AES.GCM.seal(plaintext, using: key)
      
      // Curve25519 key agreement
      let privateKey = Curve25519.KeyAgreement.PrivateKey()
      let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: peerPublicKey)
      ```
      
      ### Security Framework
      
      ```swift
      import Security
      
      // Generate cryptographically secure random data
      func secureRandomBytes(count: Int) -> Data? {
          var bytes = [UInt8](repeating: 0, count: count)
          let status = SecRandomCopyBytes(kSecRandomDefault, count, &bytes)
          return status == errSecSuccess ? Data(bytes) : nil
      }
      
      // Keychain for secure storage
      func storeInKeychain(key: Data, account: String) -> Bool {
          let query: [String: Any] = [
              kSecClass as String: kSecClassGenericPassword,
              kSecAttrAccount as String: account,
              kSecValueData as String: key
          ]
          return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
      }
      ```
      
      ## Swift-Specific Pitfalls
      
      ### Optional Unwrapping
      
      ```swift
      // Branching on optionals
      if let secret = maybeSecret {  // Introduces branch
          process(secret)
      }
      
      // Guard statements also branch
      guard let secret = maybeSecret else { return }
      ```
      
      ### Pattern Matching
      
      ```swift
      // Switch/case compiles to branching code
      switch secretEnum {
      case .optionA: handleA()  // Branch
      case .optionB: handleB()  // Branch
      }
      ```
      
      ### Array Subscripting
      
      ```swift
      // Array access indexed by secret leaks via cache timing
      let value = lookupTable[secretIndex]  // Cache timing side-channel
      ```
      
      ### String Operations
      
      ```swift
      // String comparison is NOT constant-time
      if secretString == expectedString { ... }  // Variable-time
      
      // Character iteration may also have timing variations
      for char in secretString { ... }
      ```
      
      ## Setup Requirements
      
      ### Xcode (Recommended)
      
      Install Xcode from the Mac App Store. The Swift compiler is included.
      
      ```bash
      # Verify installation
      swiftc --version
      ```
      
      ### Swift Toolchain (Alternative)
      
      Download from [swift.org](https://swift.org/download/) for standalone installation.
      
      ```bash
      # Verify
      swiftc --version
      ```
      
      ### Cross-Compilation
      
      For analyzing code targeting different architectures:
      
      ```bash
      # Analyze for iOS device
      uv run {baseDir}/ct_analyzer/analyzer.py --arch arm64 crypto.swift
      
      # Analyze for iOS simulator
      uv run {baseDir}/ct_analyzer/analyzer.py --arch x86_64 crypto.swift
      ```
      
      ## Common Mistakes
      
      1. **Using Swift's == for byte comparison** - Standard equality comparison may early-terminate; use constant-time comparison
      
      2. **Trusting CryptoKit for all operations** - CryptoKit provides constant-time primitives, but combining them incorrectly can introduce vulnerabilities
      
      3. **String manipulation on secrets** - Swift strings have complex internal representations; timing varies with content
      
      4. **Ignoring optimization levels** - Swift's optimizer can transform safe source code into unsafe assembly; test at multiple -O levels
      
      5. **Platform availability** - CryptoKit requires iOS 13+/macOS 10.15+; older platforms need alternative implementations
      
      ## Testing on Different Architectures
      
      Always test your cryptographic code on actual target architectures:
      
      ```bash
      # Apple Silicon Mac (arm64)
      uv run {baseDir}/ct_analyzer/analyzer.py crypto.swift
      
      # Cross-compile for Intel
      uv run {baseDir}/ct_analyzer/analyzer.py --arch x86_64 crypto.swift
      ```
      
      ## Further Reading
      
      - [Apple CryptoKit Documentation](https://developer.apple.com/documentation/cryptokit)
      - [Apple Security Framework](https://developer.apple.com/documentation/security)
      - [Swift.org Security](https://swift.org/blog/swift-5-release/)
      - [OWASP iOS Security Guide](https://owasp.org/www-project-mobile-security-testing-guide/)
      
    • vm-compiled.md 11.3 KB
      # 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:
      
      ```text
      Source Code (.java/.cs)
              |
              v
          Compiler (javac/csc)
              |
              v
      Bytecode (.class/.dll)
              |
              v
          JIT Compiler (HotSpot/RyuJIT)
              |
              v
         Native Code (at runtime)
      ```
      
      **Security implications:**
      
      1. **Bytecode is deterministic** - Same source always produces same bytecode
      2. **JIT is non-deterministic** - Native code varies by runtime, version, and warmup state
      3. **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
      
      ```bash
      # 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.java
      ```
      
      Note: 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
      
      ```java
      // 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
      
      ```java
      // 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
      
      ```java
      // 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
      
      ```java
      // 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
      
      ```csharp
      // 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
      
      ```csharp
      // 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
      
      ```csharp
      // 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
      
      ```csharp
      // 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 comparison
      - **JEP 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 performance
      - **NSec**: 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:
      
      1. **Speculative optimization** - JIT may convert constant-time bytecode to branching native code
      2. **Escape analysis** - May inline and optimize in ways that introduce timing
      3. **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:
      
      ```bash
      # 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 behavior
      ```
      
      ## Setup Requirements
      
      ### Java
      
      **Required:** JDK 8+ with `javac` and `javap` available.
      
      **Installation:**
      
      ```bash
      # macOS (Homebrew)
      brew install openjdk@21
      
      # Ubuntu/Debian
      sudo apt install openjdk-21-jdk
      
      # Windows (via winget)
      winget install Microsoft.OpenJDK.21
      ```
      
      **PATH Configuration (macOS):**
      
      On macOS, Homebrew installs OpenJDK as "keg-only" (not linked to `/usr/local/bin`). You must add it to your PATH:
      
      ```bash
      # 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 Mac
      ```
      
      **Verification:**
      
      ```bash
      javac --version  # Should show: javac 21.x.x
      javap -version   # Should show version info
      ```
      
      **Common Issues:**
      
      - **"Unable to locate a Java Runtime"** on macOS: The system `/usr/bin/javac` is a stub that requires a real JDK. Install OpenJDK via Homebrew.
      - **Wrong Java version**: If you have multiple JDKs, use `JAVA_HOME` or ensure the correct one is first in PATH.
      
      ### C#
      
      **Required:** .NET SDK 8.0+ with `dotnet` available, plus `ilspycmd` for IL disassembly.
      
      **Installation:**
      
      ```bash
      # macOS (Homebrew)
      brew install dotnet-sdk
      
      # Ubuntu/Debian
      sudo apt install dotnet-sdk-8.0
      
      # Windows
      winget install Microsoft.DotNet.SDK.8
      ```
      
      **Install IL Disassembler:**
      
      ```bash
      dotnet tool install -g ilspycmd
      ```
      
      **PATH Configuration:**
      
      Ensure the .NET tools directory is in your PATH:
      
      ```bash
      # Add to ~/.zshrc or ~/.bashrc
      export PATH="$HOME/.dotnet/tools:$PATH"
      ```
      
      **Verification:**
      
      ```bash
      dotnet --version    # Should show: 8.x.x or higher
      ilspycmd --version  # Should show: ilspycmd: 9.x.x
      ```
      
      **Common 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:
      
        ```bash
        # 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*'
        ```
      
        `find` rather than `ls` on a glob, because `ls ~/.dotnet/.../tools/*` aborts under zsh when
        nothing matches. If `find` itself reports "No such file or directory", the store does not exist
        and `ilspycmd` was never installed with `dotnet 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:
      
        ```bash
        # macOS — substitute the major from the command above (dotnet@8, dotnet@9, …)
        brew install dotnet@9
      
        # Other platforms: install that .NET runtime alongside your SDK
        ```
      
        Note that homebrew-core does not carry a keg for every major — `dotnet@6`, `dotnet@8` and
        `dotnet@9` exist, `dotnet@7` was 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 `ilspycmd` is installed globally and `~/.dotnet/tools` is 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:
      
      ```bash
      # macOS
      brew install mono
      
      # Ubuntu/Debian
      sudo apt install mono-complete
      
      # Verify
      mcs --version
      monodis --help
      ```
      
      Note: Mono's `monodis` produces different IL output than `ilspycmd`. The analyzer supports both formats.
      
      ## Common Mistakes
      
      1. **Trusting high-level APIs** - `Arrays.equals()` in Java and `SequenceEqual()` in C# are NOT constant-time
      
      2. **Ignoring JIT behavior** - Bytecode analysis is necessary but not sufficient; JIT can introduce leaks
      
      3. **BigInteger operations** - Both platforms' BigInteger implementations may leak timing; use crypto libraries
      
      4. **String comparisons** - Never compare secrets as strings; use byte arrays with constant-time comparison
      
      5. **Exception timing** - Try/catch blocks around secret operations may leak timing through exception handling
      
      ## Further Reading
      
      - [Java Cryptography Architecture Guide](https://docs.oracle.com/en/java/javase/17/security/java-cryptography-architecture-jca-reference-guide.html)
      - [.NET Cryptography Model](https://docs.microsoft.com/en-us/dotnet/standard/security/cryptography-model)
      - [Bouncy Castle Java](https://www.bouncycastle.org/java.html) - Constant-time crypto primitives
      - [NSec](https://nsec.rocks/) - Modern cryptographic library for .NET
      
  • README.md 4.8 KB
    # Constant-Time Analysis Skill
    
    A Claude Code skill that detects timing side-channel vulnerabilities in cryptographic code by analyzing assembly or bytecode output for dangerous instructions.
    
    ## What This Skill Does
    
    When activated, this skill helps Claude:
    
    - **Detect timing vulnerabilities** - Identifies variable-time instructions (division, floating-point) that leak secrets through execution timing
    - **Analyze across architectures** - Tests compiled output for x86_64, ARM64, RISC-V, and other targets
    - **Support scripting languages** - Analyzes PHP, JavaScript/TypeScript, Python, and Ruby via bytecode
    - **Guide constant-time fixes** - Provides patterns for Barrett reduction, constant-time selection, and safe comparisons
    - **Integrate with CI** - Produces JSON output suitable for automated pipelines
    
    ## Supported Languages
    
    | Language | Analysis Method | Reference Guide |
    |----------|-----------------|-----------------|
    | C/C++ | Assembly (gcc/clang) | [references/compiled.md](references/compiled.md) |
    | Go | Assembly (go) | [references/compiled.md](references/compiled.md) |
    | Rust | Assembly (rustc) | [references/compiled.md](references/compiled.md) |
    | Swift | Assembly (swiftc) | [references/swift.md](references/swift.md) |
    | Java | JVM bytecode (javap) | [references/vm-compiled.md](references/vm-compiled.md) |
    | Kotlin | JVM bytecode (kotlinc + javap) | [references/kotlin.md](references/kotlin.md) |
    | C# | CIL (ilspycmd) | [references/vm-compiled.md](references/vm-compiled.md) |
    | PHP | Zend opcodes (VLD/OPcache) | [references/php.md](references/php.md) |
    | JavaScript | V8 bytecode (Node.js) | [references/javascript.md](references/javascript.md) |
    | TypeScript | V8 bytecode (tsc + Node.js) | [references/javascript.md](references/javascript.md) |
    | Python | CPython bytecode (dis) | [references/python.md](references/python.md) |
    | Ruby | YARV bytecode | [references/ruby.md](references/ruby.md) |
    
    ## Supported Architectures (Compiled Languages)
    
    | Architecture | Division Instructions | Common Use |
    |--------------|----------------------|------------|
    | x86_64 | DIV, IDIV | Servers, desktops |
    | ARM64 | UDIV, SDIV | Mobile, Apple Silicon |
    | ARM | UDIV, SDIV | Embedded |
    | RISC-V | DIV, DIVU, REM | Emerging platforms |
    | PowerPC | DIVW, DIVD | Legacy servers |
    | s390x | D, DR, DL | Mainframes |
    | i386 | DIV, IDIV | Legacy |
    
    ## File Structure
    
    ```text
    skills/constant-time-analysis/
    ├── SKILL.md              # Entry point - routing, analyzer usage, triage
    ├── README.md             # This file
    └── references/
        ├── compiled.md       # C, C++, Go, Rust analysis
        ├── swift.md          # Swift analysis
        ├── vm-compiled.md    # Java and C# bytecode, JVM/.NET setup
        ├── kotlin.md         # Kotlin analysis (Android/JVM)
        ├── php.md            # PHP analysis (VLD installation, opcodes)
        ├── javascript.md     # JavaScript/TypeScript analysis
        ├── python.md         # Python analysis (dis module)
        └── ruby.md           # Ruby analysis (YARV)
    ```
    
    The analyzer tool is located at `ct_analyzer/analyzer.py` in the plugin root. Its
    test suite and samples live in `ct_analyzer/tests/`:
    
    - `test_samples/` — vulnerable and constant-time inputs for detector tests
    - `triage_samples/` — one known-answer fixture per supported language, each
      pairing true positives with false positives the analyzer cannot distinguish.
      `expectations.json` records the verdict and rationale for every case;
      `TestTriageMatrix` asserts the analyzer still reports both members of each
      pair, and fails rather than skipping if no fixture could be exercised.
    
    ## Usage
    
    The skill activates automatically when Claude detects:
    
    - Cryptographic code implementation (encryption, signing, key derivation)
    - Questions about timing attacks or constant-time programming
    - Code handling secret keys, tokens, or cryptographic material
    - Functions with division/modulo operations on potentially secret data
    
    You can also invoke it explicitly by asking Claude to check code for timing vulnerabilities.
    
    ### Example Prompts
    
    ```
    "Check this crypto function for timing vulnerabilities"
    "Is this signature verification constant-time?"
    "Help me replace this division with Barrett reduction"
    "Analyze this ML-KEM implementation for KyberSlash-style issues"
    "What constant-time patterns should I use here?"
    ```
    
    ## Quick Reference
    
    | Vulnerability | Detection | Fix |
    |--------------|-----------|-----|
    | Secret division | DIV, IDIV, SDIV, UDIV | Barrett reduction |
    | Secret branches | JE, JNE, BEQ, BNE | Bit masking, cmov |
    | Secret comparison | Early-exit memcmp | crypto/subtle |
    | Variable-time FP | FDIV, FSQRT | Avoid in crypto |
    
    ## Real-World Attacks
    
    - **KyberSlash (2023)** - Division in ML-KEM leaked keys
    - **Lucky Thirteen (2013)** - Padding timing in TLS
    - **Timing attacks on RSA** - Division in modular exponentiation
    
  • SKILL.md 12.9 KB
    ---
    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`:**
    
    ```bash
    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-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:
    
    ```bash
    for f in src/crypto/*.c; do uv run {baseDir}/ct_analyzer/analyzer.py --warnings --json "$f"; done
    ```
    
    ### Prerequisites
    
    | 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](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:
    
    ```text
    [ERROR] SDIV
      Function: decompose_vulnerable
      Reason: SDIV has early termination optimization; execution time depends on operand values
    ```
    
    ## Triaging 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:
    
    ```c
    // 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
    
    1. **Static only** — reads assembly and bytecode, never runtime behavior. Cache timing and other microarchitectural channels are invisible.
    2. **No data flow analysis** — see triage above.
    3. **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](https://github.com/veorq/cryptocoding) — defensive coding for crypto
    - [KyberSlash](https://kyberslash.cr.yp.to/) — division timing in post-quantum crypto
    - [BearSSL Constant-Time](https://www.bearssl.org/constanttime.html) — practical constant-time techniques
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related