diff --git a/examples/rejection-with-proof/chain.json b/examples/rejection-with-proof/chain.json index 20a64dd..4993281 100644 --- a/examples/rejection-with-proof/chain.json +++ b/examples/rejection-with-proof/chain.json @@ -2,8 +2,8 @@ "chain": [ { "credential_id": "cred-0-orchestrator", - "issuer": "4b6b80f73e41b5401523db167775027ee5d41a1d83954557c905b00962cd5c8e", - "subject": "f5d7ff727931d0acb4393c9012d9a4fe9c3a0837a1c191a31e5ad1c35fa2dd07", + "issuer": "5d950cd9fea67de730ac35d540c48d62a22dada5618db0c8474b5eae5562a7cf", + "subject": "2f35acc0747fb45e739288d02bbd18e54e5283f770e30391b1c272fc1285e3bf", "scope": [ "task:read", "task:write", @@ -12,12 +12,12 @@ ], "depth": 0, "parent_id": null, - "signature": "749c4a53497b1bb6ca17f4aafe6bad30674484baa4f8f3083581cf9bbbb950d5cb9e3a2ab0453b7cb9c24b339496f4eaa6a6d989960d356633aeb53c58251007" + "signature": "21056267f494e8ac773bf9aa76b23815baded9b17605c265296f719241050a76b991cbd0162385359302607f19483d81cacc971f8d3cdbf1bc3d8c4cca778d0e" }, { "credential_id": "cred-1-researcher", - "issuer": "f5d7ff727931d0acb4393c9012d9a4fe9c3a0837a1c191a31e5ad1c35fa2dd07", - "subject": "747e897486d4d32abd45d3a9e04e5357f6a88a1bad8d0ce9dc6084cd10c7b121", + "issuer": "2f35acc0747fb45e739288d02bbd18e54e5283f770e30391b1c272fc1285e3bf", + "subject": "b12a13df30c7123fe6ab8e03551d7c7465cb1acfa04701e0efb05eeba9a2b7d6", "scope": [ "task:read", "tool:purchase", @@ -25,18 +25,18 @@ ], "depth": 1, "parent_id": "cred-0-orchestrator", - "signature": "0a5d95fded81c149bc652bfa5903c7a02e3caa9e0f5a41761ffef19d2dad9fd3e4ac5e1f2ef921bc92a6ed146aad0b3765709491447e7f87d9331df502e3af02" + "signature": "308b8c1cab0b49bb1993f185b67515adf832bf50a8cc9ddbf7e9e4d785cac9f3ab20a73be8cef332e28b409fc67a904eef4955ac0b1fcded39734303f0e0bf01" }, { "credential_id": "cred-2-retriever", - "issuer": "747e897486d4d32abd45d3a9e04e5357f6a88a1bad8d0ce9dc6084cd10c7b121", - "subject": "b05e7f85927cc4a5886ec097da0200c4a02d0d944dd2d73c9a10f5662041074c", + "issuer": "b12a13df30c7123fe6ab8e03551d7c7465cb1acfa04701e0efb05eeba9a2b7d6", + "subject": "2da412a4c5cd6d9f59cf6bf84fc8d78cb9ba3352c515f85031592cd6a59fb9a2", "scope": [ "tool:search" ], "depth": 2, "parent_id": "cred-1-researcher", - "signature": "a6ec5c6c8add5923b9bf3f9fc9cb8cd0c10cf6faf53a3e04571a418f199d8191bbf9ee4f0b7ddd06f75eaeaa4e25e0a761e62ca59b09d31d6544b4bc90ce1e0d" + "signature": "6307eb2fb085b2dcca5d5cbdc21a6762c18019db5e8c29714bc7e99837d696d7f18ddbbe60ebfb3c6815f4a44e61020f71a4b30da5d387a3b9afc22ef1b49204" } ] } diff --git a/examples/rejection-with-proof/dag.json b/examples/rejection-with-proof/dag.json index 5af7884..594c7b8 100644 --- a/examples/rejection-with-proof/dag.json +++ b/examples/rejection-with-proof/dag.json @@ -3,7 +3,7 @@ { "record_id": "rec-0-orchestrator", "credential_id": "cred-0-orchestrator", - "subject": "f5d7ff727931d0acb4393c9012d9a4fe9c3a0837a1c191a31e5ad1c35fa2dd07", + "subject": "2f35acc0747fb45e739288d02bbd18e54e5283f770e30391b1c272fc1285e3bf", "scope": [ "task:read", "task:write", @@ -15,31 +15,31 @@ { "record_id": "rec-1-researcher", "credential_id": "cred-1-researcher", - "subject": "747e897486d4d32abd45d3a9e04e5357f6a88a1bad8d0ce9dc6084cd10c7b121", + "subject": "b12a13df30c7123fe6ab8e03551d7c7465cb1acfa04701e0efb05eeba9a2b7d6", "scope": [ "task:read", "tool:purchase", "tool:search" ], - "parent_record_hash": "f8fd5e5073d51406bdc9fe7e79b90cd3c70afbee1ac47d5763996d4af20af5a7" + "parent_record_hash": "3655a4752c7d0841cf4cc2adb80523ea988469a548d4b05636707132ab25c1b1" }, { "record_id": "rec-2-retriever", "credential_id": "cred-2-retriever", - "subject": "b05e7f85927cc4a5886ec097da0200c4a02d0d944dd2d73c9a10f5662041074c", + "subject": "2da412a4c5cd6d9f59cf6bf84fc8d78cb9ba3352c515f85031592cd6a59fb9a2", "scope": [ "tool:search" ], - "parent_record_hash": "f940a2e460960035bbe39f93b7c76ac965d914857e10bce66bffce871fbc102e" + "parent_record_hash": "1b5cc71b77a37400961d71ccc75c4292f5ed122bd77ef854137dda059540faf9" }, { "record_id": "rec-denied-purchase", "credential_id": "cred-2-retriever", - "subject": "b05e7f85927cc4a5886ec097da0200c4a02d0d944dd2d73c9a10f5662041074c", + "subject": "2da412a4c5cd6d9f59cf6bf84fc8d78cb9ba3352c515f85031592cd6a59fb9a2", "scope": [ "tool:search" ], - "parent_record_hash": "3867abdd0574dce6cd20cae0b1e4a22afa5ab0a22778cda449c82c12dca4f127", + "parent_record_hash": "169191a74d471318788dab7619b3914121763baa8a34b2f4465b7e16ba10398e", "decision": "deny", "requested_capability": "tool:purchase", "effective_scope": [ diff --git a/pyproject.toml b/pyproject.toml index dc7927a..a6f9c0d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "agentrust-trace>=0.5", # Shared hardware-attestation verification (generic cert-chain verifier, # SNP/TDX/TPM primitives) consumed via PyPI instead of duplicated per repo. - "agent-manifest>=0.8", + "agent-manifest>=0.10", "rfc8785>=0.1", ] diff --git a/src/ca2a_runtime/tee/sev_snp.py b/src/ca2a_runtime/tee/sev_snp.py index 6d96696..4cd0049 100644 --- a/src/ca2a_runtime/tee/sev_snp.py +++ b/src/ca2a_runtime/tee/sev_snp.py @@ -1,10 +1,15 @@ """AMD SEV-SNP attestation report parsing and the SEV-SNP provider. -This module parses the SEV-SNP ``ATTESTATION_REPORT`` structure (1184 bytes, -AMD SEV-SNP ABI) and exposes the fields the cA2A verifier appraises: the launch -measurement, the report data (which binds the runtime key and nonce), and the -ECDSA-P384 signature over the report body. The verification and certificate -chain appraisal live in :mod:`ca2a_verify.sev_snp`. +This module exposes the SEV-SNP ``ATTESTATION_REPORT`` fields the cA2A verifier +appraises: the launch measurement, the report data (which binds the runtime key +and nonce), and the ECDSA-P384 signature over the report body. The verification +and certificate chain appraisal live in :mod:`ca2a_verify.sev_snp`. + +The layout itself is **not** defined here. Offsets and parsing come from +``agent_manifest`` (>=0.10), which cmcp also consumes; between them the org +carried four mirrors of one ABI table, two inside cmcp alone. :class:`SevSnpReport` +stays as cA2A's surface, keeping its error contract and the ``signature_rs`` +helper, but the bytes are read by the shared parser. Producing a report requires a real SEV-SNP guest (``/dev/sev-guest``), so :meth:`SevSnpProvider.attest` fails closed off hardware. The verifier does not @@ -14,18 +19,20 @@ from __future__ import annotations -import struct from dataclasses import dataclass +from agent_manifest import SNP_OFFSETS, SNP_REPORT_LEN, SnpVerificationError, parse_snp_report + from ca2a_runtime.errors import AttestationFailed, AttestationUnsupported from ca2a_runtime.tee.base import AttestationReport, BaseProvider -# Layout of the SEV-SNP ATTESTATION_REPORT (offsets in bytes). -REPORT_SIZE = 0x4A0 # 1184 -SIG_OFFSET = 0x2A0 # signature covers report[:SIG_OFFSET] -REPORT_DATA_OFFSET = 0x50 +# Layout of the SEV-SNP ATTESTATION_REPORT, re-exported from agent-manifest's +# shared ABI table so cA2A and cmcp cannot disagree about where a field sits. +REPORT_SIZE = SNP_REPORT_LEN +SIG_OFFSET = SNP_OFFSETS["signature"] # signature covers report[:SIG_OFFSET] +REPORT_DATA_OFFSET = SNP_OFFSETS["report_data"] REPORT_DATA_LEN = 64 -MEASUREMENT_OFFSET = 0x90 +MEASUREMENT_OFFSET = SNP_OFFSETS["measurement"] MEASUREMENT_LEN = 48 # ECDSA-P384 signature: r then s, each in a 72-byte little-endian field. SIG_COMPONENT_LEN = 72 @@ -64,26 +71,24 @@ def signature_rs(self) -> tuple[int, int]: @classmethod def parse(cls, blob: bytes) -> SevSnpReport: - """Parse a raw report, raising AttestationFailed on any malformed input.""" - if len(blob) < REPORT_SIZE: - raise AttestationFailed( - "SEV-SNP report too short", - detail=f"got {len(blob)} bytes, need at least {REPORT_SIZE}", - ) - version, guest_svn, policy = struct.unpack_from("