Skip to content

Add SCRAM-SHA-256 authentication support#94

Merged
SeanTAllen merged 2 commits intomainfrom
sean/scram-sha-256
Feb 12, 2026
Merged

Add SCRAM-SHA-256 authentication support#94
SeanTAllen merged 2 commits intomainfrom
sean/scram-sha-256

Conversation

@SeanTAllen
Copy link
Member

Implement the SCRAM-SHA-256 authentication mechanism (RFC 5802/7677), which has been the default PostgreSQL auth method since version 10.

The implementation adds:

  • Protocol layer: three new SASL backend message types (10/11/12), two frontend message builders (SASLInitialResponse, SASLResponse)
  • _ScramSha256 computation primitive using ponylang/ssl 1.0.3 crypto (PBKDF2-SHA-256, HMAC-SHA-256, SHA-256)
  • _SessionSCRAMAuthenticating state in the session state machine, handling the multi-step SASL exchange between _SessionConnected and _SessionLoggedIn
  • ServerVerificationFailed and UnsupportedAuthenticationMethod failure reason variants
  • Unit tests: parser tests, frontend message tests, SCRAM computation tests against known vectors, mock server tests for success/unsupported mechanism/server verification failure/error during auth
  • MD5 backward-compat integration tests using a dedicated md5user
  • CI infrastructure: containers use SCRAM-SHA-256 default auth with an MD5-only user via pg_hba.conf routing; CI workflows switch from ssl=0.9.0 to ssl=1.1.x (required for PBKDF2)

Auth method selection is automatic — no user-facing API changes needed to connect to SCRAM-SHA-256 servers.

Note: After merging, trigger the build-ci-image.yml workflow dispatch to rebuild ghcr.io/ponylang/postgres-ci-pg-ssl:latest — the Dockerfile now COPYs init-md5-user.sh. CI will fail until the image is rebuilt.

Design: #83

Implement the SCRAM-SHA-256 authentication mechanism (RFC 5802/7677),
which has been the default PostgreSQL auth method since version 10.

The implementation adds:
- Protocol layer: three new SASL backend message types (10/11/12),
  two frontend message builders (SASLInitialResponse, SASLResponse)
- _ScramSha256 computation primitive using ponylang/ssl 1.0.3 crypto
  (PBKDF2-SHA-256, HMAC-SHA-256, SHA-256)
- _SessionSCRAMAuthenticating state in the session state machine,
  handling the multi-step SASL exchange between _SessionConnected
  and _SessionLoggedIn
- ServerVerificationFailed and UnsupportedAuthenticationMethod
  failure reason variants
- Unit tests: parser tests, frontend message tests, SCRAM computation
  tests against known vectors, mock server tests for success,
  unsupported mechanism, server verification failure, and error
  during auth
- MD5 backward-compat integration tests using a dedicated md5user
- CI infrastructure: containers use SCRAM-SHA-256 default auth with
  an MD5-only user via pg_hba.conf routing; CI workflows switch from
  ssl=0.9.0 to ssl=1.1.x (required for PBKDF2)

Auth method selection is automatic — no user-facing API changes
needed to connect to SCRAM-SHA-256 servers.

Design: #83
@SeanTAllen SeanTAllen added the changelog - added Automatically add "Added" CHANGELOG entry on merge label Feb 12, 2026
@ponylang-main ponylang-main added the discuss during sync Should be discussed during an upcoming sync label Feb 12, 2026
ssl 1.0.3 with -Dopenssl_1.1.x caused linker errors in CI because
the builder image has LibreSSL 4.2.0, which doesn't provide
OPENSSL_sk_pop, SSL_has_pending, OPENSSL_INIT_new, etc. ssl 2.0.0
adds first-class LibreSSL support via -Dlibressl. Also removes the
dropped 0.9.0 option from the Makefile.
@SeanTAllen SeanTAllen merged commit 361e8e0 into main Feb 12, 2026
10 of 11 checks passed
@SeanTAllen SeanTAllen deleted the sean/scram-sha-256 branch February 12, 2026 12:46
@ponylang-main ponylang-main removed the discuss during sync Should be discussed during an upcoming sync label Feb 12, 2026
github-actions bot pushed a commit that referenced this pull request Feb 12, 2026
github-actions bot pushed a commit that referenced this pull request Feb 12, 2026
SeanTAllen added a commit that referenced this pull request Feb 12, 2026
The SCRAM-SHA-256 PR (#94) changed the CI container's default auth from
MD5 to SCRAM-SHA-256, making the docstring's "MD5 authentication" claim
inaccurate. The test doesn't assert which auth method is used, so just
say "authentication".
SeanTAllen added a commit that referenced this pull request Feb 12, 2026
_UnsupportedMessage served double duty: returned for both unknown auth
types (should fail) and unknown message type bytes (should silently skip
ParameterStatus, NoticeResponse, etc.). Since the dispatcher had no
match arm for it, unsupported auth types were silently consumed and the
session hung indefinitely.

Introduce _UnsupportedAuthenticationMessage to distinguish unsupported
auth types from benign unknown messages. The dispatcher routes it to
on_authentication_failed with UnsupportedAuthenticationMethod, which
was already part of the public API but only reachable via the
SCRAM-specific mechanism check added in PR #94.

Closes #93
SeanTAllen added a commit that referenced this pull request Feb 12, 2026
_UnsupportedMessage served double duty: returned for both unknown auth
types (should fail) and unknown message type bytes (should silently skip
ParameterStatus, NoticeResponse, etc.). Since the dispatcher had no
match arm for it, unsupported auth types were silently consumed and the
session hung indefinitely.

Introduce _UnsupportedAuthenticationMessage to distinguish unsupported
auth types from benign unknown messages. The dispatcher routes it to
on_authentication_failed with UnsupportedAuthenticationMethod, which
was already part of the public API but only reachable via the
SCRAM-specific mechanism check added in PR #94.

Closes #93
SeanTAllen added a commit that referenced this pull request Feb 12, 2026
When a PostgreSQL server requests an authentication method the driver
doesn't support (e.g., cleartext password, Kerberos, GSSAPI), the
session hung indefinitely with no error reported. The root cause:
`_UnsupportedMessage` served double duty — returned for both unknown
auth types (should fail explicitly) and unknown message type bytes
(should silently skip ParameterStatus, NoticeResponse, etc.). Since
`_ResponseMessageParser` had no match arm for it, unsupported auth types
were silently consumed.

This introduces `_UnsupportedAuthenticationMessage` to distinguish the
two cases. The dispatcher routes it to `on_authentication_failed` with
`UnsupportedAuthenticationMethod` (already in the public API from PR
#94, but previously only reachable via the SCRAM-specific mechanism
check).

Closes #93
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog - added Automatically add "Added" CHANGELOG entry on merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants