/*-------------------------------------------------------------------------
* scram - common . c
* Shared frontend / backend code for SCRAM authentication
*
* This contains the common low - level functions needed in both frontend and
* backend , for implement the Salted Challenge Response Authentication
* Mechanism ( SCRAM ) , per IETF ' s RFC 5802 .
*
* Portions Copyright ( c ) 2017 - 2025 , PostgreSQL Global Development Group
*
* IDENTIFICATION
* src / common / scram - common . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include "common/base64.h"
#include "common/hmac.h"
#include "common/scram-common.h"
#ifndef FRONTEND
#include "miscadmin.h"
#endif
#include "port/pg_bswap.h"
/*
* Calculate SaltedPassword .
*
* The password should already be normalized by SASLprep . Returns 0 on
* success , - 1 on failure with * errstr pointing to a message about the
* error details .
*/
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)
{
int password_len = strlen(password);
uint32 one = pg_hton32(1 );
int i,
j;
uint8 Ui[SCRAM_MAX_KEY_LEN];
uint8 Ui_prev[SCRAM_MAX_KEY_LEN];
pg_hmac_ctx *hmac_ctx = pg_hmac_create(hash_type);
if (hmac_ctx == NULL)
{
*errstr = pg_hmac_error(NULL); /* returns OOM */
return -1 ;
}
/*
* Iterate hash calculation of HMAC entry using given salt . This is
* essentially PBKDF2 ( see RFC2898 ) with HMAC ( ) as the pseudorandom
* function .
*/
/* First iteration */
if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
pg_hmac_update(hmac_ctx, salt, saltlen) < 0 ||
pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof (uint32)) < 0 ||
pg_hmac_final(hmac_ctx, Ui_prev, key_length) < 0 )
{
*errstr = pg_hmac_error(hmac_ctx);
pg_hmac_free(hmac_ctx);
return -1 ;
}
memcpy(result, Ui_prev, key_length);
/* Subsequent iterations */
for (i = 1 ; i < iterations; i++)
{
#ifndef FRONTEND
/*
* Make sure that this is interruptible as scram_iterations could be
* set to a large value .
*/
CHECK_FOR_INTERRUPTS();
#endif
if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, key_length) < 0 ||
pg_hmac_final(hmac_ctx, Ui, key_length) < 0 )
{
*errstr = pg_hmac_error(hmac_ctx);
pg_hmac_free(hmac_ctx);
return -1 ;
}
for (j = 0 ; j < key_length; j++)
result[j] ^= Ui[j];
memcpy(Ui_prev, Ui, key_length);
}
pg_hmac_free(hmac_ctx);
return 0 ;
}
/*
* Calculate hash for a NULL - terminated string . ( The NULL terminator is
* not included in the hash ) . Returns 0 on success , - 1 on failure with * errstr
* pointing to a message about the error details .
*/
int
scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length,
uint8 *result, const char **errstr)
{
pg_cryptohash_ctx *ctx;
ctx = pg_cryptohash_create(hash_type);
if (ctx == NULL)
{
*errstr = pg_cryptohash_error(NULL); /* returns OOM */
return -1 ;
}
if (pg_cryptohash_init(ctx) < 0 ||
pg_cryptohash_update(ctx, input, key_length) < 0 ||
pg_cryptohash_final(ctx, result, key_length) < 0 )
{
*errstr = pg_cryptohash_error(ctx);
pg_cryptohash_free(ctx);
return -1 ;
}
pg_cryptohash_free(ctx);
return 0 ;
}
/*
* Calculate ClientKey . Returns 0 on success , - 1 on failure with * errstr
* pointing to a message about the error details .
*/
int
scram_ClientKey(const uint8 *salted_password,
pg_cryptohash_type hash_type, int key_length,
uint8 *result, const char **errstr)
{
pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
if (ctx == NULL)
{
*errstr = pg_hmac_error(NULL); /* returns OOM */
return -1 ;
}
if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
pg_hmac_update(ctx, (uint8 *) "Client Key" , strlen("Client Key" )) < 0 ||
pg_hmac_final(ctx, result, key_length) < 0 )
{
*errstr = pg_hmac_error(ctx);
pg_hmac_free(ctx);
return -1 ;
}
pg_hmac_free(ctx);
return 0 ;
}
/*
* Calculate ServerKey . Returns 0 on success , - 1 on failure with * errstr
* pointing to a message about the error details .
*/
int
scram_ServerKey(const uint8 *salted_password,
pg_cryptohash_type hash_type, int key_length,
uint8 *result, const char **errstr)
{
pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
if (ctx == NULL)
{
*errstr = pg_hmac_error(NULL); /* returns OOM */
return -1 ;
}
if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
pg_hmac_update(ctx, (uint8 *) "Server Key" , strlen("Server Key" )) < 0 ||
pg_hmac_final(ctx, result, key_length) < 0 )
{
*errstr = pg_hmac_error(ctx);
pg_hmac_free(ctx);
return -1 ;
}
pg_hmac_free(ctx);
return 0 ;
}
/*
* Construct a SCRAM secret , for storing in pg_authid . rolpassword .
*
* The password should already have been processed with SASLprep , if necessary !
*
* The result is palloc ' d or malloc ' d , so caller is responsible for freeing it .
*
* On error , returns NULL and sets * errstr to point to a message about the
* error details .
*/
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)
{
uint8 salted_password[SCRAM_MAX_KEY_LEN];
uint8 stored_key[SCRAM_MAX_KEY_LEN];
uint8 server_key[SCRAM_MAX_KEY_LEN];
char *result;
char *p;
int maxlen;
int encoded_salt_len;
int encoded_stored_len;
int encoded_server_len;
int encoded_result;
/* Only this hash method is supported currently */
Assert(hash_type == PG_SHA256);
Assert(iterations > 0 );
/* Calculate StoredKey and ServerKey */
if (scram_SaltedPassword(password, hash_type, key_length,
salt, saltlen, iterations,
salted_password, errstr) < 0 ||
scram_ClientKey(salted_password, hash_type, key_length,
stored_key, errstr) < 0 ||
scram_H(stored_key, hash_type, key_length,
stored_key, errstr) < 0 ||
scram_ServerKey(salted_password, hash_type, key_length,
server_key, errstr) < 0 )
{
/* errstr is filled already here */
#ifdef FRONTEND
return NULL;
#else
elog(ERROR, "could not calculate stored key and server key: %s" ,
*errstr);
#endif
}
/*----------
* The format is :
* SCRAM - SHA - 256 $ < iteration count > : < salt > $ < StoredKey > : < ServerKey >
* - - - - - - - - - -
*/
encoded_salt_len = pg_b64_enc_len(saltlen);
encoded_stored_len = pg_b64_enc_len(key_length);
encoded_server_len = pg_b64_enc_len(key_length);
maxlen = strlen("SCRAM-SHA-256" ) + 1
+ 10 + 1 /* iteration count */
+ encoded_salt_len + 1 /* Base64-encoded salt */
+ encoded_stored_len + 1 /* Base64-encoded StoredKey */
+ encoded_server_len + 1 ; /* Base64-encoded ServerKey */
#ifdef FRONTEND
result = malloc(maxlen);
if (!result)
{
*errstr = _("out of memory" );
return NULL;
}
#else
result = palloc(maxlen);
#endif
p = result + sprintf(result, "SCRAM-SHA-256$%d:" , iterations);
/* salt */
encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
if (encoded_result < 0 )
{
*errstr = _("could not encode salt" );
#ifdef FRONTEND
free(result);
return NULL;
#else
elog(ERROR, "%s" , *errstr);
#endif
}
p += encoded_result;
*(p++) = '$' ;
/* stored key */
encoded_result = pg_b64_encode(stored_key, key_length, p,
encoded_stored_len);
if (encoded_result < 0 )
{
*errstr = _("could not encode stored key" );
#ifdef FRONTEND
free(result);
return NULL;
#else
elog(ERROR, "%s" , *errstr);
#endif
}
p += encoded_result;
*(p++) = ':' ;
/* server key */
encoded_result = pg_b64_encode(server_key, key_length, p,
encoded_server_len);
if (encoded_result < 0 )
{
*errstr = _("could not encode server key" );
#ifdef FRONTEND
free(result);
return NULL;
#else
elog(ERROR, "%s" , *errstr);
#endif
}
p += encoded_result;
*(p++) = '\0' ;
Assert(p - result <= maxlen);
return result;
}
Messung V0.5 in Prozent C=94 H=94 G=93
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland