diff --git a/docs/rfcs/runtime-evidence-profile.md b/docs/rfcs/runtime-evidence-profile.md index 67e75004..e829cdc0 100644 --- a/docs/rfcs/runtime-evidence-profile.md +++ b/docs/rfcs/runtime-evidence-profile.md @@ -3,7 +3,7 @@ **Status:** Draft proposal. Binds nothing. **Scope:** A `runtime.evidence` member, the rules for checking it, and the grades a verifier may report. Additive; every v0.2 record stays valid. **Target:** `spec/trace-v0.2.md` §3.1 and §5, for v0.3. -**Conformance material:** [`examples/runtime-evidence/`](https://github.com/agentrust-io/trace-spec/tree/main/examples/runtime-evidence): 13 vectors, generator, and reference rules, built on a genuine Intel TDX quote rather than a minted one. +**Conformance material:** [`examples/runtime-evidence/`](https://github.com/agentrust-io/trace-spec/tree/main/examples/runtime-evidence): 14 vectors, generator, and reference rules, built on a genuine Intel TDX quote rather than a minted one. **Draft schema:** [`schema/trace-claim-v0.3-draft.json`](../../schema/trace-claim-v0.3-draft.json), generated from `schema/trace-claim.json` with two deliberate boundaries: the v0.3 profile URI and the new `runtime.evidence` member. Requirement keywords are lowercase throughout, deliberately, on the line `CONTRIBUTING.md` draws: normative text lives in `spec/`, informative text binds no implementation. If these rules are adopted they become uppercase there and this file becomes a pointer to where they went. A proposal that writes itself in the imperative is a specification nobody agreed to. @@ -197,13 +197,14 @@ reasons. | `reject-platform-not-the-evidence` | reject, the platform is not what this evidence roots | not graded | | `advisory-binds-cannot-raise-a-claim` | `platform-attested` | self-reported | | `commitment-cannot-attest-model` | `platform-attested` | self-reported | +| `context-embedded-key-not-trusted` | `platform-attested` | self-reported; signer trust `not-established` | -The run closes with `13/13 vectors behaved as the profile says they must (1 of them documenting a limit +The run closes with `14/14 vectors behaved as the profile says they must (1 of them documenting a limit of the rules rather than a success).` Each vector asserts both the record grade and the model-claim grade, because §6.1 is a claim about the relationship between the two and a corpus that checked only the first would not test it. -The dedicated `runtime-evidence` CI job runs these rules with the external verifier pinned to `agent-manifest` commit `934809709a2815695d65cfacb45dc0a164286046`. It checks the committed grades and the specific reason for each rejection, then regenerates all 13 vectors and compares their bytes. Missing verifier code or missing captures fail that job rather than skipping it. The ordinary TRACE suite checks the schema, the record signatures and the evidence shapes independently, and does not depend on `agent-manifest`. +The dedicated `runtime-evidence` CI job runs these rules with the external verifier pinned to `agent-manifest` commit `934809709a2815695d65cfacb45dc0a164286046`. It checks the committed grades and the specific reason for each rejection, then regenerates all 14 vectors and compares their bytes. Missing verifier code or missing captures fail that job rather than skipping it. The ordinary TRACE suite checks the schema, the record signatures and the evidence shapes independently, and does not depend on `agent-manifest`. ### 7.1 What the corpus found diff --git a/examples/runtime-evidence/README.md b/examples/runtime-evidence/README.md index 9905575b..aa61b417 100644 --- a/examples/runtime-evidence/README.md +++ b/examples/runtime-evidence/README.md @@ -73,6 +73,7 @@ is what a skipped test would have looked like: coverage that is none. | `reject-platform-not-the-evidence` | reject | `amd-sev-snp` claimed over a TDX quote | | `advisory-binds-cannot-raise-a-claim` | `platform-attested` | declares a binding it does not have; model claim stays self-reported | | `commitment-cannot-attest-model` | `platform-attested` | a recomputable `REPORT_DATA` match is a commitment, not model evidence | +| `context-embedded-key-not-trusted` | `platform-attested` | runtime evidence can be valid while external signer trust remains `not-established` | Each vector asserts the record grade **and** the model-claim grade. The profile's §6.1 is a claim about how those two relate, so a corpus checking only the first would not diff --git a/examples/runtime-evidence/generate.py b/examples/runtime-evidence/generate.py index 5eb514da..84216a42 100644 --- a/examples/runtime-evidence/generate.py +++ b/examples/runtime-evidence/generate.py @@ -274,8 +274,8 @@ def base_record(quote: bytes, key: Ed25519PrivateKey) -> dict: ) -def build_corpus() -> list[tuple[str, str, str | None, dict]]: - """Return (name, expected grade, expected model claim or None, record).""" +def build_corpus() -> list[dict]: + """Return data-driven vector objects, including verifier context when needed.""" key = Ed25519PrivateKey.from_private_bytes(PUBLISHED_TEST_KEY) quote_a = (HARDWARE / "tdx_quote.bin").read_bytes() quote_b = (HARDWARE / "tdx_quote_manifest.bin").read_bytes() @@ -438,7 +438,41 @@ def build_corpus() -> list[tuple[str, str, str | None, dict]]: ) ) - return vectors + # Normalize the legacy tuple construction above into one data model. Context and + # extra expectations belong to the vector, not to filename-specific generator code. + corpus = [ + { + "name": name, + "expected": {"grade": grade, "model_claim": model_claim}, + "record": record, + } + for name, grade, model_claim, record in vectors + ] + + # A valid embedded signing key proves internal signature consistency, not relying- + # party trust. Give the verifier a real but different external trust root so this + # case is discriminating: it would fail if an implementation silently promoted the + # embedded key into the configured trust set. + trusted_context_key = Ed25519PrivateKey.from_private_bytes( + bytes.fromhex("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb") + ) + trusted_context_record = base_record(quote_a, trusted_context_key) + corpus.append( + { + "name": "context-embedded-key-not-trusted", + "expected": { + "grade": "platform-attested", + "model_claim": "model claim: self-reported", + "signer_trust": "not-established", + }, + "context": { + "trusted_root_keys": [trusted_context_record["cnf"]["jwk"]], + }, + "record": copy.deepcopy(accept), + } + ) + + return corpus def main() -> int: @@ -447,7 +481,11 @@ def main() -> int: args = ap.parse_args() rows: list[tuple[str, str, str, bool, str]] = [] - for name, expected, expected_claim, record in build_corpus(): + for vector in build_corpus(): + name = vector["name"] + expected = vector["expected"]["grade"] + expected_claim = vector["expected"]["model_claim"] + record = vector["record"] try: grade = appraise(record) except Reject as e: @@ -464,15 +502,13 @@ def main() -> int: # without re-deriving it. `record` stays a clean TRACE record, because a # vector carrying an extra top-level member would fail the very schema the # corpus exists to exercise. + wrapper = { + key: value + for key, value in vector.items() + if key != "name" + } (out / f"{name}.json").write_text( - json.dumps( - { - "expected": {"grade": expected, "model_claim": expected_claim}, - "record": record, - }, - indent=2, - ) - + "\n", + json.dumps(wrapper, indent=2) + "\n", encoding="utf-8", ) diff --git a/examples/runtime-evidence/test_appraisal.py b/examples/runtime-evidence/test_appraisal.py index 0269286f..01a24e4e 100644 --- a/examples/runtime-evidence/test_appraisal.py +++ b/examples/runtime-evidence/test_appraisal.py @@ -14,6 +14,7 @@ import sys import pytest +from cryptography.exceptions import InvalidSignature from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey import generate as rules @@ -93,3 +94,34 @@ def test_regeneration_matches_committed_vectors(tmp_path: Path) -> None: assert {path.name for path in generated} == {path.name for path in VECTORS} for path in generated: assert path.read_bytes() == (VECTOR_DIR / path.name).read_bytes(), path.name + + +def _jwk_identity(jwk: dict) -> tuple[object, object, object]: + return (jwk.get("kty"), jwk.get("crv"), jwk.get("x")) + + +def test_embedded_signer_is_not_established_by_external_context() -> None: + vector = json.loads( + (VECTOR_DIR / "context-embedded-key-not-trusted.json").read_text(encoding="utf-8") + ) + record = vector["record"] + rules.check_envelope(record) + assert rules.appraise(record) == "platform-attested" + + external_keys = vector["context"]["trusted_root_keys"] + assert external_keys, "the trust context must be non-empty or this case is vacuous" + + embedded = _jwk_identity(record["cnf"]["jwk"]) + configured = {_jwk_identity(jwk) for jwk in external_keys} + assert embedded not in configured + + # The distinction is cryptographic, not just metadata: the record verifies under + # its embedded key, while the relying party's configured trusted key does not + # authenticate this signature. + signature = rules.unb64u(record["signature"]) + body = rules._canonical_bytes({k: v for k, v in record.items() if k != "signature"}) + for trusted_jwk in external_keys: + with pytest.raises(InvalidSignature): + rules._pubkey_from_jwk(trusted_jwk).verify(signature, body) + + assert vector["expected"]["signer_trust"] == "not-established" diff --git a/examples/runtime-evidence/vectors/context-embedded-key-not-trusted.json b/examples/runtime-evidence/vectors/context-embedded-key-not-trusted.json new file mode 100644 index 00000000..3c0071e4 --- /dev/null +++ b/examples/runtime-evidence/vectors/context-embedded-key-not-trusted.json @@ -0,0 +1,60 @@ +{ + "expected": { + "grade": "platform-attested", + "model_claim": "model claim: self-reported", + "signer_trust": "not-established" + }, + "context": { + "trusted_root_keys": [ + { + "kty": "OKP", + "crv": "Ed25519", + "x": "PUAXw-hDiVqStwqnTRt-vJyYLM8uxJaMwM1V8Sr0Zgw" + } + ] + }, + "record": { + "eat_profile": "tag:agentrust-io.com,2026:trace-v0.3", + "iat": 1753056000, + "subject": "spiffe://trust.example.org/agent/evidence-demo/prod", + "model": { + "provider": "meta", + "model_id": "llama-3.3-70b-instruct", + "version": "3.3", + "weights_digest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" + }, + "runtime": { + "platform": "intel-tdx", + "measurement": "sha384:9bf86e6280ec4282b8b5822d8166410a456cdb720109aa799f0011fa63df1de3ee5e35e293fc410c061433163acb03a6", + "firmware_version": "gcp-c3", + "evidence": { + "format": "tdx-quote-v4", + "quote": "BAACAIEAAAAAAAAAk5pyM_ecTKmUCg2zlX8GBwAAAAAAAAAAAAAAAAAAAAAAAAAADQEIAAAAAAAAAAAAAAAAAEieWF8cVLxaAgZsjG7CFhn_AzTsbyHgfio1ICxZGDeJyAV-fZfdWRuwgxSxhYGecgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAADnAAYAAAAAAJv4bmKA7EKCuLWCLYFmQQpFbNtyAQmqeZ8AEfpj3x3j7l414pP8QQwGFDMWOssDpgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH5JvF-3c_S_3KtfwVzDVMB83k_sVK9BOdb6QW5SZ2QksFxRKhxmlC9_DSkH_8QlBscWUt7JtetgosX6ys5IAkhBkiVAT84XC_tCTBY3R13CT4981bGYVQmnSQykOoViF9rNaWMCHHev1RDIf9-s7AzpKg7gZ6ykqVXd40XVyh1uPs7tfl4Gs6OFKvQX99Op6gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ68--Wwck89WDZaVEOJNYmBARf0V1OXaMmRZiT5YPauAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLEAAAcxn9MBPEr0xta9iQXW738RHQKuD6VyQ8QKaf5dr2uRFdrniFG5mFsooXiZxXfDnUM5o9HhyiumUtkPjUcV-ZKPq29ce6GzAYgdzdO67BV5sT8P44ht2hoCMNYG1nh9z5Mcu7iEjtIdvv5Y6JaQZJdGolB2kEh9h2cmzB5KdV0psGAEUQAAAICP8bBP8ABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAAAAAAOcAAAAAAAAA5aOntdgwwpU7mFNMbFmjo0_cNOkz9_WJjwqFzwiEa8oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANyeKnxvlI8XR040p_xD7QMPfBVj8bq932NAyC4OVKjFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKbxKa-KYpGj60DeYgPPGZpKP_x-MXGSlx0uoJ0U-mlWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi6vObCGoqyJOZJEwlR0P7EoEFpGyi5U6beO5LjGxfBC7VfrrkRtLF1VrkZGClxPwdYcbwqS6KPNGSKGUvV0ErIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAXQ4AAC0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlFOERDQ0JKYWdBd0lCQWdJVUs1YVpuNGR3NEJRYmN4czF2dTVpL1VKdUZFOHdDZ1lJS29aSXpqMEVBd0l3CmNERWlNQ0FHQTFVRUF3d1pTVzUwWld3Z1UwZFlJRkJEU3lCUWJHRjBabTl5YlNCRFFURWFNQmdHQTFVRUNnd1IKU1c1MFpXd2dRMjl5Y0c5eVlYUnBiMjR4RkRBU0JnTlZCQWNNQzFOaGJuUmhJRU5zWVhKaE1Rc3dDUVlEVlFRSQpEQUpEUVRFTE1Ba0dBMVVFQmhNQ1ZWTXdIaGNOTWpZd05USTNNakl4TXpVNVdoY05Nek13TlRJM01qSXhNelU1CldqQndNU0l3SUFZRFZRUUREQmxKYm5SbGJDQlRSMWdnVUVOTElFTmxjblJwWm1sallYUmxNUm93R0FZRFZRUUsKREJGSmJuUmxiQ0JEYjNKd2IzSmhkR2x2YmpFVU1CSUdBMVVFQnd3TFUyRnVkR0VnUTJ4aGNtRXhDekFKQmdOVgpCQWdNQWtOQk1Rc3dDUVlEVlFRR0V3SlZVekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRDVqClNLMlZjZmZObXJSMnlaMDN5c3RvcjdUdVhOY0VTMjNlSDY3ZzlrOXRCOE4zVXFIVzhTS0dMVllUNHhCTXNXN2YKMmNPaW4waExLQjN4aUtmMVA0ZWpnZ01NTUlJRENEQWZCZ05WSFNNRUdEQVdnQlNWYjEzTnZSdmg2VUJKeWRUMApNODRCVnd2ZVZEQnJCZ05WSFI4RVpEQmlNR0NnWHFCY2hscG9kSFJ3Y3pvdkwyRndhUzUwY25WemRHVmtjMlZ5CmRtbGpaWE11YVc1MFpXd3VZMjl0TDNObmVDOWpaWEowYVdacFkyRjBhVzl1TDNZMEwzQmphMk55YkQ5allUMXcKYkdGMFptOXliU1psYm1OdlpHbHVaejFrWlhJd0hRWURWUjBPQkJZRUZKcE16L3NsbTRMOGxrYkpsNjZUS1JkSAorSmdJTUE0R0ExVWREd0VCL3dRRUF3SUd3REFNQmdOVkhSTUJBZjhFQWpBQU1JSUNPUVlKS29aSWh2aE5BUTBCCkJJSUNLakNDQWlZd0hnWUtLb1pJaHZoTkFRMEJBUVFRQjBtL2tNN3FodmVSZlUxSVExWUlIRENDQVdNR0NpcUcKU0liNFRRRU5BUUl3Z2dGVE1CQUdDeXFHU0liNFRRRU5BUUlCQWdFSU1CQUdDeXFHU0liNFRRRU5BUUlDQWdFSQpNQkFHQ3lxR1NJYjRUUUVOQVFJREFnRUNNQkFHQ3lxR1NJYjRUUUVOQVFJRUFnRUNNQkFHQ3lxR1NJYjRUUUVOCkFRSUZBZ0VFTUJBR0N5cUdTSWI0VFFFTkFRSUdBZ0VCTUJBR0N5cUdTSWI0VFFFTkFRSUhBZ0VBTUJBR0N5cUcKU0liNFRRRU5BUUlJQWdFR01CQUdDeXFHU0liNFRRRU5BUUlKQWdFQU1CQUdDeXFHU0liNFRRRU5BUUlLQWdFQQpNQkFHQ3lxR1NJYjRUUUVOQVFJTEFnRUFNQkFHQ3lxR1NJYjRUUUVOQVFJTUFnRUFNQkFHQ3lxR1NJYjRUUUVOCkFRSU5BZ0VBTUJBR0N5cUdTSWI0VFFFTkFRSU9BZ0VBTUJBR0N5cUdTSWI0VFFFTkFRSVBBZ0VBTUJBR0N5cUcKU0liNFRRRU5BUUlRQWdFQU1CQUdDeXFHU0liNFRRRU5BUUlSQWdFTE1COEdDeXFHU0liNFRRRU5BUUlTQkJBSQpDQUlDQkFFQUJnQUFBQUFBQUFBQU1CQUdDaXFHU0liNFRRRU5BUU1FQWdBQU1CUUdDaXFHU0liNFRRRU5BUVFFCkJnQ0Fid1VBQURBUEJnb3Foa2lHK0UwQkRRRUZDZ0VCTUI0R0NpcUdTSWI0VFFFTkFRWUVFSzlyYW5vVmhRMVQKWDRpTGhJSHZOSEl3UkFZS0tvWklodmhOQVEwQkJ6QTJNQkFHQ3lxR1NJYjRUUUVOQVFjQkFRSC9NQkFHQ3lxRwpTSWI0VFFFTkFRY0NBUUVBTUJBR0N5cUdTSWI0VFFFTkFRY0RBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDCklCaUJSVnFRYnZreFY2VGlBWkQ4VXJ4RVRLdnRVMjlBYW81dEVHNTRUNnFoQWlFQWlpTEQzZDk3ZUVRYjkyVmQKOFRJZFZLU3FQeDhnRnlOYWhhd1dKZTY5YkhjPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlDbGpDQ0FqMmdBd0lCQWdJVkFKVnZYYzI5RytIcFFFbkoxUFF6emdGWEM5NVVNQW9HQ0NxR1NNNDlCQU1DCk1HZ3hHakFZQmdOVkJBTU1FVWx1ZEdWc0lGTkhXQ0JTYjI5MElFTkJNUm93R0FZRFZRUUtEQkZKYm5SbGJDQkQKYjNKd2IzSmhkR2x2YmpFVU1CSUdBMVVFQnd3TFUyRnVkR0VnUTJ4aGNtRXhDekFKQmdOVkJBZ01Ba05CTVFzdwpDUVlEVlFRR0V3SlZVekFlRncweE9EQTFNakV4TURVd01UQmFGdzB6TXpBMU1qRXhNRFV3TVRCYU1IQXhJakFnCkJnTlZCQU1NR1VsdWRHVnNJRk5IV0NCUVEwc2dVR3hoZEdadmNtMGdRMEV4R2pBWUJnTlZCQW9NRVVsdWRHVnMKSUVOdmNuQnZjbUYwYVc5dU1SUXdFZ1lEVlFRSERBdFRZVzUwWVNCRGJHRnlZVEVMTUFrR0ExVUVDQXdDUTBFeApDekFKQmdOVkJBWVRBbFZUTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFTlNCLzd0MjFsWFNPCjJDdXpweHc3NGVKQjcyRXlER2dXNXJYQ3R4MnRWVExxNmhLazZ6K1VpUlpDbnFSN3BzT3ZncUZlU3hsbVRsSmwKZVRtaTJXWXozcU9CdXpDQnVEQWZCZ05WSFNNRUdEQVdnQlFpWlF6V1dwMDBpZk9EdEpWU3YxQWJPU2NHckRCUwpCZ05WSFI4RVN6QkpNRWVnUmFCRGhrRm9kSFJ3Y3pvdkwyTmxjblJwWm1sallYUmxjeTUwY25WemRHVmtjMlZ5CmRtbGpaWE11YVc1MFpXd3VZMjl0TDBsdWRHVnNVMGRZVW05dmRFTkJMbVJsY2pBZEJnTlZIUTRFRmdRVWxXOWQKemIwYjRlbEFTY25VOURQT0FWY0wzbFF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQgpBZjhDQVFBd0NnWUlLb1pJemowRUF3SURSd0F3UkFJZ1hzVmtpMHcraTZWWUdXM1VGLzIydWFYZTBZSkRqMVVlCm5BK1RqRDFhaTVjQ0lDWWIxU0FtRDV4a2ZUVnB2bzRVb3lpU1l4ckRXTG1VUjRDSTlOS3lmUE4rCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNqekNDQWpTZ0F3SUJBZ0lVSW1VTTFscWROSW56ZzdTVlVyOVFHemtuQnF3d0NnWUlLb1pJemowRUF3SXcKYURFYU1CZ0dBMVVFQXd3UlNXNTBaV3dnVTBkWUlGSnZiM1FnUTBFeEdqQVlCZ05WQkFvTUVVbHVkR1ZzSUVOdgpjbkJ2Y21GMGFXOXVNUlF3RWdZRFZRUUhEQXRUWVc1MFlTQkRiR0Z5WVRFTE1Ba0dBMVVFQ0F3Q1EwRXhDekFKCkJnTlZCQVlUQWxWVE1CNFhEVEU0TURVeU1URXdORFV4TUZvWERUUTVNVEl6TVRJek5UazFPVm93YURFYU1CZ0cKQTFVRUF3d1JTVzUwWld3Z1UwZFlJRkp2YjNRZ1EwRXhHakFZQmdOVkJBb01FVWx1ZEdWc0lFTnZjbkJ2Y21GMAphVzl1TVJRd0VnWURWUVFIREF0VFlXNTBZU0JEYkdGeVlURUxNQWtHQTFVRUNBd0NRMEV4Q3pBSkJnTlZCQVlUCkFsVlRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVDNm5Fd01ESVlaT2ovaVBXc0N6YUVLaTcKMU9pT1NMUkZoV0dqYm5CVkpmVm5rWTR1M0lqa0RZWUwwTXhPNG1xc3lZamxCYWxUVll4RlAyc0pCSzV6bEtPQgp1ekNCdURBZkJnTlZIU01FR0RBV2dCUWlaUXpXV3AwMGlmT0R0SlZTdjFBYk9TY0dyREJTQmdOVkhSOEVTekJKCk1FZWdSYUJEaGtGb2RIUndjem92TDJObGNuUnBabWxqWVhSbGN5NTBjblZ6ZEdWa2MyVnlkbWxqWlhNdWFXNTAKWld3dVkyOXRMMGx1ZEdWc1UwZFlVbTl2ZEVOQkxtUmxjakFkQmdOVkhRNEVGZ1FVSW1VTTFscWROSW56ZzdTVgpVcjlRR3prbkJxd3dEZ1lEVlIwUEFRSC9CQVFEQWdFR01CSUdBMVVkRXdFQi93UUlNQVlCQWY4Q0FRRXdDZ1lJCktvWkl6ajBFQXdJRFNRQXdSZ0loQU9XLzVRa1IrUzlDaVNEY05vb3dMdVBSTHNXR2YvWWk3R1NYOTRCZ3dUd2cKQWlFQTRKMGxySG9NcytYbzVvL3NYNk85UVd4SFJBdlpVR09kUlE3Y3ZxUlhhcUk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "collateral": "embedded" + } + }, + "policy": { + "bundle_hash": "sha256:b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3", + "enforcement_mode": "enforce", + "version": "1.0.0" + }, + "data_class": "internal", + "build_provenance": { + "slsa_level": 2, + "builder": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml", + "digest": "sha256:e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6" + }, + "appraisal": { + "status": "affirming", + "verifier": "https://github.com/agentrust-io/agent-manifest" + }, + "cnf": { + "jwk": { + "kty": "OKP", + "crv": "Ed25519", + "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo" + } + }, + "signature": "uBUT0tPs1kbBzGAv8CKDZ5aNJ_saBmtBGTTcQFIeH7WGxHldc_x4xdh7YBUf847Cua4y9GC7lmWTCsKrx1eaAA" + } +} diff --git a/tests/test_runtime_evidence_vectors.py b/tests/test_runtime_evidence_vectors.py index c024dd83..dd85e05b 100644 --- a/tests/test_runtime_evidence_vectors.py +++ b/tests/test_runtime_evidence_vectors.py @@ -12,7 +12,7 @@ **Out of scope here**, deliberately: whether a quote verifies. That needs a TDX verifier, this project does not ship one, and the proposal argues it should not start. `examples/runtime-evidence/generate.py` performs that half against `agent-manifest`'s -verifier and reports 13/13, and `examples/runtime-evidence/test_appraisal.py` runs it in +verifier and reports 14/14, and `examples/runtime-evidence/test_appraisal.py` runs it in the dedicated `runtime-evidence` CI job against a pinned verifier commit. The split is stated rather than hidden because a test that quietly skipped the @@ -61,7 +61,7 @@ def test_the_corpus_did_not_shrink() -> None: The count is the whole assertion: which vectors exist is the generator's business, but a set that can silently lose its inconvenient members measures nothing. """ - assert len(VECTORS) == 13 + assert len(VECTORS) == 14 def test_every_vector_declares_the_v03_profile() -> None: