Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/interfaces/libpq/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 23 kB image not shown  

Quelle  fe-auth-scram.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * fe-auth-scram.c
 *    The front-end (client) implementation of SCRAM authentication.
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *   src/interfaces/libpq/fe-auth-scram.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres_fe.h"

#include "common/base64.h"
#include "common/hmac.h"
#include "common/saslprep.h"
#include "common/scram-common.h"
#include "fe-auth.h"


/* The exported SCRAM callback mechanism. */
static void *scram_init(PGconn *conn, const char *password,
      const char *sasl_mechanism);
static SASLStatus scram_exchange(void *opaq, bool final,
         char *input, int inputlen,
         char **output, int *outputlen);
static bool scram_channel_bound(void *opaq);
static void scram_free(void *opaq);

const pg_fe_sasl_mech pg_scram_mech = {
 scram_init,
 scram_exchange,
 scram_channel_bound,
 scram_free
};

/*
 * Status of exchange messages used for SCRAM authentication via the
 * SASL protocol.
 */

typedef enum
{
 FE_SCRAM_INIT,
 FE_SCRAM_NONCE_SENT,
 FE_SCRAM_PROOF_SENT,
 FE_SCRAM_FINISHED,
} fe_scram_state_enum;

typedef struct
{
 fe_scram_state_enum state;

 /* These are supplied by the user */
 PGconn    *conn;
 char    *password;
 char    *sasl_mechanism;

 /* State data depending on the hash type */
 pg_cryptohash_type hash_type;
 int   key_length;

 /* We construct these */
 uint8  SaltedPassword[SCRAM_MAX_KEY_LEN];
 char    *client_nonce;
 char    *client_first_message_bare;
 char    *client_final_message_without_proof;

 /* These come from the server-first message */
 char    *server_first_message;
 uint8    *salt;
 int   saltlen;
 int   iterations;
 char    *nonce;

 /* These come from the server-final message */
 char    *server_final_message;
 uint8  ServerSignature[SCRAM_MAX_KEY_LEN];
} fe_scram_state;

static bool read_server_first_message(fe_scram_state *state, char *input);
static bool read_server_final_message(fe_scram_state *state, char *input);
static char *build_client_first_message(fe_scram_state *state);
static char *build_client_final_message(fe_scram_state *state);
static bool verify_server_signature(fe_scram_state *state, bool *match,
         const char **errstr);
static bool calculate_client_proof(fe_scram_state *state,
           const char *client_final_message_without_proof,
           uint8 *result, const char **errstr);

/*
 * Initialize SCRAM exchange status.
 */

static void *
scram_init(PGconn *conn,
     const char *password,
     const char *sasl_mechanism)
{
 fe_scram_state *state;
 char    *prep_password;
 pg_saslprep_rc rc;

 Assert(sasl_mechanism != NULL);

 state = (fe_scram_state *) malloc(sizeof(fe_scram_state));
 if (!state)
  return NULL;
 memset(state, 0sizeof(fe_scram_state));
 state->conn = conn;
 state->state = FE_SCRAM_INIT;
 state->key_length = SCRAM_SHA_256_KEY_LEN;
 state->hash_type = PG_SHA256;

 state->sasl_mechanism = strdup(sasl_mechanism);
 if (!state->sasl_mechanism)
 {
  free(state);
  return NULL;
 }

 if (password)
 {
  /* Normalize the password with SASLprep, if possible */
  rc = pg_saslprep(password, &prep_password);
  if (rc == SASLPREP_OOM)
  {
   free(state->sasl_mechanism);
   free(state);
   return NULL;
  }
  if (rc != SASLPREP_SUCCESS)
  {
   prep_password = strdup(password);
   if (!prep_password)
   {
    free(state->sasl_mechanism);
    free(state);
    return NULL;
   }
  }
  state->password = prep_password;
 }

 return state;
}

/*
 * Return true if channel binding was employed and the SCRAM exchange
 * completed. This should be used after a successful exchange to determine
 * whether the server authenticated itself to the client.
 *
 * Note that the caller must also ensure that the exchange was actually
 * successful.
 */

static bool
scram_channel_bound(void *opaq)
{
 fe_scram_state *state = (fe_scram_state *) opaq;

 /* no SCRAM exchange done */
 if (state == NULL)
  return false;

 /* SCRAM exchange not completed */
 if (state->state != FE_SCRAM_FINISHED)
  return false;

 /* channel binding mechanism not used */
 if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
  return false;

 /* all clear! */
 return true;
}

/*
 * Free SCRAM exchange status
 */

static void
scram_free(void *opaq)
{
 fe_scram_state *state = (fe_scram_state *) opaq;

 free(state->password);
 free(state->sasl_mechanism);

 /* client messages */
 free(state->client_nonce);
 free(state->client_first_message_bare);
 free(state->client_final_message_without_proof);

 /* first message from server */
 free(state->server_first_message);
 free(state->salt);
 free(state->nonce);

 /* final message from server */
 free(state->server_final_message);

 free(state);
}

/*
 * Exchange a SCRAM message with backend.
 */

static SASLStatus
scram_exchange(void *opaq, bool final,
      char *input, int inputlen,
      char **output, int *outputlen)
{
 fe_scram_state *state = (fe_scram_state *) opaq;
 PGconn    *conn = state->conn;
 const char *errstr = NULL;

 *output = NULL;
 *outputlen = 0;

 /*
  * Check that the input length agrees with the string length of the input.
  * We can ignore inputlen after this.
 */

 if (state->state != FE_SCRAM_INIT)
 {
  if (inputlen == 0)
  {
   libpq_append_conn_error(conn, "malformed SCRAM message (empty message)");
   return SASL_FAILED;
  }
  if (inputlen != strlen(input))
  {
   libpq_append_conn_error(conn, "malformed SCRAM message (length mismatch)");
   return SASL_FAILED;
  }
 }

 switch (state->state)
 {
  case FE_SCRAM_INIT:
   /* Begin the SCRAM handshake, by sending client nonce */
   *output = build_client_first_message(state);
   if (*output == NULL)
    return SASL_FAILED;

   *outputlen = strlen(*output);
   state->state = FE_SCRAM_NONCE_SENT;
   return SASL_CONTINUE;

  case FE_SCRAM_NONCE_SENT:
   /* Receive salt and server nonce, send response. */
   if (!read_server_first_message(state, input))
    return SASL_FAILED;

   *output = build_client_final_message(state);
   if (*output == NULL)
    return SASL_FAILED;

   *outputlen = strlen(*output);
   state->state = FE_SCRAM_PROOF_SENT;
   return SASL_CONTINUE;

  case FE_SCRAM_PROOF_SENT:
   {
    bool  match;

    /* Receive server signature */
    if (!read_server_final_message(state, input))
     return SASL_FAILED;

    /*
     * Verify server signature, to make sure we're talking to the
     * genuine server.
 */

    if (!verify_server_signature(state, &match, &errstr))
    {
     libpq_append_conn_error(conn, "could not verify server signature: %s", errstr);
     return SASL_FAILED;
    }

    if (!match)
    {
     libpq_append_conn_error(conn, "incorrect server signature");
    }
    state->state = FE_SCRAM_FINISHED;
    state->conn->client_finished_auth = true;
    return match ? SASL_COMPLETE : SASL_FAILED;
   }

  default:
   /* shouldn't happen */
   libpq_append_conn_error(conn, "invalid SCRAM exchange state");
   break;
 }

 return SASL_FAILED;
}

/*
 * Read value for an attribute part of a SCRAM message.
 *
 * The buffer at **input is destructively modified, and *input is
 * advanced over the "attr=value" string and any following comma.
 *
 * On failure, append an error message to *errorMessage and return NULL.
 */

static char *
read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
{
 char    *begin = *input;
 char    *end;

 if (*begin != attr)
 {
  libpq_append_error(errorMessage,
         "malformed SCRAM message (attribute \"%c\" expected)",
         attr);
  return NULL;
 }
 begin++;

 if (*begin != '=')
 {
  libpq_append_error(errorMessage,
         "malformed SCRAM message (expected character \"=\" for attribute \"%c\")",
         attr);
  return NULL;
 }
 begin++;

 end = begin;
 while (*end && *end != ',')
  end++;

 if (*end)
 {
  *end = '\0';
  *input = end + 1;
 }
 else
  *input = end;

 return begin;
}

/*
 * Build the first exchange message sent by the client.
 */

static char *
build_client_first_message(fe_scram_state *state)
{
 PGconn    *conn = state->conn;
 uint8  raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
 char    *result;
 int   channel_info_len;
 int   encoded_len;
 PQExpBufferData buf;

 /*
  * Generate a "raw" nonce.  This is converted to ASCII-printable form by
  * base64-encoding it.
 */

 if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
 {
  libpq_append_conn_error(conn, "could not generate nonce");
  return NULL;
 }

 encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
 /* don't forget the zero-terminator */
 state->client_nonce = malloc(encoded_len + 1);
 if (state->client_nonce == NULL)
 {
  libpq_append_conn_error(conn, "out of memory");
  return NULL;
 }
 encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN,
        state->client_nonce, encoded_len);
 if (encoded_len < 0)
 {
  libpq_append_conn_error(conn, "could not encode nonce");
  return NULL;
 }
 state->client_nonce[encoded_len] = '\0';

 /*
  * Generate message.  The username is left empty as the backend uses the
  * value provided by the startup packet.  Also, as this username is not
  * prepared with SASLprep, the message parsing would fail if it includes
  * '=' or ',' characters.
 */


 initPQExpBuffer(&buf);

 /*
  * First build the gs2-header with channel binding information.
 */

 if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
 {
  Assert(conn->ssl_in_use);
  appendPQExpBufferStr(&buf, "p=tls-server-end-point");
 }
#ifdef USE_SSL
 else if (conn->channel_binding[0] != 'd' && /* disable */
    conn->ssl_in_use)
 {
  /*
   * Client supports channel binding, but thinks the server does not.
 */

  appendPQExpBufferChar(&buf, 'y');
 }
#endif
 else
 {
  /*
   * Client does not support channel binding, or has disabled it.
 */

  appendPQExpBufferChar(&buf, 'n');
 }

 if (PQExpBufferDataBroken(buf))
  goto oom_error;

 channel_info_len = buf.len;

 appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce);
 if (PQExpBufferDataBroken(buf))
  goto oom_error;

 /*
  * The first message content needs to be saved without channel binding
  * information.
 */

 state->client_first_message_bare = strdup(buf.data + channel_info_len + 2);
 if (!state->client_first_message_bare)
  goto oom_error;

 result = strdup(buf.data);
 if (result == NULL)
  goto oom_error;

 termPQExpBuffer(&buf);
 return result;

oom_error:
 termPQExpBuffer(&buf);
 libpq_append_conn_error(conn, "out of memory");
 return NULL;
}

/*
 * Build the final exchange message sent from the client.
 */

static char *
build_client_final_message(fe_scram_state *state)
{
 PQExpBufferData buf;
 PGconn    *conn = state->conn;
 uint8  client_proof[SCRAM_MAX_KEY_LEN];
 char    *result;
 int   encoded_len;
 const char *errstr = NULL;

 initPQExpBuffer(&buf);

 /*
  * Construct client-final-message-without-proof.  We need to remember it
  * for verifying the server proof in the final step of authentication.
  *
  * The channel binding flag handling (p/y/n) must be consistent with
  * build_client_first_message(), because the server will check that it's
  * the same flag both times.
 */

 if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
 {
#ifdef USE_SSL
  char    *cbind_data = NULL;
  size_t  cbind_data_len = 0;
  size_t  cbind_header_len;
  char    *cbind_input;
  size_t  cbind_input_len;
  int   encoded_cbind_len;

  /* Fetch hash data of server's SSL certificate */
  cbind_data =
   pgtls_get_peer_certificate_hash(state->conn,
           &cbind_data_len);
  if (cbind_data == NULL)
  {
   /* error message is already set on error */
   termPQExpBuffer(&buf);
   return NULL;
  }

  appendPQExpBufferStr(&buf, "c=");

  /* p=type,, */
  cbind_header_len = strlen("p=tls-server-end-point,,");
  cbind_input_len = cbind_header_len + cbind_data_len;
  cbind_input = malloc(cbind_input_len);
  if (!cbind_input)
  {
   free(cbind_data);
   goto oom_error;
  }
  memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
  memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);

  encoded_cbind_len = pg_b64_enc_len(cbind_input_len);
  if (!enlargePQExpBuffer(&buf, encoded_cbind_len))
  {
   free(cbind_data);
   free(cbind_input);
   goto oom_error;
  }
  encoded_cbind_len = pg_b64_encode((uint8 *) cbind_input, cbind_input_len,
            buf.data + buf.len,
            encoded_cbind_len);
  if (encoded_cbind_len < 0)
  {
   free(cbind_data);
   free(cbind_input);
   termPQExpBuffer(&buf);
   appendPQExpBufferStr(&conn->errorMessage,
         "could not encode cbind data for channel binding\n");
   return NULL;
  }
  buf.len += encoded_cbind_len;
  buf.data[buf.len] = '\0';

  free(cbind_data);
  free(cbind_input);
#else
  /*
   * Chose channel binding, but the SSL library doesn't support it.
   * Shouldn't happen.
 */

  termPQExpBuffer(&buf);
  appendPQExpBufferStr(&conn->errorMessage,
        "channel binding not supported by this build\n");
  return NULL;
#endif       /* USE_SSL */
 }
#ifdef USE_SSL
 else if (conn->channel_binding[0] != 'd' && /* disable */
    conn->ssl_in_use)
  appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */
#endif
 else
  appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */

 if (PQExpBufferDataBroken(buf))
  goto oom_error;

 appendPQExpBuffer(&buf, ",r=%s", state->nonce);
 if (PQExpBufferDataBroken(buf))
  goto oom_error;

 state->client_final_message_without_proof = strdup(buf.data);
 if (state->client_final_message_without_proof == NULL)
  goto oom_error;

 /* Append proof to it, to form client-final-message. */
 if (!calculate_client_proof(state,
        state->client_final_message_without_proof,
        client_proof, &errstr))
 {
  termPQExpBuffer(&buf);
  libpq_append_conn_error(conn, "could not calculate client proof: %s", errstr);
  return NULL;
 }

 appendPQExpBufferStr(&buf, ",p=");
 encoded_len = pg_b64_enc_len(state->key_length);
 if (!enlargePQExpBuffer(&buf, encoded_len))
  goto oom_error;
 encoded_len = pg_b64_encode(client_proof,
        state->key_length,
        buf.data + buf.len,
        encoded_len);
 if (encoded_len < 0)
 {
  termPQExpBuffer(&buf);
  libpq_append_conn_error(conn, "could not encode client proof");
  return NULL;
 }
 buf.len += encoded_len;
 buf.data[buf.len] = '\0';

 result = strdup(buf.data);
 if (result == NULL)
  goto oom_error;

 termPQExpBuffer(&buf);
 return result;

oom_error:
 termPQExpBuffer(&buf);
 libpq_append_conn_error(conn, "out of memory");
 return NULL;
}

/*
 * Read the first exchange message coming from the server.
 */

static bool
read_server_first_message(fe_scram_state *state, char *input)
{
 PGconn    *conn = state->conn;
 char    *iterations_str;
 char    *endptr;
 char    *encoded_salt;
 char    *nonce;
 int   decoded_salt_len;

 state->server_first_message = strdup(input);
 if (state->server_first_message == NULL)
 {
  libpq_append_conn_error(conn, "out of memory");
  return false;
 }

 /* parse the message */
 nonce = read_attr_value(&input, 'r',
       &conn->errorMessage);
 if (nonce == NULL)
 {
  /* read_attr_value() has appended an error string */
  return false;
 }

 /* Verify immediately that the server used our part of the nonce */
 if (strlen(nonce) < strlen(state->client_nonce) ||
  timingsafe_bcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
 {
  libpq_append_conn_error(conn, "invalid SCRAM response (nonce mismatch)");
  return false;
 }

 state->nonce = strdup(nonce);
 if (state->nonce == NULL)
 {
  libpq_append_conn_error(conn, "out of memory");
  return false;
 }

 encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
 if (encoded_salt == NULL)
 {
  /* read_attr_value() has appended an error string */
  return false;
 }
 decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
 state->salt = malloc(decoded_salt_len);
 if (state->salt == NULL)
 {
  libpq_append_conn_error(conn, "out of memory");
  return false;
 }
 state->saltlen = pg_b64_decode(encoded_salt,
           strlen(encoded_salt),
           state->salt,
           decoded_salt_len);
 if (state->saltlen < 0)
 {
  libpq_append_conn_error(conn, "malformed SCRAM message (invalid salt)");
  return false;
 }

 iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
 if (iterations_str == NULL)
 {
  /* read_attr_value() has appended an error string */
  return false;
 }
 state->iterations = strtol(iterations_str, &endptr, 10);
 if (*endptr != '\0' || state->iterations < 1)
 {
  libpq_append_conn_error(conn, "malformed SCRAM message (invalid iteration count)");
  return false;
 }

 if (*input != '\0')
  libpq_append_conn_error(conn, "malformed SCRAM message (garbage at end of server-first-message)");

 return true;
}

/*
 * Read the final exchange message coming from the server.
 */

static bool
read_server_final_message(fe_scram_state *state, char *input)
{
 PGconn    *conn = state->conn;
 char    *encoded_server_signature;
 uint8    *decoded_server_signature;
 int   server_signature_len;

 state->server_final_message = strdup(input);
 if (!state->server_final_message)
 {
  libpq_append_conn_error(conn, "out of memory");
  return false;
 }

 /* Check for error result. */
 if (*input == 'e')
 {
  char    *errmsg = read_attr_value(&input, 'e',
            &conn->errorMessage);

  if (errmsg == NULL)
  {
   /* read_attr_value() has appended an error message */
   return false;
  }
  libpq_append_conn_error(conn, "error received from server in SCRAM exchange: %s",
        errmsg);
  return false;
 }

 /* Parse the message. */
 encoded_server_signature = read_attr_value(&input, 'v',
              &conn->errorMessage);
 if (encoded_server_signature == NULL)
 {
  /* read_attr_value() has appended an error message */
  return false;
 }

 if (*input != '\0')
  libpq_append_conn_error(conn, "malformed SCRAM message (garbage at end of server-final-message)");

 server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
 decoded_server_signature = malloc(server_signature_len);
 if (!decoded_server_signature)
 {
  libpq_append_conn_error(conn, "out of memory");
  return false;
 }

 server_signature_len = pg_b64_decode(encoded_server_signature,
           strlen(encoded_server_signature),
           decoded_server_signature,
           server_signature_len);
 if (server_signature_len != state->key_length)
 {
  free(decoded_server_signature);
  libpq_append_conn_error(conn, "malformed SCRAM message (invalid server signature)");
  return false;
 }
 memcpy(state->ServerSignature, decoded_server_signature,
     state->key_length);
 free(decoded_server_signature);

 return true;
}

/*
 * Calculate the client proof, part of the final exchange message sent
 * by the client.  Returns true on success, false on failure with *errstr
 * pointing to a message about the error details.
 */

static bool
calculate_client_proof(fe_scram_state *state,
        const char *client_final_message_without_proof,
        uint8 *result, const char **errstr)
{
 uint8  StoredKey[SCRAM_MAX_KEY_LEN];
 uint8  ClientKey[SCRAM_MAX_KEY_LEN];
 uint8  ClientSignature[SCRAM_MAX_KEY_LEN];
 int   i;
 pg_hmac_ctx *ctx;

 ctx = pg_hmac_create(state->hash_type);
 if (ctx == NULL)
 {
  *errstr = pg_hmac_error(NULL); /* returns OOM */
  return false;
 }

 if (state->conn->scram_client_key_binary)
 {
  memcpy(ClientKey, state->conn->scram_client_key_binary, SCRAM_MAX_KEY_LEN);
 }
 else
 {
  /*
   * Calculate SaltedPassword, and store it in 'state' so that we can
   * reuse it later in verify_server_signature.
 */

  if (scram_SaltedPassword(state->password, state->hash_type,
         state->key_length, state->salt, state->saltlen,
         state->iterations, state->SaltedPassword,
         errstr) < 0 ||
   scram_ClientKey(state->SaltedPassword, state->hash_type,
       state->key_length, ClientKey, errstr) < 0)
  {
   /* errstr is already filled here */
   pg_hmac_free(ctx);
   return false;
  }
 }

 if (scram_H(ClientKey, state->hash_type, state->key_length, StoredKey, errstr) < 0)
 {
  pg_hmac_free(ctx);
  return false;
 }

 if (pg_hmac_init(ctx, StoredKey, state->key_length) < 0 ||
  pg_hmac_update(ctx,
        (uint8 *) state->client_first_message_bare,
        strlen(state->client_first_message_bare)) < 0 ||
  pg_hmac_update(ctx, (uint8 *) ","1) < 0 ||
  pg_hmac_update(ctx,
        (uint8 *) state->server_first_message,
        strlen(state->server_first_message)) < 0 ||
  pg_hmac_update(ctx, (uint8 *) ","1) < 0 ||
  pg_hmac_update(ctx,
        (uint8 *) client_final_message_without_proof,
        strlen(client_final_message_without_proof)) < 0 ||
  pg_hmac_final(ctx, ClientSignature, state->key_length) < 0)
 {
  *errstr = pg_hmac_error(ctx);
  pg_hmac_free(ctx);
  return false;
 }

 for (i = 0; i < state->key_length; i++)
  result[i] = ClientKey[i] ^ ClientSignature[i];

 pg_hmac_free(ctx);
 return true;
}

/*
 * Validate the server signature, received as part of the final exchange
 * message received from the server.  *match tracks if the server signature
 * matched or not. Returns true if the server signature got verified, and
 * false for a processing error with *errstr pointing to a message about the
 * error details.
 */

static bool
verify_server_signature(fe_scram_state *state, bool *match,
      const char **errstr)
{
 uint8  expected_ServerSignature[SCRAM_MAX_KEY_LEN];
 uint8  ServerKey[SCRAM_MAX_KEY_LEN];
 pg_hmac_ctx *ctx;

 ctx = pg_hmac_create(state->hash_type);
 if (ctx == NULL)
 {
  *errstr = pg_hmac_error(NULL); /* returns OOM */
  return false;
 }

 if (state->conn->scram_server_key_binary)
 {
  memcpy(ServerKey, state->conn->scram_server_key_binary, SCRAM_MAX_KEY_LEN);
 }
 else
 {
  if (scram_ServerKey(state->SaltedPassword, state->hash_type,
       state->key_length, ServerKey, errstr) < 0)
  {
   /* errstr is filled already */
   pg_hmac_free(ctx);
   return false;
  }
 }

 /* calculate ServerSignature */
 if (pg_hmac_init(ctx, ServerKey, state->key_length) < 0 ||
  pg_hmac_update(ctx,
        (uint8 *) state->client_first_message_bare,
        strlen(state->client_first_message_bare)) < 0 ||
  pg_hmac_update(ctx, (uint8 *) ","1) < 0 ||
  pg_hmac_update(ctx,
        (uint8 *) state->server_first_message,
        strlen(state->server_first_message)) < 0 ||
  pg_hmac_update(ctx, (uint8 *) ","1) < 0 ||
  pg_hmac_update(ctx,
        (uint8 *) state->client_final_message_without_proof,
        strlen(state->client_final_message_without_proof)) < 0 ||
  pg_hmac_final(ctx, expected_ServerSignature,
       state->key_length) < 0)
 {
  *errstr = pg_hmac_error(ctx);
  pg_hmac_free(ctx);
  return false;
 }

 pg_hmac_free(ctx);

 /* signature processed, so now check after it */
 if (timingsafe_bcmp(expected_ServerSignature, state->ServerSignature,
      state->key_length) != 0)
  *match = false;
 else
  *match = true;

 return true;
}

/*
 * Build a new SCRAM secret.
 *
 * On error, returns NULL and sets *errstr to point to a message about the
 * error details.
 */

char *
pg_fe_scram_build_secret(const char *password, int iterations, const char **errstr)
{
 char    *prep_password;
 pg_saslprep_rc rc;
 uint8  saltbuf[SCRAM_DEFAULT_SALT_LEN];
 char    *result;

 /*
  * Normalize the password with SASLprep.  If that doesn't work, because
  * the password isn't valid UTF-8 or contains prohibited characters, just
  * proceed with the original password.  (See comments at the top of
  * auth-scram.c.)
 */

 rc = pg_saslprep(password, &prep_password);
 if (rc == SASLPREP_OOM)
 {
  *errstr = libpq_gettext("out of memory");
  return NULL;
 }
 if (rc == SASLPREP_SUCCESS)
  password = (const char *) prep_password;

 /* Generate a random salt */
 if (!pg_strong_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
 {
  *errstr = libpq_gettext("could not generate random salt");
  free(prep_password);
  return NULL;
 }

 result = scram_build_secret(PG_SHA256, SCRAM_SHA_256_KEY_LEN, saltbuf,
        SCRAM_DEFAULT_SALT_LEN,
        iterations, password,
        errstr);

 free(prep_password);

 return result;
}

Messung V0.5 in Prozent
C=93 H=93 G=92

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet am  2026-08-06) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.