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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@ Format: [Semantic Versioning](https://semver.org/). Spec versions follow `MAJOR.

## [Unreleased]

- **`verify_record` reports what a caller-supplied appraiser found about `runtime.measurement`,
per layer (#279 platform-measurement row, #431).** A matching composite measurement says
nothing about which layers were measured, which were appraised, or whether the evidence
describes one boot, and `verify_record` never sees the quote, log or reference values to
decide it. The new `platform_appraiser` argument, modelled on `citation_resolver`, is called
last with a copy of `runtime`; the result's new `platform_measurement` field carries its
report as `appraised` with a `LayerCheck` per layer (`established`, or `not_established` with
`layer_not_measured`, `measured_not_appraised` or `evidence_spans_multiple_boots`),
`appraisal_rejected` when the appraiser raised, returned another shape or appraised another
measurement, or `not_attempted` without one. The names are not accepted normative text. No
outcome moves `revocation`, the thumbprint, `citations` or `appraisal.status`, and
`not_attempted` is never a pass. Sixteen signed vectors in `examples/platform-measurement/`:
each cause with a twin that differs only in the condition producing it, and two carrying
measurements from published quotes on a board with a discrete TPM. No schema or wire-format
change.

- **A surrogate code point in an object key is refused as `rfc8785.CanonicalizationError`
(found by fuzzing #452).** `rfc8785` refuses one in a value itself, but in an object key the
UTF-16 key sort reaches it first and raised `UnicodeEncodeError`. The three places that catch
Expand Down
63 changes: 63 additions & 0 deletions examples/platform-measurement/01-no-appraiser.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"id": "TRACE-PMEAS-001",
"name": "no-appraiser",
"description": "The harness supplies no appraiser: not_attempted with cause no_appraiser and no layers. not_attempted is not a pass and is never summarised as one.",
"spec": "https://github.com/agentrust-io/trace-spec/issues/279",
"synthetic": true,
"context": {
"now": 1785000000,
"max_age_seconds": 86400,
"max_future_skew_seconds": 300,
"trusted_key": {
"kty": "OKP",
"crv": "Ed25519",
"x": "72UGSnrAhzVD8uc_wmGSdtwE1r5ILsFhASe026WS2Cw"
}
},
"records": [
{
"eat_profile": "tag:agentrust-io.com,2026:trace-v0.2",
"iat": 1784996400,
"subject": "spiffe://acme.example/agent/measured",
"model": {
"provider": "anthropic",
"model_id": "claude-sonnet-4-6"
},
"runtime": {
"platform": "tpm2",
"measurement": "sha256:5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
},
"policy": {
"bundle_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"enforcement_mode": "enforce"
},
"data_class": "internal",
"build_provenance": {
"slsa_level": 0,
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"appraisal": {
"status": "affirming",
"verifier": "https://verifier.example/v1"
},
"cnf": {
"jwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "72UGSnrAhzVD8uc_wmGSdtwE1r5ILsFhASe026WS2Cw"
}
},
"signature": "YKfryO_Myl6ftBfs6ucP_5wH45xhyLCKYoFkgCEXXDp4c1dcI6rO5Nafmn-6MlDyGBc1DQBysj1-I1Ig3gQvAQ"
}
],
"expected": {
"rejected": false,
"codes": [],
"platform_measurement": {
"outcome": "not_attempted",
"cause": "no_appraiser",
"evidence": {},
"layers": {}
}
}
}
125 changes: 125 additions & 0 deletions examples/platform-measurement/02-no-appraiser-twin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"id": "TRACE-PMEAS-002",
"name": "no-appraiser-twin",
"description": "Twin of 001: the same record, with an appraiser that reports every layer established. The only difference is that an appraiser was supplied.",
"spec": "https://github.com/agentrust-io/trace-spec/issues/279",
"synthetic": true,
"twin_of": "TRACE-PMEAS-001",
"context": {
"now": 1785000000,
"max_age_seconds": 86400,
"max_future_skew_seconds": 300,
"trusted_key": {
"kty": "OKP",
"crv": "Ed25519",
"x": "72UGSnrAhzVD8uc_wmGSdtwE1r5ILsFhASe026WS2Cw"
},
"appraisals": {
"sha256:5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a": {
"measurement": "sha256:5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"layers": {
"pcr:0": "established",
"pcr:1": "established",
"pcr:2": "established",
"pcr:3": "established",
"pcr:4": "established",
"pcr:5": "established",
"pcr:6": "established",
"pcr:7": "established",
"pcr:8": "established",
"pcr:9": "established"
}
}
}
},
"records": [
{
"eat_profile": "tag:agentrust-io.com,2026:trace-v0.2",
"iat": 1784996400,
"subject": "spiffe://acme.example/agent/measured",
"model": {
"provider": "anthropic",
"model_id": "claude-sonnet-4-6"
},
"runtime": {
"platform": "tpm2",
"measurement": "sha256:5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
},
"policy": {
"bundle_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"enforcement_mode": "enforce"
},
"data_class": "internal",
"build_provenance": {
"slsa_level": 0,
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"appraisal": {
"status": "affirming",
"verifier": "https://verifier.example/v1"
},
"cnf": {
"jwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "72UGSnrAhzVD8uc_wmGSdtwE1r5ILsFhASe026WS2Cw"
}
},
"signature": "YKfryO_Myl6ftBfs6ucP_5wH45xhyLCKYoFkgCEXXDp4c1dcI6rO5Nafmn-6MlDyGBc1DQBysj1-I1Ig3gQvAQ"
}
],
"expected": {
"rejected": false,
"codes": [],
"platform_measurement": {
"outcome": "appraised",
"cause": null,
"evidence": {
"measurement": "sha256:5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"layers": 10
},
"layers": {
"pcr:0": {
"outcome": "established",
"cause": null
},
"pcr:1": {
"outcome": "established",
"cause": null
},
"pcr:2": {
"outcome": "established",
"cause": null
},
"pcr:3": {
"outcome": "established",
"cause": null
},
"pcr:4": {
"outcome": "established",
"cause": null
},
"pcr:5": {
"outcome": "established",
"cause": null
},
"pcr:6": {
"outcome": "established",
"cause": null
},
"pcr:7": {
"outcome": "established",
"cause": null
},
"pcr:8": {
"outcome": "established",
"cause": null
},
"pcr:9": {
"outcome": "established",
"cause": null
}
}
}
}
}
74 changes: 74 additions & 0 deletions examples/platform-measurement/03-appraiser-raised.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"id": "TRACE-PMEAS-003",
"name": "appraiser-raised",
"description": "The appraiser knows other measurements but not this one and raises: appraisal_rejected with cause appraiser_raised and the exception's class name. The record still verifies.",
"spec": "https://github.com/agentrust-io/trace-spec/issues/279",
"synthetic": true,
"context": {
"now": 1785000000,
"max_age_seconds": 86400,
"max_future_skew_seconds": 300,
"trusted_key": {
"kty": "OKP",
"crv": "Ed25519",
"x": "72UGSnrAhzVD8uc_wmGSdtwE1r5ILsFhASe026WS2Cw"
},
"appraisals": {
"sha256:0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e": {
"measurement": "sha256:0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e",
"layers": {
"pcr:0": "established"
}
}
}
},
"records": [
{
"eat_profile": "tag:agentrust-io.com,2026:trace-v0.2",
"iat": 1784996400,
"subject": "spiffe://acme.example/agent/measured",
"model": {
"provider": "anthropic",
"model_id": "claude-sonnet-4-6"
},
"runtime": {
"platform": "tpm2",
"measurement": "sha256:5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
},
"policy": {
"bundle_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"enforcement_mode": "enforce"
},
"data_class": "internal",
"build_provenance": {
"slsa_level": 0,
"digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"appraisal": {
"status": "affirming",
"verifier": "https://verifier.example/v1"
},
"cnf": {
"jwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "72UGSnrAhzVD8uc_wmGSdtwE1r5ILsFhASe026WS2Cw"
}
},
"signature": "YKfryO_Myl6ftBfs6ucP_5wH45xhyLCKYoFkgCEXXDp4c1dcI6rO5Nafmn-6MlDyGBc1DQBysj1-I1Ig3gQvAQ"
}
],
"expected": {
"rejected": false,
"codes": [],
"platform_measurement": {
"outcome": "appraisal_rejected",
"cause": "appraiser_raised",
"evidence": {
"measurement": "sha256:5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"exception": "KeyError"
},
"layers": {}
}
}
}
Loading
Loading