Subject: Re: [HACKERS] Changing references of password encryption to hashing

From: Bruce Momjian <[email-masked]>
Date: Tue, 26 Dec 2023 20:04:37 -0500

On Tue, Nov 28, 2023 at 10:01:57AM -0500, Robert Haas wrote:
> On Tue, Nov 28, 2023 at 9:55 AM Stephen Frost <[email-masked]> wrote:
> > I do think we should use the correct terminology in our documentation
> > and would support your working on improving things in this area.
> 
> +1.

Attached is a draft patch to use the term "hash" instead of "encrypt"
for password storage.  I was not able to use Michael Paquier's version
from 2017 because the code has changed too much.

I did _not_ change the user API, so CREATE/ALTER ROLE still uses
[ENCRYPTED] PASSWORD, the GUC is still called password_encryption, and
the libpq function is still called PQencryptPasswordConn().  This makes
the user interface confusing since the API uses "encryption" but the
text calls it "hashing".  Is there support for renaming the API to use
"hash" and keeping "encrypt" for backward compatiblity.

-- 
  Bruce Momjian  <[email-masked]>        https://momjian.us
  EDB                                      https://enterprisedb.com

  Only you can decide what is important to you.
diff --git a/contrib/passwordcheck/expected/passwordcheck.out b/contrib/passwordcheck/expected/passwordcheck.out
index 2027681daf..627b556bdf 100644
--- a/contrib/passwordcheck/expected/passwordcheck.out
+++ b/contrib/passwordcheck/expected/passwordcheck.out
@@ -11,7 +11,7 @@ ERROR:  password must not contain user name
 -- error: contains only letters
 ALTER USER regress_passwordcheck_user1 PASSWORD 'alessnicelongpassword';
 ERROR:  password must contain both letters and nonletters
--- encrypted ok (password is "secret")
+-- hashed ok (password is "secret")
 ALTER USER regress_passwordcheck_user1 PASSWORD 'md592350e12ac34e52dd598f90893bb3ae7';
 -- error: password is user name
 ALTER USER regress_passwordcheck_user1 PASSWORD 'md507a112732ed9f2087fa90b192d44e358';
diff --git a/contrib/passwordcheck/expected/passwordcheck_1.out b/contrib/passwordcheck/expected/passwordcheck_1.out
index 5d8d5dcc1c..cff6da9a63 100644
--- a/contrib/passwordcheck/expected/passwordcheck_1.out
+++ b/contrib/passwordcheck/expected/passwordcheck_1.out
@@ -11,7 +11,7 @@ ERROR:  password must not contain user name
 -- error: contains only letters
 ALTER USER regress_passwordcheck_user1 PASSWORD 'alessnicelongpassword';
 ERROR:  password must contain both letters and nonletters
--- encrypted ok (password is "secret")
+-- hashed ok (password is "secret")
 ALTER USER regress_passwordcheck_user1 PASSWORD 'md592350e12ac34e52dd598f90893bb3ae7';
 -- error: password is user name
 ALTER USER regress_passwordcheck_user1 PASSWORD 'md507a112732ed9f2087fa90b192d44e358';
diff --git a/contrib/passwordcheck/passwordcheck.c b/contrib/passwordcheck/passwordcheck.c
index ae4a0abe2e..6abd5c1634 100644
--- a/contrib/passwordcheck/passwordcheck.c
+++ b/contrib/passwordcheck/passwordcheck.c
@@ -35,13 +35,13 @@ static check_password_hook_type prev_check_password_hook = NULL;
 /*
  * check_password
  *
- * performs checks on an encrypted or unencrypted password
+ * performs checks on an hashed or unencrypted password
  * ereport's if not acceptable
  *
  * username: name of role being created or changed
- * password: new password (possibly already encrypted)
+ * password: new password (possibly already hashed)
  * password_type: PASSWORD_TYPE_* code, to indicate if the password is
- *			in plaintext or encrypted form.
+ *			in plaintext or hashed form.
  * validuntil_time: password expiration time, as a timestamptz Datum
  * validuntil_null: true if password expiration time is NULL
  *
@@ -64,9 +64,9 @@ check_password(const char *username,
 	if (password_type != PASSWORD_TYPE_PLAINTEXT)
 	{
 		/*
-		 * Unfortunately we cannot perform exhaustive checks on encrypted
+		 * Unfortunately we cannot perform exhaustive checks on hashed
 		 * passwords - we are restricted to guessing. (Alternatively, we could
-		 * insist on the password being presented non-encrypted, but that has
+		 * insist on the password being presented non-hashed, but that has
 		 * its own security disadvantages.)
 		 *
 		 * We only check for username = password.
diff --git a/contrib/passwordcheck/sql/passwordcheck.sql b/contrib/passwordcheck/sql/passwordcheck.sql
index 1fbd6b0e96..f7befd2e41 100644
--- a/contrib/passwordcheck/sql/passwordcheck.sql
+++ b/contrib/passwordcheck/sql/passwordcheck.sql
@@ -14,7 +14,7 @@ ALTER USER regress_passwordcheck_user1 PASSWORD 'xyzregress_passwordcheck_user1'
 -- error: contains only letters
 ALTER USER regress_passwordcheck_user1 PASSWORD 'alessnicelongpassword';
 
--- encrypted ok (password is "secret")
+-- hashed ok (password is "secret")
 ALTER USER regress_passwordcheck_user1 PASSWORD 'md592350e12ac34e52dd598f90893bb3ae7';
 
 -- error: password is user name
diff --git a/contrib/pgcrypto/crypt-des.c b/contrib/pgcrypto/crypt-des.c
index 98c30ea122..4d4bac7c39 100644
--- a/contrib/pgcrypto/crypt-des.c
+++ b/contrib/pgcrypto/crypt-des.c
@@ -750,7 +750,7 @@ px_crypt_des(const char *key, const char *setting)
 		output[0] = setting[0];
 
 		/*
-		 * If the encrypted password that the salt was extracted from is only
+		 * If the hashed password that the salt was extracted from is only
 		 * 1 character long, the salt will be corrupted.  We need to ensure
 		 * that the output string doesn't have an extra NUL in it!
 		 */
diff --git a/contrib/pgcrypto/expected/pgp-encrypt-md5.out b/contrib/pgcrypto/expected/pgp-encrypt-md5.out
index 339e12a434..fd5a193ba8 100644
--- a/contrib/pgcrypto/expected/pgp-encrypt-md5.out
+++ b/contrib/pgcrypto/expected/pgp-encrypt-md5.out
@@ -1,5 +1,5 @@
 --
--- PGP encrypt using MD5
+-- PGP encrypt using MD5 hash
 --
 select pgp_sym_decrypt(
 	pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'),
diff --git a/contrib/pgcrypto/expected/pgp-encrypt-md5_1.out b/contrib/pgcrypto/expected/pgp-encrypt-md5_1.out
index 612ca1d19c..80fba13a2a 100644
--- a/contrib/pgcrypto/expected/pgp-encrypt-md5_1.out
+++ b/contrib/pgcrypto/expected/pgp-encrypt-md5_1.out
@@ -1,5 +1,5 @@
 --
--- PGP encrypt using MD5
+-- PGP encrypt using MD5 hash
 --
 select pgp_sym_decrypt(
 	pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'),
diff --git a/contrib/pgcrypto/sql/pgp-encrypt-md5.sql b/contrib/pgcrypto/sql/pgp-encrypt-md5.sql
index 201636c820..a5dd74485c 100644
--- a/contrib/pgcrypto/sql/pgp-encrypt-md5.sql
+++ b/contrib/pgcrypto/sql/pgp-encrypt-md5.sql
@@ -1,5 +1,5 @@
 --
--- PGP encrypt using MD5
+-- PGP encrypt using MD5 hash
 --
 
 select pgp_sym_decrypt(
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3ec7391ec5..fb57701c3c 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1591,8 +1591,8 @@
        <structfield>rolpassword</structfield> <type>text</type>
       </para>
       <para>
-       Password (possibly encrypted); null if none. The format depends
-       on the form of encryption used.
+       Password (possibly hashed); null if none. The format depends
+       on the form of hashing used.
       </para></entry>
      </row>
 
@@ -1610,7 +1610,7 @@
   </table>
 
   <para>
-   For an MD5 encrypted password, <structfield>rolpassword</structfield>
+   For an MD5 hashed password, <structfield>rolpassword</structfield>
    column will begin with the string <literal>md5</literal> followed by a
    32-character hexadecimal MD5 hash. The MD5 hash will be of the user's
    password concatenated to their user name. For example, if user
@@ -1619,7 +1619,7 @@
   </para>
 
   <para>
-   If the password is encrypted with SCRAM-SHA-256, it has the format:
+   If the password is hashed with SCRAM-SHA-256, it has the format:
 <synopsis>
 SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&lt;salt&gt;</replaceable>$<replaceable>&lt;StoredKey&gt;</replaceable>:<replaceable>&lt;ServerKey&gt;</replaceable>
 </synopsis>
@@ -1630,7 +1630,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
 
   <para>
     A password that does not follow either of those formats is assumed to be
-    unencrypted.
+    unhashed.
   </para>
  </sect1>
 
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 477f70a65d..1fefb0e88e 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -538,7 +538,7 @@ include_dir         <replaceable>directory</replaceable>
         <term><literal>password</literal></term>
         <listitem>
          <para>
-          Require the client to supply an unencrypted password for
+          Require the client to supply an unhashed password for
           authentication.
           Since the password is sent in clear text over the
           network, this should not be used on untrusted networks.
@@ -1258,7 +1258,7 @@ omicron         bryanh                  guest1
        To ease transition from the <literal>md5</literal> method to the newer
        SCRAM method, if <literal>md5</literal> is specified as a method
        in <filename>pg_hba.conf</filename> but the user's password on the
-       server is encrypted for SCRAM (see below), then SCRAM-based
+       server is hashed for SCRAM (see below), then SCRAM-based
        authentication will automatically be chosen instead.
       </para>
      </listitem>
@@ -1295,17 +1295,17 @@ omicron         bryanh                  guest1
 
    <para>
     The availability of the different password-based authentication methods
-    depends on how a user's password on the server is encrypted (or hashed,
-    more accurately).  This is controlled by the configuration
+    depends on how a user's password on the server is hashed.
+    This is controlled by the configuration
     parameter <xref linkend="guc-password-encryption"/> at the time the
-    password is set.  If a password was encrypted using
+    password is set.  If a password was hashed using
     the <literal>scram-sha-256</literal> setting, then it can be used for the
     authentication methods <literal>scram-sha-256</literal>
     and <literal>password</literal> (but password transmission will be in
     plain text in the latter case).  The authentication method
     specification <literal>md5</literal> will automatically switch to using
     the <literal>scram-sha-256</literal> method in this case, as explained
-    above, so it will also work.  If a password was encrypted using
+    above, so it will also work.  If a password was hashed using
     the <literal>md5</literal> setting, then it can be used only for
     the <literal>md5</literal> and <literal>password</literal> authentication
     method specifications (again, with the password transmitted in plain text
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b5624ca884..20bf72ecdf 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1106,14 +1106,14 @@ include_dir 'conf.d'
        <para>
         When a password is specified in <xref linkend="sql-createrole"/> or
         <xref linkend="sql-alterrole"/>, this parameter determines the
-        algorithm to use to encrypt the password.  Possible values are
-        <literal>scram-sha-256</literal>, which will encrypt the password with
+        algorithm to use to hash the password.  Possible values are
+        <literal>scram-sha-256</literal>, which will hash the password with
         SCRAM-SHA-256, and <literal>md5</literal>, which stores the password
         as an MD5 hash.  The default is <literal>scram-sha-256</literal>.
        </para>
        <para>
         Note that older clients might lack support for the SCRAM authentication
-        mechanism, and hence not work with passwords encrypted with
+        mechanism, and hence not work with passwords hashed with
         SCRAM-SHA-256.  See <xref linkend="auth-password"/> for more details.
        </para>
       </listitem>
@@ -1127,13 +1127,13 @@ include_dir 'conf.d'
       </term>
       <listitem>
        <para>
-        The number of computational iterations to be performed when encrypting
+        The number of computational iterations to be performed when hashing
         a password using SCRAM-SHA-256. The default is <literal>4096</literal>.
         A higher number of iterations provides additional protection against
         brute-force attacks on stored passwords, but makes authentication
         slower. Changing the value has no effect on existing passwords
-        encrypted with SCRAM-SHA-256 as the iteration count is fixed at the
-        time of encryption. In order to make use of a changed value, a new
+        hashed with SCRAM-SHA-256 as the iteration count is fixed at the
+        time of hashing. In order to make use of a changed value, a new
         password must be set.
        </para>
       </listitem>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index ed88ac001a..b9ce68b58c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -7072,7 +7072,7 @@ void PQconninfoFree(PQconninfoOption *connOptions);
 
     <listitem>
      <para>
-      Prepares the encrypted form of a <productname>PostgreSQL</productname> password.
+      Prepares the hashed form of a <productname>PostgreSQL</productname> password.
 <synopsis>
 char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm);
 </synopsis>
@@ -7081,14 +7081,14 @@ char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
       'pwd'</literal>.  It is good practice not to send the original cleartext
       password in such a command, because it might be exposed in command
       logs, activity displays, and so on.  Instead, use this function to
-      convert the password to encrypted form before it is sent.
+      convert the password to hashed form before it is sent.
      </para>
 
      <para>
       The <parameter>passwd</parameter> and <parameter>user</parameter> arguments
       are the cleartext password, and the SQL name of the user it is for.
-      <parameter>algorithm</parameter> specifies the encryption algorithm
-      to use to encrypt the password. Currently supported algorithms are
+      <parameter>algorithm</parameter> specifies the hashing algorithm
+      to use to hash the password. Currently supported algorithms are
       <literal>md5</literal> and <literal>scram-sha-256</literal> (<literal>on</literal> and
       <literal>off</literal> are also accepted as aliases for <literal>md5</literal>, for
       compatibility with older server versions). Note that support for
@@ -7121,7 +7121,7 @@ char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
 
     <listitem>
      <para>
-      Prepares the md5-encrypted form of a <productname>PostgreSQL</productname> password.
+      Prepares the md5-hashed form of a <productname>PostgreSQL</productname> password.
 <synopsis>
 char *PQencryptPassword(const char *passwd, const char *user);
 </synopsis>
@@ -7129,7 +7129,7 @@ char *PQencryptPassword(const char *passwd, const char *user);
       <xref linkend="libpq-PQencryptPasswordConn"/>. The difference is that
       <xref linkend="libpq-PQencryptPassword"/> does not
       require a connection object, and <literal>md5</literal> is always used as the
-      encryption algorithm.
+      hashing algorithm.
      </para>
     </listitem>
    </varlistentry>
diff --git a/doc/src/sgml/passwordcheck.sgml b/doc/src/sgml/passwordcheck.sgml
index 601f489227..936940292e 100644
--- a/doc/src/sgml/passwordcheck.sgml
+++ b/doc/src/sgml/passwordcheck.sgml
@@ -40,8 +40,8 @@
    To prevent unencrypted passwords from being sent across the network,
    written to the server log or otherwise stolen by a database administrator,
    <productname>PostgreSQL</productname> allows the user to supply
-   pre-encrypted passwords. Many client programs make use of this
-   functionality and encrypt the password before sending it to the server.
+   pre-hashed passwords. Many client programs make use of this
+   functionality and hash the password before sending it to the server.
   </para>
   <para>
    This limits the usefulness of the <filename>passwordcheck</filename>
@@ -54,7 +54,7 @@
   </para>
   <para>
    Alternatively, you could modify <filename>passwordcheck</filename>
-   to reject pre-encrypted passwords, but forcing users to set their
+   to reject pre-hashed passwords, but forcing users to set their
    passwords in clear text carries its own security risks.
   </para>
  </caution>
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index 2e29f1d6f7..39176c8dbb 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -119,7 +119,7 @@ hmac(data bytea, key bytea, type text) returns bytea
    <listitem>
     <para>
      They use a random value, called the <firstterm>salt</firstterm>, so that users
-     having the same password will have different encrypted passwords.
+     having the same password will have different hashed passwords.
      This is also an additional defense against reversing the algorithm.
     </para>
    </listitem>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 6c3e8a631d..d84d0dacba 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -302,7 +302,7 @@
       <listitem>
        <para>
         The frontend must now send a PasswordMessage containing the
-        password (with user name) encrypted via MD5, then encrypted
+        password (with user name) hashed via MD5, then hashed
         again using the 4-byte random salt specified in the
         AuthenticationMD5Password message.  If this is the correct
         password, the server responds with an AuthenticationOk,
@@ -3527,7 +3527,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
        <term>Int32(5)</term>
        <listitem>
         <para>
-         Specifies that an MD5-encrypted password is required.
+         Specifies that an MD5-hashed password is required.
         </para>
        </listitem>
       </varlistentry>
@@ -3536,7 +3536,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
        <term>Byte4</term>
        <listitem>
         <para>
-         The salt to use when encrypting the password.
+         The salt to use when hashing the password.
         </para>
        </listitem>
       </varlistentry>
@@ -5398,7 +5398,7 @@ psql "dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
        <term>String</term>
        <listitem>
         <para>
-         The password (encrypted, if requested).
+         The password (hashed, if requested).
         </para>
        </listitem>
       </varlistentry>
diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml
index ab1ee45d54..68aeaa128a 100644
--- a/doc/src/sgml/ref/alter_role.sgml
+++ b/doc/src/sgml/ref/alter_role.sgml
@@ -88,9 +88,9 @@ ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | A
    roles for which they have been granted <literal>ADMIN OPTION</literal>.
    The current session user cannot be renamed.
    (Connect as a different user if you need to do that.)
-   Because <literal>MD5</literal>-encrypted passwords use the role name as
+   Because <literal>MD5</literal>-hashed passwords use the role name as
    cryptographic salt, renaming a role clears its password if the
-   password is <literal>MD5</literal>-encrypted.
+   password is <literal>MD5</literal>-hashed.
   </para>
 
   <para>
@@ -258,7 +258,7 @@ ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | A
   </para>
 
   <para>
-   Caution must be exercised when specifying an unencrypted password
+   Caution must be exercised when specifying an unhashed password
    with this command.  The password will be transmitted to the server
    in cleartext, and it might also be logged in the client's command
    history or the server log.  <xref linkend="app-psql"/>
diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml
index 8dd2a6395c..70f17c2794 100644
--- a/doc/src/sgml/ref/create_role.sgml
+++ b/doc/src/sgml/ref/create_role.sgml
@@ -254,15 +254,15 @@ in sync when changing the above synopsis!
          </para>
        </note>
        <para>
-        The password is always stored encrypted in the system catalogs. The
+        The password is always stored hashed in the system catalogs. The
         <literal>ENCRYPTED</literal> keyword has no effect, but is accepted for
-        backwards compatibility. The method of encryption is determined
+        backwards compatibility. The method of hashing is determined
         by the configuration parameter <xref linkend="guc-password-encryption"/>.
-        If the presented password string is already in MD5-encrypted or
-        SCRAM-encrypted format, then it is stored as-is regardless of
+        If the presented password string is already in MD5-hashed or
+        SCRAM-hashed format, then it is stored as-is regardless of
         <varname>password_encryption</varname> (since the system cannot decrypt
-        the specified encrypted password string, to encrypt it in a
-        different format).  This allows reloading of encrypted passwords
+        the specified hashed password string, to hash it in a
+        different format).  This allows reloading of hashed passwords
         during dump/restore.
        </para>
       </listitem>
@@ -387,12 +387,12 @@ in sync when changing the above synopsis!
   </para>
 
   <para>
-   Caution must be exercised when specifying an unencrypted password
+   Caution must be exercised when specifying an unhashed password
    with this command.  The password will be transmitted to the server
    in cleartext, and it might also be logged in the client's command
    history or the server log.  The command <xref
    linkend="app-createuser"/>, however, transmits
-   the password encrypted.  Also, <xref linkend="app-psql"/>
+   the password hashed.  Also, <xref linkend="app-psql"/>
    contains a command
    <command>\password</command> that can be used to safely change the
    password later.
diff --git a/doc/src/sgml/ref/createuser.sgml b/doc/src/sgml/ref/createuser.sgml
index 5c34c62342..780f5ce5d0 100644
--- a/doc/src/sgml/ref/createuser.sgml
+++ b/doc/src/sgml/ref/createuser.sgml
@@ -543,7 +543,7 @@ PostgreSQL documentation
 </screen>
     In the above example, the new password isn't actually echoed when typed,
     but we show what was typed for clarity.  As you see, the password is
-    encrypted before it is sent to the client.
+    hashed before it is sent to the client.
    </para>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index cc7d797159..68bbc5dc57 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -2784,7 +2784,7 @@ lo_import 152801
         <listitem>
         <para>
         Changes the password of the specified user (by default, the current
-        user).  This command prompts for the new password, encrypts it, and
+        user).  This command prompts for the new password, hashes it, and
         sends it to the server as an <command>ALTER ROLE</command> command.  This
         makes sure that the new password does not appear in cleartext in the
         command history, the server log, or elsewhere.
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 64753d9c01..226def6a6e 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2054,15 +2054,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   <variablelist>
 
   <varlistentry>
-   <term>Password Encryption</term>
+   <term>Password Hashing</term>
    <listitem>
 
     <para>
      Database user passwords are stored as hashes (determined by the setting
      <xref linkend="guc-password-encryption"/>), so the administrator cannot
      determine the actual password assigned to the user. If SCRAM or MD5
-     encryption is used for client authentication, the unencrypted password is
-     never even temporarily present on the server because the client encrypts
+     hashing is used for client authentication, the unhashed password is
+     never even temporarily present on the server because the client hashes
      it before being sent across the network. SCRAM is preferred, because it
      is an Internet standard and is more secure than the PostgreSQL-specific
      MD5 authentication protocol.
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 0ef1745631..f5f7f20be2 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -3478,9 +3478,9 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
        <structfield>passwd</structfield> <type>text</type>
       </para>
       <para>
-       Password (possibly encrypted); null if none.  See
+       Password (possibly hashed); null if none.  See
        <link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>
-       for details of how encrypted passwords are stored.
+       for details of how hashed passwords are stored.
       </para></entry>
      </row>
 
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index f47aa38231..46ab4f351b 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -442,7 +442,7 @@ CreateRole(ParseState *pstate, CreateRoleStmt *stmt)
 		}
 		else
 		{
-			/* Encrypt the password to the requested format. */
+			/* Hash the password to the requested format. */
 			shadow_pass = encrypt_password(Password_encryption, stmt->role,
 										   password);
 			new_record[Anum_pg_authid_rolpassword - 1] =
@@ -927,7 +927,7 @@ AlterRole(ParseState *pstate, AlterRoleStmt *stmt)
 		}
 		else
 		{
-			/* Encrypt the password to the requested format. */
+			/* Hash the password to the requested format. */
 			shadow_pass = encrypt_password(Password_encryption, rolename,
 										   password);
 			new_record[Anum_pg_authid_rolpassword - 1] =
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 81dabb9c27..25cd042f15 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -3029,7 +3029,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 						   encryptedpassword + i, &errstr))
 		{
 			ereport(LOG,
-					(errmsg("could not perform MD5 encryption of password: %s",
+					(errmsg("could not perform MD5 hashing of password: %s",
 							errstr)));
 			pfree(cryptvector);
 			pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
@@ -3221,7 +3221,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
 						   encryptedpassword, &errstr))
 		{
 			ereport(LOG,
-					(errmsg("could not perform MD5 encryption of received packet: %s",
+					(errmsg("could not perform MD5 hashing of received packet: %s",
 							errstr)));
 			pfree(cryptvector);
 			continue;
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index ef496a0bea..b856c6ab3b 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * crypt.c
- *	  Functions for dealing with encrypted passwords stored in
+ *	  Functions for dealing with hashed passwords stored in
  *	  pg_authid.rolpassword.
  *
  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
@@ -109,7 +109,7 @@ get_password_type(const char *shadow_pass)
  * Given a user-supplied password, convert it into a secret of
  * 'target_type' kind.
  *
- * If the password is already in encrypted form, we cannot reverse the
+ * If the password is already in hashed form, we cannot reverse the
  * hash, so it is stored as it is regardless of the requested type.
  */
 char *
@@ -123,7 +123,7 @@ encrypt_password(PasswordType target_type, const char *role,
 	if (guessed_type != PASSWORD_TYPE_PLAINTEXT)
 	{
 		/*
-		 * Cannot convert an already-encrypted password from one format to
+		 * Cannot convert an already-hashed password from one format to
 		 * another, so return it as it is.
 		 */
 		return pstrdup(password);
@@ -136,21 +136,21 @@ encrypt_password(PasswordType target_type, const char *role,
 
 			if (!pg_md5_encrypt(password, role, strlen(role),
 								encrypted_password, &errstr))
-				elog(ERROR, "password encryption failed: %s", errstr);
+				elog(ERROR, "password hashing failed: %s", errstr);
 			return encrypted_password;
 
 		case PASSWORD_TYPE_SCRAM_SHA_256:
 			return pg_be_scram_build_secret(password);
 
 		case PASSWORD_TYPE_PLAINTEXT:
-			elog(ERROR, "cannot encrypt password with 'plaintext'");
+			elog(ERROR, "cannot hash password with 'plaintext'");
 	}
 
 	/*
 	 * This shouldn't happen, because the above switch statements should
 	 * handle every combination of source and target password types.
 	 */
-	elog(ERROR, "cannot encrypt password to requested type");
+	elog(ERROR, "cannot hash password to requested type");
 	return NULL;				/* keep compiler quiet */
 }
 
@@ -188,7 +188,7 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
 	/*
 	 * Compute the correct answer for the MD5 challenge.
 	 */
-	/* stored password already encrypted, only do salt */
+	/* stored password already hashed, only do salt */
 	if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
 						md5_salt, md5_salt_len,
 						crypt_pwd, &errstr))
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index bad13497a3..dbec18478f 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -55,7 +55,7 @@
 # METHOD can be "trust", "reject", "md5", "password", "scram-sha-256",
 # "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert".
 # Note that "password" sends passwords in clear text; "md5" or
-# "scram-sha-256" are preferred since they send encrypted passwords.
+# "scram-sha-256" are preferred since they send hashed passwords.
 #
 # OPTIONS are a set of options for the authentication in the format
 # NAME=VALUE.  The available options depend on the different
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 63f172e175..c87a276063 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -1197,7 +1197,7 @@ AlterOptRoleElem:
 			| ENCRYPTED PASSWORD Sconst
 				{
 					/*
-					 * These days, passwords are always stored in encrypted
+					 * These days, passwords are always stored in hashed
 					 * form, so there is no difference between PASSWORD and
 					 * ENCRYPTED PASSWORD.
 					 */
@@ -1209,7 +1209,7 @@ AlterOptRoleElem:
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 							 errmsg("UNENCRYPTED PASSWORD is no longer supported"),
-							 errhint("Remove UNENCRYPTED to store the password in encrypted form instead."),
+							 errhint("Remove UNENCRYPTED to store the password in hashed form instead."),
 							 parser_errposition(@1)));
 				}
 			| INHERIT
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 9f59440526..0820da2215 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -4943,7 +4943,7 @@ struct config_enum ConfigureNamesEnum[] =
 
 	{
 		{"password_encryption", PGC_USERSET, CONN_AUTH_AUTH,
-			gettext_noop("Chooses the algorithm for encrypting passwords."),
+			gettext_noop("Chooses the algorithm for hashing passwords."),
 			NULL
 		},
 		&Password_encryption,
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index 0709491185..662e94a07f 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -306,7 +306,7 @@ main(int argc, char *argv[])
 												   newuser,
 												   NULL);
 		if (!encrypted_password)
-			pg_fatal("password encryption failed: %s",
+			pg_fatal("password hashing failed: %s",
 					 PQerrorMessage(conn));
 		appendStringLiteralConn(&sql, encrypted_password, conn);
 		PQfreemem(encrypted_password);
diff --git a/src/common/md5_common.c b/src/common/md5_common.c
index 82ce75dcf2..e9de420c76 100644
--- a/src/common/md5_common.c
+++ b/src/common/md5_common.c
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * md5_common.c
- *	  Routines shared between all MD5 implementations used for encrypted
+ *	  Routines shared between all MD5 implementations used for hashed
  *	  passwords.
  *
  * Sverre H. Huseby <[email-masked]>
diff --git a/src/include/common/md5.h b/src/include/common/md5.h
index b6089bacff..39284770f6 100644
--- a/src/include/common/md5.h
+++ b/src/include/common/md5.h
@@ -4,7 +4,7 @@
  *	  Constants and common utilities related to MD5.
  *
  * These definitions are needed by both frontend and backend code to work
- * with MD5-encrypted passwords.
+ * with MD5-hashed passwords.
  *
  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h
index d62ad4371a..b8f6a480d1 100644
--- a/src/include/libpq/crypt.h
+++ b/src/include/libpq/crypt.h
@@ -19,7 +19,7 @@
  * Types of password hashes or secrets.
  *
  * Plaintext passwords can be passed in by the user, in a CREATE/ALTER USER
- * command. They will be encrypted to MD5 or SCRAM-SHA-256 format, before
+ * command. They will be hashed to MD5 or SCRAM-SHA-256 format, before
  * storing on-disk, so only MD5 and SCRAM-SHA-256 passwords should appear
  * in pg_authid.rolpassword. They are also the allowed values for the
  * password_encryption GUC.
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 912aa14821..b55d742870 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -713,7 +713,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 			return STATUS_ERROR;	/* shouldn't happen */
 	}
 
-	/* Encrypt the password if needed. */
+	/* Hash the password if needed. */
 
 	switch (areq)
 	{
@@ -735,14 +735,14 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 									strlen(conn->pguser), crypt_pwd2,
 									&errstr))
 				{
-					libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
+					libpq_append_conn_error(conn, "could not hash password: %s", errstr);
 					free(crypt_pwd);
 					return STATUS_ERROR;
 				}
 				if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), md5Salt,
 									4, crypt_pwd, &errstr))
 				{
-					libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
+					libpq_append_conn_error(conn, "could not hash password: %s", errstr);
 					free(crypt_pwd);
 					return STATUS_ERROR;
 				}
@@ -1223,10 +1223,10 @@ pg_fe_getauthname(PQExpBuffer errorMessage)

 /*
- * PQencryptPassword -- exported routine to encrypt a password with MD5
+ * PQencryptPassword -- exported routine to hash a password with MD5
  *
  * This function is equivalent to calling PQencryptPasswordConn with
- * "md5" as the encryption method, except that this doesn't require
+ * "md5" as the hashing method, except that this doesn't require
  * a connection object.  This function is deprecated, use
  * PQencryptPasswordConn instead.
  */
@@ -1250,19 +1250,19 @@ PQencryptPassword(const char *passwd, const char *user)
 }
 
 /*
- * PQencryptPasswordConn -- exported routine to encrypt a password
+ * PQencryptPasswordConn -- exported routine to hash a password
  *
  * This is intended to be used by client applications that wish to send
  * commands like ALTER USER joe PASSWORD 'pwd'.  The password need not
- * be sent in cleartext if it is encrypted on the client side.  This is
+ * be sent in cleartext if it is hashed on the client side.  This is
  * good because it ensures the cleartext password won't end up in logs,
  * pg_stat displays, etc.  We export the function so that clients won't
- * be dependent on low-level details like whether the encryption is MD5
+ * be dependent on low-level details like whether the hashing is MD5
  * or something else.
  *
  * Arguments are a connection object, the cleartext password, the SQL
  * name of the user it is for, and a string indicating the algorithm to
- * use for encrypting the password.  If algorithm is NULL, this queries
+ * use for hashing the password.  If algorithm is NULL, this queries
  * the server for the current 'password_encryption' value.  If you wish
  * to avoid that, e.g. to avoid blocking, you can execute
  * 'show password_encryption' yourself before calling this function, and
@@ -1344,7 +1344,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
 											 conn->scram_sha_256_iterations,
 											 &errstr);
 		if (!crypt_pwd)
-			libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
+			libpq_append_conn_error(conn, "could not hash password: %s", errstr);
 	}
 	else if (strcmp(algorithm, "md5") == 0)
 	{
@@ -1355,7 +1355,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
 
 			if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd, &errstr))
 			{
-				libpq_append_conn_error(conn, "could not encrypt password: %s", errstr);
+				libpq_append_conn_error(conn, "could not hash password: %s", errstr);
 				free(crypt_pwd);
 				crypt_pwd = NULL;
 			}
@@ -1365,7 +1365,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
 	}
 	else
 	{
-		libpq_append_conn_error(conn, "unrecognized password encryption algorithm \"%s\"",
+		libpq_append_conn_error(conn, "unrecognized password hashing algorithm \"%s\"",
 								algorithm);
 		return NULL;
 	}
diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl
index 320e45ef84..918c58ec39 100644
--- a/src/test/authentication/t/001_password.pl
+++ b/src/test/authentication/t/001_password.pl
@@ -4,8 +4,8 @@
 # Set of tests for authentication and pg_hba.conf. The following password
 # methods are checked through this test:
 # - Plain
-# - MD5-encrypted
-# - SCRAM-encrypted
+# - MD5-hashed
+# - SCRAM-hashed
 # This test can only run with Unix-domain sockets.
 
 use strict;
diff --git a/src/test/regress/expected/password.out b/src/test/regress/expected/password.out
index 924d6e001d..2273e9239b 100644
--- a/src/test/regress/expected/password.out
+++ b/src/test/regress/expected/password.out
@@ -52,18 +52,18 @@ SELECT rolname, rolpassword
 (1 row)
 
 ALTER ROLE regress_passwd2_new RENAME TO regress_passwd2;
--- Change passwords with ALTER USER. With plaintext or already-encrypted
+-- Change passwords with ALTER USER. With plaintext or already-hashed
 -- passwords.
 SET password_encryption = 'md5';
--- encrypt with MD5
+-- hash with MD5
 ALTER ROLE regress_passwd2 PASSWORD 'foo';
--- already encrypted, use as they are
+-- already hashed, use as they are
 ALTER ROLE regress_passwd1 PASSWORD 'md5cd3578025fe2c3d7ed1b9a9b26238b70';
 ALTER ROLE regress_passwd3 PASSWORD 'SCRAM-SHA-256$4096:VLK4RMaQLCvNtQ==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo=';
 SET password_encryption = 'scram-sha-256';
 -- create SCRAM secret
 ALTER ROLE  regress_passwd4 PASSWORD 'foo';
--- already encrypted with MD5, use as it is
+-- already hashed with MD5, use as it is
 CREATE ROLE regress_passwd5 PASSWORD 'md5e73a4b11df52a6068f8b39f90be36023';
 -- This looks like a valid SCRAM-SHA-256 secret, but it is not
 -- so it should be hashed with SCRAM-SHA-256.
diff --git a/src/test/regress/expected/password_1.out b/src/test/regress/expected/password_1.out
index 9d2cc94f37..1bdd90a7d4 100644
--- a/src/test/regress/expected/password_1.out
+++ b/src/test/regress/expected/password_1.out
@@ -14,10 +14,10 @@ SET password_encryption = 'scram-sha-256'; -- ok
 SET password_encryption = 'md5';
 CREATE ROLE regress_passwd1;
 ALTER ROLE regress_passwd1 PASSWORD 'role_pwd1';
-ERROR:  password encryption failed: unsupported
+ERROR:  password hashing failed: unsupported
 CREATE ROLE regress_passwd2;
 ALTER ROLE regress_passwd2 PASSWORD 'role_pwd2';
-ERROR:  password encryption failed: unsupported
+ERROR:  password hashing failed: unsupported
 SET password_encryption = 'scram-sha-256';
 CREATE ROLE regress_passwd3 PASSWORD 'role_pwd3';
 CREATE ROLE regress_passwd4 PASSWORD NULL;
@@ -53,19 +53,19 @@ SELECT rolname, rolpassword
 (1 row)
 
 ALTER ROLE regress_passwd2_new RENAME TO regress_passwd2;
--- Change passwords with ALTER USER. With plaintext or already-encrypted
+-- Change passwords with ALTER USER. With plaintext or already-hashed
 -- passwords.
 SET password_encryption = 'md5';
--- encrypt with MD5
+-- hash with MD5
 ALTER ROLE regress_passwd2 PASSWORD 'foo';
-ERROR:  password encryption failed: unsupported
--- already encrypted, use as they are
+ERROR:  password hash failed: unsupported
+-- already hashed, use as they are
 ALTER ROLE regress_passwd1 PASSWORD 'md5cd3578025fe2c3d7ed1b9a9b26238b70';
 ALTER ROLE regress_passwd3 PASSWORD 'SCRAM-SHA-256$4096:VLK4RMaQLCvNtQ==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo=';
 SET password_encryption = 'scram-sha-256';
 -- create SCRAM secret
 ALTER ROLE  regress_passwd4 PASSWORD 'foo';
--- already encrypted with MD5, use as it is
+-- already hashed with MD5, use as it is
 CREATE ROLE regress_passwd5 PASSWORD 'md5e73a4b11df52a6068f8b39f90be36023';
 -- This looks like a valid SCRAM-SHA-256 secret, but it is not
 -- so it should be hashed with SCRAM-SHA-256.
diff --git a/src/test/regress/sql/password.sql b/src/test/regress/sql/password.sql
index bb82aa4aa2..f2f07f23bf 100644
--- a/src/test/regress/sql/password.sql
+++ b/src/test/regress/sql/password.sql
@@ -39,20 +39,20 @@ SELECT rolname, rolpassword
     ORDER BY rolname, rolpassword;
 ALTER ROLE regress_passwd2_new RENAME TO regress_passwd2;
 
--- Change passwords with ALTER USER. With plaintext or already-encrypted
+-- Change passwords with ALTER USER. With plaintext or already-hashed
 -- passwords.
 SET password_encryption = 'md5';
 
--- encrypt with MD5
+-- hash with MD5
 ALTER ROLE regress_passwd2 PASSWORD 'foo';
--- already encrypted, use as they are
+-- already hashed, use as they are
 ALTER ROLE regress_passwd1 PASSWORD 'md5cd3578025fe2c3d7ed1b9a9b26238b70';
 ALTER ROLE regress_passwd3 PASSWORD 'SCRAM-SHA-256$4096:VLK4RMaQLCvNtQ==$6YtlR4t69SguDiwFvbVgVZtuz6gpJQQqUMZ7IQJK5yI=:ps75jrHeYU4lXCcXI4O8oIdJ3eO8o2jirjruw9phBTo=';
 
 SET password_encryption = 'scram-sha-256';
 -- create SCRAM secret
 ALTER ROLE  regress_passwd4 PASSWORD 'foo';
--- already encrypted with MD5, use as it is
+-- already hashed with MD5, use as it is
 CREATE ROLE regress_passwd5 PASSWORD 'md5e73a4b11df52a6068f8b39f90be36023';
 
 -- This looks like a valid SCRAM-SHA-256 secret, but it is not
diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl
index 91e771ec47..cb55e66986 100644
--- a/src/test/ssl/t/002_scram.pl
+++ b/src/test/ssl/t/002_scram.pl
@@ -93,7 +93,7 @@ $node->connect_ok("$common_connstr user=ssltestuser channel_binding=disable",
 $node->connect_ok("$common_connstr user=ssltestuser channel_binding=require",
 	"SCRAM with SSL and channel_binding=require");
 
-# Now test when the user has an MD5-encrypted password; should fail
+# Now test when the user has an MD5-hashed password; should fail
 SKIP:
 {
 	skip "MD5 not supported" unless $md5_works;
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index 2c5c055222..295160d72a 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -169,7 +169,7 @@ sub configure_test_server_for_ssl
 		$node->psql('postgres',
 			"SET password_encryption='$params{password_enc}'; ALTER USER ssltestuser PASSWORD '$params{password}';"
 		);
-		# A special user that always has an md5-encrypted password
+		# A special user that always has an md5-hashed password
 		$node->psql('postgres',
 			"SET password_encryption='md5'; ALTER USER md5testuser PASSWORD '$params{password}';"
 		);

---

From: Peter Eisentraut <[email-masked]>
Date: Wed, 27 Dec 2023 22:52:15 +0100

On 27.12.23 02:04, Bruce Momjian wrote:
> I did_not_  change the user API, so CREATE/ALTER ROLE still uses

Yeah, this is clearly confusing.  I think we should just leave it alone. 
  Some gentle rewording here and there to clarify things might be 
appropriate (but the text already uses hashing on some occasions), but 
the blanket replacement does not make things better, I think.

---

From: Bruce Momjian <[email-masked]>
Date: Fri, 29 Dec 2023 12:46:14 -0500

On Wed, Dec 27, 2023 at 10:52:15PM +0100, Peter Eisentraut wrote:
> Some gentle rewording here and there to clarify things might be appropriate
> (but the text already uses hashing on some occasions), but the blanket
> replacement does not make things better, I think.

Seeing no more replies, I will abandon this improvement idea.