Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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. |
Expand Down
2 changes: 1 addition & 1 deletion docs/hardware-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 0 additions & 1 deletion docs/spec/attestation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/spec/component-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion examples/cross-operator-delegation/ca2a-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/minimal/ca2a-config.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions examples/trace-dag/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
),
Expand Down
2 changes: 1 addition & 1 deletion src/ca2a_runtime/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/ca2a_runtime/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/test_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ def test_auto_selects_a_detected_hardware_provider(monkeypatch: pytest.MonkeyPat

def test_unimplemented_provider_rejected() -> None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we still need this? Previously opaque was not implemented, then this makes sense.

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:
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_trace_binding.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
)
Expand Down
Loading