Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(signature): Ed25519 and Ed448 fully-specified algorithms, X448 for ECDH-ES (RFC 9864, RFC 8037) - #734

Merged
Spomky merged 1 commit into
4.3.xfrom
feat/ed25519-ed448-x448
Sep 13, 2026
Merged

feat(signature): Ed25519 and Ed448 fully-specified algorithms, X448 for ECDH-ES (RFC 9864, RFC 8037)#734
Spomky merged 1 commit into
4.3.xfrom
feat/ed25519-ed448-x448

Conversation

@Spomky

@Spomky Spomky commented Sep 13, 2026

Copy link
Copy Markdown
Member

Closes #724

What

RFC 9864 registers the fully-specified Ed25519 and Ed448 signature algorithms and deprecates the polymorphic EdDSA. This PR ships both, deprecates EdDSA, and closes the RFC 8037 gap on the 448 curves (Ed448 signatures, X448 key agreement).

Signature — Jose\Component\Signature\Algorithm

  • AbstractEdDSA: PureEdDSA (RFC 8037 §3.1) — backend choice, signature length check before any backend is called (a malformed or tampered signature is false, 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 throws MissingDependencyException when the platform cannot run it.
  • Each accepts its own curve only and refuses the other with InvalidKeyException.
  • EdDSA: @deprecated, restricted to Ed25519 as before, same UnsupportedCurveException as before on another curve. trigger_deprecation on sign() only — issuing new EdDSA tokens 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 and openssl_pkey_get_details() returns no raw key material before that version. The gate is PHP_VERSION_ID >= 80400 && extension_loaded('openssl'), not the OpenSSL version. Verified on OpenSSL 3.0.13.

Plumbing — OKPKey (@internal)

Centralises what JWKFactory and AbstractECDH duplicated: 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

AbstractECDH accepts X448 wherever X25519 was (derivation, ephemeral epk, curve check) — every ECDH-ES* and ECDH-SS* variant inherits it. X25519 falls back to OpenSSL without sodium on PHP 8.4.

Keys, analyzers, console, bundle

  • JWKFactory::okp() delegates to OKPKey::generate() — JSON member order unchanged.
  • New OKPKeyAnalyzer (known curve, x/d sizes, alg/crv consistency); AlgorithmAnalyzer reports alg: EdDSA as deprecated with the replacement for the curve.
  • key:generate:okp / keyset:generate:okp document the four curves.
  • signature_eddsa.php registers Ed25519 + EdDSA when Ed25519::isSupported(), Ed448 when Ed448::isSupported(); SignatureSource loads the file when either is; OKPKeyAnalyzer wired.

Acceptance criteria

  • Sign/verify round-trip for Ed25519 and Ed448 through JWSBuilder/JWSVerifier, compact, flattened and general ✔ (sodium and OpenSSL paths for Ed25519, OKPKeyTest)
  • RFC 8037 §A.4 vector verifies under EdDSA and Ed25519 — and the signature is reproduced ✔; two RFC 8032 §7.4 Ed448 vectors verified and reproduced ✔
  • ECDH-ES round-trip with X448 (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)
  • Ed25519 refuses crv: Ed448 and vice versa; alg: EdDSA key refused by Ed25519 and accepted by EdDSA, and the reverse ✔
  • 113-byte or tampered Ed448 signature is false
  • PHP 8.2 / 8.3: Ed448::isSupported() false, constructor throws, bundle compiles, Ed25519 works through sodium ✔ — full suite run in phpqa:8.2 and phpqa:8.3 with their own vendor (29 skips), green.

Backward compatibility

  1. Reflection dump of the public API, 4.3.x vs this branch: additions only. EdDSA stays final, still implements SignatureAlgorithm, same five public methods.
  2. 35 behavioural probes run against both worktrees (outcome, exception class, deprecation count): no call that worked and now throws. Differences: EdDSA::sign() +1 deprecation (intended); a 63-byte Ed25519 signature was a SodiumException and is now false; sign() with a key without x now works (derived); X448 now works; three messages change at identical exception class (The EC key is not privateThe OKP key is not private, Invalid key parameter "x"Unable to get the "x" parameter); AlgorithmAnalyzer reports one more message for alg: EdDSA.

QA

PHPUnit (8.2, 8.3, 8.5), ECS, Rector, Deptrac, PHPStan green. The PHPStan baseline shrinks by three entries (substr in JWKFactory/AbstractECDH, return.type of EdDSA) and gains the EdDSA::class access in the config file, same pattern as None/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.

…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 Spomky self-assigned this Sep 13, 2026
@Spomky Spomky added this to the 4.3.0 milestone Sep 13, 2026
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.
@Spomky
Spomky merged commit 407c516 into 4.3.x Sep 13, 2026
17 checks passed
@Spomky
Spomky deleted the feat/ed25519-ed448-x448 branch September 13, 2026 12:04
@Spomky
Spomky restored the feat/ed25519-ed448-x448 branch September 13, 2026 12:05
@Spomky
Spomky deleted the feat/ed25519-ed448-x448 branch September 13, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(signature): Ed25519 and Ed448 fully-specified algorithms, EdDSA deprecated (RFC 9864); X448 for ECDH-ES (RFC 8037)

1 participant