From 2a9231abd96ebaab968fb784030d1dc8d1f3314a Mon Sep 17 00:00:00 2001 From: Srinivasa Consultants <78673153+dinakarjs@users.noreply.github.com> Date: Mon, 19 Oct 2026 22:50:52 +0530 Subject: [PATCH 1/3] docs: propose bounded evidence resolution acceptance Signed-off-by: Srinivasa Consultants <78673153+dinakarjs@users.noreply.github.com> --- docs/rfcs/evidence-resolution-acceptance.md | 125 ++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 docs/rfcs/evidence-resolution-acceptance.md diff --git a/docs/rfcs/evidence-resolution-acceptance.md b/docs/rfcs/evidence-resolution-acceptance.md new file mode 100644 index 00000000..e202f39a --- /dev/null +++ b/docs/rfcs/evidence-resolution-acceptance.md @@ -0,0 +1,125 @@ +# Evidence resolution and re-appraisal: proposed acceptance profile + +Status: informative design proposal for [#441](https://github.com/agentrust-io/trace-spec/issues/441). +This document binds no implementation and changes no wire fields or outcomes. +It supplements the [composite appraisal proposal](composite-component-appraisal.md). +Maintainers still select the profile and local comparison representation before code. + +## Claim and trust boundary + +The first slice resolves one saved Azure SNP execution packet from +operator-configured offline storage and independently re-appraises its execution +and collateral under declared inputs. It establishes what the second appraisal +finds about those bytes under that context. It cannot establish what the original +appraiser read, current platform freshness, or protected model execution. + +Delegated appraisal remains a legitimate alternative trust model. RATS [R1] +does not require every relying party to parse raw evidence. This opt-in profile +is for consumers that explicitly require their own re-appraisal. + +The resolver has no network authority from a signed hint. Its operator controls +storage, size limits, access and retention. Missing, denied, deleted, oversized +or unavailable evidence leaves the corresponding appraisal unestablished. +A digest mismatch is a binding failure, not proof that the asserted content is false. + +## Proposed processing obligations + +1. Retain the received bytes in the declared private store. Parse unambiguously, + including duplicate-member refusal, before canonicalization. +2. Check the reference against the existing `canonical_digest(packet)` contract + in `prototype/verifier_token.py`: RFC 8785 canonical JSON and the named digest. + Whitespace differences alone do not change that object digest. Arbitrary + downloaded text hashes are not interchangeable with it [R2]. +3. Independently supply holder/challenge context, execution policy, accepted + roots, platform collateral, TCB floors and evaluation instant. +4. Reuse the authenticated execution path before collateral appraisal. + `prototype/snp_collateral.py::appraise_collateral` explicitly assumes an + already-authenticated packet; calling it alone is insufficient. +5. Retain execution and collateral results separately. Compare eligible local + results with the original signed components without rewriting those components. + Shared evidence identity does not imply that both appraisals occurred. +6. Preserve original observation/appraisal times. Fetching or serializing the + packet today does not create new freshness. + +The existing historical packet can legitimately fall below the selected vendor +floor. Such a result is an important control, not a reason to lower a real +acceptance floor. Any synthetic affirmative fixture has an explicit test policy +and is never described as production admission evidence. + +## Proposed acceptance matrix + +These are unexecuted cases. Each negative has a valid twin that reaches the +specific boundary, including genuine signatures for semantic-binding cases. + +| ID | Controlled pair | Required distinction | +| --- | --- | --- | +| ER01 | Same object, changed whitespace / changed member | Same canonical digest / digest mismatch | +| ER02 | Available packet / missing, denied or deleted packet | Appraisal attempted / unavailable evidence | +| ER03 | Authenticated expected packet / wrong AK or root | Eligible appraisal / authentication failure | +| ER04 | Correct holder and challenge / validly signed wrong context | Correct binding / no applicable appraisal | +| ER05 | Both selected appraisals / execution only | Two reported results / collateral unappraised | +| ER06 | Explicit synthetic accepted test policy / genuine below-floor packet | Scoped affirmative control / contraindicated platform | +| ER07 | Recorded historical instant / same bytes fetched later | Historical result / no renewed observation freshness | +| ER08 | Agreed local result / eligible contradictory local result | Agreement / explicit comparison difference | +| ER09 | Unambiguous JSON / duplicate member or exceeded size limit | Parseable evidence / refused resolution | + +## Corpus, second checker and evaluation plan + +A future manifest records: case ID and version; source revision/file; exact +received-byte digest and canonical-object digest as distinct named values; +evidence class; trusted input files and digests; original observation and +evaluation instants; expected named results; positive twin; targeted mutation; +reproduction command; and public/private disposition. Real device identifiers +and protected payloads remain private. Predictable-content hashes are not +automatically safe to publish. Omitted raw material makes public reproduction +incomplete and is declared as such. + +The bounded second checker validates resolution identity, invocation coverage +and result comparison without importing the reference adapter's decision logic. +Shared cryptographic libraries and specifications are disclosed dependencies; +this is not independent platform measurement. A separate qualified reader +defines expected cases before seeing checker outputs. + +Compare signature/digest-only checking with the existing full appraisal path +and a strong stateful baseline given identical evidence and context. For change +handling, compare selective invalidation with full reassessment. Report false +establishment, legitimate controls retained, unavailable results and cost by +mechanism. Keep held-out mechanisms separate from template variants [R5]. + +Planned mutations disable digest, authentication, required-profile invocation +and result-comparison guards separately. The matching negative then fails a +behavioral assertion; valid twins still pass. Import errors and reject-all +behavior do not count as adequate detection. No benchmark result is claimed. + +## Adjacent decisions, not bundled implementation + +- [#442](https://github.com/agentrust-io/trace-spec/issues/442): first decide + leaf-only appraiser status, authenticated key/grant identity, cache freshness, + effective revocation time and unavailable outcomes. Retain historical and + current-admission policies separately. A cached good response can entail a + bounded observation delay; immediate revocation does not follow [R3]. +- [#440](https://github.com/agentrust-io/trace-spec/issues/440): separately select + authenticated admission-store transport, consistency and recovery assumptions. + Partition, concurrent consume, restart, promotion, rollback and clock-boundary + cases need history-based tests. A linearizable admission object does not make + an unrelated external effect exactly once [R4]. + +These dependencies cannot be marked complete by accepting this informative text. + +## Literature and its application + +- R1: [RFC 9334, RATS Architecture (2023), sections 8 and 10](https://www.rfc-editor.org/rfc/rfc9334.html). + Roles, appraisal policies and freshness inform the trust boundary. +- R2: [RFC 8785, JSON Canonicalization Scheme (2020)](https://www.rfc-editor.org/rfc/rfc8785.html). + Defines the canonicalization antecedent; existing repository code determines + this packet's preimage contract. +- R3: [Samuel et al., Survivable Key Compromise, CCS 2010](https://theupdateframework.io/papers/survivable-key-compromise-ccs2010.pdf). + Timeliness and rollback are distinct trust-maintenance problems; not a TRACE + appraiser-status implementation or mandated delegation scheme. +- R4: [Herlihy and Wing, Linearizability, 1990](https://cs.brown.edu/people/mph/HerlihyW90/p463-herlihy.pdf). + Object-history reasoning motivates admission tests, not external-effect atomicity. +- R5: [Zabolotnii, Runtime Assurance Contracts, v2, 16 October 2026](https://arxiv.org/html/2609.39717v2). + Detector omissions and strong stateful comparisons constrain evaluation claims. + +These sources are prior art, not proof of this proposal's correctness or novelty. +No standardization, hardware run, independent acceptance or issue closure is claimed. From 624b9396f12d85eec69daaa5d9ef09a65b1444d2 Mon Sep 17 00:00:00 2001 From: Srinivasa Consultants <78673153+dinakarjs@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:21:37 +0530 Subject: [PATCH 2/3] test: execute ER01 and fold resolution proposal into composite RFC Answer section 11 question 4 in the existing RFC, remove the sibling document, and pin canonical object identity with a changed-member twin. No resolver or appraisal implementation is added. Signed-off-by: Srinivasa Dinakar Jainti --- docs/rfcs/composite-component-appraisal.md | 135 +++++++++++++++++++- docs/rfcs/evidence-resolution-acceptance.md | 125 ------------------ tests/test_evidence_resolution_digest.py | 30 +++++ 3 files changed, 164 insertions(+), 126 deletions(-) delete mode 100644 docs/rfcs/evidence-resolution-acceptance.md create mode 100644 tests/test_evidence_resolution_digest.py diff --git a/docs/rfcs/composite-component-appraisal.md b/docs/rfcs/composite-component-appraisal.md index 05e96b22..6ef2f4db 100644 --- a/docs/rfcs/composite-component-appraisal.md +++ b/docs/rfcs/composite-component-appraisal.md @@ -282,10 +282,143 @@ Both are now settled in the experimental text and pinned by vectors. The composi 1. **Component type registry and extension routing.** The ten categories are closed in this experiment, and an unknown one is rejected even on an optional component. Should a registry allow profile-defined subtypes, and should an unknown optional category be ignored instead of rejected? 2. **Relationship method registry.** Two methods, one relationship value. What does a third method need to register: a digest preimage, a check a second implementation can run, and vectors? 3. **Should `resolver` be required?** The requirement as first written said yes. This profile says no, because a required hint nobody fetches adds a field without adding a check. If evidence resolution is specified, the answer may change. -4. **Evidence resolution.** Who fetches, from where, with what retention, and what status a relying party reports when the fetch fails. The original design said `unverifiable`, not `contraindicated`, unless other evidence contradicts the claim. Nothing implements it yet. +4. **Evidence resolution.** Who fetches, from where, with what retention, and what status a relying party reports when the fetch fails. The original design said `unverifiable`, not `contraindicated`, unless other evidence contradicts the claim. Nothing implements it yet. The proposed bounded answer below specifies an opt-in offline slice; only its ER01 canonical-digest control is executed. 5. **Delegation chains and appraiser revocation.** Can an appraiser delegate further, and how does a relying party learn that an appraiser key was withdrawn before `valid_until`? 6. **MCP server components.** What evidence an `mcp-server` component rests on (artifact provenance, endpoint identity, transport key binding), and a relationship method that binds a catalog to the server that served it. For the catalog half, [`tool-catalog-observed-digest.md`](tool-catalog-observed-digest.md) states one derivation of a `tool-catalog` component's `observed_digest` from the served `tools/list`, with pinned bytes and `COMP-MCP-004` to `006`; it answers neither the server half nor the relationship method. 7. **Composite precedence and freshness (§8.3).** The experiment settles both: the token's `exp` bounds the composite, and `missing` outranks `unverifiable`. Both readings were defensible, and a reviewer may still invert either one. If so, the four vectors that pin them change with the text. 8. **Binding-only endpoints.** A binding endpoint with no entry in `requirements.components` is required but undeclared, so such a configuration can never affirm. Should requirement loading reject it? The checks behind all of this can be run from a checkout: `node tools/independent-verifier/verify.mjs examples/verifier-token-conformance/vectors/*.json` prints one outcome per vector, and `python -m pytest tests/test_stage4_relationships.py` runs the worked example in §7 on the saved packets. + + +### 11.1 Proposed answer to question 4: offline evidence resolution + +Informative proposal for [#441](https://github.com/agentrust-io/trace-spec/issues/441). +No resolver, wire field or appraisal outcome is implemented by this text. +Maintainers still select the profile and local comparison representation. + +#### Claim and trust boundary + +The first slice resolves one saved Azure SNP execution packet from +operator-configured offline storage and independently re-appraises its execution +and collateral under declared inputs. It establishes what the second appraisal +finds about those bytes under that context. It cannot establish what the original +appraiser read, current platform freshness, or protected model execution. + +Delegated appraisal remains a legitimate alternative trust model. RATS [R1] +does not require every relying party to parse raw evidence. This opt-in profile +is for consumers that explicitly require their own re-appraisal. + +The resolver has no network authority from a signed hint. Its operator controls +storage, size limits, access and retention. Missing, denied, deleted, oversized +or unavailable evidence leaves the corresponding appraisal unestablished. +A digest mismatch is a binding failure, not proof that the asserted content is false. + +#### Proposed processing obligations + +1. Retain the received bytes in the declared private store. Parse unambiguously, + including duplicate-member refusal, before canonicalization. +2. Check the reference against the existing `canonical_digest(packet)` contract + in `prototype/verifier_token.py`: RFC 8785 canonical JSON and the named digest. + Whitespace differences alone do not change that object digest. Arbitrary + downloaded text hashes are not interchangeable with it [R2]. +3. Independently supply holder/challenge context, execution policy, accepted + roots, platform collateral, TCB floors and evaluation instant. +4. Reuse the authenticated execution path before collateral appraisal. + `prototype/snp_collateral.py::appraise_collateral` explicitly assumes an + already-authenticated packet; calling it alone is insufficient. +5. Retain execution and collateral results separately. Compare eligible local + results with the original signed components without rewriting those components. + Shared evidence identity does not imply that both appraisals occurred. +6. Preserve original observation/appraisal times. Fetching or serializing the + packet today does not create new freshness. + +The existing historical packet can legitimately fall below the selected vendor +floor. Such a result is an important control, not a reason to lower a real +acceptance floor. Any synthetic affirmative fixture has an explicit test policy +and is never described as production admission evidence. + +#### Proposed acceptance matrix + +ER01 is executed by `tests/test_evidence_resolution_digest.py`; ER02 to ER09 +remain planned. Each planned negative needs a valid twin reaching its specific +boundary, including genuine signatures for semantic-binding cases. + +| ID | Controlled pair | Required distinction | +| --- | --- | --- | +| ER01 (executed) | Same synthetic object, changed whitespace and member order / changed member | Same canonical digest / different digest, checked against a pinned canonical preimage | +| ER02 | Available packet / missing, denied or deleted packet | Appraisal attempted / unavailable evidence | +| ER03 | Authenticated expected packet / wrong AK or root | Eligible appraisal / authentication failure | +| ER04 | Correct holder and challenge / validly signed wrong context | Correct binding / no applicable appraisal | +| ER05 | Both selected appraisals / execution only | Two reported results / collateral unappraised | +| ER06 | Explicit synthetic accepted test policy / genuine below-floor packet | Scoped affirmative control / contraindicated platform | +| ER07 | Recorded historical instant / same bytes fetched later | Historical result / no renewed observation freshness | +| ER08 | Agreed local result / eligible contradictory local result | Agreement / explicit comparison difference | +| ER09 | Unambiguous JSON / duplicate member or exceeded size limit | Parseable evidence / refused resolution | + +ER01 checks the existing helper only: three JSON encodings parse to the same +synthetic object and match the SHA-256 of explicitly pinned canonical bytes. +Changing one nested member changes the expected digest. It neither resolves a +reference nor authenticates or appraises a platform packet. Run +`python -m pytest -q tests/test_evidence_resolution_digest.py`. + +#### Corpus, second checker and evaluation plan + +A future manifest records: case ID and version; source revision/file; exact +received-byte digest and canonical-object digest as distinct named values; +evidence class; trusted input files and digests; original observation and +evaluation instants; expected named results; positive twin; targeted mutation; +reproduction command; and public/private disposition. Real device identifiers +and protected payloads remain private. Predictable-content hashes are not +automatically safe to publish. Omitted raw material makes public reproduction +incomplete and is declared as such. + +The bounded second checker validates resolution identity, invocation coverage +and result comparison without importing the reference adapter's decision logic. +Shared cryptographic libraries and specifications are disclosed dependencies; +this is not independent platform measurement. A separate qualified reader +defines expected cases before seeing checker outputs. + +Compare signature/digest-only checking with the existing full appraisal path +and a strong stateful baseline given identical evidence and context. For change +handling, compare selective invalidation with full reassessment. Report false +establishment, legitimate controls retained, unavailable results and cost by +mechanism. Keep held-out mechanisms separate from template variants [R5]. + +Planned mutations disable digest, authentication, required-profile invocation +and result-comparison guards separately. The matching negative then fails a +behavioral assertion; valid twins still pass. Import errors and reject-all +behavior do not count as adequate detection. No benchmark result is claimed. + +#### Adjacent decisions, not bundled implementation + +- [#442](https://github.com/agentrust-io/trace-spec/issues/442): first decide + leaf-only appraiser status, authenticated key/grant identity, cache freshness, + effective revocation time and unavailable outcomes. Retain historical and + current-admission policies separately. A cached good response can entail a + bounded observation delay; immediate revocation does not follow [R3]. +- [#440](https://github.com/agentrust-io/trace-spec/issues/440): separately select + authenticated admission-store transport, consistency and recovery assumptions. + Partition, concurrent consume, restart, promotion, rollback and clock-boundary + cases need history-based tests. A linearizable admission object does not make + an unrelated external effect exactly once [R4]. + +These dependencies cannot be marked complete by accepting this informative text. + +#### Literature and its application + +- R1: [RFC 9334, RATS Architecture (2023), sections 8 and 10](https://www.rfc-editor.org/rfc/rfc9334.html). + Roles, appraisal policies and freshness inform the trust boundary. +- R2: [RFC 8785, JSON Canonicalization Scheme (2020)](https://www.rfc-editor.org/rfc/rfc8785.html). + Defines the canonicalization antecedent; existing repository code determines + this packet's preimage contract. +- R3: [Samuel et al., Survivable Key Compromise, CCS 2010](https://theupdateframework.io/papers/survivable-key-compromise-ccs2010.pdf). + Timeliness and rollback are distinct trust-maintenance problems; not a TRACE + appraiser-status implementation or mandated delegation scheme. +- R4: [Herlihy and Wing, Linearizability, 1990](https://cs.brown.edu/people/mph/HerlihyW90/p463-herlihy.pdf). + Object-history reasoning motivates admission tests, not external-effect atomicity. +- R5: [Zabolotnii, Runtime Assurance Contracts, v2, 16 October 2026](https://arxiv.org/html/2609.39717v2). + Detector omissions and strong stateful comparisons constrain evaluation claims. + +These sources are prior art, not proof of this proposal's correctness or novelty. +No standardization, hardware run, independent acceptance or issue closure is claimed. diff --git a/docs/rfcs/evidence-resolution-acceptance.md b/docs/rfcs/evidence-resolution-acceptance.md deleted file mode 100644 index e202f39a..00000000 --- a/docs/rfcs/evidence-resolution-acceptance.md +++ /dev/null @@ -1,125 +0,0 @@ -# Evidence resolution and re-appraisal: proposed acceptance profile - -Status: informative design proposal for [#441](https://github.com/agentrust-io/trace-spec/issues/441). -This document binds no implementation and changes no wire fields or outcomes. -It supplements the [composite appraisal proposal](composite-component-appraisal.md). -Maintainers still select the profile and local comparison representation before code. - -## Claim and trust boundary - -The first slice resolves one saved Azure SNP execution packet from -operator-configured offline storage and independently re-appraises its execution -and collateral under declared inputs. It establishes what the second appraisal -finds about those bytes under that context. It cannot establish what the original -appraiser read, current platform freshness, or protected model execution. - -Delegated appraisal remains a legitimate alternative trust model. RATS [R1] -does not require every relying party to parse raw evidence. This opt-in profile -is for consumers that explicitly require their own re-appraisal. - -The resolver has no network authority from a signed hint. Its operator controls -storage, size limits, access and retention. Missing, denied, deleted, oversized -or unavailable evidence leaves the corresponding appraisal unestablished. -A digest mismatch is a binding failure, not proof that the asserted content is false. - -## Proposed processing obligations - -1. Retain the received bytes in the declared private store. Parse unambiguously, - including duplicate-member refusal, before canonicalization. -2. Check the reference against the existing `canonical_digest(packet)` contract - in `prototype/verifier_token.py`: RFC 8785 canonical JSON and the named digest. - Whitespace differences alone do not change that object digest. Arbitrary - downloaded text hashes are not interchangeable with it [R2]. -3. Independently supply holder/challenge context, execution policy, accepted - roots, platform collateral, TCB floors and evaluation instant. -4. Reuse the authenticated execution path before collateral appraisal. - `prototype/snp_collateral.py::appraise_collateral` explicitly assumes an - already-authenticated packet; calling it alone is insufficient. -5. Retain execution and collateral results separately. Compare eligible local - results with the original signed components without rewriting those components. - Shared evidence identity does not imply that both appraisals occurred. -6. Preserve original observation/appraisal times. Fetching or serializing the - packet today does not create new freshness. - -The existing historical packet can legitimately fall below the selected vendor -floor. Such a result is an important control, not a reason to lower a real -acceptance floor. Any synthetic affirmative fixture has an explicit test policy -and is never described as production admission evidence. - -## Proposed acceptance matrix - -These are unexecuted cases. Each negative has a valid twin that reaches the -specific boundary, including genuine signatures for semantic-binding cases. - -| ID | Controlled pair | Required distinction | -| --- | --- | --- | -| ER01 | Same object, changed whitespace / changed member | Same canonical digest / digest mismatch | -| ER02 | Available packet / missing, denied or deleted packet | Appraisal attempted / unavailable evidence | -| ER03 | Authenticated expected packet / wrong AK or root | Eligible appraisal / authentication failure | -| ER04 | Correct holder and challenge / validly signed wrong context | Correct binding / no applicable appraisal | -| ER05 | Both selected appraisals / execution only | Two reported results / collateral unappraised | -| ER06 | Explicit synthetic accepted test policy / genuine below-floor packet | Scoped affirmative control / contraindicated platform | -| ER07 | Recorded historical instant / same bytes fetched later | Historical result / no renewed observation freshness | -| ER08 | Agreed local result / eligible contradictory local result | Agreement / explicit comparison difference | -| ER09 | Unambiguous JSON / duplicate member or exceeded size limit | Parseable evidence / refused resolution | - -## Corpus, second checker and evaluation plan - -A future manifest records: case ID and version; source revision/file; exact -received-byte digest and canonical-object digest as distinct named values; -evidence class; trusted input files and digests; original observation and -evaluation instants; expected named results; positive twin; targeted mutation; -reproduction command; and public/private disposition. Real device identifiers -and protected payloads remain private. Predictable-content hashes are not -automatically safe to publish. Omitted raw material makes public reproduction -incomplete and is declared as such. - -The bounded second checker validates resolution identity, invocation coverage -and result comparison without importing the reference adapter's decision logic. -Shared cryptographic libraries and specifications are disclosed dependencies; -this is not independent platform measurement. A separate qualified reader -defines expected cases before seeing checker outputs. - -Compare signature/digest-only checking with the existing full appraisal path -and a strong stateful baseline given identical evidence and context. For change -handling, compare selective invalidation with full reassessment. Report false -establishment, legitimate controls retained, unavailable results and cost by -mechanism. Keep held-out mechanisms separate from template variants [R5]. - -Planned mutations disable digest, authentication, required-profile invocation -and result-comparison guards separately. The matching negative then fails a -behavioral assertion; valid twins still pass. Import errors and reject-all -behavior do not count as adequate detection. No benchmark result is claimed. - -## Adjacent decisions, not bundled implementation - -- [#442](https://github.com/agentrust-io/trace-spec/issues/442): first decide - leaf-only appraiser status, authenticated key/grant identity, cache freshness, - effective revocation time and unavailable outcomes. Retain historical and - current-admission policies separately. A cached good response can entail a - bounded observation delay; immediate revocation does not follow [R3]. -- [#440](https://github.com/agentrust-io/trace-spec/issues/440): separately select - authenticated admission-store transport, consistency and recovery assumptions. - Partition, concurrent consume, restart, promotion, rollback and clock-boundary - cases need history-based tests. A linearizable admission object does not make - an unrelated external effect exactly once [R4]. - -These dependencies cannot be marked complete by accepting this informative text. - -## Literature and its application - -- R1: [RFC 9334, RATS Architecture (2023), sections 8 and 10](https://www.rfc-editor.org/rfc/rfc9334.html). - Roles, appraisal policies and freshness inform the trust boundary. -- R2: [RFC 8785, JSON Canonicalization Scheme (2020)](https://www.rfc-editor.org/rfc/rfc8785.html). - Defines the canonicalization antecedent; existing repository code determines - this packet's preimage contract. -- R3: [Samuel et al., Survivable Key Compromise, CCS 2010](https://theupdateframework.io/papers/survivable-key-compromise-ccs2010.pdf). - Timeliness and rollback are distinct trust-maintenance problems; not a TRACE - appraiser-status implementation or mandated delegation scheme. -- R4: [Herlihy and Wing, Linearizability, 1990](https://cs.brown.edu/people/mph/HerlihyW90/p463-herlihy.pdf). - Object-history reasoning motivates admission tests, not external-effect atomicity. -- R5: [Zabolotnii, Runtime Assurance Contracts, v2, 16 October 2026](https://arxiv.org/html/2609.39717v2). - Detector omissions and strong stateful comparisons constrain evaluation claims. - -These sources are prior art, not proof of this proposal's correctness or novelty. -No standardization, hardware run, independent acceptance or issue closure is claimed. diff --git a/tests/test_evidence_resolution_digest.py b/tests/test_evidence_resolution_digest.py new file mode 100644 index 00000000..e4ca1e38 --- /dev/null +++ b/tests/test_evidence_resolution_digest.py @@ -0,0 +1,30 @@ +"""ER01: object identity uses the existing JCS digest, not downloaded JSON bytes.""" + +import copy +import hashlib +import json + +import pytest + +from prototype.verifier_token import canonical_digest + + +@pytest.mark.parametrize("encoded", [ + '{"packet":{"name":"synthetic","count":1},"version":1}', + '{\n "version": 1,\n "packet": { "count": 1, "name": "synthetic" }\n}', + '{"version":1.0,"packet":{"name":"synthetic","count":1.0}}', +], ids=["compact", "whitespace-and-order", "equivalent-numbers"]) +def test_er01_canonical_identity_survives_encoding_but_not_member_change(encoded): + packet = json.loads(encoded) + # Pin the expected preimages without importing the production serializer. + canonical = b'{"packet":{"count":1,"name":"synthetic"},"version":1}' + changed_canonical = b'{"packet":{"count":2,"name":"synthetic"},"version":1}' + expected = "sha256:" + hashlib.sha256(canonical).hexdigest() + assert canonical_digest(packet) == expected + assert "sha256:" + hashlib.sha256(encoded.encode()).hexdigest() != expected + + changed = copy.deepcopy(packet) + changed["packet"]["count"] = 2 + assert canonical_digest(changed) == "sha256:" + hashlib.sha256(changed_canonical).hexdigest() + assert canonical_digest(changed) != expected + assert canonical_digest(packet) == expected From 6690f0c8e5a2828cfc7e8326bf2ec60fe7880d7b Mon Sep 17 00:00:00 2001 From: Srinivasa Consultants <78673153+dinakarjs@users.noreply.github.com> Date: Fri, 23 Oct 2026 21:31:24 +0530 Subject: [PATCH 3/3] test(evidence): pin JCS ordering and exponent spelling Add ER01 controls that reject sorted JSON with integral-float normalization. Clarify proposed processing actors, current non-goals and the matrix scope. Prepared with OpenAI Codex. Signed-off-by: Srinivasa Consultants <78673153+dinakarjs@users.noreply.github.com> --- docs/rfcs/composite-component-appraisal.md | 22 +++++++++++++--------- tests/test_evidence_resolution_digest.py | 12 ++++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/docs/rfcs/composite-component-appraisal.md b/docs/rfcs/composite-component-appraisal.md index 6ef2f4db..c15ae3ac 100644 --- a/docs/rfcs/composite-component-appraisal.md +++ b/docs/rfcs/composite-component-appraisal.md @@ -296,6 +296,8 @@ The checks behind all of this can be run from a checkout: `node tools/independen Informative proposal for [#441](https://github.com/agentrust-io/trace-spec/issues/441). No resolver, wire field or appraisal outcome is implemented by this text. Maintainers still select the profile and local comparison representation. +If this slice is adopted, section 9's first non-goal would change to reflect the +new resolution and re-appraisal scope. #### Claim and trust boundary @@ -316,21 +318,21 @@ A digest mismatch is a binding failure, not proof that the asserted content is f #### Proposed processing obligations -1. Retain the received bytes in the declared private store. Parse unambiguously, +1. The resolver retains the received bytes in the declared private store and parses unambiguously, including duplicate-member refusal, before canonicalization. -2. Check the reference against the existing `canonical_digest(packet)` contract +2. The resolver checks the reference against the existing `canonical_digest(packet)` contract in `prototype/verifier_token.py`: RFC 8785 canonical JSON and the named digest. Whitespace differences alone do not change that object digest. Arbitrary downloaded text hashes are not interchangeable with it [R2]. -3. Independently supply holder/challenge context, execution policy, accepted +3. The relying party independently supplies holder/challenge context, execution policy, accepted roots, platform collateral, TCB floors and evaluation instant. -4. Reuse the authenticated execution path before collateral appraisal. +4. The appraiser reuses the authenticated execution path before collateral appraisal. `prototype/snp_collateral.py::appraise_collateral` explicitly assumes an already-authenticated packet; calling it alone is insufficient. -5. Retain execution and collateral results separately. Compare eligible local +5. The relying party retains execution and collateral results separately and compares eligible local results with the original signed components without rewriting those components. Shared evidence identity does not imply that both appraisals occurred. -6. Preserve original observation/appraisal times. Fetching or serializing the +6. The resolver preserves original observation/appraisal times. Fetching or serializing the packet today does not create new freshness. The existing historical packet can legitimately fall below the selected vendor @@ -346,7 +348,7 @@ boundary, including genuine signatures for semantic-binding cases. | ID | Controlled pair | Required distinction | | --- | --- | --- | -| ER01 (executed) | Same synthetic object, changed whitespace and member order / changed member | Same canonical digest / different digest, checked against a pinned canonical preimage | +| ER01 (executed) | Same synthetic object, changed whitespace, member order and number spelling / changed member; UTF-16 key order and exponent spelling controls | Same canonical digest / different digest, checked against a pinned canonical preimage | | ER02 | Available packet / missing, denied or deleted packet | Appraisal attempted / unavailable evidence | | ER03 | Authenticated expected packet / wrong AK or root | Eligible appraisal / authentication failure | | ER04 | Correct holder and challenge / validly signed wrong context | Correct binding / no applicable appraisal | @@ -358,7 +360,9 @@ boundary, including genuine signatures for semantic-binding cases. ER01 checks the existing helper only: three JSON encodings parse to the same synthetic object and match the SHA-256 of explicitly pinned canonical bytes. -Changing one nested member changes the expected digest. It neither resolves a +Changing one nested member changes the expected digest. Two additional cases pin +UTF-16 key ordering and exponent spelling, distinguishing JCS from sorted JSON +even if whole-valued floats are first normalized to integers. It neither resolves a reference nor authenticates or appraises a platform packet. Run `python -m pytest -q tests/test_evidence_resolution_digest.py`. @@ -408,7 +412,7 @@ These dependencies cannot be marked complete by accepting this informative text. #### Literature and its application - R1: [RFC 9334, RATS Architecture (2023), sections 8 and 10](https://www.rfc-editor.org/rfc/rfc9334.html). - Roles, appraisal policies and freshness inform the trust boundary. + Conceptual messages, appraisal policies and freshness inform the trust boundary. - R2: [RFC 8785, JSON Canonicalization Scheme (2020)](https://www.rfc-editor.org/rfc/rfc8785.html). Defines the canonicalization antecedent; existing repository code determines this packet's preimage contract. diff --git a/tests/test_evidence_resolution_digest.py b/tests/test_evidence_resolution_digest.py index e4ca1e38..1cd6b1f2 100644 --- a/tests/test_evidence_resolution_digest.py +++ b/tests/test_evidence_resolution_digest.py @@ -28,3 +28,15 @@ def test_er01_canonical_identity_survives_encoding_but_not_member_change(encoded assert canonical_digest(changed) == "sha256:" + hashlib.sha256(changed_canonical).hexdigest() assert canonical_digest(changed) != expected assert canonical_digest(packet) == expected + + +@pytest.mark.parametrize(("packet", "canonical"), [ + ({"\ue000": 1, "\U0001f600": 2}, '{"\U0001f600":2,"\ue000":1}'.encode()), + ({"small": 1e-7}, b'{"small":1e-7}'), +], ids=["utf16-key-order", "exponent-spelling"]) +def test_er01_pins_jcs_beyond_sorted_json(packet, canonical): + # Both cases distinguish JCS from sorted JSON even after integral-float normalization. + expected = "sha256:" + hashlib.sha256(canonical).hexdigest() + assert canonical_digest(packet) == expected + sorted_json = json.dumps(packet, sort_keys=True, separators=(",", ":"), ensure_ascii=False) + assert "sha256:" + hashlib.sha256(sorted_json.encode()).hexdigest() != expected