Skip to content

Feat/tinfoil conformance#93

Open
lsd-cat wants to merge 22 commits into
mainfrom
feat/tinfoil-conformance
Open

Feat/tinfoil conformance#93
lsd-cat wants to merge 22 commits into
mainfrom
feat/tinfoil-conformance

Conversation

@lsd-cat

@lsd-cat lsd-cat commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Follows the other SDKs to implement conformance testing. Build the dedicated CLI and add a CI job.


Summary by cubic

Adds a cross-SDK tinfoil-conformance CLI and CI workflow to exercise policy-layer verification across Sigstore, TDX, and SEV-SNP, plus EHBP key binding. The TDX public‑API path now runs the real verifier end‑to‑end with config re‑baseline and accepted‑QV‑results gating.

  • New Features

    • tinfoil-conformance CLI: capabilities, verify-sigstore, verify-measurement, verify-hardware-measurements, verify-attestation-tdx, verify-attestation-sev (adapter/public API), verify-full (standard/bundle/pinned; JSON-in/JSON-out), and verify-ehbp-key-binding.
    • Sigstore: verify_sigstore_bundle_with_policy with SigstorePolicy/SigstoreVerification; hermetic trust-root via sigstore.TrustedRoot; emits bundle observables; capability flags align with sigstore behavior.
    • TDX: structural quote checks; header/body parsing with TD attribute decode; full §4.7 TCB evaluation with injected collateral and verification-time override; §4.8 extended-TD pins; Intel root injection; accepts non-terminal TCB; execution_mode="public_api" now drives the real verifier end‑to‑end with re‑baselined config and §4.7.7 accepted‑QV‑results enforcement.
    • SEV-SNP: v3 report verification with injected VCEK and embedded/injected ASK/ARK; §3.4 cross-checks; rejects MIGRATE_MA; execution_mode="public_api" uses attestation.verify_sev_attestation_v2 (no network) and applies §3.7 policy pins; time override is system-clock-only.
    • verify-full: chains Sigstore → SEV, compares measurements (SPEC §11), supports pinned/bundle flows, and returns a final fingerprint with originating-stage rejections.
    • EHBP key binding: extracts HPKE key from report_data[32:64] and validates via the SDK’s EHBP parser; rejects with EHBP_KEY_BINDING_MISMATCH.
    • Capabilities updated: add stages verify-measurement, verify-hardware-measurements, verify-full, verify-ehbp-key-binding; flow_modes_supported=["standard","bundle","pinned"]; TDX/SEV support and extended checks; verification_time_override="supported" (TDX) / "system-clock-only" (SEV); measurement.compare_multiplatform_to_tdx_supported=true; transport_ehbp_supported=true; ehbp.key_binding_supported=true.
  • Refactors

    • Expanded TDX/SEV error classifiers to SPEC-coded rejections, including policy/collateral messages; strict prefix matching for workflow_ref_prefix (SPEC §5.3).
    • Added inner helpers for Sigstore/SEV used by verify-full; isolated Python-specific Sigstore hooks under tinfoil.conformance.sigstore.
    • CI workflow runs the cross-SDK suite against the tinfoil-conformance binary.

Written for commit 33b1785. Summary will update on new commits.

Review in cubic

@lsd-cat lsd-cat marked this pull request as ready for review June 8, 2026 20:02

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 issues found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/tinfoil/conformance/cli.py">

<violation number="1" location="src/tinfoil/conformance/cli.py:944">
P1: Custom agent: **Check System Design and Architectural Patterns**

Runtime monkey-patching of library module globals from CLI code creates tight coupling and bypasses explicit interfaces. The conformance CLI directly mutates `collateral_tdx.datetime`, `cert_utils.datetime`, `fetch_collateral`, and `intel_root_ca.INTEL_SGX_ROOT_CA_PEM` rather than using dependency injection or parameterized library calls. This breaks proper layering and makes internal library refactors hazardous.</violation>

<violation number="2" location="src/tinfoil/conformance/cli.py:1008">
P1: CRLs are accepted in `_evaluate_collateral` without signature verification, enabling revocation-check bypass with injected collateral.</violation>

<violation number="3" location="src/tinfoil/conformance/cli.py:1261">
P2: Invalid `min_tee_tcb_svn_hex` is silently ignored, effectively disabling the minimum TCB policy check.</violation>

<violation number="4" location="src/tinfoil/conformance/cli.py:1568">
P2: Mandatory VCEK TCB extension checks are bypassed when extensions are missing or malformed due to `continue` paths.</violation>

<violation number="5" location="src/tinfoil/conformance/cli.py:1689">
P1: Custom agent: **Flag Security Vulnerabilities**

Unbounded gzip decompression of untrusted attestation input enables memory/CPU DoS via a crafted gzip bomb.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

return ("REPORT_FORMAT_UNSUPPORTED", "3.1",
f"attestation_doc_b64 not valid base64: {e}")
try:
report_bytes = _gzip.decompress(gz_bytes)

@cubic-dev-ai cubic-dev-ai Bot Jun 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Flag Security Vulnerabilities

Unbounded gzip decompression of untrusted attestation input enables memory/CPU DoS via a crafted gzip bomb.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 1689:

<comment>Unbounded gzip decompression of untrusted attestation input enables memory/CPU DoS via a crafted gzip bomb.</comment>

<file context>
@@ -0,0 +1,2029 @@
+        return ("REPORT_FORMAT_UNSUPPORTED", "3.1",
+                f"attestation_doc_b64 not valid base64: {e}")
+    try:
+        report_bytes = _gzip.decompress(gz_bytes)
+    except Exception as e:
+        return ("REPORT_FORMAT_UNSUPPORTED", "3.1",
</file context>
Fix with cubic

root_crl_obj = None
if pck_crl_der:
crl = x509.load_der_x509_crl(pck_crl_der)
pck_crl_obj = PckCrl(crl=crl, ca_type="platform",

@cubic-dev-ai cubic-dev-ai Bot Jun 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: CRLs are accepted in _evaluate_collateral without signature verification, enabling revocation-check bypass with injected collateral.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 1008:

<comment>CRLs are accepted in `_evaluate_collateral` without signature verification, enabling revocation-check bypass with injected collateral.</comment>

<file context>
@@ -0,0 +1,2029 @@
+        root_crl_obj = None
+        if pck_crl_der:
+            crl = x509.load_der_x509_crl(pck_crl_der)
+            pck_crl_obj = PckCrl(crl=crl, ca_type="platform",
+                                 next_update=crl.next_update_utc or datetime.now(timezone.utc))
+        if root_crl_der:
</file context>
Fix with cubic

from ..attestation import cert_utils as _cert_mod
orig_coll_dt = _coll_mod.datetime
orig_cert_dt = _cert_mod.datetime
_coll_mod.datetime = _FixedDatetime

@cubic-dev-ai cubic-dev-ai Bot Jun 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Custom agent: Check System Design and Architectural Patterns

Runtime monkey-patching of library module globals from CLI code creates tight coupling and bypasses explicit interfaces. The conformance CLI directly mutates collateral_tdx.datetime, cert_utils.datetime, fetch_collateral, and intel_root_ca.INTEL_SGX_ROOT_CA_PEM rather than using dependency injection or parameterized library calls. This breaks proper layering and makes internal library refactors hazardous.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 944:

<comment>Runtime monkey-patching of library module globals from CLI code creates tight coupling and bypasses explicit interfaces. The conformance CLI directly mutates `collateral_tdx.datetime`, `cert_utils.datetime`, `fetch_collateral`, and `intel_root_ca.INTEL_SGX_ROOT_CA_PEM` rather than using dependency injection or parameterized library calls. This breaks proper layering and makes internal library refactors hazardous.</comment>

<file context>
@@ -0,0 +1,2029 @@
+    from ..attestation import cert_utils as _cert_mod
+    orig_coll_dt = _coll_mod.datetime
+    orig_cert_dt = _cert_mod.datetime
+    _coll_mod.datetime = _FixedDatetime
+    _cert_mod.datetime = _FixedDatetime
+    try:
</file context>
Fix with cubic

Comment thread src/tinfoil/sigstore.py Outdated
]
for name, oid, report_val in tcb_pairs:
raw = ext_map.get(oid)
if raw is None:

@cubic-dev-ai cubic-dev-ai Bot Jun 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Mandatory VCEK TCB extension checks are bypassed when extensions are missing or malformed due to continue paths.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 1568:

<comment>Mandatory VCEK TCB extension checks are bypassed when extensions are missing or malformed due to `continue` paths.</comment>

<file context>
@@ -0,0 +1,2029 @@
+    ]
+    for name, oid, report_val in tcb_pairs:
+        raw = ext_map.get(oid)
+        if raw is None:
+            continue
+        cert_val = _decode_int_ext_value(raw)
</file context>
Fix with cubic

f"tee_tcb_svn[{i}]={tee_tcb_svn[i]} < min[{i}]={minimum[i]} "
f"(quote={tee_tcb_svn.hex()}, minimum={minimum.hex()})")
except ValueError:
pass

@cubic-dev-ai cubic-dev-ai Bot Jun 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Invalid min_tee_tcb_svn_hex is silently ignored, effectively disabling the minimum TCB policy check.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 1261:

<comment>Invalid `min_tee_tcb_svn_hex` is silently ignored, effectively disabling the minimum TCB policy check.</comment>

<file context>
@@ -0,0 +1,2029 @@
+                                f"tee_tcb_svn[{i}]={tee_tcb_svn[i]} < min[{i}]={minimum[i]} "
+                                f"(quote={tee_tcb_svn.hex()}, minimum={minimum.hex()})")
+        except ValueError:
+            pass
+
+    return "", ""
</file context>
Fix with cubic

Comment thread src/tinfoil/conformance/cli.py
Comment thread src/tinfoil/conformance/cli.py
Comment thread src/tinfoil/sigstore.py Outdated
Comment thread src/tinfoil/sigstore.py Outdated
lsd-cat and others added 17 commits June 15, 2026 04:17
…oil-conformance binary

Mid-level entry point + CLI binary that bring tinfoil-python into the
cross-SDK conformance suite (https://github.com/lsd-cat/tinfoil-conformance).
Mirrors the same split landed earlier in tinfoil-rs and tinfoil-js.

Library changes (src/tinfoil/sigstore.py):

  * New SigstorePolicy dataclass — one field per SPEC §5 clause.
    Mirrors the structs in tinfoil-rs / tinfoil-js so the conformance
    harness can pass identical policy objects to every SDK.
  * `default_sigstore_policy(repo)` returns the canonical Tinfoil
    settings.
  * New SigstoreVerification dataclass carries the extracted
    measurement plus cert/rekor/tlog/sct fields surfaced to the
    conformance harness.
  * New GitHubWorkflowRefPrefix policy class — strict-prefix
    startswith() check on the cert's GitHubWorkflowRef extension (.1.6).
    Replaces the previous regex-based GitHubWorkflowRefPattern, matching
    SPEC §5.3's prefix semantics.
  * New `verify_sigstore_bundle_with_policy(bundle_bytes, expected_digest,
    policy, trust_root_json)`. Hermetic — no network, no embedded trust
    root. Trust root is loaded via sigstore.models.TrustedRoot.from_file
    after the inline JSON is written to a scoped temp directory (PyCA
    `sigstore` 4.x only exposes the file loader).
  * Post-DSSE verification checks: payload_type exact match,
    in-toto statement type allow-list, subject digest binding
    (lowercase normalize per SPEC §7.3), predicate type allow-list,
    register extraction, and pure bundle observables for the harness
    (rekor logId hex, integrated time, tlog count, sct count, cert
    OIDC issuer, cert workflow repository, cert BuildSignerURI).

CLI binary (src/tinfoil/conformance/{__init__,cli}.py):

  * tinfoil-conformance console_script — implements the
    capabilities / verify-sigstore subcommands and the exit-code
    contract (0 / 10 / 20 / 30 / 1) the harness expects.
  * capabilities honestly declares the known limitations of the
    sigstore PyPI package:
      - accepts_multi_tlog_entries: False  (lib hardcodes exactly-1)
      - oidc_issuer_v2_preferred: False    (lib reads V1 before V2)
      - scts_count_distinguish_missing_vs_duplicate: False
        (lib raises the same "Expected one certificate timestamp"
         error for missing and duplicate SCTs)
      - legacy_bundle_format_supported: False  (sigstore-python
        Bundle.from_json validates against v0.3 layout)
  * Error classifier maps both our own policy-driven prefix codes and
    sigstore-python's natural English error phrasings to the SPEC-
    anchored rejection-code taxonomy.

CI (.github/workflows/tinfoil-conformance.yml):

  Self-contained workflow matching the patterns in tinfoil-rs and
  tinfoil-js: uv sync to build the SDK, checkout the conformance
  suite, install harness via pip, run vectors, upload results,
  append summary.

Verification:
  - 286/286 existing pytest unit tests pass.
  - Full conformance suite (45 fixtures) runs against tinfoil-py:
    42 pass + 3 capability-gated skips + 0 fails.
  - All three SDKs now run the same suite simultaneously; 131 of 135
    assertions are green, the 4 skips are honestly declared
    capability gaps in either Rust (legacy bundle format) or Python
    (multi-tlog, V2 preference, SCT duplicate distinction).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same three flags landing in tinfoil-rs and tinfoil-js, declared
honestly for sigstore-python's behavior:

  * rejects_duplicate_sct_log: True — sigstore-python uses the same
    "Expected one certificate timestamp" count-check for both
    missing-SCT and duplicate-SCT, so any duplicate gets rejected
    (though through the same error path that rejects missing — see
    scts_count_distinguish_missing_vs_duplicate=False).
  * checks_only_subject_0: True — sigstore-python's verify_dsse path
    only inspects subject[0].
  * in_toto_statement_tolerates_extra_fields: True — sigstore-python's
    statement parser uses pydantic models with extra=allow.

Lets the four-SDK suite cleanly gate fixtures 066/073/074 on tinfoil-go
(which declares all three as False because sigstore-go behaves
differently).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmd_verify_measurement wraps tinfoil.attestation.types.Measurement —
its existing fingerprint() and assert_equal() already implement SPEC §7.2/
§7.3 with the right semantics. The conformance binary adds:

  * type/register-count validation against the SPEC §7.1 layout table
    (returns MEASUREMENT_TYPE_UNKNOWN / MEASUREMENT_REGISTER_COUNT_INVALID
    before reaching the lib)

  * lowercase normalization of registers per SPEC §7.3 normative rule

  * exception → rejection code classifier:
      Rtmr3NotZeroError      → MEASUREMENT_RTMR3_NONZERO
      MeasurementMismatchError → MEASUREMENT_MISMATCH
      FormatMismatchError    → MEASUREMENT_TYPE_COMBINATION_UNSUPPORTED

Capabilities:
  - stages_supported += "verify-measurement"
  - measurement.compare_multiplatform_to_tdx_supported = true

17 of 17 measurement fixtures pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmd_verify_hardware_measurements wraps tinfoil.attestation.verify_tdx_hardware,
which already implements SPEC §6.3 step 1–4 with the right semantics. The
conformance binary adds:

  * SPEC §7.3 lowercase normalization on both sides before calling the lib
  * type/count validation at the binary boundary (so the SPEC-anchored
    code surfaces as ENCLAVE_MEASUREMENT_TYPE_INVALID /
    ENCLAVE_REGISTER_COUNT_INVALID rather than a ValueError from the lib)
  * HardwareMeasurementError → HARDWARE_NO_MATCH mapping

Capabilities:
  - stages_supported += "verify-hardware-measurements"

11 of 11 hardware-measurement fixtures pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cross-SDK conformance repo moved from lsd-cat/ to tinfoilsh/ on GitHub.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the new attestation_tdx capability bag from
tinfoilsh/tinfoil-conformance Phase 1, declared honestly as false:
TDX attestation verification through the conformance contract isn't
wired up yet (SDK either lacks TDX entirely or needs a custom
collateral-injection Getter wrapper). Phase 1.5 lands the wrapper and
flips the flag to true.

For now, attestation-tdx fixtures skip cleanly on this SDK with
reason "stage not in stages_supported".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…se 1.5)

tinfoil-python's native TDX verifier (tinfoil.attestation.verify_tdx) has
clean injection-friendly APIs all along — verify_tdx_quote(quote,
raw_quote) does Intel §4.1.2 steps 1-4 (PCK chain, quote signature, QE
report signature, AK ↔ QE report data binding) with zero network calls.
The "needs Phase 1.5 wrapper" disclaimer I added on the original
capabilities declaration was wrong; the API was already injection-ready
for the structural path.

  * cmd_verify_attestation_tdx parses the raw quote via abi_tdx.parse_quote
    and calls verify_tdx_quote_crypto for steps 1-4. Body field extraction
    + TDATTRIBUTES bit-decoding mirrors the Go binary 1:1 so the harness
    output diff is comparable across SDKs.

  * tcb_evaluation_required=true is currently rejected with a
    QV_RESULT_TERMINAL_UNSPECIFIED carrying a clear "Phase 1.5 only
    structural" message. Phase 3 will wire the full collateral path
    via verify_tcb_info_signature + verify_qe_identity_signature
    (which already accept injected response bytes + issuer chain
    parameters — no API change needed in the lib).

Capabilities flipped to true:
  attestation_tdx.supported = true
  attestation_tdx.injected_collateral_supported = true

Fixture 300-tdx-v4-happy passes; full suite tally:
  tinfoil-py: pass=74 fail=0 skip=3 (was 73 / 4 before)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds substring patterns for tinfoil-python's TDX verifier error phrasings
to map onto SPEC-anchored rejection codes. Cleaner messages than go-tdx-
guest emits (e.g. "Invalid TEE type: 0x0. Expected 0x81 (TDX).") but the
classifier needed updating to recognize them.

Order matters: chain errors mention "signature" but should map to
PCK_CHAIN_INVALID, not QUOTE_SIGNATURE_INVALID. The chain matchers run
before the generic signature pattern.

Recognized patterns:
  * "invalid tee type" / "tee type"           → WRONG_TEE_TYPE
  * "attestation key type"                    → ATTESTATION_KEY_TYPE_UNSUPPORTED
  * "qe vendor" / "unknown qe"                → QE_VENDOR_UNKNOWN
  * "quote too short" / "minimum size"        → QUOTE_TRUNCATED
  * "certification data" / "size mismatch"    → QUOTE_FORMAT_UNSUPPORTED
  * "pck...chain" / "certificate chain"       → PCK_CHAIN_INVALID
  * "expired" / "not yet valid"               → PCK_EXPIRED

After: 13/15 Phase 2A fixtures pass on Python. Two skip:
  * 324-pck-leaf-expired — verify_intel_chain calls datetime.now()
    unconditionally; gated on verification_time_override="supported"
    (declared "system-clock-only").
  * 325-pck-fmspc-mismatch — gated on FMSPC policy capability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…= false

Phase 2B introduces collateral-tampering fixtures (326, 327, 340-345)
gated on attestation_tdx.tcb_evaluation_supported=true. tinfoil-python's
cmd_verify_attestation_tdx still rejects tcb_evaluation_required=true
with a "Phase 1.5 only structural" message — the collateral wrapper
(verify_tcb_info_signature, verify_qe_identity_signature, TCB level
matching) hasn't been wired yet. Declaring false so Phase 2B fixtures
skip honestly until that work lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ment

Mirrors the Go binary's enforceExtendedPolicy: a pure-Python helper
that parses the TD body fields out of the raw quote bytes and compares
each against any policy.expected_*_hex pin the fixture sets.

Mapping (each enforced only when policy field present):
  * td_attributes      → TD_ATTRIBUTES_MISMATCH       (§4.8.2)
  * xfam               → XFAM_MISMATCH                (§4.8.1)
  * mr_signer_seam     → MR_SIGNER_SEAM_MISMATCH      (§4.8.4)
  * seam_attributes    → SEAM_ATTRIBUTES_MISMATCH     (§4.8.3)
  * mr_seam (allowlist)→ MR_SEAM_NOT_ALLOWED          (§4.8.5)
  * mrtd               → MRTD_MISMATCH                (§4.10)
  * mr_config_id       → MR_CONFIG_ID_MISMATCH        (§4.8.6)
  * mr_owner           → MR_OWNER_MISMATCH            (§4.8.6)
  * mr_owner_config    → MR_OWNER_CONFIG_MISMATCH     (§4.8.6)
  * rtmr3              → RTMR3_NONZERO                (§4.10/§7.3.6)
  * report_data        → REPORT_DATA_MISMATCH         (§8.2)
  * qe_vendor_id       → QE_VENDOR_ID_MISMATCH        (§4.8.6)
  * min_tee_tcb_svn    → TEE_TCB_SVN_BELOW_MINIMUM    (§4.8.7)

Capability flipped to true:
  attestation_tdx.extended_td_checks_supported = true

13 of 13 Phase 4 fixtures pass on Python.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
You were right — the lib already had the complete collateral evaluation
API (verify_tcb_info_signature, verify_qe_identity_signature,
check_collateral_freshness, validate_certificate_revocation,
validate_tcb_status, validate_tdx_module_identity, validate_qe_identity).
The conformance binary now orchestrates them per SPEC §4.9 step 10 when
policy.tcb_evaluation_required=true, with fixture-injected collateral
bytes (no Intel PCS fetch) and fixture-controlled verification time.

  * _evaluate_collateral runs:
      1. extract_pck_extensions from PCK leaf
      2. parse_tcb_info_response + parse_qe_identity_response
      3. parse_pem_chain on injected issuer chains
      4. verify_tcb_info_signature + verify_qe_identity_signature
         (when issuer chains supplied)
      5. Load PCK CRL + Root CRL DER
      6. inline freshness using fixture's expiration_check_date
      7. validate_certificate_revocation
      8. validate_tcb_status
      9. validate_tdx_module_identity
     10. validate_qe_identity

  * _maybe_override_intel_root monkey-patches the embedded Intel SGX
    Root CA PEM for the duration of the call, enabling Phase 3
    synthetic-chain fixtures.

  * _maybe_override_time monkey-patches the module-level datetime in
    collateral_tdx + cert_utils so all internal freshness/validity
    checks use the fixture's expiration_check_date_unix. The lib has
    no public time-injection API; this is the cleanest workaround
    until check_collateral_freshness / verify_intel_chain /
    validate_certificate_revocation gain a `now` parameter.

  * Classifier maps CollateralError messages to SPEC rejection codes
    (TCB_REVOKED, TCB_INFO_SIGNATURE_INVALID, TCB_INFO_EXPIRED,
    QE_IDENTITY_*, PCK_REVOKED, etc.).

Capability flags flipped:
  attestation_tdx.tcb_evaluation_supported = true       (was false)
  attestation_tdx.accepts_non_terminal_tcb_statuses = true  (new)

After: 114/119 attestation-tdx fixtures pass on Python — including the
three Phase 3 non-terminal statuses (360-362) that Go can't accept
because go-tdx-guest's ErrTcbStatus collapses every non-UpToDate
status. Python's validate_tcb_status follows SPEC §4.7.7 properly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors Go's enforce_spec_defaults handling in _enforce_extended_policy:

  * TD Attributes DEBUG bit (§4.8.2) — must be 0
  * TD Attributes FIXED0 mask (§4.8.2) — only {0, 28, 30, 63} may be set
  * XFAM FIXED1 (§4.8.1) — bits 0 + 1 (FP + SSE) must be set
  * XFAM FIXED0 (§4.8.1) — only 0x0006DBE7 bits may be set

7 of 8 Phase 4B fixtures pass on Python (401-405, 412-413).

Also flipped verification_time_override from "system-clock-only" to
"supported" since the monkey-patched datetime in cmd_verify_attestation_
tdx propagates through cert_utils.verify_intel_chain to the structural
PCK chain validation too — fixture 324 (pck-leaf-expired) now passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmd_verify_attestation_sev builds CertificateChain directly with the
fixture-supplied VCEK + lib's embedded ARK_CERT/ASK_CERT, bypassing the
lib's CertificateChain.from_report which would fetch VCEK from AMD KDS
over the network. Fixture-supplied amd_root_ca_pem / ask_pem swap the
embedded constants for Phase 4B-SEV synthetic-chain fixtures.

Decodes the 1184-byte SEV-SNP v3 report into the cross-SDK body_fields
shape (policy bits per AMD APM Vol 3 Table B-3, platform_info bits,
current/committed/launch TCB parts, every measurement register).

SPEC §3.4 mandatory cross-checks (VCEK HWID ↔ report.chip_id, every SPL
extension ↔ report.reported_tcb part) live in _enforce_sev_vcek_cross_
checks since the conformance binary bypasses the lib's validate_report.
Unconditional MIGRATE_MA check rejects guest_policy bit 18 set.

The lib's verify_attestation / verify_chain print error diagnostics to
stdout when verification fails (the lib's contract). The conformance
binary's contract is JSON-only on stdout, so the lib's stdout is
captured into stderr; the captured message also feeds the error
classifier so e.g. "certificate signature failure" lands on
VCEK_CHAIN_INVALID rather than the generic fallback.

Capabilities: declare verification_time_override=system-clock-only
(pyOpenSSL's X509Store.verify_certificate delegates to OpenSSL's clock
with no Python-level override hook).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Chains verify-sigstore + verify-attestation-sev + a Measurement.assert_equal
cross-stage comparison, parallel to the Go/Rust conformance binaries.
Brings tinfoil-python to verify-full parity so the four-SDK matrix has
no n/a columns.

Refactors cmd_verify_sigstore and cmd_verify_attestation_sev to expose
inner functions (_run_verify_sigstore_inner, _run_verify_attestation_
sev_inner) returning either the successful result or a (code, spec_ref,
message) rejection triple. Outer cmd_* functions become thin emit
wrappers. The inner helpers default missing schema_version to "1" so
the verify-full envelope's nested sub-blocks (which omit it — the
envelope carries it) work.

cmd_verify_full handles SPEC §11.1 standard/bundle mode (sigstore →
MultiPlatform → SEV cross-check) and §11.3 pinned-measurement mode.
Rejections carry the originating sub-stage so fixtures can pin both
code AND stage. TDX path is not wired (Rust + Go are TDX-capable but
Python's SEV-focused fixtures already pass the n/a column).

Capabilities:
  + "verify-full" added to stages_supported
  + flow_modes_supported expanded to ["standard", "pinned"]

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lsd-cat lsd-cat force-pushed the feat/tinfoil-conformance branch from 0433927 to f198856 Compare June 15, 2026 02:20
lsd-cat and others added 2 commits June 15, 2026 15:29
EHBP transport is implemented in the SDK (default transport) and
cmd_verify_full already accepts the bundle flow mode (the SDK ships a
full bundle pipeline), but the conformance binary declared neither.
Declare ehbp transport and the bundle flow mode. No fixture gates on
transport or the bundle flow, so this is a library-capability
declaration matching tinfoil-go and tinfoil-js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the verify-ehbp-key-binding stage + ehbp.key_binding_supported
capability. The handler extracts the attested HPKE key from
report_data[32:64] using the SDK's own layout constants
(TLS_KEY_FP_SIZE/HPKE_KEY_SIZE) and validates the offered key with the
same parser the EHBP transport uses (ehbp.ServerIdentity.from_public_key_hex).
A mismatch fails closed with EHBP_KEY_BINDING_MISMATCH.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/tinfoil/conformance/cli.py">

<violation number="1" location="src/tinfoil/conformance/cli.py:2034">
P2: Missing top-level JSON object validation can crash the CLI on non-object input.</violation>

<violation number="2" location="src/tinfoil/conformance/cli.py:2040">
P2: Hex parsing error handling is incomplete; non-string report_data_hex bypasses the current exception handler.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic


try:
report_data = bytes.fromhex(inp.get("report_data_hex", ""))
except ValueError as e:

@cubic-dev-ai cubic-dev-ai Bot Jun 15, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Hex parsing error handling is incomplete; non-string report_data_hex bypasses the current exception handler.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 2040:

<comment>Hex parsing error handling is incomplete; non-string report_data_hex bypasses the current exception handler.</comment>

<file context>
@@ -2003,6 +2010,88 @@ def cmd_verify_full() -> int:
+
+    try:
+        report_data = bytes.fromhex(inp.get("report_data_hex", ""))
+    except ValueError as e:
+        sys.stderr.write(f"report_data_hex not valid hex: {e}\n")
+        return EXIT_BAD_INPUT
</file context>
Suggested change
except ValueError as e:
except (TypeError, ValueError) as e:
Fix with cubic

except json.JSONDecodeError as e:
sys.stderr.write(f"input is not valid JSON: {e}\n")
return EXIT_BAD_INPUT
if inp.get("schema_version") != "1":

@cubic-dev-ai cubic-dev-ai Bot Jun 15, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Missing top-level JSON object validation can crash the CLI on non-object input.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 2034:

<comment>Missing top-level JSON object validation can crash the CLI on non-object input.</comment>

<file context>
@@ -2003,6 +2010,88 @@ def cmd_verify_full() -> int:
+    except json.JSONDecodeError as e:
+        sys.stderr.write(f"input is not valid JSON: {e}\n")
+        return EXIT_BAD_INPUT
+    if inp.get("schema_version") != "1":
+        sys.stderr.write('input.schema_version != "1"\n')
+        return EXIT_BAD_INPUT
</file context>
Fix with cubic

lsd-cat and others added 3 commits June 15, 2026 21:13
Adds execution_mode=public_api to the SEV stage: drives the SDK's real
public verifier attestation.verify_sev_attestation_v2 with only the VCEK
injected (embedded ARK/ASK, no network) instead of the adapter's manual
chain assembly + verify_attestation path. Captures the lib's stdout
diagnostics (it prints on failure) to keep stdout JSON-only and reuses
_classify_sev_error / _decode_sev_body_fields. Declares
attestation_sev.public_api_hooks_supported. Time-override / injected-ARK /
policy-pin fixtures stay adapter-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The execution_mode=public_api SEV handler drove the real verifier but
skipped the fixture's §3.7 policy pins (measurement / host_data /
report_data / key-digest / TCB-minimum), so policy-mismatch fixtures
(400/410/420/430/440/450/451) wrongly ACCEPTED in full flow while the Go
public handler and the adapter path correctly rejected them. Apply
_enforce_sev_policy after the verifier accepts, matching the Go public
handler. Conformance-binary only; no SDK lib changes.

Verified: the policy-mismatch SEV fixtures now reject in full flow
(MEASUREMENT_MISMATCH, etc.); 366 unit tests still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Makes execution_mode=public_api actually exercise the SDK's real TDX
verifier for the deep fixtures, not just pre-policy ones. Three pieces,
all conformance-binary only (no lib changes):

  * Re-baseline the verifier config (expected_td_attributes/xfam/
    tee_tcb_svn/mr_seams + min_tcb_evaluation_data_number=0) to the
    synthetic quote's own values, so the lib's production-policy baseline
    doesn't reject the synthetic vectors before the targeted check; the
    fixture's §4.8 pins are still enforced by _enforce_extended_policy.
  * Classifier patterns for the lib's policy/collateral messages reached
    in the full path: TD_ATTRIBUTES (debug/reserved/mismatch), XFAM
    (required-clear/forbidden-set), QE-report mrsigner/field mismatch,
    PCK FMSPC mismatch, root-CRL signature.
  * §4.7.7 accepted_qv_results gate (the verifier accepts non-terminal
    TCB statuses by default; enforce the fixture's policy against the
    matched TCB level — fixes 365 wrongly accepting).

Result: 50/65 TDX fixtures verify correctly in full flow (was effectively
0 past the PCK chain). The remaining 15 (300/325 + the 4xx extended-TD
suite) are blocked by synthetic quote SVN ↔ TCB-info level inconsistency
that the real TCB-level match correctly rejects — needs fixturegen, not
config. No regression: full harness 206/0-fail, 366 unit tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/tinfoil/conformance/cli.py">

<violation number="1" location="src/tinfoil/conformance/cli.py:1340">
P2: Custom agent: **Nuke belt-and-suspenders**

Broad `except Exception: pass` swallows re-parsing errors for data already validated upstream, constituting unnecessary belt-and-suspenders defensive coding.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment on lines +1340 to +1347
try:
_tb = parse_tdx_quote(base64.b64decode(inp["quote_b64"])).td_quote_body
config_kwargs.setdefault("expected_td_attributes", _tb.td_attributes)
config_kwargs.setdefault("expected_xfam", _tb.xfam)
config_kwargs.setdefault("expected_minimum_tee_tcb_svn", _tb.tee_tcb_svn)
config_kwargs.setdefault("accepted_mr_seams", (_tb.mr_seam,))
except Exception:
pass

@cubic-dev-ai cubic-dev-ai Bot Jun 15, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: Nuke belt-and-suspenders

Broad except Exception: pass swallows re-parsing errors for data already validated upstream, constituting unnecessary belt-and-suspenders defensive coding.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tinfoil/conformance/cli.py, line 1340:

<comment>Broad `except Exception: pass` swallows re-parsing errors for data already validated upstream, constituting unnecessary belt-and-suspenders defensive coding.</comment>

<file context>
@@ -1291,6 +1327,24 @@ def _cmd_verify_attestation_tdx_public(inp: dict[str, Any], policy: dict[str, An
+    # synthetic vectors at the baseline gate before the fixture's targeted
+    # check. Re-baseline to the quote's own values; the fixture's SPEC §4.8
+    # pins are then enforced precisely by _enforce_extended_policy below.
+    try:
+        _tb = parse_tdx_quote(base64.b64decode(inp["quote_b64"])).td_quote_body
+        config_kwargs.setdefault("expected_td_attributes", _tb.td_attributes)
</file context>
Suggested change
try:
_tb = parse_tdx_quote(base64.b64decode(inp["quote_b64"])).td_quote_body
config_kwargs.setdefault("expected_td_attributes", _tb.td_attributes)
config_kwargs.setdefault("expected_xfam", _tb.xfam)
config_kwargs.setdefault("expected_minimum_tee_tcb_svn", _tb.tee_tcb_svn)
config_kwargs.setdefault("accepted_mr_seams", (_tb.mr_seam,))
except Exception:
pass
_tb = parse_tdx_quote(base64.b64decode(inp["quote_b64"])).td_quote_body
config_kwargs.setdefault("expected_td_attributes", _tb.td_attributes)
config_kwargs.setdefault("expected_xfam", _tb.xfam)
config_kwargs.setdefault("expected_minimum_tee_tcb_svn", _tb.tee_tcb_svn)
config_kwargs.setdefault("accepted_mr_seams", (_tb.mr_seam,))
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant