Add SCRAM-SHA-256 authentication support#94
Merged
SeanTAllen merged 2 commits intomainfrom Feb 12, 2026
Merged
Conversation
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
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
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
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.
Implement the SCRAM-SHA-256 authentication mechanism (RFC 5802/7677), which has been the default PostgreSQL auth method since version 10.
The implementation adds:
_ScramSha256computation primitive usingponylang/ssl1.0.3 crypto (PBKDF2-SHA-256, HMAC-SHA-256, SHA-256)_SessionSCRAMAuthenticatingstate in the session state machine, handling the multi-step SASL exchange between_SessionConnectedand_SessionLoggedInServerVerificationFailedandUnsupportedAuthenticationMethodfailure reason variantsmd5userpg_hba.confrouting; CI workflows switch fromssl=0.9.0tossl=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.ymlworkflow dispatch to rebuildghcr.io/ponylang/postgres-ci-pg-ssl:latest— the Dockerfile now COPYsinit-md5-user.sh. CI will fail until the image is rebuilt.Design: #83