Skip to content

Add opt-in EdDSA (Ed25519) support; harden cache completion and GUC prefix - #5

Merged
proddata merged 2 commits into
mainfrom
eddsa-and-cache-hardening
Oct 1, 2026
Merged

proddata merged 2 commits into
mainfrom
eddsa-and-cache-hardening

Conversation

@proddata

@proddata proddata commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Summary

Two independent changes, kept as separate commits so each can be reviewed on its own:

  1. Cache hardening (e09aaa3): release cache refresh ownership when a completion is rejected; reserve the GUC prefix.
  2. Opt-in EdDSA (Ed25519) (6528374): add EdDSA as a third allowed signing algorithm. Decision record: ADR 0003 (status Proposed, needs review).

No release, version bump, or tag is part of this PR. Release work stays under REL-01.

EdDSA: requirement and threat model

Some authorization servers sign access tokens with Ed25519 by default (JWKs of type OKP, alg: "EdDSA"). Without this change they can only be used by downgrading every PostgreSQL client to RSA.

  • Trust boundary: unchanged. Algorithm and key type are still chosen by local policy and the trusted JWKS, never by the token.
  • Fail-closed behavior: the token alg must be exactly EdDSA; the selected JWK must be kty=OKP, crv=Ed25519, alg=EdDSA, use=sig/key_ops=[verify], a 32-byte x, and no private members. Ed448, X25519, wrong-length keys, mismatched/missing JWK alg, case variants, and the RFC 9864 Ed25519 name are rejected. The curve is pinned in this project's code, not inherited from libjwt. An EdDSA token cannot select an RSA/EC key, and vice versa.
  • Compatibility: opt-in only. pg_oauth_validator.allowed_algorithms still defaults to RS256,ES256, so existing deployments accept exactly what they did before. Enable with e.g. 'RS256,EdDSA'.
  • Rollback: remove EdDSA from the setting and reload. Downgrading the module while EdDSA is listed fails closed (earlier modules reject the unknown entry as invalid configuration).
  • Known caveat: RFC 9864 deprecates the polymorphic EdDSA name in favor of Ed25519/Ed448. Deployed issuers still emit EdDSA; supporting the new name is a recorded follow-up.
  • Operators on FIPS-restricted OpenSSL should confirm Ed25519 support before enabling it.

Tests and commands run

All in the pinned PostgreSQL 18 container (compose.test.yml), on each commit's final tree:

  • dev-verify-pg18 (unit/component gate): pass, including new JWK, policy, token-header, and signature negative/boundary cases (a genuine Ed25519 signature verifies; wrong key, tampered payload, tampered signature are rejected).
  • dev-integration-pg18 (real server, non-slow): 18 passed, 2 skipped (existing pg19-only tests). New test: EdDSA denied by default, accepted after opt-in, algorithm-relabelling and wrong-kid tokens denied, EdDSA-only allowlist denies RSA tokens, no token in logs or client errors.
  • fuzz-jwks / fuzz-jwt-envelope: 2000 runs each, clean; targets now exercise EdDSA with a valid seed.
  • scripts/format-c.sh --check (pinned pgindent): pass.

Not run / remaining risks

  • PostgreSQL 19 build and tests (image not built locally).
  • Static analysis, sanitizer builds, slow integration tests, Keycloak interop.
  • No test against a real production IdP; the integration IdP mimics the key format only.
  • docs/operations.md sample config still shows the default 'RS256,ES256', which remains correct.

🤖 Generated with Claude Code

Georg Traar and others added 2 commits October 1, 2026 23:12
…refix

complete_refresh returned false for invalid ttl/payload arguments without
clearing the refreshing flag, which would have left the entry in progress
for every backend until restart. It is unreachable today because the
issuer-key layer rejects oversized responses before a refresh starts, but
the invariant should not depend on that. A rejected completion now
releases ownership and counts as a failure.

Reserve the pg_oauth_validator GUC prefix after all definitions so a
misspelled setting is reported instead of silently ignored.

Document minor-release rebuilds and that playground files are not
production configuration.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Accept `EdDSA` as a third allowed algorithm. The default allowlist stays
`RS256,ES256`; an administrator must list `EdDSA` explicitly, so upgrading
changes no existing deployment's accepted tokens.

Ed25519 only: the token header must be exactly `EdDSA` and the selected JWK
must be kty=OKP, crv=Ed25519, alg=EdDSA with a 32-byte key. Ed448, X25519,
wrong-length keys, private-key material, and the RFC 9864 fully-specified
names are rejected before the key reaches libjwt. Key selection rules are
unchanged, and a JWKS that also publishes other keys remains valid.

Adds unit, signature, fuzz, and PostgreSQL integration coverage, ADR 0003,
and the spec/configuration updates. Not run: PostgreSQL 19, static analysis,
sanitizer builds.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@proddata
proddata force-pushed the eddsa-and-cache-hardening branch from 6528374 to f103076 Compare October 1, 2026 21:13
@proddata
proddata merged commit cadfe7c into main Oct 1, 2026
10 checks passed
@proddata
proddata deleted the eddsa-and-cache-hardening branch October 15, 2026 06:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant