Verifying Binary Patches with Stella Ops Micro-Witnesses

Audience: security engineers, auditors, and procurement reviewers who need to confirm that a shipped binary actually carries a specific security patch — not just that the patch exists upstream.

This guide explains how to generate, verify, and bundle binary micro-witnesses with the stella CLI to prove patch status at the binary level.

Overview

Binary micro-witnesses provide cryptographic proof of patch status at the binary level. Unlike source-level attestations, they verify what is actually deployed, not what should be deployed.

Use Cases

Quick Start

Generate a Witness

stella witness generate /path/to/libssl.so.3 \
  --cve CVE-2024-0567 \
  --sbom sbom.cdx.json \
  --output witness.json

stella witness generate requires the patch-verification service to be wired into the CLI host. If the service is not available, or if the verification itself fails, the command exits with code 9 (NotImplemented) and does not emit a placeholder witness.

NOT IMPLEMENTED — --sign / --rekor. As of the current code, passing either --sign or --rekor to stella witness generate causes the command to exit early with code 9 and the message: “witness signing and Rekor publication require the Attestor/Signer integration. No local placeholder signature or transparency metadata was generated.” Local detached-signature and Rekor inclusion-metadata generation exist in the handler but are gated off pending the Attestor/Signer integration. Until that integration lands, generate unsigned witnesses and rely on the binary/SBOM digests plus the deltaSigDigest back-reference for integrity. See Transparency Logging.

Verify a Witness

# Default verification (signature + any embedded Rekor metadata + SBOM digest when --sbom is supplied)
stella witness verify witness.json

# Offline verification (air-gapped). NB: verify is always offline today, so --offline
# is currently a no-op flag — see the CLI Reference note for stella witness verify.
stella witness verify witness.json --offline

Verification checks the witness’s detached-signature metadata and, when a rekor block is present, re-derives and checks the inclusion metadata; it also compares the SBOM digest when --sbom is supplied. Note that current generate output is unsigned and carries no rekor block (see Transparency Logging), so an out-of-the-box witness reports the signature as not verified.

Create Portable Bundle

stella witness bundle witness.json --output ./audit-bundle

Understanding Verdicts

VerdictMeaning
patchedBinary matches patched version signature
vulnerableBinary matches vulnerable version signature
inconclusiveUnable to determine (insufficient evidence)
partialSome functions patched, others not

Confidence Scores

Confidence ranges from 0.0 to 1.0 (the predicate schema enforces this range). The bands below are interpretive guidance for readers, not thresholds enforced by the witness schema:

The underlying patch-verification engine reports a Verified (→ patched) result when match confidence meets MinConfidenceThreshold, which defaults to 0.70 in PatchVerificationOptions. A witness can therefore carry a patched verdict with a confidence between 0.70 and 0.80 — below the “Medium” band above. Treat the bands as a presentation aid and the 0.70 default as the engine’s actual decision point.

Evidence Types

Each evidence entry carries a function, a state, a score (0.0-1.0), the method used, and an optional hash. The evidence array must contain at least one entry and is capped at 10 (top matches only) per the predicate schema.

{
  "evidence": [
    {
      "function": "SSL_CTX_new",
      "state": "patched",
      "score": 0.97,
      "method": "semantic_ksg",
      "hash": "..."
    }
  ]
}

The state field accepts one of: patched, vulnerable, modified, unchanged, unknown.

Match Methods

The predicate schema constrains evidence[].method to the following enum:

MethodDescriptionRobustness
byte_exactExact byte-level matchFragile to recompilation
cfg_structuralControl flow graph structureModerate
semantic_ksgSemantic key-semantics graphRobust to optimization
ir_semanticIR-level semantic comparisonMost robust
chunk_rollingRolling-chunk content hashModerate

Source/schema mismatch (flagged). The values above are the schema-permitted set (stellaops-binary-micro-witness.v1.schema.json). However, the CLI generate handler currently derives method directly from the patch-verification engine’s FingerprintMethod enum via .ToString().ToLowerInvariant(), which yields tlsh, cfghash, instructionhash, symbolhash, or sectionhash. None of those are in the schema enum, so a witness produced by the current generate path will not validate against the published schema’s method constraint. This is an open inconsistency between WitnessCoreCommandHandlers and the predicate schema, not a documentation choice; treat the schema enum as the contract and the emitted strings as a known gap.

Offline Verification

For air-gapped environments:

  1. Create bundle (witness predicate + verification scripts; no Rekor tile proof is embedded today — see the flag below):

    stella witness bundle witness.json --output ./bundle
    
  2. Transfer bundle to air-gapped system

  3. Verify offline:

    # PowerShell
    .\verify.ps1
    
    # Bash
    ./verify.sh
    

The bundle written by stella witness bundle contains:

The generated verify.ps1/verify.sh scripts parse and display the witness fields, then delegate to stella witness verify if the stella CLI is on PATH; if it is not, they print a warning and skip the signature/Rekor metadata checks.

Flags vs. behaviour (flagged). stella witness bundle accepts --include-binary and --include-sbom, but the current handler does not copy the analyzed binary or the SBOM into the bundle — only witness.json, the two scripts, and README.md are written regardless of those flags. Likewise, no Rekor tile proof is embedded today (see Transparency Logging — Rekor metadata generation is gated off). Plan bundle contents around the four files listed above.

Integration with SBOMs

Micro-witnesses can reference SBOM components:

{
  "sbomRef": {
    "sbomDigest": "sha256:...",
    "purl": "pkg:deb/debian/openssl@3.0.11",
    "bomRef": "openssl-3.0.11"
  }
}

This links binary verification to your software inventory. All three sbomRef fields (sbomDigest, purl, bomRef) are optional in the schema. Note that the CLI generate path only populates sbomDigest (the SHA-256 of the --sbom file); purl and bomRef are reserved for callers that build the predicate programmatically.

Predicate Schema

The required top-level fields are schemaVersion, binary, cve, verdict, confidence, evidence, tooling, and computedAt. deltaSigDigest and sbomRef are optional. schemaVersion is pinned to the constant 1.0.0.

{
  "schemaVersion": "1.0.0",
  "binary": {
    "digest": "sha256:...",
    "purl": "pkg:...",
    "arch": "linux-amd64",
    "filename": "libssl.so.3"
  },
  "cve": {
    "id": "CVE-2024-0567",
    "advisory": "https://...",
    "patchCommit": "abc123"
  },
  "verdict": "patched",
  "confidence": 0.95,
  "evidence": [...],
  "deltaSigDigest": "sha256:...",
  "sbomRef": {...},
  "tooling": {
    "binaryIndexVersion": "2.1.0",
    "lifter": "b2r2",
    "matchAlgorithm": "semantic_ksg"
  },
  "computedAt": "2026-01-28T12:00:00Z"
}

Schema notes grounded in stellaops-binary-micro-witness.v1.schema.json:

Limitations

What Micro-Witnesses Prove

What They Do NOT Prove

Technical Limitations

Transparency Logging

NOT IMPLEMENTED (Draft/roadmap). Rekor transparency logging is not wired in the current CLI. Passing --rekor (which also requires --sign) to stella witness generate causes the command to exit with code 9 before any witness is produced, because both signing and Rekor publication are gated behind the Attestor/Signer integration. The handler contains deterministic local inclusion-metadata generation (entryId, logIndex, integratedTime, leafHash, rootHash) and stella witness verify can re-derive and check that metadata if it is present in a witness, but the generate path never reaches that code today.

The intended behaviour, once the Attestor/Signer integration lands, is:

Until then, offline bundles do not embed Rekor tile proofs (see Offline Verification).

CLI Reference

stella witness generate

stella witness generate <binary> --cve <id> [options]

Arguments:
  binary              Path to binary file to analyze

Options:
  -c, --cve <id>      CVE identifier (required; validated at runtime)
  -s, --sbom <path>   Path to SBOM file (CycloneDX or SPDX) for component mapping
  -o, --output <path> Output file (default: stdout)
  --sign              Sign the witness  [NOT IMPLEMENTED — exits with code 9]
  --rekor             Log to Rekor transparency log  [NOT IMPLEMENTED — exits with code 9; also requires --sign]
  -f, --format        Output format: json, envelope (default: json)
  -v, --verbose       Enable verbose output

Notes:

stella witness verify

stella witness verify <witness> [options]

Arguments:
  witness             Path to witness file (JSON or DSSE envelope)

Options:
  --offline           Verify without network access  [no-op — verify is always offline]
  -s, --sbom <path>   Validate SBOM digest against the witness sbomRef.sbomDigest
  -f, --format        Output format: text, json (default: text)
  -v, --verbose       Enable verbose output

--offline is currently a no-op (flagged). stella witness verify performs no network access in either mode: signature checks use a local HMAC and any embedded rekor block is re-derived in-process. The --offline flag only changes a verbose log line (Mode: offline (no network access)) in HandleVerifyAsync — it does not load a separate bundled proof or otherwise alter the verification path. Treat verify as always-offline; the flag is reserved for the future Rekor integration.

Exit codes: 0 when the witness passes (signature valid; Rekor proof valid when present; SBOM digest matches when --sbom is supplied), 5 (VerificationFailed) otherwise, and 1 if the witness file is missing or cannot be parsed. An unsigned witness reports the signature as “not verified” and fails the overall check.

stella witness bundle

stella witness bundle <witness> --output <dir> [options]

Arguments:
  witness             Path to witness file to bundle

Options:
  -o, --output <dir>  Output directory (required)
  --include-binary    Include analyzed binary   [accepted but currently no-op]
  --include-sbom      Include SBOM file          [accepted but currently no-op]
  -v, --verbose       Enable verbose output