Repository navigation
spec(appraisal): platform_measurement, what a matching measurement covers (#279) - #477
Conversation
imran-siddique
left a comment
There was a problem hiding this comment.
@revenue7-eng this is what #279 asked for, and I am carrying it as sponsoring maintainer under CONTRIBUTING. Put that in the CHANGELOG entry in place of "no organizational sponsor", as #364 did. The open points, in one pass:
Rule 2 stays, labelled breaking. Its last clause bars affirming when the verifier's policy requires a layer and it writes no block. That is a new duty on every verifier that reports affirming after a §3.3 step 4 reference match, so the CHANGELOG entry should say BREAKING and name exactly that. v0.2 is a Draft, so the 30-day period in GOVERNANCE does not apply; the statement of what breaks does.
Twins for the rejections. None of the 14 rejection vectors (11 to 22, 25, 26) has a committed passing twin. The nearest accepting vector differs in 3 to 5 fields, and only 21 and 22 get one inside the test. Each needs a twin that differs only in the rule it tests. Add one accepting vector off TPM as well, so a non-TPM layer name is shown to pass.
Rebase. CHANGELOG.md conflicts with #476; keeping both entries resolves it.
Two nits, your call: an explicit reason: null passes the model and fails the schema, and pcr: has no upper bound.
The generator reproduces all 26 vectors byte for byte, and the v0.2 schema change moves no verdict across the 643 existing documents. Merging once the three land.
…vers (agentrust-io#279) Section 3.1.5: a verifier records, per layer of a measured-boot platform, established or not-established with one of three reasons: layer-not-measured, measured-not-appraised, evidence-spans-multiple-boots. An unlisted layer is not established, nor is any layer of a record without the block; a replay mismatch the verifier cannot attribute to one boot is not, on that basis alone, contraindicated; a record carrying a result does not report status none. BREAKING: a verifier that reports affirming after a section 3.3 step 4 reference match, while its policy requires a layer, must now establish that layer in appraisal.platform_measurement. Records do not change: the schema gains one optional member of appraisal. Schema and packaged copy, reference model, docs/schema.md and the v0.3 draft carry it. Forty-four signed vectors in examples/platform-measurement-appraisal/: five case-and-twin pairs, two records with published measurements, at least two rejections per rule, and an accepting twin for every rejection. Proposed and written by @revenue7-eng in agentrust-io#279; carried by @imran-siddique as sponsoring maintainer. Signed-off-by: Andrey Lazarev <lazarev@tactiqedge.com>
5494686 to
ca6a13c
Compare
|
Thank you for carrying it. All three are in, rebased on main, as one commit:
I took both nits. An explicit |
imran-siddique
left a comment
There was a problem hiding this comment.
All three are in and both nits taken. Carrying as sponsoring maintainer.
…measurement agentrust-io#477 added appraisal.platform_measurement.measurement after this branch was cut. Its pattern is the same ^sha(256:[0-9a-f]{64}|384:[0-9a-f]{96})$ as the other digest fields, and its row named no algorithm, so merging main reddened the guard this branch adds: the field and both algorithms were named, and the counterfactual test failed because its baseline was no longer clean. The row now takes the form the other corrected rows use. That makes thirteen digest fields, of which seven rows were narrower than the pattern, and the CHANGELOG entry carries those counts. The commit message of 7ca1d25 says twelve and six, which was true of the tree it was cut from. Reverting this row alone reds the guard and names that field alone. sha512: is refused by its pattern, as by the other twelve. Editorial: no schema, wire-format or normative change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: opento-suggestions <opentosuggestionsofficial@gmail.com>
spec(appraisal): platform_measurement, what a matching measurement covers (#279)
Closes the appraisal-outcome part of #279, as agreed in the thread: the three cases are appraisal outcomes, not
runtimeproperties, and today a record can only sayappraisal.status: noneabout them.What it adds
Spec section 3.1.5 (normative). On a measured-boot platform
runtime.measurementis a composite over many layers, and a match against a reference does not say which layers recorded anything, which were appraised, or whether the evidence describes one boot. A verifier now writes, per layer,establishedornot-establishedwith one of three reasons. The right column is the collapse this section removes, not what it prescribes; under the section all three arenot-established, which is neither a pass nor a finding of tampering:layer-not-measuredmeasured-not-appraisedevidence-spans-multiple-bootsA layer is
establishedwhen its log replays from the initial value to the quoted value and nothing else the verifier's policy requires of it is missing; a log that replays accounts for every extension since the last reset, so the boot count matters only when it does not.Five rules:
not-establishedis never reported asestablished, and neither an unlisted layer nor any layer of a record without the block is established; a verifier does not reportaffirmingwhile a layer its policy requires is not established, including when it writes no block at all; a replay mismatch the verifier cannot attribute to one boot is not, on that basis alone,contraindicated;measured-not-appraisedis not a finding that appraisal was off; a record carrying a result does not reportstatus: none. Rules 1, 4 and 5 can be checked from the record; rules 2 and 3 bind what a verifier writes, since the record does not carry its policy, and the section says so and tells a relying party that needs the assurance to read the layers itself. A profile that wants a replay mismatch read as a contradiction names how its verifiers establish a single boot; without one, rule 3 applies, and a verifier that overuses the reason loses a diagnosis, never a rejection. Rule 2 constrainsstatuswhere §3.1.4 does not, and the section says why: a re-execution outcome is about one claim read in its own block, while a required layer not established means the appraisal did not establish what its own policy asked, which is whataffirmingreports. The section also says what rule 3 costs (on a platform whose quote does not show the registers were reset since the log began, a replay mismatch is nevercontraindicatedon its own) and why it does not open a downgrade (rules 1 and 2 keepnot-establishedfrom passing, for a reader of the layers and for a reader ofstatusalone).Placement.
appraisal.platform_measurementis a member ofappraisalin its own right, asprovenance_depth_verifiedis, not anappraisal.methodvalue, so it sits beside a re-execution result. Itsmeasurementmust equalruntime.measurement; that is a rule of the reference model and of conformance, not a JSON Schema constraint, since the schema cannot compare two members of a record (the vectors for it carry"schema_sees": false). Ontpm2a layer is namedpcr:<n>, decimal, no leading zeros. How a verifier decides each outcome, and how it establishes a single boot, are left to the platform's profile.Schema and model.
schema/trace-claim.jsonand the packaged copy (identical bytes),models.py(PlatformMeasurementLayer,PlatformMeasurement, twoTrustRecordchecks),docs/schema.md, the regenerated v0.3 draft schema. The TPM layer pattern is added to the closed set in_patterns.py; it has no dot and no\d, so only the anchor is adapted, and the JavaScript comparison now samplespcr:0,pcr:10,pcr:01,pcr:andPCR0.Why in the core rather than a profile. The three reasons are a closed set so that a relying party reads one vocabulary across platforms; how a verifier reaches each one, and how it establishes a single boot, are left to the platform's profile.
Vectors.
examples/platform-measurement-appraisal/, 44 signed records:pcr:2, each twin differing from its case in that one condition only: a measured layer, an appraised layer, a single-boot log, a listed layer, and a result at all (the last two are rule 1);29to44) that differs from it only in the member that breaks the rule;38, the twin of20, is the record off TPM, with a layer namedrtmr:0.The two sourced vectors cite the published evidence at
revenue7-eng/tactiq-os, the same evidence the merged #457 vectors cite; the files are a release asset and a committed record with an offline checker beside it.The generator and
tests/test_platform_measurement_appraisal_vectors.pyfollow the layout ofexamples/reproducibility-claim/gen_reproducibility_vectors.pyandtests/test_reproducibility_claim_vectors.py(seed-derived key, one code per rule, two vectors per code, theLOCUScheck), withtwin_of,syntheticandsourceas inexamples/platform-measurement/from #457. What is new is the reading check below andschema_sees.Accepting vectors carry
expected.reading: what a relying party takes from the record for each layer asked about.tests/test_platform_measurement_appraisal_vectors.pychecks that a reader refusing everything fails all five twins, a reader trustingappraisal.statusfails all five cases, and a reader taking an unlisted layer as established fails07and23. The set is registered intests/test_adequacy_all_sets.py.One existing test rule changed
tests/test_safe_integer_range.py::test_no_object_leaves_an_additional_member_unconstrainedadmittedadditionalProperties: falseor thecanonicalizableValuereference.layersis a map whose member is one closed object schema. The rule now also admits a map whose every member is itself an object schema, which the same test then holds to the same rule:_object_nodes, unchanged, recurses into every value of a schema node,additionalPropertiesincluded, so the member schema is checked as an object in its own right. The alternative was an array of{layer, outcome, reason}, which the schema could not keep free of duplicate layers.Tests that fail when each rule is switched off
Run locally on this branch, one rule at a time, restoring the files after each run; the switch-off script is not part of the PR. What was disabled is named so the runs can be repeated:
allOf, and the check inPlatformMeasurementLayerminPropertiesonlayers, andmin_lengthinPlatformMeasurementreasonenum, and itsLiteralin the modelallOfentry, and thetpm2branch inTrustRecordruntime.measurement(model only): the comparison inTrustRecordtest_the_mismatch_vectors_fail_on_the_mismatch_and_nothing_elsenonewith a result: the lastallOfentry underappraisal, and the check inAppraisalreasonnevernull: the null check inPlatformMeasurementLayer(the schema's enum refusesnullalready)Relation to #457
verify_record'splatform_measurementfield reports the same three causes with underscores. That field is library output and binds no record; this PR names what a record carries, hyphenated as the spec's other outcomes are. Aligning the library's spelling is left out of this PR.Compatibility
BREAKING for verifiers: one that reports
affirmingafter a section 3.3 step 4 reference match, while its appraisal policy requires a layer, must now establish that layer inappraisal.platform_measurement;affirmingwith the block absent, or with a required layer not established or not listed, is no longer conformant. Records do not change: the schema gains one optional member ofappraisal, and no existing document changes verdict. v0.2 is a Draft, so the 30-day period in GOVERNANCE does not apply.Proposed and written by @revenue7-eng in #279; carried by @imran-siddique as sponsoring maintainer.
Read by someone who had not seen the work
Three new sessions with no access to this work read the patch, this description, CONTRIBUTING.md and, for one of them, #279. Two could not reach GitHub or PyPI and read statically; the third applied the patch to a fresh clone of main and ran the four checks, all passing (pytest, ruff,
tools/check_dashes.py, mypy). Each read an earlier revision. What they found, and what changed:not-established, which is neither.affirmingon atpm2record carrying noplatform_measurementat all. Rules 1 and 2 now cover a record without the block; vectors 23 and 24 pin it.establishedhad no positive definition, and a log that replays while a single boot is not established seemed to fit no reason. The section now definesestablished, and says why a replaying log settles the boot count for its layer.status: nonebeside a result was unconstrained. Rule 5 forbids it; schema, model and vectors 25 and 26 hold it.statuswhen §3.1.4 does not was unexplained. The section now says.$commentnamed the wrong check. Fixed.test_safe_integer_rangerule still recurses into map members. It does; see above.They also raised the missing organizational sponsor, which this description states, and judged the per-layer
not-establishedwith a reason to fit #279.