Verifying EVE Artifact Scan Evidence

Artifact scan evidence (eve.artifact_scan.evidence.v1) is a signed record of ingesting a file, detecting its real media type from content, applying bounded safe-parsing limits, running deterministic scanners, and deciding to allow, block, quarantine, redact, or require approval — with the artifact digest bound to the decision. The evidence lets a reviewer confirm the scan result is authentic and that a substituted file would be detected — without trusting the EVE server.

Readiness: deterministic multimodal artifact scanning + signed evidence and artifact substitution protection are PILOT_READY (customer-pilot ready at supported boundaries).

What it proves

  • Integrity + authenticity — the scan result (findings, final action, completeness) was signed by the EVE signing key (Ed25519 in the production configuration; HMAC-SHA256 fallback is symmetric and not independently verifiable) over jcs-1 canonical bytes.
  • Substitution protection — the evidence binds the artifact_digest. If the file is swapped for a different one after approval, verification of the evidence against the new file's digest fails. Verification rejects an evidence whose artifact_digest was changed.
  • Deterministic derivation — the shipped scanners are deterministic (pattern / structure based); the recorded media type was detected from content (magic-byte / structural), and the declared type is only compared, never trusted.
  • Completeness honesty — incomplete extraction is reported as partial and cannot masquerade as complete; the completeness status is signature-covered.

What it does NOT prove

  • It does not claim complete understanding of every artifact. OCR, vision, and QR/barcode decoding are probabilistic, disabled by default, and recorded as unavailable. A clean scan under default settings is a deterministic-scanner result, not a full-content understanding.
  • It does not by itself enforce re-scan-before-use. Substitution is detected at verification time; wiring a re-scan at the ingestion boundary is a per-deployment responsibility. If your integration does not verify before use, a swap is not caught.
  • It does not attest that a permitted file is safe in an absolute sense — deterministic detectors are pattern-based and a novel obfuscation may evade them until a detector is added (findings are versioned, so coverage changes are auditable).

Required keys

eve.artifact_scan.evidence.v1 binds at least:

Field Meaning
schema version eve.artifact_scan.evidence.v1 identifier
artifact_digest SHA-256 of the ingested artifact (substitution binding)
descriptor Content-based ArtifactDescriptor (detected vs declared media type)
parser / scanner / policy versions Versioned components
findings_digest Digest of the full findings set
extraction-status digest Digest of what was / was not extracted
completeness status complete / partial (fail-closed on incomplete)
final_action BLOCK / QUARANTINE / REQUIRE_APPROVAL / REDACT / ALLOW_WITH_FINDINGS / ALLOW / UNSUPPORTED
CoreGuard decision The bound governance decision
tenant / principal / workflow / session / destination / tool Context
prev / current hash Evidence-chain links
kid, canon, signature Signer key, jcs-1, signature

Verification fails closed if a required key is absent.

Canonicalization (jcs-1)

Evidence is signed over jcs-1, a constrained RFC 8785 profile with proven byte-for-byte parity across Python, TypeScript, and the browser for the supported value domain. jcs-1 fails closed on non-integer floats / NaN / Infinity. A verifier reading evidence whose canon is not jcs-1 reports unsupported canonicalization.

Verification profiles: strict vs standard

  • Standard — verifies canonicalization, content hash, signature, schema, and that the artifact_digest matches the file you hold.
  • Strict — additionally requires an independently verifiable Ed25519 signature and rejects HMAC-fallback evidence. Use strict for external audit. The Node verifier for scan evidence is verify_scan_evidence.mjs; the Python path is verify_scan_evidence / verify_evidence.

Linked evidence

  • Decision Certificate — the CoreGuard decision bound to the scan.
  • MCP Execution Evidence — if the artifact was a tool-argument file, the approved-versus-executed binding for the tool call.
  • Red Team Report — adversarial artifact cases (credential / exfil / traversal / bomb / polyglot) and multi-step chains that start with a poisoned document.

Verify each linked artifact independently.

Selective-disclosure / omission checks

The signature covers the whole canonical payload; the findings_digest binds the full findings set, so dropping a finding is detected. Removing or altering the completeness status, the descriptor, or the artifact_digest invalidates the content hash and the signature. Verify the full evidence before reading a subset; do not re-canonicalize a reduced object.

Failure categories

Category Meaning
invalid signature Signature does not match the canonical content under the given key
unknown key kid / public key not recognized or not supplied for a strict check
unsupported schema Evidence schema version is not understood by this verifier
unsupported canonicalization canon is not jcs-1
digest mismatch artifact_digest != the file you hold, or content_hash mismatch
selective omission A signature-covered finding or field was removed / altered
stale Evidence is older than your freshness window

Note: unsupported artifact formats are surfaced as ARTIFACT_UNSUPPORTED_FORMAT (fail closed), and archive hazards fail closed with explicit reasons (e.g. ARCHIVE_PATH_TRAVERSAL, ARCHIVE_DECOMPRESSION_BOMB) — these are recorded in the evidence, not hidden.

Freshness

Scan evidence records a past scan under specific scanner and policy versions. It does not expire on its own; enforce a freshness policy so you do not accept a scan produced under an outdated scanner or policy version, and treat over-age evidence as stale. Because the scan cache is keyed by (tenant, sha256, scanner_version, policy_version), a bump in scanner or policy version means a fresh scan is warranted.

Replay behavior

Scan evidence is a record, not an authorization. Re-presenting it does not re-ingest a file and causes no side effect. A cache entry for one tenant never satisfies another tenant. To rely on a prior clean result, verify the evidence against the exact file digest you are about to use — this is what makes substitution detectable.

Verify it yourself

Python

# Verify the signature, then bind the result to the file you hold by comparing the
# signed artifact_digest against a local recompute. Synthetic fixture — no creds.
import hashlib, json
from core.eve_sdk import verify_evidence  # ships with the EVE service

with open("scan_evidence.json", "r", encoding="utf-8") as fh:
    evidence = json.load(fh)

# verify_evidence's first argument is the evidence KIND ("scan").
result = verify_evidence("scan", evidence, expected_tenant="org_abc")
assert result["valid"], result["reason"]

# Substitution check: recompute the local digest and compare to the signed field.
local_digest = hashlib.sha256(open("attachment.bin", "rb").read()).hexdigest()
signed_digest = evidence.get("artifact_digest", "").removeprefix("sha256:")
assert local_digest == signed_digest, "artifact substituted — digest mismatch"
print(result["valid"], result["reason"])  # True, "verified"

Node (TypeScript / ESM, Node >= 18)

import { verifyEvidence } from "@eve/coreguard";
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";

const publicKeyPem = readFileSync("public_key.pem", "utf8");
const evidence = JSON.parse(readFileSync("scan_evidence.json", "utf8"));

// 1) Verify content hash + Ed25519 signature.
const result = verifyEvidence(evidence, { publicKeyPem });
if (!result.valid) throw new Error(result.reason);

// 2) Substitution check: recompute the local digest and compare to the signed field.
const localDigest = createHash("sha256").update(readFileSync("attachment.bin")).digest("hex");
const signedDigest = String(evidence.artifact_digest ?? "").replace(/^sha256:/, "");
if (localDigest !== signedDigest) throw new Error("artifact substituted — digest mismatch");
console.log(result.valid, result.signature); // true "ed25519-valid"

CLI

# The `eve` CLI ships with the EVE service/repo. Form: verify <kind> <evidence.json>
# [--strict]; verify the signature here, then compare the signed artifact_digest to
# `sha256sum attachment.bin` to catch a substituted file.
python -m core.eve_sdk.cli verify scan scan_evidence.json --strict

Readiness

  • Deterministic artifact scanning + signed evidence and artifact substitution protection are PILOT_READY (customer-pilot ready at supported boundaries).
  • Offline verification (Python / Node / browser for the evidence) is SUPPORTED; jcs-1 is SUPPORTED (constrained RFC 8785 profile).
  • Scanning runs in embedded (service) / sidecar mode; the Python eve-coreguard client (0.2.2) reaches the service via hosted mode. TypeScript verifies scan evidence offline and never signs.
  • Independent verification requires the Ed25519 public key; the HMAC fallback is symmetric and not independently verifiable.
  • The release-candidate clients are distributed privately for pilots — install the pilot artifact, not a public pip/npm install (see readiness for public-registry status).

Limitations

  • Deterministic scanners are pattern / structure based. OCR, vision, and QR/barcode are probabilistic and disabled by default (recorded as unavailable); incomplete extraction is reported as partial and cannot masquerade as complete.
  • Substitution is detected at verification time; enforcing re-scan-before-use at the ingestion boundary is a per-deployment wiring responsibility.
  • Novel obfuscations may evade pattern-based detectors until a detector is added; findings are versioned so coverage changes are auditable.
  • In the pilot, the scan cache is in-process (bounded, tenant-partitioned); durability is a deployment concern.
  • HMAC-fallback signatures are not independently verifiable; use the Ed25519 configuration and the strict profile for external audit.
Part of the EVE AI Core control plane Deterministic AI Governance Control Plane → Policy decisions that return the same result for the same input every time, before execution.