Skip to content

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 #567

Closed

Description

@drk-ram

What is wrong

Contributor: Raghuvar Durga (@drk-ram)

On main (e1560f3), while schema/trace-claim.json and models.py reject private JWK parameters on record["cnf"]["jwk"] (per GHSA-vc4p-h84j-7qxj), the core JWK primitives in both the Python SDK (src/agentrust_trace/sign.py) and the TypeScript SDK (trust-record-verify/src/crypto.ts, trust-record-verify/src/jwk.ts) accept JWK objects carrying private or symmetric key material (d, p, q, dp, dq, qi, oth, k):

  1. sign._pubkey_from_jwk and sign.jwk_thumbprint (src/agentrust_trace/sign.py):

    • _pubkey_from_jwk(jwk) checks kty == "OKP", crv == "Ed25519", and x, but ignores private JWK members (d, p, q, dp, dq, qi, oth, k).
    • Consequently, passing an unredacted private key JWK to:
      • sign.verify_record(record, public_key_or_jwk=jwk_with_d)
      • revocation.check_bundle(bundle, ..., trusted_bundle_keys=[jwk_with_d])
      • intent_bridge.verify_bridge(bridge, trusted_authorizer_jwk=jwk_with_d, ...)
      • intent_bridge.verify_successor_artifact(artifact, trusted_observer_jwk=jwk_with_d, ...)
      • sign.jwk_thumbprint(jwk_with_d)
        silently succeeds instead of failing closed.
  2. importEd25519 and jwkThumbprint (trust-record-verify/src/crypto.ts, trust-record-verify/src/jwk.ts):

    • importEd25519(jwk, role) and jwkThumbprint(jwk) inspect only kty, crv, and x (or e/n / x/y), accepting JWKs that carry private parameters (d, p, q, dp, dq, qi, oth, k) on trustedKey, trustedBundleKeys, or jwkThumbprint.

Reproduction on main (e1560f3):

from agentrust_trace import generate_key, jwk_thumbprint, key_to_jwk, sign_record, verify_record

key = generate_key()
leaked_jwk = {**key_to_jwk(key), "d": "secret-private-scalar"}
rec = sign_record(
    {
        "eat_profile": "tag:agentrust-io.com,2026:trace-v0.2",
        "iat": 1791936000,
        "subject": "spiffe://example.org/agent/demo",
        "model": {"provider": "example", "model_id": "demo"},
        "runtime": {"platform": "software-only", "measurement": "sha256:" + "0" * 64},
        "policy": {"bundle_hash": "sha256:" + "b" * 64, "enforcement_mode": "declared"},
        "data_class": "internal",
        "build_provenance": {"slsa_level": 1, "digest": "sha256:" + "e" * 64},
        "appraisal": {"status": "none", "verifier": "https://verifier.example.org"},
    },
    key,
)

# Both accept the JWK carrying private key material 'd':
jwk_thumbprint(leaked_jwk)
verify_record(rec, leaked_jwk, now=1791936000)

Spec section or file

  • src/agentrust_trace/sign.py (_pubkey_from_jwk, jwk_thumbprint)
  • trust-record-verify/src/crypto.ts (importEd25519)
  • trust-record-verify/src/jwk.ts (jwkThumbprint)

Expected behavior

  1. sign._pubkey_from_jwk and sign.jwk_thumbprint should reject any JWK containing RFC 7517 / RFC 7518 / RFC 8037 private or symmetric key parameters (d, p, q, dp, dq, qi, oth, k) with ValueError, naming only the parameter key(s) and never echoing the secret value.
  2. trust-record-verify importEd25519 and jwkThumbprint should likewise reject any JWK with own properties in {"d", "p", "q", "dp", "dq", "qi", "oth", "k"} (malformed for importEd25519, jwk_invalid for jwkThumbprint), naming only the parameter key(s).

Impact

Prevents unredacted private key JWKs from being accepted as verification keys or thumbprinted across sign.verify_record, revocation.check_bundle, intent_bridge.verify_bridge, intent_bridge.verify_successor_artifact, and trust-record-verify.

Activity

  1. added a commit that references this issue on Oct 20, 2026
    8035c15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions