feat(signature): Ed25519 and Ed448 fully-specified algorithms, X448 for ECDH-ES (RFC 9864, RFC 8037) - #734
Merged
Merged
Conversation
…or ECDH-ES (RFC 9864, RFC 8037) RFC 9864 registers the fully-specified "Ed25519" and "Ed448" signature algorithms and deprecates the polymorphic "EdDSA": the name of the algorithm alone must say which curve is in use. The library only knew "EdDSA", on Ed25519 through ext-sodium, and refused the Ed448 and X448 keys its converters already read. AbstractEdDSA carries PureEdDSA (RFC 8037 section 3.1): backend choice, signature length check before any backend is called so that a malformed signature is false and never an exception, and the curve check. Ed25519 and Ed448 each accept their own curve and refuse the other with an InvalidKeyException. EdDSA stays, restricted to Ed25519 as it always was, throws the same UnsupportedCurveException as before on another curve, and is deprecated: signing with it raises the deprecation, verifying the tokens already in circulation does not, and its removal is a 5.0.0 candidate (#717). ext-sodium knows neither Ed448 nor X448. Both run on ext-openssl, which PHP exposes for these curves since 8.4 only: openssl_sign() refuses the null digest and openssl_pkey_get_details() returns no raw material before that version. Ed448 and X448 are therefore gated on the PHP version, not on the OpenSSL one: isSupported() tells, the constructor throws a MissingDependencyException, and the bundle registers the algorithm only when it can run. Ed25519 and X25519 keep sodium when it is loaded and fall back to OpenSSL on PHP 8.4 otherwise. OKPKey centralises the generation, the Diffie-Hellman and the OpenSSL signature primitives that JWKFactory and AbstractECDH duplicated; the OpenSSL variants are public so that the OpenSSL path of the 25519 curves is testable when sodium is loaded. AbstractECDH accepts X448 wherever X25519 was, in every ECDH-ES and ECDH-SS variant. A new OKPKeyAnalyzer checks the curve, the x and d sizes and the consistency of alg and crv; AlgorithmAnalyzer reports "alg": "EdDSA" as deprecated with the fully-specified replacement. Backward compatibility was verified: the reflection dump of the public API only shows additions, and the behavioural probes run against 4.3.x show no call that worked and now throws. Verified on PHP 8.2, 8.3 and 8.5. Closes #724
Spomky
added a commit
to web-token/jwt-doc
that referenced
this pull request
Sep 13, 2026
…64) (#52) web-token/jwt-framework#734 adds the fully-specified Ed25519 and Ed448 algorithms, X448 for the ECDH-ES family, and deprecates EdDSA. The algorithm and curve tables gain the new entries with the PHP 8.4 note, the signature algorithms page gets a section with the three-step migration away from EdDSA, the key factory and console pages list the four OKP curves, the prerequisites say when sodium is still needed, the signed-token example moves to Ed25519, and the 4.2 → 4.3 guide carries the feature entry and the deprecation line.
This was referenced Sep 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #724
What
RFC 9864 registers the fully-specified
Ed25519andEd448signature algorithms and deprecates the polymorphicEdDSA. This PR ships both, deprecatesEdDSA, and closes the RFC 8037 gap on the 448 curves (Ed448 signatures, X448 key agreement).Signature —
Jose\Component\Signature\AlgorithmAbstractEdDSA: PureEdDSA (RFC 8037 §3.1) — backend choice, signature length check before any backend is called (a malformed or tampered signature isfalse, never an exception), curve check.Ed25519: sodium when loaded, OpenSSL otherwise on PHP ≥ 8.4.Ed448: OpenSSL only, PHP ≥ 8.4.Ed448::isSupported(); the constructor throwsMissingDependencyExceptionwhen the platform cannot run it.InvalidKeyException.EdDSA:@deprecated, restricted to Ed25519 as before, sameUnsupportedCurveExceptionas before on another curve.trigger_deprecationonsign()only — issuing newEdDSAtokens is what RFC 9864 asks to stop; verifying the tokens in circulation is legitimate, and the bundle instantiates the algorithm on every container build, so a constructor deprecation would have been noise for everyone. Removal is a 5.0.0 candidate (5.0.0: remove what 4.3 deprecated, seal what it announced #717).Why the PHP 8.4 gate
ext-sodium knows neither Ed448 nor X448. PHP exposes these curves through ext-openssl since 8.4 only:
openssl_sign()refuses the null digest andopenssl_pkey_get_details()returns no raw key material before that version. The gate isPHP_VERSION_ID >= 80400 && extension_loaded('openssl'), not the OpenSSL version. Verified on OpenSSL 3.0.13.Plumbing —
OKPKey(@internal)Centralises what
JWKFactoryandAbstractECDHduplicated:generate(),deriveSharedSecret(),signWithOpenSSL()/verifyWithOpenSSL(),supportsOpenSSL(),supportsSodium($curve),isCurveSupported(), key sizes per curve. The…WithOpenSSL()variants are public so the OpenSSL path of the 25519 curves is testable when sodium is loaded.Encryption
AbstractECDHacceptsX448whereverX25519was (derivation, ephemeralepk, curve check) — everyECDH-ES*andECDH-SS*variant inherits it. X25519 falls back to OpenSSL without sodium on PHP 8.4.Keys, analyzers, console, bundle
JWKFactory::okp()delegates toOKPKey::generate()— JSON member order unchanged.OKPKeyAnalyzer(known curve,x/dsizes,alg/crvconsistency);AlgorithmAnalyzerreportsalg: EdDSAas deprecated with the replacement for the curve.key:generate:okp/keyset:generate:okpdocument the four curves.signature_eddsa.phpregistersEd25519+EdDSAwhenEd25519::isSupported(),Ed448whenEd448::isSupported();SignatureSourceloads the file when either is;OKPKeyAnalyzerwired.Acceptance criteria
Ed25519andEd448throughJWSBuilder/JWSVerifier, compact, flattened and general ✔ (sodium and OpenSSL paths for Ed25519,OKPKeyTest)EdDSAandEd25519— and the signature is reproduced ✔; two RFC 8032 §7.4 Ed448 vectors verified and reproduced ✔ECDH-ES,+A128KW,+A256KW,ECDH-SS,ECDH-SS+A256KW) ✔; RFC 7748 §6.2 X448 vector reproduced, checked down to the Concat KDF output ✔ (§6.1 X25519 vector too)Ed25519refusescrv: Ed448and vice versa;alg: EdDSAkey refused byEd25519and accepted byEdDSA, and the reverse ✔false✔Ed448::isSupported()false, constructor throws, bundle compiles,Ed25519works through sodium ✔ — full suite run inphpqa:8.2andphpqa:8.3with their ownvendor(29 skips), green.Backward compatibility
EdDSAstaysfinal, still implementsSignatureAlgorithm, same five public methods.EdDSA::sign()+1 deprecation (intended); a 63-byte Ed25519 signature was aSodiumExceptionand is nowfalse;sign()with a key withoutxnow works (derived); X448 now works; three messages change at identical exception class (The EC key is not private→The OKP key is not private,Invalid key parameter "x"→Unable to get the "x" parameter);AlgorithmAnalyzerreports one more message foralg: EdDSA.QA
PHPUnit (8.2, 8.3, 8.5), ECS, Rector, Deptrac, PHPStan green. The PHPStan baseline shrinks by three entries (
substrinJWKFactory/AbstractECDH,return.typeofEdDSA) and gains theEdDSA::classaccess in the config file, same pattern asNone/RSA15.Out of scope
Removing
EdDSA(5.0.0, #717); fully-specified ECDH-ES variants (none registered, RFC 9864 §6.2); Ed25519ph / Ed25519ctx / Ed448ph (RFC 8037 §3.1).Documentation: web-token/jwt-doc.