Repository navigation
fix(verifiers): enforce canonical base64url, reject private JWKs and duplicate JSON keys in independent_verify and guard request_sha256 in companion_v0 - #364
Conversation
|
🟡 Contributor Check: MEDIUM
Automated check by AgenTrust Contributor Check. |
dinakarjs
left a comment
There was a problem hiding this comment.
Reviewed exact head 346beed (full commit attached). Checked independent TRACE signature verification against the explicit out-of-band anchor, canonical key/signature decoding and duplicate-member loading, plus the companion's malformed request digest handling without accidental rank/dedup lookups. Focused local tests: independent verifier 33 passed; companion 86 passed. Hosted sovereign-agent-connector and companion conformance jobs succeed. The overall Examples failure is in unrelated weight-custody-manifest: missing OpenSSL during collection (job 120225017589). Link check fails on two existing Nobulex links returning HTTP 451 (job 120225049655). Those are unresolved integration-wide checks, not passing evidence. No blocking source finding in these changed paths.
carloshvp
left a comment
There was a problem hiding this comment.
Reviewed exact head 346beed26423c421b7bbfa75c0e0e0ad60ce4e79 in an isolated checkout.
Approving the scoped verifier hardening. The independent TRACE verifier enforces canonical unpadded base64url, rejects private/symmetric JWK members and duplicate JSON keys, and binds the embedded public key to the explicit out-of-band anchor. The companion rejects malformed request_sha256 values without using them as dictionary keys or reporting a successful receipt.
Independent macOS/Python 3.12 validation: all 74 sovereign-connector tests and 86 companion tests pass; the independent positive bundle CLI, CI-selected Ruff and compilation checks pass. Additional controls pass 80 canonical byte round trips and 522 negative cases. The private-JWK cases were freshly signed by the pinned test key, so rejection is not merely caused by an invalid signature; duplicate-key controls include nested and escaped-equivalent names.
These checks preserve the documented software-only trust boundary. The failed hosted links job identifies the unchanged Nobulex URL returning 451, and the WCM job reports the inherited missing OpenSSL import. #357 and #362 carry those separate repairs. The normal current-head CI and approval gates remain applicable. The commit carries DCO sign-off.
Reviewed with OpenAI Codex.
There was a problem hiding this comment.
Reviewed the head 346beed, the independent_verify hardening looks good canonical base64url, private/symmetric JWK rejection, length checks, and duplicate-key loading are correct and well tested.
Two issues on the companion side:
- Provenance is inconsistent - README still says the reference verifier is “unchanged” and points at horizon-shield 05b4924. The actual file at that commit has SHA a6e2b2c9… and the original unguarded requests.get(it["request_sha256"]). This PR changes the file (new guards + new SHA fc53665c…). The vendored test only checks the local hard-coded SHA, so it doesn’t catch the mismatch. Either land the guard upstream and bump SOURCE_COMMIT + SHA + README, or drop the claim for the file is an unchanged byte-for-byte copy.
- README still says Expected: 83 passed. Suite is now 86.
non-blocking: the new request_sha256 regression test only hits the guard via monkeypatch. parse_item already rejects non-HEX32 values, so a normal external malformed input never reaches the settle path. The guard is still good defense-in-depth, but the test doesn’t demonstrate the original external TypeError claim.
…duplicate JSON keys in independent_verify and guard request_sha256 in companion_v0 - Enforce canonical unpadded base64url in decode_b64url (rejecting padding, non-alphabet characters, len % 4 == 1, and non-zero trailing bits). - Validate TRACE cnf.jwk in verify_trace_signature, rejecting RFC 7517 / RFC 7518 / RFC 8037 private or symmetric JWK parameters (d, p, q, dp, dq, qi, oth, k) and requiring 32-byte Ed25519 keys and 64-byte signatures. - Reject duplicate JSON object member names via object_pairs_hook in load_json. - Guard request_sha256 in companion_v0.settle so non-string or unhashable values fail closed with bad_request_sha256 instead of raising TypeError, and document the local defense-in-depth provenance against upstream horizon-shield 05b4924 (a6e2b2c9...) and updated 86-test suite count. Closes agentrust-io#363 Signed-off-by: Raghuvar Karthik Durga <177662415+drk-ram@users.noreply.github.com>
346beed to
4327773
Compare
|
@rajnisht7 @carloshvp Rebased onto
|
carloshvp
left a comment
There was a problem hiding this comment.
Reviewed current head 43277730a9878e1a41e9497346de6aa60dfb27e7 in an isolated checkout.
The independent TRACE verifier checks canonical encodings, exact Ed25519 sizes, public-only JWKs and the explicit trust-anchor binding. The companion validates request digests before dictionary lookup and no longer claims its locally modified verifier is byte-identical to upstream; unchanged generator/vector provenance is retained. All 74 sovereign connector tests, 86 companion tests and 60 root tests pass.
The repository CI Ruff selection and whitespace checks pass; all PR commits carry DCO sign-offs. This approves the bounded software scope. Current-head hosted checks and maintainer requirements remain applicable.
Reviewed with OpenAI Codex.
What this changes
Closes #363
Hardens the independent AUCP/TRACE verifier (
examples/sovereign-agent-connector/tools/independent_verify.py) and the cA2A companion verifier (integrations/horizonshield-ca2a-companion/vendor/ca2a-companion-v0/companion_v0.py) so malformed inputs fail closed:examples/sovereign-agent-connector/tools/independent_verify.py:decode_b64url: enforces canonical unpadded base64url (^[A-Za-z0-9_-]+\Z,len(value) % 4 != 1, and round-trip re-encoding equality), raisingVerificationErroron=padding, standard base64+//characters, whitespace, or non-zero trailing bits.verify_trace_signature/verify_ed25519: validatestrace.cnf.jwk(kty == "OKP",crv == "Ed25519"), rejects RFC 7517 / RFC 7518 / RFC 8037 private or symmetric JWK parameters (d,p,q,dp,dq,qi,oth,k), verifiescnf.jwk.xmatches the pinned trust anchor, and requireslen(key_bytes) == 32andlen(sig_bytes) == 64.load_json: rejects duplicate JSON object member names viaobject_pairs_hook, raisingVerificationError.integrations/horizonshield-ca2a-companion/vendor/ca2a-companion-v0/companion_v0.py:request_sha256with_is(HEX32, req_sha)before dict lookups insettle(requests.get(...),rank, andexecuted), failing closed withbad_request_sha256(disposition: "outside") rather than raisingTypeError: unhashable typeonlistordictvalues.Checked that
companion_v0.pypreserves LF line endings and updated the provenance note and pinned SHA-256 intests/test_ca2a_companion_vendored.pyandREADME.mdso the vendored-file integrity and vector-rebuild tests pass alongside the new regression tests.Type of change
Spec section
examples/sovereign-agent-connector/tools/independent_verify.pyandintegrations/horizonshield-ca2a-companion/vendor/ca2a-companion-v0/companion_v0.py.Tests for a fix
examples/sovereign-agent-connector/tests/test_independent_verifier.py:test_decode_b64url_rejects_padding_non_alphabet_and_non_canonical_trailing_bits(fails whendecode_b64urlhardening is switched off)test_verify_trace_signature_rejects_private_and_symmetric_jwk_parameters(fails whenverify_trace_signatureJWK validation is switched off)test_verify_trace_signature_enforces_ed25519_key_and_signature_lengths(fails when key/signature byte-length checks are switched off)test_load_json_and_cli_reject_duplicate_json_keys(fails whenload_jsonobject_pairs_hookis switched off)integrations/horizonshield-ca2a-companion/tests/test_ca2a_companion_gaps.py:test_settle_guards_unhashable_request_sha256_without_type_error(fails withTypeError: unhashable typewhen therequest_sha256guard incompanion_v0.settleis switched off)test_load_strict_rejects_duplicate_json_keysValidation commands and results
pytest examples/sovereign-agent-connector/tests -q-> passed (74 passed)pytest integrations/horizonshield-ca2a-companion/tests -q-> passed (86 passed)pytest tests -q-> passed (60 passed)ruff check integrations packages scripts tests claude-code copilot plugins scheduled-agents shadow-ai examples/sovereign-agent-connector --target-version py39 --select E4,E7,E9,F-> passed (All checks passed!)python examples/sovereign-agent-connector/tools/independent_verify.py examples/sovereign-agent-connector/vectors/french-healthcare/cmcp-live-bundle.json --trust-anchors examples/sovereign-agent-connector/vectors/french-healthcare/cmcp-live-trust-anchors.json-> passed (PASS: AUCP MVE-1 bundle independently verified)Checklist
git commit -s)<!-- CHANGED: #NNN: description -->in spec text