Skip to content

sign,trust-record-verify: reject private JWK members in thumbprint and key import (#567) - #574

Merged
imran-siddique merged 1 commit into
agentrust-io:mainfrom
drk-ram:drk-ram-fix-jwk-private-material-sign-and-ts
Oct 19, 2026
Merged

imran-siddique merged 1 commit into
agentrust-io:mainfrom
drk-ram:drk-ram-fix-jwk-private-material-sign-and-ts

Conversation

@drk-ram

@drk-ram drk-ram commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

What this changes

Contributor: Raghuvar Durga (@drk-ram)
Closes #567

While schema/trace-claim.json and models.py already reject private JWK parameters on record["cnf"]["jwk"] (per GHSA-vc4p-h84j-7qxj), the JWK thumbprint and public-key reconstruction primitives in both the Python SDK (src/agentrust_trace/sign.py) and the TypeScript SDK (trust-record-verify/src/jwk.ts, trust-record-verify/src/crypto.ts) previously inspected only public parameters (kty, crv, x, y, e, n) and silently ignored private or symmetric JWK members (d, p, q, dp, dq, qi, oth, k).

This change updates:

  • sign.jwk_thumbprint and sign._pubkey_from_jwk (src/agentrust_trace/sign.py) to reject any JWK containing RFC 7517 / RFC 7518 / RFC 8037 private or symmetric key members (d, p, q, dp, dq, qi, oth, k) with ValueError, naming only the offending member keys in sorted order and never echoing the secret material.
  • jwkThumbprint (trust-record-verify/src/jwk.ts) and importEd25519 (trust-record-verify/src/crypto.ts) to reject any JWK carrying private key members with jwk_invalid and the role's malformed code (trusted_key_malformed, cnf_key_malformed, or invalid_argument), respectively, without echoing secret values.
  • trust-record-verify/differential/oracle.py (KEY_MESSAGES) to classify "JWK contains private key material" as malformed so differential verification remains aligned across Python and TypeScript.

Independent review noted that error messages rejecting private JWKs must never include jwk[m] secret values and that revocation.check_bundle reaches jwk_thumbprint via _trusted_bundle_key -> _key_identifiers before _pubkey_from_jwk, so both jwk_thumbprint and _pubkey_from_jwk are tested directly and through verify_record, check_bundle, verify_bridge, and verify_successor_artifact with a sentinel secret assertion.

Type of change

  • Editorial (typo, link fix, clarification: no normative effect)
  • Non-breaking spec change (new optional field, new platform profile, informative addition)
  • Breaking spec change (requires 14-day comment period and Project Lead sign-off)
  • Schema change
  • Example addition

Spec section

Reference SDKs (src/agentrust_trace/sign.py, trust-record-verify/src/jwk.ts, trust-record-verify/src/crypto.ts) implementing spec/trace-v0.2.md section 3.2.2 and section 3.2.3 JWK handling.

Tests for a fix

python tools/review_bar.py --base origin/main --head HEAD reports 49 tests failing on behaviour against origin/main (e1560f3) and passing on HEAD:

  • tests/test_sign_jwk_private_boundary.py::test_jwk_thumbprint_rejects_private_key_material_without_echoing_secret[d..k] (8 cases)
  • tests/test_sign_jwk_private_boundary.py::test_pubkey_from_jwk_rejects_private_key_material_without_echoing_secret[d..k] (8 cases)
  • tests/test_sign_jwk_private_boundary.py::test_verify_record_rejects_trusted_jwk_carrying_private_material[d..k] (8 cases)
  • tests/test_sign_jwk_private_boundary.py::test_check_bundle_rejects_trusted_bundle_key_carrying_private_material[d..k] (8 cases)
  • tests/test_sign_jwk_private_boundary.py::test_verify_bridge_rejects_trusted_authorizer_jwk_carrying_private_material[d..k] (8 cases)
  • tests/test_sign_jwk_private_boundary.py::test_verify_successor_artifact_rejects_trusted_observer_jwk_carrying_private_material[d..k] (8 cases)
  • tests/test_sign_jwk_private_boundary.py::test_multiple_private_jwk_members_are_listed_in_sorted_order (1 case)
  • trust-record-verify/tests/units.test.mjs: jwkThumbprint and verifyRecord refuse private JWK members without echoing secret values

Validation commands executed:

  • pytest tests/test_sign_jwk_private_boundary.py tests/test_sign.py tests/test_cnf_jwk_carries_key_material.py tests/test_provenance_cnf_boundary.py tests/test_intent_bridge.py tests/test_revocation_bundle.py tests/test_public_functions_raise_what_they_document.py -> passed (628 passed)
  • npm --prefix trust-record-verify run build && npm --prefix trust-record-verify test -> passed (119 passed)
  • trust-record-verify differential suite (generate_cases.py, oracle.py, run.mjs, compare.py --expect-external 12) -> passed (1644 cases, 1643 identical, 1 known divergence, 0 unexpected)
  • python tools/review_bar.py --base origin/main --head HEAD -> passed (49 fail on behaviour on base, 0 fail on missing name)
  • python tools/check_dashes.py -> passed
  • ruff check src/agentrust_trace/sign.py trust-record-verify/differential/oracle.py tests/test_sign_jwk_private_boundary.py -> passed
  • mypy --strict src/agentrust_trace/sign.py tests/test_sign_jwk_private_boundary.py -> passed

Checklist

  • DCO sign-off on all commits (git commit -s)
  • CHANGELOG.md updated (for any normative change)
  • Breaking changes marked with <!-- CHANGED: #NNN: description --> in spec text
  • Backward compatibility statement included (for breaking changes)

…d key import

Reject RFC 7517, RFC 7518, and RFC 8037 private or symmetric JWK members
(d, p, q, dp, dq, qi, oth, k) in sign.jwk_thumbprint, sign._pubkey_from_jwk,
trust-record-verify jwkThumbprint, and trust-record-verify importEd25519,
naming only the offending member keys without echoing secret values.

Closes agentrust-io#567

Signed-off-by: Raghuvar Durga <177662415+drk-ram@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@drk-ram
drk-ram requested review from a team, lywinged and rajnisht7 as code owners October 19, 2026 03:49

@dinakarjs dinakarjs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent review at 48510f0. Both Python public-key primitives reject the complete eight-member private/symmetric set before constructing the thumbprint/key. The TypeScript thumbprint and Ed25519-import paths use the same member set and preserve role-specific error codes; errors name members, not secret values. Public extensions remain allowed.

Linux/Python 3.12.14 with this head's hash-locked dev/conformance dependencies: 628 selected signing, provenance, cnf, bridge, revocation and public-error tests pass. npm ci from the committed lock and npm test: 119 pass. An independent 80-case sweep covers null/empty/list/object/sentinel values for all eight members through both Python primitives, with a passing public-extension twin and no sentinel in errors. Returned technical CI succeeds; Dependabot is skipped. DCO present. Nonblocking formatting nit: units.test.mjs has a new blank line at EOF (git diff --check); no source correction retained. This is bounded public-key-input validation review, not a broader security certification. Reviewed with OpenAI Codex assistance.

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @drk-ram, and @dinakarjs for running both suites. With #541 this closes both sides: records and schemas refuse private members, and so do the thumbprint and key-import helpers in Python and TypeScript. Non-blocking: sign.py now has its own copy of the private-member set; a follow-up could have provenance and models import it from one place.

@imran-siddique
imran-siddique merged commit 8035c15 into agentrust-io:main Oct 19, 2026
18 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sign,trust-record-verify: reject private JWK members (d, p, q, dp, dq, qi, oth, k) in _pubkey_from_jwk, jwk_thumbprint, importEd25519, and jwkThumbprint

3 participants