Friday, 16 October 2026
1 agent hack today▼ 7 vs yesterday (8)

PyJWT accepts mismatched Ed25519 key pairs in JWK imports

A flaw disclosed on 5 October 2026 lets a crafted JSON Web Key pair one user's public key with an attacker's private key, breaking DPoP token binding.

By The Agentic Times ·

A locksmith hands a stranger a key cut from someone else's lock, unknowingly pairing mismatched tumblers that now open doors to restricted rooms.
A locksmith hands a stranger a key cut from someone else's lock, unknowingly pairing mismatched tumblers that now open doors to restricted rooms. · Illustration: The Agentic Times

PyJWT, the widely used Python library for JSON Web Tokens, accepts internally inconsistent OKP private keys, according to a GitHub Security Advisory published on 5 October 2026. The flaw, tracked as CVE-2026-102275 and GHSA-x33g-cr3x-6449, means the library can perform cryptographic operations with one key while the key document declares a different public identity. In affected DPoP integrations, the advisory says, this can allow a stolen sender-constrained access token to be used without possession of the legitimate holder's private key.

OKP is the JSON Web Key type used for Edwards-curve keys such as Ed25519 and Ed448. A private OKP key document carries both a public parameter, x, and a private parameter, d. The two are supposed to belong to the same key pair, because the public key can be derived from the private one.

According to the advisory, PyJWT's OKPAlgorithm.from_jwk() does not check that relationship. When d is present, the library parses x and then ignores it, building the private key purely from d. A key document that pairs a legitimate user's x with an attacker's d is accepted without complaint. The advisory states that a correct import should derive the public key from d and reject the document when it does not match the supplied x.

The practical impact described in the advisory concerns DPoP, the mechanism defined in RFC 9449 that binds an access token to a particular client key so that a stolen token cannot be replayed elsewhere. A resource server checks that the key in the proof matches a thumbprint recorded in the token's cnf.jkt claim. Because the RFC 7638 thumbprint is computed from crv, kty and x only, the attacker's substituted d does not change it.

The published proof of concept generates two Ed25519 key pairs, one for a legitimate holder and one for an attacker. It issues an access token bound to the legitimate public key, then builds a DPoP proof whose header key contains the legitimate x alongside the attacker's d. The thumbprint still matches the token binding. When the full key document is passed to PyJWK.from_dict(), PyJWT imports the attacker's private key, and a proof signed with that key verifies successfully. The reproducer reports that the stolen sender-constrained token is accepted while the attacker does not hold the legitimate private key.

The advisory says the behaviour has existed since private OKP key import was introduced: from PyJWT 2.1.0 for Ed25519 and 2.2.0 for Ed448. It was confirmed through PyJWT 2.14.0 and current master, with testing on 2.13.0, 2.14.0 and master.

The reproducer includes an optional server-side check that rejects any DPoP header key containing private members such as d, p, q or k. That check is modelled as a defence in the test harness rather than described as a vendor fix. No patched release is identified in the advisory, and the issue is listed as reported.

Teams running PyJWT in token-binding roles may want to review whether their own code or framework accepts private key material from untrusted inputs.

Sources

  1. github.comhttps://github.com/advisories/GHSA-x33g-cr3x-6449