gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated (e5d0b314 -> 2699d278)


From: gnunet
Subject: [libmicrohttpd] branch master updated (e5d0b314 -> 2699d278)
Date: Sun, 04 Sep 2022 13:43:23 +0200

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from e5d0b314 microhttpd.h: Fixed missing version bump
     new 2a9071b8 Modified public Digest Username struct to include the 
algorithm
     new 0ccef4c1 Added new public functions for userhash and userdigest 
calculations
     new e3e01bd7 microhttpd.h: fixed typos in comments
     new 62175acf Added MHD_FEATURE_DIGEST_AUTH_SHA512_256 value
     new 87fe3bdd Added tests for userdigest and userhash calculations
     new 0130c23c test_digestauth2: removed copy-paste leftovers
     new 2699d278 .gitignore: added universal patter to ignore test binaries

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/include/microhttpd.h               | 200 +++++++++++--
 src/microhttpd/.gitignore              |   3 +
 src/microhttpd/Makefile.am             |  14 +-
 src/microhttpd/daemon.c                |   6 +
 src/microhttpd/digestauth.c            | 301 ++++++++++++++++---
 src/microhttpd/test_dauth_userdigest.c | 432 +++++++++++++++++++++++++++
 src/microhttpd/test_dauth_userhash.c   | 529 +++++++++++++++++++++++++++++++++
 src/testcurl/.gitignore                |   1 +
 src/testcurl/test_digestauth2.c        |  17 +-
 src/testzzuf/.gitignore                |   1 +
 10 files changed, 1441 insertions(+), 63 deletions(-)
 create mode 100644 src/microhttpd/test_dauth_userdigest.c
 create mode 100644 src/microhttpd/test_dauth_userhash.c

diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 60edf6b7..7b72dfa6 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097533
+#define MHD_VERSION 0x00097536
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -4543,6 +4543,8 @@ enum MHD_DigestAuthAlgo3
  * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
  *         #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not
  *         recognised/valid
+ * @sa #MHD_digest_auth_calc_userdigest()
+ * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex()
  * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
@@ -4642,6 +4644,97 @@ enum MHD_DigestAuthMultiAlgo3
     (0x3F) | MHD_DIGEST_AUTH_ALGO3_NON_SESSION | MHD_DIGEST_AUTH_ALGO3_SESSION
 };
 
+
+/**
+ * Calculate "userhash", return it as binary data.
+ *
+ * The "userhash" is the hash of the string "username:realm".
+ *
+ * The "Userhash" could be used to avoid sending username in cleartext in 
Digest
+ * Authorization client's header.
+ *
+ * Userhash is not designed to hide the username in local database or files,
+ * as username in cleartext is required for #MHD_digest_auth_check3() function
+ * to check the response, but it can be used to hide username in HTTP headers.
+ *
+ * This function could be used when the new username is added to the username
+ * database to save the "userhash" alongside with the username (preferably) or
+ * when loading list of the usernames to generate the userhash for every loaded
+ * username (this will cause delays at the start with the long lists).
+ *
+ * Once "userhash" is generated it could be used to identify users for clients
+ * with "userhash" support.
+ * Avoid repetitive usage of this function for the same username/realm
+ * combination as it will cause excessive CPU load; save and re-use the result
+ * instead.
+ *
+ * @param algo3 the algorithm for userhash calculations
+ * @param username the username
+ * @param realm the realm
+ * @param[out] userhash_bin the output buffer for userhash as binary data;
+ *                          if this function succeeds, then this buffer has
+ *                          #MHD_digest_get_hash_size(algo3) bytes of userhash
+ *                          upon return
+ * @param bin_buf_size the size of the @a userhash_bin buffer, must be
+ *                     at least #MHD_digest_get_hash_size(algo3) bytes long
+ * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or
+ *                             if @a algo3 algorithm is not supported.
+ * @note Available since #MHD_VERSION 0x00097535
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3,
+                               const char *username,
+                               const char *realm,
+                               void *userhash_bin,
+                               size_t bin_buf_size);
+
+
+/**
+ * Calculate "userhash", return it as hexadecimal data.
+ *
+ * The "userhash" is the hash of the string "username:realm".
+ *
+ * The "Userhash" could be used to avoid sending username in cleartext in 
Digest
+ * Authorization client's header.
+ *
+ * Userhash is not designed to hide the username in local database or files,
+ * as username in cleartext is required for #MHD_digest_auth_check3() function
+ * to check the response, but it can be used to hide username in HTTP headers.
+ *
+ * This function could be used when the new username is added to the username
+ * database to save the "userhash" alongside with the username (preferably) or
+ * when loading list of the usernames to generate the userhash for every loaded
+ * username (this will cause delays at the start with the long lists).
+ *
+ * Once "userhash" is generated it could be used to identify users for clients
+ * with "userhash" support.
+ * Avoid repetitive usage of this function for the same username/realm
+ * combination as it will cause excessive CPU load; save and re-use the result
+ * instead.
+ *
+ * @param algo3 the algorithm for userhash calculations
+ * @param username the username
+ * @param realm the realm
+ * @param[out] userhash_hex the output buffer for userhash as hex data;
+ *                          if this function succeeds, then this buffer has
+ *                          #MHD_digest_get_hash_size(algo3)*2 chars long
+ *                          userhash string
+ * @param bin_buf_size the size of the @a userhash_bin buffer, must be
+ *                     at least #MHD_digest_get_hash_size(algo3)*2+1 chars long
+ * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or
+ *                             if @a algo3 algorithm is not supported.
+ * @note Available since #MHD_VERSION 0x00097535
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_digest_auth_calc_userhash_hex (enum MHD_DigestAuthAlgo3 algo3,
+                                   const char *username,
+                                   const char *realm,
+                                   char *userhash_hex,
+                                   size_t hex_buf_size);
+
+
 /**
  * The type of username used by client in Digest Authorization header
  *
@@ -4671,6 +4764,7 @@ enum MHD_DigestAuthUsernameType
   /**
    * The username provided in form of 'userhash' as
    * specified by RFC 7616 #section-3.4.4.
+   * @sa #MHD_digest_auth_calc_userhash_hex(), #MHD_digest_auth_calc_userhash()
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = 3,
 
@@ -4815,6 +4909,7 @@ struct MHD_DigestAuthInfo
    * with charset and language tag removed (i.e. it is original username
    * extracted from the extended notation).
    * This can be NULL is username is missing or invalid.
+   * @sa #MHD_digest_auth_calc_userhash_hex()
    */
   char *username;
 
@@ -4834,6 +4929,7 @@ struct MHD_DigestAuthInfo
    * @warning To avoid buffer overruns, always check the size of the data 
before
    *          use, because @a userhash_bin can point even to zero-sized
    *          data.
+   * @sa #MHD_digest_auth_calc_userhash()
    */
   uint8_t *userhash_bin;
 
@@ -4911,10 +5007,18 @@ MHD_digest_auth_get_request_info3 (struct 
MHD_Connection *connection);
  *
  * Application may modify buffers as needed until #MHD_free() is called for
  * pointer to this structure
- * @note Available since #MHD_VERSION 0x00097525
+ * @note Available since #MHD_VERSION 0x00097534
  */
 struct MHD_DigestAuthUsernameInfo
 {
+  /**
+   * The algorithm as defined by client.
+   * Set automatically to MD5 if not specified by client.
+   * @warning Do not be confused with #MHD_DigestAuthAlgorithm,
+   *          which uses other values!
+   */
+  enum MHD_DigestAuthAlgo3 algo3;
+
   /**
    * The type of username used by client.
    * The 'invalid' and 'missing' types are not used in this structure,
@@ -4924,13 +5028,12 @@ struct MHD_DigestAuthUsernameInfo
 
   /**
    * The username string.
-   * Valid only if username is standard, extended, or userhash.
    * For userhash this is unqoted string without decoding of the
    * hexadecimal digits (as provided by client).
    * If extended notation is used, this string is pct-decoded string
    * with charset and language tag removed (i.e. it is original username
    * extracted from the extended notation).
-   * This can be NULL is username is missing or invalid.
+   * @sa #MHD_digest_auth_calc_userhash_hex()
    */
   char *username;
 
@@ -4950,6 +5053,7 @@ struct MHD_DigestAuthUsernameInfo
    * @warning To avoid buffer overruns, always check the size of the data 
before
    *          use, because @a userhash_bin can point even to zero-sized
    *          data.
+   * @sa #MHD_digest_auth_calc_userhash()
    */
   uint8_t *userhash_bin;
 };
@@ -5078,7 +5182,8 @@ enum MHD_DigestAuthResult
  *
  * @param connection the MHD connection structure
  * @param realm the realm to be used for authorization of the client
- * @param username the username needs to be authenticated
+ * @param username the username needs to be authenticated, must be in clear 
text
+ *                 even if userhash is used by the client
  * @param password the password used in the authentication
  * @param nonce_timeout the nonce validity duration in seconds
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
@@ -5104,6 +5209,46 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
                         enum MHD_DigestAuthMultiAlgo3 malgo3);
 
 
+/**
+ * Calculate userdigest, return it as binary data.
+ *
+ * The "userdigest" is the hash of the "username:realm:password" string.
+ *
+ * The "userdigest" can be used to avoid storing the password in clear text
+ * in database/files
+ *
+ * This function is designed to improve security of stored credentials,
+ * the "userdigest" does not improve security of the authentication process.
+ *
+ * The results can be used to store username & userdigest pairs instead of
+ * username & password pairs. To further improve security, application may
+ * store username & userhash & userdigest triplets.
+ *
+ * @param algo3 the digest algorithm
+ * @param username the username
+ * @param realm the realm
+ * @param password the password, must be zero-terminated
+ * @param[out] userdigest_bin the output buffer for userdigest;
+ *                            if this function succeeds, then this buffer has
+ *                            #MHD_digest_get_hash_size(algo3) bytes of
+ *                            userdigest upon return
+ * @param userdigest_bin the size of the @a userdigest_bin buffer, must be
+ *                       at least #MHD_digest_get_hash_size(algo3) bytes long
+ * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or
+ *                             if @a algo3 algorithm is not supported.
+ * @sa #MHD_digest_auth_check_digest3()
+ * @note Available since #MHD_VERSION 0x00097535
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo3 algo3,
+                                 const char *username,
+                                 const char *realm,
+                                 const char *password,
+                                 void *userdigest_bin,
+                                 size_t bin_buf_size);
+
+
 /**
  * Authenticates the authorization header sent by the client by using
  * hash of "username:realm:password".
@@ -5117,13 +5262,15 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
  * nonce and second time to perform an authorised request).
  *
  * @param connection the MHD connection structure
- * @param realm the realm presented to the client
- * @param username the username needs to be authenticated
+ * @param realm the realm to be used for authorization of the client
+ * @param username the username needs to be authenticated, must be in clear 
text
+ *                 even if userhash is used by the client
  * @param userdigest the precalculated binary hash of the string
- *                   "username:realm:password"
+ *                   "username:realm:password",
+ *                   see #MHD_digest_auth_calc_userdigest()
  * @param userdigest_size the size of the @a userdigest in bytes, must match 
the
  *                        hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
- *                        #MHD_SHA256_DIGEST_SIZE)
+ *                        #MHD_SHA256_DIGEST_SIZE, #MHD_digest_get_hash_size())
  * @param nonce_timeout the period of seconds since nonce generation, when
  *                      the nonce is recognised as valid and not stale.
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
@@ -5138,6 +5285,7 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection,
  *               match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
+ * @sa #MHD_digest_auth_calc_userdigest()
  * @note Available since #MHD_VERSION 0x00097528
  * @ingroup authentication
  */
@@ -5197,9 +5345,13 @@ MHD_digest_auth_check_digest3 (struct MHD_Connection 
*connection,
  * @param userhash_support if set to non-zero value (#MHD_YES) then support of
  *                         userhash is indicated, the client may provide
  *                         hash("username:realm") instead of username in
- *                         clear text; note that client is allowed to provide
- *                         the username in cleartext even if this parameter set
- *                         to non-zero
+ *                         clear text;
+ *                         note that clients are allowed to provide the 
username
+ *                         in cleartext even if this parameter set to non-zero;
+ *                         when userhash is used, application must be ready to
+ *                         identify users by provided userhash value instead of
+ *                         username; see #MHD_digest_auth_calc_userhash() and
+ *                         #MHD_digest_auth_calc_userhash_hex()
  * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
  *                    added, indicating for the client that UTF-8 encoding
  *                    is preferred
@@ -5885,7 +6037,7 @@ enum MHD_FEATURE
   /**
    * Get whether the MD5-based hashing algorithms are supported for Digest
    * Authorization.
-   * Currently it is always not supported if Digest Auth module is built.
+   * Currently it is always supported if Digest Auth module is built.
    * @note Available since #MHD_VERSION 0x00097527
    */
   MHD_FEATURE_DIGEST_AUTH_MD5 = 26,
@@ -5899,29 +6051,37 @@ enum MHD_FEATURE
    */
   MHD_FEATURE_DIGEST_AUTH_SHA256 = 27,
 
+  /**
+   * Get whether the SHA-512/256-based hashing algorithms are supported
+   * for Digest Authorization.
+   * Currently it is always not supported.
+   * @note Available since #MHD_VERSION 0x00097536
+   */
+  MHD_FEATURE_DIGEST_AUTH_SHA512_256 = 28,
+
   /**
    * Get whether QOP with value 'auth-int' (authentication with integrity
    * protection) is supported for Digest Authorization.
    * Currently it is always not supported.
-   * @note Available since #MHD_VERSION 0x00097527
+   * @note Available since #MHD_VERSION 0x00097536
    */
-  MHD_FEATURE_DIGEST_AUTH_AUTH_INT = 28,
+  MHD_FEATURE_DIGEST_AUTH_AUTH_INT = 29,
 
   /**
    * Get whether 'session' algorithms (like 'MD5-sess') are supported for 
Digest
    * Authorization.
    * Currently it is always not supported.
-   * @note Available since #MHD_VERSION 0x00097527
+   * @note Available since #MHD_VERSION 0x00097536
    */
-  MHD_FEATURE_DIGEST_AUTH_ALGO_SESSION = 29,
+  MHD_FEATURE_DIGEST_AUTH_ALGO_SESSION = 30,
 
   /**
    * Get whether 'userhash' is supported for Digest Authorization.
-   * It it always supported since #MHD_VERSION 0x00097526 if Digest Auth
+   * It is always supported since #MHD_VERSION 0x00097526 if Digest Auth
    * module is built.
-   * @note Available since #MHD_VERSION 0x00097527
+   * @note Available since #MHD_VERSION 0x00097536
    */
-  MHD_FEATURE_DIGEST_AUTH_USERHASH = 30
+  MHD_FEATURE_DIGEST_AUTH_USERHASH = 31
 };
 
 
diff --git a/src/microhttpd/.gitignore b/src/microhttpd/.gitignore
index 1353f1ec..27d65b7d 100644
--- a/src/microhttpd/.gitignore
+++ b/src/microhttpd/.gitignore
@@ -83,3 +83,6 @@ test_postprocessor_md
 /test_str_base64
 /test_str_pct
 /test_str_bin_hex
+/test_dauth_userdigest
+/test_dauth_userhash
+test_*[a-z0-9_][a-z0-9_][a-z0-9_]
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index b4339f08..fbc545eb 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -263,7 +263,9 @@ check_PROGRAMS += \
 endif
 if ENABLE_DAUTH
 check_PROGRAMS += \
-  test_str_quote
+  test_str_quote \
+  test_dauth_userdigest \
+  test_dauth_userhash
 endif
 if ENABLE_BAUTH
 check_PROGRAMS += \
@@ -535,4 +537,14 @@ test_set_panic_SOURCES = \
 test_set_panic_LDADD = \
   libmicrohttpd.la
 
+test_dauth_userdigest_SOURCES = \
+  test_dauth_userdigest.c
+test_dauth_userdigest_LDADD = \
+  libmicrohttpd.la
+
+test_dauth_userhash_SOURCES = \
+  test_dauth_userhash.c
+test_dauth_userhash_LDADD = \
+  libmicrohttpd.la
+
 .PHONY: update-po-POTFILES.in
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 3d96150d..fc7d4da3 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -8440,6 +8440,12 @@ MHD_is_feature_supported (enum MHD_FEATURE feature)
     return MHD_YES;
 #else
     return MHD_NO;
+#endif
+  case MHD_FEATURE_DIGEST_AUTH_SHA512_256:
+#ifdef DAUTH_SUPPORT
+    return MHD_NO;
+#else
+    return MHD_NO;
 #endif
   case MHD_FEATURE_DIGEST_AUTH_AUTH_INT:
 #ifdef DAUTH_SUPPORT
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index cc6cd0ac..fac3e280 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -225,6 +225,8 @@ digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
  * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
  *         #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not
  *         recognised/valid
+ * @sa #MHD_digest_auth_calc_userdigest()
+ * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex()
  * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
@@ -780,7 +782,7 @@ get_rq_extended_uname_copy_z (const char *uname_ext, size_t 
uname_ext_len,
  * Get copy of username used by the client.
  * @param params the Digest Authorization parameters
  * @param uname_type the type of username
- * @param[out] unames the pointer to the structure to be filled
+ * @param[out] uname_info the pointer to the structure to be filled
  * @param buf the buffer to be used for usernames
  * @param buf_size the size of the @a buf
  * @return the size of the @a buf used by pointers in @a unames structure
@@ -975,17 +977,11 @@ MHD_digest_auth_get_request_info3 (struct MHD_Connection 
*connection)
 
   if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type) &&
        (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type) )
-  {
-    struct MHD_DigestAuthUsernameInfo uname_strct;
-    memset (&uname_strct, 0, sizeof(uname_strct));
-    unif_buf_used += get_rq_uname (params, uname_type, &uname_strct,
-                                   unif_buf_ptr + unif_buf_used,
-                                   unif_buf_size - unif_buf_used);
-    info->uname_type = uname_strct.uname_type;
-    info->username = uname_strct.username;
-    info->username_len = uname_strct.username_len;
-    info->userhash_bin = uname_strct.userhash_bin;
-  }
+    unif_buf_used +=
+      get_rq_uname (params, uname_type,
+                    (struct MHD_DigestAuthUsernameInfo *) info,
+                    unif_buf_ptr + unif_buf_used,
+                    unif_buf_size - unif_buf_used);
   else
     info->uname_type = uname_type;
 
@@ -1072,6 +1068,7 @@ MHD_digest_auth_get_username3 (struct MHD_Connection 
*connection)
     return NULL;
   }
   mhd_assert (uname_type == uname_info->uname_type);
+  uname_info->algo3 = params->algo3;
 
   return uname_info;
 }
@@ -1142,7 +1139,7 @@ MHD_digest_auth_get_username (struct MHD_Connection 
*connection)
 /**
  * Calculate the server nonce so that it mitigates replay attacks
  * The current format of the nonce is ...
- * H(various parameters) + Hex(timestamp)
+ * H(timestamp:random data:various parameters) + Hex(timestamp)
  *
  * @param nonce_time The amount of time in seconds for a nonce to be invalid
  * @param mthd_e HTTP method as enum value
@@ -1521,6 +1518,240 @@ calculate_add_nonce_with_retry (struct MHD_Connection 
*const connection,
 }
 
 
+/**
+ * Calculate userdigest, return it as binary data.
+ *
+ * It is equal to H(A1) for non-session algorithms.
+ *
+ * MHD internal version.
+ *
+ * @param da the digest algorithm
+ * @param username the username to use
+ * @param username_len the length of the @a username
+ * @param realm the realm to use
+ * @param realm_len the length of the @a realm
+ * @param password the password, must be zero-terminated
+ * @param[out] ha1_bin the output buffer, must have at least
+ *                     #digest_get_size(da) bytes available
+ */
+_MHD_static_inline void
+calc_userdigest (struct DigestAlgorithm *da,
+                 const char *username, const size_t username_len,
+                 const char *realm, const size_t realm_len,
+                 const char *password,
+                 uint8_t *ha1_bin)
+{
+  digest_init (da);
+  digest_update (da, username, username_len);
+  digest_update_with_colon (da);
+  digest_update (da, realm, realm_len);
+  digest_update_with_colon (da);
+  digest_update_str (da, password);
+  digest_calc_hash (da, ha1_bin);
+}
+
+
+/**
+ * Calculate userdigest, return it as binary data.
+ *
+ * The "userdigest" is the hash of the "username:realm:password" string.
+ *
+ * The "userdigest" can be used to avoid storing the password in clear text
+ * in database/files
+ *
+ * This function is designed to improve security of stored credentials,
+ * the "userdigest" does not improve security of the authentication process.
+ *
+ * The results can be used to store username & userdigest pairs instead of
+ * username & password pairs. To further improve security, application may
+ * store username & userhash & userdigest triplets.
+ *
+ * @param algo3 the digest algorithm
+ * @param username the username
+ * @param realm the realm
+ * @param password the password, must be zero-terminated
+ * @param[out] userdigest_bin the output buffer for userdigest;
+ *                            if this function succeeds, then this buffer has
+ *                            #MHD_digest_get_hash_size(algo3) bytes of
+ *                            userdigest upon return
+ * @param userdigest_bin the size of the @a userdigest_bin buffer, must be
+ *                       at least #MHD_digest_get_hash_size(algo3) bytes long
+ * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or
+ *                             if @a algo3 algorithm is not supported.
+ * @sa #MHD_digest_auth_check_digest3()
+ * @note Available since #MHD_VERSION 0x00097535
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo3 algo3,
+                                 const char *username,
+                                 const char *realm,
+                                 const char *password,
+                                 void *userdigest_bin,
+                                 size_t bin_buf_size)
+{
+  struct DigestAlgorithm da;
+  if (! digest_setup (&da, get_base_digest_algo (algo3)))
+    return MHD_NO;
+  if (digest_get_size (&da) > bin_buf_size)
+    return MHD_NO;
+  calc_userdigest (&da,
+                   username,
+                   strlen (username),
+                   realm,
+                   strlen (realm),
+                   password,
+                   userdigest_bin);
+  return MHD_YES;
+}
+
+
+/**
+ * Calculate userhash, return it as binary data.
+ *
+ * MHD internal version.
+ *
+ * @param da the digest algorithm
+ * @param username the username to use
+ * @param username_len the length of the @a username
+ * @param realm the realm to use
+ * @param realm_len the length of the @a realm
+ * @param[out] digest_bin the output buffer, must have at least
+ *                        #MHD_digest_get_hash_size(algo3) bytes available
+ */
+_MHD_static_inline void
+calc_userhash (struct DigestAlgorithm *da,
+               const char *username, const size_t username_len,
+               const char *realm, const size_t realm_len,
+               uint8_t *digest_bin)
+{
+  mhd_assert (NULL != username);
+  digest_init (da);
+  digest_update (da, username, username_len);
+  digest_update_with_colon (da);
+  digest_update (da, realm, realm_len);
+  digest_calc_hash (da, digest_bin);
+}
+
+
+/**
+ * Calculate "userhash", return it as binary data.
+ *
+ * The "userhash" is the hash of the string "username:realm".
+ *
+ * The "Userhash" could be used to avoid sending username in cleartext in 
Digest
+ * Authorization client's header.
+ *
+ * Userhash is not designed to hide the username in local database or files,
+ * as username in cleartext is required for #MHD_digest_auth_check3() function
+ * to check the response, but it can be used to hide username in HTTP headers.
+ *
+ * This function could be used when the new username is added to the username
+ * database to save the "userhash" alongside with the username (preferably) or
+ * when loading list of the usernames to generate the userhash for every loaded
+ * username (this will cause delays at the start with the long lists).
+ *
+ * Once "userhash" is generated it could be used to identify users for clients
+ * with "userhash" support.
+ * Avoid repetitive usage of this function for the same username/realm
+ * combination as it will cause excessive CPU load; save and re-use the result
+ * instead.
+ *
+ * @param algo3 the algorithm for userhash calculations
+ * @param username the username
+ * @param realm the realm
+ * @param[out] userhash_bin the output buffer for userhash as binary data;
+ *                          if this function succeeds, then this buffer has
+ *                          #MHD_digest_get_hash_size(algo3) bytes of userhash
+ *                          upon return
+ * @param bin_buf_size the size of the @a userhash_bin buffer, must be
+ *                     at least #MHD_digest_get_hash_size(algo3) bytes long
+ * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or
+ *                             if @a algo3 algorithm is not supported.
+ * @note Available since #MHD_VERSION 0x00097535
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3,
+                               const char *username,
+                               const char *realm,
+                               void *userhash_bin,
+                               size_t bin_buf_size)
+{
+  struct DigestAlgorithm da;
+  if (! digest_setup (&da, get_base_digest_algo (algo3)))
+    return MHD_NO;
+  if (digest_get_size (&da) > bin_buf_size)
+    return MHD_NO;
+  calc_userhash (&da,
+                 username,
+                 strlen (username),
+                 realm,
+                 strlen (realm),
+                 userhash_bin);
+  return MHD_YES;
+}
+
+
+/**
+ * Calculate "userhash", return it as hexadecimal data.
+ *
+ * The "userhash" is the hash of the string "username:realm".
+ *
+ * The "Userhash" could be used to avoid sending username in cleartext in 
Digest
+ * Authorization client's header.
+ *
+ * Userhash is not designed to hide the username in local database or files,
+ * as username in cleartext is required for #MHD_digest_auth_check3() function
+ * to check the response, but it can be used to hide username in HTTP headers.
+ *
+ * This function could be used when the new username is added to the username
+ * database to save the "userhash" alongside with the username (preferably) or
+ * when loading list of the usernames to generate the userhash for every loaded
+ * username (this will cause delays at the start with the long lists).
+ *
+ * Once "userhash" is generated it could be used to identify users for clients
+ * with "userhash" support.
+ * Avoid repetitive usage of this function for the same username/realm
+ * combination as it will cause excessive CPU load; save and re-use the result
+ * instead.
+ *
+ * @param algo3 the algorithm for userhash calculations
+ * @param username the username
+ * @param realm the realm
+ * @param[out] userhash_hex the output buffer for userhash as hex data;
+ *                          if this function succeeds, then this buffer has
+ *                          #MHD_digest_get_hash_size(algo3)*2 chars long
+ *                          userhash string
+ * @param bin_buf_size the size of the @a userhash_bin buffer, must be
+ *                     at least #MHD_digest_get_hash_size(algo3)*2+1 chars long
+ * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or
+ *                             if @a algo3 algorithm is not supported.
+ * @note Available since #MHD_VERSION 0x00097535
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_digest_auth_calc_userhash_hex (enum MHD_DigestAuthAlgo3 algo3,
+                                   const char *username,
+                                   const char *realm,
+                                   char *userhash_hex,
+                                   size_t hex_buf_size)
+{
+  uint8_t userhash_bin[MAX_DIGEST];
+  size_t digest_size;
+
+  digest_size = digest_get_hash_size (algo3);
+  if (digest_size * 2 + 1 > hex_buf_size)
+    return MHD_NO;
+  if (MHD_NO == MHD_digest_auth_calc_userhash (algo3, username, realm,
+                                               userhash_bin, MAX_DIGEST))
+    return MHD_NO;
+
+  MHD_bin_to_hex_z (userhash_bin, digest_size, userhash_hex);
+  return MHD_YES;
+}
+
+
 struct test_header_param
 {
   struct MHD_Connection *connection;
@@ -2121,11 +2352,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   else
   { /* Userhash */
     mhd_assert (NULL != params->username.value.str);
-    digest_init (&da);
-    digest_update (&da, username, username_len);
-    digest_update_with_colon (&da);
-    digest_update (&da, realm, realm_len);
-    digest_calc_hash (&da, hash1_bin);
+    calc_userhash (&da, username, username_len, realm, realm_len, hash1_bin);
     mhd_assert (sizeof (tmp1) >= (2 * digest_size));
     MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
     if (! is_param_equal_caseless (&params->username, tmp1, 2 * digest_size))
@@ -2265,15 +2492,11 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 
   /* ** Build H(A1) ** */
   if (NULL == userdigest)
-  {
-    digest_init (&da);
-    digest_update (&da, (const uint8_t *) username, username_len);
-    digest_update_with_colon (&da);
-    digest_update (&da, (const uint8_t *) realm, realm_len);
-    digest_update_with_colon (&da);
-    digest_update_str (&da, password);
-    digest_calc_hash (&da, hash1_bin);
-  }
+    calc_userdigest (&da,
+                     username, username_len,
+                     realm, realm_len,
+                     password,
+                     hash1_bin);
   /* TODO: support '-sess' versions */
   /* Got H(A1) */
 
@@ -2481,7 +2704,8 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
  *
  * @param connection the MHD connection structure
  * @param realm the realm to be used for authorization of the client
- * @param username the username needs to be authenticated
+ * @param username the username needs to be authenticated, must be in clear 
text
+ *                 even if userhash is used by the client
  * @param password the password used in the authentication
  * @param nonce_timeout the nonce validity duration in seconds
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
@@ -2533,13 +2757,15 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
  * nonce and second time to perform an authorised request).
  *
  * @param connection the MHD connection structure
- * @param realm the realm presented to the client
- * @param username the username needs to be authenticated
+ * @param realm the realm to be used for authorization of the client
+ * @param username the username needs to be authenticated, must be in clear 
text
+ *                 even if userhash is used by the client
  * @param userdigest the precalculated binary hash of the string
- *                   "username:realm:password"
+ *                   "username:realm:password",
+ *                   see #MHD_digest_auth_calc_userdigest()
  * @param userdigest_size the size of the @a userdigest in bytes, must match 
the
  *                        hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
- *                        #MHD_SHA256_DIGEST_SIZE)
+ *                        #MHD_SHA256_DIGEST_SIZE, #MHD_digest_get_hash_size())
  * @param nonce_timeout the period of seconds since nonce generation, when
  *                      the nonce is recognised as valid and not stale.
  * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
@@ -2554,6 +2780,7 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection,
  *               match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
+ * @sa #MHD_digest_auth_calc_userdigest()
  * @note Available since #MHD_VERSION 0x00097528
  * @ingroup authentication
  */
@@ -2782,9 +3009,13 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
  * @param userhash_support if set to non-zero value (#MHD_YES) then support of
  *                         userhash is indicated, the client may provide
  *                         hash("username:realm") instead of username in
- *                         clear text; note that client is allowed to provide
- *                         the username in cleartext even if this parameter set
- *                         to non-zero
+ *                         clear text;
+ *                         note that clients are allowed to provide the 
username
+ *                         in cleartext even if this parameter set to non-zero;
+ *                         when userhash is used, application must be ready to
+ *                         identify users by provided userhash value instead of
+ *                         username; see #MHD_digest_auth_calc_userhash() and
+ *                         #MHD_digest_auth_calc_userhash_hex()
  * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
  *                    added, indicating for the client that UTF-8 encoding
  *                    is preferred
diff --git a/src/microhttpd/test_dauth_userdigest.c 
b/src/microhttpd/test_dauth_userdigest.c
new file mode 100644
index 00000000..3af45156
--- /dev/null
+++ b/src/microhttpd/test_dauth_userdigest.c
@@ -0,0 +1,432 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2022 Evgeny Grin (Karlson2)
+
+  This test tool is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2, or
+  (at your option) any later version.
+
+  This test tool is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+*/
+
+/**
+ * @file microhttpd/test_dauth_userdigest.c
+ * @brief  Tests for Digest Auth calculations of userdigest
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "microhttpd.h"
+#include "test_helpers.h"
+
+static int verbose = 1; /* verbose level (0-1)*/
+
+/* Declarations and data */
+
+struct data_md5
+{
+  unsigned int line_num;
+  const char *const username;
+  const char *const realm;
+  const char *const password;
+  const uint8_t hash[MHD_MD5_DIGEST_SIZE];
+};
+
+
+static const struct data_md5 md5_tests[] = {
+  {__LINE__,
+   "u", "r", "p",
+   {0x44, 0xad, 0xd2, 0x2b, 0x6f, 0x31, 0x79, 0xb7, 0x51, 0xea, 0xfd, 0x68,
+    0xee, 0x37, 0x0f, 0x7d}},
+  {__LINE__,
+   "testuser", "testrealm", "testpass",
+   {0xeb, 0xff, 0x22, 0x5e, 0x1c, 0xeb, 0x73, 0xe0, 0x26, 0xfc, 0xc6, 0x45,
+    0xaf, 0x3e, 0x84, 0xf6}},
+  {__LINE__,  /* Values from testcurl/test_digestauth2.c */
+   "test_user", "TestRealm", "test pass",
+   {0xd8, 0xb4, 0xa6, 0xd0, 0x01, 0x13, 0x07, 0xb7, 0x67, 0x94, 0xea, 0x66,
+    0x86, 0x03, 0x6b, 0x43}},
+  {__LINE__,
+   "Mufasa", "myhost@testrealm.com", "CircleOfLife",
+   {0x7e, 0xbe, 0xcc, 0x07, 0x18, 0xa5, 0x4a, 0xb4, 0x7e, 0x21, 0x65, 0x69,
+    0x07, 0x66, 0x41, 0x6a}},
+  {__LINE__,
+   "Mufasa", "myhost@example.com", "Circle Of Life",
+   {0x6a, 0x6d, 0x4e, 0x7c, 0xd7, 0x15, 0x18, 0x68, 0xf9, 0xb8, 0xc7, 0xc8,
+    0xd1, 0xcd, 0xd4, 0xe0}},
+  {__LINE__,
+   "Mufasa", "http-auth@example.org", "Circle of Life",
+   {0x3d, 0x78, 0x80, 0x7d, 0xef, 0xe7, 0xde, 0x21, 0x57, 0xe2, 0xb0, 0xb6,
+    0x57, 0x3a, 0x85, 0x5f}},
+  {__LINE__,
+   "J" "\xC3\xA4" "s" "\xC3\xB8" "n Doe" /* "Jäsøn Doe" */,
+   "api@example.org", "Secret, or not?",
+   {0x83, 0xa3, 0xf7, 0xf6, 0xb8, 0x3f, 0x71, 0xc5, 0xc2, 0xeb, 0x7c, 0x6d,
+    0xd2, 0xdd, 0x4c, 0x4b}}
+};
+
+struct data_sha256
+{
+  unsigned int line_num;
+  const char *const username;
+  const char *const realm;
+  const char *const password;
+  const uint8_t hash[MHD_SHA256_DIGEST_SIZE];
+};
+
+static const struct data_sha256 sha256_tests[] = {
+  {__LINE__,
+   "u", "r", "p",
+   {0xdc, 0xb0, 0x21, 0x10, 0x2e, 0x49, 0x1e, 0x70, 0x1a, 0x4a, 0x23, 0x6d,
+    0xaa, 0x89, 0x23, 0xaf, 0x21, 0x61, 0x44, 0x7b, 0xce, 0x7b, 0xb7, 0x26,
+    0x0a, 0x35, 0x1e, 0xe8, 0x3e, 0x9f, 0x81, 0x54}},
+  {__LINE__,
+   "testuser", "testrealm", "testpass",
+   {0xa9, 0x2e, 0xf6, 0x3b, 0x3d, 0xec, 0x38, 0x95, 0xb0, 0x8f, 0x3d, 0x4d,
+    0x67, 0x33, 0xf0, 0x70, 0x74, 0xcb, 0xe6, 0xd4, 0xa0, 0x01, 0x27, 0xf5,
+    0x74, 0x1a, 0x77, 0x4f, 0x05, 0xf9, 0xd4, 0x99}},
+  {__LINE__,  /* Values from testcurl/test_digestauth2.c */
+   "test_user", "TestRealm", "test pass",
+   {0xc3, 0x4e, 0x16, 0x5a, 0x17, 0x0f, 0xe5, 0xac, 0x04, 0xf1, 0x6e, 0x46,
+    0x48, 0x2b, 0xa0, 0xc6, 0x56, 0xc1, 0xfb, 0x8f, 0x66, 0xa6, 0xd6, 0x3f,
+    0x91, 0x12, 0xf8, 0x56, 0xa5, 0xec, 0x6d, 0x6d}},
+  {__LINE__,
+   "Mufasa", "myhost@testrealm.com", "CircleOfLife",
+   {0x8e, 0x64, 0x1f, 0xaa, 0x71, 0x7d, 0x20, 0x70, 0x5a, 0xd7, 0x3c, 0x54,
+    0xfb, 0x04, 0x9e, 0x32, 0x6a, 0xe1, 0x1c, 0x80, 0xd6, 0x05, 0x9f, 0xc3,
+    0x7e, 0xbb, 0x2d, 0x7b, 0x60, 0x6c, 0x11, 0xb9}},
+  {__LINE__,
+   "Mufasa", "myhost@example.com", "Circle Of Life",
+   {0x8b, 0xc5, 0xa8, 0xed, 0xe3, 0x02, 0x15, 0x6b, 0x9f, 0x51, 0xce, 0x97,
+    0x81, 0xb5, 0x26, 0xff, 0x99, 0x29, 0x0b, 0xb2, 0xc3, 0xe4, 0x41, 0x71,
+    0x8e, 0xa3, 0xa1, 0x7e, 0x5a, 0xd9, 0xd6, 0x49}},
+  {__LINE__,
+   "Mufasa", "http-auth@example.org", "Circle of Life",
+   {0x79, 0x87, 0xc6, 0x4c, 0x30, 0xe2, 0x5f, 0x1b, 0x74, 0xbe, 0x53, 0xf9,
+    0x66, 0xb4, 0x9b, 0x90, 0xf2, 0x80, 0x8a, 0xa9, 0x2f, 0xaf, 0x9a, 0x00,
+    0x26, 0x23, 0x92, 0xd7, 0xb4, 0x79, 0x42, 0x32}},
+  {__LINE__,
+   "J" "\xC3\xA4" "s" "\xC3\xB8" "n Doe" /* "Jäsøn Doe" */,
+   "api@example.org", "Secret, or not?",
+   {0xfd, 0x0b, 0xe3, 0x93, 0x9d, 0xca, 0x4b, 0x5c, 0x2d, 0x46, 0xe8, 0xfa,
+    0x6a, 0x3d, 0x16, 0xdb, 0xea, 0x82, 0x47, 0x4c, 0xb9, 0xa5, 0x88, 0xd4,
+    0xcb, 0x14, 0x9c, 0x54, 0xf3, 0x7c, 0xff, 0x37}}
+};
+
+
+/*
+ *  Helper functions
+ */
+
+/**
+ * Print bin as lower case hex
+ *
+ * @param bin binary data
+ * @param len number of bytes in bin
+ * @param hex pointer to len*2+1 bytes buffer
+ */
+static void
+bin2hex (const uint8_t *bin,
+         size_t len,
+         char *hex)
+{
+  while (len-- > 0)
+  {
+    unsigned int b1, b2;
+    b1 = (*bin >> 4) & 0xf;
+    *hex++ = (char) ((b1 > 9) ? (b1 + 'a' - 10) : (b1 + '0'));
+    b2 = *bin++ & 0xf;
+    *hex++ = (char) ((b2 > 9) ? (b2 + 'a' - 10) : (b2 + '0'));
+  }
+  *hex = 0;
+}
+
+
+/* Tests */
+
+static unsigned int
+check_md5 (const struct data_md5 *const data)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_MD5;
+  uint8_t hash_bin[MHD_MD5_DIGEST_SIZE];
+  char hash_hex[MHD_MD5_DIGEST_SIZE * 2 + 1];
+  char expected_hex[MHD_MD5_DIGEST_SIZE * 2 + 1];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userdigest";
+  if (MHD_YES != MHD_digest_auth_calc_userdigest (algo3,
+                                                  data->username,
+                                                  data->realm,
+                                                  data->password,
+                                                  hash_bin, sizeof(hash_bin)))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_YES.\n",
+             func_name);
+  }
+  else if (0 != memcmp (hash_bin, data->hash, sizeof(data->hash)))
+  {
+    failed++;
+    bin2hex (hash_bin, sizeof(hash_bin), hash_hex);
+    bin2hex (data->hash, sizeof(data->hash), expected_hex);
+    fprintf (stderr,
+             "FAILED: %s() produced wrong hash. "
+             "Calculated digest %s, expected digest %s.\n",
+             func_name,
+             hash_hex, expected_hex);
+  }
+
+  if (failed)
+  {
+    fprintf (stderr,
+             "The check failed for data located at line: %u.\n",
+             data->line_num);
+    fflush (stderr);
+  }
+  else if (verbose)
+  {
+    printf ("PASSED: check for data at line: %u.\n",
+            data->line_num);
+  }
+  return failed ? 1 : 0;
+}
+
+
+static unsigned int
+test_md5 (void)
+{
+  unsigned int num_failed = 0;
+  size_t i;
+
+  for (i = 0; i < sizeof(md5_tests) / sizeof(md5_tests[0]); i++)
+    num_failed += check_md5 (md5_tests + i);
+  return num_failed;
+}
+
+
+static unsigned int
+test_md5_failure (void)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_MD5;
+  uint8_t hash_bin[MHD_MD5_DIGEST_SIZE];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userdigest";
+  if (MHD_NO != MHD_digest_auth_calc_userdigest (algo3,
+                                                 "u", "r", "p",
+                                                 hash_bin, sizeof(hash_bin)
+                                                 - 1))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO != MHD_digest_auth_calc_userdigest (algo3,
+                                                 "u", "r", "p",
+                                                 hash_bin, 0))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+  {
+    if (MHD_NO != MHD_digest_auth_calc_userdigest (algo3,
+                                                   "u", "r", "p",
+                                                   hash_bin, sizeof(hash_bin)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+               func_name, (unsigned) __LINE__);
+    }
+  }
+
+  if (! failed && verbose)
+  {
+    printf ("PASSED: all checks with expected MHD_NO result near line: %u.\n",
+            (unsigned) __LINE__);
+  }
+  return failed ? 1 : 0;
+}
+
+
+static unsigned int
+check_sha256 (const struct data_sha256 *const data)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_SHA256;
+  uint8_t hash_bin[MHD_SHA256_DIGEST_SIZE];
+  char hash_hex[MHD_SHA256_DIGEST_SIZE * 2 + 1];
+  char expected_hex[MHD_SHA256_DIGEST_SIZE * 2 + 1];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userdigest";
+  if (MHD_YES != MHD_digest_auth_calc_userdigest (algo3,
+                                                  data->username,
+                                                  data->realm,
+                                                  data->password,
+                                                  hash_bin, sizeof(hash_bin)))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_YES.\n",
+             func_name);
+  }
+  else if (0 != memcmp (hash_bin, data->hash, sizeof(data->hash)))
+  {
+    failed++;
+    bin2hex (hash_bin, sizeof(hash_bin), hash_hex);
+    bin2hex (data->hash, sizeof(data->hash), expected_hex);
+    fprintf (stderr,
+             "FAILED: %s() produced wrong hash. "
+             "Calculated digest %s, expected digest %s.\n",
+             func_name,
+             hash_hex, expected_hex);
+  }
+
+  if (failed)
+  {
+    fprintf (stderr,
+             "The check failed for data located at line: %u.\n",
+             data->line_num);
+    fflush (stderr);
+  }
+  else if (verbose)
+  {
+    printf ("PASSED: check for data at line: %u.\n",
+            data->line_num);
+  }
+  return failed ? 1 : 0;
+}
+
+
+static unsigned int
+test_sha256 (void)
+{
+  unsigned int num_failed = 0;
+  size_t i;
+
+  for (i = 0; i < sizeof(sha256_tests) / sizeof(sha256_tests[0]); i++)
+    num_failed += check_sha256 (sha256_tests + i);
+  return num_failed;
+}
+
+
+static unsigned int
+test_sha256_failure (void)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_SHA256;
+  uint8_t hash_bin[MHD_SHA256_DIGEST_SIZE];
+  char hash_hex[MHD_SHA256_DIGEST_SIZE * 2 + 1];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userhash";
+  if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                               "u", "r",
+                                               hash_bin, sizeof(hash_bin) - 1))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                               "u", "r",
+                                               hash_bin, 0))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+  {
+    if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                                 "u", "r",
+                                                 hash_bin, sizeof(hash_bin)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+               func_name, (unsigned) __LINE__);
+    }
+  }
+
+  func_name = "MHD_digest_auth_calc_userhash_hex";
+  if (MHD_NO !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         "u", "r",
+                                         hash_hex, sizeof(hash_hex) - 1))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         "u", "r",
+                                         hash_hex, 0))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+  {
+    if (MHD_NO !=
+        MHD_digest_auth_calc_userhash_hex (algo3,
+                                           "u", "r",
+                                           hash_hex, sizeof(hash_hex)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+               func_name, (unsigned) __LINE__);
+    }
+  }
+
+  if (! failed && verbose)
+  {
+    printf ("PASSED: all checks with expected MHD_NO result near line: %u.\n",
+            (unsigned) __LINE__);
+  }
+  return failed ? 1 : 0;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  unsigned int num_failed = 0;
+  (void) has_in_name; /* Mute compiler warning. */
+  if (has_param (argc, argv, "-s") || has_param (argc, argv, "--silent"))
+    verbose = 0;
+
+  if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+    num_failed += test_md5 ();
+  num_failed += test_md5_failure ();
+  if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_SHA256))
+    num_failed += test_sha256 ();
+  num_failed += test_sha256_failure ();
+
+  return num_failed ? 1 : 0;
+}
diff --git a/src/microhttpd/test_dauth_userhash.c 
b/src/microhttpd/test_dauth_userhash.c
new file mode 100644
index 00000000..6e525773
--- /dev/null
+++ b/src/microhttpd/test_dauth_userhash.c
@@ -0,0 +1,529 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2022 Evgeny Grin (Karlson2)
+
+  This test tool is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2, or
+  (at your option) any later version.
+
+  This test tool is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  
USA
+*/
+
+/**
+ * @file microhttpd/test_dauth_userhash.c
+ * @brief  Tests for Digest Auth calculations of userhash
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "microhttpd.h"
+#include "test_helpers.h"
+
+static int verbose = 1; /* verbose level (0-1)*/
+
+/* Declarations and data */
+
+struct data_md5
+{
+  unsigned int line_num;
+  const char *const username;
+  const char *const realm;
+  const uint8_t hash[MHD_MD5_DIGEST_SIZE];
+};
+
+
+static const struct data_md5 md5_tests[] = {
+  {__LINE__,
+   "u", "r",
+   {0xba, 0x84, 0xbe, 0x20, 0x3f, 0xdf, 0xc7, 0xd3, 0x4e, 0x05, 0x4a, 0x76,
+    0xd2, 0x85, 0xd0, 0xc9}},
+  {__LINE__,
+   "testuser", "testrealm",
+   {0xab, 0xae, 0x15, 0x95, 0x24, 0xe5, 0x17, 0xbf, 0x48, 0xf4, 0x4a, 0xab,
+    0xfe, 0xb9, 0x37, 0x40}},
+  {__LINE__,
+   "test_user", "TestRealm", /* Values from testcurl/test_digestauth2.c */
+   {0xc5, 0x3c, 0x60, 0x15, 0x03, 0xff, 0x17, 0x6f, 0x18, 0xf6, 0x23, 0x72,
+    0x5f, 0xba, 0x42, 0x81}},
+  {__LINE__,
+   "Mufasa", "myhost@testrealm.com",
+   {0x26, 0x45, 0xae, 0x13, 0xd1, 0xa2, 0xa6, 0x9e, 0xd2, 0x6d, 0xd2, 0x1a,
+    0xa5, 0x52, 0x86, 0xe3}},
+  {__LINE__,
+   "Mufasa", "myhost@example.com",
+   {0x4f, 0xc8, 0x64, 0x02, 0xd7, 0x18, 0x5d, 0x7b, 0x38, 0xd4, 0x38, 0xad,
+    0xd5, 0x5a, 0x35, 0x84}},
+  {__LINE__,
+   "Mufasa", "http-auth@example.org",
+   {0x42, 0x38, 0xf3, 0xa1, 0x61, 0x67, 0x37, 0x3f, 0xeb, 0xb9, 0xbc, 0x4d,
+    0x43, 0xdb, 0x9c, 0xc4}},
+  {__LINE__,
+   "J" "\xC3\xA4" "s" "\xC3\xB8" "n Doe" /* "Jäsøn Doe" */, "api@example.org",
+   {0x2e, 0x06, 0x3f, 0xa2, 0xc5, 0x4d, 0xea, 0x1c, 0x36, 0x80, 0x8b, 0x7a,
+    0x6e, 0x3b, 0x14, 0xc9}}
+};
+
+struct data_sha256
+{
+  unsigned int line_num;
+  const char *const username;
+  const char *const realm;
+  const uint8_t hash[MHD_SHA256_DIGEST_SIZE];
+};
+
+static const struct data_sha256 sha256_tests[] = {
+  {__LINE__,
+   "u", "r",
+   {0x1d, 0x8a, 0x03, 0xa6, 0xe2, 0x1a, 0x4c, 0xe7, 0x75, 0x06, 0x0e, 0xa5,
+    0x73, 0x60, 0x32, 0x9a, 0xc7, 0x50, 0xde, 0xa5, 0xd8, 0x47, 0x29, 0x7b,
+    0x42, 0xf0, 0xd4, 0x65, 0x39, 0xaf, 0x8a, 0xb2}},
+  {__LINE__,
+   "testuser", "testrealm",
+   {0x75, 0xaf, 0x8a, 0x35, 0x00, 0xf7, 0x71, 0xe5, 0x8a, 0x52, 0x09, 0x3a,
+    0x25, 0xe7, 0x90, 0x5d, 0x6e, 0x42, 0x8a, 0x51, 0x12, 0x85, 0xc1, 0x2e,
+    0xa1, 0x42, 0x0c, 0x73, 0x07, 0x8d, 0xfd, 0x61}},
+  {__LINE__,
+   "test_user", "TestRealm", /* Values from testcurl/test_digestauth2.c */
+   {0x09, 0x0c, 0x7e, 0x06, 0xb7, 0x7d, 0x66, 0x14, 0xcf, 0x5f, 0xe6, 0xca,
+    0xfa, 0x00, 0x4d, 0x2e, 0x5f, 0x8f, 0xb3, 0x6b, 0xa4, 0x5a, 0x0e, 0x35,
+    0xea, 0xcb, 0x2e, 0xb7, 0x72, 0x8f, 0x34, 0xde}},
+  {__LINE__,
+   "Mufasa", "myhost@testrealm.com",
+   {0x92, 0x9f, 0xac, 0x9e, 0x6c, 0x8b, 0x76, 0xcc, 0xab, 0xa9, 0xe0, 0x6f,
+    0xf6, 0x4d, 0xf2, 0x6f, 0xcb, 0x40, 0x56, 0x4c, 0x19, 0x9c, 0x32, 0xd9,
+    0xea, 0xd9, 0x12, 0x4b, 0x25, 0x34, 0xe1, 0xf9}},
+  {__LINE__,
+   "Mufasa", "myhost@example.com",
+   {0x97, 0x06, 0xf0, 0x07, 0x1c, 0xec, 0x05, 0x3f, 0x88, 0x22, 0xb6, 0x63,
+    0x69, 0xc4, 0xa4, 0x00, 0x39, 0x79, 0xb7, 0xe7, 0x42, 0xb7, 0x4e, 0x42,
+    0x59, 0x63, 0x57, 0xf4, 0xd3, 0x02, 0xae, 0x16}},
+  {__LINE__,
+   "Mufasa", "http-auth@example.org",
+   {0xa9, 0x47, 0xaa, 0xd2, 0x05, 0xe8, 0x0e, 0x42, 0x99, 0x58, 0xa3, 0x87,
+    0x39, 0x49, 0x44, 0xc6, 0xb4, 0x96, 0x30, 0x1e, 0x79, 0xf8, 0x9d, 0x35,
+    0xa4, 0xcc, 0x23, 0xb6, 0xee, 0x12, 0xb5, 0xb6}},
+  {__LINE__,
+   "J" "\xC3\xA4" "s" "\xC3\xB8" "n Doe" /* "Jäsøn Doe" */, "api@example.org",
+   {0x5a, 0x1a, 0x8a, 0x47, 0xdf, 0x5c, 0x29, 0x85, 0x51, 0xb9, 0xb4, 0x2b,
+    0xa9, 0xb0, 0x58, 0x35, 0x17, 0x4a, 0x5b, 0xd7, 0xd5, 0x11, 0xff, 0x7f,
+    0xe9, 0x19, 0x1d, 0x8e, 0x94, 0x6f, 0xc4, 0xe7}}
+};
+
+
+/*
+ *  Helper functions
+ */
+
+/**
+ * Print bin as lower case hex
+ *
+ * @param bin binary data
+ * @param len number of bytes in bin
+ * @param hex pointer to len*2+1 bytes buffer
+ */
+static void
+bin2hex (const uint8_t *bin,
+         size_t len,
+         char *hex)
+{
+  while (len-- > 0)
+  {
+    unsigned int b1, b2;
+    b1 = (*bin >> 4) & 0xf;
+    *hex++ = (char) ((b1 > 9) ? (b1 + 'a' - 10) : (b1 + '0'));
+    b2 = *bin++ & 0xf;
+    *hex++ = (char) ((b2 > 9) ? (b2 + 'a' - 10) : (b2 + '0'));
+  }
+  *hex = 0;
+}
+
+
+/* Tests */
+
+static unsigned int
+check_md5 (const struct data_md5 *const data)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_MD5;
+  uint8_t hash_bin[MHD_MD5_DIGEST_SIZE];
+  char hash_hex[MHD_MD5_DIGEST_SIZE * 2 + 1];
+  char expected_hex[MHD_MD5_DIGEST_SIZE * 2 + 1];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userhash";
+  if (MHD_YES != MHD_digest_auth_calc_userhash (algo3,
+                                                data->username, data->realm,
+                                                hash_bin, sizeof(hash_bin)))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_YES.\n",
+             func_name);
+  }
+  else if (0 != memcmp (hash_bin, data->hash, sizeof(data->hash)))
+  {
+    failed++;
+    bin2hex (hash_bin, sizeof(hash_bin), hash_hex);
+    bin2hex (data->hash, sizeof(data->hash), expected_hex);
+    fprintf (stderr,
+             "FAILED: %s() produced wrong hash. "
+             "Calculated digest %s, expected digest %s.\n",
+             func_name,
+             hash_hex, expected_hex);
+  }
+
+  func_name = "MHD_digest_auth_calc_userhash_hex";
+  if (MHD_YES !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         data->username, data->realm,
+                                         hash_hex, sizeof(hash_hex)))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_YES.\n",
+             func_name);
+  }
+  else if (sizeof(hash_hex) - 1 != strlen (hash_hex))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s produced hash with wrong length. "
+             "Calculated length %u, expected digest %u.\n",
+             func_name,
+             (unsigned) strlen (hash_hex),
+             (unsigned) (sizeof(hash_hex) - 1));
+  }
+  else
+  {
+    bin2hex (data->hash, sizeof(data->hash), expected_hex);
+    if (0 != memcmp (hash_hex, expected_hex, sizeof(hash_hex)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() produced wrong hash. "
+               "Calculated digest %s, expected digest %s.\n",
+               func_name,
+               hash_hex, expected_hex);
+    }
+  }
+
+  if (failed)
+  {
+    fprintf (stderr,
+             "The check failed for data located at line: %u.\n",
+             data->line_num);
+    fflush (stderr);
+  }
+  else if (verbose)
+  {
+    printf ("PASSED: check for data at line: %u.\n",
+            data->line_num);
+  }
+  return failed ? 1 : 0;
+}
+
+
+static unsigned int
+test_md5 (void)
+{
+  unsigned int num_failed = 0;
+  size_t i;
+
+  for (i = 0; i < sizeof(md5_tests) / sizeof(md5_tests[0]); i++)
+    num_failed += check_md5 (md5_tests + i);
+  return num_failed;
+}
+
+
+static unsigned int
+test_md5_failure (void)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_MD5;
+  uint8_t hash_bin[MHD_MD5_DIGEST_SIZE];
+  char hash_hex[MHD_MD5_DIGEST_SIZE * 2 + 1];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userhash";
+  if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                               "u", "r",
+                                               hash_bin, sizeof(hash_bin) - 1))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                               "u", "r",
+                                               hash_bin, 0))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+  {
+    if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                                 "u", "r",
+                                                 hash_bin, sizeof(hash_bin)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+               func_name, (unsigned) __LINE__);
+    }
+  }
+
+  func_name = "MHD_digest_auth_calc_userhash_hex";
+  if (MHD_NO !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         "u", "r",
+                                         hash_hex, sizeof(hash_hex) - 1))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         "u", "r",
+                                         hash_hex, 0))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+  {
+    if (MHD_NO !=
+        MHD_digest_auth_calc_userhash_hex (algo3,
+                                           "u", "r",
+                                           hash_hex, sizeof(hash_hex)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+               func_name, (unsigned) __LINE__);
+    }
+  }
+
+  if (! failed && verbose)
+  {
+    printf ("PASSED: all checks with expected MHD_NO result near line: %u.\n",
+            (unsigned) __LINE__);
+  }
+  return failed ? 1 : 0;
+}
+
+
+static unsigned int
+check_sha256 (const struct data_sha256 *const data)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_SHA256;
+  uint8_t hash_bin[MHD_SHA256_DIGEST_SIZE];
+  char hash_hex[MHD_SHA256_DIGEST_SIZE * 2 + 1];
+  char expected_hex[MHD_SHA256_DIGEST_SIZE * 2 + 1];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userhash";
+  if (MHD_YES != MHD_digest_auth_calc_userhash (algo3,
+                                                data->username, data->realm,
+                                                hash_bin, sizeof(hash_bin)))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_YES.\n",
+             func_name);
+  }
+  else if (0 != memcmp (hash_bin, data->hash, sizeof(data->hash)))
+  {
+    failed++;
+    bin2hex (hash_bin, sizeof(hash_bin), hash_hex);
+    bin2hex (data->hash, sizeof(data->hash), expected_hex);
+    fprintf (stderr,
+             "FAILED: %s() produced wrong hash. "
+             "Calculated digest %s, expected digest %s.\n",
+             func_name,
+             hash_hex, expected_hex);
+  }
+
+  func_name = "MHD_digest_auth_calc_userhash_hex";
+  if (MHD_YES !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         data->username, data->realm,
+                                         hash_hex, sizeof(hash_hex)))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_YES.\n",
+             func_name);
+  }
+  else if (sizeof(hash_hex) - 1 != strlen (hash_hex))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s produced hash with wrong length. "
+             "Calculated length %u, expected digest %u.\n",
+             func_name,
+             (unsigned) strlen (hash_hex),
+             (unsigned) (sizeof(hash_hex) - 1));
+  }
+  else
+  {
+    bin2hex (data->hash, sizeof(data->hash), expected_hex);
+    if (0 != memcmp (hash_hex, expected_hex, sizeof(hash_hex)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() produced wrong hash. "
+               "Calculated digest %s, expected digest %s.\n",
+               func_name,
+               hash_hex, expected_hex);
+    }
+  }
+
+  if (failed)
+  {
+    fprintf (stderr,
+             "The check failed for data located at line: %u.\n",
+             data->line_num);
+    fflush (stderr);
+  }
+  else if (verbose)
+  {
+    printf ("PASSED: check for data at line: %u.\n",
+            data->line_num);
+  }
+  return failed ? 1 : 0;
+}
+
+
+static unsigned int
+test_sha256 (void)
+{
+  unsigned int num_failed = 0;
+  size_t i;
+
+  for (i = 0; i < sizeof(sha256_tests) / sizeof(sha256_tests[0]); i++)
+    num_failed += check_sha256 (sha256_tests + i);
+  return num_failed;
+}
+
+
+static unsigned int
+test_sha256_failure (void)
+{
+  static const enum MHD_DigestAuthAlgo3 algo3 = MHD_DIGEST_AUTH_ALGO3_SHA256;
+  uint8_t hash_bin[MHD_SHA256_DIGEST_SIZE];
+  char hash_hex[MHD_SHA256_DIGEST_SIZE * 2 + 1];
+  const char *func_name;
+  unsigned int failed = 0;
+
+  func_name = "MHD_digest_auth_calc_userhash";
+  if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                               "u", "r",
+                                               hash_bin, sizeof(hash_bin) - 1))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                               "u", "r",
+                                               hash_bin, 0))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+  {
+    if (MHD_NO != MHD_digest_auth_calc_userhash (algo3,
+                                                 "u", "r",
+                                                 hash_bin, sizeof(hash_bin)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+               func_name, (unsigned) __LINE__);
+    }
+  }
+
+  func_name = "MHD_digest_auth_calc_userhash_hex";
+  if (MHD_NO !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         "u", "r",
+                                         hash_hex, sizeof(hash_hex) - 1))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO !=
+      MHD_digest_auth_calc_userhash_hex (algo3,
+                                         "u", "r",
+                                         hash_hex, 0))
+  {
+    failed++;
+    fprintf (stderr,
+             "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+             func_name, (unsigned) __LINE__);
+  }
+  if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+  {
+    if (MHD_NO !=
+        MHD_digest_auth_calc_userhash_hex (algo3,
+                                           "u", "r",
+                                           hash_hex, sizeof(hash_hex)))
+    {
+      failed++;
+      fprintf (stderr,
+               "FAILED: %s() has not returned MHD_NO at line: %u.\n",
+               func_name, (unsigned) __LINE__);
+    }
+  }
+
+  if (! failed && verbose)
+  {
+    printf ("PASSED: all checks with expected MHD_NO result near line: %u.\n",
+            (unsigned) __LINE__);
+  }
+  return failed ? 1 : 0;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  unsigned int num_failed = 0;
+  (void) has_in_name; /* Mute compiler warning. */
+  if (has_param (argc, argv, "-s") || has_param (argc, argv, "--silent"))
+    verbose = 0;
+
+  if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_MD5))
+    num_failed += test_md5 ();
+  num_failed += test_md5_failure ();
+  if (MHD_is_feature_supported (MHD_FEATURE_DIGEST_AUTH_SHA256))
+    num_failed += test_sha256 ();
+  num_failed += test_sha256_failure ();
+
+  return num_failed ? 1 : 0;
+}
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index a31cb1ee..34ea21f0 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -177,3 +177,4 @@ core
 /test_digestauth2_bind_uri
 /test_digestauth2_oldapi1_bind_all
 /test_digestauth2_oldapi1_bind_uri
+test_*[a-z0-9_][a-z0-9_][a-z0-9_]
diff --git a/src/testcurl/test_digestauth2.c b/src/testcurl/test_digestauth2.c
index e3c12d85..f92d6d14 100644
--- a/src/testcurl/test_digestauth2.c
+++ b/src/testcurl/test_digestauth2.c
@@ -254,16 +254,11 @@ _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
 /* The hex form of MD5("test_user:TestRealm:test pass") */
 #define USERDIGEST1_MD5_BIN 0xd8, 0xb4, 0xa6, 0xd0, 0x01, 0x13, 0x07, 0xb7, \
   0x67, 0x94, 0xea, 0x66, 0x86, 0x03, 0x6b, 0x43
-/* The hex form of SHA-256("test_user:TestRealm:test pass") */
 /* The binary form of SHA-256("test_user:TestRealm:test pass") */
 #define USERDIGEST1_SHA256_BIN 0xc3, 0x4e, 0x16, 0x5a, 0x17, 0x0f, 0xe5, \
   0xac, 0x04, 0xf1, 0x6e, 0x46, 0x48, 0x2b, 0xa0, 0xc6, 0x56, 0xc1, 0xfb, \
   0x8f, 0x66, 0xa6, 0xd6, 0x3f, 0x91, 0x12, 0xf8, 0x56, 0xa5, 0xec, 0x6d, \
   0x6d
-/* "titkos szuperügynök" in UTF-8 */
-#define USERNAME2 "titkos szuper" "\xC3\xBC" "gyn" "\xC3\xB6" "k"
-/* percent-encoded username */
-#define USERNAME2_PCTENC "titkos%20szuper%C3%BCgyn%C3%B6k"
 #define PASSWORD_VALUE "test pass"
 #define OPAQUE_VALUE "opaque+content" /* Base64 character set */
 
@@ -550,11 +545,11 @@ ahc_echo (void *cls,
       }
       if (algo3 != dinfo->algo3)
       {
-        fprintf (stderr, "Unexpected 'algo'.\n"
+        fprintf (stderr, "Unexpected 'algo3'.\n"
                  "Expected: %d\tRecieved: %d. ",
                  (int) algo3,
                  (int) dinfo->algo3);
-        mhdErrorExitDesc ("Wrong 'algo'");
+        mhdErrorExitDesc ("Wrong 'algo3'");
       }
       if (! test_rfc2069)
       {
@@ -692,6 +687,14 @@ ahc_echo (void *cls,
         else if (NULL != uname->userhash_bin)
           mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
       }
+      if (algo3 != uname->algo3)
+      {
+        fprintf (stderr, "Unexpected 'algo3'.\n"
+                 "Expected: %d\tRecieved: %d. ",
+                 (int) algo3,
+                 (int) uname->algo3);
+        mhdErrorExitDesc ("Wrong 'algo3'");
+      }
       MHD_free (uname);
 
       if (! test_userdigest)
diff --git a/src/testzzuf/.gitignore b/src/testzzuf/.gitignore
index 1de479de..e57a1764 100644
--- a/src/testzzuf/.gitignore
+++ b/src/testzzuf/.gitignore
@@ -41,3 +41,4 @@
 /daemontest_get_chunked
 /daemontest_get11
 *.exe
+test_*[a-z0-9_][a-z0-9_][a-z0-9_]

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]