Repository navigation
Conversation
Add normative verifier rules for signature verification order, state isolation, constant-time comparison, missing signature rejection, and non-negotiated algorithm rejection. Align schema optionality with ACS-Core conformance requirements. Refs GenAI-Security-Project#315, GenAI-Security-Project#316 Signed-off-by: Andrey Lesnikov <52860508+justrunme@users.noreply.github.com>
|
Thanks for this. It is queued rather than ignored. This pull request does not reference an issue carrying If this is an editorial correction, tick that box in the description and this comment stops applying. |
drk-ram
left a comment
There was a problem hiding this comment.
I merged e1b2fca onto origin/integration at d09e1bd. 1b8f970 is an ancestor of that tip. The merge is clean.
python -m pytest -q on the merge gave 1169 passed, 10 failed, 49 errors, and 1 skipped. tests/test_verifier_rules.py itself was 13 passed. The 10 failures and 49 errors are this Windows machine. gh is not a Win32 application, symlink creation is denied, python3 is missing from the shebang tests, and the theme partials do not match the installed copy. The same 10 and 49 show up on the other three merges. This diff does not touch those files.
I then broke the new text and reran that file.
Deleting the atomic reservation sentence still passed, 13 tests. The test looks for the word atomic anywhere, and the batching roadmap already says atomicity. Deleting the constant-time MUST sentence, and leaving the heading, still passed. The heading contains Constant-time. Rewording "no fallback" still passed, because the key_id paragraph already says MUST NOT fall back to another secret. Restoring the empty-array exemption failed one test. Renaming intersection in the schema failed one test. Renaming the four state kinds failed one test.
Two spec problems should be fixed before this lands.
Section 10.1 still says signatures are already field-optional. Rule 3 says a missing signature MUST be rejected with SIGNATURE_INVALID. Issue 316 asked for both the rule and the fix to that sentence. Pull request 339 rewrites the sentence and merges cleanly with this branch, but 339 is not on integration. Landing this alone leaves the contradiction in the section this pull request edits. Drop that clause here, or stack on 339. Keep signature optional in the JSON Schema. The exception for system/ping and an unresolvable key_id is the right reason, and your schema test already pins that.
Rule 2 applies regardless of algorithm, then requires a comparison of decoded signature bytes. That is the right check for HMAC-SHA256. ECDSA-P256, RSA-PSS-SHA256, and ML-DSA do not compare a signature to an expected value. They verify an equation. A literal reading makes those verifiers non-conformant. Limit the byte comparison to MAC tags. For the asymmetric algorithms, say the verifier must not compare the base64 wire text, and must not leak the key through an early exit.
One smaller ambiguity in rule 1. The atomic reservation is "after verification succeeds". The sentence before it withholds state changes until both the signature check and the timestamp check pass. Say "after both checks pass". Otherwise a skewed but well-signed request can burn a request_id.
The Observed Agent clause says it returns SIGNATURE_INVALID. The agent refuses the response locally. It has nowhere to return that code. Say it refuses the response.
The schema change is otherwise fine. ClientHello.signature_algorithms_supported is optional, the enums match the request envelope, and the empty-array exemption is gone from the ServerHello description. The omitted-field baseline of HMAC-SHA256 only is a choice you already flagged. I am not asking to reopen it. The rule sits in section 10, which is the right altitude for a verifier mechanism.
Please pin the atomic sentence, the decoded-byte comparison, and the non-negotiated no-fallback sentence with phrases that do not already appear elsewhere. The three breaks above are the ones the file does not catch.
Scope constant-time comparison to MAC tags, reserve request_id after both checks pass, have the Observed Agent refuse a bad response locally, align 10.1 with the missing-signature rule, and pin the normative phrases in tests. Signed-off-by: Andrey Lesnikov <52860508+justrunme@users.noreply.github.com>
3dbadd2 to
8907c53
Compare
|
Thanks for the careful review, and for running the mutations. Pushed 8907c53, all checks are green.
|
drk-ram
left a comment
There was a problem hiding this comment.
Re-verified at head 8907c53bcf07be2740a5c3d919994ffda01c581e, which is the head I am approving.
Tests. python -m pytest tests/test_verifier_rules.py -q on a clean checkout of 8907c53: 13 passed. A merge-tree against current origin/integration (bba1b84) is clean. The schema, handshake and spec-related selection (-k "handshake or schema or spec or verifier or openapi or conform") gives 88 passed, 1 skipped and 2 failures. Both failures are the symlink tests in tests/test_publish_schemas.py, which cannot create symlinks without elevation on this Windows machine and do not touch this diff.
Each finding from my last review, against the new text:
- Rule 1 now says the check-and-reserve of the
request_idis one atomic operation performed after both checks pass, and states that a skewed but validly signed request cannot burn an ID. Resolved. - Rule 2 limits the constant-time decoded-byte comparison to MAC tags (HMAC-SHA256). For ECDSA, RSA-PSS and ML-DSA it forbids comparing base64 wire text and forbids leaking key or secret material through an early exit or data-dependent timing. A verifier that runs an ordinary signature-verification equation is no longer non-conformant by a literal reading. Resolved.
- Rule 4 now has the Observed Agent refuse the response locally rather than "return"
SIGNATURE_INVALID. Resolved. - §10.1 no longer says signatures are "already field-optional". The replacement sentence is identical, character for character, to the one in #339, so the two branches converge without a conflict in either order. The JSON Schema keeps
signatureoptional, with thesystem/pingand unresolvable-key_idexemption stated in the text. Resolved. - The three sentences I could break without a failing test are now pinned. I deleted each one individually and reran the file: the "one atomic operation performed after both checks pass" sentence now fails 1 test, the "with no fallback to another algorithm or key" sentence fails 1 test, and the "verifiers MUST compare the decoded signature bytes in constant time" sentence fails 1 test. The phrases are unique in the spec, so the earlier false passes are gone.
Residual, non-blocking. Three clauses are still unpinned: the base64 wire-text prohibition for asymmetric algorithms, the "cannot burn an ID" clause, and the Observed Agent's local refusal. Deleting each still leaves 13 passing. They are normative text, so a short follow-up test for each would close the gap, but I would not hold this PR for it.
Schema. ClientHello.signature_algorithms_supported is optional, its enum matches the request envelope, the omitted-field baseline of HMAC-SHA256 only is documented in both places, and the empty-array exemption is gone from the ServerHello description. The handshake-time asymmetry (the Guardian verifies the ClientHello against its own set; the Observed Agent verifies the ServerHello against the client's set or the baseline) is spelled out, which closes a bootstrapping ambiguity I had not raised.
For anyone reading the verifier rules against the evidence layer, draft-durga-agenttrust-trace-verification-00 (work in progress) uses the same discipline of rejecting before any state transition; this PR states it at the right altitude for section 10.
Approving.
What changed
Adds a normative "Verifier rules" block to §10 and a client-side algorithm list to the handshake, following the discussion with @bar-capsule in both issues.
key_idresolution come first. Until the signature and timestamp checks pass, a request changes no session, replay, audit-chain or authorization state, and policy does not evaluate it. Security logs and rate-limit counters outside the session chain stay allowed. The replay check and therequest_idreservation are one atomic step after verification.SIGNATURE_INVALID, exceptsystem/pingand error responses where no signing key can be resolved. I left the §10 intro and the §10.1 "field-optional" sentence untouched, since docs(spec): reconcile ACS-Core signature requirement with schema optionality in §10 #339 rewrites them. The branch merges with docs(spec): reconcile ACS-Core signature requirement with schema optionality in §10 #339 without conflicts.SIGNATURE_INVALID, with no fallback.ClientHellogets an optionalsignature_algorithms_supportedlist. The negotiated set for both directions is the intersection of the client and server lists. The "empty array means no signatures" meaning is dropped from theServerHellodescription. The text also says what is verified during the handshake itself.Which issue does this implement
Refs #315
Refs #316
meaning, so it needs no issue
Base branch
integration, because this touches the specification, schemas, a referenceimplementation, an adapter, tests, or CI
main, because every changed path is on the documentation lane allowlist inCONTRIBUTING.md
Type of change
Specification changes
Breaking for implementers? Possibly for verifiers that accepted unsigned envelopes or fell back to a non-negotiated algorithm; those now get
SIGNATURE_INVALID. The newClientHellofield is optional.Open points for review
signature_algorithms_supported, I defined the negotiated set as the ACS-Core baselineHMAC-SHA256only. That is a choice, not something the issues settled.I tested this
uv run pytest -vpasses on my machineuv run mkdocs build --strictpasses on my machinetests/test_verifier_rules.pychecks the schema change and the normative text. This repository has no reference verifier, so there are no runtime tests for constant-time comparison, atomic replay reservation or state isolation. Those belong with the Guardian implementation. The full test suite andmkdocs build --strictpass.Checklist
git commit -s(required by the DCO)Security
It tightens signature verification in §10: verification order, state isolation before verification, constant-time comparison, and rejection of missing or non-negotiated signatures.