From e718c982147f83588be5aee801f2b9727c203418 Mon Sep 17 00:00:00 2001 From: Imran Siddique <45405841+imran-siddique@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:50:51 -0700 Subject: [PATCH 1/4] chore(tee): remove the opaque provider value The opaque provider had no collector and failed closed at startup. Drop it from VALID_PROVIDERS so a config naming it fails at load as an unknown provider, and remove it from docs, example configs and the issue template. Co-Authored-By: Claude Opus 5.5 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- CHANGELOG.md | 7 +++++++ SECURITY.md | 2 +- docs/configuration.md | 4 ++-- docs/spec/attestation.md | 1 - docs/spec/component-model.md | 2 +- examples/cross-operator-delegation/ca2a-config.yaml | 2 +- examples/minimal/ca2a-config.yaml | 2 +- src/ca2a_runtime/config.py | 2 +- tests/unit/test_bootstrap.py | 7 ++++++- 10 files changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e820ac6..a13ccd7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,7 +25,7 @@ assignees: '' - OS: - Python version: -- TEE provider (`auto` / `tpm` / `sev-snp` / `tdx` / `opaque` / `software-only`): +- TEE provider (`auto` / `tpm` / `sev-snp` / `tdx` / `software-only`): ## Security impact diff --git a/CHANGELOG.md b/CHANGELOG.md index 69cfaf1..c00dc72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Removed + +- The `opaque` attestation provider value. It had no collector and was never + selectable at runtime; a config naming `provider: opaque` now fails at load + with `unknown attestation provider`, like any other unknown provider. Docs, + example configs and the issue template no longer list it. + ## [0.4.0] - 2026-09-30 ### Security diff --git a/SECURITY.md b/SECURITY.md index 5e1cb46..8b6df9b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -21,7 +21,7 @@ The following components are in scope: - **Delegation chain**: scope attenuation correctness (a child grant must be a provable subset of its parent), signature verification, depth limits, and cross-chain replay protection - **Sealed peer channel**: any path by which a task payload could be read outside the peer's attested measurement -- **TEE attestation path**: peer measurement verification for TPM 2.0, AMD SEV-SNP, Intel TDX, and OPAQUE Managed Runtime providers, and binding of the channel key to that measurement +- **TEE attestation path**: peer measurement verification for TPM 2.0, AMD SEV-SNP, and Intel TDX providers, and binding of the channel key to that measurement - **Signing key handling**: hardware-sealed key generation, storage, and use - **Provenance record**: integrity of the delegation DAG; any path by which a valid TRACE record could be forged, reparented, or suppressed diff --git a/docs/configuration.md b/docs/configuration.md index 95de021..67397d4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -14,7 +14,7 @@ the reference HTTP transport. ```yaml attestation: - provider: auto # auto | tpm | sev-snp | tdx | opaque | software-only + provider: auto # auto | tpm | sev-snp | tdx | software-only enforcement_mode: enforcing # enforcing | advisory | silent require_caller_attestation: none # none | any | hardware # caller_verifier: # required for `hardware`, optional for `any` @@ -46,7 +46,7 @@ proof). | Field | Default | Description | |---|---|---| -| `attestation.provider` | `auto` | TEE provider for peer attestation. `auto` selects a detected hardware provider and fails if there is none; it never falls back to `software-only`, which has to be named explicitly. `opaque` is not implemented. | +| `attestation.provider` | `auto` | TEE provider for peer attestation. `auto` selects a detected hardware provider and fails if there is none; it never falls back to `software-only`, which has to be named explicitly. Any other value is rejected as an unknown provider. | | `attestation.enforcement_mode` | `enforcing` | Intended mode. The peer path always fails closed on cA2A denials today; advisory and silent are accepted in config but not applied on the wire. | | `attestation.require_caller_attestation` | `none` | What the callee demands of a caller's own attestation, per [mutual-attestation.md](spec/mutual-attestation.md). `none` records the outcome and demands nothing; `any` requires an offer that appraises, software assurance included; `hardware` requires hardware assurance. At every rung an offer that is present and does not appraise is refused. | | `attestation.caller_verifier` | none | How to appraise a hardware report a caller offers. `platform` plus `trusted_roots_path` (a PEM bundle, resolved relative to the config file). Required when the rung is `hardware`; without it, a hardware offer at `none` or `any` is refused as unappraisable rather than accepted. Only `tpm` can be built today, via `ca2a_verify.tpm.tpm_verifier`. `sev-snp` and `tdx` are accepted by the vocabulary and refused at startup with the reason: their verifiers take raw evidence and a certificate chain, and no report-level wrapper exists yet. | diff --git a/docs/spec/attestation.md b/docs/spec/attestation.md index 3b0ba54..ff3f7ab 100644 --- a/docs/spec/attestation.md +++ b/docs/spec/attestation.md @@ -63,7 +63,6 @@ All four are absent on `software-only`, which has no evidence by construction. A | `sev-snp` | AMD SEV-SNP | Verifier and collector both implemented (see below). `attest` produces a real report on a non-paravisor SNP guest through configfs-TSM. | | `tdx` | Intel TDX | Verifier and collector both implemented (see below). `attest` produces a real DCAP quote on a TDX guest through configfs-TSM. | | `tpm` | TPM 2.0 / vTPM | Verifier and collector both implemented (see below). `attest` produces a real quote on a Linux host with a TPM and tpm2-pytss. | -| `opaque` | OPAQUE Confidential Runtime | Tier 3, explicit opt-in, not auto-selected | ## SEV-SNP verification diff --git a/docs/spec/component-model.md b/docs/spec/component-model.md index 1dbde76..da92a7f 100644 --- a/docs/spec/component-model.md +++ b/docs/spec/component-model.md @@ -14,7 +14,7 @@ cA2A composes delegation verification, local policy, caller authentication, atte | Inbound handler | `ca2a_runtime.peer` | Verify, authenticate, appraise, authorize, and open the payload | | Channel | `ca2a_runtime.channel` | X25519 key agreement and authenticated encryption; reject wrong keys and altered ciphertext | | Attestation | `ca2a_runtime.attestation` | Challenge-bound channel offers and peer appraisal | -| Providers | `ca2a_runtime.tee` | Software evidence or hardware collection for TPM, SEV-SNP, and TDX; OPAQUE has no collector | +| Providers | `ca2a_runtime.tee` | Software evidence or hardware collection for TPM, SEV-SNP, and TDX | | Provenance | `ca2a_runtime.provenance` | Hash-linked decision records and consistency checks | | TRACE binding | `ca2a_runtime.trace_binding` | Signed TRACE records carrying delegation links | | Offline verification | `ca2a_verify.verify`, `ca2a_verify.dag` | Verify credentials and signed TRACE record paths against caller-supplied trust anchors | diff --git a/examples/cross-operator-delegation/ca2a-config.yaml b/examples/cross-operator-delegation/ca2a-config.yaml index 66c2ffa..9512200 100644 --- a/examples/cross-operator-delegation/ca2a-config.yaml +++ b/examples/cross-operator-delegation/ca2a-config.yaml @@ -5,7 +5,7 @@ # validates with `ca2a validate-config`. See README.md for what is real vs # software-asserted in this example. attestation: - provider: software-only # auto | tpm | sev-snp | tdx | opaque | software-only + provider: software-only # auto | tpm | sev-snp | tdx | software-only enforcement_mode: advisory # enforcing | advisory | silent max_delegation_depth: 8 diff --git a/examples/minimal/ca2a-config.yaml b/examples/minimal/ca2a-config.yaml index c5a0e54..98602d3 100644 --- a/examples/minimal/ca2a-config.yaml +++ b/examples/minimal/ca2a-config.yaml @@ -1,5 +1,5 @@ attestation: - provider: software-only # auto | tpm | sev-snp | tdx | opaque | software-only + provider: software-only # auto | tpm | sev-snp | tdx | software-only enforcement_mode: enforcing # the peer path always fails closed today require_caller_attestation: none # none | any | hardware; see docs/configuration.md # caller_verifier: # needed for `hardware`; TPM roots only today diff --git a/src/ca2a_runtime/config.py b/src/ca2a_runtime/config.py index adb2365..ccbfe2a 100644 --- a/src/ca2a_runtime/config.py +++ b/src/ca2a_runtime/config.py @@ -16,7 +16,7 @@ from ca2a_runtime.errors import ConfigError from ca2a_runtime.peer import REQUIRE_HARDWARE, REQUIRE_NONE, REQUIREMENT_VALUES -VALID_PROVIDERS = frozenset({"auto", "tpm", "sev-snp", "tdx", "opaque", "software-only"}) +VALID_PROVIDERS = frozenset({"auto", "tpm", "sev-snp", "tdx", "software-only"}) VALID_ENFORCEMENT = frozenset({"enforcing", "advisory", "silent"}) # Platforms a config can name under ``attestation.caller_verifier.platform``. This diff --git a/tests/unit/test_bootstrap.py b/tests/unit/test_bootstrap.py index 76d0411..1275861 100644 --- a/tests/unit/test_bootstrap.py +++ b/tests/unit/test_bootstrap.py @@ -90,7 +90,12 @@ def test_auto_selects_a_detected_hardware_provider(monkeypatch: pytest.MonkeyPat def test_unimplemented_provider_rejected() -> None: with pytest.raises(ConfigError, match="not implemented"): - select_provider(Ca2aConfig(provider="opaque")) + select_provider(Ca2aConfig(provider="example-unknown")) + + +def test_opaque_provider_rejected_as_unknown() -> None: + with pytest.raises(ConfigError, match="unknown attestation provider"): + Ca2aConfig.from_dict({"attestation": {"provider": "opaque"}}) def test_no_caller_verifier_unless_configured() -> None: From ac1e5ef28e8b608a2364dd196fbb63400562ba29 Mon Sep 17 00:00:00 2001 From: Imran Siddique <45405841+imran-siddique@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:50:52 -0700 Subject: [PATCH 2/4] docs: describe the GCP capture project generically Co-Authored-By: Claude Opus 5.5 --- docs/hardware-validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hardware-validation.md b/docs/hardware-validation.md index 9731eac..cb95bfd 100644 --- a/docs/hardware-validation.md +++ b/docs/hardware-validation.md @@ -202,7 +202,7 @@ Trusted Launch VMs actually present, not a discrete TPM chip. ## Collection on real silicon: SEV-SNP and TDX, GCP, 2026-08-24 -Both collectors were run on genuine confidential VMs in GCP `opaque-dev`, +Both collectors were run on genuine confidential VMs in a GCP project in `us-central1-a`, and the evidence each produced was appraised by this codebase's own verifiers to the vendor roots. The VMs were ephemeral and deleted after the capture. From 554d8839eef07a8f25d48bf98cedf9c86cfca864 Mon Sep 17 00:00:00 2001 From: Imran Siddique <45405841+imran-siddique@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:50:53 -0700 Subject: [PATCH 3/4] docs(policy): point the module docstring at CedarPolicy The docstring said the Cedar binding was tracked separately, but ca2a_runtime.cedar ships it. Co-Authored-By: Claude Opus 5.5 --- src/ca2a_runtime/policy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ca2a_runtime/policy.py b/src/ca2a_runtime/policy.py index 7026d45..3970e85 100644 --- a/src/ca2a_runtime/policy.py +++ b/src/ca2a_runtime/policy.py @@ -5,9 +5,9 @@ intersected with this allow set, so a peer can never exercise more than both its grant and the callee's local policy permit. -The intersection semantics here are policy-language-agnostic. Binding a full -Cedar policy engine (as cMCP does) is tracked separately; this allow-set model -is the enforcement primitive the peer path uses today. +The intersection semantics here are policy-language-agnostic. ``LocalPolicy`` +below is the allow-set model; ``ca2a_runtime.cedar.CedarPolicy`` evaluates a +Cedar policy bundle behind the same ``Policy`` protocol. """ from __future__ import annotations From 57b241ba01060b3fd0e7332bc8094a11e54f46ca Mon Sep 17 00:00:00 2001 From: Imran Siddique <45405841+imran-siddique@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:50:54 -0700 Subject: [PATCH 4/4] test: use vendor-neutral model fields in TRACE examples Co-Authored-By: Claude Opus 5.5 --- examples/trace-dag/demo.py | 4 ++-- tests/unit/test_trace_binding.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/trace-dag/demo.py b/examples/trace-dag/demo.py index 555ea92..b730593 100644 --- a/examples/trace-dag/demo.py +++ b/examples/trace-dag/demo.py @@ -73,8 +73,8 @@ def main() -> None: subject=SUBJECTS[i], signing_key=keys[i], context=HopContext.software( - model_provider="anthropic", - model_id="claude-opus-4-8", + model_provider="example-provider", + model_id="example-model", image_label=f"ca2a-peer:{SUBJECTS[i].rsplit('/', 1)[-1]}", policy_bundle_hash=digest(b"demo-policy-bundle"), ), diff --git a/tests/unit/test_trace_binding.py b/tests/unit/test_trace_binding.py index d504d66..873e1aa 100644 --- a/tests/unit/test_trace_binding.py +++ b/tests/unit/test_trace_binding.py @@ -41,8 +41,8 @@ def _software_context(label: str = "ca2a-peer") -> HopContext: return HopContext.software( - model_provider="anthropic", - model_id="claude-opus-4-8", + model_provider="example-provider", + model_id="example-model", image_label=label, policy_bundle_hash=digest(b"policy-bundle"), )