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/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. 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/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/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/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 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: 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"), )