pg_saslprep_rc rc;
    char        saltbuf[SCRAM_DEFAULT_SALT_LEN];
    char       *result;
+   const char *errstr = NULL;
 
    /*
     * Normalize the password with SASLprep.  If that doesn't work, because
                 errmsg("could not generate random salt")));
 
    result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
-                               SCRAM_DEFAULT_ITERATIONS, password);
+                               SCRAM_DEFAULT_ITERATIONS, password,
+                               &errstr);
 
    if (prep_password)
        pfree(prep_password);
    uint8       computed_key[SCRAM_KEY_LEN];
    char       *prep_password;
    pg_saslprep_rc rc;
+   const char *errstr = NULL;
 
    if (!parse_scram_secret(secret, &iterations, &encoded_salt,
                            stored_key, server_key))
 
    /* Compute Server Key based on the user-supplied plaintext password */
    if (scram_SaltedPassword(password, salt, saltlen, iterations,
-                            salted_password) < 0 ||
-       scram_ServerKey(salted_password, computed_key) < 0)
+                            salted_password, &errstr) < 0 ||
+       scram_ServerKey(salted_password, computed_key, &errstr) < 0)
    {
-       elog(ERROR, "could not compute server key");
+       elog(ERROR, "could not compute server key: %s", errstr);
    }
 
    if (prep_password)
    uint8       client_StoredKey[SCRAM_KEY_LEN];
    pg_hmac_ctx *ctx = pg_hmac_create(PG_SHA256);
    int         i;
+   const char *errstr = NULL;
 
    /*
     * Calculate ClientSignature.  Note that we don't log directly a failure
                       strlen(state->client_final_message_without_proof)) < 0 ||
        pg_hmac_final(ctx, ClientSignature, sizeof(ClientSignature)) < 0)
    {
-       elog(ERROR, "could not calculate client signature");
+       elog(ERROR, "could not calculate client signature: %s",
+            pg_hmac_error(ctx));
    }
 
    pg_hmac_free(ctx);
        ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
 
    /* Hash it one more time, and compare with StoredKey */
-   if (scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey) < 0)
-       elog(ERROR, "could not hash stored key");
+   if (scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey, &errstr) < 0)
+       elog(ERROR, "could not hash stored key: %s", errstr);
 
    if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
        return false;
                       strlen(state->client_final_message_without_proof)) < 0 ||
        pg_hmac_final(ctx, ServerSignature, sizeof(ServerSignature)) < 0)
    {
-       elog(ERROR, "could not calculate server signature");
+       elog(ERROR, "could not calculate server signature: %s",
+            pg_hmac_error(ctx));
    }
 
    pg_hmac_free(ctx);
 
 #define FREE(ptr) free(ptr)
 #endif
 
+/* Set of error states */
+typedef enum pg_hmac_errno
+{
+   PG_HMAC_ERROR_NONE = 0,
+   PG_HMAC_ERROR_OOM,
+   PG_HMAC_ERROR_INTERNAL
+} pg_hmac_errno;
+
 /* Internal pg_hmac_ctx structure */
 struct pg_hmac_ctx
 {
    pg_cryptohash_ctx *hash;
    pg_cryptohash_type type;
+   pg_hmac_errno error;
+   const char *errreason;
    int         block_size;
    int         digest_size;
 
        return NULL;
    memset(ctx, 0, sizeof(pg_hmac_ctx));
    ctx->type = type;
+   ctx->error = PG_HMAC_ERROR_NONE;
+   ctx->errreason = NULL;
 
    /*
     * Initialize the context data.  This requires to know the digest and
        /* temporary buffer for one-time shrink */
        shrinkbuf = ALLOC(digest_size);
        if (shrinkbuf == NULL)
+       {
+           ctx->error = PG_HMAC_ERROR_OOM;
            return -1;
+       }
        memset(shrinkbuf, 0, digest_size);
 
        hash_ctx = pg_cryptohash_create(ctx->type);
        if (hash_ctx == NULL)
        {
+           ctx->error = PG_HMAC_ERROR_OOM;
            FREE(shrinkbuf);
            return -1;
        }
            pg_cryptohash_update(hash_ctx, key, len) < 0 ||
            pg_cryptohash_final(hash_ctx, shrinkbuf, digest_size) < 0)
        {
+           ctx->error = PG_HMAC_ERROR_INTERNAL;
+           ctx->errreason = pg_cryptohash_error(hash_ctx);
            pg_cryptohash_free(hash_ctx);
            FREE(shrinkbuf);
            return -1;
    if (pg_cryptohash_init(ctx->hash) < 0 ||
        pg_cryptohash_update(ctx->hash, ctx->k_ipad, ctx->block_size) < 0)
    {
+       ctx->error = PG_HMAC_ERROR_INTERNAL;
+       ctx->errreason = pg_cryptohash_error(ctx->hash);
        if (shrinkbuf)
            FREE(shrinkbuf);
        return -1;
        return -1;
 
    if (pg_cryptohash_update(ctx->hash, data, len) < 0)
+   {
+       ctx->error = PG_HMAC_ERROR_INTERNAL;
+       ctx->errreason = pg_cryptohash_error(ctx->hash);
        return -1;
+   }
 
    return 0;
 }
 
    h = ALLOC(ctx->digest_size);
    if (h == NULL)
+   {
+       ctx->error = PG_HMAC_ERROR_OOM;
        return -1;
+   }
    memset(h, 0, ctx->digest_size);
 
    if (pg_cryptohash_final(ctx->hash, h, ctx->digest_size) < 0)
    {
+       ctx->error = PG_HMAC_ERROR_INTERNAL;
+       ctx->errreason = pg_cryptohash_error(ctx->hash);
        FREE(h);
        return -1;
    }
        pg_cryptohash_update(ctx->hash, h, ctx->digest_size) < 0 ||
        pg_cryptohash_final(ctx->hash, dest, len) < 0)
    {
+       ctx->error = PG_HMAC_ERROR_INTERNAL;
+       ctx->errreason = pg_cryptohash_error(ctx->hash);
        FREE(h);
        return -1;
    }
    explicit_bzero(ctx, sizeof(pg_hmac_ctx));
    FREE(ctx);
 }
+
+/*
+ * pg_hmac_error
+ *
+ * Returns a static string providing details about an error that happened
+ * during a HMAC computation.
+ */
+const char *
+pg_hmac_error(pg_hmac_ctx *ctx)
+{
+   if (ctx == NULL)
+       return _("out of memory");
+
+   /*
+    * If a reason is provided, rely on it, else fallback to any error code
+    * set.
+    */
+   if (ctx->errreason)
+       return ctx->errreason;
+
+   switch (ctx->error)
+   {
+       case PG_HMAC_ERROR_NONE:
+           return _("success");
+       case PG_HMAC_ERROR_INTERNAL:
+           return _("internal error");
+       case PG_HMAC_ERROR_OOM:
+           return _("out of memory");
+   }
+
+   Assert(false);              /* cannot be reached */
+   return _("success");
+}
 
 #include "postgres_fe.h"
 #endif
 
+
+#include <openssl/err.h>
 #include <openssl/hmac.h>
 
 #include "common/hmac.h"
 #define FREE(ptr) free(ptr)
 #endif                         /* FRONTEND */
 
+/* Set of error states */
+typedef enum pg_hmac_errno
+{
+   PG_HMAC_ERROR_NONE = 0,
+   PG_HMAC_ERROR_DEST_LEN,
+   PG_HMAC_ERROR_OPENSSL
+} pg_hmac_errno;
+
 /* Internal pg_hmac_ctx structure */
 struct pg_hmac_ctx
 {
    HMAC_CTX   *hmacctx;
    pg_cryptohash_type type;
+   pg_hmac_errno error;
+   const char *errreason;
 
 #ifndef FRONTEND
    ResourceOwner resowner;
 #endif
 };
 
+static const char *
+SSLerrmessage(unsigned long ecode)
+{
+   if (ecode == 0)
+       return NULL;
+
+   /*
+    * This may return NULL, but we would fall back to a default error path if
+    * that were the case.
+    */
+   return ERR_reason_error_string(ecode);
+}
+
 /*
  * pg_hmac_create
  *
    memset(ctx, 0, sizeof(pg_hmac_ctx));
 
    ctx->type = type;
+   ctx->error = PG_HMAC_ERROR_NONE;
+   ctx->errreason = NULL;
 
    /*
     * Initialization takes care of assigning the correct type for OpenSSL.
 
    /* OpenSSL internals return 1 on success, 0 on failure */
    if (status <= 0)
+   {
+       ctx->errreason = SSLerrmessage(ERR_get_error());
+       ctx->error = PG_HMAC_ERROR_OPENSSL;
        return -1;
+   }
 
    return 0;
 }
 
    /* OpenSSL internals return 1 on success, 0 on failure */
    if (status <= 0)
+   {
+       ctx->errreason = SSLerrmessage(ERR_get_error());
+       ctx->error = PG_HMAC_ERROR_OPENSSL;
        return -1;
+   }
    return 0;
 }
 
    {
        case PG_MD5:
            if (len < MD5_DIGEST_LENGTH)
+           {
+               ctx->error = PG_HMAC_ERROR_DEST_LEN;
                return -1;
+           }
            break;
        case PG_SHA1:
            if (len < SHA1_DIGEST_LENGTH)
+           {
+               ctx->error = PG_HMAC_ERROR_DEST_LEN;
                return -1;
+           }
            break;
        case PG_SHA224:
            if (len < PG_SHA224_DIGEST_LENGTH)
+           {
+               ctx->error = PG_HMAC_ERROR_DEST_LEN;
                return -1;
+           }
            break;
        case PG_SHA256:
            if (len < PG_SHA256_DIGEST_LENGTH)
+           {
+               ctx->error = PG_HMAC_ERROR_DEST_LEN;
                return -1;
+           }
            break;
        case PG_SHA384:
            if (len < PG_SHA384_DIGEST_LENGTH)
+           {
+               ctx->error = PG_HMAC_ERROR_DEST_LEN;
                return -1;
+           }
            break;
        case PG_SHA512:
            if (len < PG_SHA512_DIGEST_LENGTH)
+           {
+               ctx->error = PG_HMAC_ERROR_DEST_LEN;
                return -1;
+           }
            break;
    }
 
 
    /* OpenSSL internals return 1 on success, 0 on failure */
    if (status <= 0)
+   {
+       ctx->errreason = SSLerrmessage(ERR_get_error());
+       ctx->error = PG_HMAC_ERROR_OPENSSL;
        return -1;
+   }
    return 0;
 }
 
    explicit_bzero(ctx, sizeof(pg_hmac_ctx));
    FREE(ctx);
 }
+
+/*
+ * pg_hmac_error
+ *
+ * Returns a static string providing details about an error that happened
+ * during a HMAC computation.
+ */
+const char *
+pg_hmac_error(pg_hmac_ctx *ctx)
+{
+   if (ctx == NULL)
+       return _("out of memory");
+
+   /*
+    * If a reason is provided, rely on it, else fallback to any error code
+    * set.
+    */
+   if (ctx->errreason)
+       return ctx->errreason;
+
+   switch (ctx->error)
+   {
+       case PG_HMAC_ERROR_NONE:
+           return _("success");
+       case PG_HMAC_ERROR_DEST_LEN:
+           return _("destination buffer too small");
+       case PG_HMAC_ERROR_OPENSSL:
+           return _("OpenSSL failure");
+   }
+
+   Assert(false);              /* cannot be reached */
+   return _("success");
+}
 
  * Calculate SaltedPassword.
  *
  * The password should already be normalized by SASLprep.  Returns 0 on
- * success, -1 on failure.
+ * success, -1 on failure with *errstr pointing to a message about the
+ * error details.
  */
 int
 scram_SaltedPassword(const char *password,
                     const char *salt, int saltlen, int iterations,
-                    uint8 *result)
+                    uint8 *result, const char **errstr)
 {
    int         password_len = strlen(password);
    uint32      one = pg_hton32(1);
    pg_hmac_ctx *hmac_ctx = pg_hmac_create(PG_SHA256);
 
    if (hmac_ctx == NULL)
+   {
+       *errstr = pg_hmac_error(NULL);  /* returns OOM */
        return -1;
+   }
 
    /*
     * Iterate hash calculation of HMAC entry using given salt.  This is
        pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof(uint32)) < 0 ||
        pg_hmac_final(hmac_ctx, Ui_prev, sizeof(Ui_prev)) < 0)
    {
+       *errstr = pg_hmac_error(hmac_ctx);
        pg_hmac_free(hmac_ctx);
        return -1;
    }
            pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, SCRAM_KEY_LEN) < 0 ||
            pg_hmac_final(hmac_ctx, Ui, sizeof(Ui)) < 0)
        {
+           *errstr = pg_hmac_error(hmac_ctx);
            pg_hmac_free(hmac_ctx);
            return -1;
        }
 
 /*
  * Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is
- * not included in the hash).  Returns 0 on success, -1 on failure.
+ * 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, int len, uint8 *result)
+scram_H(const uint8 *input, int len, uint8 *result, const char **errstr)
 {
    pg_cryptohash_ctx *ctx;
 
    ctx = pg_cryptohash_create(PG_SHA256);
    if (ctx == NULL)
+   {
+       *errstr = pg_cryptohash_error(NULL);    /* returns OOM */
        return -1;
+   }
 
    if (pg_cryptohash_init(ctx) < 0 ||
        pg_cryptohash_update(ctx, input, len) < 0 ||
        pg_cryptohash_final(ctx, result, SCRAM_KEY_LEN) < 0)
    {
+       *errstr = pg_cryptohash_error(ctx);
        pg_cryptohash_free(ctx);
        return -1;
    }
 }
 
 /*
- * Calculate ClientKey.  Returns 0 on success, -1 on failure.
+ * 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, uint8 *result)
+scram_ClientKey(const uint8 *salted_password, uint8 *result,
+               const char **errstr)
 {
    pg_hmac_ctx *ctx = pg_hmac_create(PG_SHA256);
 
    if (ctx == NULL)
+   {
+       *errstr = pg_hmac_error(NULL);  /* returns OOM */
        return -1;
+   }
 
    if (pg_hmac_init(ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
        pg_hmac_update(ctx, (uint8 *) "Client Key", strlen("Client Key")) < 0 ||
        pg_hmac_final(ctx, result, SCRAM_KEY_LEN) < 0)
    {
+       *errstr = pg_hmac_error(ctx);
        pg_hmac_free(ctx);
        return -1;
    }
 }
 
 /*
- * Calculate ServerKey.  Returns 0 on success, -1 on failure.
+ * 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, uint8 *result)
+scram_ServerKey(const uint8 *salted_password, uint8 *result,
+               const char **errstr)
 {
    pg_hmac_ctx *ctx = pg_hmac_create(PG_SHA256);
 
    if (ctx == NULL)
+   {
+       *errstr = pg_hmac_error(NULL);  /* returns OOM */
        return -1;
+   }
 
    if (pg_hmac_init(ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
        pg_hmac_update(ctx, (uint8 *) "Server Key", strlen("Server Key")) < 0 ||
        pg_hmac_final(ctx, result, SCRAM_KEY_LEN) < 0)
    {
+       *errstr = pg_hmac_error(ctx);
        pg_hmac_free(ctx);
        return -1;
    }
  *
  * If iterations is 0, default number of iterations is used.  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(const char *salt, int saltlen, int iterations,
-                  const char *password)
+                  const char *password, const char **errstr)
 {
    uint8       salted_password[SCRAM_KEY_LEN];
    uint8       stored_key[SCRAM_KEY_LEN];
 
    /* Calculate StoredKey and ServerKey */
    if (scram_SaltedPassword(password, salt, saltlen, iterations,
-                            salted_password) < 0 ||
-       scram_ClientKey(salted_password, stored_key) < 0 ||
-       scram_H(stored_key, SCRAM_KEY_LEN, stored_key) < 0 ||
-       scram_ServerKey(salted_password, server_key) < 0)
+                            salted_password, errstr) < 0 ||
+       scram_ClientKey(salted_password, stored_key, errstr) < 0 ||
+       scram_H(stored_key, SCRAM_KEY_LEN, stored_key, errstr) < 0 ||
+       scram_ServerKey(salted_password, server_key, errstr) < 0)
    {
+       /* errstr is filled already here */
 #ifdef FRONTEND
        return NULL;
 #else
-       elog(ERROR, "could not calculate stored key and server key");
+       elog(ERROR, "could not calculate stored key and server key: %s",
+            *errstr);
 #endif
    }
 
 #ifdef FRONTEND
    result = malloc(maxlen);
    if (!result)
+   {
+       *errstr = _("out of memory");
        return NULL;
+   }
 #else
    result = palloc(maxlen);
 #endif
    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, "could not encode salt");
+       elog(ERROR, "%s", *errstr);
 #endif
    }
    p += encoded_result;
                                   encoded_stored_len);
    if (encoded_result < 0)
    {
+       *errstr = _("could not encode stored key");
 #ifdef FRONTEND
        free(result);
        return NULL;
 #else
-       elog(ERROR, "could not encode stored key");
+       elog(ERROR, "%s", *errstr);
 #endif
    }
 
                                   encoded_server_len);
    if (encoded_result < 0)
    {
+       *errstr = _("could not encode server key");
 #ifdef FRONTEND
        free(result);
        return NULL;
 #else
-       elog(ERROR, "could not encode server key");
+       elog(ERROR, "%s", *errstr);
 #endif
    }
 
 
 extern int pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len);
 extern int pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len);
 extern void pg_hmac_free(pg_hmac_ctx *ctx);
+extern const char *pg_hmac_error(pg_hmac_ctx *ctx);
 
 #endif                         /* PG_HMAC_H */
 
 #define SCRAM_DEFAULT_ITERATIONS   4096
 
 extern int scram_SaltedPassword(const char *password, const char *salt,
-                                int saltlen, int iterations, uint8 *result);
-extern int scram_H(const uint8 *str, int len, uint8 *result);
-extern int scram_ClientKey(const uint8 *salted_password, uint8 *result);
-extern int scram_ServerKey(const uint8 *salted_password, uint8 *result);
+                                int saltlen, int iterations, uint8 *result,
+                                const char **errstr);
+extern int scram_H(const uint8 *str, int len, uint8 *result,
+                   const char **errstr);
+extern int scram_ClientKey(const uint8 *salted_password, uint8 *result,
+                           const char **errstr);
+extern int scram_ServerKey(const uint8 *salted_password, uint8 *result,
+                           const char **errstr);
 
 extern char *scram_build_secret(const char *salt, int saltlen, int iterations,
-                               const char *password);
+                               const char *password, const char **errstr);
 
 #endif                         /* SCRAM_COMMON_H */
 
 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);
+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);
+                                  uint8 *result, const char **errstr);
 
 /*
  * Initialize SCRAM exchange status.
 {
    fe_scram_state *state = (fe_scram_state *) opaq;
    PGconn     *conn = state->conn;
+   const char *errstr = NULL;
 
    *done = false;
    *success = false;
             * Verify server signature, to make sure we're talking to the
             * genuine server.
             */
-           if (!verify_server_signature(state, success))
+           if (!verify_server_signature(state, success, &errstr))
            {
-               appendPQExpBufferStr(&conn->errorMessage,
-                                    libpq_gettext("could not verify server signature\n"));
+               appendPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("could not verify server signature: %s\n"), errstr);
                goto error;
            }
 
    uint8       client_proof[SCRAM_KEY_LEN];
    char       *result;
    int         encoded_len;
+   const char *errstr = NULL;
 
    initPQExpBuffer(&buf);
 
    /* Append proof to it, to form client-final-message. */
    if (!calculate_client_proof(state,
                                state->client_final_message_without_proof,
-                               client_proof))
+                               client_proof, &errstr))
    {
        termPQExpBuffer(&buf);
-       appendPQExpBufferStr(&conn->errorMessage,
-                            libpq_gettext("could not calculate client proof\n"));
+       appendPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("could not calculate client proof: %s\n"),
+                         errstr);
        return NULL;
    }
 
 
 /*
  * Calculate the client proof, part of the final exchange message sent
- * by the client.  Returns true on success, false on failure.
+ * 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)
+                      uint8 *result, const char **errstr)
 {
    uint8       StoredKey[SCRAM_KEY_LEN];
    uint8       ClientKey[SCRAM_KEY_LEN];
 
    ctx = pg_hmac_create(PG_SHA256);
    if (ctx == NULL)
+   {
+       *errstr = pg_hmac_error(NULL);  /* returns OOM */
        return false;
+   }
 
    /*
     * Calculate SaltedPassword, and store it in 'state' so that we can reuse
     * it later in verify_server_signature.
     */
    if (scram_SaltedPassword(state->password, state->salt, state->saltlen,
-                            state->iterations, state->SaltedPassword) < 0 ||
-       scram_ClientKey(state->SaltedPassword, ClientKey) < 0 ||
-       scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey) < 0 ||
-       pg_hmac_init(ctx, StoredKey, SCRAM_KEY_LEN) < 0 ||
+                            state->iterations, state->SaltedPassword,
+                            errstr) < 0 ||
+       scram_ClientKey(state->SaltedPassword, ClientKey, errstr) < 0 ||
+       scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey, errstr) < 0)
+   {
+       /* errstr is already filled here */
+       pg_hmac_free(ctx);
+       return false;
+   }
+
+   if (pg_hmac_init(ctx, StoredKey, SCRAM_KEY_LEN) < 0 ||
        pg_hmac_update(ctx,
                       (uint8 *) state->client_first_message_bare,
                       strlen(state->client_first_message_bare)) < 0 ||
                       strlen(client_final_message_without_proof)) < 0 ||
        pg_hmac_final(ctx, ClientSignature, sizeof(ClientSignature)) < 0)
    {
+       *errstr = pg_hmac_error(ctx);
        pg_hmac_free(ctx);
        return false;
    }
  * 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.
+ * 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)
+verify_server_signature(fe_scram_state *state, bool *match,
+                       const char **errstr)
 {
    uint8       expected_ServerSignature[SCRAM_KEY_LEN];
    uint8       ServerKey[SCRAM_KEY_LEN];
 
    ctx = pg_hmac_create(PG_SHA256);
    if (ctx == NULL)
+   {
+       *errstr = pg_hmac_error(NULL);  /* returns OOM */
+       return false;
+   }
+
+   if (scram_ServerKey(state->SaltedPassword, ServerKey, errstr) < 0)
+   {
+       /* errstr is filled already */
+       pg_hmac_free(ctx);
        return false;
+   }
 
-   if (scram_ServerKey(state->SaltedPassword, ServerKey) < 0 ||
    /* calculate ServerSignature */
-       pg_hmac_init(ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
+   if (pg_hmac_init(ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
        pg_hmac_update(ctx,
                       (uint8 *) state->client_first_message_bare,
                       strlen(state->client_first_message_bare)) < 0 ||
        pg_hmac_final(ctx, expected_ServerSignature,
                      sizeof(expected_ServerSignature)) < 0)
    {
+       *errstr = pg_hmac_error(ctx);
        pg_hmac_free(ctx);
        return false;
    }
 
 /*
  * 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)
+pg_fe_scram_build_secret(const char *password, const char **errstr)
 {
    char       *prep_password;
    pg_saslprep_rc rc;
     */
    rc = pg_saslprep(password, &prep_password);
    if (rc == SASLPREP_OOM)
+   {
+       *errstr = _("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 = _("failed to generate random salt");
        if (prep_password)
            free(prep_password);
        return NULL;
    }
 
    result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
-                               SCRAM_DEFAULT_ITERATIONS, password);
+                               SCRAM_DEFAULT_ITERATIONS, password,
+                               errstr);
 
    if (prep_password)
        free(prep_password);
 
     */
    if (strcmp(algorithm, "scram-sha-256") == 0)
    {
-       crypt_pwd = pg_fe_scram_build_secret(passwd);
-       /* We assume the only possible failure is OOM */
+       const char *errstr = NULL;
+
+       crypt_pwd = pg_fe_scram_build_secret(passwd, &errstr);
        if (!crypt_pwd)
-           appendPQExpBufferStr(&conn->errorMessage,
-                                libpq_gettext("out of memory\n"));
+           appendPQExpBuffer(&conn->errorMessage,
+                             libpq_gettext("could not encrypt password: %s\n"),
+                             errstr);
    }
    else if (strcmp(algorithm, "md5") == 0)
    {
 
 
 /* Mechanisms in fe-auth-scram.c */
 extern const pg_fe_sasl_mech pg_scram_mech;
-extern char *pg_fe_scram_build_secret(const char *password);
+extern char *pg_fe_scram_build_secret(const char *password,
+                                     const char **errstr);
 
 #endif                         /* FE_AUTH_H */
 
 pg_funcptr_t
 pg_gssinfo
 pg_hmac_ctx
+pg_hmac_errno
 pg_int64
 pg_local_to_utf_combined
 pg_locale_t