scram

package
v0.0.0-...-d587f9d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package scram implements SCRAM-SHA-256 authentication for PostgreSQL protocol connections.

Overview

This package provides SCRAM-SHA-256 authentication for both server and client roles, enabling Multigres to verify client credentials and extract SCRAM keys for passthrough authentication to backend PostgreSQL servers. This eliminates the need to store plaintext passwords while maintaining compatibility with PostgreSQL's native authentication.

SCRAM-SHA-256 Protocol

SCRAM (Salted Challenge Response Authentication Mechanism) is defined in RFC 5802: https://datatracker.ietf.org/doc/html/rfc5802

PostgreSQL's SCRAM-SHA-256 implementation is documented at: https://www.postgresql.org/docs/current/sasl-authentication.html

The protocol involves a three-message exchange:

  1. Client → Server: client-first-message (username, nonce)
  2. Server → Client: server-first-message (combined nonce, salt, iterations)
  3. Client → Server: client-final-message (proof)
  4. Server → Client: server-final-message (server signature for mutual auth)

Why Not Use an Existing Library?

Several Go SCRAM libraries exist (xdg-go/scram, lib/pq, jackc/pgx), but none support our critical requirement: ClientKey extraction for passthrough authentication.

Existing libraries:

  • xdg-go/scram: Most comprehensive, but lacks ClientKey extraction and context.Context support
  • lib/pq: Maintenance mode, client-side only
  • jackc/pgx: Client library, no server-side SCRAM

Our implementation adds:

  • ExtractAndVerifyClientProof: Recovers ClientKey from client's proof for passthrough auth
  • context.Context support: Allows timeout/cancellation during credential lookup
  • Minimum security thresholds: Enforces 4096+ iterations and 8+ byte salts

These features enable Multigres to verify clients and reuse extracted keys to authenticate to backend PostgreSQL servers without storing plaintext passwords.

Architecture

The package is organized into several components:

  • ScramAuthenticator: Stateful server-side authenticator handling the protocol exchange
  • SCRAMClient: Client-side authenticator supporting password and passthrough modes
  • ScramHash: PostgreSQL pg_authid SCRAM-SHA-256 hash (salt, iterations, StoredKey, ServerKey)
  • Cryptographic functions: RFC 5802 compliant key derivation and verification
  • Protocol parsers/generators: Message construction and parsing (unexported)

Usage Example

// Server-side authentication. The caller looks up the user's
// SCRAM hash up front (e.g. via a credential provider) and passes
// it to NewScramAuthenticator.
hash := fetchScramHashForUser(user, db)
auth := scram.NewScramAuthenticator(hash, "mydb")

// Optional: enable SCRAM-SHA-256-PLUS (channel binding) over TLS.
auth.SetOverTLS(true)
auth.SetChannelBinding(&scram.ChannelBinding{
    TLSServerEndPointHash: certHash, // see ComputeTLSServerEndPointHash
})

// Start SASL negotiation
mechanisms := auth.StartAuthentication()
// Send AuthenticationSASL with mechanisms...

// Handle client-first-message. `mechanism` is the SASL mechanism the
// client picked in SASLInitialResponse; `startupUser` is the username
// from the StartupMessage (used when the SCRAM message itself has none).
serverFirst, err := auth.HandleClientFirst(mechanism, clientFirstMsg, startupUser)
// Send AuthenticationSASLContinue with serverFirst...

// Handle client-final-message
serverFinal, err := auth.HandleClientFinal(clientFinalMsg)
if auth.IsAuthenticated() {
    // Authentication successful
    clientKey, serverKey := auth.ExtractedKeys()
    // Use keys for passthrough authentication...
}

Key Passthrough Authentication

A critical feature of this implementation is extracting the ClientKey from the client's proof during authentication. This enables SCRAM passthrough:

  1. Client authenticates to Multigres multigateway
  2. Multigateway extracts ClientKey from the authentication proof
  3. Multigateway uses ClientKey to authenticate to PostgreSQL as that user
  4. No plaintext password needed at any stage

This is possible because SCRAM proofs reveal the ClientKey through XOR:

ClientKey = ClientProof XOR ClientSignature

The extracted ClientKey can then be used with the stored ServerKey to perform subsequent SCRAM authentications to backend PostgreSQL servers without knowing the original password.

Password Hash Storage

This package expects password hashes in PostgreSQL's SCRAM-SHA-256 format:

SCRAM-SHA-256$<iterations>:<salt>$<StoredKey>:<ServerKey>

The caller looks up the user's SCRAM hash (typically via a credential provider that wraps the storage backend) and passes the parsed *ScramHash to NewScramAuthenticator. Storage backends can:

  • Query PostgreSQL's pg_authid directly
  • Use a credential cache with TTL and invalidation
  • Fetch from a centralized credential service
  • Combine multiple sources with fallback logic

Future Directions

Current implementation:

  • Core SCRAM protocol and cryptography
  • Server-side authentication (ScramAuthenticator)
  • Client-side authentication with passthrough support (SCRAMClient)
  • No credential caching
  • No integration with multigateway/multipooler

Planned enhancements:

  • Caching password hashes in multigateway to save a round-trip to multipooler on connect
  • Integration with multigateway/multipooler for end-to-end passthrough

Credential Cache Design Considerations

When implementing credential caching:

  • TTL: Balance security (shorter) vs performance (longer)

    PgBouncer: No cache, runs auth_query on every connection See src/client.c start_auth_query() https://github.com/pgbouncer/pgbouncer/tree/master/src

    Supavisor: 24-hour cache with 15-second background refresh + refresh on auth failure See lib/supavisor/secret_cache.ex @default_secrets_ttl, fetch_validation_secrets/3 See lib/supavisor/secret_checker.ex @interval, check_secrets/2 (background polling) See lib/supavisor/client_handler/auth.ex check_and_update_secrets/7 (refresh on auth failure) https://github.com/supabase/supavisor/tree/main/lib/supavisor

  • Invalidation: Consider cache busting on password changes Option 1: PostgreSQL triggers + notification channel Option 2: Periodic refresh on access Option 3: External invalidation API

Security Considerations

  • All password hash comparisons use constant-time algorithms (crypto/subtle)
  • Nonce validation prevents replay attacks
  • State machine prevents protocol violations
  • No plaintext passwords stored or logged
  • ClientKey extraction requires successful authentication

Password Normalization (SASLprep)

This implementation includes SASLprep password normalization (RFC 4013) for full PostgreSQL compatibility. SASLprep applies NFKC Unicode normalization and character mapping to passwords before hashing.

Key behaviors:

  • Non-ASCII spaces normalized to ASCII space (U+0020)
  • Soft hyphens and zero-width characters removed
  • Unicode combining characters normalized
  • Fallback to raw password on normalization failure (prohibited chars, bidi violations)

This matches PostgreSQL's lenient approach: passwords that fail SASLprep validation (invalid UTF-8, prohibited characters, bidirectional check failures) are accepted using their raw byte representation.

Compatibility

This implementation is compatible with:

  • PostgreSQL 10+ SCRAM-SHA-256 authentication
  • PostgreSQL 11+ SCRAM-SHA-256-PLUS channel binding (tls-server-end-point, RFC 5929). Advertised alongside SCRAM-SHA-256 on TLS connections.
  • Standard PostgreSQL client libraries (psql, libpq, pgx, etc.)
  • PostgreSQL's pg_authid password hash format
  • PostgreSQL's SASLprep implementation (RFC 4013)

Not currently supported:

  • SCRAM-SHA-1 (deprecated, not used by PostgreSQL)
  • tls-unique channel binding (deprecated; TLS 1.3 removed it)
  • Channel binding with TLS configs that use GetCertificate/SNI dynamic certs without populating tlsConfig.Certificates[0]; PLUS falls back to SCRAM-SHA-256 in that case
  • Custom iteration counts (uses hash's iteration count)

References

Index

Constants

View Source
const (
	// ScramSHA256Prefix is the prefix for SCRAM-SHA-256 password hashes in PostgreSQL.
	ScramSHA256Prefix = "SCRAM-SHA-256"

	// MinIterationCount is the minimum PBKDF2 iteration count accepted for security.
	// RFC 5802 recommends a minimum of 4096 iterations to make brute-force attacks harder.
	MinIterationCount = 4096

	// MinSaltLength is the minimum salt length in bytes accepted for security.
	MinSaltLength = 8
)
View Source
const (
	// ScramSHA256Mechanism is the SASL mechanism name for SCRAM-SHA-256.
	ScramSHA256Mechanism = "SCRAM-SHA-256"

	// ScramSHA256PlusMechanism is the SASL mechanism name for SCRAM-SHA-256
	// with channel binding (RFC 5802 §6 + PostgreSQL convention). Advertised
	// only over TLS, alongside ScramSHA256Mechanism.
	ScramSHA256PlusMechanism = "SCRAM-SHA-256-PLUS"
)
View Source
const ChannelBindingTypeTLSServerEndPoint = "tls-server-end-point"

ChannelBindingTypeTLSServerEndPoint is the channel binding type defined in RFC 5929 §4: a hash of the TLS server's certificate. This is the type PostgreSQL advertises for SCRAM-SHA-256-PLUS.

Variables

View Source
var (
	// ErrUserNotFound indicates the user does not exist. Callers fetching
	// credentials before constructing a ScramAuthenticator should return
	// this so the gateway can emit PG's "password authentication failed"
	// (SQLSTATE 28P01) without exposing user existence.
	ErrUserNotFound = errors.New("user not found")

	// ErrAuthenticationFailed indicates the password proof was invalid.
	ErrAuthenticationFailed = errors.New("authentication failed")

	// ErrLoginDisabled indicates the role has rolcanlogin=false in pg_authid.
	// Caller should emit PG's "role \"X\" is not permitted to log in" with
	// SQLSTATE 28000, matching native PostgreSQL.
	ErrLoginDisabled = errors.New("role not permitted to log in")

	// ErrPasswordExpired indicates the role's rolvaliduntil is in the past.
	// Caller should emit the opaque "password authentication failed" message
	// with SQLSTATE 28P01, matching native PostgreSQL's handling of expired
	// passwords.
	ErrPasswordExpired = errors.New("password expired")

	// ErrChannelBindingNegotiation indicates the client signaled GS2 flag
	// "y" while the server actually advertised SCRAM-SHA-256-PLUS — a
	// classic downgrade attempt. Per PostgreSQL, caller emits SQLSTATE
	// 28000 with "SCRAM channel binding negotiation error".
	ErrChannelBindingNegotiation = errors.New("scram: channel binding negotiation error")

	// ErrChannelBindingCheck indicates the cbind data the client echoed
	// in client-final-message does not match the value the server expects
	// (gs2-header + cbind-data for PLUS, or "biws"/"eSws" for base). Per
	// PostgreSQL, caller emits SQLSTATE 28000 with "SCRAM channel binding
	// check failed".
	ErrChannelBindingCheck = errors.New("scram: channel binding check failed")

	// ErrSASLProtocol indicates a SCRAM-level protocol violation tied to
	// channel binding: PLUS mechanism with non-"p=" flag, base mechanism
	// with "p=" flag, unsupported binding type, malformed messages. Per
	// PostgreSQL, caller emits SQLSTATE 08P01 with "malformed SCRAM
	// message" (or the more specific "unsupported SCRAM channel-binding
	// type" variant) plus an error detail.
	ErrSASLProtocol = errors.New("scram: protocol violation")

	// ErrAuthzidNotSupported indicates the client included a SASL
	// authorization identity ("a=...") in its gs2-header. PostgreSQL
	// rejects this with SQLSTATE 0A000 (feature_not_supported).
	ErrAuthzidNotSupported = errors.New("scram: authzid not supported")
)

Sentinel errors for SCRAM authentication.

Functions

func ComputeClientKey

func ComputeClientKey(saltedPassword []byte) []byte

ComputeClientKey computes ClientKey = HMAC(SaltedPassword, "Client Key").

func ComputeClientSignature

func ComputeClientSignature(storedKey []byte, authMessage string) []byte

ComputeClientSignature computes ClientSignature = HMAC(StoredKey, AuthMessage).

func ComputeSaltedPassword

func ComputeSaltedPassword(password string, salt []byte, iterations int) []byte

ComputeSaltedPassword computes the SCRAM SaltedPassword using PBKDF2. SaltedPassword = Hi(Normalize(password), salt, iterations) Where Hi is PBKDF2 with HMAC-SHA-256.

Passwords are normalized using SASLprep (RFC 4013), which applies stringprep with NFKC normalization and character mapping. If normalization fails (invalid UTF-8, prohibited characters, bidirectional check failures), the raw password is used unchanged, matching PostgreSQL's lenient behavior.

func ComputeServerKey

func ComputeServerKey(saltedPassword []byte) []byte

ComputeServerKey computes ServerKey = HMAC(SaltedPassword, "Server Key").

func ComputeServerSignature

func ComputeServerSignature(serverKey []byte, authMessage string) []byte

ComputeServerSignature computes ServerSignature = HMAC(ServerKey, AuthMessage).

func ComputeStoredKey

func ComputeStoredKey(clientKey []byte) []byte

ComputeStoredKey computes StoredKey = H(ClientKey) where H is SHA-256.

func ComputeTLSServerEndPointHash

func ComputeTLSServerEndPointHash(cert *x509.Certificate) ([]byte, error)

ComputeTLSServerEndPointHash computes the tls-server-end-point channel binding data for a TLS server certificate per RFC 5929 §4: the hash of the DER-encoded certificate, using the certificate signature algorithm's hash function — with one PostgreSQL-compatible quirk: certificates signed with MD5 or SHA-1 are hashed with SHA-256 instead (mirroring PG's be_tls_get_certificate_hash). This is what libpq expects, so any client computing the binding the same way will interoperate.

func ExtractAndVerifyClientProof

func ExtractAndVerifyClientProof(storedKey []byte, authMessage string, clientProof []byte) ([]byte, error)

ExtractAndVerifyClientProof verifies the client's proof and extracts the ClientKey. This is used for SCRAM key passthrough: after verifying a client, we can use the extracted ClientKey to authenticate as that user to PostgreSQL.

The verification process: 1. Compute ClientSignature = HMAC(StoredKey, AuthMessage) 2. Recover ClientKey = ClientProof XOR ClientSignature 3. Compute RecoveredStoredKey = H(ClientKey) 4. Verify RecoveredStoredKey == StoredKey

Returns the extracted ClientKey on successful verification (error == nil). Returns ErrAuthenticationFailed if the proof is invalid (wrong password). Returns other errors for unexpected conditions (malformed proof, length mismatches). Uses constant-time comparison to prevent timing attacks.

func IsScramSHA256Hash

func IsScramSHA256Hash(hash string) bool

IsScramSHA256Hash returns true if the hash string appears to be a SCRAM-SHA-256 hash. This is a quick check based on the prefix; it does not validate the entire format.

Types

type ChannelBinding

type ChannelBinding struct {
	// TLSServerEndPointHash is the hash of the server's TLS certificate, as
	// defined for the tls-server-end-point channel binding type (RFC 5929).
	// Compute it with ComputeTLSServerEndPointHash.
	TLSServerEndPointHash []byte
}

ChannelBinding carries TLS channel binding context into the SCRAM authenticator. When non-nil and TLSServerEndPointHash is populated, the authenticator advertises SCRAM-SHA-256-PLUS in addition to SCRAM-SHA-256.

type SASLProtocolError

type SASLProtocolError struct {
	// Detail is the PostgreSQL errdetail() string — keep it byte-identical
	// to the PG source so client-side log parsers (and humans) can't tell
	// they're talking to multigres.
	Detail string
	// Msg overrides the primary errmsg(); empty means "malformed SCRAM
	// message" (PG's default for cbind protocol errors).
	Msg string
}

SASLProtocolError wraps ErrSASLProtocol with a PG-style detail message so the listener can emit "malformed SCRAM message" with errdetail("...") verbatim — matching libpq-readable output. Use as the wire-facing detail only; the sentinel chain still exposes ErrSASLProtocol via errors.Is.

func (*SASLProtocolError) Error

func (e *SASLProtocolError) Error() string

func (*SASLProtocolError) Unwrap

func (e *SASLProtocolError) Unwrap() error

Unwrap lets errors.Is(err, ErrSASLProtocol) succeed.

type SCRAMClient

type SCRAMClient struct {
	// contains filtered or unexported fields
}

SCRAMClient implements client-side SCRAM-SHA-256 authentication. It supports two modes: 1. Password mode: authenticate using a plaintext password 2. Passthrough mode: authenticate using pre-extracted ClientKey/ServerKey

Passthrough mode enables SCRAM key passthrough: after a proxy verifies a client, it can extract the ClientKey and use it to authenticate to the backend database without knowing the plaintext password.

When constructed with EnableChannelBinding, the client speaks SCRAM-SHA-256-PLUS instead, embedding the supplied tls-server-end-point hash into its messages.

func NewSCRAMClientWithKeys

func NewSCRAMClientWithKeys(username string, clientKey, serverKey []byte) *SCRAMClient

NewSCRAMClientWithKeys creates a SCRAM client that authenticates with extracted SCRAM keys. This enables SCRAM passthrough: a proxy can verify a client, extract the ClientKey, and use it to authenticate to PostgreSQL without the plaintext password.

The clientKey and serverKey should be extracted from a previous SCRAM authentication using ExtractAndVerifyClientProof and the hash's ServerKey.

func NewSCRAMClientWithPassword

func NewSCRAMClientWithPassword(username, password string) *SCRAMClient

NewSCRAMClientWithPassword creates a SCRAM client that authenticates with a password. This is the standard mode where the password is used to derive SCRAM keys.

func (*SCRAMClient) ClientFirstMessage

func (c *SCRAMClient) ClientFirstMessage() (string, error)

ClientFirstMessage generates the client-first-message to send to the server. This starts the SCRAM authentication handshake. Returns the full message including the GS2 header.

func (*SCRAMClient) EnableChannelBinding

func (c *SCRAMClient) EnableChannelBinding(tlsServerEndPointHash []byte)

EnableChannelBinding configures the client to negotiate SCRAM-SHA-256-PLUS using the tls-server-end-point binding type, with the supplied hash as the cbind-data. Call before ClientFirstMessage. Pass nil to clear.

func (*SCRAMClient) Mechanism

func (c *SCRAMClient) Mechanism() string

Mechanism returns the SASL mechanism name the client will use, based on whether channel binding has been enabled.

func (*SCRAMClient) ProcessServerFirst

func (c *SCRAMClient) ProcessServerFirst(serverFirst string) (string, error)

ProcessServerFirst processes the server-first-message and generates the client-final-message. The serverFirst parameter is the server's response to the client-first-message. Returns the client-final-message to send to the server.

func (*SCRAMClient) VerifyServerFinal

func (c *SCRAMClient) VerifyServerFinal(serverFinal string) error

VerifyServerFinal verifies the server-final-message for mutual authentication. The serverFinal parameter is the server's response to the client-final-message. Returns nil if the server signature is valid, or an error if verification fails.

type ScramAuthenticator

type ScramAuthenticator struct {
	// contains filtered or unexported fields
}

ScramAuthenticator handles SCRAM-SHA-256 authentication. It implements the server side of the SCRAM protocol as defined in RFC 5802.

Usage:

  1. Call StartAuthentication() to get the list of supported mechanisms.
  2. After receiving client-first-message, call HandleClientFirst().
  3. After receiving client-final-message, call HandleClientFinal().
  4. Check IsAuthenticated() to see if auth succeeded.
  5. Call ExtractedKeys() to get SCRAM keys for passthrough authentication.

The authenticator maintains state between calls and enforces valid state transitions to prevent protocol errors.

Thread Safety: ScramAuthenticator is NOT thread-safe. Each connection must use its own authenticator instance. Do not share authenticators across goroutines or reuse them for multiple concurrent authentication attempts. The Reset() method allows reusing an authenticator sequentially, but only after the previous authentication has completed.

func NewScramAuthenticator

func NewScramAuthenticator(hash *ScramHash, database string) *ScramAuthenticator

NewScramAuthenticator creates a new SCRAM authenticator for the given pre-fetched password hash and database name. The caller must look up the hash (e.g. via a credential provider) before constructing the authenticator so a single credential fetch can satisfy both SCRAM and any subsequent role-attribute checks the caller wants to perform.

Panics if hash is nil. Unknown-user / login-disabled / password-expired cases must be handled by the caller before reaching this constructor.

func (*ScramAuthenticator) AuthenticatedUser

func (a *ScramAuthenticator) AuthenticatedUser() string

AuthenticatedUser returns the username that was successfully authenticated. Returns an empty string if authentication has not completed successfully.

func (*ScramAuthenticator) ExtractedKeys

func (a *ScramAuthenticator) ExtractedKeys() (clientKey, serverKey []byte)

ExtractedKeys returns the SCRAM keys extracted during authentication. These can be used for passthrough authentication to backend PostgreSQL servers.

ClientKey is recovered from the client's proof (ClientKey = ClientProof XOR ClientSignature). ServerKey comes from the stored password hash.

Returns nil, nil if authentication has not completed successfully.

func (*ScramAuthenticator) HandleClientFinal

func (a *ScramAuthenticator) HandleClientFinal(clientFinalMessage string) (string, error)

HandleClientFinal processes the client-final-message from the client. Returns the server-final-message to send back on success.

The client-final-message contains the channel binding, combined nonce, and client proof. This method verifies the proof and, if valid, returns the server signature for mutual authentication.

Returns ErrAuthenticationFailed if the proof is invalid. Returns ErrChannelBindingCheck (PLUS) or ErrSASLProtocol (base) when the cbind data the client echoes back does not match the gs2-header (+ TLS cert hash for PLUS) the server expects.

func (*ScramAuthenticator) HandleClientFirst

func (a *ScramAuthenticator) HandleClientFirst(mechanism, clientFirstMessage, startupMessageUsername string) (string, error)

HandleClientFirst processes the client-first-message from the client. Returns the server-first-message to send back.

The mechanism is the SASL mechanism the client selected in the SASLInitialResponse (SCRAM-SHA-256 or SCRAM-SHA-256-PLUS); the authenticator validates it against what StartAuthentication advertised and against the GS2 channel-binding flag the client carries in the client-first-message.

The client-first-message comes from SASLInitialResponse and contains the GS2 header and client-first-message-bare (username, client nonce).

The startupMessageUsername is used when the client sends an empty username in the client-first-message. PostgreSQL allows this and uses the username from the startup message. This parameter should be the username from the startup message.

The user's password hash was supplied at construction time; this method generates the server-first-message containing the combined nonce, salt, and iteration count from that hash.

func (*ScramAuthenticator) IsAuthenticated

func (a *ScramAuthenticator) IsAuthenticated() bool

IsAuthenticated returns true if the authentication completed successfully.

func (*ScramAuthenticator) Reset

func (a *ScramAuthenticator) Reset()

Reset clears the authenticator state, allowing it to be reused for another authentication attempt with the same hash. The channel binding context and overTLS flag are preserved — they're tied to the TLS session, not to a particular handshake attempt. The extracted ClientKey is zeroized before being nilled so a memory dump after Reset cannot recover the previous session's secret.

func (*ScramAuthenticator) SetChannelBinding

func (a *ScramAuthenticator) SetChannelBinding(cb *ChannelBinding)

SetChannelBinding attaches TLS channel binding context to the authenticator. Must be called before StartAuthentication; calling it afterwards panics, as the advertised mechanism list would already be locked in. When the binding hash is non-empty the authenticator advertises SCRAM-SHA-256-PLUS in addition to SCRAM-SHA-256 and enforces the corresponding GS2 flag rules (RFC 5802 §6).

Pass nil to clear any previously set binding.

func (*ScramAuthenticator) SetOverTLS

func (a *ScramAuthenticator) SetOverTLS(overTLS bool)

SetOverTLS records that the underlying connection is TLS-encrypted. Must be called before StartAuthentication. This is independent of whether SetChannelBinding succeeded — downgrade detection still needs to fire over TLS even when cbind material couldn't be derived.

func (*ScramAuthenticator) StartAuthentication

func (a *ScramAuthenticator) StartAuthentication() []string

StartAuthentication begins the SCRAM authentication process. Returns the list of supported SASL mechanisms. When channel binding is available, SCRAM-SHA-256-PLUS is listed first so libpq-style clients pick the stronger mechanism by default.

This corresponds to sending AuthenticationSASL (auth type 10) to the client.

type ScramHash

type ScramHash struct {
	// Iterations is the PBKDF2 iteration count used to derive the salted password.
	Iterations int

	// Salt is the random salt used in PBKDF2 key derivation.
	Salt []byte

	// StoredKey is H(ClientKey) where H is SHA-256 and ClientKey = HMAC(SaltedPassword, "Client Key").
	// Used to verify the client's proof.
	StoredKey []byte

	// ServerKey is HMAC(SaltedPassword, "Server Key").
	// Used to generate the server's signature for mutual authentication.
	ServerKey []byte
}

ScramHash contains the parsed components of a PostgreSQL SCRAM-SHA-256 password hash. The hash format is: SCRAM-SHA-256$<iterations>:<salt>$<StoredKey>:<ServerKey> where salt, StoredKey, and ServerKey are base64-encoded.

func ParseScramSHA256Hash

func ParseScramSHA256Hash(hash string) (*ScramHash, error)

ParseScramSHA256Hash parses a PostgreSQL SCRAM-SHA-256 password hash string. The expected format is: SCRAM-SHA-256$<iterations>:<salt>$<StoredKey>:<ServerKey>

Example: SCRAM-SHA-256$4096:W22ZaJ0SNY7soEsUEjb6gQ==$WG5d8oPm3OtcPnkdi4Oln6rNiYzlYY42lUpMtdJ7U90=:HKZfkuYXDxJboM9DFNR0yFNHpRx/rbdVdNOTk/V0v0Q=

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL