fix(oidc): pass raw 32-byte Ed25519 key, not SPKI-wrapped DER (LC-22) #351

Merged
YousifShkara merged 1 commit from fix/lc-22-ed25519-spki-verify into main 2026-06-18 07:35:25 +02:00
Owner

jsonwebtoken::DecodingKey::from_ed_der is misleadingly named: it does
NOT parse SPKI DER. It stores its byte slice verbatim into the
DecodingKey, and verify_ring eventually calls
ring::signature::UnparsedPublicKey::new(ED25519, key) which expects
raw 32-byte Ed25519 public key bytes - not the SPKI envelope
(SEQUENCE(SEQUENCE(OID 1.3.101.112), BIT STRING(...))) that
ed25519_raw_to_der was constructing.

Result post-cutover deploy: every id_token verify failed with
InvalidSignature because the lookup key handed to ring was 44 bytes of
SPKI DER while bunyip's actual signature was over the raw 32-byte key.
The unit test path never caught it because we have no integration test
against a live OIDC OP yet (parked in v2 backlog).

Fix: keep the 32 raw bytes off the JWK x parameter unchanged, store
them under EdKey.raw (renamed from EdKey.der), and pass them
straight through to DecodingKey::from_ed_der. Verify length is 32
during JWKS fetch so a malformed JWK fails loud at fetch time instead
of mysteriously failing every callback. The helper that built the SPKI
wrapper is deleted entirely.

#LC-22

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

`jsonwebtoken::DecodingKey::from_ed_der` is misleadingly named: it does NOT parse SPKI DER. It stores its byte slice verbatim into the `DecodingKey`, and `verify_ring` eventually calls `ring::signature::UnparsedPublicKey::new(ED25519, key)` which expects **raw 32-byte Ed25519 public key bytes** - not the SPKI envelope (`SEQUENCE(SEQUENCE(OID 1.3.101.112), BIT STRING(...))`) that `ed25519_raw_to_der` was constructing. Result post-cutover deploy: every id_token verify failed with `InvalidSignature` because the lookup key handed to ring was 44 bytes of SPKI DER while bunyip's actual signature was over the raw 32-byte key. The unit test path never caught it because we have no integration test against a live OIDC OP yet (parked in v2 backlog). Fix: keep the 32 raw bytes off the JWK `x` parameter unchanged, store them under `EdKey.raw` (renamed from `EdKey.der`), and pass them straight through to `DecodingKey::from_ed_der`. Verify length is 32 during JWKS fetch so a malformed JWK fails loud at fetch time instead of mysteriously failing every callback. The helper that built the SPKI wrapper is deleted entirely. #LC-22 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(oidc): pass raw 32-byte Ed25519 key, not SPKI-wrapped DER (LC-22)
All checks were successful
check-secrets / Nosey parker (push) Successful in 3s
check-secrets / TruffleHog (push) Successful in 4s
check-secrets / Kingfisher (push) Successful in 5s
Create release / Create release from merged PR (pull_request) Has been skipped
check-secrets / Nosey parker (pull_request) Successful in 4s
check-secrets / Kingfisher (pull_request) Successful in 4s
check-secrets / TruffleHog (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Successful in 3m4s
c6065f3f87
`jsonwebtoken::DecodingKey::from_ed_der` is misleadingly named: it does
NOT parse SPKI DER. It stores its byte slice verbatim into the
`DecodingKey`, and `verify_ring` eventually calls
`ring::signature::UnparsedPublicKey::new(ED25519, key)` which expects
**raw 32-byte Ed25519 public key bytes** - not the SPKI envelope
(`SEQUENCE(SEQUENCE(OID 1.3.101.112), BIT STRING(...))`) that
`ed25519_raw_to_der` was constructing.

Result post-cutover deploy: every id_token verify failed with
`InvalidSignature` because the lookup key handed to ring was 44 bytes of
SPKI DER while bunyip's actual signature was over the raw 32-byte key.
The unit test path never caught it because we have no integration test
against a live OIDC OP yet (parked in v2 backlog).

Fix: keep the 32 raw bytes off the JWK `x` parameter unchanged, store
them under `EdKey.raw` (renamed from `EdKey.der`), and pass them
straight through to `DecodingKey::from_ed_der`. Verify length is 32
during JWKS fetch so a malformed JWK fails loud at fetch time instead
of mysteriously failing every callback. The helper that built the SPKI
wrapper is deleted entirely.

#LC-22

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
YousifShkara deleted branch fix/lc-22-ed25519-spki-verify 2026-06-18 07:35:26 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!351
No description provided.