1/*-------------------------------------------------------------------------
3 * Shared frontend/backend code for SCRAM authentication
5 * This contains the common low-level functions needed in both frontend and
6 * backend, for implement the Salted Challenge Response Authentication
7 * Mechanism (SCRAM), per IETF's RFC 5802.
9 * Portions Copyright (c) 2017-2025, PostgreSQL Global Development Group
12 * src/common/scram-common.c
14 *-------------------------------------------------------------------------
31 * Calculate SaltedPassword.
33 * The password should already be normalized by SASLprep. Returns 0 on
34 * success, -1 on failure with *errstr pointing to a message about the
41 uint8 *result,
const char **errstr)
58 * Iterate hash calculation of HMAC entry using given salt. This is
59 * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom
74 memcpy(result, Ui_prev, key_length);
76 /* Subsequent iterations */
81 * Make sure that this is interruptible as scram_iterations could be
82 * set to a large value.
96 for (
j = 0;
j < key_length;
j++)
98 memcpy(Ui_prev, Ui, key_length);
107 * Calculate hash for a NULL-terminated string. (The NULL terminator is
108 * not included in the hash). Returns 0 on success, -1 on failure with *errstr
109 * pointing to a message about the error details.
113 uint8 *result,
const char **errstr)
138 * Calculate ClientKey. Returns 0 on success, -1 on failure with *errstr
139 * pointing to a message about the error details.
144 uint8 *result,
const char **errstr)
154 if (
pg_hmac_init(ctx, salted_password, key_length) < 0 ||
168 * Calculate ServerKey. Returns 0 on success, -1 on failure with *errstr
169 * pointing to a message about the error details.
174 uint8 *result,
const char **errstr)
184 if (
pg_hmac_init(ctx, salted_password, key_length) < 0 ||
199 * Construct a SCRAM secret, for storing in pg_authid.rolpassword.
201 * The password should already have been processed with SASLprep, if necessary!
203 * The result is palloc'd or malloc'd, so caller is responsible for freeing it.
205 * On error, returns NULL and sets *errstr to point to a message about the
211 const char *
password,
const char **errstr)
219 int encoded_salt_len;
220 int encoded_stored_len;
221 int encoded_server_len;
224 /* Only this hash method is supported currently */
229 /* Calculate StoredKey and ServerKey */
232 salted_password, errstr) < 0 ||
234 stored_key, errstr) < 0 ||
235 scram_H(stored_key, hash_type, key_length,
236 stored_key, errstr) < 0 ||
238 server_key, errstr) < 0)
240 /* errstr is filled already here */
244 elog(
ERROR,
"could not calculate stored key and server key: %s",
251 * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
258 maxlen = strlen(
"SCRAM-SHA-256") + 1
259 + 10 + 1
/* iteration count */
260 + encoded_salt_len + 1
/* Base64-encoded salt */
261 + encoded_stored_len + 1
/* Base64-encoded StoredKey */
262 + encoded_server_len + 1;
/* Base64-encoded ServerKey */
268 *errstr =
_(
"out of memory");
278 encoded_result =
pg_b64_encode(salt, saltlen, p, encoded_salt_len);
279 if (encoded_result < 0)
281 *errstr =
_(
"could not encode salt");
295 if (encoded_result < 0)
297 *errstr =
_(
"could not encode stored key");
312 if (encoded_result < 0)
314 *errstr =
_(
"could not encode server key");
326 Assert(p - result <= maxlen);
int pg_b64_enc_len(int srclen)
int pg_b64_encode(const uint8 *src, int len, char *dst, int dstlen)
const char * pg_cryptohash_error(pg_cryptohash_ctx *ctx)
int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
pg_cryptohash_ctx * pg_cryptohash_create(pg_cryptohash_type type)
int pg_cryptohash_init(pg_cryptohash_ctx *ctx)
void pg_cryptohash_free(pg_cryptohash_ctx *ctx)
int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
Assert(PointerIsAligned(start, uint64))
pg_hmac_ctx * pg_hmac_create(pg_cryptohash_type type)
void pg_hmac_free(pg_hmac_ctx *ctx)
const char * pg_hmac_error(pg_hmac_ctx *ctx)
int pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
int pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
int pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
#define CHECK_FOR_INTERRUPTS()
int scram_ServerKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
int scram_SaltedPassword(const char *password, pg_cryptohash_type hash_type, int key_length, const uint8 *salt, int saltlen, int iterations, uint8 *result, const char **errstr)
char * scram_build_secret(pg_cryptohash_type hash_type, int key_length, const uint8 *salt, int saltlen, int iterations, const char *password, const char **errstr)
int scram_ClientKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
int scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
#define SCRAM_MAX_KEY_LEN